You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Read images with deepseek-v4-flash-vision-exp, and move DeepSeek onto the Responses protocol (#185)
deepseek-v4-flash-vision-exp reads images, in a prompt and in a tool result, so it
joins the registry on the official API with Text, Image input, a 1M context window
and the deepseek-v4-flash price. A model without vision refuses an image item
instead of sending one: Chat Completions answers 400, but the Responses API
silently swaps the image for placeholder text.
DeepSeekV4Client moves from /chat/completions to /responses. thinking_level maps to
reasoning.effort pre-collapsed to what DeepSeek settles on (none/low/high/high/high/
max); effort "none" is what disables thinking there, since the Chat Completions
thinking toggle is ignored on this endpoint. The temperature, tool_choice, fast_mode
and prompt_caching restrictions are unchanged. The two SiliconFlow entries move to
the generic openai-chat client, which is the only protocol that endpoint serves.
Also in this change:
- All four Responses clients keep the order of the content items they replay. An
assistant turn that spoke before calling a tool used to replay as call, then text,
which DeepSeek answers with "No tool output found for tool call <id>". Claude,
Gemini and the Chat Completions clients were audited and needed nothing.
- An internal "unused" event never reaches a caller: the base client drops one that
escapes and raises on it under AGENTHUB_DEBUG. The development skill records that
rule and the ordering rule.
- The README lists the wire protocol behind every client_type, and the DeepSeek row
of the model table takes image input.
- New tests pin both contracts: test_message_order.py / message-order.test.ts run a
thinking-text-tool_call turn through all ten clients, and the unknown-event suites
run every client over its ignorable events with the debug guard on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBwEdjegVGtfY2yJKwhZ77
Copy file name to clipboardExpand all lines: .agents/skills/agenthub-dev/SKILL.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,6 +56,8 @@ CHANGELOG.md One brief line per release linking into change
56
56
-**Follow the reference client; do not redesign.**`gpt5_5/` is the shape for Responses-style protocols, `openai/` for Chat Completions: same method order, same control flow, same names. A new client should read as a diff against its reference, because that is how it will be reviewed.
57
57
-**Keep it readable top to bottom.** A reader should follow one request or response straight through the file without jumping between definitions, so inlining is the default: read the SDK's attributes directly (`model_output.delta`, `model_output.item.call_id`) and keep field access, usage arithmetic, and error text where they are used. Extract a helper only for a genuinely large, self-contained block — the kind that would bury the main flow if inlined, such as fetching and decoding an image — never for a few lines. Mirror the reference client's own private methods (`_convert_thinking_level_*`, `_convert_tool_choice`) instead of inventing a layer beside them; a shim that accepts both dicts and SDK objects is never one of them, because the events are typed with the SDK's own types.
58
58
-**Stream on deltas only.** Open a partial tool call on the item-added event (name plus call id), accumulate its arguments from the argument deltas, and emit the complete `tool_call` item at the terminal event, exactly as `gpt5_5` does — every client must emit a complete `tool_call`, not just partials. List completion events (`response.output_item.done` and its equivalents) with the ignored types: never re-read a completed item or cross-check it against what the deltas produced. Leave provider error events (`response.failed`, `response.error`, `error`) to the unknown-event guard rather than translating them into AgentHub errors.
59
+
-**`unused` never leaves the client.** A wire event a client has nothing universal to emit for gets `event_type: "unused"`, and the streaming loop skips it — it is the client's own bookkeeping, not an event a caller may see. The base client drops one that escapes and raises on it under `AGENTHUB_DEBUG`, so a client that forgets the filter fails in CI rather than shipping empty events.
60
+
-**The message transform keeps the order of the content items.** Whatever a turn produced — thinking, then text, then a tool call — has to leave the transform in that order. Where a protocol makes an item its own entry (a Responses `reasoning` / `function_call`, say) and the message text is collected separately, flush the collected text before appending the entry; a message appended after the items it preceded is what DeepSeek answers with "No tool output found for tool call". Anthropic Messages and Gemini keep one ordered block list per message, so appending in item order is enough; Chat Completions splits content and `tool_calls` into fields of one message and carries no interleaving at all.
59
61
-`UniConfig` keys rarely map one-to-one onto provider config keys. **Stop and ask**: list every non-obvious mapping and confirm it with the user before coding. Never decide silently.
60
62
- Every `ThinkingLevel` must stay usable on every client — never raise for a thinking level. Map each level to the closest level the model supports and degrade silently when a level has no exact equivalent (e.g. `gemini3_7` maps `NONE` to `MINIMAL`, or to `low` on the models that reject `minimal`; `kimi_k3` maps `NONE` to `low` because K3 cannot disable reasoning).
61
63
-`temperature` and `tool_choice` (and other unsupported parameter values, e.g. `prompt_caching`) may reject with an exception, but must raise the AgentHub-specific `UnsupportedParameterError` from `errors.py` / `errors.ts`, never a bare `ValueError`/`Error`. Keep the message wording consistent with existing clients (containing "not support").
`response.function_call_arguments.delta` and `response.completed` events, and rebuilding a
15
+
replayed chain of thought as a `reasoning` item whose `content` is `reasoning_text`.
16
+
- The two SiliconFlow entries, `deepseek-ai/DeepSeek-V4-Flash` and
17
+
`deepseek-ai/DeepSeek-V4-Pro`, moved to the generic `openai-chat` client: SiliconFlow serves
18
+
Chat Completions only. The OpenRouter and official entries keep the `deepseek-v4` client.
19
+
- The DeepSeek cases in the empty-response, tool-call-argument and unknown-event unit suites
20
+
moved to the Responses wire shape, and those first two suites gained a case for the generic
21
+
`openai-responses` client alongside it.
22
+
23
+
-`README.md` gained a table of the wire protocol each `client_type` speaks: `google-genai`,
24
+
`ant-messages`, `openai-responses`, `openai-chat`, and OpenAI Embeddings.
25
+
26
+
## Configuration behavior
27
+
28
+
|`UniConfig` key | On the wire |
29
+
| --- | --- |
30
+
|`thinking_level`|`reasoning.effort`, pre-mapped to what DeepSeek settles on: `none` / `low` / `high` / `high` / `high` / `max`. Effort `none` turns thinking off; the Chat Completions `thinking` toggle is ignored on this endpoint |
31
+
|`thinking_summary`| left out: the endpoint accepts `summary` but never generates one |
32
+
|`max_tokens`|`max_output_tokens`|
33
+
|`system_prompt`|`instructions`|
34
+
|`temperature`|`UnsupportedParameterError` unless it is `1.0`|
35
+
|`tool_choice`|`auto` and `none` only; anything else raises `UnsupportedParameterError`|
36
+
|`fast_mode`|`UnsupportedParameterError`: `service_tier` is not supported |
37
+
|`prompt_caching`|`ENABLE` only; caching is automatic |
38
+
39
+
Usage arrives as `input_tokens` with `input_tokens_details.cached_tokens`, and `output_tokens`
0 commit comments