theorum 0.1.15 → 1.1.3
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 +241 -98
- package/esm/mod.d.ts +57 -28
- package/esm/mod.js +43 -23
- package/esm/src/cli/commands/bench.js +18 -18
- package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
- package/esm/src/cli/commands/fuzz-canary.js +191 -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/guardrails-eval.d.ts +14 -0
- package/esm/src/cli/commands/guardrails-eval.js +15 -0
- package/esm/src/cli/commands/profile.js +35 -15
- package/esm/src/cli/commands/run.d.ts +3 -0
- package/esm/src/cli/commands/run.js +23 -32
- package/esm/src/cli/commands/test.d.ts +10 -1
- package/esm/src/cli/commands/test.js +34 -34
- 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 +57 -11
- package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
- package/esm/src/cli/matrix/synthesizer.js +45 -118
- package/esm/src/guardrails/canary-gate.d.ts +21 -0
- package/esm/src/guardrails/canary-gate.js +32 -0
- package/esm/src/guardrails/canary.d.ts +34 -0
- package/esm/src/guardrails/canary.js +150 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -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 +213 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
- package/esm/src/guardrails/corpus/live-attacks.js +231 -0
- package/esm/src/guardrails/corpus/mod.d.ts +14 -0
- package/esm/src/guardrails/corpus/mod.js +11 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
- package/esm/src/guardrails/corpus/secrets.js +17 -0
- package/esm/src/guardrails/corpus/strings.d.ts +28 -0
- package/esm/src/guardrails/corpus/strings.js +34 -0
- package/esm/src/guardrails/corpus/types.d.ts +38 -0
- package/esm/src/guardrails/corpus/types.js +6 -0
- package/esm/src/guardrails/egress.d.ts +32 -0
- package/esm/src/guardrails/egress.js +87 -0
- package/esm/src/guardrails/error.d.ts +14 -23
- package/esm/src/guardrails/error.js +87 -76
- package/esm/src/guardrails/eval/corpus.d.ts +108 -0
- package/esm/src/guardrails/eval/corpus.js +978 -0
- package/esm/src/guardrails/eval/mod.d.ts +51 -0
- package/esm/src/guardrails/eval/mod.js +133 -0
- package/esm/src/guardrails/eval/score.d.ts +66 -0
- package/esm/src/guardrails/eval/score.js +114 -0
- package/esm/src/guardrails/events.d.ts +25 -0
- package/esm/src/guardrails/events.js +56 -0
- package/esm/src/guardrails/hits.d.ts +24 -0
- package/esm/src/guardrails/hits.js +45 -0
- package/esm/src/guardrails/injection.js +28 -5
- package/esm/src/guardrails/lexicon.d.ts +39 -0
- package/esm/src/guardrails/lexicon.js +200 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
- package/esm/src/guardrails/live-outbound-gate.js +222 -0
- package/esm/src/guardrails/mod.d.ts +30 -6
- package/esm/src/guardrails/mod.js +20 -5
- package/esm/src/guardrails/network.d.ts +19 -0
- package/esm/src/guardrails/network.js +234 -0
- package/esm/src/guardrails/policy.d.ts +35 -0
- package/esm/src/guardrails/policy.js +50 -0
- package/esm/src/guardrails/progressive-yield.d.ts +51 -0
- package/esm/src/guardrails/progressive-yield.js +98 -0
- package/esm/src/guardrails/quota.d.ts +17 -3
- package/esm/src/guardrails/quota.js +18 -4
- package/esm/src/guardrails/sanitize.d.ts +45 -19
- package/esm/src/guardrails/sanitize.js +177 -94
- package/esm/src/guardrails/sensitive.js +2 -1
- package/esm/src/guardrails/serialize.d.ts +35 -0
- package/esm/src/guardrails/serialize.js +58 -0
- package/esm/src/guardrails/testing.d.ts +17 -0
- package/esm/src/guardrails/testing.js +13 -0
- package/esm/src/guardrails/theorum-error.d.ts +12 -0
- package/esm/src/guardrails/theorum-error.js +15 -0
- package/esm/src/guardrails/tool-directives.d.ts +48 -0
- package/esm/src/guardrails/tool-directives.js +124 -0
- package/esm/src/guardrails/tool-result.d.ts +93 -0
- package/esm/src/guardrails/tool-result.js +276 -0
- package/esm/src/guardrails/types.d.ts +291 -0
- package/esm/src/guardrails/types.js +72 -0
- package/esm/src/host/client-turn.d.ts +19 -0
- package/esm/src/host/client-turn.js +36 -0
- package/esm/src/host/mint-trace.d.ts +1 -1
- package/esm/src/host/mod.d.ts +5 -3
- package/esm/src/host/mod.js +4 -3
- package/esm/src/kernel/auth/crypto.d.ts +42 -0
- package/esm/src/kernel/auth/crypto.js +106 -0
- package/esm/src/kernel/auth/mod.d.ts +11 -0
- package/esm/src/kernel/auth/mod.js +11 -0
- package/esm/src/kernel/auth/oauth.d.ts +47 -0
- package/esm/src/kernel/auth/oauth.js +278 -0
- package/esm/src/kernel/auth/types.d.ts +133 -0
- package/esm/src/kernel/auth/types.js +13 -0
- package/esm/src/kernel/engine/delta.d.ts +24 -2
- package/esm/src/kernel/engine/delta.js +478 -39
- package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
- package/esm/src/kernel/engine/live-inbound.js +31 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
- package/esm/src/kernel/engine/live-ingress.js +47 -0
- package/esm/src/kernel/engine/repair.js +13 -12
- package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
- package/esm/src/kernel/engine/runner/gates.js +130 -43
- package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
- package/esm/src/kernel/engine/runner/mod.js +192 -53
- package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
- package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
- package/esm/src/kernel/engine/runner/stages.js +89 -0
- package/esm/src/kernel/engine/runner/state.d.ts +31 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
- package/esm/src/kernel/engine/runner/steps.js +244 -43
- package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
- package/esm/src/kernel/engine/runner/stream.js +140 -44
- package/esm/src/kernel/engine/session/mod.d.ts +25 -0
- package/esm/src/kernel/engine/session/mod.js +557 -0
- package/esm/src/kernel/interaction-parts.d.ts +14 -0
- package/esm/src/kernel/interaction-parts.js +23 -0
- package/esm/src/kernel/mod.d.ts +21 -10
- package/esm/src/kernel/mod.js +11 -8
- package/esm/src/kernel/profile-graph.d.ts +159 -0
- package/esm/src/kernel/profile-graph.js +156 -0
- package/esm/src/kernel/registry/attachments.d.ts +12 -10
- package/esm/src/kernel/registry/attachments.js +33 -27
- package/esm/src/kernel/registry/catalog.d.ts +25 -24
- package/esm/src/kernel/registry/catalog.js +60 -101
- package/esm/src/kernel/registry/ingress.d.ts +9 -4
- package/esm/src/kernel/registry/ingress.js +97 -75
- package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
- package/esm/src/kernel/registry/profile-outputs.js +8 -0
- package/esm/src/kernel/registry/profiles.d.ts +55 -12
- package/esm/src/kernel/registry/profiles.js +413 -73
- package/esm/src/kernel/registry/provider-request.js +13 -7
- package/esm/src/kernel/registry/resolve.d.ts +8 -8
- package/esm/src/kernel/registry/resolve.js +169 -154
- package/esm/src/kernel/registry/schemas.js +1 -1
- package/esm/src/kernel/registry/sole-model.d.ts +8 -0
- package/esm/src/kernel/registry/sole-model.js +10 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
- package/esm/src/kernel/registry/system-prompt.js +40 -0
- package/esm/src/kernel/registry/system-role.d.ts +8 -0
- package/esm/src/kernel/registry/system-role.js +14 -0
- package/esm/src/kernel/registry/vault.d.ts +12 -7
- package/esm/src/kernel/registry/vault.js +32 -10
- package/esm/src/kernel/schema.d.ts +231 -0
- package/esm/src/kernel/schema.js +607 -0
- package/esm/src/kernel/stages.d.ts +175 -0
- package/esm/src/kernel/stages.js +476 -0
- package/esm/src/kernel/stop.d.ts +78 -19
- package/esm/src/kernel/stop.js +51 -16
- package/esm/src/kernel/tools/events.d.ts +41 -0
- package/esm/src/kernel/tools/events.js +71 -0
- package/esm/src/kernel/tools/execute.d.ts +84 -0
- package/esm/src/kernel/tools/execute.js +614 -0
- package/esm/src/kernel/tools/harness.d.ts +8 -0
- package/esm/src/kernel/tools/harness.js +46 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +101 -0
- package/esm/src/kernel/tools/mod.d.ts +13 -0
- package/esm/src/kernel/tools/mod.js +11 -0
- package/esm/src/kernel/tools/permission.d.ts +15 -0
- package/esm/src/kernel/tools/permission.js +47 -0
- package/esm/src/kernel/tools/project.d.ts +12 -0
- package/esm/src/kernel/tools/project.js +36 -0
- package/esm/src/kernel/tools/registry.d.ts +23 -0
- package/esm/src/kernel/tools/registry.js +81 -0
- package/esm/src/kernel/tools/remote.d.ts +94 -0
- package/esm/src/kernel/tools/remote.js +577 -0
- package/esm/src/kernel/tools/resolve.d.ts +39 -0
- package/esm/src/kernel/tools/resolve.js +283 -0
- package/esm/src/kernel/tools/schema.d.ts +15 -0
- package/esm/src/kernel/tools/schema.js +176 -0
- package/esm/src/kernel/tools/stage-run.d.ts +105 -0
- package/esm/src/kernel/tools/stage-run.js +155 -0
- package/esm/src/kernel/tools/types.d.ts +394 -0
- package/esm/src/kernel/tools/types.js +9 -0
- package/esm/src/kernel/types.d.ts +540 -256
- package/esm/src/kernel/util/find-last.d.ts +2 -0
- package/esm/src/kernel/util/find-last.js +10 -0
- package/esm/src/observability/destinations.d.ts +31 -0
- package/esm/src/observability/destinations.js +67 -0
- package/esm/src/observability/mod.d.ts +10 -3
- package/esm/src/observability/mod.js +6 -2
- package/esm/src/observability/policy.d.ts +27 -0
- package/esm/src/observability/policy.js +80 -0
- package/esm/src/observability/resolve-policy.d.ts +16 -0
- package/esm/src/observability/resolve-policy.js +64 -0
- package/esm/src/observability/trace-attach.d.ts +8 -4
- package/esm/src/observability/trace-attach.js +50 -29
- package/esm/src/observability/trace-record.d.ts +23 -13
- package/esm/src/observability/trace-record.js +96 -39
- package/esm/src/observability/trace-sink.d.ts +19 -0
- package/esm/src/observability/trace-sink.js +10 -0
- 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 +18 -7
- package/esm/src/observability/trace.js +34 -17
- package/esm/src/observability/types.d.ts +113 -0
- package/esm/src/observability/types.js +11 -0
- 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 +20 -17
- package/esm/src/providers/create-provider.js +72 -26
- package/esm/src/providers/google/interactions/framing.d.ts +23 -0
- package/esm/src/providers/google/interactions/framing.js +269 -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 +49 -0
- package/esm/src/providers/google/live/framing.js +552 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
- package/esm/src/providers/google/live/openapi-schema.js +46 -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 +45 -0
- package/esm/src/providers/google/live/stream.js +214 -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/cache-control.d.ts +24 -0
- package/esm/src/providers/openrouter/cache-control.js +23 -0
- package/esm/src/providers/openrouter/chat.d.ts +107 -0
- package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
- package/esm/src/providers/openrouter/image.d.ts +34 -0
- package/esm/src/providers/openrouter/image.js +275 -0
- package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
- package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
- package/esm/src/providers/openrouter/openai/compat.js +213 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -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 -202
- 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 -202
- 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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible image generation payloads.
|
|
3
|
+
*
|
|
4
|
+
* Maps kernel `ImageResponseFormat` pins to the `/images` REST body shared by
|
|
5
|
+
* OpenRouter and other OpenAI-compat gateways.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { TheorumError } from '../../../guardrails/error.js';
|
|
10
|
+
import { isMediaRefPart } from '../../../kernel/interaction-parts.js';
|
|
11
|
+
export function extractPromptText(input) {
|
|
12
|
+
return input
|
|
13
|
+
.filter((part) => part.type === 'text')
|
|
14
|
+
.map((part) => (part.type === 'text' ? part.text : ''))
|
|
15
|
+
.join('\n')
|
|
16
|
+
.trim();
|
|
17
|
+
}
|
|
18
|
+
export function outputFormatFromMime(mimeType) {
|
|
19
|
+
const essence = mimeType.toLowerCase().replace(/^image\//, '');
|
|
20
|
+
if (essence === 'jpg') {
|
|
21
|
+
return 'jpeg';
|
|
22
|
+
}
|
|
23
|
+
if (essence === 'png' || essence === 'jpeg' || essence === 'webp') {
|
|
24
|
+
return essence;
|
|
25
|
+
}
|
|
26
|
+
return 'png';
|
|
27
|
+
}
|
|
28
|
+
export function wireInputReference(part) {
|
|
29
|
+
return {
|
|
30
|
+
type: 'image_url',
|
|
31
|
+
image_url: { url: `data:${part.mimeType};base64,${part.data}` },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function wireInputReferences(input) {
|
|
35
|
+
const references = [];
|
|
36
|
+
for (const part of input) {
|
|
37
|
+
if (part.type !== 'image') {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (isMediaRefPart(part)) {
|
|
41
|
+
throw new TheorumError('media references are not supported on openAi');
|
|
42
|
+
}
|
|
43
|
+
references.push(wireInputReference(part));
|
|
44
|
+
}
|
|
45
|
+
return references;
|
|
46
|
+
}
|
|
47
|
+
export function attachImagePins(payload, image) {
|
|
48
|
+
if (image.aspectRatio) {
|
|
49
|
+
payload.aspect_ratio = image.aspectRatio;
|
|
50
|
+
}
|
|
51
|
+
if (image.size) {
|
|
52
|
+
payload.resolution = image.size;
|
|
53
|
+
}
|
|
54
|
+
if (image.mimeType) {
|
|
55
|
+
payload.output_format = outputFormatFromMime(image.mimeType);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Build a POST `/images` body for native image-generation models. */
|
|
59
|
+
export function buildImagesPayload(req) {
|
|
60
|
+
if (!req.image) {
|
|
61
|
+
throw new Error('buildImagesPayload requires req.image');
|
|
62
|
+
}
|
|
63
|
+
const payload = {
|
|
64
|
+
model: req.apiId,
|
|
65
|
+
prompt: extractPromptText(req.input),
|
|
66
|
+
};
|
|
67
|
+
attachImagePins(payload, req.image);
|
|
68
|
+
const references = wireInputReferences(req.input);
|
|
69
|
+
if (references.length > 0) {
|
|
70
|
+
payload.input_references = references;
|
|
71
|
+
}
|
|
72
|
+
if (req.stream === false) {
|
|
73
|
+
payload.stream = false;
|
|
74
|
+
}
|
|
75
|
+
return payload;
|
|
76
|
+
}
|
|
77
|
+
/** Tool parameters for gateways that generate images inside chat completions. */
|
|
78
|
+
export function imageToolParameters(image) {
|
|
79
|
+
const params = {};
|
|
80
|
+
if (image.mimeType) {
|
|
81
|
+
params.output_format = outputFormatFromMime(image.mimeType);
|
|
82
|
+
}
|
|
83
|
+
if (image.aspectRatio) {
|
|
84
|
+
params.aspect_ratio = image.aspectRatio;
|
|
85
|
+
}
|
|
86
|
+
if (image.size) {
|
|
87
|
+
params.resolution = image.size;
|
|
88
|
+
}
|
|
89
|
+
return params;
|
|
90
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert THEORUM turn input into Vercel AI SDK `ModelMessage[]`.
|
|
3
|
+
*
|
|
4
|
+
* Semantic twin of `openai/compat.ts` (REST wire format). OpenRouter's AI SDK
|
|
5
|
+
* adapter uses this module; local and payload paths use `buildChatMessages`.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { ModelMessage } from 'ai';
|
|
10
|
+
import type { InteractionPart, ProviderCompleteRequest, TurnHistoryMessage } from '../../../kernel/types.js';
|
|
11
|
+
export declare function sdkPart(input: InteractionPart): Record<string, unknown>;
|
|
12
|
+
export declare function sdkContentFromParts(parts: InteractionPart[]): string | Array<Record<string, unknown>>;
|
|
13
|
+
export declare function sdkContentFromOptionalParts(parts: InteractionPart[] | undefined, text: string | undefined): string | Array<Record<string, unknown>>;
|
|
14
|
+
export declare function toolResultMessage(msg: TurnHistoryMessage): ModelMessage;
|
|
15
|
+
export declare function assistantToolCallMessage(msg: TurnHistoryMessage): ModelMessage | null;
|
|
16
|
+
export declare function contentHistoryMessage(msg: TurnHistoryMessage): ModelMessage;
|
|
17
|
+
export declare function historyToSdk(msg: TurnHistoryMessage): ModelMessage | null;
|
|
18
|
+
/**
|
|
19
|
+
* Build AI SDK messages for history + user input.
|
|
20
|
+
* Caller supplies `req.system` via `streamText({ instructions })`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildAiSdkMessages(req: ProviderCompleteRequest): ModelMessage[];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert THEORUM turn input into Vercel AI SDK `ModelMessage[]`.
|
|
3
|
+
*
|
|
4
|
+
* Semantic twin of `openai/compat.ts` (REST wire format). OpenRouter's AI SDK
|
|
5
|
+
* adapter uses this module; local and payload paths use `buildChatMessages`.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { TheorumError } from '../../../guardrails/error.js';
|
|
10
|
+
import { isMediaRefPart } from '../../../kernel/interaction-parts.js';
|
|
11
|
+
import { fallbackToolCallId, parseToolInput } from './compat.js';
|
|
12
|
+
function inlineMediaPart(part) {
|
|
13
|
+
if (isMediaRefPart(part)) {
|
|
14
|
+
throw new TheorumError('media references are not supported on openAi');
|
|
15
|
+
}
|
|
16
|
+
return part;
|
|
17
|
+
}
|
|
18
|
+
function stringDefault(value, fallback) {
|
|
19
|
+
return value === undefined ? fallback : value;
|
|
20
|
+
}
|
|
21
|
+
export function sdkPart(input) {
|
|
22
|
+
if (input.type === 'text') {
|
|
23
|
+
return { type: 'text', text: input.text };
|
|
24
|
+
}
|
|
25
|
+
const part = inlineMediaPart(input);
|
|
26
|
+
if (part.type === 'image') {
|
|
27
|
+
return {
|
|
28
|
+
type: 'image',
|
|
29
|
+
image: `data:${part.mimeType};base64,${part.data}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { type: 'file', mediaType: part.mimeType, data: part.data };
|
|
33
|
+
}
|
|
34
|
+
export function sdkContentFromParts(parts) {
|
|
35
|
+
if (parts.every((part) => part.type === 'text')) {
|
|
36
|
+
return parts
|
|
37
|
+
.map((part) => (part.type === 'text' ? part.text : ''))
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.join('\n');
|
|
40
|
+
}
|
|
41
|
+
return parts.map(sdkPart);
|
|
42
|
+
}
|
|
43
|
+
export function sdkContentFromOptionalParts(parts, text) {
|
|
44
|
+
if (!parts || parts.length === 0) {
|
|
45
|
+
return stringDefault(text, '');
|
|
46
|
+
}
|
|
47
|
+
return sdkContentFromParts(parts);
|
|
48
|
+
}
|
|
49
|
+
export function toolResultMessage(msg) {
|
|
50
|
+
const toolName = stringDefault(msg.name, 'tool');
|
|
51
|
+
const toolCallId = stringDefault(msg.tool_call_id, fallbackToolCallId(msg.name));
|
|
52
|
+
const output = msg.parts && msg.parts.length > 0
|
|
53
|
+
? {
|
|
54
|
+
type: 'content',
|
|
55
|
+
value: msg.parts.map((input) => {
|
|
56
|
+
if (input.type === 'text') {
|
|
57
|
+
return { type: 'text', text: input.text };
|
|
58
|
+
}
|
|
59
|
+
// image / audio / video / document — AI SDK tool-result file parts
|
|
60
|
+
const part = inlineMediaPart(input);
|
|
61
|
+
return {
|
|
62
|
+
type: 'file',
|
|
63
|
+
mediaType: part.mimeType,
|
|
64
|
+
data: { type: 'data', data: part.data },
|
|
65
|
+
};
|
|
66
|
+
}),
|
|
67
|
+
}
|
|
68
|
+
: { type: 'text', value: stringDefault(msg.content, '') };
|
|
69
|
+
// AI SDK ToolModelMessage is a branded union; structural tool-result is correct at runtime.
|
|
70
|
+
return {
|
|
71
|
+
role: 'tool',
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'tool-result',
|
|
75
|
+
toolCallId,
|
|
76
|
+
toolName,
|
|
77
|
+
output,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function assistantToolCallMessage(msg) {
|
|
83
|
+
if (!msg.tool_calls || msg.tool_calls.length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
role: 'assistant',
|
|
88
|
+
content: msg.tool_calls.map((call) => ({
|
|
89
|
+
type: 'tool-call',
|
|
90
|
+
toolCallId: call.id,
|
|
91
|
+
toolName: call.function.name,
|
|
92
|
+
input: parseToolInput(call.function.arguments),
|
|
93
|
+
})),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function contentHistoryMessage(msg) {
|
|
97
|
+
const content = sdkContentFromOptionalParts(msg.parts, msg.content);
|
|
98
|
+
return { role: msg.role, content };
|
|
99
|
+
}
|
|
100
|
+
export function historyToSdk(msg) {
|
|
101
|
+
if (msg.role === 'tool') {
|
|
102
|
+
return toolResultMessage(msg);
|
|
103
|
+
}
|
|
104
|
+
return assistantToolCallMessage(msg) || contentHistoryMessage(msg);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Build AI SDK messages for history + user input.
|
|
108
|
+
* Caller supplies `req.system` via `streamText({ instructions })`.
|
|
109
|
+
*/
|
|
110
|
+
export function buildAiSdkMessages(req) {
|
|
111
|
+
const messages = [];
|
|
112
|
+
for (const msg of req.history ?? []) {
|
|
113
|
+
const wired = historyToSdk(msg);
|
|
114
|
+
if (wired) {
|
|
115
|
+
messages.push(wired);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (req.input.length > 0) {
|
|
119
|
+
messages.push({ role: 'user', content: sdkContentFromParts(req.input) });
|
|
120
|
+
}
|
|
121
|
+
return messages;
|
|
122
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve an API key from an OpenAI-gateway config using an optional vault slot.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { KeySlot } from '../../kernel/types.js';
|
|
7
|
+
import type { OpenAiGatewayConfig } from '../types.js';
|
|
8
|
+
/** Pick the credential for this turn from vault[keySlot] or flat apiKey. */
|
|
9
|
+
export declare function resolveOpenAiGatewayApiKey(config: OpenAiGatewayConfig, keySlot: KeySlot | undefined): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve an API key from an OpenAI-gateway config using an optional vault slot.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { TheorumError, UPSTREAM_FAILED } from '../../guardrails/error.js';
|
|
7
|
+
/** Pick the credential for this turn from vault[keySlot] or flat apiKey. */
|
|
8
|
+
export function resolveOpenAiGatewayApiKey(config, keySlot) {
|
|
9
|
+
if (keySlot !== undefined) {
|
|
10
|
+
if (!config.vault) {
|
|
11
|
+
throw new TheorumError('openAiGateway.vault is required when keySlot is set');
|
|
12
|
+
}
|
|
13
|
+
const fromVault = config.vault[keySlot]?.trim();
|
|
14
|
+
if (!fromVault) {
|
|
15
|
+
throw new TheorumError(UPSTREAM_FAILED);
|
|
16
|
+
}
|
|
17
|
+
return fromVault;
|
|
18
|
+
}
|
|
19
|
+
const flat = config.apiKey?.trim();
|
|
20
|
+
if (!flat) {
|
|
21
|
+
throw new TheorumError('openAiGateway.apiKey is required when keySlot is omitted');
|
|
22
|
+
}
|
|
23
|
+
return flat;
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible `/audio/speech` transport (internal).
|
|
3
|
+
*
|
|
4
|
+
* Hosts use `createProvider(profile, { openAiGateway })` — this module is selected
|
|
5
|
+
* when the profile is an openAi speech role. Not a separate public door.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { InteractionPart, ModelProvider, ProfileSpeechSpec, ProviderCompleteRequest, SpeechAudioFormat, TurnEvent } from '../../kernel/types.js';
|
|
10
|
+
import type { OpenAiGatewayConfig } from '../types.js';
|
|
11
|
+
/** Credentials for the openAi speech path — gateway config + optional voice. */
|
|
12
|
+
export type SpeechProviderConfig = OpenAiGatewayConfig & {
|
|
13
|
+
/** Fallback TTS voice when the profile does not pin `speech.voice`. */
|
|
14
|
+
voice?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function extractInputText(input: InteractionPart[]): string;
|
|
17
|
+
export declare function buildSpeechHeaders(apiKey: string, config: SpeechProviderConfig): Record<string, string>;
|
|
18
|
+
export declare function buildPayload(req: ProviderCompleteRequest, text: string, speech: ProfileSpeechSpec | undefined, configVoice?: string): Record<string, unknown>;
|
|
19
|
+
export declare function requestSpeech(apiKey: string, text: string, req: ProviderCompleteRequest, config: SpeechProviderConfig): Promise<Response>;
|
|
20
|
+
export declare function yieldSpeechSuccess(rawBytes: Uint8Array, text: string, format?: SpeechAudioFormat, contentType?: string | null): Generator<TurnEvent>;
|
|
21
|
+
export declare function streamSpeech(req: ProviderCompleteRequest, config?: SpeechProviderConfig): AsyncGenerator<TurnEvent>;
|
|
22
|
+
/** Internal ModelProvider for openAi speech roles. */
|
|
23
|
+
export declare function createSpeechProvider(config?: SpeechProviderConfig): ModelProvider;
|
|
@@ -1,84 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenAI-compatible `/audio/speech` transport (internal).
|
|
3
3
|
*
|
|
4
|
-
* Hosts use `createProvider(profile, {
|
|
4
|
+
* Hosts use `createProvider(profile, { openAiGateway })` — this module is selected
|
|
5
5
|
* when the profile is an openAi speech role. Not a separate public door.
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
-
import { toErrorEvent } from '
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { toErrorEvent } from '../../guardrails/error.js';
|
|
10
|
+
import { bytesToBase64, wrapPcmAsWav } from '../shared/pcm.js';
|
|
11
|
+
import { openAiGatewayHeaders } from './openai/compat.js';
|
|
12
|
+
import { resolveOpenAiGatewayApiKey } from './resolve-api-key.js';
|
|
12
13
|
const HTTP_OK = 200;
|
|
13
|
-
function
|
|
14
|
-
let bin = '';
|
|
15
|
-
for (let i = 0; i < bytes.length; i++)
|
|
16
|
-
bin += String.fromCharCode(bytes[i]);
|
|
17
|
-
return btoa(bin);
|
|
18
|
-
}
|
|
19
|
-
function extractInputText(input) {
|
|
14
|
+
export function extractInputText(input) {
|
|
20
15
|
return input
|
|
21
16
|
.filter((p) => p.type === 'text')
|
|
22
17
|
.map((p) => (p.type === 'text' ? p.text : ''))
|
|
23
18
|
.join(' ')
|
|
24
19
|
.trim();
|
|
25
20
|
}
|
|
26
|
-
function
|
|
27
|
-
if (req.openRouterId) {
|
|
28
|
-
return req.openRouterId;
|
|
29
|
-
}
|
|
30
|
-
if (req.apiId.includes('/')) {
|
|
31
|
-
return req.apiId;
|
|
32
|
-
}
|
|
33
|
-
if (req.apiId) {
|
|
34
|
-
return `google/${req.apiId}`;
|
|
35
|
-
}
|
|
36
|
-
return String(req.model);
|
|
37
|
-
}
|
|
38
|
-
function buildHeaders(apiKey, config) {
|
|
21
|
+
export function buildSpeechHeaders(apiKey, config) {
|
|
39
22
|
const headers = {
|
|
40
23
|
Authorization: `Bearer ${apiKey}`,
|
|
41
24
|
'Content-Type': 'application/json',
|
|
42
25
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (config.siteName) {
|
|
47
|
-
headers['X-Title'] = config.siteName;
|
|
48
|
-
}
|
|
26
|
+
const gateway = openAiGatewayHeaders(config);
|
|
27
|
+
if (gateway)
|
|
28
|
+
Object.assign(headers, gateway);
|
|
49
29
|
return headers;
|
|
50
30
|
}
|
|
51
|
-
function buildPayload(req, text, speech, configVoice) {
|
|
31
|
+
export function buildPayload(req, text, speech, configVoice) {
|
|
52
32
|
const voice = speech?.voice ?? configVoice;
|
|
53
|
-
const format = speech?.format ?? 'pcm';
|
|
54
33
|
const payload = {
|
|
55
|
-
model:
|
|
34
|
+
model: req.apiId,
|
|
56
35
|
input: text,
|
|
57
|
-
response_format: format,
|
|
58
36
|
};
|
|
37
|
+
if (speech?.format) {
|
|
38
|
+
payload.response_format = speech.format;
|
|
39
|
+
}
|
|
59
40
|
if (voice) {
|
|
60
41
|
payload.voice = voice;
|
|
61
42
|
}
|
|
62
43
|
return payload;
|
|
63
44
|
}
|
|
64
|
-
async function requestSpeech(apiKey, text, req, config) {
|
|
45
|
+
export async function requestSpeech(apiKey, text, req, config) {
|
|
65
46
|
const fetchFn = config.fetch ?? fetch;
|
|
66
47
|
const baseUrl = config.baseUrl?.replace(/\/+$/, '') ?? 'https://openrouter.ai/api/v1';
|
|
67
48
|
const url = `${baseUrl}/audio/speech`;
|
|
68
49
|
return await fetchFn(url, {
|
|
69
50
|
method: 'POST',
|
|
70
|
-
headers:
|
|
51
|
+
headers: buildSpeechHeaders(apiKey, config),
|
|
71
52
|
body: JSON.stringify(buildPayload(req, text, req.speech, config.voice)),
|
|
72
53
|
signal: req.signal,
|
|
73
54
|
});
|
|
74
55
|
}
|
|
75
|
-
function* yieldSpeechSuccess(rawBytes, text, format) {
|
|
76
|
-
let mediaMime = '
|
|
56
|
+
export function* yieldSpeechSuccess(rawBytes, text, format, contentType) {
|
|
57
|
+
let mediaMime = contentType?.split(';')[0]?.trim() || 'application/octet-stream';
|
|
77
58
|
let mediaBytes = rawBytes;
|
|
78
59
|
if (format === 'pcm') {
|
|
79
60
|
mediaMime = 'audio/wav';
|
|
80
61
|
mediaBytes = wrapPcmAsWav(rawBytes);
|
|
81
62
|
}
|
|
63
|
+
else if (format === 'mp3') {
|
|
64
|
+
mediaMime = 'audio/mpeg';
|
|
65
|
+
}
|
|
82
66
|
yield {
|
|
83
67
|
type: 'media',
|
|
84
68
|
media: { mimeType: mediaMime, data: bytesToBase64(mediaBytes) },
|
|
@@ -91,10 +75,13 @@ function* yieldSpeechSuccess(rawBytes, text, format) {
|
|
|
91
75
|
};
|
|
92
76
|
yield { type: 'done' };
|
|
93
77
|
}
|
|
94
|
-
async function* streamSpeech(req, config = {}) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
78
|
+
export async function* streamSpeech(req, config = {}) {
|
|
79
|
+
let apiKey;
|
|
80
|
+
try {
|
|
81
|
+
apiKey = resolveOpenAiGatewayApiKey(config, req.keySlot);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
yield toErrorEvent(err);
|
|
98
85
|
return;
|
|
99
86
|
}
|
|
100
87
|
const text = extractInputText(req.input);
|
|
@@ -113,24 +100,14 @@ async function* streamSpeech(req, config = {}) {
|
|
|
113
100
|
yield toErrorEvent('no audio returned from speech');
|
|
114
101
|
return;
|
|
115
102
|
}
|
|
116
|
-
const format = req.speech?.format
|
|
117
|
-
for (const ev of yieldSpeechSuccess(rawBytes, text, format)) {
|
|
103
|
+
const format = req.speech?.format;
|
|
104
|
+
for (const ev of yieldSpeechSuccess(rawBytes, text, format, res.headers.get('content-type'))) {
|
|
118
105
|
yield ev;
|
|
119
106
|
}
|
|
120
107
|
}
|
|
121
108
|
/** Internal ModelProvider for openAi speech roles. */
|
|
122
|
-
function createSpeechProvider(config = {}) {
|
|
109
|
+
export function createSpeechProvider(config = {}) {
|
|
123
110
|
return {
|
|
124
111
|
complete: (req) => streamSpeech(req, config),
|
|
125
112
|
};
|
|
126
113
|
}
|
|
127
|
-
export { createSpeechProvider, streamSpeech };
|
|
128
|
-
exposeForTests('speech', {
|
|
129
|
-
bytesToBase64,
|
|
130
|
-
extractInputText,
|
|
131
|
-
resolveSpeechWireModel,
|
|
132
|
-
buildHeaders,
|
|
133
|
-
buildPayload,
|
|
134
|
-
requestSpeech,
|
|
135
|
-
yieldSpeechSuccess,
|
|
136
|
-
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function markModuleLoad(label: string): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import-isolation probe for `createProvider`'s lazy adapter loader.
|
|
3
|
+
*
|
|
4
|
+
* Only `create-provider.ts` calls this. Adapters must not import it.
|
|
5
|
+
* When `THEORUM_IMPORT_PROBE=1`, writes `LOADED:<label>` to stdout once per
|
|
6
|
+
* lazy load so subprocess tests can assert adapters stay unloaded until
|
|
7
|
+
* `complete` runs. No-op in normal hosts.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
12
|
+
export function markModuleLoad(label) {
|
|
13
|
+
try {
|
|
14
|
+
const d = dntShim.dntGlobalThis.Deno;
|
|
15
|
+
if (d?.env?.get('THEORUM_IMPORT_PROBE') === '1' && d.stdout?.writeSync) {
|
|
16
|
+
d.stdout.writeSync(new TextEncoder().encode(`LOADED:${label}\n`));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Non-Deno runtime or missing --allow-env.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PCM → WAV helpers shared by speech transports.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export declare function writeAscii(view: DataView, offset: number, str: string): void;
|
|
7
|
+
/** Decode base64 ASCII string to raw byte array. */
|
|
8
|
+
export declare function base64ToBytes(data: string): Uint8Array;
|
|
9
|
+
/** Encode raw byte array to base64 ASCII string. */
|
|
10
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
11
|
+
/** Wrap raw PCM bytes in a RIFF/WAVE container. */
|
|
12
|
+
export declare function wrapPcmAsWav(pcm: Uint8Array, sampleRate?: number): Uint8Array;
|
|
@@ -3,12 +3,26 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { exposeForTests } from './expose-for-tests.js';
|
|
7
6
|
const SAMPLE_RATE = 24000;
|
|
8
|
-
function writeAscii(view, offset, str) {
|
|
7
|
+
export function writeAscii(view, offset, str) {
|
|
9
8
|
for (let i = 0; i < str.length; i++)
|
|
10
9
|
view.setUint8(offset + i, str.charCodeAt(i));
|
|
11
10
|
}
|
|
11
|
+
/** Decode base64 ASCII string to raw byte array. */
|
|
12
|
+
export function base64ToBytes(data) {
|
|
13
|
+
const bin = atob(data);
|
|
14
|
+
const out = new Uint8Array(bin.length);
|
|
15
|
+
for (let i = 0; i < bin.length; i++)
|
|
16
|
+
out[i] = bin.charCodeAt(i);
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
/** Encode raw byte array to base64 ASCII string. */
|
|
20
|
+
export function bytesToBase64(bytes) {
|
|
21
|
+
let bin = '';
|
|
22
|
+
for (let i = 0; i < bytes.length; i++)
|
|
23
|
+
bin += String.fromCharCode(bytes[i]);
|
|
24
|
+
return btoa(bin);
|
|
25
|
+
}
|
|
12
26
|
/** Wrap raw PCM bytes in a RIFF/WAVE container. */
|
|
13
27
|
export function wrapPcmAsWav(pcm, sampleRate = SAMPLE_RATE) {
|
|
14
28
|
const numChannels = 1;
|
|
@@ -34,4 +48,3 @@ export function wrapPcmAsWav(pcm, sampleRate = SAMPLE_RATE) {
|
|
|
34
48
|
new Uint8Array(buf, 44).set(pcm);
|
|
35
49
|
return new Uint8Array(buf);
|
|
36
50
|
}
|
|
37
|
-
exposeForTests('pcm', { writeAscii, wrapPcmAsWav });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function asObject(parsed: unknown): Record<string, unknown> | undefined;
|
|
2
|
+
export declare function dataRecord(raw: string, sseEvent: string): Record<string, unknown>;
|
|
3
|
+
export declare function takeSsePayloads(buffer: string, pendingEvent?: string): {
|
|
4
|
+
rest: string;
|
|
5
|
+
payloads: Record<string, unknown>[];
|
|
6
|
+
pendingEvent: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Async SSE stream chunk reader built on `takeSsePayloads`.
|
|
10
|
+
* Yields every raw payload record (including `sse_done` and `sse_unparsed`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function readSseChunks(body: ReadableStream<Uint8Array>): AsyncGenerator<Record<string, unknown>>;
|
|
13
|
+
/**
|
|
14
|
+
* Generic async SSE stream parser built on `readSseChunks`.
|
|
15
|
+
* Yields parsed JSON objects from `data:` lines; silently skips malformed
|
|
16
|
+
* payloads and terminates on `[DONE]`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseSseStream(body: ReadableStream<Uint8Array>): AsyncGenerator<Record<string, unknown>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const DATA_PREFIX = 'data: ';
|
|
2
|
+
const EVENT_PREFIX = 'event: ';
|
|
3
|
+
const DONE = '[DONE]';
|
|
4
|
+
export function asObject(parsed) {
|
|
5
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
6
|
+
return parsed;
|
|
7
|
+
}
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
export function dataRecord(raw, sseEvent) {
|
|
11
|
+
const data = raw.slice(DATA_PREFIX.length).trim();
|
|
12
|
+
const row = {};
|
|
13
|
+
if (sseEvent) {
|
|
14
|
+
row.sseEvent = sseEvent;
|
|
15
|
+
}
|
|
16
|
+
if (!data || data === DONE) {
|
|
17
|
+
row.eventType = 'sse_done';
|
|
18
|
+
return row;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(data);
|
|
22
|
+
const obj = asObject(parsed);
|
|
23
|
+
if (obj) {
|
|
24
|
+
return { ...obj, ...row };
|
|
25
|
+
}
|
|
26
|
+
row.eventType = 'sse_unparsed';
|
|
27
|
+
row.data = parsed;
|
|
28
|
+
return row;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
row.eventType = 'sse_unparsed';
|
|
32
|
+
row.data = data;
|
|
33
|
+
return row;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function takeSsePayloads(buffer, pendingEvent = '') {
|
|
37
|
+
const payloads = [];
|
|
38
|
+
const chunks = buffer.split('\n');
|
|
39
|
+
const rest = chunks.pop() ?? '';
|
|
40
|
+
let sseEvent = pendingEvent;
|
|
41
|
+
for (const line of chunks) {
|
|
42
|
+
if (line.startsWith(EVENT_PREFIX)) {
|
|
43
|
+
sseEvent = line.slice(EVENT_PREFIX.length).trim();
|
|
44
|
+
}
|
|
45
|
+
else if (line.startsWith(DATA_PREFIX)) {
|
|
46
|
+
payloads.push(dataRecord(line, sseEvent));
|
|
47
|
+
sseEvent = '';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { rest, payloads, pendingEvent: sseEvent };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Async SSE stream chunk reader built on `takeSsePayloads`.
|
|
54
|
+
* Yields every raw payload record (including `sse_done` and `sse_unparsed`).
|
|
55
|
+
*/
|
|
56
|
+
export async function* readSseChunks(body) {
|
|
57
|
+
const reader = body.getReader();
|
|
58
|
+
const decoder = new TextDecoder();
|
|
59
|
+
let buffer = '';
|
|
60
|
+
let pendingEvent = '';
|
|
61
|
+
for (;;) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done)
|
|
64
|
+
break;
|
|
65
|
+
buffer += decoder.decode(value, { stream: true });
|
|
66
|
+
const result = takeSsePayloads(buffer, pendingEvent);
|
|
67
|
+
buffer = result.rest;
|
|
68
|
+
pendingEvent = result.pendingEvent;
|
|
69
|
+
for (const payload of result.payloads) {
|
|
70
|
+
yield payload;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generic async SSE stream parser built on `readSseChunks`.
|
|
76
|
+
* Yields parsed JSON objects from `data:` lines; silently skips malformed
|
|
77
|
+
* payloads and terminates on `[DONE]`.
|
|
78
|
+
*/
|
|
79
|
+
export async function* parseSseStream(body) {
|
|
80
|
+
for await (const payload of readSseChunks(body)) {
|
|
81
|
+
if (payload.eventType === 'sse_done')
|
|
82
|
+
return;
|
|
83
|
+
if (payload.eventType === 'sse_unparsed')
|
|
84
|
+
continue;
|
|
85
|
+
yield payload;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tool-argument JSON parsing for all provider adapters.
|
|
3
|
+
*
|
|
4
|
+
* Malformed or non-object JSON is a hard failure — never invent `{}` or `{ _raw }`.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
export type ParsedToolArguments = {
|
|
9
|
+
ok: true;
|
|
10
|
+
value: Record<string, unknown>;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: string;
|
|
14
|
+
raw: string;
|
|
15
|
+
};
|
|
16
|
+
/** Parse provider / history tool-call arguments. */
|
|
17
|
+
export declare function parseToolArgumentsObject(raw: unknown): ParsedToolArguments;
|