@deepstrike/sdk 0.2.51 → 0.2.60
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/providers/anthropic-adapter.d.ts +47 -0
- package/dist/providers/anthropic-adapter.js +444 -0
- package/dist/providers/anthropic-compatible.d.ts +2 -3
- package/dist/providers/anthropic-compatible.js +8 -5
- package/dist/providers/anthropic.d.ts +19 -23
- package/dist/providers/anthropic.js +169 -395
- package/dist/providers/base.js +43 -3
- 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 -21
- 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 +9 -3
- package/dist/providers/gemini-adapter.d.ts +33 -0
- package/dist/providers/gemini-adapter.js +272 -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 +98 -0
- package/dist/providers/model-registry.js +315 -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 +418 -51
- package/dist/providers/openai-responses-adapter.d.ts +42 -0
- package/dist/providers/openai-responses-adapter.js +341 -0
- package/dist/providers/openai-responses.d.ts +14 -33
- package/dist/providers/openai-responses.js +131 -264
- package/dist/providers/openai.d.ts +29 -76
- package/dist/providers/openai.js +195 -292
- package/dist/providers/protocol-adapter.d.ts +34 -0
- package/dist/providers/protocol-adapter.js +9 -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 +149 -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 +86 -0
- package/dist/providers/request-plan.js +175 -0
- package/dist/providers/usage-normalizer.d.ts +48 -0
- package/dist/providers/usage-normalizer.js +96 -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 +10 -2
- package/dist/runtime/canonical-kernel-step.js +106 -32
- 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 +1 -1
- package/dist/runtime/provider-replay.js +12 -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 +26 -40
- package/dist/runtime/runner.js +145 -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 +128 -13
- package/package.json +3 -3
- 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
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { projectToolOutputToText } from "./content-normalization.js";
|
|
2
|
+
import { normalizeToolCall } from "./base.js";
|
|
3
|
+
import { ProtocolResponseError, OLLAMA_PROTOCOL_CAPABILITIES, } from "./protocol-adapter.js";
|
|
4
|
+
function messageContent(message) {
|
|
5
|
+
const text = [];
|
|
6
|
+
const images = [];
|
|
7
|
+
for (const item of message.blocks) {
|
|
8
|
+
if (item.type === "tool_result") {
|
|
9
|
+
text.push(projectToolOutputToText(item.blocks));
|
|
10
|
+
}
|
|
11
|
+
else if (item.type === "text") {
|
|
12
|
+
text.push(item.text);
|
|
13
|
+
}
|
|
14
|
+
else if (item.type === "image" && item.source.kind === "base64") {
|
|
15
|
+
images.push(item.source.data);
|
|
16
|
+
text.push("[image]");
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
throw new ProtocolResponseError("ollama-chat", `cannot serialize ${item.type}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
role: message.role,
|
|
24
|
+
content: text.join("\n"),
|
|
25
|
+
...(images.length ? { images } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function requestExtensions(extensions) {
|
|
29
|
+
const blocked = new Set([
|
|
30
|
+
"model",
|
|
31
|
+
"messages",
|
|
32
|
+
"tools",
|
|
33
|
+
"stream",
|
|
34
|
+
"__deepstrikeThinkingEnabled",
|
|
35
|
+
"degradeMissingReasoningReplay",
|
|
36
|
+
]);
|
|
37
|
+
return Object.fromEntries(Object.entries(extensions).filter(([key]) => !blocked.has(key)));
|
|
38
|
+
}
|
|
39
|
+
function validCount(raw, field) {
|
|
40
|
+
const value = raw[field];
|
|
41
|
+
if (value === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
44
|
+
throw new ProtocolResponseError("ollama-chat", `${field} must be a non-negative finite number`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
export class OllamaNdjsonDecoder {
|
|
49
|
+
buffer = "";
|
|
50
|
+
push(text) {
|
|
51
|
+
this.buffer += text;
|
|
52
|
+
const lines = this.buffer.split("\n");
|
|
53
|
+
this.buffer = lines.pop() ?? "";
|
|
54
|
+
return this.parse(lines);
|
|
55
|
+
}
|
|
56
|
+
finish(text = "") {
|
|
57
|
+
this.buffer += text;
|
|
58
|
+
const tail = this.buffer;
|
|
59
|
+
this.buffer = "";
|
|
60
|
+
return this.parse(tail ? [tail] : []);
|
|
61
|
+
}
|
|
62
|
+
parse(lines) {
|
|
63
|
+
const chunks = [];
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
if (!line.trim())
|
|
66
|
+
continue;
|
|
67
|
+
try {
|
|
68
|
+
const value = JSON.parse(line);
|
|
69
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
70
|
+
chunks.push(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Preserve the established Ollama behavior: malformed complete lines are skipped.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return chunks;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class OllamaAdapter {
|
|
81
|
+
protocol = "ollama-chat";
|
|
82
|
+
protocolCapabilities = OLLAMA_PROTOCOL_CAPABILITIES;
|
|
83
|
+
buildRequest(input) {
|
|
84
|
+
const messages = [];
|
|
85
|
+
if (input.context.systemText) {
|
|
86
|
+
messages.push({ role: "system", content: input.context.systemText });
|
|
87
|
+
}
|
|
88
|
+
const turns = input.context.stateTurn
|
|
89
|
+
? [...input.context.turns, input.context.stateTurn]
|
|
90
|
+
: input.context.turns;
|
|
91
|
+
messages.push(...turns.map(messageContent));
|
|
92
|
+
return {
|
|
93
|
+
...requestExtensions(input.extensions),
|
|
94
|
+
model: input.resolved.identity.modelId,
|
|
95
|
+
messages,
|
|
96
|
+
...(input.tools.length ? {
|
|
97
|
+
tools: input.tools.map(tool => ({
|
|
98
|
+
type: "function",
|
|
99
|
+
function: {
|
|
100
|
+
name: tool.name,
|
|
101
|
+
description: tool.description,
|
|
102
|
+
parameters: JSON.parse(tool.parameters),
|
|
103
|
+
},
|
|
104
|
+
})),
|
|
105
|
+
} : {}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
decodeComplete(raw, _input) {
|
|
109
|
+
return {
|
|
110
|
+
message: {
|
|
111
|
+
role: "assistant",
|
|
112
|
+
content: raw.message?.content ?? "",
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
createStreamState(input) {
|
|
117
|
+
return { input: input.input, pendingToolCalls: new Map() };
|
|
118
|
+
}
|
|
119
|
+
pushStreamChunk(chunk, state) {
|
|
120
|
+
const events = [];
|
|
121
|
+
if (chunk.message?.content) {
|
|
122
|
+
events.push({ type: "text_delta", delta: chunk.message.content });
|
|
123
|
+
}
|
|
124
|
+
for (const call of chunk.message?.tool_calls ?? []) {
|
|
125
|
+
const normalized = normalizeToolCall("", call.function.name, call.function.arguments);
|
|
126
|
+
if (!normalized)
|
|
127
|
+
continue;
|
|
128
|
+
const key = `${normalized.name}:${normalized.arguments}`;
|
|
129
|
+
if (!state.pendingToolCalls.has(key)) {
|
|
130
|
+
state.pendingToolCalls.set(key, {
|
|
131
|
+
id: `call_${state.pendingToolCalls.size + 1}`,
|
|
132
|
+
name: normalized.name,
|
|
133
|
+
arguments: JSON.parse(normalized.arguments),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (chunk.done)
|
|
138
|
+
state.finalChunk = chunk;
|
|
139
|
+
return { events };
|
|
140
|
+
}
|
|
141
|
+
finishStream(state, final) {
|
|
142
|
+
const terminal = final ?? state.finalChunk;
|
|
143
|
+
const events = Array.from(state.pendingToolCalls.values(), call => ({ type: "tool_call", ...call }));
|
|
144
|
+
const usage = this.normalizeUsage(terminal);
|
|
145
|
+
if (usage) {
|
|
146
|
+
const rawStopReason = terminal?.done_reason;
|
|
147
|
+
const stopReason = this.normalizeStopReason(rawStopReason);
|
|
148
|
+
events.push({
|
|
149
|
+
type: "usage",
|
|
150
|
+
totalTokens: usage.inputTokens + usage.outputTokens,
|
|
151
|
+
inputTokens: usage.inputTokens,
|
|
152
|
+
outputTokens: usage.outputTokens,
|
|
153
|
+
providerUsage: usage,
|
|
154
|
+
...(stopReason ? { stopReason } : {}),
|
|
155
|
+
...(rawStopReason ? { rawStopReason } : {}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return { events };
|
|
159
|
+
}
|
|
160
|
+
normalizeUsage(raw) {
|
|
161
|
+
if (raw === undefined || raw === null)
|
|
162
|
+
return undefined;
|
|
163
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
164
|
+
throw new ProtocolResponseError("ollama-chat", "usage source must be an object");
|
|
165
|
+
}
|
|
166
|
+
const record = raw;
|
|
167
|
+
const inputTokens = validCount(record, "prompt_eval_count");
|
|
168
|
+
const outputTokens = validCount(record, "eval_count");
|
|
169
|
+
if (inputTokens === undefined && outputTokens === undefined)
|
|
170
|
+
return undefined;
|
|
171
|
+
return {
|
|
172
|
+
inputTokens: inputTokens ?? 0,
|
|
173
|
+
outputTokens: outputTokens ?? 0,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
normalizeStopReason(raw) {
|
|
177
|
+
if (raw === undefined)
|
|
178
|
+
return undefined;
|
|
179
|
+
switch (raw) {
|
|
180
|
+
case "stop": return "end_turn";
|
|
181
|
+
case "length": return "max_tokens";
|
|
182
|
+
default: return "other";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
createNdjsonDecoder() {
|
|
186
|
+
return new OllamaNdjsonDecoder();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
|
|
2
|
+
import { type CanonicalAdapterInput } from "./content-normalization.js";
|
|
3
|
+
type ResolvedOllamaRuntime = CanonicalAdapterInput["resolved"];
|
|
2
4
|
export declare class OllamaProvider implements LLMProvider {
|
|
3
5
|
private readonly model;
|
|
4
6
|
private readonly baseUrl;
|
|
5
|
-
|
|
7
|
+
private readonly resolvedRuntimePolicy;
|
|
8
|
+
private resolvedRuntime?;
|
|
9
|
+
private readonly adapter;
|
|
10
|
+
constructor(model?: string, baseUrl?: string, resolvedRuntimePolicy?: RuntimePolicy, resolvedRuntime?: ResolvedOllamaRuntime | undefined);
|
|
6
11
|
runtimePolicy(): RuntimePolicy;
|
|
7
|
-
|
|
8
|
-
private
|
|
9
|
-
private requestExtensions;
|
|
12
|
+
bindResolvedRuntime(resolved: ResolvedOllamaRuntime): void;
|
|
13
|
+
private adapterInput;
|
|
10
14
|
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
|
11
15
|
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
12
16
|
}
|
|
17
|
+
export {};
|
package/dist/providers/ollama.js
CHANGED
|
@@ -1,129 +1,116 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
["qwq", { maxTurns: 35 }],
|
|
6
|
-
["llama3.3", { maxTurns: 25 }],
|
|
7
|
-
["llama3.2", { maxTurns: 20 }],
|
|
8
|
-
["llama3.1", { maxTurns: 20 }],
|
|
9
|
-
["llama3", { maxTurns: 20 }],
|
|
10
|
-
["mistral", { maxTurns: 20 }],
|
|
11
|
-
["gemma2", { maxTurns: 20 }],
|
|
12
|
-
["phi4", { maxTurns: 20 }],
|
|
13
|
-
["phi3", { maxTurns: 15 }],
|
|
14
|
-
["codellama", { maxTurns: 20 }],
|
|
15
|
-
];
|
|
1
|
+
import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
|
|
2
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
3
|
+
import { OllamaAdapter } from "./ollama-adapter.js";
|
|
4
|
+
import { classifyProviderError, ProviderError } from "./provider-error.js";
|
|
16
5
|
export class OllamaProvider {
|
|
17
6
|
model;
|
|
18
7
|
baseUrl;
|
|
19
|
-
|
|
8
|
+
resolvedRuntimePolicy;
|
|
9
|
+
resolvedRuntime;
|
|
10
|
+
adapter = new OllamaAdapter();
|
|
11
|
+
constructor(model = "llama3", baseUrl = "http://localhost:11434", resolvedRuntimePolicy = {}, resolvedRuntime) {
|
|
20
12
|
this.model = model;
|
|
21
13
|
this.baseUrl = baseUrl;
|
|
14
|
+
this.resolvedRuntimePolicy = resolvedRuntimePolicy;
|
|
15
|
+
this.resolvedRuntime = resolvedRuntime;
|
|
22
16
|
}
|
|
23
17
|
runtimePolicy() {
|
|
24
|
-
|
|
25
|
-
for (const [prefix, policy] of OLLAMA_PREFIX_POLICIES) {
|
|
26
|
-
if (m.startsWith(prefix))
|
|
27
|
-
return policy;
|
|
28
|
-
}
|
|
29
|
-
return { maxTurns: 20 };
|
|
18
|
+
return this.resolvedRuntimePolicy;
|
|
30
19
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
for (const m of turnsWithStateAppended(context)) {
|
|
36
|
-
const images = [];
|
|
37
|
-
if (m.contentParts?.length) {
|
|
38
|
-
for (const p of m.contentParts) {
|
|
39
|
-
if (p.type === "image" && p.data)
|
|
40
|
-
images.push(p.data);
|
|
41
|
-
else if (p.type === "audio")
|
|
42
|
-
throw new UnsupportedModalityError("audio", "ollama");
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
result.push({ role: m.role, content: m.content, ...(images.length ? { images } : {}) });
|
|
20
|
+
bindResolvedRuntime(resolved) {
|
|
21
|
+
if (resolved.identity.protocol !== "ollama-chat"
|
|
22
|
+
|| resolved.identity.modelId !== this.model) {
|
|
23
|
+
throw new Error("OllamaProvider received a mismatched resolved runtime");
|
|
46
24
|
}
|
|
47
|
-
|
|
25
|
+
this.resolvedRuntime = resolved;
|
|
48
26
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
27
|
+
adapterInput(context, tools, extensions) {
|
|
28
|
+
const resolved = this.resolvedRuntime ?? {
|
|
29
|
+
identity: {
|
|
30
|
+
providerId: "ollama",
|
|
31
|
+
modelId: this.model,
|
|
32
|
+
endpointId: "ollama.local",
|
|
33
|
+
protocol: "ollama-chat",
|
|
34
|
+
},
|
|
35
|
+
model: { id: `ollama/${this.model}`, providerId: "ollama", kind: "generation", intrinsic: {} },
|
|
36
|
+
endpoint: endpointProfiles["ollama.local"],
|
|
37
|
+
adapter: this,
|
|
38
|
+
effectiveCapabilities: {
|
|
39
|
+
inputModalities: Object.fromEntries(["text", "image", "audio", "video", "file"].map(modality => [modality, { state: modality === "audio" || modality === "video" || modality === "file" ? "unsupported" : "unknown", evidence: [] }])),
|
|
40
|
+
outputModalities: Object.fromEntries(["text", "image", "audio", "embedding"].map(modality => [modality, { state: "unknown", evidence: [] }])),
|
|
41
|
+
tools: { state: "unknown", evidence: [] },
|
|
42
|
+
reasoning: { state: "unknown", evidence: [] },
|
|
43
|
+
parallelToolCalls: { state: "unknown", evidence: [] },
|
|
44
|
+
structuredOutput: { state: "unknown", evidence: [] },
|
|
45
|
+
promptCaching: { state: "unknown", evidence: [] },
|
|
46
|
+
nativeTokenCounting: { state: "unknown", evidence: [] },
|
|
47
|
+
mediaForms: Object.fromEntries(["imageUrl", "imageBase64", "fileId", "audioUrl", "audioBase64"].map(form => [form, { state: "unknown", evidence: [] }])),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return normalizeCanonicalAdapterInput({ context, tools, resolved, extensions });
|
|
57
51
|
}
|
|
58
52
|
async complete(context, tools, extensions) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
53
|
+
try {
|
|
54
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
55
|
+
const body = { ...this.adapter.buildRequest(input), stream: false };
|
|
56
|
+
const resp = await fetch(`${this.baseUrl}/api/chat`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
if (!resp.ok)
|
|
62
|
+
throw ollamaHttpError(resp.status);
|
|
63
|
+
const data = await resp.json();
|
|
64
|
+
return this.adapter.decodeComplete(data, { input }).message;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw classifyProviderError("ollama", error);
|
|
68
|
+
}
|
|
74
69
|
}
|
|
75
70
|
async *stream(context, tools, extensions) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (chunk.message?.content)
|
|
106
|
-
yield { type: "text_delta", delta: chunk.message.content };
|
|
107
|
-
for (const tc of chunk.message?.tool_calls ?? []) {
|
|
108
|
-
const norm = normalizeToolCall("", tc.function.name, tc.function.arguments);
|
|
109
|
-
if (!norm)
|
|
110
|
-
continue;
|
|
111
|
-
const args = JSON.parse(norm.arguments);
|
|
112
|
-
const key = `${norm.name}:${norm.arguments}`;
|
|
113
|
-
if (!pendingToolCalls.has(key)) {
|
|
114
|
-
pendingToolCalls.set(key, {
|
|
115
|
-
id: `call_${pendingToolCalls.size + 1}`,
|
|
116
|
-
name: norm.name,
|
|
117
|
-
arguments: args,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
71
|
+
try {
|
|
72
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
73
|
+
const body = { ...this.adapter.buildRequest(input), stream: true };
|
|
74
|
+
const resp = await fetch(`${this.baseUrl}/api/chat`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "Content-Type": "application/json" },
|
|
77
|
+
body: JSON.stringify(body),
|
|
78
|
+
});
|
|
79
|
+
if (!resp.ok)
|
|
80
|
+
throw ollamaHttpError(resp.status);
|
|
81
|
+
if (!resp.body) {
|
|
82
|
+
throw new ProviderError({
|
|
83
|
+
provider: "ollama",
|
|
84
|
+
kind: "protocol",
|
|
85
|
+
retryable: false,
|
|
86
|
+
message: "Ollama stream response has no body",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const reader = resp.body.getReader();
|
|
90
|
+
const decoder = new TextDecoder();
|
|
91
|
+
const ndjson = this.adapter.createNdjsonDecoder();
|
|
92
|
+
const state = this.adapter.createStreamState({ input });
|
|
93
|
+
while (true) {
|
|
94
|
+
const { done, value } = await reader.read();
|
|
95
|
+
if (done)
|
|
96
|
+
break;
|
|
97
|
+
for (const chunk of ndjson.push(decoder.decode(value, { stream: true }))) {
|
|
98
|
+
for (const event of this.adapter.pushStreamChunk(chunk, state).events)
|
|
99
|
+
yield event;
|
|
121
100
|
}
|
|
122
|
-
catch { /* skip malformed lines */ }
|
|
123
101
|
}
|
|
102
|
+
for (const chunk of ndjson.finish(decoder.decode())) {
|
|
103
|
+
for (const event of this.adapter.pushStreamChunk(chunk, state).events)
|
|
104
|
+
yield event;
|
|
105
|
+
}
|
|
106
|
+
for (const event of this.adapter.finishStream(state, state.finalChunk).events)
|
|
107
|
+
yield event;
|
|
124
108
|
}
|
|
125
|
-
|
|
126
|
-
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw classifyProviderError("ollama", error);
|
|
127
111
|
}
|
|
128
112
|
}
|
|
129
113
|
}
|
|
114
|
+
function ollamaHttpError(status) {
|
|
115
|
+
return classifyProviderError("ollama", Object.assign(new Error(`Ollama error: ${status}`), { status }));
|
|
116
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { ProviderDescriptor, ProviderReplay, ToolCall } from "../types.js";
|
|
2
|
+
import type { EndpointProfileId, ProviderId } from "./endpoints.js";
|
|
3
|
+
export interface OpenAIChatTurnReasoning {
|
|
4
|
+
reasoningContent: string;
|
|
5
|
+
reasoningDetails?: unknown;
|
|
6
|
+
nativeToolCalls: unknown[];
|
|
7
|
+
}
|
|
8
|
+
export type OpenAIChatReplayStrategy = "none" | "generic_stream" | "deepseek" | "minimax";
|
|
9
|
+
export interface OpenAIChatWireDialect {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly providerId: ProviderId;
|
|
12
|
+
readonly endpointId: EndpointProfileId;
|
|
13
|
+
readonly descriptor: {
|
|
14
|
+
reasoning: ProviderDescriptor["reasoning"];
|
|
15
|
+
};
|
|
16
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
17
|
+
readonly serverTools?: (extensions: Readonly<Record<string, unknown>>) => unknown[];
|
|
18
|
+
readonly cacheKey: "openai" | "none";
|
|
19
|
+
readonly inlineThinkingTags: boolean;
|
|
20
|
+
readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
21
|
+
readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
22
|
+
readonly replay: OpenAIChatReplayStrategy;
|
|
23
|
+
}
|
|
24
|
+
export declare const openAIChatDialects: {
|
|
25
|
+
readonly openai: {
|
|
26
|
+
readonly id: "openai";
|
|
27
|
+
readonly providerId: "openai";
|
|
28
|
+
readonly endpointId: "openai.chat";
|
|
29
|
+
readonly descriptor: {
|
|
30
|
+
readonly reasoning: {
|
|
31
|
+
supported: boolean;
|
|
32
|
+
preserveAcrossToolTurns: boolean;
|
|
33
|
+
requiresReplayForToolTurns?: boolean;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
37
|
+
readonly cacheKey: "openai";
|
|
38
|
+
readonly inlineThinkingTags: true;
|
|
39
|
+
readonly exposeReasoning: () => boolean;
|
|
40
|
+
readonly requireReasoningReplay: () => boolean;
|
|
41
|
+
readonly replay: "generic_stream";
|
|
42
|
+
};
|
|
43
|
+
readonly deepseek: {
|
|
44
|
+
readonly id: "deepseek";
|
|
45
|
+
readonly providerId: "deepseek";
|
|
46
|
+
readonly endpointId: "deepseek.openai";
|
|
47
|
+
readonly descriptor: {
|
|
48
|
+
readonly reasoning: {
|
|
49
|
+
supported: boolean;
|
|
50
|
+
preserveAcrossToolTurns: boolean;
|
|
51
|
+
requiresReplayForToolTurns?: boolean;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
|
|
55
|
+
__deepstrikeThinkingEnabled: boolean;
|
|
56
|
+
reasoning_effort: string;
|
|
57
|
+
extra_body: {
|
|
58
|
+
thinking: {
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
degradeMissingReasoningReplay?: boolean | undefined;
|
|
63
|
+
};
|
|
64
|
+
readonly cacheKey: "none";
|
|
65
|
+
readonly inlineThinkingTags: false;
|
|
66
|
+
readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
67
|
+
readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
68
|
+
readonly replay: "deepseek";
|
|
69
|
+
};
|
|
70
|
+
readonly kimi: {
|
|
71
|
+
readonly id: "kimi";
|
|
72
|
+
readonly providerId: "kimi";
|
|
73
|
+
readonly endpointId: "kimi.openai";
|
|
74
|
+
readonly descriptor: {
|
|
75
|
+
readonly reasoning: {
|
|
76
|
+
supported: boolean;
|
|
77
|
+
preserveAcrossToolTurns: boolean;
|
|
78
|
+
requiresReplayForToolTurns?: boolean;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
82
|
+
readonly cacheKey: "openai";
|
|
83
|
+
readonly inlineThinkingTags: true;
|
|
84
|
+
readonly exposeReasoning: () => boolean;
|
|
85
|
+
readonly requireReasoningReplay: () => boolean;
|
|
86
|
+
readonly replay: "generic_stream";
|
|
87
|
+
};
|
|
88
|
+
readonly qwen: {
|
|
89
|
+
readonly id: "qwen";
|
|
90
|
+
readonly providerId: "qwen";
|
|
91
|
+
readonly endpointId: "qwen.dashscope";
|
|
92
|
+
readonly descriptor: {
|
|
93
|
+
readonly reasoning: {
|
|
94
|
+
supported: boolean;
|
|
95
|
+
preserveAcrossToolTurns: boolean;
|
|
96
|
+
requiresReplayForToolTurns?: boolean;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
|
|
100
|
+
extra_body?: Record<string, unknown> | undefined;
|
|
101
|
+
};
|
|
102
|
+
readonly cacheKey: "none";
|
|
103
|
+
readonly inlineThinkingTags: false;
|
|
104
|
+
readonly exposeReasoning: () => boolean;
|
|
105
|
+
readonly requireReasoningReplay: () => boolean;
|
|
106
|
+
readonly replay: "generic_stream";
|
|
107
|
+
};
|
|
108
|
+
readonly glm: {
|
|
109
|
+
readonly id: "glm";
|
|
110
|
+
readonly providerId: "glm";
|
|
111
|
+
readonly endpointId: "glm.openai";
|
|
112
|
+
readonly descriptor: {
|
|
113
|
+
readonly reasoning: {
|
|
114
|
+
supported: boolean;
|
|
115
|
+
preserveAcrossToolTurns: boolean;
|
|
116
|
+
requiresReplayForToolTurns?: boolean;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
120
|
+
readonly serverTools: (extensions: Readonly<Record<string, unknown>>) => {
|
|
121
|
+
type: string;
|
|
122
|
+
web_search: object;
|
|
123
|
+
}[];
|
|
124
|
+
readonly cacheKey: "openai";
|
|
125
|
+
readonly inlineThinkingTags: true;
|
|
126
|
+
readonly exposeReasoning: () => boolean;
|
|
127
|
+
readonly requireReasoningReplay: () => boolean;
|
|
128
|
+
readonly replay: "generic_stream";
|
|
129
|
+
};
|
|
130
|
+
readonly minimax: {
|
|
131
|
+
readonly id: "minimax";
|
|
132
|
+
readonly providerId: "minimax";
|
|
133
|
+
readonly endpointId: "minimax.openai";
|
|
134
|
+
readonly descriptor: {
|
|
135
|
+
readonly reasoning: {
|
|
136
|
+
supported: boolean;
|
|
137
|
+
preserveAcrossToolTurns: boolean;
|
|
138
|
+
requiresReplayForToolTurns?: boolean;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
|
|
142
|
+
__deepstrikeThinkingEnabled: boolean;
|
|
143
|
+
reasoning_split: boolean;
|
|
144
|
+
degradeMissingReasoningReplay?: boolean | undefined;
|
|
145
|
+
};
|
|
146
|
+
readonly cacheKey: "none";
|
|
147
|
+
readonly inlineThinkingTags: false;
|
|
148
|
+
readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
149
|
+
readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
|
|
150
|
+
readonly replay: "minimax";
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
export type OpenAIChatDialectId = keyof typeof openAIChatDialects;
|
|
154
|
+
export declare function replayForTurn(dialect: OpenAIChatWireDialect, phase: "complete" | "stream", model: string, content: string, toolCalls: ToolCall[], reasoning: OpenAIChatTurnReasoning): ProviderReplay | undefined;
|