@deepstrike/sdk 0.2.52 → 0.2.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -28
- package/dist/agent-ir.d.ts +103 -0
- package/dist/agent-ir.js +134 -0
- package/dist/agent.d.ts +67 -0
- package/dist/agent.js +36 -0
- package/dist/collaboration/harness.js +1 -1
- package/dist/collaboration/modes/creator-verifier.d.ts +2 -7
- package/dist/collaboration/modes/creator-verifier.js +4 -6
- package/dist/collaboration/pool.d.ts +8 -20
- package/dist/collaboration/pool.js +27 -97
- package/dist/compat/anthropic/mcp.d.ts +15 -0
- package/dist/compat/anthropic/mcp.js +10 -0
- package/dist/compat/openai/agent.d.ts +34 -0
- package/dist/compat/openai/agent.js +24 -0
- package/dist/governance.d.ts +1 -17
- package/dist/governance.js +1 -34
- package/dist/guardrail.d.ts +6 -0
- package/dist/guardrail.js +1 -0
- package/dist/handoff-target.d.ts +12 -0
- package/dist/handoff-target.js +1 -0
- package/dist/harness/manifest.js +0 -4
- package/dist/index.d.ts +19 -7
- package/dist/index.js +8 -5
- package/dist/kernel.d.ts +2 -20
- package/dist/knowledge/public.d.ts +29 -0
- package/dist/knowledge/public.js +1 -0
- package/dist/mcp-server.d.ts +28 -0
- package/dist/mcp-server.js +1 -0
- package/dist/memory/agent.d.ts +2 -2
- package/dist/memory/agent.js +2 -2
- package/dist/memory/durable.d.ts +16 -0
- package/dist/memory/durable.js +46 -0
- package/dist/memory/in-memory-store.d.ts +9 -7
- package/dist/memory/in-memory-store.js +8 -2
- package/dist/memory/protocols.d.ts +22 -4
- package/dist/memory/public.d.ts +4 -3
- package/dist/memory/public.js +3 -2
- package/dist/os/public.d.ts +1 -1
- package/dist/os/public.js +1 -1
- package/dist/providers/anthropic-adapter.d.ts +59 -0
- package/dist/providers/anthropic-adapter.js +530 -0
- package/dist/providers/anthropic-compatible.d.ts +2 -3
- package/dist/providers/anthropic-compatible.js +8 -5
- package/dist/providers/anthropic.d.ts +20 -23
- package/dist/providers/anthropic.js +176 -395
- package/dist/providers/base.d.ts +2 -2
- package/dist/providers/base.js +50 -8
- package/dist/providers/capability-router.d.ts +29 -0
- package/dist/providers/capability-router.js +43 -0
- package/dist/providers/catalog.d.ts +16 -4
- package/dist/providers/catalog.js +112 -36
- package/dist/providers/content-normalization.d.ts +57 -0
- package/dist/providers/content-normalization.js +238 -0
- package/dist/providers/content-policy.d.ts +16 -0
- package/dist/providers/content-policy.js +39 -0
- package/dist/providers/credentials.d.ts +83 -0
- package/dist/providers/credentials.js +190 -0
- package/dist/providers/endpoints.d.ts +137 -0
- package/dist/providers/endpoints.js +128 -0
- package/dist/providers/factories.js +25 -9
- package/dist/providers/gemini-adapter.d.ts +33 -0
- package/dist/providers/gemini-adapter.js +264 -0
- package/dist/providers/gemini.d.ts +16 -3
- package/dist/providers/gemini.js +97 -195
- package/dist/providers/model-catalog.d.ts +37 -0
- package/dist/providers/model-catalog.js +62 -0
- package/dist/providers/model-registry.d.ts +119 -0
- package/dist/providers/model-registry.js +379 -0
- package/dist/providers/ollama-adapter.d.ts +65 -0
- package/dist/providers/ollama-adapter.js +188 -0
- package/dist/providers/ollama.d.ts +9 -4
- package/dist/providers/ollama.js +96 -109
- package/dist/providers/openai-chat-dialects.d.ts +154 -0
- package/dist/providers/openai-chat-dialects.js +179 -0
- package/dist/providers/openai-chat.d.ts +46 -18
- package/dist/providers/openai-chat.js +416 -51
- package/dist/providers/openai-responses-adapter.d.ts +42 -0
- package/dist/providers/openai-responses-adapter.js +343 -0
- package/dist/providers/openai-responses.d.ts +19 -33
- package/dist/providers/openai-responses.js +164 -264
- package/dist/providers/openai.d.ts +29 -76
- package/dist/providers/openai.js +195 -292
- package/dist/providers/protocol-adapter.d.ts +39 -0
- package/dist/providers/protocol-adapter.js +13 -0
- package/dist/providers/protocol-capabilities.d.ts +34 -0
- package/dist/providers/protocol-capabilities.js +44 -0
- package/dist/providers/provider-error.d.ts +31 -0
- package/dist/providers/provider-error.js +153 -0
- package/dist/providers/public.d.ts +26 -3
- package/dist/providers/public.js +13 -1
- package/dist/providers/registry.d.ts +7 -6
- package/dist/providers/registry.js +47 -20
- package/dist/providers/request-plan.d.ts +89 -0
- package/dist/providers/request-plan.js +199 -0
- package/dist/providers/usage-normalizer.d.ts +48 -0
- package/dist/providers/usage-normalizer.js +139 -0
- package/dist/providers/vendor-profiles.d.ts +2 -15
- package/dist/providers/vendor-profiles.js +14 -60
- package/dist/runtime/canonical-kernel-step.d.ts +1 -2
- package/dist/runtime/canonical-kernel-step.js +47 -12
- package/dist/runtime/context-policy.d.ts +10 -12
- package/dist/runtime/context-policy.js +6 -8
- package/dist/runtime/durable-content.d.ts +50 -0
- package/dist/runtime/durable-content.js +159 -0
- package/dist/runtime/execution-plane.d.ts +2 -2
- package/dist/runtime/execution-plane.js +2 -2
- package/dist/runtime/kernel-event-log.js +0 -1
- package/dist/runtime/kernel-step.d.ts +0 -1
- package/dist/runtime/kernel-step.js +4 -2
- package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
- package/dist/runtime/mcp-proxy-plane.js +44 -6
- package/dist/runtime/output-schema.d.ts +1 -2
- package/dist/runtime/provider-replay.d.ts +5 -1
- package/dist/runtime/provider-replay.js +26 -27
- package/dist/runtime/reactive-session.d.ts +1 -1
- package/dist/runtime/reactive-session.js +2 -3
- package/dist/runtime/run-group.d.ts +1 -1
- package/dist/runtime/runner.d.ts +31 -45
- package/dist/runtime/runner.js +178 -63
- package/dist/runtime/session-log.d.ts +8 -1
- package/dist/runtime/session-log.js +42 -2
- package/dist/runtime/session-repair.d.ts +1 -1
- package/dist/runtime/session-repair.js +1 -1
- package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
- package/dist/runtime/sub-agent-orchestrator.js +8 -11
- package/dist/runtime/workflow-control-flow.d.ts +0 -4
- package/dist/runtime/workflow-control-flow.js +0 -16
- package/dist/session.d.ts +11 -0
- package/dist/session.js +1 -0
- package/dist/skill.d.ts +17 -0
- package/dist/skill.js +16 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/tools/errors.d.ts +1 -3
- package/dist/tools/errors.js +1 -3
- package/dist/tools/index.d.ts +3 -0
- package/dist/types/agent.d.ts +21 -9
- package/dist/types/agent.js +30 -4
- package/dist/types.d.ts +135 -17
- package/package.json +4 -4
- package/dist/providers/deepseek.d.ts +0 -46
- package/dist/providers/deepseek.js +0 -97
- package/dist/providers/glm.d.ts +0 -25
- package/dist/providers/glm.js +0 -48
- package/dist/providers/kimi.d.ts +0 -23
- package/dist/providers/kimi.js +0 -30
- package/dist/providers/minimax.d.ts +0 -49
- package/dist/providers/minimax.js +0 -98
- package/dist/providers/profiles.d.ts +0 -1992
- package/dist/providers/profiles.js +0 -796
- package/dist/providers/qwen.d.ts +0 -38
- package/dist/providers/qwen.js +0 -97
package/dist/providers/openai.js
CHANGED
|
@@ -1,39 +1,12 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
+
import { assistantReplayKey } from "../runtime/provider-replay.js";
|
|
2
3
|
import { withServerRuntimeGuard } from "../runtime/server.js";
|
|
3
|
-
import { CircuitBreaker, omitExtensionKeys,
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"gpt-5.4-nano": { maxTurns: 15 },
|
|
10
|
-
"gpt-5.2": { maxTurns: 50 },
|
|
11
|
-
"gpt-5.2-pro": { maxTurns: 60 },
|
|
12
|
-
"gpt-5.1": { maxTurns: 50 },
|
|
13
|
-
"gpt-4o": { maxTurns: 25 },
|
|
14
|
-
"gpt-4o-mini": { maxTurns: 15 },
|
|
15
|
-
"gpt-4.1": { maxTurns: 35 },
|
|
16
|
-
"gpt-4.1-mini": { maxTurns: 20 },
|
|
17
|
-
"gpt-4.1-nano": { maxTurns: 15 },
|
|
18
|
-
"gpt-5": { maxTurns: 50 },
|
|
19
|
-
"gpt-5-pro": { maxTurns: 60 },
|
|
20
|
-
"gpt-5-mini": { maxTurns: 25 },
|
|
21
|
-
"gpt-5-nano": { maxTurns: 15 },
|
|
22
|
-
"o1": { maxTurns: 50 },
|
|
23
|
-
"o1-mini": { maxTurns: 25 },
|
|
24
|
-
"o3": { maxTurns: 50 },
|
|
25
|
-
"o3-mini": { maxTurns: 25 },
|
|
26
|
-
"o4-mini": { maxTurns: 25 },
|
|
27
|
-
};
|
|
28
|
-
/** Rebuild OpenAI-native `tool_calls` blocks from the streamed buffers — needed by reasoning
|
|
29
|
-
* vendors (DeepSeek/MiniMax) that persist the native blocks in their replay envelope. */
|
|
30
|
-
export function nativeToolCallsFromBuffers(toolCallBufs) {
|
|
31
|
-
return Object.values(toolCallBufs).map(tb => ({
|
|
32
|
-
id: tb.id,
|
|
33
|
-
type: "function",
|
|
34
|
-
function: { name: tb.name, arguments: tb.argsBuf || "{}" },
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
4
|
+
import { CircuitBreaker, omitExtensionKeys, stablePromptCacheKey } from "./base.js";
|
|
5
|
+
import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
|
|
6
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
7
|
+
import { OpenAIChatAdapter, } from "./openai-chat.js";
|
|
8
|
+
import { openAIChatDialects, } from "./openai-chat-dialects.js";
|
|
9
|
+
import { circuitOpenError, classifyProviderError } from "./provider-error.js";
|
|
37
10
|
export class OpenAIChatProvider {
|
|
38
11
|
client;
|
|
39
12
|
circuit;
|
|
@@ -41,302 +14,232 @@ export class OpenAIChatProvider {
|
|
|
41
14
|
baseDelay;
|
|
42
15
|
model;
|
|
43
16
|
chat = new OpenAIChatAdapter();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
17
|
+
dialect;
|
|
18
|
+
replayStore = new Map();
|
|
19
|
+
resolvedRuntimePolicy;
|
|
20
|
+
resolvedRuntime;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
const resolved = {
|
|
23
|
+
apiKey: options.apiKey,
|
|
24
|
+
model: options.model ?? "gpt-4o",
|
|
25
|
+
retry: options.retry ?? { maxRetries: 3, baseDelay: 1000 },
|
|
26
|
+
baseURL: options.baseURL ?? endpointProfiles["openai.chat"].baseURL,
|
|
27
|
+
runtimePolicy: options.runtimePolicy ?? {},
|
|
28
|
+
dialect: options.dialect ?? openAIChatDialects.openai,
|
|
29
|
+
authMode: options.authMode ?? "api_key",
|
|
30
|
+
};
|
|
31
|
+
this.model = resolved.model;
|
|
32
|
+
this.client = withServerRuntimeGuard(() => new OpenAI({
|
|
33
|
+
apiKey: resolved.apiKey,
|
|
34
|
+
baseURL: resolved.baseURL,
|
|
35
|
+
...(resolved.authMode === "bearer" ? { defaultHeaders: { Authorization: `Bearer ${resolved.apiKey}` } } : {}),
|
|
36
|
+
}));
|
|
52
37
|
this.circuit = new CircuitBreaker();
|
|
53
|
-
this.maxRetries =
|
|
54
|
-
this.baseDelay =
|
|
38
|
+
this.maxRetries = resolved.retry.maxRetries;
|
|
39
|
+
this.baseDelay = resolved.retry.baseDelay;
|
|
40
|
+
this.resolvedRuntimePolicy = resolved.runtimePolicy;
|
|
41
|
+
this.dialect = resolved.dialect;
|
|
55
42
|
}
|
|
56
43
|
runtimePolicy() {
|
|
57
|
-
return
|
|
44
|
+
return this.resolvedRuntimePolicy;
|
|
58
45
|
}
|
|
59
46
|
descriptor() {
|
|
60
47
|
return {
|
|
61
|
-
provider:
|
|
48
|
+
provider: this.dialect.providerId,
|
|
62
49
|
protocol: "openai-chat",
|
|
63
50
|
model: this.model,
|
|
64
|
-
reasoning:
|
|
65
|
-
|
|
66
|
-
preserveAcrossToolTurns: false,
|
|
67
|
-
},
|
|
68
|
-
toolCalls: {
|
|
69
|
-
supported: true,
|
|
70
|
-
requiresStrictPairing: true,
|
|
71
|
-
},
|
|
51
|
+
reasoning: this.dialect.descriptor.reasoning,
|
|
52
|
+
toolCalls: { supported: true, requiresStrictPairing: true },
|
|
72
53
|
};
|
|
73
54
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
buildChatMessages(context, extensions) {
|
|
81
|
-
return this.chat.buildMessages(context, {
|
|
82
|
-
descriptor: this.descriptor(),
|
|
83
|
-
requireNonEmptyReasoningForToolCalls: this.requireNonEmptyReasoningReplayForToolTurns(extensions),
|
|
84
|
-
degradeMissingReasoning: this.degradeMissingReasoningReplay(extensions),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
// ── Template-Method hooks ───────────────────────────────────────────────────
|
|
88
|
-
// Defaults reproduce the plain OpenAI-chat behavior; reasoning vendors
|
|
89
|
-
// (DeepSeek/MiniMax) override these instead of duplicating complete()/stream().
|
|
90
|
-
/** Pre-process caller extensions before they reach buildChatMessages + the wire request
|
|
91
|
-
* (e.g. set `__deepstrikeThinkingEnabled`). Default: pass through unchanged. */
|
|
92
|
-
prepareExtensions(extensions) {
|
|
93
|
-
return extensions;
|
|
94
|
-
}
|
|
95
|
-
/** Extra top-level request-body fields merged into the chat.completions call (vendor thinking
|
|
96
|
-
* knobs like `reasoning_effort`, `extra_body`, `reasoning_split`). Default: none. */
|
|
97
|
-
requestBodyExtras(_extensions) {
|
|
98
|
-
return {};
|
|
99
|
-
}
|
|
100
|
-
/** Vendor server tools (e.g. web search) injected into the `tools[]` array alongside the function
|
|
101
|
-
* tools, driven by caller `extensions`. These run server-side — the model invokes them and the
|
|
102
|
-
* results come back inline, with no client tool-loop round-trip. Default: none. Vendors that ship
|
|
103
|
-
* built-in tools (GLM web_search, …) override this and strip the consumed key in `prepareExtensions`
|
|
104
|
-
* so it does not also leak into the request body. */
|
|
105
|
-
serverTools(_extensions) {
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
108
|
-
/** Merge function tools + vendor server tools into the wire `tools[]` (undefined when empty). Server
|
|
109
|
-
* tools (e.g. web_search) are non-standard wire entries, so the array is cast to the SDK tool type. */
|
|
110
|
-
assembleTools(tools, extensions) {
|
|
111
|
-
const fnTools = tools.length ? this.chat.buildTools(tools) : [];
|
|
112
|
-
const all = [...fnTools, ...this.serverTools(extensions)];
|
|
113
|
-
return all.length ? all : undefined;
|
|
114
|
-
}
|
|
115
|
-
/** Request-body params controlling prompt caching. Default sends OpenAI's `prompt_cache_key`;
|
|
116
|
-
* vendors whose endpoints reject unknown params (e.g. DeepSeek 400s) override to `{}`. */
|
|
117
|
-
cacheKeyParams(context, tools) {
|
|
118
|
-
return { prompt_cache_key: this.promptCacheKey(context, tools) };
|
|
119
|
-
}
|
|
120
|
-
/** Whether streamed `content` may carry inline `<thinking>…</thinking>` tags to split out.
|
|
121
|
-
* Default true (OpenAI). Reasoning vendors emit reasoning out-of-band, so they return false. */
|
|
122
|
-
usesInlineThinkingTags() {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
/** Whether to surface streamed `reasoning_content` as thinking_delta events. Default true;
|
|
126
|
-
* vendors gate this behind an `exposeReasoning` extension. */
|
|
127
|
-
exposeReasoningDelta(_extensions) {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
/** Persist replay after a non-streaming turn. Default: nothing (plain OpenAI has no reasoning
|
|
131
|
-
* to replay). Reasoning vendors override to store their envelope. */
|
|
132
|
-
rememberCompleteReplay(_content, _toolCalls, _reasoning) {
|
|
133
|
-
/* no-op */
|
|
134
|
-
}
|
|
135
|
-
/** Persist replay after a streamed turn. Default: store `{ reasoning_content }` when there is a
|
|
136
|
-
* tool-call turn or captured reasoning (the prior base behavior). Vendors override. */
|
|
137
|
-
rememberStreamReplay(content, toolCalls, reasoning) {
|
|
138
|
-
if (toolCalls.length || reasoning.reasoningContent) {
|
|
139
|
-
this.chat.rememberReplayFields({ content, toolCalls }, { reasoning_content: reasoning.reasoningContent });
|
|
55
|
+
bindResolvedRuntime(resolved) {
|
|
56
|
+
if (resolved.identity.protocol !== "openai-chat"
|
|
57
|
+
|| resolved.identity.providerId !== this.dialect.providerId
|
|
58
|
+
|| resolved.identity.modelId !== this.model) {
|
|
59
|
+
throw new Error("OpenAIChatProvider received a mismatched resolved runtime");
|
|
140
60
|
}
|
|
61
|
+
this.resolvedRuntime = resolved;
|
|
141
62
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Pre-flight query: would this history validate against this provider with the
|
|
144
|
-
* given extensions, without sending the request? Lets an embedder route around
|
|
145
|
-
* a reasoning-replay failure (keep thinking on, disable it, or skip this
|
|
146
|
-
* candidate) before issuing the request. `ok: true` when this provider does
|
|
147
|
-
* not require reasoning replay for the current extensions.
|
|
148
|
-
*/
|
|
149
63
|
assessReplayability(context, extensions) {
|
|
150
|
-
|
|
64
|
+
const prepared = this.dialect.prepareExtensions(extensions ?? {});
|
|
65
|
+
if (!this.dialect.requireReasoningReplay(prepared)) {
|
|
151
66
|
return { ok: true, offendingCallIds: [] };
|
|
152
67
|
}
|
|
153
|
-
|
|
68
|
+
const offendingCallIds = context.turns.flatMap(message => {
|
|
69
|
+
if (message.role !== "assistant" || !message.toolCalls?.length)
|
|
70
|
+
return [];
|
|
71
|
+
const replay = this.peekProviderReplay(message);
|
|
72
|
+
return typeof replay?.reasoning_content === "string" && replay.reasoning_content.trim()
|
|
73
|
+
? []
|
|
74
|
+
: message.toolCalls.map(call => call.id);
|
|
75
|
+
});
|
|
76
|
+
return { ok: offendingCallIds.length === 0, offendingCallIds };
|
|
154
77
|
}
|
|
155
78
|
peekProviderReplay(message) {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
79
|
+
const replay = this.replayStore.get(assistantReplayKey(message));
|
|
80
|
+
if (!replay || !("reasoning_content" in replay || "reasoning_details" in replay))
|
|
158
81
|
return undefined;
|
|
159
|
-
|
|
82
|
+
if (this.dialect.id === "qwen" && replay.reasoning_content !== undefined) {
|
|
83
|
+
return { protocol: "openai-chat", reasoning_content: String(replay.reasoning_content ?? "") };
|
|
84
|
+
}
|
|
85
|
+
return replay;
|
|
160
86
|
}
|
|
161
87
|
seedProviderReplay(message, replay) {
|
|
162
|
-
if (replay.
|
|
163
|
-
|
|
164
|
-
|
|
88
|
+
if (replay.protocol !== "openai-chat"
|
|
89
|
+
|| (replay.reasoning_content === undefined && replay.reasoning_details === undefined))
|
|
90
|
+
return;
|
|
91
|
+
this.replayStore.set(assistantReplayKey(message), this.dialect.id === "qwen"
|
|
92
|
+
? { protocol: "openai-chat", reasoning_content: replay.reasoning_content }
|
|
93
|
+
: replay);
|
|
94
|
+
}
|
|
95
|
+
rememberReplay(message, replay) {
|
|
96
|
+
if (replay)
|
|
97
|
+
this.replayStore.set(assistantReplayKey(message), replay);
|
|
98
|
+
}
|
|
99
|
+
adapterInput(context, tools, extensions) {
|
|
100
|
+
const endpoint = endpointProfiles[this.dialect.endpointId];
|
|
101
|
+
const resolved = this.resolvedRuntime ?? {
|
|
102
|
+
identity: {
|
|
103
|
+
providerId: this.dialect.providerId,
|
|
104
|
+
modelId: this.model,
|
|
105
|
+
endpointId: this.dialect.endpointId,
|
|
106
|
+
protocol: "openai-chat",
|
|
107
|
+
},
|
|
108
|
+
model: {
|
|
109
|
+
id: `${this.dialect.providerId}/${this.model}`,
|
|
110
|
+
providerId: this.dialect.providerId,
|
|
111
|
+
kind: "generation",
|
|
112
|
+
intrinsic: {},
|
|
113
|
+
},
|
|
114
|
+
endpoint,
|
|
115
|
+
adapter: this,
|
|
116
|
+
effectiveCapabilities: compatibilityCapabilities(),
|
|
117
|
+
};
|
|
118
|
+
return normalizeCanonicalAdapterInput({
|
|
119
|
+
context,
|
|
120
|
+
tools,
|
|
121
|
+
resolved,
|
|
122
|
+
extensions,
|
|
123
|
+
replayForMessage: message => this.peekProviderReplay(message),
|
|
124
|
+
});
|
|
165
125
|
}
|
|
166
126
|
async complete(context, tools, extensions) {
|
|
167
|
-
const
|
|
127
|
+
const provider = this.dialect.providerId;
|
|
168
128
|
if (this.circuit.isOpen())
|
|
169
|
-
throw
|
|
170
|
-
|
|
171
|
-
let
|
|
172
|
-
|
|
129
|
+
throw circuitOpenError(provider);
|
|
130
|
+
let input;
|
|
131
|
+
let plan;
|
|
132
|
+
try {
|
|
133
|
+
input = this.adapterInput(context, tools, extensions);
|
|
134
|
+
plan = this.chat.buildRequest(input, this.dialect);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
throw classifyProviderError(provider, error);
|
|
138
|
+
}
|
|
139
|
+
let lastError;
|
|
140
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
173
141
|
try {
|
|
174
|
-
const
|
|
175
|
-
...this.cacheKeyParams(context, tools),
|
|
176
|
-
...this.requestExtensions(prepared),
|
|
177
|
-
...this.requestBodyExtras(extensions),
|
|
178
|
-
model: this.model,
|
|
179
|
-
messages: msgs,
|
|
180
|
-
...((t => t ? { tools: t } : {})(this.assembleTools(tools, extensions))),
|
|
181
|
-
});
|
|
142
|
+
const response = await this.client.chat.completions.create(plan.params);
|
|
182
143
|
this.circuit.recordSuccess();
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const content = choice.content ?? "";
|
|
187
|
-
this.rememberCompleteReplay(content, toolCalls, {
|
|
188
|
-
reasoningContent: typeof choice.reasoning_content === "string" ? choice.reasoning_content : "",
|
|
189
|
-
reasoningDetails: choice.reasoning_details,
|
|
190
|
-
nativeToolCalls: nativeToolCalls,
|
|
191
|
-
});
|
|
192
|
-
return { role: "assistant", content, tokenCount: resp.usage?.completion_tokens ?? resp.usage?.total_tokens, toolCalls };
|
|
144
|
+
const decoded = this.chat.decodeComplete(response, { input }, this.dialect);
|
|
145
|
+
this.rememberReplay(decoded.message, decoded.replay);
|
|
146
|
+
return decoded.message;
|
|
193
147
|
}
|
|
194
|
-
catch (
|
|
195
|
-
|
|
148
|
+
catch (error) {
|
|
149
|
+
lastError = error;
|
|
196
150
|
this.circuit.recordFailure();
|
|
197
|
-
if (
|
|
198
|
-
await new Promise(
|
|
151
|
+
if (attempt < this.maxRetries - 1) {
|
|
152
|
+
await new Promise(resolve => setTimeout(resolve, this.baseDelay * 2 ** attempt));
|
|
153
|
+
}
|
|
199
154
|
}
|
|
200
155
|
}
|
|
201
|
-
throw
|
|
156
|
+
throw classifyProviderError(provider, lastError);
|
|
202
157
|
}
|
|
203
158
|
async *stream(context, tools, extensions, _state, signal) {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const rememberStream = () => {
|
|
226
|
-
const toolCalls = Object.values(toolCallBufs).map(tb => ({ id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}" }));
|
|
227
|
-
this.rememberStreamReplay(accumulatedContent, toolCalls, {
|
|
228
|
-
reasoningContent: accumulatedReasoning,
|
|
229
|
-
reasoningDetails: accumulatedReasoningDetails,
|
|
230
|
-
nativeToolCalls: nativeToolCallsFromBuffers(toolCallBufs),
|
|
231
|
-
});
|
|
232
|
-
};
|
|
233
|
-
const emitPendingToolCalls = function* () {
|
|
234
|
-
for (const [index, tb] of Object.entries(toolCallBufs)) {
|
|
235
|
-
const idx = Number(index);
|
|
236
|
-
if (emittedToolCallIndexes.has(idx))
|
|
237
|
-
continue;
|
|
238
|
-
let args = {};
|
|
239
|
-
try {
|
|
240
|
-
args = JSON.parse(tb.argsBuf || "{}");
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
args = {};
|
|
244
|
-
}
|
|
245
|
-
emittedToolCallIndexes.add(idx);
|
|
246
|
-
yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
let totalTokens = 0;
|
|
250
|
-
let inputTokens = 0;
|
|
251
|
-
let outputTokens = 0;
|
|
252
|
-
let cacheReadTokens = 0;
|
|
253
|
-
// Phase 4: OpenAI signals an output-cap truncation via finish_reason="length", which arrives on
|
|
254
|
-
// a `choices` frame separate from the trailing `usage` frame — so capture it and attach it to the
|
|
255
|
-
// usage event the runner reads. The kernel treats "length" as a truncation (== Anthropic
|
|
256
|
-
// "max_tokens"); other reasons ("stop"/"tool_calls") pass through harmlessly.
|
|
257
|
-
let finishReason;
|
|
258
|
-
for await (const chunk of stream) {
|
|
259
|
-
if (chunk.usage) {
|
|
260
|
-
totalTokens = chunk.usage.total_tokens;
|
|
261
|
-
inputTokens = chunk.usage.prompt_tokens ?? 0;
|
|
262
|
-
outputTokens = chunk.usage.completion_tokens ?? 0;
|
|
263
|
-
cacheReadTokens = openAICachedPromptTokens(chunk.usage);
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
const choice = chunk.choices[0];
|
|
267
|
-
if (!choice)
|
|
268
|
-
continue;
|
|
269
|
-
if (choice.finish_reason)
|
|
270
|
-
finishReason = choice.finish_reason;
|
|
271
|
-
const delta = choice.delta;
|
|
272
|
-
if (!delta)
|
|
273
|
-
continue;
|
|
274
|
-
if (delta.reasoning_content) {
|
|
275
|
-
accumulatedReasoning += String(delta.reasoning_content);
|
|
276
|
-
if (exposeReasoning)
|
|
277
|
-
yield { type: "thinking_delta", delta: String(delta.reasoning_content) };
|
|
278
|
-
}
|
|
279
|
-
if (delta.reasoning_details !== undefined && delta.reasoning_details !== null)
|
|
280
|
-
accumulatedReasoningDetails = delta.reasoning_details;
|
|
281
|
-
if (delta.content) {
|
|
282
|
-
if (useTags) {
|
|
283
|
-
for (const part of extractor.feed(String(delta.content))) {
|
|
284
|
-
if (part.type === "thinking") {
|
|
285
|
-
accumulatedReasoning += part.content;
|
|
286
|
-
yield { type: "thinking_delta", delta: part.content };
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
accumulatedContent += part.content;
|
|
290
|
-
yield { type: "text_delta", delta: part.content };
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
accumulatedContent += String(delta.content);
|
|
296
|
-
yield { type: "text_delta", delta: delta.content };
|
|
159
|
+
const provider = this.dialect.providerId;
|
|
160
|
+
try {
|
|
161
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
162
|
+
const plan = this.chat.buildRequest(input, this.dialect);
|
|
163
|
+
const state = this.chat.createStreamState({ input }, this.dialect);
|
|
164
|
+
const stream = await this.client.chat.completions.create({
|
|
165
|
+
...plan.params,
|
|
166
|
+
stream: true,
|
|
167
|
+
stream_options: { include_usage: true },
|
|
168
|
+
}, signal ? { signal } : undefined);
|
|
169
|
+
for await (const chunk of stream) {
|
|
170
|
+
const output = this.chat.pushStreamChunk(chunk, state);
|
|
171
|
+
if (output.replay) {
|
|
172
|
+
this.rememberReplay({
|
|
173
|
+
content: state.accumulatedContent,
|
|
174
|
+
toolCalls: Object.values(state.toolCallBuffers).map(call => ({
|
|
175
|
+
id: call.id,
|
|
176
|
+
name: call.name,
|
|
177
|
+
arguments: call.argsBuffer || "{}",
|
|
178
|
+
})),
|
|
179
|
+
}, output.replay);
|
|
297
180
|
}
|
|
181
|
+
for (const event of output.events)
|
|
182
|
+
yield event;
|
|
298
183
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
184
|
+
const final = this.chat.finishStream(state);
|
|
185
|
+
for (const event of final.events)
|
|
186
|
+
yield event;
|
|
187
|
+
this.rememberReplay({
|
|
188
|
+
content: state.accumulatedContent,
|
|
189
|
+
toolCalls: Object.values(state.toolCallBuffers).map(call => ({
|
|
190
|
+
id: call.id,
|
|
191
|
+
name: call.name,
|
|
192
|
+
arguments: call.argsBuffer || "{}",
|
|
193
|
+
})),
|
|
194
|
+
}, final.replay);
|
|
311
195
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (part.type === "thinking") {
|
|
315
|
-
accumulatedReasoning += part.content;
|
|
316
|
-
yield { type: "thinking_delta", delta: part.content };
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
accumulatedContent += part.content;
|
|
320
|
-
yield { type: "text_delta", delta: part.content };
|
|
321
|
-
}
|
|
322
|
-
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
throw classifyProviderError(provider, error);
|
|
323
198
|
}
|
|
324
|
-
rememberStream();
|
|
325
|
-
yield* emitPendingToolCalls();
|
|
326
|
-
if (totalTokens > 0)
|
|
327
|
-
yield { type: "usage", totalTokens, inputTokens, outputTokens, ...(cacheReadTokens > 0 ? { cacheReadInputTokens: cacheReadTokens } : {}), ...(finishReason ? { stopReason: finishReason } : {}) };
|
|
328
199
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
200
|
+
// White-box test seams. Runtime request shaping uses the dialect through the adapter.
|
|
201
|
+
prepareExtensions(extensions) {
|
|
202
|
+
return this.dialect.prepareExtensions(extensions ?? {});
|
|
203
|
+
}
|
|
204
|
+
requestBodyExtras(extensions) {
|
|
205
|
+
const prepared = this.dialect.prepareExtensions(extensions ?? {});
|
|
206
|
+
return Object.fromEntries(Object.entries(prepared).filter(([key]) => key === "extra_body" || key === "reasoning_effort" || key === "reasoning_split"));
|
|
207
|
+
}
|
|
208
|
+
serverTools(extensions) {
|
|
209
|
+
return this.dialect.serverTools?.(extensions ?? {}) ?? [];
|
|
337
210
|
}
|
|
338
211
|
requestExtensions(extensions) {
|
|
339
|
-
|
|
212
|
+
const prepared = this.dialect.prepareExtensions(extensions ?? {});
|
|
213
|
+
return omitExtensionKeys(prepared, [
|
|
214
|
+
"model", "messages", "tools", "stream", "stream_options", "extra_body",
|
|
215
|
+
"reasoning_effort", "reasoning_split", "__deepstrikeThinkingEnabled",
|
|
216
|
+
]);
|
|
340
217
|
}
|
|
218
|
+
promptCacheKey(context, tools) {
|
|
219
|
+
return stablePromptCacheKey([context.systemText, tools.map(tool => tool.name).join(",")]);
|
|
220
|
+
}
|
|
221
|
+
rememberCompleteReplay(_content, _toolCalls, _reasoning) { }
|
|
222
|
+
rememberStreamReplay(_content, _toolCalls, _reasoning) { }
|
|
223
|
+
}
|
|
224
|
+
function compatibilityCapabilities() {
|
|
225
|
+
const unknown = { state: "unknown", evidence: [] };
|
|
226
|
+
const unsupported = { state: "unsupported", evidence: ["protocol"] };
|
|
227
|
+
return {
|
|
228
|
+
inputModalities: { text: unknown, image: unknown, audio: unknown, video: unsupported, file: unsupported },
|
|
229
|
+
outputModalities: { text: unknown, image: unsupported, audio: unsupported, embedding: unsupported },
|
|
230
|
+
tools: unknown,
|
|
231
|
+
reasoning: unknown,
|
|
232
|
+
parallelToolCalls: unknown,
|
|
233
|
+
structuredOutput: unknown,
|
|
234
|
+
promptCaching: unknown,
|
|
235
|
+
nativeTokenCounting: unknown,
|
|
236
|
+
mediaForms: {
|
|
237
|
+
imageUrl: unknown,
|
|
238
|
+
imageBase64: unknown,
|
|
239
|
+
fileId: unsupported,
|
|
240
|
+
audioUrl: unsupported,
|
|
241
|
+
audioBase64: unknown,
|
|
242
|
+
},
|
|
243
|
+
};
|
|
341
244
|
}
|
|
342
245
|
export { OpenAIChatProvider as OpenAIProvider };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Message, ProviderReplay, ProviderRunState, ProviderUsage, StreamEvent } from "../types.js";
|
|
2
|
+
import type { CanonicalAdapterInput } from "./content-normalization.js";
|
|
3
|
+
import type { GenerationProtocol, ProtocolRuntimeCapabilities } from "./protocol-capabilities.js";
|
|
4
|
+
export { GEMINI_PROTOCOL_CAPABILITIES, OLLAMA_PROTOCOL_CAPABILITIES, } from "./protocol-capabilities.js";
|
|
5
|
+
export type CanonicalStopReason = "end_turn" | "tool_use" | "max_tokens" | "stop_sequence" | "content_filter" | "other";
|
|
6
|
+
export interface AdapterOutput {
|
|
7
|
+
events: StreamEvent[];
|
|
8
|
+
replay?: ProviderReplay;
|
|
9
|
+
runStatePatch?: Partial<ProviderRunState>;
|
|
10
|
+
}
|
|
11
|
+
export interface AdapterDecodeInput {
|
|
12
|
+
input: CanonicalAdapterInput;
|
|
13
|
+
}
|
|
14
|
+
export interface AdapterStreamInput {
|
|
15
|
+
input: CanonicalAdapterInput;
|
|
16
|
+
}
|
|
17
|
+
export interface ProtocolAdapter<TRequest, TCompleteResponse, TStreamChunk, TStreamState, TStreamFinal = undefined> {
|
|
18
|
+
readonly protocol: GenerationProtocol;
|
|
19
|
+
readonly protocolCapabilities: ProtocolRuntimeCapabilities;
|
|
20
|
+
buildRequest(input: CanonicalAdapterInput): TRequest;
|
|
21
|
+
decodeComplete(raw: TCompleteResponse, input: AdapterDecodeInput): {
|
|
22
|
+
message: Message;
|
|
23
|
+
replay?: ProviderReplay;
|
|
24
|
+
};
|
|
25
|
+
createStreamState(input: AdapterStreamInput): TStreamState;
|
|
26
|
+
pushStreamChunk(chunk: TStreamChunk, state: TStreamState): AdapterOutput;
|
|
27
|
+
finishStream(state: TStreamState, final: TStreamFinal): AdapterOutput | Promise<AdapterOutput>;
|
|
28
|
+
normalizeUsage(raw: unknown): ProviderUsage | undefined;
|
|
29
|
+
normalizeStopReason(raw: string | undefined): CanonicalStopReason | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare class ProtocolResponseError extends Error {
|
|
32
|
+
readonly protocol: GenerationProtocol;
|
|
33
|
+
readonly providerCode?: string;
|
|
34
|
+
readonly retryable?: boolean;
|
|
35
|
+
constructor(protocol: GenerationProtocol, message: string, options?: {
|
|
36
|
+
providerCode?: string;
|
|
37
|
+
retryable?: boolean;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { GEMINI_PROTOCOL_CAPABILITIES, OLLAMA_PROTOCOL_CAPABILITIES, } from "./protocol-capabilities.js";
|
|
2
|
+
export class ProtocolResponseError extends Error {
|
|
3
|
+
protocol;
|
|
4
|
+
providerCode;
|
|
5
|
+
retryable;
|
|
6
|
+
constructor(protocol, message, options = {}) {
|
|
7
|
+
super(options.providerCode ? message : `${protocol} protocol response error: ${message}`);
|
|
8
|
+
this.name = "ProtocolResponseError";
|
|
9
|
+
this.protocol = protocol;
|
|
10
|
+
this.providerCode = options.providerCode;
|
|
11
|
+
this.retryable = options.retryable;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type InputModality = "text" | "image" | "audio" | "video" | "file";
|
|
2
|
+
export type OutputModality = "text" | "image" | "audio" | "embedding";
|
|
3
|
+
export type GenerationProtocol = "anthropic-messages" | "openai-chat" | "openai-responses" | "gemini" | "ollama-chat";
|
|
4
|
+
export interface ProtocolRuntimeCapabilities {
|
|
5
|
+
acceptedInputModalities: readonly InputModality[];
|
|
6
|
+
emittedOutputModalities: readonly OutputModality[];
|
|
7
|
+
tools: boolean;
|
|
8
|
+
parallelToolCalls?: boolean;
|
|
9
|
+
structuredOutput?: boolean;
|
|
10
|
+
reasoningReplay: "none" | "optional" | "required";
|
|
11
|
+
promptCaching?: boolean;
|
|
12
|
+
mediaForms: {
|
|
13
|
+
imageUrl?: boolean;
|
|
14
|
+
imageBase64?: boolean;
|
|
15
|
+
fileId?: boolean;
|
|
16
|
+
audioUrl?: boolean;
|
|
17
|
+
audioBase64?: boolean;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface ProtocolRuntimeCapabilityOverrides {
|
|
21
|
+
acceptedInputModalities?: readonly InputModality[];
|
|
22
|
+
emittedOutputModalities?: readonly OutputModality[];
|
|
23
|
+
tools?: boolean;
|
|
24
|
+
parallelToolCalls?: boolean;
|
|
25
|
+
structuredOutput?: boolean;
|
|
26
|
+
reasoningReplay?: ProtocolRuntimeCapabilities["reasoningReplay"];
|
|
27
|
+
promptCaching?: boolean;
|
|
28
|
+
mediaForms?: Partial<ProtocolRuntimeCapabilities["mediaForms"]>;
|
|
29
|
+
}
|
|
30
|
+
export declare const ANTHROPIC_PROTOCOL_CAPABILITIES: ProtocolRuntimeCapabilities;
|
|
31
|
+
export declare const GEMINI_PROTOCOL_CAPABILITIES: ProtocolRuntimeCapabilities;
|
|
32
|
+
export declare const OLLAMA_PROTOCOL_CAPABILITIES: ProtocolRuntimeCapabilities;
|
|
33
|
+
export declare const OPENAI_RESPONSES_PROTOCOL_CAPABILITIES: ProtocolRuntimeCapabilities;
|
|
34
|
+
export declare const OPENAI_CHAT_PROTOCOL_CAPABILITIES: ProtocolRuntimeCapabilities;
|