21 Commits

Author SHA1 Message Date
NCBM f89c8dec31 lmproto/openai_compatible: drop provider blocks on a chat wire
`provider_blocks` means something only to the surface that produced them (Anthropic `thinking` / server-tool blocks). A session that switched surface would otherwise send those dicts to a chat-completions provider as an assistant-message key it does not know; the request normalization drops the field, on a copy, so the history keeps it.
2026-10-09 20:09:36 +08:00
NCBM 2fdbc3ce5d core/agent: keep a Messages round's provider blocks on the assistant message
DeepSeek's Anthropic compat answers a hosted search inside the turn: the response carries the `thinking` that picked the query, the `server_tool_use`, and the `web_search_tool_result`. The response mapping kept text and tool_use only, so the round lost its evidence — and the `thinking` signature, which only the provider can verify, could not go back at all. `AssistantChatMessage.provider_blocks` carries those blocks as raw JSON now (`thinking` text *and* signature, the query, the result entries with an `encrypted_content` the API rejects when trimmed) and the assistant turn hands them back first, ahead of its text and tool_use blocks; `thinking` also lands in `reasoning_content`, so the chain of thought streams as reasoning like every other surface's.

`AnthropicRawContentBlock` names the provider fields a streamed block carries, and `AnthropicServerToolUseContent` keeps the `caller` metadata the wire sends.
2026-10-09 20:09:20 +08:00
NCBM e2ee937d8f lmproto: search through DeepSeek's Anthropic-compatible endpoint
DeepSeek runs ``web_search`` inside a turn — the request declares the server
tool, the response carries a ``server_tool_use`` per query and the
``web_search_tool_result`` it produced — so a search is one ``/messages`` call
whose hits are read out of the content blocks. The turn stops as soon as it has
searched (a live probe with 64 output tokens spent all of them before the search
and returned nothing, 512 answers with the hits).

Hits carry a title, a URL and an ``encrypted_content`` only the index can read,
so the hit list is all a caller gets: the page bodies stay server-side.
2026-10-06 23:45:29 +08:00
NCBM 45246bf949 lmproto: keep the thinking block's type on the wire
``omit_defaults`` dropped the block's defaulted discriminator, and a request
that carried thinking was answered with 422 "thinking: missing field ``type``" —
which is what the compat sent for any configured ``reasoning_effort``. The field
has no default now, so it always encodes (both call sites pass it).
2026-10-06 23:36:29 +08:00
NCBM 1fa41c9fde core/agent: run a hosted search as the surface's own tool
DeepSeek's Anthropic compat runs a hosted ``web_search`` itself and answers with
the blocks that search produced — ``thinking``, ``server_tool_use`` (the query)
and ``web_search_tool_result`` — so the content union that knew only text and
tool_use could not decode a live search turn. It carries them now, ``tools``
accepts the opaque server-tool dicts the surface declares, and
``web_search_tool_result.content`` stays loose on purpose: its
``web_search_result`` entries hold an ``encrypted_content`` the API demands back
unchanged (a trimmed result is answered with 422 "missing field
``encrypted_content``").

``provider_tools`` reached only the Responses surface, and the Anthropic one
wants its own definition of the same intent: a bare ``web_search`` becomes
``{type="web_search_20250305", name="web_search"}`` (the server tool Claude
declares and DeepSeek's compat reads, keeping a ``max_uses`` cap or any other
key), merged after the local function tools.

Such a tool is the server's: it streams ``input_json_delta`` fragments like a
call, and reading them as one asked the registry for a tool the server had
already run. Block types are now tracked, so only a ``tool_use`` block becomes a
tool call; the search blocks and ``thinking`` are skipped by the response
mapping (the answer the model wrote already used them) and never executed.
2026-10-06 23:35:46 +08:00
NCBM 0da6e8da8d lmproto: give every DeepSeek assistant round its reasoning field
Thinking mode checks the field per round, not per call: an answer whose message
carried no `reasoning_content` came back 400 with the wording a call gets ("the
`reasoning_content` … must be passed back to the API"), while an empty string
satisfies the check. The injection now touches every assistant message instead
of only the ones carrying `tool_calls`.
2026-10-06 23:23:24 +08:00
NCBM ec5340bdce config: rename the DeepSeek flash model to deepseek-flash
DeepSeek dropped the `v4` prefix from its flash model, and the old id stays
valid only as a compatibility alias. Seed the default catalog with the current
name so a provider that omits `models` no longer offers a deprecated id, and
follow the rename in the wire fixtures and the minimal config template. An
explicit `deepseek-v4-flash` in TOML still passes through unchanged.
2026-10-06 21:22:35 +08:00
NCBM 445aba7e5f lmproto: carry thinking options on the wire 2026-10-06 21:11:28 +08:00
NCBM 439c24a1ce lmproto: add DeepSeek Anthropic client 2026-08-01 02:30:19 +08:00
NCBM db6d2a83f4 lmproto: add DeepSeek Responses client 2026-08-01 02:01:06 +08:00
NCBM 06f1b386a0 test/lmproto: cover anthropic messages client HTTP round-trips 2026-08-01 00:41:59 +08:00
NCBM 9600d2f757 core/lmproto: coerce developer role for chat completions 2026-07-28 22:16:36 +08:00
NCBM d267a5f869 ci/lint: apply ruff format and import sort fixes 2026-07-15 22:09:52 +08:00
NCBM 38edc09854 core/lmproto: move Responses API into openai package
openai_compatible stays chat-completions-only; OpenAI provider uses lmproto.openai.OpenAIClient.
2026-07-15 20:52:17 +08:00
NCBM 3e20b1559c core/lmproto: add OpenAI Responses API models and client
POST/GET/DELETE /responses with stream events; helpers for output_text and function_call items.
2026-07-15 20:43:11 +08:00
NCBM e1ea43cb8f core/lmproto: add OpenAI-compatible models() list API
Decode GET /models into ModelsResponse and return sorted unique ids.
2026-07-15 17:31:57 +08:00
NCBM 9cc2216807 core/lmproto: allow null logprobs and usage on stream chunks
Providers send logprobs=null (and sometimes usage=null) on SSE choices;
accept None so decode does not fail mid-stream.
2026-07-15 14:23:23 +08:00
NCBM 366752efb9 core/lmproto: allow null content on stream deltas
OpenAI-compatible and DeepSeek SSE chunks often set content and
reasoning_content to null; accept None so decode does not fail mid-stream.
2026-07-15 14:21:50 +08:00
NCBM d5e7c3aa53 core/lmproto: await async AsyncResponse.content
niquests exposes content as an async property; reading it without await
left a coroutine warning and skipped the body. Only load body on HTTP
errors for streams so SSE is not consumed early.
2026-07-15 10:41:33 +08:00
NCBM a78791b3a9 core/lmproto: extract pure SSE helpers; drop fake response tests
Test sse_data_payload and http_error_message with plain bytes instead
of a cast-heavy AsyncResponse double.
2026-07-15 10:38:17 +08:00
NCBM c5745e0711 core/lmproto: stop SSE stream at data: [DONE]
Previously DONE was skipped with continue, so keep-alive connections
could hang forever after the model finished. Also fail fast on HTTP
error statuses for stream and non-stream completions.
2026-07-15 10:33:47 +08:00