Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
path: |
deps
_build
priv/plts
key: mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('mix.lock') }}
restore-keys: |
mix-${{ matrix.elixir }}-${{ matrix.otp }}-
Expand All @@ -50,6 +51,9 @@ jobs:
- name: Credo
run: mix credo --strict

- name: Dialyzer
run: mix dialyzer

- name: Run tests
run: mix test

Expand Down
27 changes: 10 additions & 17 deletions lib/codex_wrapper/session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,20 @@ defmodule CodexWrapper.Session do
@doc """
Send a message and return a stream of events.

Returns `{updated_session, stream}`. The session_id is captured from
the stream, so consume the stream before sending the next message.
Returns `{session, stream}`. The returned `session` is the **same
session passed in** — this function does *not* thread `session_id`
across turns. If you need multi-turn continuity, use `send/3`
instead, which runs the turn synchronously and updates `session_id`
from the final events.

Use `stream/3` when you want to observe events from a single turn
(for example, to render intermediate output as the CLI produces it)
and do not need to chain into a follow-up turn on the same thread.
"""
@spec stream(t(), String.t(), keyword()) :: {t(), Enumerable.t()}
def stream(%__MODULE__{} = session, prompt, opts \\ []) do
raw_stream = build_stream(session, prompt, opts)

session_ref = make_ref()
parent = self()

wrapped_stream =
Stream.each(raw_stream, &maybe_capture_session_id(&1, session_ref, parent))

{%{session | session_id: session_ref}, wrapped_stream}
{session, raw_stream}
end

@doc """
Expand Down Expand Up @@ -260,13 +260,6 @@ defmodule CodexWrapper.Session do
end)
end

defp maybe_capture_session_id(event, ref, parent) do
case JsonLineEvent.get(event, "session_id") do
nil -> :ok
sid -> Kernel.send(parent, {ref, :session_id, sid})
end
end

defp events_to_result(events) do
stdout = Enum.map_join(events, "\n", fn event -> event.raw end)

Expand Down
4 changes: 3 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule CodexWrapperEx.MixProject do
elixir: "~> 1.19",
start_permanent: Mix.env() == :prod,
deps: deps(),
dialyzer: [plt_file: {:no_warn, "_build/dev/dialyxir_#{System.otp_release()}.plt"}],
docs: docs(),
package: package(),
name: "CodexWrapper",
Expand All @@ -28,7 +29,8 @@ defmodule CodexWrapperEx.MixProject do
[
{:jason, "~> 1.4"},
{:ex_doc, "~> 0.35", only: :dev, runtime: false},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
]
end

Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
%{
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"},
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
"ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
Expand Down