Merge remote-tracking branch 'origin/master' into worktree/provider-routed-llm-adapters
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl # examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl # examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl # examples/acp-agent/tests/snapshots/skill-load/session.jsonl # examples/acp-agent/tests/snapshots/text-turn/session.jsonl # examples/sandbox-acp-agent/cordis.yml # examples/sandbox-acp-agent/tests/snapshots/escalation-approved/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/escalation-rejected/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/mode-switching/session.jsonl # packages/compact/compact-basic/README.md # packages/compact/compact-basic/src/index.ts # packages/compact/compact-basic/tests/compact-basic.spec.ts # packages/core/agent-loop/README.md # packages/core/agent-loop/src/loop.ts # packages/core/agent-loop/tests/properties.spec.ts # packages/core/session/README.md # packages/core/session/src/types.ts # packages/core/session/tests/derived-cache.spec.ts # packages/llm/llm-deepseek/src/index.ts # packages/llm/llm-pi-ai/README.md # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm-pi-ai/src/convert.ts # packages/llm/llm-pi-ai/tests/adapter.spec.ts # packages/llm/llm/README.md # packages/llm/llm/src/call-config.ts # packages/llm/llm/src/index.ts # packages/ui/acp-agent/src/index.ts # packages/ui/acp/tests/harness.ts # packages/ui/jsonrpc/README.md # packages/ui/jsonrpc/src/server.ts # packages/ui/stdio-agent/README.md # packages/ui/stdio-agent/src/index.ts # python/sdk/README.i18n.yaml
This commit is contained in:
@@ -52,14 +52,8 @@ export class DeepSeekAdapter extends LlmAdapter {
|
||||
async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
const body = serializeRequest(options, this.options.defaults ?? {})
|
||||
|
||||
// TODO(http): deliberately raw `fetch` for the hand-rolled SSE body.
|
||||
// `@cordisjs/plugin-http` (ctx.http) would give proxy/intercept/timeout
|
||||
// uniformity AND can stream (`responseType: 'stream'` yields the same
|
||||
// ReadableStream<Uint8Array> parseSse consumes), but adopting it today
|
||||
// costs a hard `undici` dependency (it does `require('undici')` with no
|
||||
// globalThis.fetch fallback) plus an unconditional `@cordisjs/fetch-file`
|
||||
// import (pulling file-type + mime-types) for a file:// path we never hit.
|
||||
// Revisit when a second adapter wants shared proxy/intercept config.
|
||||
// TODO(http): adopt the Cordis HTTP service when shared transport configuration
|
||||
// outweighs its additional runtime dependencies.
|
||||
const response = await fetch(`${this.options.baseURL}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -79,15 +73,8 @@ export class DeepSeekAdapter extends LlmAdapter {
|
||||
const parsed = await response.json() as WireError
|
||||
if (parsed.error?.message) message = parsed.error.message
|
||||
} catch {
|
||||
// Paranoid by design: `code` and the HTTP status are ALREADY captured
|
||||
// above (and passed to LlmError below), so the only thing this `try`
|
||||
// can add is a richer provider-supplied message. A malformed, empty,
|
||||
// or non-JSON error body is a normal thing for gateways/proxies to
|
||||
// return on a 5xx/429 — swallowing the parse failure keeps the usable
|
||||
// status-line message instead of letting a JSON.parse throw mask the
|
||||
// real HTTP error. Nothing else reaches this catch: response.json()
|
||||
// is the sole statement, and any non-parse failure (e.g. body already
|
||||
// consumed) is equally non-actionable here.
|
||||
// Only swallow error-body parsing: status and code are already captured,
|
||||
// so malformed gateway JSON must not mask the actionable HTTP failure.
|
||||
}
|
||||
throw new LlmError(message, code, response.status)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
/**
|
||||
* DeepSeek LLM adapter plugin: registers a {@link DeepSeekAdapter} for the
|
||||
* `deepseek` provider route on `ctx.llm`.
|
||||
*
|
||||
* Config is cordis-native (schemastery). Secrets flow per the repo policy:
|
||||
* `apiKey` from cordis.yml via the `!!js` tag (`!!js process.env.DEEPSEEK_API_KEY`)
|
||||
* or from the environment directly; never from ad-hoc files.
|
||||
*
|
||||
* ```yaml
|
||||
* - id: llm-deepseek
|
||||
* name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
* config:
|
||||
* apiKey: !!js process.env.DEEPSEEK_API_KEY
|
||||
* baseURL: !!js process.env.DEEPSEEK_BASE_URL
|
||||
* ```
|
||||
*
|
||||
* Register a {@link DeepSeekAdapter} for the `deepseek` provider route on `ctx.llm`. Configuration uses
|
||||
* Cordis schemastery; pass secrets from environment variables through `cordis.yml` with `!!js`,
|
||||
* as shown in the package README, rather than reading ad hoc files.
|
||||
* @module @deepseek-ai/dsh-llm-deepseek
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
/**
|
||||
* Serialize harness vocabulary (`GenerateOptions`, `Message[]`) into the
|
||||
* DeepSeek chat-completions request body.
|
||||
*
|
||||
* Block-type mapping (core types handled explicitly; merge-extensible unions
|
||||
* mean plugin-added block types exist — they are skipped, never errors):
|
||||
*
|
||||
* - user `text` → string content (joined)
|
||||
* - assistant `text` → `content`; `reasoning` → `reasoning_content`, but
|
||||
* ONLY on assistant messages that carry tool calls (the official passback
|
||||
* rule for thinking mode — required there, ignored elsewhere, so we save
|
||||
* the tokens elsewhere); `tool-call` → `tool_calls[]`
|
||||
* - `tool-result` → its own `{role: 'tool'}` message (text flattened)
|
||||
*
|
||||
* Serialize harness messages into DeepSeek chat completions. User text is joined; assistant text
|
||||
* becomes `content`, tool calls become `tool_calls`, and tool results become separate tool messages.
|
||||
* Assistant reasoning is replayed as `reasoning_content` only on tool-call turns, as required by
|
||||
* thinking-mode passback. Unknown declaration-merged block types are skipped rather than rejected.
|
||||
* @module dsh-llm-deepseek/serialize
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
/**
|
||||
* Decode an SSE byte stream into event `data` payloads. Network reads may split UTF-8 or lines;
|
||||
* CRLF, comments, non-data fields, and multi-data events are handled per SSE rules. The literal
|
||||
* `[DONE]` is yielded so the caller owns final flushing, and EOF before it raises {@link LlmError}.
|
||||
*
|
||||
* Minimal SSE (text/event-stream) parser for the chat-completions stream.
|
||||
*
|
||||
* Yields each event's `data:` payload as a string, ending with the literal
|
||||
* `'[DONE]'` sentinel so the consumer owns end-of-stream flushing. A stream
|
||||
* that closes WITHOUT `[DONE]` is a protocol violation → `LlmError`.
|
||||
*
|
||||
* Handles the wire realities: payloads split across network reads at
|
||||
* arbitrary byte positions (including mid-UTF-8), CRLF line endings,
|
||||
* multi-`data:` events (joined with newlines per the SSE spec), comment
|
||||
* lines, and non-data fields (ignored).
|
||||
*
|
||||
* @module dsh-llm-deepseek/sse
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
/**
|
||||
* Translate DeepSeek SSE payloads with one stateful harness block per content, reasoning, or tool
|
||||
* call index. An empty initial reasoning delta does not open a block. Finish reason and the latest
|
||||
* usage are deferred until `[DONE]`, covering both finish-attached and trailing usage-only shapes
|
||||
* while ensuring no chunk follows `finish`.
|
||||
*
|
||||
* Translate DeepSeek wire chunks into the harness `StreamChunk` protocol.
|
||||
*
|
||||
* A small state machine over the SSE payload stream:
|
||||
* - `delta.content` / `delta.reasoning_content` / `delta.tool_calls[i]` each
|
||||
* own one harness block (index allocated on first sight). The first
|
||||
* thinking-mode chunk carries `reasoning_content: ""` — that must NOT open
|
||||
* a reasoning block.
|
||||
* - `finish_reason` and `usage` are DEFERRED: emitted only at the `[DONE]`
|
||||
* sentinel, so the wire's two usage shapes (attached to the finish chunk,
|
||||
* or a trailing usage-only chunk) both work and nothing ever follows
|
||||
* `finish`. Last usage wins.
|
||||
*
|
||||
* @module dsh-llm-deepseek/translate
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user