@deepstrike/sdk 0.1.11 → 0.1.12
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 +25 -0
- package/dist/agent.d.ts +10 -9
- package/dist/agent.js +64 -12
- package/dist/harness/harness.js +7 -1
- package/dist/index.d.ts +2 -2
- package/dist/kernel.d.ts +5 -2
- package/dist/memory/protocols.d.ts +8 -1
- package/dist/providers/anthropic.d.ts +3 -3
- package/dist/providers/anthropic.js +9 -9
- package/dist/providers/base.d.ts +7 -4
- package/dist/providers/base.js +41 -38
- package/dist/providers/deepseek.d.ts +2 -2
- package/dist/providers/deepseek.js +2 -2
- package/dist/providers/gemini.d.ts +3 -3
- package/dist/providers/gemini.js +8 -13
- package/dist/providers/ollama.d.ts +3 -3
- package/dist/providers/ollama.js +12 -8
- package/dist/providers/openai-chat.d.ts +2 -2
- package/dist/providers/openai-chat.js +6 -4
- package/dist/providers/openai-responses.d.ts +5 -5
- package/dist/providers/openai-responses.js +13 -19
- package/dist/providers/openai.d.ts +3 -3
- package/dist/providers/openai.js +4 -4
- package/dist/providers/qwen.d.ts +3 -3
- package/dist/providers/qwen.js +4 -4
- package/dist/types.d.ts +12 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -50,6 +50,15 @@ const result = await agent.run("What is 17 + 28?")
|
|
|
50
50
|
console.log(result)
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
Same-session conversation continuity is explicit via `sessionId`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
await agent.run("My name is Ada.", undefined, undefined, "chat-1")
|
|
57
|
+
const reply = await agent.run("What is my name?", undefined, undefined, "chat-1")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
By default, the agent keeps session transcripts in memory for the lifetime of that `Agent` instance. Provide a `sessionStore` when the transcript must survive process restarts or be shared across workers.
|
|
61
|
+
|
|
53
62
|
Streaming:
|
|
54
63
|
|
|
55
64
|
```typescript
|
|
@@ -196,6 +205,22 @@ const agent = new Agent(provider, {
|
|
|
196
205
|
const result = await agent.dream("my-agent", Date.now())
|
|
197
206
|
```
|
|
198
207
|
|
|
208
|
+
### SessionStore (same-session transcript continuity)
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import type { SessionStore } from "@deepstrike/sdk"
|
|
212
|
+
|
|
213
|
+
class MySessionStore implements SessionStore {
|
|
214
|
+
async loadSession(sessionId) { return db.sessions.get(sessionId) }
|
|
215
|
+
async saveSession(session) { await db.sessions.put(session.sessionId, session) }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const agent = new Agent(provider, {
|
|
219
|
+
maxTokens: 4096,
|
|
220
|
+
sessionStore: new MySessionStore(),
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
199
224
|
---
|
|
200
225
|
|
|
201
226
|
## Governance
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LLMProvider, StreamEvent } from "./types.js";
|
|
2
2
|
import type { RegisteredTool } from "./tools/index.js";
|
|
3
|
-
import type { DreamStore, DreamResult } from "./memory/protocols.js";
|
|
3
|
+
import type { DreamStore, DreamResult, SessionStore } from "./memory/protocols.js";
|
|
4
4
|
import type { KnowledgeSource } from "./knowledge/source.js";
|
|
5
5
|
import type { SignalSource } from "./signals/types.js";
|
|
6
6
|
export interface AgentOptions {
|
|
@@ -13,12 +13,6 @@ export interface AgentOptions {
|
|
|
13
13
|
* Passed to the kernel's `system` partition before the first LLM call.
|
|
14
14
|
*/
|
|
15
15
|
systemPrompt?: string;
|
|
16
|
-
/**
|
|
17
|
-
* Long-term memory snippets pre-seeded into the context before the first LLM call.
|
|
18
|
-
* Each string is pushed to the kernel's `memory` partition (highest-priority context
|
|
19
|
-
* after system). Use to inject memories retrieved from a DreamStore before a run.
|
|
20
|
-
*/
|
|
21
|
-
initialMemory?: string[];
|
|
22
16
|
/**
|
|
23
17
|
* Directory containing skill `.md` files. The kernel auto-injects a `skill`
|
|
24
18
|
* meta-tool so the model can load any skill by name on demand.
|
|
@@ -28,6 +22,8 @@ export interface AgentOptions {
|
|
|
28
22
|
signalSource?: SignalSource;
|
|
29
23
|
/** Backing store for the idle dreaming pipeline. Required to call `Agent.dream()`. */
|
|
30
24
|
dreamStore?: DreamStore;
|
|
25
|
+
/** Optional durable transcript store for same-session conversational continuity. */
|
|
26
|
+
sessionStore?: SessionStore;
|
|
31
27
|
/**
|
|
32
28
|
* Stable identifier for this agent. Required to enable in-session memory retrieval
|
|
33
29
|
* when `dreamStore` is configured.
|
|
@@ -56,6 +52,7 @@ export declare class Agent {
|
|
|
56
52
|
private knowledgeSource?;
|
|
57
53
|
private signalSource?;
|
|
58
54
|
private dreamStore?;
|
|
55
|
+
private readonly inMemorySessions;
|
|
59
56
|
private interrupted;
|
|
60
57
|
private pendingInterrupt;
|
|
61
58
|
private _turn;
|
|
@@ -72,8 +69,12 @@ export declare class Agent {
|
|
|
72
69
|
* Collect the full text response and return it.
|
|
73
70
|
* For richer control (streaming, tool events, token counts) use `runStreaming`.
|
|
74
71
|
*/
|
|
75
|
-
run(goal: string, criteria?: string[], extensions?: Record<string, unknown
|
|
76
|
-
runStreaming(goal: string, criteria?: string[], extensions?: Record<string, unknown
|
|
72
|
+
run(goal: string, criteria?: string[], extensions?: Record<string, unknown>, sessionId?: string): Promise<string>;
|
|
73
|
+
runStreaming(goal: string, criteria?: string[], extensions?: Record<string, unknown>, sessionId?: string): AsyncIterable<StreamEvent>;
|
|
74
|
+
private loadSession;
|
|
75
|
+
private saveSession;
|
|
76
|
+
private toMessage;
|
|
77
|
+
private kernelMsgToSessionMsg;
|
|
77
78
|
/**
|
|
78
79
|
* Trigger the idle dreaming cycle for this agent.
|
|
79
80
|
* Requires `dreamStore` and `agentId` to be configured.
|
package/dist/agent.js
CHANGED
|
@@ -10,6 +10,7 @@ export class Agent {
|
|
|
10
10
|
knowledgeSource;
|
|
11
11
|
signalSource;
|
|
12
12
|
dreamStore;
|
|
13
|
+
inMemorySessions = new Map();
|
|
13
14
|
interrupted = false;
|
|
14
15
|
pendingInterrupt = false;
|
|
15
16
|
// Live telemetry — updated each runStreaming call
|
|
@@ -44,15 +45,15 @@ export class Agent {
|
|
|
44
45
|
* Collect the full text response and return it.
|
|
45
46
|
* For richer control (streaming, tool events, token counts) use `runStreaming`.
|
|
46
47
|
*/
|
|
47
|
-
async run(goal, criteria, extensions) {
|
|
48
|
+
async run(goal, criteria, extensions, sessionId) {
|
|
48
49
|
let content = "";
|
|
49
|
-
for await (const evt of this.runStreaming(goal, criteria, extensions)) {
|
|
50
|
+
for await (const evt of this.runStreaming(goal, criteria, extensions, sessionId)) {
|
|
50
51
|
if (evt.type === "text_delta")
|
|
51
52
|
content += evt.delta;
|
|
52
53
|
}
|
|
53
54
|
return content;
|
|
54
55
|
}
|
|
55
|
-
async *runStreaming(goal, criteria, extensions) {
|
|
56
|
+
async *runStreaming(goal, criteria, extensions, sessionId) {
|
|
56
57
|
this.interrupted = false;
|
|
57
58
|
this.pendingInterrupt = false;
|
|
58
59
|
this._turn = 0;
|
|
@@ -76,8 +77,10 @@ export class Agent {
|
|
|
76
77
|
const tokens = Math.max(1, Math.ceil(this.options.systemPrompt.length / 4));
|
|
77
78
|
sm.addSystemMessage(this.options.systemPrompt, tokens);
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const previousSession = sessionId ? await this.loadSession(sessionId) : undefined;
|
|
81
|
+
const previousMsgs = previousSession?.messages ?? [];
|
|
82
|
+
if (previousMsgs.length > 0) {
|
|
83
|
+
sm.preloadHistory(previousMsgs.map(m => this.toMessage(m)));
|
|
81
84
|
}
|
|
82
85
|
if (this.skillDir) {
|
|
83
86
|
const skillMetas = await scanSkillDir(this.skillDir);
|
|
@@ -97,7 +100,6 @@ export class Agent {
|
|
|
97
100
|
}
|
|
98
101
|
let action = sm.start({ goal, criteria: criteria ?? [] });
|
|
99
102
|
const sessionStart = Date.now();
|
|
100
|
-
const sessionMsgs = [{ role: "user", content: goal }];
|
|
101
103
|
while (!sm.isTerminal()) {
|
|
102
104
|
// Update telemetry
|
|
103
105
|
this._turn = sm.turn;
|
|
@@ -153,11 +155,11 @@ export class Agent {
|
|
|
153
155
|
if (action.kind === "call_llm") {
|
|
154
156
|
const finalToolCalls = [];
|
|
155
157
|
let finalText = "";
|
|
156
|
-
const
|
|
158
|
+
const context = action.context;
|
|
157
159
|
const tools = (action.tools ?? []);
|
|
158
160
|
let turnTokens = 0;
|
|
159
161
|
try {
|
|
160
|
-
for await (const evt of this.provider.stream(
|
|
162
|
+
for await (const evt of this.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
|
|
161
163
|
if (evt.type === "usage") {
|
|
162
164
|
turnTokens = evt.totalTokens;
|
|
163
165
|
continue;
|
|
@@ -177,7 +179,6 @@ export class Agent {
|
|
|
177
179
|
break;
|
|
178
180
|
}
|
|
179
181
|
action = sm.feedLlmResponse({ role: "assistant", content: finalText, toolCalls: finalToolCalls, tokenCount: turnTokens || undefined });
|
|
180
|
-
sessionMsgs.push({ role: "assistant", content: finalText, toolCalls: finalToolCalls });
|
|
181
182
|
}
|
|
182
183
|
else if (action.kind === "execute_tools") {
|
|
183
184
|
const allCalls = action.calls ?? [];
|
|
@@ -266,12 +267,13 @@ export class Agent {
|
|
|
266
267
|
this._pressure = sm.pressure();
|
|
267
268
|
const status = result?.termination ?? "error";
|
|
268
269
|
const iterations = result ? Math.max(1, result.turnsUsed) : 0;
|
|
269
|
-
|
|
270
|
+
const newMsgs = sm.drainNewMessages().map(m => this.kernelMsgToSessionMsg(m));
|
|
271
|
+
if (this.options.dreamStore && this.options.agentId && newMsgs.length > 0) {
|
|
270
272
|
try {
|
|
271
273
|
await this.options.dreamStore.saveSession({
|
|
272
274
|
sessionId: crypto.randomUUID(),
|
|
273
275
|
agentId: this.options.agentId,
|
|
274
|
-
messages:
|
|
276
|
+
messages: newMsgs,
|
|
275
277
|
metadata: null,
|
|
276
278
|
createdAtMs: sessionStart,
|
|
277
279
|
updatedAtMs: Date.now(),
|
|
@@ -279,6 +281,17 @@ export class Agent {
|
|
|
279
281
|
}
|
|
280
282
|
catch { /* session save failure must not surface to caller */ }
|
|
281
283
|
}
|
|
284
|
+
if (sessionId) {
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
await this.saveSession({
|
|
287
|
+
sessionId,
|
|
288
|
+
agentId: this.options.agentId ?? "default",
|
|
289
|
+
messages: [...previousMsgs, ...newMsgs],
|
|
290
|
+
metadata: previousSession?.metadata ?? null,
|
|
291
|
+
createdAtMs: previousSession?.createdAtMs ?? sessionStart,
|
|
292
|
+
updatedAtMs: now,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
282
295
|
yield {
|
|
283
296
|
type: "done",
|
|
284
297
|
iterations,
|
|
@@ -286,6 +299,36 @@ export class Agent {
|
|
|
286
299
|
status,
|
|
287
300
|
};
|
|
288
301
|
}
|
|
302
|
+
async loadSession(sessionId) {
|
|
303
|
+
return this.options.sessionStore
|
|
304
|
+
? this.options.sessionStore.loadSession(sessionId)
|
|
305
|
+
: this.inMemorySessions.get(sessionId);
|
|
306
|
+
}
|
|
307
|
+
async saveSession(data) {
|
|
308
|
+
if (this.options.sessionStore) {
|
|
309
|
+
await this.options.sessionStore.saveSession(data);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.inMemorySessions.set(data.sessionId, data);
|
|
313
|
+
}
|
|
314
|
+
toMessage(message) {
|
|
315
|
+
return {
|
|
316
|
+
role: message.role,
|
|
317
|
+
content: message.content,
|
|
318
|
+
contentParts: message.contentParts,
|
|
319
|
+
tokenCount: message.tokenCount,
|
|
320
|
+
toolCalls: message.toolCalls ?? [],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
kernelMsgToSessionMsg(msg) {
|
|
324
|
+
return {
|
|
325
|
+
role: msg.role,
|
|
326
|
+
content: msg.content,
|
|
327
|
+
contentParts: msg.contentParts,
|
|
328
|
+
tokenCount: msg.tokenCount,
|
|
329
|
+
toolCalls: msg.toolCalls?.length ? msg.toolCalls : undefined,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
289
332
|
/**
|
|
290
333
|
* Trigger the idle dreaming cycle for this agent.
|
|
291
334
|
* Requires `dreamStore` and `agentId` to be configured.
|
|
@@ -332,7 +375,16 @@ export class Agent {
|
|
|
332
375
|
}
|
|
333
376
|
let synthesisText = "";
|
|
334
377
|
const providerState = this.provider.createRunState?.();
|
|
335
|
-
|
|
378
|
+
// IdlePipeline produces raw messages for synthesis; wrap them in a RenderedContext.
|
|
379
|
+
// The first system message (if any) becomes systemText; the rest are turns.
|
|
380
|
+
const synthMsgs = (action1.messages ?? []);
|
|
381
|
+
const synthSystemMsgs = synthMsgs.filter(m => m.role === "system");
|
|
382
|
+
const synthTurns = synthMsgs.filter(m => m.role !== "system");
|
|
383
|
+
const synthContext = {
|
|
384
|
+
systemText: synthSystemMsgs.map(m => m.content).join("\n\n"),
|
|
385
|
+
turns: synthTurns,
|
|
386
|
+
};
|
|
387
|
+
for await (const evt of this.provider.stream(synthContext, [], undefined, providerState)) {
|
|
336
388
|
if (evt.type === "text_delta")
|
|
337
389
|
synthesisText += evt.delta;
|
|
338
390
|
}
|
package/dist/harness/harness.js
CHANGED
|
@@ -92,7 +92,13 @@ export class HarnessLoop {
|
|
|
92
92
|
if (evalAction.kind !== "evaluate")
|
|
93
93
|
break;
|
|
94
94
|
let evalText = "";
|
|
95
|
-
|
|
95
|
+
// Wrap harness eval messages in a RenderedContext (system messages → systemText, rest → turns).
|
|
96
|
+
const evalMsgs = evalAction.messages ?? [];
|
|
97
|
+
const evalContext = {
|
|
98
|
+
systemText: evalMsgs.filter((m) => m.role === "system").map((m) => m.content).join("\n\n"),
|
|
99
|
+
turns: evalMsgs.filter((m) => m.role !== "system"),
|
|
100
|
+
};
|
|
101
|
+
for await (const evt of this.evalProvider.stream(evalContext, [], undefined)) {
|
|
96
102
|
if (evt.type === "text_delta")
|
|
97
103
|
evalText += evt.delta;
|
|
98
104
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type { RegisteredTool } from "./tools/index.js";
|
|
|
20
20
|
export { scanSkillDir, readSkillFile } from "./skills/loader.js";
|
|
21
21
|
export type { SkillMetadata } from "./skills/loader.js";
|
|
22
22
|
export { WorkingMemory } from "./memory/working.js";
|
|
23
|
-
export type { DreamStore, DreamResult, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, } from "./memory/protocols.js";
|
|
23
|
+
export type { DreamStore, DreamResult, SessionStore, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, } from "./memory/protocols.js";
|
|
24
24
|
export type { KnowledgeSource } from "./knowledge/source.js";
|
|
25
25
|
export { SinglePassHarness, EvalLoopHarness, HarnessLoop } from "./harness/harness.js";
|
|
26
26
|
export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions, QualityGate } from "./harness/harness.js";
|
|
@@ -31,7 +31,7 @@ export { PermissionManager, PermissionMode } from "./safety/permissions.js";
|
|
|
31
31
|
export type { PermissionDecision, Permission } from "./safety/permissions.js";
|
|
32
32
|
export { Governance } from "./governance.js";
|
|
33
33
|
export type { GovernanceVerdict } from "./governance.js";
|
|
34
|
-
export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, LLMProvider, RetryConfig, TokenUsage, ProviderToolSpec, ProviderRunState, } from "./types.js";
|
|
34
|
+
export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, LLMProvider, RetryConfig, TokenUsage, ProviderToolSpec, ProviderRunState, RenderedContext, } from "./types.js";
|
|
35
35
|
export type { AcceptanceCriterion, VerificationContract, ContractCheckResult, } from "./collaboration/contract.js";
|
|
36
36
|
export { ContractBuilder, formatContractForSystemPrompt, contractToCriteriaStrings, } from "./collaboration/contract.js";
|
|
37
37
|
export { AgentPool } from "./collaboration/pool.js";
|
package/dist/kernel.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message, ToolCall, ToolResult, ToolSchema } from "./types.js";
|
|
1
|
+
import type { Message, RenderedContext, ToolCall, ToolResult, ToolSchema } from "./types.js";
|
|
2
2
|
import type { SkillMetadata } from "./skills/loader.js";
|
|
3
3
|
export interface GovernanceVerdict {
|
|
4
4
|
kind: "allow" | "deny" | "rate_limited" | "ask_user";
|
|
@@ -28,7 +28,7 @@ export interface RuntimeSignal {
|
|
|
28
28
|
}
|
|
29
29
|
export interface LoopAction {
|
|
30
30
|
kind: "call_llm" | "execute_tools" | "done";
|
|
31
|
-
|
|
31
|
+
context?: RenderedContext;
|
|
32
32
|
tools?: ToolSchema[];
|
|
33
33
|
calls?: ToolCall[];
|
|
34
34
|
result?: {
|
|
@@ -48,6 +48,7 @@ interface LoopStateMachineInstance {
|
|
|
48
48
|
setKnowledgeEnabled(enabled: boolean): void;
|
|
49
49
|
addSystemMessage(content: string, tokens: number): void;
|
|
50
50
|
addMemoryMessage(content: string, tokens: number): void;
|
|
51
|
+
addHistoryMessage(message: Message, tokens: number): void;
|
|
51
52
|
setTools(tools: ToolSchema[]): void;
|
|
52
53
|
start(task: {
|
|
53
54
|
goal: string;
|
|
@@ -57,6 +58,8 @@ interface LoopStateMachineInstance {
|
|
|
57
58
|
feedToolResults(results: ToolResult[]): LoopAction;
|
|
58
59
|
feedTimeout(): LoopAction;
|
|
59
60
|
isTerminal(): boolean;
|
|
61
|
+
preloadHistory(messages: Message[]): void;
|
|
62
|
+
drainNewMessages(): Message[];
|
|
60
63
|
readonly turn: number;
|
|
61
64
|
pressure(): number;
|
|
62
65
|
takeObservations(): LoopObservation[];
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type { Message } from "../types.js";
|
|
1
|
+
import type { Message, ContentPart } from "../types.js";
|
|
2
2
|
export interface SessionMessage {
|
|
3
3
|
role: Message["role"];
|
|
4
4
|
content: string;
|
|
5
|
+
/** Structured multimodal parts. Preserved for round-trip fidelity (e.g. tool result messages). */
|
|
6
|
+
contentParts?: ContentPart[];
|
|
5
7
|
tokenCount?: number;
|
|
6
8
|
toolCalls?: Array<{
|
|
7
9
|
id: string;
|
|
@@ -43,6 +45,11 @@ export interface DreamStore {
|
|
|
43
45
|
/** Persist a completed session for future consolidation via `Agent.dream()`. */
|
|
44
46
|
saveSession(data: SessionData): Promise<void>;
|
|
45
47
|
}
|
|
48
|
+
/** Durable transcript storage for same-session conversational continuity. */
|
|
49
|
+
export interface SessionStore {
|
|
50
|
+
loadSession(sessionId: string): Promise<SessionData | undefined>;
|
|
51
|
+
saveSession(data: SessionData): Promise<void>;
|
|
52
|
+
}
|
|
46
53
|
export interface DreamResult {
|
|
47
54
|
sessionsProcessed: number;
|
|
48
55
|
insightsExtracted: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
1
|
+
import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
2
2
|
interface AnthropicProviderOptions {
|
|
3
3
|
baseURL?: string;
|
|
4
4
|
authMode?: "api-key" | "bearer";
|
|
@@ -15,8 +15,8 @@ export declare class AnthropicProvider implements LLMProvider {
|
|
|
15
15
|
baseDelay: number;
|
|
16
16
|
}, options?: AnthropicProviderOptions);
|
|
17
17
|
private buildTools;
|
|
18
|
-
complete(
|
|
19
|
-
stream(
|
|
18
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
19
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
20
20
|
private buildMessages;
|
|
21
21
|
private rememberNativeBlocks;
|
|
22
22
|
private assistantReplayKey;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { withServerRuntimeGuard } from "../runtime/server.js";
|
|
3
|
-
import { CircuitBreaker, normalizeToolCall,
|
|
3
|
+
import { CircuitBreaker, normalizeToolCall, toAnthropicMessages } from "./base.js";
|
|
4
4
|
export class AnthropicProvider {
|
|
5
5
|
model;
|
|
6
6
|
client;
|
|
@@ -25,11 +25,11 @@ export class AnthropicProvider {
|
|
|
25
25
|
input_schema: JSON.parse(t.parameters),
|
|
26
26
|
}));
|
|
27
27
|
}
|
|
28
|
-
async complete(
|
|
28
|
+
async complete(context, tools) {
|
|
29
29
|
if (this.circuit.isOpen())
|
|
30
30
|
throw new Error("Circuit breaker open");
|
|
31
|
-
const system =
|
|
32
|
-
const msgs = this.buildMessages(
|
|
31
|
+
const system = context.systemText || undefined;
|
|
32
|
+
const msgs = this.buildMessages(context);
|
|
33
33
|
let lastErr;
|
|
34
34
|
for (let i = 0; i < this.maxRetries; i++) {
|
|
35
35
|
try {
|
|
@@ -65,9 +65,9 @@ export class AnthropicProvider {
|
|
|
65
65
|
}
|
|
66
66
|
throw lastErr;
|
|
67
67
|
}
|
|
68
|
-
async *stream(
|
|
69
|
-
const system =
|
|
70
|
-
const msgs = this.buildMessages(
|
|
68
|
+
async *stream(context, tools, extensions) {
|
|
69
|
+
const system = context.systemText || undefined;
|
|
70
|
+
const msgs = this.buildMessages(context);
|
|
71
71
|
const toolBlocks = {};
|
|
72
72
|
const nativeBlocks = {};
|
|
73
73
|
let finalText = "";
|
|
@@ -121,8 +121,8 @@ export class AnthropicProvider {
|
|
|
121
121
|
}
|
|
122
122
|
this.rememberNativeBlocks({ content: finalText, toolCalls: finalToolCalls }, Object.keys(nativeBlocks).map(Number).sort((a, b) => a - b).map(index => nativeBlocks[index]));
|
|
123
123
|
}
|
|
124
|
-
buildMessages(
|
|
125
|
-
return toAnthropicMessages(
|
|
124
|
+
buildMessages(context) {
|
|
125
|
+
return toAnthropicMessages(context.turns, message => this.nativeAssistantBlocks.get(this.assistantReplayKey(message)));
|
|
126
126
|
}
|
|
127
127
|
rememberNativeBlocks(message, blocks) {
|
|
128
128
|
if (!message.toolCalls?.length)
|
package/dist/providers/base.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Message, RenderedContext } from "../types.js";
|
|
1
2
|
export declare class CircuitBreaker {
|
|
2
3
|
private readonly openAfter;
|
|
3
4
|
private readonly resetAfter;
|
|
@@ -13,9 +14,11 @@ export declare function normalizeToolCall(id: string, name: string, args: unknow
|
|
|
13
14
|
name: string;
|
|
14
15
|
arguments: string;
|
|
15
16
|
} | null;
|
|
16
|
-
import type { Message } from "../types.js";
|
|
17
17
|
export declare function toAnthropicContent(msg: Message): string | Array<Record<string, unknown>>;
|
|
18
|
+
/** Convert RenderedContext.turns to Anthropic messages array.
|
|
19
|
+
* `turns` contains only user / assistant / tool roles — no system filtering needed. */
|
|
20
|
+
export declare function toAnthropicMessages(turns: Message[], nativeReplay?: (message: Message) => Array<Record<string, unknown>> | undefined): Array<Record<string, unknown>>;
|
|
18
21
|
export declare function toOpenAIContent(msg: Message): string | Array<Record<string, unknown>>;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export declare function toOpenAIMessageParams(
|
|
22
|
+
/** Build the full OpenAI messages array from a RenderedContext.
|
|
23
|
+
* Prepends systemText as the first system message, then converts turns. */
|
|
24
|
+
export declare function toOpenAIMessageParams(context: RenderedContext): Array<Record<string, unknown>>;
|
package/dist/providers/base.js
CHANGED
|
@@ -44,6 +44,15 @@ export function normalizeToolCall(id, name, args) {
|
|
|
44
44
|
}
|
|
45
45
|
return { id: String(id ?? ""), name: n, arguments: JSON.stringify(parsed) };
|
|
46
46
|
}
|
|
47
|
+
function parseToolArguments(args) {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(args || "{}");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ─── Anthropic message conversion ────────────────────────────────────────────
|
|
47
56
|
export function toAnthropicContent(msg) {
|
|
48
57
|
if (!msg.contentParts?.length)
|
|
49
58
|
return msg.content;
|
|
@@ -65,39 +74,11 @@ export function toAnthropicContent(msg) {
|
|
|
65
74
|
return { type: "text", text: "" };
|
|
66
75
|
});
|
|
67
76
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return msg.contentParts.map(p => {
|
|
72
|
-
if (p.type === "text")
|
|
73
|
-
return { type: "text", text: p.text };
|
|
74
|
-
if (p.type === "image") {
|
|
75
|
-
const url = p.data ? `data:${p.mediaType ?? "image/png"};base64,${p.data}` : p.url;
|
|
76
|
-
return { type: "image_url", image_url: { url, ...(p.detail ? { detail: p.detail } : {}) } };
|
|
77
|
-
}
|
|
78
|
-
if (p.type === "audio") {
|
|
79
|
-
return { type: "input_audio", input_audio: { data: p.data, format: p.mediaType?.split("/")[1] ?? "wav" } };
|
|
80
|
-
}
|
|
81
|
-
if (p.type === "tool_result") {
|
|
82
|
-
return { type: "text", text: p.output };
|
|
83
|
-
}
|
|
84
|
-
return { type: "text", text: "" };
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
function parseToolArguments(args) {
|
|
88
|
-
try {
|
|
89
|
-
return JSON.parse(args || "{}");
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return {};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
export function splitAnthropicSystem(messages) {
|
|
96
|
-
return messages.filter(m => m.role === "system").map(m => m.content).join("\n\n");
|
|
97
|
-
}
|
|
98
|
-
export function toAnthropicMessages(messages, nativeReplay) {
|
|
77
|
+
/** Convert RenderedContext.turns to Anthropic messages array.
|
|
78
|
+
* `turns` contains only user / assistant / tool roles — no system filtering needed. */
|
|
79
|
+
export function toAnthropicMessages(turns, nativeReplay) {
|
|
99
80
|
const result = [];
|
|
100
|
-
for (const msg of
|
|
81
|
+
for (const msg of turns) {
|
|
101
82
|
if (msg.role === "tool") {
|
|
102
83
|
const parts = (msg.contentParts ?? [])
|
|
103
84
|
.filter((p) => p.type === "tool_result")
|
|
@@ -124,16 +105,38 @@ export function toAnthropicMessages(messages, nativeReplay) {
|
|
|
124
105
|
result.push({ role: "assistant", content: blocks });
|
|
125
106
|
continue;
|
|
126
107
|
}
|
|
127
|
-
result.push({
|
|
128
|
-
role: msg.role,
|
|
129
|
-
content: toAnthropicContent(msg),
|
|
130
|
-
});
|
|
108
|
+
result.push({ role: msg.role, content: toAnthropicContent(msg) });
|
|
131
109
|
}
|
|
132
110
|
return result;
|
|
133
111
|
}
|
|
134
|
-
|
|
112
|
+
// ─── OpenAI-compatible message conversion ────────────────────────────────────
|
|
113
|
+
export function toOpenAIContent(msg) {
|
|
114
|
+
if (!msg.contentParts?.length)
|
|
115
|
+
return msg.content;
|
|
116
|
+
return msg.contentParts.map(p => {
|
|
117
|
+
if (p.type === "text")
|
|
118
|
+
return { type: "text", text: p.text };
|
|
119
|
+
if (p.type === "image") {
|
|
120
|
+
const url = p.data ? `data:${p.mediaType ?? "image/png"};base64,${p.data}` : p.url;
|
|
121
|
+
return { type: "image_url", image_url: { url, ...(p.detail ? { detail: p.detail } : {}) } };
|
|
122
|
+
}
|
|
123
|
+
if (p.type === "audio") {
|
|
124
|
+
return { type: "input_audio", input_audio: { data: p.data, format: p.mediaType?.split("/")[1] ?? "wav" } };
|
|
125
|
+
}
|
|
126
|
+
if (p.type === "tool_result") {
|
|
127
|
+
return { type: "text", text: p.output };
|
|
128
|
+
}
|
|
129
|
+
return { type: "text", text: "" };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Build the full OpenAI messages array from a RenderedContext.
|
|
133
|
+
* Prepends systemText as the first system message, then converts turns. */
|
|
134
|
+
export function toOpenAIMessageParams(context) {
|
|
135
135
|
const result = [];
|
|
136
|
-
|
|
136
|
+
if (context.systemText) {
|
|
137
|
+
result.push({ role: "system", content: context.systemText });
|
|
138
|
+
}
|
|
139
|
+
for (const msg of context.turns) {
|
|
137
140
|
if (msg.role === "tool") {
|
|
138
141
|
const parts = (msg.contentParts ?? [])
|
|
139
142
|
.filter((p) => p.type === "tool_result");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RenderedContext, ToolSchema, StreamEvent } from "../types.js";
|
|
2
2
|
import { OpenAIChatProvider } from "./openai.js";
|
|
3
3
|
export declare class DeepSeekProvider extends OpenAIChatProvider {
|
|
4
4
|
constructor(apiKey: string, model?: "deepseek-v4-flash" | "deepseek-v4-pro", retry?: {
|
|
5
5
|
maxRetries: number;
|
|
6
6
|
baseDelay: number;
|
|
7
7
|
}, baseURL?: string);
|
|
8
|
-
stream(
|
|
8
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
9
9
|
}
|
|
@@ -5,11 +5,11 @@ export class DeepSeekProvider extends OpenAIChatProvider {
|
|
|
5
5
|
constructor(apiKey, model = "deepseek-v4-flash", retry, baseURL = DEEPSEEK_BASE) {
|
|
6
6
|
super(apiKey, model, retry, baseURL);
|
|
7
7
|
}
|
|
8
|
-
async *stream(
|
|
8
|
+
async *stream(context, tools, extensions) {
|
|
9
9
|
const exposeReasoning = extensions?.exposeReasoning ?? false;
|
|
10
10
|
const thinking = extensions?.thinking === false ? "disabled" : "enabled";
|
|
11
11
|
const reasoningEffort = extensions?.reasoningEffort === "max" ? "max" : "high";
|
|
12
|
-
const msgs = this.chat.buildMessages(
|
|
12
|
+
const msgs = this.chat.buildMessages(context);
|
|
13
13
|
const toolCallBufs = {};
|
|
14
14
|
let reasoningContent = "";
|
|
15
15
|
let finalText = "";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
1
|
+
import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
2
2
|
export declare class GeminiProvider implements LLMProvider {
|
|
3
3
|
private readonly model;
|
|
4
4
|
private genAI;
|
|
@@ -9,6 +9,6 @@ export declare class GeminiProvider implements LLMProvider {
|
|
|
9
9
|
maxRetries: number;
|
|
10
10
|
baseDelay: number;
|
|
11
11
|
}, baseURL?: string);
|
|
12
|
-
complete(
|
|
13
|
-
stream(
|
|
12
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
13
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
14
14
|
}
|
package/dist/providers/gemini.js
CHANGED
|
@@ -3,11 +3,9 @@ import { withServerRuntimeGuard } from "../runtime/server.js";
|
|
|
3
3
|
import { CircuitBreaker, normalizeToolCall } from "./base.js";
|
|
4
4
|
import { endpointProfiles } from "./profiles.js";
|
|
5
5
|
const GEMINI_BASE = endpointProfiles["gemini.google"].baseURL;
|
|
6
|
-
function buildContents(
|
|
6
|
+
function buildContents(turns) {
|
|
7
7
|
const contents = [];
|
|
8
|
-
for (const msg of
|
|
9
|
-
if (msg.role === "system")
|
|
10
|
-
continue;
|
|
8
|
+
for (const msg of turns) {
|
|
11
9
|
if (msg.role === "tool") {
|
|
12
10
|
const parts = (msg.contentParts ?? [])
|
|
13
11
|
.filter(p => p.type === "tool_result")
|
|
@@ -50,9 +48,6 @@ function buildTools(tools) {
|
|
|
50
48
|
})),
|
|
51
49
|
}];
|
|
52
50
|
}
|
|
53
|
-
function systemInstruction(messages) {
|
|
54
|
-
return messages.find(m => m.role === "system")?.content;
|
|
55
|
-
}
|
|
56
51
|
export class GeminiProvider {
|
|
57
52
|
model;
|
|
58
53
|
genAI;
|
|
@@ -66,11 +61,11 @@ export class GeminiProvider {
|
|
|
66
61
|
this.maxRetries = retry.maxRetries;
|
|
67
62
|
this.baseDelay = retry.baseDelay;
|
|
68
63
|
}
|
|
69
|
-
async complete(
|
|
64
|
+
async complete(context, tools) {
|
|
70
65
|
if (this.circuit.isOpen())
|
|
71
66
|
throw new Error("Circuit breaker open");
|
|
72
|
-
const system =
|
|
73
|
-
const contents = buildContents(
|
|
67
|
+
const system = context.systemText || undefined;
|
|
68
|
+
const contents = buildContents(context.turns);
|
|
74
69
|
const geminiTools = buildTools(tools);
|
|
75
70
|
let lastErr;
|
|
76
71
|
for (let i = 0; i < this.maxRetries; i++) {
|
|
@@ -111,9 +106,9 @@ export class GeminiProvider {
|
|
|
111
106
|
}
|
|
112
107
|
throw lastErr;
|
|
113
108
|
}
|
|
114
|
-
async *stream(
|
|
115
|
-
const system =
|
|
116
|
-
const contents = buildContents(
|
|
109
|
+
async *stream(context, tools, extensions) {
|
|
110
|
+
const system = context.systemText || undefined;
|
|
111
|
+
const contents = buildContents(context.turns);
|
|
117
112
|
const geminiTools = buildTools(tools);
|
|
118
113
|
const m = this.genAI.getGenerativeModel({
|
|
119
114
|
model: this.model,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
1
|
+
import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
2
2
|
export declare class OllamaProvider implements LLMProvider {
|
|
3
3
|
private readonly model;
|
|
4
4
|
private readonly baseUrl;
|
|
5
5
|
constructor(model?: string, baseUrl?: string);
|
|
6
6
|
private toOllamaMessages;
|
|
7
|
-
complete(
|
|
8
|
-
stream(
|
|
7
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
8
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
9
9
|
}
|
package/dist/providers/ollama.js
CHANGED
|
@@ -6,8 +6,11 @@ export class OllamaProvider {
|
|
|
6
6
|
this.model = model;
|
|
7
7
|
this.baseUrl = baseUrl;
|
|
8
8
|
}
|
|
9
|
-
toOllamaMessages(
|
|
10
|
-
|
|
9
|
+
toOllamaMessages(context) {
|
|
10
|
+
const result = [];
|
|
11
|
+
if (context.systemText)
|
|
12
|
+
result.push({ role: "system", content: context.systemText });
|
|
13
|
+
for (const m of context.turns) {
|
|
11
14
|
const images = [];
|
|
12
15
|
if (m.contentParts?.length) {
|
|
13
16
|
for (const p of m.contentParts) {
|
|
@@ -15,25 +18,26 @@ export class OllamaProvider {
|
|
|
15
18
|
images.push(p.data);
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
|
-
|
|
19
|
-
}
|
|
21
|
+
result.push({ role: m.role, content: m.content, ...(images.length ? { images } : {}) });
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
20
24
|
}
|
|
21
|
-
async complete(
|
|
25
|
+
async complete(context, tools) {
|
|
22
26
|
const resp = await fetch(`${this.baseUrl}/api/chat`, {
|
|
23
27
|
method: "POST",
|
|
24
28
|
headers: { "Content-Type": "application/json" },
|
|
25
|
-
body: JSON.stringify({ model: this.model, messages: this.toOllamaMessages(
|
|
29
|
+
body: JSON.stringify({ model: this.model, messages: this.toOllamaMessages(context), stream: false }),
|
|
26
30
|
});
|
|
27
31
|
if (!resp.ok)
|
|
28
32
|
throw new Error(`Ollama error: ${resp.status}`);
|
|
29
33
|
const data = await resp.json();
|
|
30
34
|
return { role: "assistant", content: data.message.content };
|
|
31
35
|
}
|
|
32
|
-
async *stream(
|
|
36
|
+
async *stream(context, tools, extensions) {
|
|
33
37
|
const resp = await fetch(`${this.baseUrl}/api/chat`, {
|
|
34
38
|
method: "POST",
|
|
35
39
|
headers: { "Content-Type": "application/json" },
|
|
36
|
-
body: JSON.stringify({ model: this.model, messages: this.toOllamaMessages(
|
|
40
|
+
body: JSON.stringify({ model: this.model, messages: this.toOllamaMessages(context), stream: true }),
|
|
37
41
|
});
|
|
38
42
|
if (!resp.ok)
|
|
39
43
|
throw new Error(`Ollama error: ${resp.status}`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type OpenAI from "openai";
|
|
2
|
-
import type { Message, ToolSchema } from "../types.js";
|
|
2
|
+
import type { Message, RenderedContext, ToolSchema } from "../types.js";
|
|
3
3
|
export declare class OpenAIChatAdapter {
|
|
4
4
|
private replayFields;
|
|
5
5
|
buildTools(tools: ToolSchema[]): {
|
|
@@ -10,7 +10,7 @@ export declare class OpenAIChatAdapter {
|
|
|
10
10
|
parameters: any;
|
|
11
11
|
};
|
|
12
12
|
}[];
|
|
13
|
-
buildMessages(
|
|
13
|
+
buildMessages(context: RenderedContext): OpenAI.ChatCompletionMessageParam[];
|
|
14
14
|
normalizeToolCalls(toolCalls?: Array<{
|
|
15
15
|
id: string;
|
|
16
16
|
function: {
|
|
@@ -7,10 +7,12 @@ export class OpenAIChatAdapter {
|
|
|
7
7
|
function: { name: t.name, description: t.description, parameters: JSON.parse(t.parameters) },
|
|
8
8
|
}));
|
|
9
9
|
}
|
|
10
|
-
buildMessages(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
buildMessages(context) {
|
|
11
|
+
// toOpenAIMessageParams prepends systemText as messages[0], then turns.
|
|
12
|
+
const serialized = toOpenAIMessageParams(context);
|
|
13
|
+
// Cursor starts at 1 to skip the system message injected by toOpenAIMessageParams.
|
|
14
|
+
let cursor = context.systemText ? 1 : 0;
|
|
15
|
+
for (const source of context.turns) {
|
|
14
16
|
if (source.role === "tool") {
|
|
15
17
|
cursor += (source.contentParts ?? []).filter(p => p.type === "tool_result").length;
|
|
16
18
|
continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import type { Message, ProviderRunState, StreamEvent, ToolSchema, LLMProvider } from "../types.js";
|
|
2
|
+
import type { Message, ProviderRunState, RenderedContext, StreamEvent, ToolSchema, LLMProvider } from "../types.js";
|
|
3
3
|
import { CircuitBreaker } from "./base.js";
|
|
4
4
|
export interface OpenAIResponsesRunState extends ProviderRunState {
|
|
5
5
|
previousResponseId?: string;
|
|
@@ -12,8 +12,8 @@ export declare class OpenAIResponsesAdapter {
|
|
|
12
12
|
description: string;
|
|
13
13
|
parameters: any;
|
|
14
14
|
}[];
|
|
15
|
-
buildInstructions(
|
|
16
|
-
buildInput(
|
|
15
|
+
buildInstructions(context: RenderedContext): string | undefined;
|
|
16
|
+
buildInput(context: RenderedContext, state?: OpenAIResponsesRunState): Array<Record<string, unknown>>;
|
|
17
17
|
decodeOutput(output: Array<Record<string, unknown>>): {
|
|
18
18
|
content: string;
|
|
19
19
|
toolCalls: Array<{
|
|
@@ -36,7 +36,7 @@ export declare class OpenAIResponsesProvider implements LLMProvider {
|
|
|
36
36
|
baseDelay: number;
|
|
37
37
|
}, baseURL?: string);
|
|
38
38
|
createRunState(): OpenAIResponsesRunState;
|
|
39
|
-
complete(
|
|
40
|
-
stream(
|
|
39
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
40
|
+
stream(context: RenderedContext, tools: ToolSchema[], _extensions?: Record<string, unknown>, state?: ProviderRunState): AsyncIterable<StreamEvent>;
|
|
41
41
|
private asRunState;
|
|
42
42
|
}
|
|
@@ -11,22 +11,16 @@ export class OpenAIResponsesAdapter {
|
|
|
11
11
|
parameters: JSON.parse(t.parameters),
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
|
-
buildInstructions(
|
|
15
|
-
|
|
16
|
-
.filter(message => message.role === "system")
|
|
17
|
-
.map(message => message.content)
|
|
18
|
-
.filter(Boolean);
|
|
19
|
-
return instructions.length ? instructions.join("\n\n") : undefined;
|
|
14
|
+
buildInstructions(context) {
|
|
15
|
+
return context.systemText || undefined;
|
|
20
16
|
}
|
|
21
|
-
buildInput(
|
|
17
|
+
buildInput(context, state) {
|
|
22
18
|
const input = [];
|
|
19
|
+
const turns = context.turns;
|
|
23
20
|
const uncoveredMessages = state?.previousResponseId
|
|
24
|
-
?
|
|
25
|
-
:
|
|
21
|
+
? turns.slice(state.coveredMessageCount)
|
|
22
|
+
: turns;
|
|
26
23
|
for (const message of uncoveredMessages) {
|
|
27
|
-
if (message.role === "system") {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
24
|
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
31
25
|
if (message.content || message.contentParts?.length) {
|
|
32
26
|
input.push({
|
|
@@ -122,16 +116,16 @@ export class OpenAIResponsesProvider {
|
|
|
122
116
|
createRunState() {
|
|
123
117
|
return { coveredMessageCount: 0 };
|
|
124
118
|
}
|
|
125
|
-
async complete(
|
|
119
|
+
async complete(context, tools) {
|
|
126
120
|
if (this.circuit.isOpen())
|
|
127
121
|
throw new Error("Circuit breaker open");
|
|
128
122
|
let lastErr;
|
|
129
123
|
for (let i = 0; i < this.maxRetries; i++) {
|
|
130
124
|
try {
|
|
131
|
-
const instructions = this.responses.buildInstructions(
|
|
125
|
+
const instructions = this.responses.buildInstructions(context);
|
|
132
126
|
const resp = await this.client.responses.create({
|
|
133
127
|
model: this.model,
|
|
134
|
-
input: this.responses.buildInput(
|
|
128
|
+
input: this.responses.buildInput(context),
|
|
135
129
|
...(instructions ? { instructions } : {}),
|
|
136
130
|
...(tools.length ? { tools: this.responses.buildTools(tools) } : {}),
|
|
137
131
|
});
|
|
@@ -153,13 +147,13 @@ export class OpenAIResponsesProvider {
|
|
|
153
147
|
}
|
|
154
148
|
throw lastErr;
|
|
155
149
|
}
|
|
156
|
-
async *stream(
|
|
150
|
+
async *stream(context, tools, _extensions, state) {
|
|
157
151
|
const runState = this.asRunState(state);
|
|
158
152
|
const functionCalls = new Map();
|
|
159
|
-
const instructions = this.responses.buildInstructions(
|
|
153
|
+
const instructions = this.responses.buildInstructions(context);
|
|
160
154
|
const stream = await this.client.responses.create({
|
|
161
155
|
model: this.model,
|
|
162
|
-
input: this.responses.buildInput(
|
|
156
|
+
input: this.responses.buildInput(context, runState),
|
|
163
157
|
...(instructions ? { instructions } : {}),
|
|
164
158
|
...(runState.previousResponseId ? { previous_response_id: runState.previousResponseId } : {}),
|
|
165
159
|
...(tools.length ? { tools: this.responses.buildTools(tools) } : {}),
|
|
@@ -203,7 +197,7 @@ export class OpenAIResponsesProvider {
|
|
|
203
197
|
}
|
|
204
198
|
else if (evt.type === "response.completed") {
|
|
205
199
|
runState.previousResponseId = evt.response.id;
|
|
206
|
-
runState.coveredMessageCount =
|
|
200
|
+
runState.coveredMessageCount = context.turns.length + 1;
|
|
207
201
|
if (evt.response.usage?.total_tokens) {
|
|
208
202
|
yield { type: "usage", totalTokens: evt.response.usage.total_tokens };
|
|
209
203
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
2
|
+
import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
|
|
3
3
|
import { CircuitBreaker } from "./base.js";
|
|
4
4
|
import { OpenAIChatAdapter } from "./openai-chat.js";
|
|
5
5
|
export declare class OpenAIChatProvider implements LLMProvider {
|
|
@@ -13,7 +13,7 @@ export declare class OpenAIChatProvider implements LLMProvider {
|
|
|
13
13
|
maxRetries: number;
|
|
14
14
|
baseDelay: number;
|
|
15
15
|
}, baseURL?: string);
|
|
16
|
-
complete(
|
|
17
|
-
stream(
|
|
16
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
17
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
18
18
|
}
|
|
19
19
|
export { OpenAIChatProvider as OpenAIProvider };
|
package/dist/providers/openai.js
CHANGED
|
@@ -16,10 +16,10 @@ export class OpenAIChatProvider {
|
|
|
16
16
|
this.maxRetries = retry.maxRetries;
|
|
17
17
|
this.baseDelay = retry.baseDelay;
|
|
18
18
|
}
|
|
19
|
-
async complete(
|
|
19
|
+
async complete(context, tools) {
|
|
20
20
|
if (this.circuit.isOpen())
|
|
21
21
|
throw new Error("Circuit breaker open");
|
|
22
|
-
const msgs = this.chat.buildMessages(
|
|
22
|
+
const msgs = this.chat.buildMessages(context);
|
|
23
23
|
let lastErr;
|
|
24
24
|
for (let i = 0; i < this.maxRetries; i++) {
|
|
25
25
|
try {
|
|
@@ -42,8 +42,8 @@ export class OpenAIChatProvider {
|
|
|
42
42
|
}
|
|
43
43
|
throw lastErr;
|
|
44
44
|
}
|
|
45
|
-
async *stream(
|
|
46
|
-
const msgs = this.chat.buildMessages(
|
|
45
|
+
async *stream(context, tools, extensions) {
|
|
46
|
+
const msgs = this.chat.buildMessages(context);
|
|
47
47
|
const toolCallBufs = {};
|
|
48
48
|
const stream = await this.client.chat.completions.create({
|
|
49
49
|
model: this.model,
|
package/dist/providers/qwen.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import type { LLMProvider, Message, StreamEvent, ToolSchema } from "../types.js";
|
|
2
|
+
import type { LLMProvider, Message, RenderedContext, StreamEvent, ToolSchema } from "../types.js";
|
|
3
3
|
import { CircuitBreaker } from "./base.js";
|
|
4
4
|
import { OpenAIChatAdapter } from "./openai-chat.js";
|
|
5
5
|
export declare class QwenProvider implements LLMProvider {
|
|
@@ -13,6 +13,6 @@ export declare class QwenProvider implements LLMProvider {
|
|
|
13
13
|
maxRetries: number;
|
|
14
14
|
baseDelay: number;
|
|
15
15
|
}, baseURL?: string);
|
|
16
|
-
complete(
|
|
17
|
-
stream(
|
|
16
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
17
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
18
18
|
}
|
package/dist/providers/qwen.js
CHANGED
|
@@ -18,10 +18,10 @@ export class QwenProvider {
|
|
|
18
18
|
this.maxRetries = retry.maxRetries;
|
|
19
19
|
this.baseDelay = retry.baseDelay;
|
|
20
20
|
}
|
|
21
|
-
async complete(
|
|
21
|
+
async complete(context, tools) {
|
|
22
22
|
if (this.circuit.isOpen())
|
|
23
23
|
throw new Error("Circuit breaker open");
|
|
24
|
-
const msgs = this.chat.buildMessages(
|
|
24
|
+
const msgs = this.chat.buildMessages(context);
|
|
25
25
|
let lastErr;
|
|
26
26
|
for (let i = 0; i < this.maxRetries; i++) {
|
|
27
27
|
try {
|
|
@@ -44,10 +44,10 @@ export class QwenProvider {
|
|
|
44
44
|
}
|
|
45
45
|
throw lastErr;
|
|
46
46
|
}
|
|
47
|
-
async *stream(
|
|
47
|
+
async *stream(context, tools, extensions) {
|
|
48
48
|
const enableThinking = Boolean(extensions?.enableThinking ?? extensions?.enable_thinking);
|
|
49
49
|
const thinkingBudget = extensions?.thinkingBudget ?? extensions?.thinking_budget;
|
|
50
|
-
const msgs = this.chat.buildMessages(
|
|
50
|
+
const msgs = this.chat.buildMessages(context);
|
|
51
51
|
const toolCallBufs = {};
|
|
52
52
|
const stream = await this.client.chat.completions.create({
|
|
53
53
|
model: this.model,
|
package/dist/types.d.ts
CHANGED
|
@@ -117,8 +117,18 @@ export interface RetryConfig {
|
|
|
117
117
|
* Responses `previous_response_id` without leaking those semantics into the kernel.
|
|
118
118
|
*/
|
|
119
119
|
export type ProviderRunState = Record<string, unknown>;
|
|
120
|
+
/** Structured render output produced by the kernel for each LLM call. */
|
|
121
|
+
export interface RenderedContext {
|
|
122
|
+
/** Combined system text: system partition + dashboard (when non-empty).
|
|
123
|
+
* Anthropic → `system` param · OpenAI → messages[0] system role ·
|
|
124
|
+
* Gemini → `systemInstruction`. */
|
|
125
|
+
systemText: string;
|
|
126
|
+
/** Strictly alternating user / assistant / tool turns.
|
|
127
|
+
* Working-partition signals are already folded into the first user turn. */
|
|
128
|
+
turns: Message[];
|
|
129
|
+
}
|
|
120
130
|
export interface LLMProvider {
|
|
121
131
|
createRunState?(): ProviderRunState;
|
|
122
|
-
complete(
|
|
123
|
-
stream(
|
|
132
|
+
complete(context: RenderedContext, tools: ToolSchema[]): Promise<Message>;
|
|
133
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState): AsyncIterable<StreamEvent>;
|
|
124
134
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "DeepStrike Node.js SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
19
|
-
"@deepstrike/core": "0.1.
|
|
19
|
+
"@deepstrike/core": "0.1.12",
|
|
20
20
|
"@google/generative-ai": "^0.24.1",
|
|
21
21
|
"openai": "^4.77.0"
|
|
22
22
|
},
|