feat(llm): halkyon_llm, language models' APIs streamed over plain HTTP (ADR 0021, step 2)
A pure Dart package, no SDKs, one interface (LlmClient) over Anthropic's Messages API, OpenAI's chat completions, in each known host's dialect and the local servers' (D45), and OpenAI's Responses API, stateless (D46). A model's turn keeps what its provider wants back with the tools' results: Anthropic's thinking blocks, the Responses API's reasoning items and message phase, Gemini's thought signatures. Failures come by kind, the provider's words cut and without keys. Tested on replies written from the providers' documentation, replayed in pieces of 1, 7 and 4,096 bytes; tool/record_llm.dart records real ones with a key of one's own, which the tests then run on too (D36). FakeLlmClient for the code that uses it. BSD-3-Clause, as halkyon_oidc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e4c7259df3
commit
038aca5439
13 changed files with 4841 additions and 0 deletions
4
packages/halkyon_llm/test/fixtures/documented/openai/gemini_status_400.http
vendored
Normal file
4
packages/halkyon_llm/test/fixtures/documented/openai/gemini_status_400.http
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
HTTP 400
|
||||
content-type: application/json
|
||||
|
||||
[{"error":{"code":400,"message":"Invalid JSON payload received. Unknown name \"store\": Cannot find field.","status":"INVALID_ARGUMENT"}}]
|
||||
9
packages/halkyon_llm/test/fixtures/documented/openai/gemini_tool.http
vendored
Normal file
9
packages/halkyon_llm/test/fixtures/documented/openai/gemini_tool.http
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream
|
||||
|
||||
data: {"choices":[{"delta":{"content":"Ich sehe nach.","role":"assistant"},"index":0}],"created":1761400300,"id":"gR0qaZ6uNvqU","model":"gemini-3-flash","object":"chat.completion.chunk","usage":{"completion_tokens":4,"prompt_tokens":472,"total_tokens":540}}
|
||||
|
||||
data: {"choices":[{"delta":{"role":"assistant","tool_calls":[{"extra_content":{"google":{"thought_signature":"CiQBcsjafQ=="}},"function":{"arguments":"{\"query\":\"Hund\",\"limit\":5}","name":"find_cards"},"id":"function-call-1","type":"function"}]},"finish_reason":"stop","index":0}],"created":1761400300,"id":"gR0qaZ6uNvqU","model":"gemini-3-flash","object":"chat.completion.chunk","usage":{"completion_tokens":21,"prompt_tokens":472,"total_tokens":561}}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
7
packages/halkyon_llm/test/fixtures/documented/openai/llamacpp_error.http
vendored
Normal file
7
packages/halkyon_llm/test/fixtures/documented/openai/llamacpp_error.http
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream
|
||||
|
||||
data: {"choices":[{"finish_reason":null,"index":0,"delta":{"role":"assistant","content":null}}],"created":1761400500,"id":"chatcmpl-x3Yb","model":"gpt-oss-20b","system_fingerprint":"b6900","object":"chat.completion.chunk"}
|
||||
|
||||
data: {"error":{"code":500,"message":"the request exceeds the available context size","type":"server_error"}}
|
||||
|
||||
4
packages/halkyon_llm/test/fixtures/documented/openai/mistral_status_422.http
vendored
Normal file
4
packages/halkyon_llm/test/fixtures/documented/openai/mistral_status_422.http
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
HTTP 422
|
||||
content-type: application/json
|
||||
|
||||
{"object":"error","message":{"detail":[{"type":"extra_forbidden","loc":["body","messages",0,"content"],"msg":"Extra inputs are not permitted","input":"Mein geheimer Text"}]},"type":"invalid_request_message_error","param":null,"code":null}
|
||||
9
packages/halkyon_llm/test/fixtures/documented/openai/mistral_tool.http
vendored
Normal file
9
packages/halkyon_llm/test/fixtures/documented/openai/mistral_tool.http
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream; charset=utf-8
|
||||
|
||||
data: {"id":"8c1f0e2a5b7d4c3e9f6a1b2c3d4e5f60","object":"chat.completion.chunk","created":1761400200,"model":"mistral-medium-latest","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}],"p":"abcdef"}
|
||||
|
||||
data: {"id":"8c1f0e2a5b7d4c3e9f6a1b2c3d4e5f60","object":"chat.completion.chunk","created":1761400200,"model":"mistral-medium-latest","choices":[{"index":0,"delta":{"tool_calls":[{"id":"D681PevKs","function":{"name":"find_cards","arguments":"{\"query\": \"Hund\", \"limit\": 5}"},"index":0},{"id":"Qk2Ty7Wb1","function":{"name":"find_cards","arguments":"{\"query\": \"犬\"}"},"index":1}]},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":472,"total_tokens":561,"completion_tokens":89},"p":"ab"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
7
packages/halkyon_llm/test/fixtures/documented/openai/ollama_lost_error.http
vendored
Normal file
7
packages/halkyon_llm/test/fixtures/documented/openai/ollama_lost_error.http
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream
|
||||
|
||||
data: {"id":"chatcmpl-412","object":"chat.completion.chunk","created":1761400400,"model":"qwen3:8b","system_fingerprint":"fp_ollama","choices":[{"index":0,"delta":{"role":"assistant","content":"„Hund“"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-412","object":"chat.completion.chunk","created":1761400400,"model":"","system_fingerprint":"fp_ollama","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
|
||||
|
||||
9
packages/halkyon_llm/test/fixtures/documented/openai/openrouter_error.http
vendored
Normal file
9
packages/halkyon_llm/test/fixtures/documented/openai/openrouter_error.http
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream
|
||||
|
||||
: OPENROUTER PROCESSING
|
||||
|
||||
data: {"id":"gen-1761400600-a","object":"chat.completion.chunk","created":1761400600,"model":"mistralai/mistral-small","provider":"Mistral","choices":[{"index":0,"delta":{"role":"assistant","content":"„Hund“"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"gen-1761400600-a","object":"chat.completion.chunk","created":1761400600,"model":"mistralai/mistral-small","provider":"Mistral","error":{"code":"server_error","message":"Provider disconnected"},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}
|
||||
|
||||
4
packages/halkyon_llm/test/fixtures/documented/openai/status_400.http
vendored
Normal file
4
packages/halkyon_llm/test/fixtures/documented/openai/status_400.http
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
HTTP 400
|
||||
content-type: application/json
|
||||
|
||||
{"error":{"message":"Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.","type":"invalid_request_error","param":"max_tokens","code":"unsupported_parameter"}}
|
||||
4
packages/halkyon_llm/test/fixtures/documented/openai/status_401.http
vendored
Normal file
4
packages/halkyon_llm/test/fixtures/documented/openai/status_401.http
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
HTTP 401
|
||||
content-type: application/json
|
||||
|
||||
{"error":{"message":"Incorrect API key provided: sk-proj-****wxyz. You can find your API key at https://platform.openai.com/account/api-keys.","type":"invalid_request_error","param":null,"code":"invalid_api_key"}}
|
||||
5
packages/halkyon_llm/test/fixtures/documented/openai/status_429.http
vendored
Normal file
5
packages/halkyon_llm/test/fixtures/documented/openai/status_429.http
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
HTTP 429
|
||||
content-type: application/json
|
||||
retry-after: 20
|
||||
|
||||
{"error":{"message":"Rate limit reached for gpt-5-mini in organization org-x on requests per min (RPM): Limit 500, Used 500, Requested 1.","type":"requests","param":null,"code":"rate_limit_exceeded"}}
|
||||
4
packages/halkyon_llm/test/fixtures/documented/openai/status_429_quota.http
vendored
Normal file
4
packages/halkyon_llm/test/fixtures/documented/openai/status_429_quota.http
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
HTTP 429
|
||||
content-type: application/json
|
||||
|
||||
{"error":{"message":"You exceeded your current quota, please check your plan and billing details.","type":"insufficient_quota","param":null,"code":"insufficient_quota"}}
|
||||
15
packages/halkyon_llm/test/fixtures/documented/openai/text.http
vendored
Normal file
15
packages/halkyon_llm/test/fixtures/documented/openai/text.http
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream; charset=utf-8
|
||||
|
||||
data: {"id":"chatcmpl-CQ3kT8vLwPz1","object":"chat.completion.chunk","created":1761400000,"model":"gpt-5-mini-2026-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Qx7"}
|
||||
|
||||
data: {"id":"chatcmpl-CQ3kT8vLwPz1","object":"chat.completion.chunk","created":1761400000,"model":"gpt-5-mini-2026-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"„Hund“ heißt auf Japanisch"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"bR2k9"}
|
||||
|
||||
data: {"id":"chatcmpl-CQ3kT8vLwPz1","object":"chat.completion.chunk","created":1761400000,"model":"gpt-5-mini-2026-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" 犬 (いぬ, inu)."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Z"}
|
||||
|
||||
data: {"id":"chatcmpl-CQ3kT8vLwPz1","object":"chat.completion.chunk","created":1761400000,"model":"gpt-5-mini-2026-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"m4Tq"}
|
||||
|
||||
data: {"id":"chatcmpl-CQ3kT8vLwPz1","object":"chat.completion.chunk","created":1761400000,"model":"gpt-5-mini-2026-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":412,"completion_tokens":38,"total_tokens":450,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"7y"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
19
packages/halkyon_llm/test/fixtures/documented/openai/tool.http
vendored
Normal file
19
packages/halkyon_llm/test/fixtures/documented/openai/tool.http
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
HTTP 200
|
||||
content-type: text/event-stream; charset=utf-8
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_DdmO9pD3xa9XTPNJ32zg2hcA","type":"function","function":{"name":"find_cards","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"query\":"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Hund\",\"limit\":5}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_7Hq2ZtX0aP4kLm9sB1cD3eF5","type":"function","function":{"name":"find_cards","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"query\":\"犬\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-CQ4aB2","object":"chat.completion.chunk","created":1761400100,"model":"gpt-5-mini-2026-08-07","choices":[],"usage":{"prompt_tokens":472,"completion_tokens":89,"total_tokens":561,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":64}}}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
Loading…
Reference in a new issue