@plumbus/core 0.6.10 → 0.6.14
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/CHANGELOG.md +53 -0
- package/README.md +1 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ai/ai/provider.d.ts +79 -6
- package/dist/ai/ai/refusal.d.ts +14 -0
- package/dist/ai/ai-service.d.ts +2 -1
- package/dist/ai/ai-service.d.ts.map +1 -1
- package/dist/ai/ai-service.js +92 -16
- package/dist/ai/ai-service.js.map +1 -1
- package/dist/ai/index.d.ts +3 -2
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +4 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/provider.d.ts +79 -6
- package/dist/ai/provider.d.ts.map +1 -1
- package/dist/ai/provider.js +279 -33
- package/dist/ai/provider.js.map +1 -1
- package/dist/ai/refusal.d.ts +14 -0
- package/dist/ai/refusal.d.ts.map +1 -1
- package/dist/ai/refusal.js +15 -0
- package/dist/ai/refusal.js.map +1 -1
- package/dist/ai/tool-loop.d.ts +58 -0
- package/dist/ai/tool-loop.d.ts.map +1 -0
- package/dist/ai/tool-loop.js +212 -0
- package/dist/ai/tool-loop.js.map +1 -0
- package/dist/ai/types/entity.d.ts +6 -1
- package/dist/cli/commands/agent.js +1 -1
- package/dist/cli/commands/agent.js.map +1 -1
- package/dist/cli/commands/browser-extension.d.ts.map +1 -1
- package/dist/cli/commands/browser-extension.js +6 -1
- package/dist/cli/commands/browser-extension.js.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +14 -0
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +5 -4
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts +8 -3
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +141 -16
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +14 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/voice.d.ts.map +1 -1
- package/dist/cli/commands/voice.js +18 -3
- package/dist/cli/commands/voice.js.map +1 -1
- package/dist/cli/commands/worker.d.ts.map +1 -1
- package/dist/cli/commands/worker.js +14 -0
- package/dist/cli/commands/worker.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-serve-context.js +1 -1
- package/dist/cli/mcp-serve-context.js.map +1 -1
- package/dist/cli/voice-agent-bootstrap.d.ts +2 -0
- package/dist/cli/voice-agent-bootstrap.d.ts.map +1 -1
- package/dist/cli/voice-agent-bootstrap.js +1 -0
- package/dist/cli/voice-agent-bootstrap.js.map +1 -1
- package/dist/cli/voice-serve-context.d.ts +1 -0
- package/dist/cli/voice-serve-context.d.ts.map +1 -1
- package/dist/cli/voice-serve-context.js +19 -2
- package/dist/cli/voice-serve-context.js.map +1 -1
- package/dist/data/repository.d.ts.map +1 -1
- package/dist/data/repository.js +34 -0
- package/dist/data/repository.js.map +1 -1
- package/dist/data/schema-generator.d.ts.map +1 -1
- package/dist/data/schema-generator.js +8 -4
- package/dist/data/schema-generator.js.map +1 -1
- package/dist/define/defineEntity.d.ts +2 -2
- package/dist/define/defineEntity.d.ts.map +1 -1
- package/dist/define/defineEntity.js +2 -1
- package/dist/define/defineEntity.js.map +1 -1
- package/dist/errors-entry.d.ts +14 -0
- package/dist/errors-entry.d.ts.map +1 -0
- package/dist/errors-entry.js +13 -0
- package/dist/errors-entry.js.map +1 -0
- package/dist/execution/capability-executor.d.ts.map +1 -1
- package/dist/execution/capability-executor.js +3 -1
- package/dist/execution/capability-executor.js.map +1 -1
- package/dist/execution/capability-invocation.d.ts +3 -1
- package/dist/execution/capability-invocation.d.ts.map +1 -1
- package/dist/execution/capability-invocation.js +3 -1
- package/dist/execution/capability-invocation.js.map +1 -1
- package/dist/flows/engine.d.ts.map +1 -1
- package/dist/flows/engine.js +51 -13
- package/dist/flows/engine.js.map +1 -1
- package/dist/flows/flow-service.d.ts +6 -1
- package/dist/flows/flow-service.d.ts.map +1 -1
- package/dist/flows/flow-service.js +39 -1
- package/dist/flows/flow-service.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/register-consumers.d.ts +3 -0
- package/dist/runtime/register-consumers.d.ts.map +1 -1
- package/dist/runtime/register-consumers.js +3 -1
- package/dist/runtime/register-consumers.js.map +1 -1
- package/dist/server/bootstrap.d.ts +1 -0
- package/dist/server/bootstrap.d.ts.map +1 -1
- package/dist/server/bootstrap.js +4 -4
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/testing/context.d.ts +8 -2
- package/dist/testing/context.d.ts.map +1 -1
- package/dist/testing/context.js +35 -4
- package/dist/testing/context.js.map +1 -1
- package/dist/types/context.d.ts +107 -17
- package/dist/types/context.d.ts.map +1 -1
- package/dist/types/entity.d.ts +6 -1
- package/dist/types/entity.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/prompt.d.ts +9 -0
- package/dist/types/prompt.d.ts.map +1 -1
- package/dist/worker/bootstrap.js +2 -2
- package/dist/worker/bootstrap.js.map +1 -1
- package/instructions/README.md +1 -1
- package/instructions/ai.md +61 -1
- package/instructions/capabilities.md +1 -1
- package/instructions/cli.md +1 -0
- package/instructions/peer-dependencies.md +17 -2
- package/instructions/prompts.md +4 -1
- package/package.json +7 -3
package/dist/ai/provider.d.ts
CHANGED
|
@@ -1,15 +1,63 @@
|
|
|
1
1
|
import type { AIProviderConfig } from '../types/config.js';
|
|
2
2
|
import { type Kind } from './model-pricing.js';
|
|
3
|
+
export interface AITool {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
/** JSON Schema object root (from zodToProviderJsonSchema(...).schema). */
|
|
7
|
+
parameters: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export type AIToolChoice = 'auto' | 'none' | {
|
|
10
|
+
type: 'function';
|
|
11
|
+
function: {
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/** Discriminated on argumentsStatus. Adapters parse at the boundary (§6.4). */
|
|
16
|
+
export type AIToolCall = {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
argumentsStatus: 'parsed';
|
|
20
|
+
arguments: unknown;
|
|
21
|
+
} | {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
argumentsStatus: 'invalid';
|
|
25
|
+
/** Internal transcript data — MUST NOT reach browser events, audit, or user errors. Bounded. */
|
|
26
|
+
rawArguments: string;
|
|
27
|
+
/** Internal transcript data — MUST NOT reach browser events, audit, or user errors. Bounded. */
|
|
28
|
+
parseError: string;
|
|
29
|
+
};
|
|
30
|
+
export interface AIToolExecutionOptions {
|
|
31
|
+
/** Chat tool rounds MUST set this false (§6.2). */
|
|
32
|
+
parallelToolCalls?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface AIProviderCapabilities {
|
|
35
|
+
tools: boolean;
|
|
36
|
+
streamingTools: boolean;
|
|
37
|
+
parallelToolCalls: boolean;
|
|
38
|
+
parallelToolCallControl: boolean;
|
|
39
|
+
namedToolChoice: boolean;
|
|
40
|
+
}
|
|
3
41
|
/**
|
|
4
42
|
* A single chat-history turn. Used for native multi-turn requests where the
|
|
5
|
-
* model should see real conversation flow
|
|
6
|
-
*
|
|
7
|
-
*
|
|
43
|
+
* model should see real conversation flow. Optional — when omitted, providers
|
|
44
|
+
* fall back to single-user-message mode built from `ProviderRequest.prompt`.
|
|
45
|
+
* The user/assistant variants remain construction-compatible with existing
|
|
46
|
+
* callers; the `tool` variant carries a tool observation back to the model.
|
|
8
47
|
*/
|
|
9
|
-
export
|
|
10
|
-
role: 'user'
|
|
48
|
+
export type ChatMessage = {
|
|
49
|
+
role: 'user';
|
|
11
50
|
content: string;
|
|
12
|
-
}
|
|
51
|
+
} | {
|
|
52
|
+
role: 'assistant';
|
|
53
|
+
content: string;
|
|
54
|
+
toolCalls?: AIToolCall[];
|
|
55
|
+
} | {
|
|
56
|
+
role: 'tool';
|
|
57
|
+
content: string;
|
|
58
|
+
toolCallId: string;
|
|
59
|
+
name: string;
|
|
60
|
+
};
|
|
13
61
|
export interface ProviderRequest {
|
|
14
62
|
/** System prompt / instructions */
|
|
15
63
|
system?: string;
|
|
@@ -34,6 +82,12 @@ export interface ProviderRequest {
|
|
|
34
82
|
temperature?: number;
|
|
35
83
|
/** Max tokens for response */
|
|
36
84
|
maxTokens?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Reasoning effort for models that support it. Sent ONLY when set (OpenAI
|
|
87
|
+
* `reasoning_effort` on o-series / gpt-5 family); adapters for providers
|
|
88
|
+
* without an equivalent parameter ignore it.
|
|
89
|
+
*/
|
|
90
|
+
reasoningEffort?: 'low' | 'medium' | 'high';
|
|
37
91
|
/** Response format hint */
|
|
38
92
|
responseFormat?: 'text' | 'json';
|
|
39
93
|
/** Provider-compatible JSON Schema for strict structured outputs. */
|
|
@@ -57,6 +111,12 @@ export interface ProviderRequest {
|
|
|
57
111
|
* by providers that do not support it.
|
|
58
112
|
*/
|
|
59
113
|
seed?: number;
|
|
114
|
+
/** Provider-native caller tools. When present the adapter forwards them and returns tool calls. */
|
|
115
|
+
tools?: AITool[];
|
|
116
|
+
/** Tool-choice directive forwarded to the provider. */
|
|
117
|
+
toolChoice?: AIToolChoice;
|
|
118
|
+
/** Tool-execution options (e.g. disable parallel tool calls). */
|
|
119
|
+
toolExecution?: AIToolExecutionOptions;
|
|
60
120
|
}
|
|
61
121
|
/** Compose a request's optional user signal with a timeout, returning a single AbortSignal. */
|
|
62
122
|
export declare function combineRequestSignals(userSignal: AbortSignal | undefined, timeoutMs: number): AbortSignal;
|
|
@@ -65,6 +125,8 @@ export interface ProviderResponse {
|
|
|
65
125
|
model: string;
|
|
66
126
|
usage: TokenUsage;
|
|
67
127
|
finishReason: string;
|
|
128
|
+
/** Parsed provider tool calls (present when finishReason is 'tool_calls'/'tool_use'). */
|
|
129
|
+
toolCalls?: AIToolCall[];
|
|
68
130
|
}
|
|
69
131
|
export interface TokenUsage {
|
|
70
132
|
inputTokens: number;
|
|
@@ -83,6 +145,8 @@ export interface ProviderStreamEvent {
|
|
|
83
145
|
usage?: TokenUsage;
|
|
84
146
|
/** Finish reason (for done events) */
|
|
85
147
|
finishReason?: string;
|
|
148
|
+
/** Provider tool calls on a done event (streaming tools; unused by built-in adapters this release). */
|
|
149
|
+
toolCalls?: AIToolCall[];
|
|
86
150
|
/** Error message (for error events) */
|
|
87
151
|
error?: string;
|
|
88
152
|
}
|
|
@@ -142,6 +206,13 @@ export interface ListModelsFilter {
|
|
|
142
206
|
}
|
|
143
207
|
export interface AIProviderAdapter {
|
|
144
208
|
readonly name: string;
|
|
209
|
+
/**
|
|
210
|
+
* Declared provider capabilities. Optional for backward compat with external
|
|
211
|
+
* adapters: an adapter that omits this MUST be treated by callers as
|
|
212
|
+
* declaring every value `false` (§6.2). Both built-in adapters declare it and
|
|
213
|
+
* implement caller tools natively (C11).
|
|
214
|
+
*/
|
|
215
|
+
readonly capabilities?: AIProviderCapabilities;
|
|
145
216
|
/** Send a completion request */
|
|
146
217
|
complete(request: ProviderRequest): Promise<ProviderResponse>;
|
|
147
218
|
/** Stream a completion request, yielding incremental events */
|
|
@@ -193,6 +264,8 @@ export declare class ProviderAPIError extends Error {
|
|
|
193
264
|
});
|
|
194
265
|
}
|
|
195
266
|
export declare function isProviderAPIError(value: unknown): value is ProviderAPIError;
|
|
267
|
+
/** Normalize a provider-specific finish/stop reason into the framework's vocabulary. */
|
|
268
|
+
export declare function normalizeFinishReason(raw: string | undefined): 'stop' | 'length' | 'refusal' | 'tool_calls' | 'other';
|
|
196
269
|
export interface OpenAIAdapterConfig {
|
|
197
270
|
apiKey: string;
|
|
198
271
|
model?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAsC,KAAK,IAAI,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/provider.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ── AI Provider Adapter Interface ──
|
|
2
2
|
// Abstract interface for AI provider adapters (OpenAI, Anthropic, etc.)
|
|
3
3
|
import { allKnownModels, calculateModelCost } from './model-pricing.js';
|
|
4
|
-
import { AIIncompleteOutputError, AIRefusalError } from './refusal.js';
|
|
4
|
+
import { AIIncompleteOutputError, AIInvalidRequestError, AIRefusalError } from './refusal.js';
|
|
5
5
|
/** Compose a request's optional user signal with a timeout, returning a single AbortSignal. */
|
|
6
6
|
export function combineRequestSignals(userSignal, timeoutMs) {
|
|
7
7
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
@@ -194,6 +194,17 @@ function applyOpenAITemperature(body, model, temperature) {
|
|
|
194
194
|
return;
|
|
195
195
|
body.temperature = temperature ?? 0.7;
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* `reasoning_effort` is sent ONLY when explicitly configured — no model
|
|
199
|
+
* detection. A model that rejects the parameter fails loudly (HTTP 400)
|
|
200
|
+
* instead of silently running without reasoning, so a misconfigured prompt
|
|
201
|
+
* is visible immediately.
|
|
202
|
+
*/
|
|
203
|
+
function applyOpenAIReasoningEffort(body, reasoningEffort) {
|
|
204
|
+
if (reasoningEffort) {
|
|
205
|
+
body.reasoning_effort = reasoningEffort;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
197
208
|
function anthropicUsageToTokenUsage(usage) {
|
|
198
209
|
return {
|
|
199
210
|
inputTokens: usage.input_tokens,
|
|
@@ -237,6 +248,197 @@ async function executeRequestWithRetry(args) {
|
|
|
237
248
|
message: `${args.errorLabel} request failed`,
|
|
238
249
|
});
|
|
239
250
|
}
|
|
251
|
+
// ── Provider tool-calling helpers ──
|
|
252
|
+
/** Portable tool-name grammar (C9). The reserved `flow__` prefix is enforced at chat bind time, not here. */
|
|
253
|
+
const CALLER_TOOL_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
254
|
+
/** Validate caller-supplied tool definitions: portable name grammar + uniqueness. */
|
|
255
|
+
function validateCallerTools(tools) {
|
|
256
|
+
const seen = new Set();
|
|
257
|
+
for (const tool of tools) {
|
|
258
|
+
if (!CALLER_TOOL_NAME_PATTERN.test(tool.name)) {
|
|
259
|
+
throw new AIInvalidRequestError({
|
|
260
|
+
reason: 'tool_name_invalid',
|
|
261
|
+
message: `Tool name "${tool.name}" does not match ^[A-Za-z][A-Za-z0-9_-]{0,63}$`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (seen.has(tool.name)) {
|
|
265
|
+
throw new AIInvalidRequestError({
|
|
266
|
+
reason: 'duplicate_tool_name',
|
|
267
|
+
message: `Duplicate tool name "${tool.name}"`,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
seen.add(tool.name);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** Normalize a provider-specific finish/stop reason into the framework's vocabulary. */
|
|
274
|
+
export function normalizeFinishReason(raw) {
|
|
275
|
+
switch (raw) {
|
|
276
|
+
case 'stop':
|
|
277
|
+
case 'end_turn':
|
|
278
|
+
case 'stop_sequence':
|
|
279
|
+
return 'stop';
|
|
280
|
+
case 'length':
|
|
281
|
+
case 'max_tokens':
|
|
282
|
+
return 'length';
|
|
283
|
+
case 'tool_calls':
|
|
284
|
+
case 'tool_use':
|
|
285
|
+
return 'tool_calls';
|
|
286
|
+
case 'refusal':
|
|
287
|
+
case 'content_filter':
|
|
288
|
+
return 'refusal';
|
|
289
|
+
default:
|
|
290
|
+
return 'other';
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/** Guard used by both adapters: caller tools cannot coexist with the tool-transport structured output (§6.6). */
|
|
294
|
+
function assertNoStructuredOutputToolConflict(request) {
|
|
295
|
+
if (request.structuredOutputTransport === 'tool') {
|
|
296
|
+
throw new AIInvalidRequestError({
|
|
297
|
+
reason: 'caller_tools_conflict_with_structured_output_tool',
|
|
298
|
+
message: "Caller tools cannot be combined with structuredOutputTransport: 'tool'.",
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Map framework ChatMessage[] to OpenAI chat-completions messages. */
|
|
303
|
+
function toOpenAIMessages(request) {
|
|
304
|
+
const messages = [];
|
|
305
|
+
if (request.system)
|
|
306
|
+
messages.push({ role: 'system', content: request.system });
|
|
307
|
+
if (request.messages && request.messages.length > 0) {
|
|
308
|
+
for (const msg of request.messages) {
|
|
309
|
+
if (msg.role === 'tool') {
|
|
310
|
+
messages.push({ role: 'tool', tool_call_id: msg.toolCallId, content: msg.content });
|
|
311
|
+
}
|
|
312
|
+
else if (msg.role === 'assistant') {
|
|
313
|
+
const entry = {
|
|
314
|
+
role: 'assistant',
|
|
315
|
+
content: msg.content.length > 0 ? msg.content : null,
|
|
316
|
+
};
|
|
317
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
318
|
+
entry.tool_calls = msg.toolCalls.map((tc) => ({
|
|
319
|
+
id: tc.id,
|
|
320
|
+
type: 'function',
|
|
321
|
+
function: {
|
|
322
|
+
name: tc.name,
|
|
323
|
+
arguments: tc.argumentsStatus === 'parsed'
|
|
324
|
+
? JSON.stringify(tc.arguments ?? {})
|
|
325
|
+
: tc.rawArguments,
|
|
326
|
+
},
|
|
327
|
+
}));
|
|
328
|
+
}
|
|
329
|
+
messages.push(entry);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
messages.push({ role: 'user', content: msg.content });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
messages.push({ role: 'user', content: request.prompt });
|
|
338
|
+
}
|
|
339
|
+
return messages;
|
|
340
|
+
}
|
|
341
|
+
/** Map AIToolChoice to the OpenAI `tool_choice` wire value. */
|
|
342
|
+
function buildOpenAIToolChoice(toolChoice) {
|
|
343
|
+
if (toolChoice === undefined)
|
|
344
|
+
return undefined;
|
|
345
|
+
if (toolChoice === 'auto' || toolChoice === 'none')
|
|
346
|
+
return toolChoice;
|
|
347
|
+
return { type: 'function', function: { name: toolChoice.function.name } };
|
|
348
|
+
}
|
|
349
|
+
/** Parse OpenAI tool_calls into framework AIToolCall records (§6.4). */
|
|
350
|
+
function parseOpenAIToolCalls(toolCalls) {
|
|
351
|
+
if (!toolCalls)
|
|
352
|
+
return [];
|
|
353
|
+
const result = [];
|
|
354
|
+
for (const tc of toolCalls) {
|
|
355
|
+
const name = tc.function?.name;
|
|
356
|
+
if (!name)
|
|
357
|
+
continue;
|
|
358
|
+
const id = tc.id ?? crypto.randomUUID();
|
|
359
|
+
const rawArguments = tc.function?.arguments ?? '';
|
|
360
|
+
try {
|
|
361
|
+
const parsed = rawArguments.trim().length === 0 ? {} : JSON.parse(rawArguments);
|
|
362
|
+
result.push({ id, name, argumentsStatus: 'parsed', arguments: parsed });
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
result.push({
|
|
366
|
+
id,
|
|
367
|
+
name,
|
|
368
|
+
argumentsStatus: 'invalid',
|
|
369
|
+
rawArguments: rawArguments.slice(0, 4096),
|
|
370
|
+
parseError: (err instanceof Error ? err.message : String(err)).slice(0, 512),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
/** Map framework ChatMessage[] to Anthropic messages (tool_use / tool_result blocks). */
|
|
377
|
+
function toAnthropicMessages(request) {
|
|
378
|
+
if (!request.messages || request.messages.length === 0) {
|
|
379
|
+
return [{ role: 'user', content: request.prompt }];
|
|
380
|
+
}
|
|
381
|
+
const out = [];
|
|
382
|
+
for (const msg of request.messages) {
|
|
383
|
+
if (msg.role === 'tool') {
|
|
384
|
+
const block = { type: 'tool_result', tool_use_id: msg.toolCallId, content: msg.content };
|
|
385
|
+
const last = out[out.length - 1];
|
|
386
|
+
if (last && last.role === 'user' && Array.isArray(last.content)) {
|
|
387
|
+
last.content.push(block);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
out.push({ role: 'user', content: [block] });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (msg.role === 'assistant') {
|
|
394
|
+
const blocks = [];
|
|
395
|
+
if (msg.content.length > 0)
|
|
396
|
+
blocks.push({ type: 'text', text: msg.content });
|
|
397
|
+
if (msg.toolCalls) {
|
|
398
|
+
for (const tc of msg.toolCalls) {
|
|
399
|
+
blocks.push({
|
|
400
|
+
type: 'tool_use',
|
|
401
|
+
id: tc.id,
|
|
402
|
+
name: tc.name,
|
|
403
|
+
input: tc.argumentsStatus === 'parsed' ? (tc.arguments ?? {}) : {},
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
out.push({ role: 'assistant', content: blocks.length > 0 ? blocks : msg.content });
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
out.push({ role: 'user', content: msg.content });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
/** Map AIToolChoice + execution options to the Anthropic `tool_choice` wire value. */
|
|
416
|
+
function buildAnthropicToolChoice(toolChoice, toolExecution) {
|
|
417
|
+
const disableParallel = toolExecution?.parallelToolCalls === false;
|
|
418
|
+
if (toolChoice === undefined && !disableParallel)
|
|
419
|
+
return undefined;
|
|
420
|
+
let base;
|
|
421
|
+
if (toolChoice === undefined || toolChoice === 'auto')
|
|
422
|
+
base = { type: 'auto' };
|
|
423
|
+
else if (toolChoice === 'none')
|
|
424
|
+
base = { type: 'none' };
|
|
425
|
+
else
|
|
426
|
+
base = { type: 'tool', name: toolChoice.function.name };
|
|
427
|
+
if (disableParallel && base.type !== 'none')
|
|
428
|
+
base.disable_parallel_tool_use = true;
|
|
429
|
+
return base;
|
|
430
|
+
}
|
|
431
|
+
/** Parse Anthropic tool_use content blocks into framework AIToolCall records. */
|
|
432
|
+
function parseAnthropicToolUse(content) {
|
|
433
|
+
return content
|
|
434
|
+
.filter((c) => c.type === 'tool_use')
|
|
435
|
+
.map((c) => ({
|
|
436
|
+
id: c.id ?? crypto.randomUUID(),
|
|
437
|
+
name: c.name ?? '',
|
|
438
|
+
argumentsStatus: 'parsed',
|
|
439
|
+
arguments: c.input ?? {},
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
240
442
|
export function createOpenAIAdapter(config) {
|
|
241
443
|
const defaultModel = config.model ?? 'gpt-4o';
|
|
242
444
|
const defaultEmbeddingModel = config.embeddingModel ?? 'text-embedding-3-small';
|
|
@@ -244,43 +446,53 @@ export function createOpenAIAdapter(config) {
|
|
|
244
446
|
const defaultTimeout = config.requestTimeout ?? 120_000;
|
|
245
447
|
return {
|
|
246
448
|
name: 'openai',
|
|
449
|
+
capabilities: {
|
|
450
|
+
tools: true,
|
|
451
|
+
streamingTools: false,
|
|
452
|
+
parallelToolCalls: true,
|
|
453
|
+
parallelToolCallControl: true,
|
|
454
|
+
namedToolChoice: true,
|
|
455
|
+
},
|
|
247
456
|
async complete(request) {
|
|
248
457
|
const model = request.model ?? defaultModel;
|
|
249
|
-
const messages =
|
|
250
|
-
if (request.system) {
|
|
251
|
-
messages.push({ role: 'system', content: request.system });
|
|
252
|
-
}
|
|
253
|
-
// Multi-turn path: when `messages` is supplied, append them verbatim
|
|
254
|
-
// (preserving role + order). Single-turn path (legacy): build a single
|
|
255
|
-
// user message from `prompt`.
|
|
256
|
-
if (request.messages && request.messages.length > 0) {
|
|
257
|
-
for (const msg of request.messages) {
|
|
258
|
-
messages.push({ role: msg.role, content: msg.content });
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
messages.push({ role: 'user', content: request.prompt });
|
|
263
|
-
}
|
|
458
|
+
const messages = toOpenAIMessages(request);
|
|
264
459
|
const body = {
|
|
265
460
|
model,
|
|
266
461
|
messages,
|
|
267
462
|
};
|
|
268
463
|
applyOpenAITemperature(body, model, request.temperature);
|
|
464
|
+
applyOpenAIReasoningEffort(body, request.reasoningEffort);
|
|
269
465
|
applyOpenAITokenLimit(body, model, request.maxTokens);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
body.tools = [buildOpenAIStructuredOutputTool(structuredToolSchema)];
|
|
275
|
-
body.tool_choice = {
|
|
466
|
+
if (request.tools && request.tools.length > 0) {
|
|
467
|
+
assertNoStructuredOutputToolConflict(request);
|
|
468
|
+
validateCallerTools(request.tools);
|
|
469
|
+
body.tools = request.tools.map((t) => ({
|
|
276
470
|
type: 'function',
|
|
277
|
-
function: { name:
|
|
278
|
-
};
|
|
471
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
472
|
+
}));
|
|
473
|
+
const callerToolChoice = buildOpenAIToolChoice(request.toolChoice);
|
|
474
|
+
if (callerToolChoice !== undefined)
|
|
475
|
+
body.tool_choice = callerToolChoice;
|
|
476
|
+
if (request.toolExecution?.parallelToolCalls === false) {
|
|
477
|
+
body.parallel_tool_calls = false;
|
|
478
|
+
}
|
|
279
479
|
}
|
|
280
480
|
else {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
481
|
+
const structuredToolSchema = shouldUseOpenAIStructuredTool({ request })
|
|
482
|
+
? request.responseSchema
|
|
483
|
+
: undefined;
|
|
484
|
+
if (structuredToolSchema) {
|
|
485
|
+
body.tools = [buildOpenAIStructuredOutputTool(structuredToolSchema)];
|
|
486
|
+
body.tool_choice = {
|
|
487
|
+
type: 'function',
|
|
488
|
+
function: { name: OPENAI_STRUCTURED_OUTPUT_TOOL_NAME },
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
const responseFormat = buildOpenAIResponseFormat(request);
|
|
493
|
+
if (responseFormat) {
|
|
494
|
+
body.response_format = responseFormat;
|
|
495
|
+
}
|
|
284
496
|
}
|
|
285
497
|
}
|
|
286
498
|
if (typeof request.seed === 'number') {
|
|
@@ -313,6 +525,15 @@ export function createOpenAIAdapter(config) {
|
|
|
313
525
|
usage,
|
|
314
526
|
});
|
|
315
527
|
}
|
|
528
|
+
if (request.tools && request.tools.length > 0 && choice.finish_reason === 'tool_calls') {
|
|
529
|
+
return {
|
|
530
|
+
content,
|
|
531
|
+
model: data.model,
|
|
532
|
+
usage,
|
|
533
|
+
finishReason: 'tool_calls',
|
|
534
|
+
toolCalls: parseOpenAIToolCalls(choice.message.tool_calls),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
316
537
|
// Only treat `finish_reason === 'length'` as a hard failure when the
|
|
317
538
|
// caller asked for structured JSON output. For free-text generation,
|
|
318
539
|
// a `length` finish is a partial-success: return what we have rather
|
|
@@ -360,6 +581,7 @@ export function createOpenAIAdapter(config) {
|
|
|
360
581
|
stream_options: { include_usage: true },
|
|
361
582
|
};
|
|
362
583
|
applyOpenAITemperature(body, model, request.temperature);
|
|
584
|
+
applyOpenAIReasoningEffort(body, request.reasoningEffort);
|
|
363
585
|
applyOpenAITokenLimit(body, model, request.maxTokens);
|
|
364
586
|
const responseFormat = buildOpenAIResponseFormat(request);
|
|
365
587
|
if (responseFormat) {
|
|
@@ -463,12 +685,15 @@ export function createAnthropicAdapter(config) {
|
|
|
463
685
|
const defaultTimeout = config.requestTimeout ?? 120_000;
|
|
464
686
|
return {
|
|
465
687
|
name: 'anthropic',
|
|
688
|
+
capabilities: {
|
|
689
|
+
tools: true,
|
|
690
|
+
streamingTools: false,
|
|
691
|
+
parallelToolCalls: true,
|
|
692
|
+
parallelToolCallControl: true,
|
|
693
|
+
namedToolChoice: true,
|
|
694
|
+
},
|
|
466
695
|
async complete(request) {
|
|
467
|
-
|
|
468
|
-
// Single-turn path: synthesize a single user message from `prompt`.
|
|
469
|
-
const messages = request.messages && request.messages.length > 0
|
|
470
|
-
? request.messages.map((m) => ({ role: m.role, content: m.content }))
|
|
471
|
-
: [{ role: 'user', content: request.prompt }];
|
|
696
|
+
const messages = toAnthropicMessages(request);
|
|
472
697
|
const body = {
|
|
473
698
|
model: request.model ?? defaultModel,
|
|
474
699
|
messages,
|
|
@@ -485,6 +710,18 @@ export function createAnthropicAdapter(config) {
|
|
|
485
710
|
},
|
|
486
711
|
};
|
|
487
712
|
}
|
|
713
|
+
if (request.tools && request.tools.length > 0) {
|
|
714
|
+
assertNoStructuredOutputToolConflict(request);
|
|
715
|
+
validateCallerTools(request.tools);
|
|
716
|
+
body.tools = request.tools.map((t) => ({
|
|
717
|
+
name: t.name,
|
|
718
|
+
description: t.description,
|
|
719
|
+
input_schema: t.parameters,
|
|
720
|
+
}));
|
|
721
|
+
const callerToolChoice = buildAnthropicToolChoice(request.toolChoice, request.toolExecution);
|
|
722
|
+
if (callerToolChoice !== undefined)
|
|
723
|
+
body.tool_choice = callerToolChoice;
|
|
724
|
+
}
|
|
488
725
|
const resp = await executeRequestWithRetry({
|
|
489
726
|
providerName: 'anthropic',
|
|
490
727
|
errorLabel: 'Anthropic API',
|
|
@@ -503,7 +740,7 @@ export function createAnthropicAdapter(config) {
|
|
|
503
740
|
const usage = anthropicUsageToTokenUsage(data.usage);
|
|
504
741
|
const text = data.content
|
|
505
742
|
.filter((c) => c.type === 'text')
|
|
506
|
-
.map((c) => c.text)
|
|
743
|
+
.map((c) => c.text ?? '')
|
|
507
744
|
.join('');
|
|
508
745
|
if (data.stop_reason === 'refusal') {
|
|
509
746
|
throw new AIRefusalError({
|
|
@@ -513,6 +750,15 @@ export function createAnthropicAdapter(config) {
|
|
|
513
750
|
usage,
|
|
514
751
|
});
|
|
515
752
|
}
|
|
753
|
+
if (request.tools && request.tools.length > 0 && data.stop_reason === 'tool_use') {
|
|
754
|
+
return {
|
|
755
|
+
content: text,
|
|
756
|
+
model: data.model,
|
|
757
|
+
usage,
|
|
758
|
+
finishReason: 'tool_use',
|
|
759
|
+
toolCalls: parseAnthropicToolUse(data.content),
|
|
760
|
+
};
|
|
761
|
+
}
|
|
516
762
|
// Mirror the OpenAI adapter: only throw on max_tokens when the caller
|
|
517
763
|
// requested structured JSON output. Free-text completions should return
|
|
518
764
|
// the partial content.
|