theorum 0.1.14 → 1.0.0
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 +178 -89
- package/esm/mod.d.ts +21 -11
- package/esm/mod.js +15 -8
- package/esm/src/cli/commands/bench.js +6 -3
- package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
- package/esm/src/cli/commands/fuzz-canary.js +195 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
- package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
- package/esm/src/cli/commands/profile.js +25 -14
- package/esm/src/cli/commands/run.d.ts +3 -0
- package/esm/src/cli/commands/run.js +22 -30
- package/esm/src/cli/commands/test.d.ts +10 -1
- package/esm/src/cli/commands/test.js +31 -31
- package/esm/src/cli/event-log.d.ts +19 -0
- package/esm/src/cli/event-log.js +147 -0
- package/esm/src/cli/index.js +45 -11
- package/esm/src/cli/matrix/synthesizer.d.ts +7 -9
- package/esm/src/cli/matrix/synthesizer.js +32 -106
- package/esm/src/guardrails/canary-gate.d.ts +20 -0
- package/esm/src/guardrails/canary-gate.js +31 -0
- package/esm/src/guardrails/canary.d.ts +26 -0
- package/esm/src/guardrails/canary.js +139 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +16 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +216 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +124 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
- package/esm/src/guardrails/corpus/live-attacks.js +232 -0
- package/esm/src/guardrails/corpus/mod.d.ts +13 -0
- package/esm/src/guardrails/corpus/mod.js +11 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +16 -0
- package/esm/src/guardrails/corpus/secrets.js +16 -0
- package/esm/src/guardrails/corpus/strings.d.ts +27 -0
- package/esm/src/guardrails/corpus/strings.js +33 -0
- package/esm/src/guardrails/corpus/types.d.ts +37 -0
- package/esm/src/guardrails/corpus/types.js +6 -0
- package/esm/src/guardrails/egress.d.ts +9 -0
- package/esm/src/guardrails/egress.js +36 -0
- package/esm/src/guardrails/error.js +6 -4
- package/esm/src/guardrails/injection.js +28 -5
- package/esm/src/guardrails/live-outbound-gate.d.ts +39 -0
- package/esm/src/guardrails/live-outbound-gate.js +180 -0
- package/esm/src/guardrails/mod.d.ts +13 -4
- package/esm/src/guardrails/mod.js +10 -4
- package/esm/src/guardrails/quota.js +1 -1
- package/esm/src/guardrails/sanitize.d.ts +11 -15
- package/esm/src/guardrails/sanitize.js +40 -49
- package/esm/src/guardrails/sensitive.js +2 -1
- package/esm/src/guardrails/testing.d.ts +10 -0
- package/esm/src/guardrails/testing.js +9 -0
- package/esm/src/host/client-turn.d.ts +19 -0
- package/esm/src/host/client-turn.js +32 -0
- package/esm/src/host/mod.d.ts +5 -3
- package/esm/src/host/mod.js +4 -3
- package/esm/src/interface/blocks.d.ts +20 -0
- package/esm/src/interface/blocks.js +180 -0
- package/esm/src/interface/from-profile.d.ts +14 -0
- package/esm/src/interface/from-profile.js +104 -0
- package/esm/src/interface/inputs.d.ts +8 -0
- package/esm/src/interface/inputs.js +127 -0
- package/esm/src/interface/mod.d.ts +10 -0
- package/esm/src/interface/mod.js +9 -0
- package/esm/src/interface/types.d.ts +153 -0
- package/esm/src/interface/types.js +13 -0
- package/esm/src/kernel/engine/delta.d.ts +24 -2
- package/esm/src/kernel/engine/delta.js +519 -29
- package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
- package/esm/src/kernel/engine/live-inbound.js +17 -0
- package/esm/src/kernel/engine/repair.js +1 -1
- package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
- package/esm/src/kernel/engine/runner/gates.js +7 -7
- package/esm/src/kernel/engine/runner/mod.js +13 -13
- package/esm/src/kernel/engine/runner/state.d.ts +7 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
- package/esm/src/kernel/engine/runner/steps.js +171 -37
- package/esm/src/kernel/engine/runner/stream.d.ts +1 -2
- package/esm/src/kernel/engine/runner/stream.js +66 -29
- package/esm/src/kernel/engine/session/mod.d.ts +23 -0
- package/esm/src/kernel/engine/session/mod.js +205 -0
- package/esm/src/kernel/mod.d.ts +9 -10
- package/esm/src/kernel/mod.js +6 -8
- package/esm/src/kernel/registry/attachments.d.ts +4 -2
- package/esm/src/kernel/registry/attachments.js +6 -3
- package/esm/src/kernel/registry/catalog.d.ts +3 -16
- package/esm/src/kernel/registry/catalog.js +5 -81
- package/esm/src/kernel/registry/ingress.d.ts +9 -4
- package/esm/src/kernel/registry/ingress.js +73 -63
- package/esm/src/kernel/registry/profiles.d.ts +35 -12
- package/esm/src/kernel/registry/profiles.js +159 -66
- package/esm/src/kernel/registry/provider-request.js +11 -7
- package/esm/src/kernel/registry/resolve.d.ts +5 -6
- package/esm/src/kernel/registry/resolve.js +100 -70
- package/esm/src/kernel/registry/vault.d.ts +12 -7
- package/esm/src/kernel/registry/vault.js +30 -8
- package/esm/src/kernel/schema.d.ts +145 -0
- package/esm/src/kernel/schema.js +462 -0
- package/esm/src/kernel/stop.d.ts +15 -10
- package/esm/src/kernel/stop.js +5 -5
- package/esm/src/kernel/tools/execute.d.ts +45 -0
- package/esm/src/kernel/tools/execute.js +363 -0
- package/esm/src/kernel/tools/harness.d.ts +8 -0
- package/esm/src/kernel/tools/harness.js +44 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +83 -0
- package/esm/src/kernel/tools/mod.d.ts +11 -0
- package/esm/src/kernel/tools/mod.js +10 -0
- package/esm/src/kernel/tools/project.d.ts +12 -0
- package/esm/src/kernel/tools/project.js +34 -0
- package/esm/src/kernel/tools/registry.d.ts +23 -0
- package/esm/src/kernel/tools/registry.js +71 -0
- package/esm/src/kernel/tools/resolve.d.ts +29 -0
- package/esm/src/kernel/tools/resolve.js +262 -0
- package/esm/src/kernel/tools/schema.d.ts +13 -0
- package/esm/src/kernel/tools/schema.js +159 -0
- package/esm/src/kernel/tools/types.d.ts +216 -0
- package/esm/src/kernel/tools/types.js +9 -0
- package/esm/src/kernel/types.d.ts +338 -193
- package/esm/src/observability/trace-attach.d.ts +6 -4
- package/esm/src/observability/trace-attach.js +39 -25
- package/esm/src/observability/trace-record.d.ts +11 -9
- package/esm/src/observability/trace-record.js +36 -23
- package/esm/src/observability/trace-usage.d.ts +10 -3
- package/esm/src/observability/trace-usage.js +70 -17
- package/esm/src/observability/trace.d.ts +9 -1
- package/esm/src/observability/trace.js +11 -3
- package/esm/src/presets/google/speech-voices.d.ts +11 -0
- package/esm/src/presets/google/speech-voices.js +41 -0
- package/esm/src/presets/google.d.ts +36 -24
- package/esm/src/presets/google.js +50 -63
- package/esm/src/presets/mod.d.ts +2 -2
- package/esm/src/presets/mod.js +1 -1
- package/esm/src/providers/create-provider.d.ts +14 -14
- package/esm/src/providers/create-provider.js +55 -23
- package/esm/src/providers/google/interactions/framing.d.ts +15 -0
- package/esm/src/providers/google/interactions/framing.js +208 -0
- package/esm/src/providers/google/interactions/mod.d.ts +7 -0
- package/esm/src/providers/google/interactions/mod.js +7 -0
- package/esm/src/providers/google/interactions/stream.d.ts +83 -0
- package/esm/src/providers/google/interactions/stream.js +588 -0
- package/esm/src/providers/google/keys.d.ts +26 -0
- package/esm/src/providers/{keys.js → google/keys.js} +19 -31
- package/esm/src/providers/google/live/framing.d.ts +46 -0
- package/esm/src/providers/google/live/framing.js +515 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
- package/esm/src/providers/google/live/openapi-schema.js +45 -0
- package/esm/src/providers/google/live/session.d.ts +25 -0
- package/esm/src/providers/google/live/session.js +134 -0
- package/esm/src/providers/google/live/stream.d.ts +36 -0
- package/esm/src/providers/google/live/stream.js +201 -0
- package/esm/src/providers/google/urls.d.ts +6 -0
- package/esm/src/providers/google/urls.js +6 -0
- package/esm/src/providers/local/local.d.ts +30 -0
- package/esm/src/providers/{local.js → local/local.js} +66 -126
- package/esm/src/providers/local/mod.d.ts +9 -0
- package/esm/src/providers/local/mod.js +9 -0
- package/esm/src/providers/mod.d.ts +6 -3
- package/esm/src/providers/mod.js +3 -1
- package/esm/src/providers/openrouter/chat.d.ts +87 -0
- package/esm/src/providers/{openrouter.js → openrouter/chat.js} +70 -229
- package/esm/src/providers/openrouter/image.d.ts +34 -0
- package/esm/src/providers/openrouter/image.js +286 -0
- package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
- package/esm/src/providers/openrouter/openai/chat-payload.js +60 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +47 -0
- package/esm/src/providers/openrouter/openai/compat.js +194 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +84 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +95 -0
- package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
- package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
- package/esm/src/providers/openrouter/speech.d.ts +23 -0
- package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
- package/esm/src/providers/probe.d.ts +1 -0
- package/esm/src/providers/probe.js +22 -0
- package/esm/src/providers/shared/pcm.d.ts +12 -0
- package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
- package/esm/src/providers/shared/sse.d.ts +18 -0
- package/esm/src/providers/shared/sse.js +87 -0
- package/esm/src/providers/shared/tool-args.d.ts +17 -0
- package/esm/src/providers/shared/tool-args.js +45 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
- package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
- package/esm/src/providers/types.d.ts +27 -0
- package/esm/src/providers/types.js +1 -0
- package/package.json +11 -7
- package/docs/cli.md +0 -97
- package/docs/guardrails.md +0 -178
- package/docs/host.md +0 -97
- package/docs/kernel.md +0 -404
- package/docs/observability.md +0 -105
- package/docs/openrouter.md +0 -125
- package/docs/presets-google.md +0 -91
- package/docs/presets.md +0 -88
- package/docs/providers.md +0 -201
- package/docs/streaming.md +0 -96
- package/esm/src/kernel/engine/boundary.d.ts +0 -10
- package/esm/src/kernel/engine/boundary.js +0 -55
- package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
- package/esm/src/kernel/engine/runner/tools.js +0 -198
- package/esm/src/kernel/registry/tools.d.ts +0 -12
- package/esm/src/kernel/registry/tools.js +0 -36
- package/esm/src/providers/expose-for-tests.d.ts +0 -1
- package/esm/src/providers/expose-for-tests.js +0 -25
- package/esm/src/providers/gemini-tape.d.ts +0 -2
- package/esm/src/providers/google-tap.d.ts +0 -3
- package/esm/src/providers/interactions.d.ts +0 -5
- package/esm/src/providers/interactions.js +0 -169
- package/esm/src/providers/keys.d.ts +0 -19
- package/esm/src/providers/local.d.ts +0 -29
- package/esm/src/providers/openrouter-mod.d.ts +0 -13
- package/esm/src/providers/openrouter-mod.js +0 -12
- package/esm/src/providers/openrouter-payload.d.ts +0 -39
- package/esm/src/providers/openrouter-payload.js +0 -195
- package/esm/src/providers/openrouter.d.ts +0 -15
- package/esm/src/providers/pcm.d.ts +0 -7
- package/esm/src/providers/provider.d.ts +0 -15
- package/esm/src/providers/provider.js +0 -176
- package/esm/src/providers/speech.d.ts +0 -23
- package/esm/src/providers/sse.d.ts +0 -7
- package/esm/src/providers/sse.js +0 -55
- package/esm/src/streaming/mod.d.ts +0 -9
- package/esm/src/streaming/mod.js +0 -8
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
|
@@ -3,25 +3,26 @@
|
|
|
3
3
|
*
|
|
4
4
|
* THEORUM keeps the public `ModelProvider` and `TurnEvent` contract; AI SDK
|
|
5
5
|
* owns the OpenRouter call, stream parsing, provider compatibility, and tool
|
|
6
|
-
* call normalization.
|
|
6
|
+
* call normalization. Message assembly delegates to `openai/sdk-messages.ts`.
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
11
|
-
import { jsonSchema, streamText, tool
|
|
12
|
-
import { isAbortError, toErrorEvent } from '
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
11
|
+
import { jsonSchema, streamText, tool } from 'ai';
|
|
12
|
+
import { isAbortError, TheorumError, toErrorEvent } from '../../guardrails/error.js';
|
|
13
|
+
import { parseStructuredOutput } from '../../kernel/engine/delta.js';
|
|
14
|
+
import { turnStopFromOpenAiFinishReason } from '../../kernel/stop.js';
|
|
15
|
+
import { resolveOpenRouterPlugins } from './openai/chat-payload.js';
|
|
16
|
+
import { openAiGatewayHeaders, resolveResponseFormat } from './openai/compat.js';
|
|
17
|
+
import { buildAiSdkMessages } from './openai/sdk-messages.js';
|
|
18
|
+
import { resolveOpenAiGatewayApiKey } from './resolve-api-key.js';
|
|
19
|
+
export function trimApiKey(explicitKey) {
|
|
19
20
|
if (explicitKey?.trim()) {
|
|
20
21
|
return explicitKey.trim();
|
|
21
22
|
}
|
|
22
23
|
return undefined;
|
|
23
24
|
}
|
|
24
|
-
function createAccumulator() {
|
|
25
|
+
export function createAccumulator() {
|
|
25
26
|
return {
|
|
26
27
|
text: '',
|
|
27
28
|
evidenceSeen: false,
|
|
@@ -29,127 +30,35 @@ function createAccumulator() {
|
|
|
29
30
|
errored: false,
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
if (part.type === 'text') {
|
|
34
|
-
return { type: 'text', text: part.text };
|
|
35
|
-
}
|
|
36
|
-
if (part.type === 'image') {
|
|
37
|
-
return {
|
|
38
|
-
type: 'image',
|
|
39
|
-
image: `data:${part.mimeType};base64,${part.data}`,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
return { type: 'file', mediaType: part.mimeType, data: part.data };
|
|
43
|
-
}
|
|
44
|
-
function contentFromParts(parts) {
|
|
45
|
-
if (parts.every((part) => part.type === 'text')) {
|
|
46
|
-
return parts
|
|
47
|
-
.map((part) => (part.type === 'text' ? part.text : ''))
|
|
48
|
-
.filter(Boolean)
|
|
49
|
-
.join('\n');
|
|
50
|
-
}
|
|
51
|
-
return parts.map(mediaPart);
|
|
52
|
-
}
|
|
53
|
-
function parseToolInput(raw) {
|
|
54
|
-
try {
|
|
55
|
-
return JSON.parse(raw);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return { _raw: raw };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function stringDefault(value, fallback) {
|
|
62
|
-
return value === undefined ? fallback : value;
|
|
63
|
-
}
|
|
64
|
-
function fallbackToolCallId(name) {
|
|
65
|
-
return `call_${stringDefault(name, 'tool')}`;
|
|
66
|
-
}
|
|
67
|
-
function toolResultContent(msg) {
|
|
68
|
-
const toolName = stringDefault(msg.name, 'tool');
|
|
69
|
-
return {
|
|
70
|
-
role: 'tool',
|
|
71
|
-
content: [
|
|
72
|
-
{
|
|
73
|
-
type: 'tool-result',
|
|
74
|
-
toolCallId: stringDefault(msg.tool_call_id, fallbackToolCallId(msg.name)),
|
|
75
|
-
toolName,
|
|
76
|
-
output: { type: 'text', value: stringDefault(msg.content, '') },
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function assistantToolCallContent(msg) {
|
|
82
|
-
if (!msg.tool_calls || msg.tool_calls.length === 0) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
role: 'assistant',
|
|
87
|
-
content: msg.tool_calls.map((call) => ({
|
|
88
|
-
type: 'tool-call',
|
|
89
|
-
toolCallId: call.id,
|
|
90
|
-
toolName: call.function.name,
|
|
91
|
-
input: parseToolInput(call.function.arguments),
|
|
92
|
-
})),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function historyMessage(msg) {
|
|
96
|
-
if (msg.role === 'tool') {
|
|
97
|
-
return toolResultContent(msg);
|
|
98
|
-
}
|
|
99
|
-
return assistantToolCallContent(msg) || contentHistoryMessage(msg);
|
|
100
|
-
}
|
|
101
|
-
function contentFromOptionalParts(parts, text) {
|
|
102
|
-
if (!parts || parts.length === 0) {
|
|
103
|
-
return stringDefault(text, '');
|
|
104
|
-
}
|
|
105
|
-
return contentFromParts(parts);
|
|
106
|
-
}
|
|
107
|
-
function contentHistoryMessage(msg) {
|
|
108
|
-
const content = contentFromOptionalParts(msg.parts, msg.content);
|
|
109
|
-
return { role: msg.role, content };
|
|
110
|
-
}
|
|
111
|
-
function sourceEvent(part) {
|
|
33
|
+
export function sourceEvent(part) {
|
|
112
34
|
if (part.sourceType !== 'url') {
|
|
113
35
|
return {
|
|
114
36
|
type: 'evidence',
|
|
115
37
|
evidence: { provider: 'openrouter', raw: part },
|
|
116
38
|
};
|
|
117
39
|
}
|
|
118
|
-
const title = part.title ?? part.url;
|
|
40
|
+
const title = part.title ?? part.url ?? '';
|
|
119
41
|
return {
|
|
120
42
|
type: 'evidence',
|
|
121
43
|
evidence: {
|
|
122
44
|
provider: 'openrouter',
|
|
123
45
|
raw: part,
|
|
124
|
-
citations: [part.url],
|
|
125
|
-
sources: [{ title, uri: part.url, type: 'web' }],
|
|
46
|
+
citations: part.url ? [part.url] : [],
|
|
47
|
+
sources: part.url ? [{ title, uri: part.url, type: 'web' }] : [],
|
|
126
48
|
},
|
|
127
49
|
};
|
|
128
50
|
}
|
|
129
|
-
function
|
|
130
|
-
const messages = [];
|
|
131
|
-
for (const msg of req.history ?? []) {
|
|
132
|
-
const wired = historyMessage(msg);
|
|
133
|
-
if (wired) {
|
|
134
|
-
messages.push(wired);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (req.input.length > 0) {
|
|
138
|
-
messages.push({ role: 'user', content: contentFromParts(req.input) });
|
|
139
|
-
}
|
|
140
|
-
return messages;
|
|
141
|
-
}
|
|
142
|
-
function schemaForTool(decl) {
|
|
51
|
+
export function schemaForTool(decl) {
|
|
143
52
|
return decl.parameters ?? { type: 'object', properties: {}, additionalProperties: true };
|
|
144
53
|
}
|
|
145
|
-
function buildTools(
|
|
146
|
-
if (!
|
|
54
|
+
export function buildTools(wireTools) {
|
|
55
|
+
if (!wireTools || wireTools.length === 0) {
|
|
147
56
|
return undefined;
|
|
148
57
|
}
|
|
149
58
|
const tools = {};
|
|
150
|
-
for (const decl of
|
|
59
|
+
for (const decl of wireTools) {
|
|
151
60
|
tools[decl.name] = tool({
|
|
152
|
-
description: decl.description
|
|
61
|
+
description: decl.description,
|
|
153
62
|
inputSchema: jsonSchema(schemaForTool(decl)),
|
|
154
63
|
});
|
|
155
64
|
}
|
|
@@ -167,7 +76,7 @@ function openRouterSettings(req) {
|
|
|
167
76
|
settings.web_search_options = {};
|
|
168
77
|
return settings;
|
|
169
78
|
}
|
|
170
|
-
function tokensFromUsage(usage) {
|
|
79
|
+
export function tokensFromUsage(usage) {
|
|
171
80
|
const input = usage.inputTokens ?? 0;
|
|
172
81
|
const output = usage.outputTokens ?? 0;
|
|
173
82
|
const total = usage.totalTokens ?? input + output;
|
|
@@ -180,23 +89,23 @@ function tokensFromUsage(usage) {
|
|
|
180
89
|
total,
|
|
181
90
|
};
|
|
182
91
|
}
|
|
183
|
-
function rawRecord(value) {
|
|
92
|
+
export function rawRecord(value) {
|
|
184
93
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
185
94
|
return value;
|
|
186
95
|
}
|
|
187
96
|
return undefined;
|
|
188
97
|
}
|
|
189
|
-
function stringArray(value) {
|
|
98
|
+
export function stringArray(value) {
|
|
190
99
|
if (!Array.isArray(value)) {
|
|
191
100
|
return undefined;
|
|
192
101
|
}
|
|
193
102
|
const out = value.filter((item) => typeof item === 'string');
|
|
194
103
|
return out.length > 0 ? out : undefined;
|
|
195
104
|
}
|
|
196
|
-
function metadataRecord(raw, key) {
|
|
105
|
+
export function metadataRecord(raw, key) {
|
|
197
106
|
return rawRecord(raw[key]);
|
|
198
107
|
}
|
|
199
|
-
function citationCandidates(raw) {
|
|
108
|
+
export function citationCandidates(raw) {
|
|
200
109
|
const openrouter = metadataRecord(raw, 'openrouter') ?? {};
|
|
201
110
|
return [
|
|
202
111
|
raw.citations,
|
|
@@ -207,7 +116,7 @@ function citationCandidates(raw) {
|
|
|
207
116
|
metadataRecord(openrouter, 'provider_metadata')?.citations,
|
|
208
117
|
];
|
|
209
118
|
}
|
|
210
|
-
function nestedCitations(raw) {
|
|
119
|
+
export function nestedCitations(raw) {
|
|
211
120
|
for (const candidate of citationCandidates(raw)) {
|
|
212
121
|
const citations = stringArray(candidate);
|
|
213
122
|
if (citations) {
|
|
@@ -216,7 +125,7 @@ function nestedCitations(raw) {
|
|
|
216
125
|
}
|
|
217
126
|
return undefined;
|
|
218
127
|
}
|
|
219
|
-
function metadataAnnotations(raw) {
|
|
128
|
+
export function metadataAnnotations(raw) {
|
|
220
129
|
const openrouter = metadataRecord(raw, 'openrouter');
|
|
221
130
|
if (Array.isArray(raw.annotations)) {
|
|
222
131
|
return raw.annotations;
|
|
@@ -226,7 +135,7 @@ function metadataAnnotations(raw) {
|
|
|
226
135
|
}
|
|
227
136
|
return undefined;
|
|
228
137
|
}
|
|
229
|
-
function evidenceFromMetadata(metadata, acc) {
|
|
138
|
+
export function evidenceFromMetadata(metadata, acc) {
|
|
230
139
|
const raw = rawRecord(metadata);
|
|
231
140
|
if (!raw || acc.evidenceSeen) {
|
|
232
141
|
return undefined;
|
|
@@ -242,7 +151,7 @@ function evidenceFromMetadata(metadata, acc) {
|
|
|
242
151
|
evidence: { provider: 'openrouter', raw, citations, annotations },
|
|
243
152
|
};
|
|
244
153
|
}
|
|
245
|
-
function toolArguments(input) {
|
|
154
|
+
export function toolArguments(input) {
|
|
246
155
|
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
247
156
|
return input;
|
|
248
157
|
}
|
|
@@ -251,10 +160,10 @@ function toolArguments(input) {
|
|
|
251
160
|
}
|
|
252
161
|
return { value: input };
|
|
253
162
|
}
|
|
254
|
-
function toolResultData(output) {
|
|
163
|
+
export function toolResultData(output) {
|
|
255
164
|
return rawRecord(output);
|
|
256
165
|
}
|
|
257
|
-
function rawThoughtEvent(raw) {
|
|
166
|
+
export function rawThoughtEvent(raw) {
|
|
258
167
|
const choices = raw.choices;
|
|
259
168
|
if (!Array.isArray(choices)) {
|
|
260
169
|
return undefined;
|
|
@@ -267,7 +176,7 @@ function rawThoughtEvent(raw) {
|
|
|
267
176
|
}
|
|
268
177
|
return undefined;
|
|
269
178
|
}
|
|
270
|
-
function rawChoiceMessageEvidence(raw, acc) {
|
|
179
|
+
export function rawChoiceMessageEvidence(raw, acc) {
|
|
271
180
|
const choices = raw.choices;
|
|
272
181
|
if (!Array.isArray(choices)) {
|
|
273
182
|
return undefined;
|
|
@@ -284,7 +193,7 @@ function rawChoiceMessageEvidence(raw, acc) {
|
|
|
284
193
|
}
|
|
285
194
|
return undefined;
|
|
286
195
|
}
|
|
287
|
-
function rawEvents(raw, acc) {
|
|
196
|
+
export function rawEvents(raw, acc) {
|
|
288
197
|
const record = rawRecord(raw);
|
|
289
198
|
if (!record) {
|
|
290
199
|
return [];
|
|
@@ -316,7 +225,7 @@ function rawEvents(raw, acc) {
|
|
|
316
225
|
}
|
|
317
226
|
return events;
|
|
318
227
|
}
|
|
319
|
-
function toolCallEvent(part) {
|
|
228
|
+
export function toolCallEvent(part) {
|
|
320
229
|
return {
|
|
321
230
|
type: 'tool',
|
|
322
231
|
tool: {
|
|
@@ -326,32 +235,30 @@ function toolCallEvent(part) {
|
|
|
326
235
|
},
|
|
327
236
|
};
|
|
328
237
|
}
|
|
329
|
-
function toolResultEvent(part) {
|
|
238
|
+
export function toolResultEvent(part) {
|
|
239
|
+
const output = typeof part.output === 'string' ? part.output : toolResultData(part.output);
|
|
330
240
|
return {
|
|
331
241
|
type: 'tool',
|
|
332
242
|
tool: {
|
|
333
243
|
name: part.toolName,
|
|
334
244
|
arguments: toolArguments(part.input),
|
|
335
|
-
result: {
|
|
336
|
-
status: 'ok',
|
|
337
|
-
data: toolResultData(part.output),
|
|
338
|
-
finding: typeof part.output === 'string' ? part.output : undefined,
|
|
339
|
-
},
|
|
340
245
|
id: part.toolCallId,
|
|
246
|
+
phase: 'complete',
|
|
247
|
+
output,
|
|
341
248
|
},
|
|
342
249
|
};
|
|
343
250
|
}
|
|
344
|
-
function tokenEvent(part) {
|
|
251
|
+
export function tokenEvent(part) {
|
|
345
252
|
const tokens = tokensFromUsage(part.totalUsage);
|
|
346
253
|
return tokens ? { type: 'tokens', tokens } : undefined;
|
|
347
254
|
}
|
|
348
|
-
function providerMetadataEvent(part, acc) {
|
|
255
|
+
export function providerMetadataEvent(part, acc) {
|
|
349
256
|
if (!('providerMetadata' in part)) {
|
|
350
257
|
return undefined;
|
|
351
258
|
}
|
|
352
259
|
return evidenceFromMetadata(part.providerMetadata, acc);
|
|
353
260
|
}
|
|
354
|
-
function eventFromPart(part, acc) {
|
|
261
|
+
export function eventFromPart(part, acc) {
|
|
355
262
|
const mapped = primaryEventFromPart(part, acc);
|
|
356
263
|
if (mapped) {
|
|
357
264
|
return [mapped];
|
|
@@ -359,7 +266,7 @@ function eventFromPart(part, acc) {
|
|
|
359
266
|
const evidence = providerMetadataEvent(part, acc);
|
|
360
267
|
return evidence ? [evidence] : [];
|
|
361
268
|
}
|
|
362
|
-
function primaryEventFromPart(part, acc) {
|
|
269
|
+
export function primaryEventFromPart(part, acc) {
|
|
363
270
|
switch (part.type) {
|
|
364
271
|
case 'text-delta':
|
|
365
272
|
acc.text += part.text;
|
|
@@ -385,59 +292,61 @@ function primaryEventFromPart(part, acc) {
|
|
|
385
292
|
return undefined;
|
|
386
293
|
}
|
|
387
294
|
}
|
|
388
|
-
function finishEvent(part, acc) {
|
|
389
|
-
if (
|
|
295
|
+
export function finishEvent(part, acc) {
|
|
296
|
+
if (part.finishReason != null) {
|
|
390
297
|
acc.finishReason = String(part.finishReason);
|
|
391
298
|
}
|
|
392
299
|
if (acc.emittedTokens) {
|
|
393
300
|
return undefined;
|
|
394
301
|
}
|
|
395
|
-
|
|
302
|
+
if (!part.totalUsage) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
const event = tokenEvent({ totalUsage: part.totalUsage });
|
|
396
306
|
if (event) {
|
|
397
307
|
acc.emittedTokens = true;
|
|
398
308
|
}
|
|
399
309
|
return event;
|
|
400
310
|
}
|
|
401
|
-
function* finalEvents(req, acc) {
|
|
311
|
+
export function* finalEvents(req, acc) {
|
|
402
312
|
if (acc.errored) {
|
|
403
313
|
return;
|
|
404
314
|
}
|
|
405
315
|
if (req.structured && acc.text) {
|
|
406
|
-
const
|
|
407
|
-
if (
|
|
408
|
-
yield
|
|
316
|
+
const parsed = parseStructuredOutput(acc.text);
|
|
317
|
+
if (!parsed.ok) {
|
|
318
|
+
yield toErrorEvent(new TheorumError(parsed.error));
|
|
319
|
+
return;
|
|
409
320
|
}
|
|
321
|
+
yield { type: 'structured', structured: parsed.structured };
|
|
410
322
|
}
|
|
411
323
|
yield {
|
|
412
324
|
type: 'done',
|
|
413
|
-
stop:
|
|
325
|
+
stop: turnStopFromOpenAiFinishReason(acc.finishReason, acc.nativeFinishReason),
|
|
414
326
|
};
|
|
415
327
|
}
|
|
416
328
|
function createStreamContext(req, config, apiKey) {
|
|
417
329
|
const openrouter = createOpenRouter({
|
|
418
330
|
apiKey,
|
|
419
331
|
baseURL: config.baseUrl,
|
|
420
|
-
headers:
|
|
332
|
+
headers: openAiGatewayHeaders(config),
|
|
421
333
|
fetch: config.fetch,
|
|
422
334
|
compatibility: 'strict',
|
|
423
335
|
});
|
|
424
336
|
return {
|
|
425
337
|
openrouter,
|
|
426
|
-
modelName:
|
|
427
|
-
apiId: req.apiId,
|
|
428
|
-
openRouterId: req.openRouterId,
|
|
429
|
-
}),
|
|
338
|
+
modelName: req.apiId,
|
|
430
339
|
};
|
|
431
340
|
}
|
|
432
341
|
function streamTextOptions(req, context) {
|
|
433
342
|
return {
|
|
434
343
|
model: context.openrouter.chat(context.modelName, openRouterSettings(req)),
|
|
435
344
|
instructions: req.system,
|
|
436
|
-
messages:
|
|
345
|
+
messages: buildAiSdkMessages(req),
|
|
437
346
|
allowSystemInMessages: true,
|
|
438
347
|
temperature: req.temperature,
|
|
439
348
|
maxOutputTokens: req.maxOutputTokens,
|
|
440
|
-
tools: buildTools(req.
|
|
349
|
+
tools: buildTools(req.wireTools),
|
|
441
350
|
providerOptions: providerOptionsFor(req),
|
|
442
351
|
include: { rawChunks: true },
|
|
443
352
|
abortSignal: req.signal,
|
|
@@ -448,7 +357,7 @@ async function* yieldAiSdkStream(req, acc, context) {
|
|
|
448
357
|
const result = streamText(streamTextOptions(req, context));
|
|
449
358
|
for await (const part of result.stream) {
|
|
450
359
|
if (part.type === 'raw') {
|
|
451
|
-
req.
|
|
360
|
+
req.tapUpstream?.(rawRecord(part.rawValue) ?? { rawValue: part.rawValue });
|
|
452
361
|
for (const event of rawEvents(part.rawValue, acc)) {
|
|
453
362
|
yield event;
|
|
454
363
|
}
|
|
@@ -459,13 +368,16 @@ async function* yieldAiSdkStream(req, acc, context) {
|
|
|
459
368
|
}
|
|
460
369
|
}
|
|
461
370
|
}
|
|
462
|
-
function missingOpenRouterKey() {
|
|
371
|
+
export function missingOpenRouterKey() {
|
|
463
372
|
return toErrorEvent('missing OpenRouter API key');
|
|
464
373
|
}
|
|
465
374
|
async function* streamOpenRouter(req, config) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
375
|
+
let apiKey;
|
|
376
|
+
try {
|
|
377
|
+
apiKey = resolveOpenAiGatewayApiKey(config, req.keySlot);
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
yield toErrorEvent(err);
|
|
469
381
|
return;
|
|
470
382
|
}
|
|
471
383
|
const acc = createAccumulator();
|
|
@@ -481,27 +393,12 @@ async function* streamOpenRouter(req, config) {
|
|
|
481
393
|
yield toErrorEvent(err);
|
|
482
394
|
}
|
|
483
395
|
}
|
|
484
|
-
function
|
|
485
|
-
if (!req.structured)
|
|
486
|
-
return undefined;
|
|
487
|
-
const spec = getStructured(req.structured);
|
|
488
|
-
if (!spec.jsonSchema)
|
|
489
|
-
return undefined;
|
|
490
|
-
return {
|
|
491
|
-
type: 'json_schema',
|
|
492
|
-
json_schema: {
|
|
493
|
-
name: String(req.structured),
|
|
494
|
-
strict: true,
|
|
495
|
-
schema: spec.jsonSchema,
|
|
496
|
-
},
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
function providerOptionsFor(req) {
|
|
396
|
+
export function providerOptionsFor(req) {
|
|
500
397
|
const openrouter = {};
|
|
501
398
|
if (req.thinking !== 'none') {
|
|
502
399
|
openrouter.reasoning = { effort: req.thinking };
|
|
503
400
|
}
|
|
504
|
-
const responseFormat =
|
|
401
|
+
const responseFormat = resolveResponseFormat(req.structured);
|
|
505
402
|
if (responseFormat) {
|
|
506
403
|
openrouter.response_format = responseFormat;
|
|
507
404
|
}
|
|
@@ -509,65 +406,9 @@ function providerOptionsFor(req) {
|
|
|
509
406
|
return undefined;
|
|
510
407
|
return { openrouter };
|
|
511
408
|
}
|
|
512
|
-
function openRouterHeaders(config) {
|
|
513
|
-
const headers = {};
|
|
514
|
-
if (config.siteUrl) {
|
|
515
|
-
headers['HTTP-Referer'] = config.siteUrl;
|
|
516
|
-
}
|
|
517
|
-
if (config.siteName) {
|
|
518
|
-
headers['X-Title'] = config.siteName;
|
|
519
|
-
}
|
|
520
|
-
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
521
|
-
}
|
|
522
409
|
/** Create a `ModelProvider` backed by OpenRouter through AI SDK Core. */
|
|
523
|
-
function createOpenRouterProvider(config = {}) {
|
|
410
|
+
export function createOpenRouterProvider(config = {}) {
|
|
524
411
|
return {
|
|
525
412
|
complete: (req) => streamOpenRouter(req, config),
|
|
526
413
|
};
|
|
527
414
|
}
|
|
528
|
-
export { createOpenRouterProvider, resolveOpenRouterModel, toOpenRouterPayload };
|
|
529
|
-
exposeForTests('openrouter', {
|
|
530
|
-
trimApiKey,
|
|
531
|
-
createAccumulator,
|
|
532
|
-
mediaPart,
|
|
533
|
-
contentFromParts,
|
|
534
|
-
parseToolInput,
|
|
535
|
-
stringDefault,
|
|
536
|
-
fallbackToolCallId,
|
|
537
|
-
toolResultContent,
|
|
538
|
-
assistantToolCallContent,
|
|
539
|
-
historyMessage,
|
|
540
|
-
contentFromOptionalParts,
|
|
541
|
-
contentHistoryMessage,
|
|
542
|
-
buildMessages,
|
|
543
|
-
schemaForTool,
|
|
544
|
-
buildTools,
|
|
545
|
-
openRouterSettings,
|
|
546
|
-
tokensFromUsage,
|
|
547
|
-
rawRecord,
|
|
548
|
-
stringArray,
|
|
549
|
-
metadataRecord,
|
|
550
|
-
citationCandidates,
|
|
551
|
-
nestedCitations,
|
|
552
|
-
metadataAnnotations,
|
|
553
|
-
evidenceFromMetadata,
|
|
554
|
-
toolArguments,
|
|
555
|
-
toolResultData,
|
|
556
|
-
rawThoughtEvent,
|
|
557
|
-
rawChoiceMessageEvidence,
|
|
558
|
-
rawEvents,
|
|
559
|
-
toolCallEvent,
|
|
560
|
-
toolResultEvent,
|
|
561
|
-
tokenEvent,
|
|
562
|
-
providerMetadataEvent,
|
|
563
|
-
eventFromPart,
|
|
564
|
-
primaryEventFromPart,
|
|
565
|
-
finishEvent,
|
|
566
|
-
sourceEvent,
|
|
567
|
-
finalEvents,
|
|
568
|
-
responseFormatFor,
|
|
569
|
-
providerOptionsFor,
|
|
570
|
-
openRouterHeaders,
|
|
571
|
-
missingOpenRouterKey,
|
|
572
|
-
streamTextOptions,
|
|
573
|
-
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible image generation transport (internal).
|
|
3
|
+
*
|
|
4
|
+
* Hosts use `createProvider(profile, { openAiGateway })` — this module is selected
|
|
5
|
+
* when the profile is an openAi image role on OpenRouter.
|
|
6
|
+
*
|
|
7
|
+
* Image-only turns POST `/images`. When `image.includeText` is set, chat
|
|
8
|
+
* completions carry an OpenRouter image-generation server tool so the model may
|
|
9
|
+
* return interleaved assistant text and images.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { ModelProvider, ProviderCompleteRequest, TurnEvent } from '../../kernel/types.js';
|
|
14
|
+
import type { OpenAiGatewayConfig } from '../types.js';
|
|
15
|
+
/** OpenRouter chat server tool for inline image generation. */
|
|
16
|
+
export declare const OPENROUTER_IMAGE_TOOL = "openrouter:image_generation";
|
|
17
|
+
export type ImageProviderConfig = OpenAiGatewayConfig;
|
|
18
|
+
export declare function buildImageHeaders(apiKey: string, config: ImageProviderConfig): Record<string, string>;
|
|
19
|
+
export declare function mediaFromImagesResponse(body: Record<string, unknown>, fallbackMime?: string): {
|
|
20
|
+
mimeType: string;
|
|
21
|
+
data: string;
|
|
22
|
+
} | null;
|
|
23
|
+
export declare function fetchImageAsBase64(url: string, fetchFn: typeof fetch, signal?: AbortSignal): Promise<{
|
|
24
|
+
mimeType: string;
|
|
25
|
+
data: string;
|
|
26
|
+
} | null>;
|
|
27
|
+
export declare function markdownImageUrls(content: string): string[];
|
|
28
|
+
export declare function plainTextFromContent(content: unknown): string;
|
|
29
|
+
export declare function buildInterleavedChatPayload(req: ProviderCompleteRequest): Record<string, unknown>;
|
|
30
|
+
export declare function yieldInterleavedChat(req: ProviderCompleteRequest, config: ImageProviderConfig, apiKey: string): AsyncGenerator<TurnEvent>;
|
|
31
|
+
export declare function yieldImagesEndpoint(req: ProviderCompleteRequest, config: ImageProviderConfig, apiKey: string): AsyncGenerator<TurnEvent>;
|
|
32
|
+
export declare function streamImage(req: ProviderCompleteRequest, config?: ImageProviderConfig): AsyncGenerator<TurnEvent>;
|
|
33
|
+
/** Internal ModelProvider for openAi image roles on OpenRouter. */
|
|
34
|
+
export declare function createImageProvider(config?: ImageProviderConfig): ModelProvider;
|