@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
|
@@ -1,137 +1,19 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { withServerRuntimeGuard } from "../runtime/server.js";
|
|
3
|
-
import { CircuitBreaker
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
buildInput(context, state) {
|
|
19
|
-
const input = [];
|
|
20
|
-
const turns = context.turns;
|
|
21
|
-
const uncoveredMessages = state?.previousResponseId
|
|
22
|
-
? turns.slice(state.coveredMessageCount)
|
|
23
|
-
: turns;
|
|
24
|
-
for (const message of uncoveredMessages) {
|
|
25
|
-
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
26
|
-
if (message.content || message.contentParts?.length) {
|
|
27
|
-
input.push({
|
|
28
|
-
role: "assistant",
|
|
29
|
-
content: this.buildMessageContent(message),
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
for (const tc of message.toolCalls) {
|
|
33
|
-
input.push({
|
|
34
|
-
type: "function_call",
|
|
35
|
-
call_id: tc.id,
|
|
36
|
-
name: tc.name,
|
|
37
|
-
arguments: tc.arguments,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (message.role === "tool") {
|
|
43
|
-
for (const part of message.contentParts ?? []) {
|
|
44
|
-
if (part.type !== "tool_result")
|
|
45
|
-
continue;
|
|
46
|
-
input.push({
|
|
47
|
-
type: "function_call_output",
|
|
48
|
-
call_id: part.callId,
|
|
49
|
-
output: part.output,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
input.push({
|
|
55
|
-
role: message.role,
|
|
56
|
-
content: this.buildMessageContent(message),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
// The volatile State turn is sent every turn (it changes each call and is
|
|
60
|
-
// never "covered" by previous_response_id). Absent on un-rebuilt bindings,
|
|
61
|
-
// where the state is already inside the covered/uncovered history.
|
|
62
|
-
// Rendered through the same assistant-toolCalls / tool-result branches above
|
|
63
|
-
// so tool_use blocks are not silently dropped.
|
|
64
|
-
if (context.stateTurn) {
|
|
65
|
-
const st = context.stateTurn;
|
|
66
|
-
if (st.role === "assistant" && st.toolCalls?.length) {
|
|
67
|
-
if (st.content || st.contentParts?.length) {
|
|
68
|
-
input.push({ role: "assistant", content: this.buildMessageContent(st) });
|
|
69
|
-
}
|
|
70
|
-
for (const tc of st.toolCalls) {
|
|
71
|
-
input.push({ type: "function_call", call_id: tc.id, name: tc.name, arguments: tc.arguments });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else if (st.role === "tool") {
|
|
75
|
-
for (const part of st.contentParts ?? []) {
|
|
76
|
-
if (part.type !== "tool_result")
|
|
77
|
-
continue;
|
|
78
|
-
input.push({ type: "function_call_output", call_id: part.callId, output: part.output });
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
input.push({ role: st.role, content: this.buildMessageContent(st) });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return input;
|
|
86
|
-
}
|
|
87
|
-
decodeOutput(output) {
|
|
88
|
-
let content = "";
|
|
89
|
-
const toolCalls = [];
|
|
90
|
-
for (const item of output) {
|
|
91
|
-
if (item.type === "message") {
|
|
92
|
-
for (const part of item.content ?? []) {
|
|
93
|
-
if (part.type === "output_text")
|
|
94
|
-
content += String(part.text ?? "");
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else if (item.type === "function_call") {
|
|
98
|
-
const toolCall = normalizeToolCall(String(item.call_id ?? item.id ?? ""), String(item.name ?? ""), item.arguments ?? "{}");
|
|
99
|
-
if (toolCall)
|
|
100
|
-
toolCalls.push(toolCall);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return { content, toolCalls };
|
|
104
|
-
}
|
|
105
|
-
buildMessageContent(message) {
|
|
106
|
-
if (!message.contentParts?.length)
|
|
107
|
-
return message.content;
|
|
108
|
-
const content = [];
|
|
109
|
-
for (const part of message.contentParts) {
|
|
110
|
-
if (part.type === "text") {
|
|
111
|
-
content.push({ type: "input_text", text: part.text });
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (part.type === "image") {
|
|
115
|
-
// Default the MIME type (like every other serializer) so a data-only image is
|
|
116
|
-
// not silently dropped; only a part with neither url nor data yields undefined.
|
|
117
|
-
const imageUrl = part.url ?? (part.data
|
|
118
|
-
? `data:${part.mediaType ?? "image/png"};base64,${part.data}`
|
|
119
|
-
: undefined);
|
|
120
|
-
if (imageUrl)
|
|
121
|
-
content.push({
|
|
122
|
-
type: "input_image",
|
|
123
|
-
detail: part.detail ?? "auto",
|
|
124
|
-
image_url: imageUrl,
|
|
125
|
-
});
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (part.type === "audio") {
|
|
129
|
-
throw new UnsupportedModalityError("audio", "openai-responses");
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return content;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
3
|
+
import { CircuitBreaker } from "./base.js";
|
|
4
|
+
import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
|
|
5
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
6
|
+
import { OpenAIResponsesAdapter, } from "./openai-responses-adapter.js";
|
|
7
|
+
import { circuitOpenError, classifyProviderError } from "./provider-error.js";
|
|
8
|
+
export { OpenAIResponsesAdapter } from "./openai-responses-adapter.js";
|
|
9
|
+
const OFFICIAL_OPENAI_RESPONSES_BASE_URL = "https://api.openai.com/v1";
|
|
10
|
+
/** Params the official input-token count endpoint accepts (SDK `InputTokenCountParams`). The
|
|
11
|
+
* create plan is projected onto this set — remaining keys (max_output_tokens, store, …) cannot
|
|
12
|
+
* change the input token count — rather than maintaining a second serialization. */
|
|
13
|
+
const INPUT_TOKEN_COUNT_PARAM_KEYS = [
|
|
14
|
+
"conversation", "input", "instructions", "model", "parallel_tool_calls",
|
|
15
|
+
"previous_response_id", "reasoning", "text", "tool_choice", "tools", "truncation",
|
|
16
|
+
];
|
|
135
17
|
export class OpenAIResponsesProvider {
|
|
136
18
|
model;
|
|
137
19
|
client;
|
|
@@ -139,167 +21,185 @@ export class OpenAIResponsesProvider {
|
|
|
139
21
|
maxRetries;
|
|
140
22
|
baseDelay;
|
|
141
23
|
responses = new OpenAIResponsesAdapter();
|
|
142
|
-
|
|
24
|
+
resolvedRuntimePolicy;
|
|
25
|
+
directNativeTokenCounting;
|
|
26
|
+
resolvedRuntime;
|
|
27
|
+
constructor(apiKey, model = "gpt-4.1", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = "https://api.openai.com/v1", runtimePolicy = {}, authMode = "api_key") {
|
|
143
28
|
this.model = model;
|
|
144
|
-
this.client = withServerRuntimeGuard(() => new OpenAI({
|
|
29
|
+
this.client = withServerRuntimeGuard(() => new OpenAI({
|
|
30
|
+
apiKey,
|
|
31
|
+
baseURL,
|
|
32
|
+
...(authMode === "bearer" ? { defaultHeaders: { Authorization: `Bearer ${apiKey}` } } : {}),
|
|
33
|
+
}));
|
|
145
34
|
this.circuit = new CircuitBreaker();
|
|
146
35
|
this.maxRetries = retry.maxRetries;
|
|
147
36
|
this.baseDelay = retry.baseDelay;
|
|
37
|
+
this.resolvedRuntimePolicy = runtimePolicy;
|
|
38
|
+
this.directNativeTokenCounting = baseURL.replace(/\/+$/, "") === OFFICIAL_OPENAI_RESPONSES_BASE_URL;
|
|
148
39
|
}
|
|
149
40
|
runtimePolicy() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"gpt-4.1-mini": { maxTurns: 20 },
|
|
160
|
-
"gpt-4.1-nano": { maxTurns: 15 },
|
|
161
|
-
"gpt-5": { maxTurns: 50 },
|
|
162
|
-
"gpt-5-pro": { maxTurns: 60 },
|
|
163
|
-
"gpt-5-mini": { maxTurns: 25 },
|
|
164
|
-
"gpt-5-nano": { maxTurns: 15 },
|
|
165
|
-
"o3": { maxTurns: 50 },
|
|
166
|
-
"o3-mini": { maxTurns: 25 },
|
|
167
|
-
"o4-mini": { maxTurns: 25 },
|
|
168
|
-
};
|
|
169
|
-
return table[this.model] ?? {};
|
|
41
|
+
return this.resolvedRuntimePolicy;
|
|
42
|
+
}
|
|
43
|
+
bindResolvedRuntime(resolved) {
|
|
44
|
+
if (resolved.identity.protocol !== "openai-responses"
|
|
45
|
+
|| resolved.identity.providerId !== "openai"
|
|
46
|
+
|| resolved.identity.modelId !== this.model) {
|
|
47
|
+
throw new Error("OpenAIResponsesProvider received a mismatched resolved runtime");
|
|
48
|
+
}
|
|
49
|
+
this.resolvedRuntime = resolved;
|
|
170
50
|
}
|
|
171
51
|
createRunState() {
|
|
172
52
|
return { coveredMessageCount: 0 };
|
|
173
53
|
}
|
|
54
|
+
adapterInput(context, tools, extensions) {
|
|
55
|
+
const resolved = this.resolvedRuntime ?? {
|
|
56
|
+
identity: {
|
|
57
|
+
providerId: "openai",
|
|
58
|
+
modelId: this.model,
|
|
59
|
+
endpointId: "openai.responses",
|
|
60
|
+
protocol: "openai-responses",
|
|
61
|
+
},
|
|
62
|
+
model: {
|
|
63
|
+
id: `openai/${this.model}`,
|
|
64
|
+
providerId: "openai",
|
|
65
|
+
kind: "generation",
|
|
66
|
+
intrinsic: {},
|
|
67
|
+
},
|
|
68
|
+
endpoint: endpointProfiles["openai.responses"],
|
|
69
|
+
adapter: this,
|
|
70
|
+
effectiveCapabilities: compatibilityCapabilities(),
|
|
71
|
+
};
|
|
72
|
+
return normalizeCanonicalAdapterInput({ context, tools, resolved, extensions });
|
|
73
|
+
}
|
|
174
74
|
async complete(context, tools, extensions) {
|
|
175
75
|
if (this.circuit.isOpen())
|
|
176
|
-
throw
|
|
177
|
-
let
|
|
178
|
-
|
|
76
|
+
throw circuitOpenError("openai");
|
|
77
|
+
let input;
|
|
78
|
+
let plan;
|
|
79
|
+
try {
|
|
80
|
+
input = this.adapterInput(context, tools, extensions);
|
|
81
|
+
plan = this.responses.buildRequest(input);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw classifyProviderError("openai", error);
|
|
85
|
+
}
|
|
86
|
+
let lastError;
|
|
87
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
179
88
|
try {
|
|
180
|
-
const
|
|
181
|
-
const resp = await this.client.responses.create({
|
|
182
|
-
...this.requestExtensions(extensions),
|
|
183
|
-
model: this.model,
|
|
184
|
-
input: this.responses.buildInput(context),
|
|
185
|
-
...(instructions ? { instructions } : {}),
|
|
186
|
-
...((t => t ? { tools: t } : {})(this.allTools(tools, extensions))),
|
|
187
|
-
});
|
|
89
|
+
const response = await this.client.responses.create(plan.params);
|
|
188
90
|
this.circuit.recordSuccess();
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
role: "assistant",
|
|
192
|
-
content: decoded.content,
|
|
193
|
-
toolCalls: decoded.toolCalls,
|
|
194
|
-
tokenCount: resp.usage?.output_tokens ?? resp.usage?.total_tokens,
|
|
195
|
-
};
|
|
91
|
+
return this.responses.decodeComplete(response, { input }).message;
|
|
196
92
|
}
|
|
197
|
-
catch (
|
|
198
|
-
|
|
93
|
+
catch (error) {
|
|
94
|
+
lastError = error;
|
|
199
95
|
this.circuit.recordFailure();
|
|
200
|
-
if (
|
|
201
|
-
await new Promise(
|
|
96
|
+
if (attempt < this.maxRetries - 1) {
|
|
97
|
+
await new Promise(resolve => setTimeout(resolve, this.baseDelay * 2 ** attempt));
|
|
98
|
+
}
|
|
202
99
|
}
|
|
203
100
|
}
|
|
204
|
-
throw
|
|
101
|
+
throw classifyProviderError("openai", lastError);
|
|
205
102
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
argsBuf: evt.item.arguments ?? "{}",
|
|
245
|
-
};
|
|
246
|
-
let args = {};
|
|
247
|
-
try {
|
|
248
|
-
args = JSON.parse(call.argsBuf || "{}");
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
args = {};
|
|
103
|
+
/** spc_024-05: native preflight via the official Responses input-token count endpoint. Counts
|
|
104
|
+
* the exact create request plan (stateful `previous_response_id` continuation included) —
|
|
105
|
+
* native measurement belongs to the verified official endpoint, not the wire protocol. */
|
|
106
|
+
async countTokens(context, tools, extensions, state) {
|
|
107
|
+
const enabled = this.resolvedRuntime
|
|
108
|
+
? this.resolvedRuntime.effectiveCapabilities.nativeTokenCounting.state === "supported"
|
|
109
|
+
: this.directNativeTokenCounting;
|
|
110
|
+
const inputTokens = this.client.responses.inputTokens;
|
|
111
|
+
if (!enabled || typeof inputTokens?.count !== "function") {
|
|
112
|
+
throw new Error("Native token counting is unavailable on this OpenAI-compatible endpoint");
|
|
113
|
+
}
|
|
114
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
115
|
+
const plan = this.responses.buildRequest(input, this.asRunState(state));
|
|
116
|
+
const body = Object.fromEntries(INPUT_TOKEN_COUNT_PARAM_KEYS
|
|
117
|
+
.filter(key => key in plan.params)
|
|
118
|
+
.map(key => [key, plan.params[key]]));
|
|
119
|
+
const response = await inputTokens.count(body);
|
|
120
|
+
return {
|
|
121
|
+
inputTokens: response.input_tokens,
|
|
122
|
+
source: { kind: "native", provider: "openai" },
|
|
123
|
+
confidence: "exact",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async *stream(context, tools, extensions, state, signal) {
|
|
127
|
+
try {
|
|
128
|
+
const runState = this.asRunState(state);
|
|
129
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
130
|
+
const plan = this.responses.buildRequest(input, runState);
|
|
131
|
+
const streamState = this.responses.createStreamState({ input }, runState);
|
|
132
|
+
const stream = await this.client.responses.create({ ...plan.params, stream: true }, signal ? { signal } : undefined);
|
|
133
|
+
for await (const chunk of stream) {
|
|
134
|
+
const output = this.responses.pushStreamChunk(chunk, streamState);
|
|
135
|
+
for (const event of output.events)
|
|
136
|
+
yield event;
|
|
137
|
+
if (output.runStatePatch) {
|
|
138
|
+
Object.assign(runState, output.runStatePatch);
|
|
139
|
+
if (state)
|
|
140
|
+
Object.assign(state, output.runStatePatch);
|
|
252
141
|
}
|
|
253
|
-
yield { type: "tool_call", id: call.id, name: call.name, arguments: args };
|
|
254
142
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
yield {
|
|
263
|
-
type: "usage",
|
|
264
|
-
totalTokens: evt.response.usage.total_tokens,
|
|
265
|
-
...(evt.response.usage.input_tokens ? { inputTokens: evt.response.usage.input_tokens } : {}),
|
|
266
|
-
...(evt.response.usage.output_tokens ? { outputTokens: evt.response.usage.output_tokens } : {}),
|
|
267
|
-
...(cachedTokens > 0 ? { cacheReadInputTokens: cachedTokens } : {}),
|
|
268
|
-
};
|
|
269
|
-
}
|
|
143
|
+
const final = this.responses.finishStream(streamState);
|
|
144
|
+
for (const event of final.events)
|
|
145
|
+
yield event;
|
|
146
|
+
if (final.runStatePatch) {
|
|
147
|
+
Object.assign(runState, final.runStatePatch);
|
|
148
|
+
if (state)
|
|
149
|
+
Object.assign(state, final.runStatePatch);
|
|
270
150
|
}
|
|
271
151
|
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
throw classifyProviderError("openai", error);
|
|
154
|
+
}
|
|
272
155
|
}
|
|
273
|
-
|
|
274
|
-
return omitExtensionKeys(extensions, [
|
|
275
|
-
"model", "input", "instructions", "tools", "stream", "previous_response_id",
|
|
276
|
-
"web_search", "builtin_tools",
|
|
277
|
-
]);
|
|
278
|
-
}
|
|
279
|
-
/** Responses API built-in server tools from extensions (live in the same tools[] as function tools):
|
|
280
|
-
* `web_search: true` (or a config object), plus a `builtin_tools` list passed through verbatim for
|
|
281
|
-
* file_search / code_interpreter. They run server-side; results return inline. Mirrors py. */
|
|
156
|
+
// White-box test seams. Protocol request shaping belongs to the adapter.
|
|
282
157
|
builtinTools(extensions) {
|
|
283
|
-
|
|
284
|
-
const out = [];
|
|
285
|
-
const ws = ext.web_search;
|
|
286
|
-
if (ws)
|
|
287
|
-
out.push(typeof ws === "object" ? { type: "web_search", ...ws } : { type: "web_search" });
|
|
288
|
-
if (Array.isArray(ext.builtin_tools))
|
|
289
|
-
out.push(...ext.builtin_tools);
|
|
290
|
-
return out;
|
|
158
|
+
return this.responses.builtinTools(extensions);
|
|
291
159
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const fnTools = tools.length ? this.responses.buildTools(tools) : [];
|
|
295
|
-
const all = [...fnTools, ...this.builtinTools(extensions)];
|
|
296
|
-
return all.length ? all : undefined;
|
|
160
|
+
requestExtensions(extensions) {
|
|
161
|
+
return this.responses.requestExtensions(extensions);
|
|
297
162
|
}
|
|
298
163
|
asRunState(state) {
|
|
299
164
|
if (!state)
|
|
300
165
|
return this.createRunState();
|
|
301
|
-
|
|
302
|
-
state
|
|
303
|
-
|
|
166
|
+
return {
|
|
167
|
+
...state,
|
|
168
|
+
coveredMessageCount: typeof state.coveredMessageCount === "number"
|
|
169
|
+
? state.coveredMessageCount
|
|
170
|
+
: 0,
|
|
171
|
+
};
|
|
304
172
|
}
|
|
305
173
|
}
|
|
174
|
+
function compatibilityCapabilities() {
|
|
175
|
+
const unknown = { state: "unknown", evidence: [] };
|
|
176
|
+
const unsupported = { state: "unsupported", evidence: ["protocol"] };
|
|
177
|
+
return {
|
|
178
|
+
inputModalities: {
|
|
179
|
+
text: unknown,
|
|
180
|
+
image: unknown,
|
|
181
|
+
audio: unsupported,
|
|
182
|
+
video: unsupported,
|
|
183
|
+
file: unknown,
|
|
184
|
+
},
|
|
185
|
+
outputModalities: {
|
|
186
|
+
text: unknown,
|
|
187
|
+
image: unsupported,
|
|
188
|
+
audio: unsupported,
|
|
189
|
+
embedding: unsupported,
|
|
190
|
+
},
|
|
191
|
+
tools: unknown,
|
|
192
|
+
reasoning: unknown,
|
|
193
|
+
parallelToolCalls: unknown,
|
|
194
|
+
structuredOutput: unknown,
|
|
195
|
+
promptCaching: unknown,
|
|
196
|
+
nativeTokenCounting: unknown,
|
|
197
|
+
mediaForms: {
|
|
198
|
+
imageUrl: unknown,
|
|
199
|
+
imageBase64: unknown,
|
|
200
|
+
fileId: unknown,
|
|
201
|
+
audioUrl: unsupported,
|
|
202
|
+
audioBase64: unsupported,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import type { Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext,
|
|
2
|
+
import type { LLMProvider, Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext, ReplayabilityAssessment, RuntimePolicy, StreamEvent, ToolSchema } from "../types.js";
|
|
3
3
|
import { CircuitBreaker } from "./base.js";
|
|
4
|
+
import { type CanonicalAdapterInput } from "./content-normalization.js";
|
|
4
5
|
import { OpenAIChatAdapter } from "./openai-chat.js";
|
|
5
|
-
import type
|
|
6
|
-
/**
|
|
7
|
-
* provider (custom `baseURL` no longer needs a positional hole). */
|
|
6
|
+
import { type OpenAIChatTurnReasoning, type OpenAIChatWireDialect } from "./openai-chat-dialects.js";
|
|
7
|
+
/** Canonical constructor contract for OpenAI-chat providers. */
|
|
8
8
|
export interface OpenAIProviderOptions {
|
|
9
9
|
apiKey: string;
|
|
10
10
|
model?: string;
|
|
@@ -12,23 +12,13 @@ export interface OpenAIProviderOptions {
|
|
|
12
12
|
maxRetries: number;
|
|
13
13
|
baseDelay: number;
|
|
14
14
|
};
|
|
15
|
-
/** Custom OpenAI-compatible endpoint (MiMo, DeepSeek, Kimi, …). Defaults to the OpenAI API. */
|
|
16
15
|
baseURL?: string;
|
|
16
|
+
runtimePolicy?: RuntimePolicy;
|
|
17
|
+
dialect?: OpenAIChatWireDialect;
|
|
18
|
+
authMode?: "api_key" | "bearer";
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export interface OpenAIChatTurnReasoning {
|
|
21
|
-
reasoningContent: string;
|
|
22
|
-
reasoningDetails?: unknown;
|
|
23
|
-
nativeToolCalls: unknown[];
|
|
24
|
-
}
|
|
25
|
-
/** Rebuild OpenAI-native `tool_calls` blocks from the streamed buffers — needed by reasoning
|
|
26
|
-
* vendors (DeepSeek/MiniMax) that persist the native blocks in their replay envelope. */
|
|
27
|
-
export declare function nativeToolCallsFromBuffers(toolCallBufs: Record<number, {
|
|
28
|
-
id: string;
|
|
29
|
-
name: string;
|
|
30
|
-
argsBuf: string;
|
|
31
|
-
}>): Array<Record<string, unknown>>;
|
|
20
|
+
export type { OpenAIChatTurnReasoning } from "./openai-chat-dialects.js";
|
|
21
|
+
type ResolvedOpenAIChatRuntime = CanonicalAdapterInput["resolved"];
|
|
32
22
|
export declare class OpenAIChatProvider implements LLMProvider {
|
|
33
23
|
protected client: OpenAI;
|
|
34
24
|
protected circuit: CircuitBreaker;
|
|
@@ -36,72 +26,35 @@ export declare class OpenAIChatProvider implements LLMProvider {
|
|
|
36
26
|
protected baseDelay: number;
|
|
37
27
|
protected readonly model: string;
|
|
38
28
|
protected readonly chat: OpenAIChatAdapter;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
protected readonly dialect: OpenAIChatWireDialect;
|
|
30
|
+
private readonly replayStore;
|
|
31
|
+
private readonly resolvedRuntimePolicy;
|
|
32
|
+
private resolvedRuntime?;
|
|
33
|
+
constructor(options: OpenAIProviderOptions);
|
|
43
34
|
runtimePolicy(): RuntimePolicy;
|
|
44
35
|
descriptor(): ProviderDescriptor;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
protected
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
* so it does not also leak into the request body. */
|
|
59
|
-
protected serverTools(_extensions?: Record<string, unknown>): unknown[];
|
|
60
|
-
/** Merge function tools + vendor server tools into the wire `tools[]` (undefined when empty). Server
|
|
61
|
-
* tools (e.g. web_search) are non-standard wire entries, so the array is cast to the SDK tool type. */
|
|
62
|
-
protected assembleTools(tools: ToolSchema[], extensions?: Record<string, unknown>): OpenAI.Chat.Completions.ChatCompletionTool[] | undefined;
|
|
63
|
-
/** Request-body params controlling prompt caching. Default sends OpenAI's `prompt_cache_key`;
|
|
64
|
-
* vendors whose endpoints reject unknown params (e.g. DeepSeek 400s) override to `{}`. */
|
|
65
|
-
protected cacheKeyParams(context: RenderedContext, tools: ToolSchema[]): Record<string, unknown>;
|
|
66
|
-
/** Whether streamed `content` may carry inline `<thinking>…</thinking>` tags to split out.
|
|
67
|
-
* Default true (OpenAI). Reasoning vendors emit reasoning out-of-band, so they return false. */
|
|
68
|
-
protected usesInlineThinkingTags(): boolean;
|
|
69
|
-
/** Whether to surface streamed `reasoning_content` as thinking_delta events. Default true;
|
|
70
|
-
* vendors gate this behind an `exposeReasoning` extension. */
|
|
71
|
-
protected exposeReasoningDelta(_extensions?: Record<string, unknown>): boolean;
|
|
72
|
-
/** Persist replay after a non-streaming turn. Default: nothing (plain OpenAI has no reasoning
|
|
73
|
-
* to replay). Reasoning vendors override to store their envelope. */
|
|
36
|
+
bindResolvedRuntime(resolved: ResolvedOpenAIChatRuntime): void;
|
|
37
|
+
assessReplayability(context: RenderedContext, extensions?: Record<string, unknown>): ReplayabilityAssessment;
|
|
38
|
+
peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
|
|
39
|
+
seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
|
|
40
|
+
private rememberReplay;
|
|
41
|
+
private adapterInput;
|
|
42
|
+
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
|
43
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
|
|
44
|
+
protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
|
|
45
|
+
protected requestBodyExtras(extensions?: Record<string, unknown>): Record<string, unknown>;
|
|
46
|
+
protected serverTools(extensions?: Record<string, unknown>): unknown[];
|
|
47
|
+
protected requestExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
|
|
48
|
+
protected promptCacheKey(context: RenderedContext, tools: ToolSchema[]): string;
|
|
74
49
|
protected rememberCompleteReplay(_content: string, _toolCalls: Array<{
|
|
75
50
|
id: string;
|
|
76
51
|
name: string;
|
|
77
52
|
arguments: string;
|
|
78
53
|
}>, _reasoning: OpenAIChatTurnReasoning): void;
|
|
79
|
-
|
|
80
|
-
* tool-call turn or captured reasoning (the prior base behavior). Vendors override. */
|
|
81
|
-
protected rememberStreamReplay(content: string, toolCalls: Array<{
|
|
54
|
+
protected rememberStreamReplay(_content: string, _toolCalls: Array<{
|
|
82
55
|
id: string;
|
|
83
56
|
name: string;
|
|
84
57
|
arguments: string;
|
|
85
|
-
}>,
|
|
86
|
-
/**
|
|
87
|
-
* Pre-flight query: would this history validate against this provider with the
|
|
88
|
-
* given extensions, without sending the request? Lets an embedder route around
|
|
89
|
-
* a reasoning-replay failure (keep thinking on, disable it, or skip this
|
|
90
|
-
* candidate) before issuing the request. `ok: true` when this provider does
|
|
91
|
-
* not require reasoning replay for the current extensions.
|
|
92
|
-
*/
|
|
93
|
-
assessReplayability(context: RenderedContext, extensions?: Record<string, unknown>): ReplayabilityAssessment;
|
|
94
|
-
peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
|
|
95
|
-
seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
|
|
96
|
-
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
|
97
|
-
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
|
|
98
|
-
/**
|
|
99
|
-
* Default `prompt_cache_key` derived from the cacheable prefix (system prompt +
|
|
100
|
-
* tool names) so requests for the same agent config route to the same cache.
|
|
101
|
-
* A caller-supplied `prompt_cache_key` in extensions overrides it (it is spread
|
|
102
|
-
* after this default). Unknown to non-OpenAI compatible endpoints, which ignore it.
|
|
103
|
-
*/
|
|
104
|
-
protected promptCacheKey(context: RenderedContext, tools: ToolSchema[]): string;
|
|
105
|
-
protected requestExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
|
|
58
|
+
}>, _reasoning: OpenAIChatTurnReasoning): void;
|
|
106
59
|
}
|
|
107
60
|
export { OpenAIChatProvider as OpenAIProvider };
|