Something went wrong. Try again.
small gleam coding and (not yet) persistent agent daemon with a detachable cli forked from okami.mom/albedo
Something went wrong. Try again.
17 kB · 427 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428// OpenAI streaming wire-format errors and partial tool calls must never become executable output.import albedo/openai_api/sseimport albedo/openai_api/streamimport albedo/openai_api/typesimport gleam/dynamic/decodeimport gleam/jsonimport gleam/listimport gleam/option.{type Option, None, Some}
pub fn responses_incomplete_never_exposes_executable_calls_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let data = "{\"type\":\"response.incomplete\",\"response\":{\"output\":[{\"type\":\"function_call\",\"call_id\":\"danger\",\"name\":\"delete\",\"arguments\":\"{}\"}],\"incomplete_details\":{\"reason\":\"max_output_tokens\"}}}" let assert Ok(#( _, [], Some(types.Turn(None, [_], [], None, types.LengthLimit, None)), )) = send(stream.new(types.Responses), "response.incomplete", data)}
pub fn responses_provider_failures_and_malformed_fields_are_typed_test() -> Nil { let failed = "{\"type\":\"response.failed\",\"response\":{\"error\":{\"message\":\"capacity\"}}}" assert send(stream.new(types.Responses), "response.failed", failed) == Error(types.ProviderError("capacity")) let malformed = "{\"type\":\"response.created\",\"response\":{\"id\":42}}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.Responses), "response.created", malformed) let unsupported = "{\"type\":\"response.custom_tool_call_input.delta\",\"delta\":\"rm\"}" assert send( stream.new(types.Responses), "response.custom_tool_call_input.delta", unsupported, ) == Error(types.Unsupported( "unsupported Responses semantic event: response.custom_tool_call_input.delta", ))}
pub fn responses_argument_deltas_name_the_tool_their_item_opened_with_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let added = "{\"type\":\"response.output_item.added\",\"output_index\":1,\"item\":{\"type\":\"function_call\",\"call_id\":\"c\",\"name\":\"web_search\",\"arguments\":\"\"}}" let assert Ok(#(state, [], None)) = send(stream.new(types.Responses), "response.output_item.added", added) let delta = "{\"type\":\"response.function_call_arguments.delta\",\"output_index\":1,\"delta\":\"{\\\"q\"}" let assert Ok(#(_, [types.ArgumentsDelta(1, "web_search", "{\"q")], None)) = send(state, "response.function_call_arguments.delta", delta)}
pub fn chat_accumulates_interleaved_tools_usage_and_native_replay_test() -> Nil { let state = stream.new(types.ChatCompletions) let first = "{\"id\":\"chat_1\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"hel\",\"reasoning_content\":\"think \"},\"finish_reason\":null}]}" let assert Ok(#( state, [ types.Started("chat_1"), types.ThinkingDelta("think "), types.TextDelta(0, 0, "hel"), ], None, )) = send(state, "", first) let one = "{\"id\":\"chat_1\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":1,\"id\":\"call_b\",\"type\":\"function\",\"function\":{\"name\":\"beta\",\"arguments\":\"{\\\"b\\\":\"}},{\"index\":0,\"id\":\"call_a\",\"type\":\"function\",\"function\":{\"name\":\"alpha\",\"arguments\":\"{\\\"a\\\":\"}}]},\"finish_reason\":null}]}" let assert Ok(#( state, [ types.ArgumentsDelta(1, "beta", "{\"b\":"), types.ArgumentsDelta(0, "alpha", "{\"a\":"), ], None, )) = send(state, "", one) let two = "{\"id\":\"chat_1\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"lo\",\"refusal\":\"no\",\"reasoning_content\":\"carefully\",\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"1}\"}},{\"index\":1,\"function\":{\"arguments\":\"2}\"}}]},\"finish_reason\":\"tool_calls\"}]}" let assert Ok(#(state, events, None)) = send(state, "", two) assert events == [ types.ThinkingDelta("carefully"), types.TextDelta(0, 0, "lo"), types.ArgumentsDelta(0, "alpha", "1}"), types.ArgumentsDelta(1, "beta", "2}"), ] let usage = "{\"id\":\"chat_1\",\"choices\":[],\"usage\":{\"prompt_tokens\":5,\"completion_tokens\":8,\"total_tokens\":13,\"prompt_tokens_details\":{\"cached_tokens\":0}}}" let assert Ok(#(state, [], None)) = send(state, "", usage) let assert Ok(#( _, [], Some(types.Turn( Some("chat_1"), [message], [ types.ToolCall("call_a", "alpha", "{\"a\":1}"), types.ToolCall("call_b", "beta", "{\"b\":2}"), ], Some(types.Usage(5, 8, Some(0), None, None, None, None)), types.ToolCalls, None, )), )) = send(state, "", "[DONE]") let decoder = { use content <- decode.field("content", decode.string) use refusal <- decode.field("refusal", decode.string) use reasoning <- decode.field("reasoning_content", decode.string) use tool_ids <- decode.field( "tool_calls", decode.list({ use id <- decode.field("id", decode.string) decode.success(id) }), ) decode.success(#(content, refusal, reasoning, tool_ids)) } assert types.inspect_item(message, decoder) == Ok(#("hello", "no", "think carefully", ["call_a", "call_b"]))}
pub fn cache_usage_details_tolerate_null_and_reject_malformed_values_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let responses_null = "{\"type\":\"response.completed\",\"response\":{\"output\":[],\"usage\":{\"input_tokens\":3,\"output_tokens\":2,\"input_tokens_details\":null}}}" let assert Ok(#( _, [], Some(types.Turn( _, [], [], Some(types.Usage(3, 2, None, None, None, None, None)), _, None, )), )) = send(stream.new(types.Responses), "", responses_null)
let chat_null = "{\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":3,\"completion_tokens\":2,\"prompt_tokens_details\":null}}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", chat_null) let assert Ok(#( _, [], Some(types.Turn( _, _, [], Some(types.Usage(3, 2, None, None, None, None, None)), _, None, )), )) = send(state, "", "[DONE]")
let responses_malformed = "{\"type\":\"response.completed\",\"response\":{\"output\":[],\"usage\":{\"input_tokens\":3,\"output_tokens\":2,\"input_tokens_details\":{\"cached_tokens\":\"3\"}}}}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.Responses), "", responses_malformed)
let chat_malformed = "{\"choices\":[],\"usage\":{\"prompt_tokens\":3,\"completion_tokens\":2,\"prompt_tokens_details\":{\"cached_tokens\":\"3\"}}}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", chat_malformed)}
pub fn responses_separates_reasoning_summary_parts_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let part = fn(index) { "{\"type\":\"response.reasoning_summary_part.added\",\"output_index\":0,\"summary_index\":" <> index <> ",\"part\":{\"type\":\"summary_text\",\"text\":\"\"}}" } let state = stream.new(types.Responses) let assert Ok(#(state, [], None)) = send(state, "", part("0")) let assert Ok(#(_state, [types.ThinkingDelta("\n\n")], None)) = send(state, "", part("1"))}
pub fn chat_rejects_multiple_choices_and_nonempty_unknown_semantics_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let multiple = "{\"choices\":[{\"index\":0,\"delta\":{}},{\"index\":1,\"delta\":{}}]}" assert send(stream.new(types.ChatCompletions), "", multiple) == Error(types.Unsupported("multiple chat completion choices")) let audio = "{\"choices\":[{\"index\":0,\"delta\":{\"audio\":{\"id\":\"a\"}}}]}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", audio)}
pub fn chat_ignores_only_empty_unknown_fields_test() -> Nil { list.each(["null", "\"\"", "[]", "{}"], fn(value) { let data = "{\"choices\":[{\"index\":0,\"delta\":{\"content\":\"hello\",\"audio\":" <> value <> "}}]}" let assert Ok(#(_, [types.TextDelta(0, 0, "hello")], None)) = send(stream.new(types.ChatCompletions), "", data) }) list.each(["\"x\"", "[0]", "{\"id\":null}", "0", "false", "true"], fn(value) { let data = "{\"choices\":[{\"index\":0,\"delta\":{\"audio\":" <> value <> "}}]}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", data) })}
pub fn chat_rejects_non_object_deltas_test() -> Nil { list.each(["null", "[]", "42", "\"x\"", "false"], fn(value) { let data = "{\"choices\":[{\"index\":0,\"delta\":" <> value <> "}]}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", data) })}
pub fn chat_provider_errors_take_precedence_only_with_string_messages_test() -> Nil { list.each(["", "capacity"], fn(message) { let data = json.object([ #("error", json.object([#("message", json.string(message))])), #( "choices", json.array(["hello"], fn(content) { json.object([ #("index", json.int(0)), #("delta", json.object([#("content", json.string(content))])), ]) }), ), ]) |> json.to_string assert send(stream.new(types.ChatCompletions), "", data) == Error(types.ProviderError(message)) }) list.each( [ "null", "42", "\"x\"", "{}", "{\"message\":null}", "{\"message\":42}", "{\"message\":false}", "{\"message\":[]}", ], fn(value) { let data = "{\"error\":" <> value <> ",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"hello\"}}]}" let assert Ok(#(_, [types.TextDelta(0, 0, "hello")], None)) = send(stream.new(types.ChatCompletions), "", data) }, ) let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", "{\"error\":{\"message\":42}}") Nil}
pub fn chat_requires_finish_and_suppresses_partial_tools_on_limits_test() -> Nil { assert send(stream.new(types.ChatCompletions), "", "[DONE]") == Error(types.InvalidEvent("[DONE] before chat finish reason")) let partial = "{\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_partial\",\"type\":\"function\",\"function\":{\"name\":\"unsafe\",\"arguments\":\"{\"}}]},\"finish_reason\":\"length\"}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", partial) let assert Ok(#( _, [], Some(types.Turn(None, [message], [], None, types.LengthLimit, None)), )) = send(state, "", "[DONE]") let decoder = { use calls <- decode.optional_field( "tool_calls", None, decode.optional(decode.list(decode.dynamic)), ) decode.success(calls) } assert types.inspect_item(message, decoder) == Ok(None)}
pub fn malformed_json_and_content_filter_finish_are_typed_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let assert Error(types.InvalidEvent(_)) = send(stream.new(types.ChatCompletions), "", "{") let assert Error(types.InvalidEvent(_)) = send(stream.new(types.Responses), "", "[]")
let filtered = "{\"choices\":[{\"index\":0,\"delta\":{\"content\":\"safe prefix\"},\"finish_reason\":\"content_filter\"}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", filtered) let assert Ok(#( _, [], Some(types.Turn(_, _, [], _, types.ContentFiltered, None)), )) = send(state, "", "[DONE]")}
pub fn chat_assembles_name_fragments_and_encodes_tool_only_content_as_null_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let first = "{\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call1\",\"function\":{\"name\":\"read_\",\"arguments\":\"{\"}}]}}]}" let second = "{\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"name\":\"file\",\"arguments\":\"}\"}}]},\"finish_reason\":\"tool_calls\"}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", first) let assert Ok(#(state, _, None)) = send(state, "", second) let assert Ok(#(state, _, Some(turn))) = send(state, "", "[DONE]") assert turn.tool_calls == [types.ToolCall("call1", "read_file", "{}")] let assert [item] = turn.output let encoded = types.replay_json(item) |> json.to_string assert json.parse( encoded, decode.at(["content"], decode.optional(decode.string)), ) == Ok(None) let assert Error(types.InvalidEvent(_)) = send(state, "", "[DONE]")}
pub fn chat_rejects_data_after_finish_reason_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let first = "{\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}]}" let late = "{\"choices\":[{\"index\":0,\"delta\":{\"content\":\"late\"}}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", first) let assert Error(types.InvalidEvent(_)) = send(state, "", late)}
pub fn responses_rejects_incomplete_call_in_completed_response_test() -> Result( #(stream.State, List(types.Event), Option(types.Turn)), types.Error,) { let event = "{\"type\":\"response.completed\",\"response\":{\"output\":[{\"type\":\"function_call\",\"call_id\":\"c\",\"name\":\"tool\",\"arguments\":\"{}\",\"status\":\"in_progress\"}]}}" let assert Error(types.InvalidEvent(_)) = send(stream.new(types.Responses), "", event)}
pub fn compatible_reasoning_fields_are_preserved_without_renaming_test() -> Nil { let first = "{\"choices\":[{\"index\":0,\"delta\":{\"reasoning\":\"r1\",\"reasoning_content\":\"c1\"}}]}" let second = "{\"choices\":[{\"index\":0,\"delta\":{\"reasoning\":\"r2\",\"reasoning_content\":\"c2\",\"content\":\"answer\"},\"finish_reason\":\"stop\"}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", first) let assert Ok(#(state, _, None)) = send(state, "", second) let assert Ok(#(_, _, Some(turn))) = send(state, "", "[DONE]") let assert [item] = turn.output assert types.inspect_item(item, decode.at(["reasoning"], decode.string)) == Ok("r1r2") assert types.inspect_item( item, decode.at(["reasoning_content"], decode.string), ) == Ok("c1c2")}
pub fn reasoning_details_merge_fragments_by_index_and_keep_metadata_test() -> Nil { let first = "{\"choices\":[{\"index\":0,\"delta\":{\"reasoning\":\"a\",\"reasoning_details\":[{\"type\":\"reasoning.text\",\"index\":0,\"format\":\"unknown\",\"text\":\"a\"}]}}]}" let second = "{\"choices\":[{\"index\":0,\"delta\":{\"reasoning\":\"b\",\"reasoning_details\":[{\"type\":\"reasoning.text\",\"index\":0,\"format\":\"unknown\",\"text\":\"b\"}],\"content\":\"answer\"},\"finish_reason\":\"stop\"}]}" let assert Ok(#(state, _, None)) = send(stream.new(types.ChatCompletions), "", first) let assert Ok(#(state, _, None)) = send(state, "", second) let assert Ok(#(_, _, Some(turn))) = send(state, "", "[DONE]") let assert [item] = turn.output assert types.inspect_item( item, decode.at( ["reasoning_details"], decode.list(decode.at(["text"], decode.string)), ), ) == Ok(["ab"]) assert types.inspect_item( item, decode.at( ["reasoning_details"], decode.list(decode.at(["format"], decode.string)), ), ) == Ok(["unknown"])}
pub fn responses_uses_streamed_done_items_when_terminal_output_is_empty_test() -> Nil { let state = stream.new(types.Responses) let done = "{\"type\":\"response.output_item.done\",\"output_index\":0,\"item\":{\"type\":\"function_call\",\"call_id\":\"call_1\",\"name\":\"python\",\"arguments\":\"{\\\"code\\\":\\\"20 + 22\\\",\\\"timeout_ms\\\":1000}\",\"status\":\"completed\"}}" let assert Ok(#(state, [], None)) = send(state, "", done) let completed = "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_1\",\"output\":[],\"usage\":{\"input_tokens\":10,\"output_tokens\":5}}}" let assert Ok(#( _, [types.Started("resp_1")], Some(types.Turn( Some("resp_1"), [_], [types.ToolCall("call_1", "python", arguments)], _, types.ToolCalls, None, )), )) = send(state, "", completed) assert arguments == "{\"code\":\"20 + 22\",\"timeout_ms\":1000}"}
fn send( state: stream.State, name: String, data: String,) -> Result(#(stream.State, List(types.Event), Option(types.Turn)), types.Error) { stream.feed(state, sse.Event(name, data))}