theorum 0.1.7 → 0.1.9
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/esm/src/providers/create-provider.d.ts +7 -0
- package/esm/src/providers/create-provider.js +2 -0
- package/esm/src/providers/gemini-tape.d.ts +14 -0
- package/esm/src/providers/gemini-tape.js +9 -0
- package/esm/src/providers/google-tap.d.ts +10 -0
- package/esm/src/providers/google-tap.js +2 -0
- package/esm/src/providers/interactions.d.ts +37 -1
- package/esm/src/providers/interactions.js +16 -0
- package/esm/src/providers/keys.d.ts +19 -0
- package/esm/src/providers/keys.js +11 -0
- package/esm/src/providers/openrouter-payload.d.ts +9 -2
- package/esm/src/providers/openrouter-payload.js +23 -7
- package/esm/src/providers/openrouter.d.ts +118 -1
- package/esm/src/providers/openrouter.js +101 -140
- package/esm/src/providers/pcm.d.ts +7 -0
- package/esm/src/providers/pcm.js +2 -0
- package/esm/src/providers/provider.d.ts +29 -1
- package/esm/src/providers/provider.js +13 -0
- package/esm/src/providers/speech.d.ts +18 -1
- package/esm/src/providers/speech.js +10 -0
- package/esm/src/providers/sse.d.ts +8 -0
- package/esm/src/providers/sse.js +2 -0
- package/esm/src/streaming/mod.d.ts +7 -0
- package/esm/src/streaming/mod.js +7 -0
- package/esm/src/streaming/readStreamingJsonStringField.d.ts +6 -0
- package/esm/src/streaming/readStreamingJsonStringField.js +55 -0
- package/package.json +6 -3
|
@@ -22,8 +22,15 @@ export interface CreateProviderOptions {
|
|
|
22
22
|
voice?: string;
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
+
declare function isSpeechRole(profile: Profile): boolean;
|
|
25
26
|
/**
|
|
26
27
|
* Create a `ModelProvider` for a profile.
|
|
27
28
|
* One call: protocol/provider (and speech role) pick the transport.
|
|
28
29
|
*/
|
|
29
30
|
export declare function createProvider(profile: Profile, options?: CreateProviderOptions): ModelProvider;
|
|
31
|
+
/** @internal Exported for direct unit testing only. */
|
|
32
|
+
export declare const _internals: {
|
|
33
|
+
isSpeechRole: typeof isSpeechRole;
|
|
34
|
+
createProvider: typeof createProvider;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -36,3 +36,5 @@ export function createProvider(profile, options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
throw new TheorumError(`createProvider: unsupported protocol/provider pair '${protocol}'/'${provider}'`);
|
|
38
38
|
}
|
|
39
|
+
/** @internal Exported for direct unit testing only. */
|
|
40
|
+
export const _internals = { isSpeechRole, createProvider };
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
+
declare function isImageBlob(rec: Record<string, unknown>): boolean;
|
|
2
|
+
declare function scrubEntry(rec: Record<string, unknown>, key: string, nested: unknown): Promise<[string, unknown]>;
|
|
3
|
+
declare function scrubRecord(rec: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
4
|
+
declare function scrubGemini(value: unknown): Promise<unknown>;
|
|
5
|
+
declare function redactCanaryInTree(value: unknown, canary: string): unknown;
|
|
1
6
|
declare function tapeGemini(value: unknown, canary: string): Promise<unknown>;
|
|
2
7
|
export { tapeGemini };
|
|
8
|
+
/** @internal Exported for direct unit testing only. */
|
|
9
|
+
export declare const _internals: {
|
|
10
|
+
isImageBlob: typeof isImageBlob;
|
|
11
|
+
scrubEntry: typeof scrubEntry;
|
|
12
|
+
scrubRecord: typeof scrubRecord;
|
|
13
|
+
scrubGemini: typeof scrubGemini;
|
|
14
|
+
redactCanaryInTree: typeof redactCanaryInTree;
|
|
15
|
+
tapeGemini: typeof tapeGemini;
|
|
16
|
+
};
|
|
@@ -44,3 +44,12 @@ async function tapeGemini(value, canary) {
|
|
|
44
44
|
return redactCanaryInTree(await scrubGemini(value), canary);
|
|
45
45
|
}
|
|
46
46
|
export { tapeGemini };
|
|
47
|
+
/** @internal Exported for direct unit testing only. */
|
|
48
|
+
export const _internals = {
|
|
49
|
+
isImageBlob,
|
|
50
|
+
scrubEntry,
|
|
51
|
+
scrubRecord,
|
|
52
|
+
scrubGemini,
|
|
53
|
+
redactCanaryInTree,
|
|
54
|
+
tapeGemini,
|
|
55
|
+
};
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import type { ProviderCompleteRequest } from '../kernel/types.js';
|
|
2
|
+
declare function tapeHeaderValue(key: string, value: string): string;
|
|
3
|
+
declare function tapeHeaders(headers?: HeadersInit): Record<string, string>;
|
|
4
|
+
declare function throwRow(err: unknown): Record<string, unknown>;
|
|
2
5
|
declare function tapFetch(tap: ProviderCompleteRequest['tapGemini'], send?: typeof fetch): typeof fetch;
|
|
3
6
|
export { tapFetch };
|
|
7
|
+
/** @internal Exported for direct unit testing only. */
|
|
8
|
+
export declare const _internals: {
|
|
9
|
+
tapeHeaderValue: typeof tapeHeaderValue;
|
|
10
|
+
tapeHeaders: typeof tapeHeaders;
|
|
11
|
+
throwRow: typeof throwRow;
|
|
12
|
+
tapFetch: typeof tapFetch;
|
|
13
|
+
};
|
|
@@ -1,5 +1,41 @@
|
|
|
1
|
-
import type { ProviderCompleteRequest } from '../kernel/types.js';
|
|
1
|
+
import type { InteractionPart, ProviderCompleteRequest } from '../kernel/types.js';
|
|
2
2
|
declare function camelToSnake(key: string): string;
|
|
3
|
+
declare function toGoogleValue(value: unknown): unknown;
|
|
4
|
+
declare function wirePart(part: InteractionPart): Record<string, string>;
|
|
5
|
+
declare function userInputStep(parts: InteractionPart[]): {
|
|
6
|
+
type: string;
|
|
7
|
+
content: Record<string, string>[];
|
|
8
|
+
};
|
|
9
|
+
declare function historyStep(msg: import('../kernel/types.js').TurnHistoryMessage): {
|
|
10
|
+
type: string;
|
|
11
|
+
content: Record<string, string>[];
|
|
12
|
+
};
|
|
13
|
+
declare function systemHoldsUserInput(system: string, parts: InteractionPart[]): boolean;
|
|
14
|
+
declare function jsonResponseFormat(schema: Record<string, unknown>): unknown[];
|
|
15
|
+
declare function attachResponseFormat(req: ProviderCompleteRequest, camel: Record<string, unknown>): void;
|
|
16
|
+
declare function attachSpeechConfig(req: ProviderCompleteRequest, generationConfig: Record<string, unknown>): void;
|
|
17
|
+
declare function inputStepsFromRequest(req: ProviderCompleteRequest): {
|
|
18
|
+
type: string;
|
|
19
|
+
content: Record<string, string>[];
|
|
20
|
+
}[];
|
|
21
|
+
declare function applyOptionalRequestFields(req: ProviderCompleteRequest, camel: Record<string, unknown>): void;
|
|
22
|
+
declare function baseInteractionsBody(req: ProviderCompleteRequest): Record<string, unknown>;
|
|
3
23
|
/** Interactions REST body for one complete() call (Google snake_case keys). */
|
|
4
24
|
declare function toInteractionsBody(req: ProviderCompleteRequest): Record<string, unknown>;
|
|
5
25
|
export { camelToSnake, toInteractionsBody };
|
|
26
|
+
/** @internal Exported for direct unit testing only. */
|
|
27
|
+
export declare const _internals: {
|
|
28
|
+
camelToSnake: typeof camelToSnake;
|
|
29
|
+
toGoogleValue: typeof toGoogleValue;
|
|
30
|
+
wirePart: typeof wirePart;
|
|
31
|
+
userInputStep: typeof userInputStep;
|
|
32
|
+
historyStep: typeof historyStep;
|
|
33
|
+
systemHoldsUserInput: typeof systemHoldsUserInput;
|
|
34
|
+
jsonResponseFormat: typeof jsonResponseFormat;
|
|
35
|
+
attachResponseFormat: typeof attachResponseFormat;
|
|
36
|
+
attachSpeechConfig: typeof attachSpeechConfig;
|
|
37
|
+
inputStepsFromRequest: typeof inputStepsFromRequest;
|
|
38
|
+
applyOptionalRequestFields: typeof applyOptionalRequestFields;
|
|
39
|
+
baseInteractionsBody: typeof baseInteractionsBody;
|
|
40
|
+
toInteractionsBody: typeof toInteractionsBody;
|
|
41
|
+
};
|
|
@@ -151,3 +151,19 @@ function toInteractionsBody(req) {
|
|
|
151
151
|
return toGoogleValue(camel);
|
|
152
152
|
}
|
|
153
153
|
export { camelToSnake, toInteractionsBody };
|
|
154
|
+
/** @internal Exported for direct unit testing only. */
|
|
155
|
+
export const _internals = {
|
|
156
|
+
camelToSnake,
|
|
157
|
+
toGoogleValue,
|
|
158
|
+
wirePart,
|
|
159
|
+
userInputStep,
|
|
160
|
+
historyStep,
|
|
161
|
+
systemHoldsUserInput,
|
|
162
|
+
jsonResponseFormat,
|
|
163
|
+
attachResponseFormat,
|
|
164
|
+
attachSpeechConfig,
|
|
165
|
+
inputStepsFromRequest,
|
|
166
|
+
applyOptionalRequestFields,
|
|
167
|
+
baseInteractionsBody,
|
|
168
|
+
toInteractionsBody,
|
|
169
|
+
};
|
|
@@ -13,7 +13,26 @@ interface GeminiTransport {
|
|
|
13
13
|
wait?: (ms: number) => Promise<void>;
|
|
14
14
|
fetch?: typeof fetch;
|
|
15
15
|
}
|
|
16
|
+
declare function waitDefault(ms: number): Promise<void>;
|
|
17
|
+
declare function isQuota(err: unknown): boolean;
|
|
18
|
+
declare function isTransientHttp(status: number): boolean;
|
|
19
|
+
declare function isTransientThrown(err: unknown): boolean;
|
|
20
|
+
declare function requireKey(vault: GeminiVault, bucket: GeminiBucket): string;
|
|
21
|
+
declare function backoffMs(attempt: number): number;
|
|
22
|
+
declare function canOverflow(bucket: GeminiBucket, vault: GeminiVault, primary: string): string | undefined;
|
|
16
23
|
declare function withGeminiKey<T>(bucket: GeminiBucket, run: (apiKey: string) => Promise<T>, transport: GeminiTransport): Promise<T>;
|
|
24
|
+
declare function withApiKey(init: RequestInit, apiKey: string): RequestInit;
|
|
17
25
|
declare function fetchGemini(url: string, init: RequestInit, bucket: GeminiBucket, transport: GeminiTransport): Promise<Response>;
|
|
18
26
|
export type { GeminiTransport, GeminiVault };
|
|
19
27
|
export { fetchGemini, withGeminiKey };
|
|
28
|
+
/** @internal Exported for direct unit testing only. */
|
|
29
|
+
export declare const _internals: {
|
|
30
|
+
waitDefault: typeof waitDefault;
|
|
31
|
+
isQuota: typeof isQuota;
|
|
32
|
+
isTransientHttp: typeof isTransientHttp;
|
|
33
|
+
isTransientThrown: typeof isTransientThrown;
|
|
34
|
+
requireKey: typeof requireKey;
|
|
35
|
+
backoffMs: typeof backoffMs;
|
|
36
|
+
canOverflow: typeof canOverflow;
|
|
37
|
+
withApiKey: typeof withApiKey;
|
|
38
|
+
};
|
|
@@ -137,3 +137,14 @@ async function fetchGemini(url, init, bucket, transport) {
|
|
|
137
137
|
return last;
|
|
138
138
|
}
|
|
139
139
|
export { fetchGemini, withGeminiKey };
|
|
140
|
+
/** @internal Exported for direct unit testing only. */
|
|
141
|
+
export const _internals = {
|
|
142
|
+
waitDefault,
|
|
143
|
+
isQuota,
|
|
144
|
+
isTransientHttp,
|
|
145
|
+
isTransientThrown,
|
|
146
|
+
requireKey,
|
|
147
|
+
backoffMs,
|
|
148
|
+
canOverflow,
|
|
149
|
+
withApiKey,
|
|
150
|
+
};
|
|
@@ -28,5 +28,12 @@ interface OpenRouterWireIds {
|
|
|
28
28
|
declare function resolveOpenRouterModel(modelId: ModelId | string, customMap?: Record<string, string>, wire?: OpenRouterWireIds): string;
|
|
29
29
|
/** Convert a provider-neutral request into an OpenRouter chat completion payload. */
|
|
30
30
|
declare function toOpenRouterPayload(req: ProviderCompleteRequest, config: OpenRouterConfig): Record<string, unknown>;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
interface ResolvedPlugins {
|
|
32
|
+
plugins: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
}>;
|
|
35
|
+
webSearch: boolean;
|
|
36
|
+
}
|
|
37
|
+
declare function resolveOpenRouterPlugins(builtins: readonly string[]): ResolvedPlugins;
|
|
38
|
+
export type { OpenRouterConfig, OpenRouterWireIds, ResolvedPlugins };
|
|
39
|
+
export { resolveOpenRouterModel, resolveOpenRouterPlugins, toOpenRouterPayload };
|
|
@@ -167,13 +167,29 @@ function toOpenRouterPayload(req, config) {
|
|
|
167
167
|
if (tools.length > 0) {
|
|
168
168
|
payload.tools = tools;
|
|
169
169
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
.
|
|
173
|
-
|
|
174
|
-
if (plugins.length > 0) {
|
|
175
|
-
payload.plugins = plugins;
|
|
170
|
+
const resolved = resolveOpenRouterPlugins(req.builtins);
|
|
171
|
+
if (resolved.webSearch) {
|
|
172
|
+
payload.web_search_options = {};
|
|
173
|
+
}
|
|
174
|
+
if (resolved.plugins.length > 0) {
|
|
175
|
+
payload.plugins = resolved.plugins;
|
|
176
176
|
}
|
|
177
177
|
return payload;
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
function resolveOpenRouterPlugins(builtins) {
|
|
180
|
+
let webSearch = false;
|
|
181
|
+
const plugins = [];
|
|
182
|
+
for (const id of builtins) {
|
|
183
|
+
const pluginId = getTool(id)?.openRouterPlugin;
|
|
184
|
+
if (!pluginId)
|
|
185
|
+
continue;
|
|
186
|
+
if (pluginId === 'web') {
|
|
187
|
+
webSearch = true;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
plugins.push({ id: pluginId });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return { plugins, webSearch };
|
|
194
|
+
}
|
|
195
|
+
export { resolveOpenRouterModel, resolveOpenRouterPlugins, toOpenRouterPayload };
|
|
@@ -7,9 +7,126 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import type
|
|
10
|
+
import { createOpenRouter, type OpenRouterChatSettings } from '@openrouter/ai-sdk-provider';
|
|
11
|
+
import { type LanguageModelUsage, type ModelMessage, streamText, type TextStreamPart, type ToolSet } from 'ai';
|
|
12
|
+
import type { DynamicToolDeclaration, InteractionPart, ModelProvider, ProviderCompleteRequest, TurnEvent, TurnHistoryMessage, TurnTokens } from '../kernel/types.js';
|
|
11
13
|
import { type OpenRouterConfig, resolveOpenRouterModel, toOpenRouterPayload } from './openrouter-payload.js';
|
|
14
|
+
interface StreamAccumulator {
|
|
15
|
+
text: string;
|
|
16
|
+
evidenceSeen: boolean;
|
|
17
|
+
emittedTokens: boolean;
|
|
18
|
+
errored: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface OpenRouterStreamContext {
|
|
21
|
+
openrouter: ReturnType<typeof createOpenRouter>;
|
|
22
|
+
modelName: string;
|
|
23
|
+
}
|
|
24
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
25
|
+
[key: string]: JsonValue;
|
|
26
|
+
};
|
|
27
|
+
type ProviderOptions = Record<string, {
|
|
28
|
+
[key: string]: JsonValue;
|
|
29
|
+
}>;
|
|
30
|
+
declare function trimApiKey(explicitKey?: string): string | undefined;
|
|
31
|
+
declare function createAccumulator(): StreamAccumulator;
|
|
32
|
+
declare function mediaPart(part: InteractionPart): Record<string, unknown>;
|
|
33
|
+
declare function contentFromParts(parts: InteractionPart[]): string | Array<Record<string, unknown>>;
|
|
34
|
+
declare function parseToolInput(raw: string): unknown;
|
|
35
|
+
declare function stringDefault(value: string | undefined, fallback: string): string;
|
|
36
|
+
declare function fallbackToolCallId(name?: string): string;
|
|
37
|
+
declare function toolResultContent(msg: TurnHistoryMessage): ModelMessage;
|
|
38
|
+
declare function assistantToolCallContent(msg: TurnHistoryMessage): ModelMessage | null;
|
|
39
|
+
declare function historyMessage(msg: TurnHistoryMessage): ModelMessage | null;
|
|
40
|
+
declare function contentFromOptionalParts(parts: InteractionPart[] | undefined, text: string | undefined): string | Array<Record<string, unknown>>;
|
|
41
|
+
declare function contentHistoryMessage(msg: TurnHistoryMessage): ModelMessage;
|
|
42
|
+
declare function sourceEvent(part: Extract<TextStreamPart<ToolSet>, {
|
|
43
|
+
type: 'source';
|
|
44
|
+
}>): TurnEvent;
|
|
45
|
+
declare function buildMessages(req: ProviderCompleteRequest): ModelMessage[];
|
|
46
|
+
declare function schemaForTool(decl: DynamicToolDeclaration): Record<string, unknown>;
|
|
47
|
+
declare function buildTools(dynamicTools?: DynamicToolDeclaration[]): ToolSet | undefined;
|
|
48
|
+
declare function openRouterSettings(req: ProviderCompleteRequest): OpenRouterChatSettings | undefined;
|
|
49
|
+
declare function tokensFromUsage(usage: LanguageModelUsage): TurnTokens | undefined;
|
|
50
|
+
declare function rawRecord(value: unknown): Record<string, unknown> | undefined;
|
|
51
|
+
declare function stringArray(value: unknown): string[] | undefined;
|
|
52
|
+
declare function metadataRecord(raw: Record<string, unknown>, key: string): Record<string, unknown> | undefined;
|
|
53
|
+
declare function citationCandidates(raw: Record<string, unknown>): unknown[];
|
|
54
|
+
declare function nestedCitations(raw: Record<string, unknown>): string[] | undefined;
|
|
55
|
+
declare function metadataAnnotations(raw: Record<string, unknown>): unknown[] | undefined;
|
|
56
|
+
declare function evidenceFromMetadata(metadata: unknown, acc: StreamAccumulator): TurnEvent | undefined;
|
|
57
|
+
declare function toolArguments(input: unknown): Record<string, unknown> | undefined;
|
|
58
|
+
declare function toolResultData(output: unknown): Record<string, unknown> | undefined;
|
|
59
|
+
declare function rawThoughtEvent(raw: Record<string, unknown>): TurnEvent | undefined;
|
|
60
|
+
declare function rawChoiceMessageEvidence(raw: Record<string, unknown>, acc: StreamAccumulator): TurnEvent | undefined;
|
|
61
|
+
declare function rawEvents(raw: unknown, acc: StreamAccumulator): TurnEvent[];
|
|
62
|
+
declare function toolCallEvent(part: Extract<TextStreamPart<ToolSet>, {
|
|
63
|
+
type: 'tool-call';
|
|
64
|
+
}>): TurnEvent;
|
|
65
|
+
declare function toolResultEvent(part: Extract<TextStreamPart<ToolSet>, {
|
|
66
|
+
type: 'tool-result';
|
|
67
|
+
}>): TurnEvent;
|
|
68
|
+
declare function tokenEvent(part: Extract<TextStreamPart<ToolSet>, {
|
|
69
|
+
type: 'finish';
|
|
70
|
+
}>): TurnEvent | undefined;
|
|
71
|
+
declare function providerMetadataEvent(part: TextStreamPart<ToolSet>, acc: StreamAccumulator): TurnEvent | undefined;
|
|
72
|
+
declare function eventFromPart(part: TextStreamPart<ToolSet>, acc: StreamAccumulator): TurnEvent[];
|
|
73
|
+
declare function primaryEventFromPart(part: TextStreamPart<ToolSet>, acc: StreamAccumulator): TurnEvent | undefined;
|
|
74
|
+
declare function finishEvent(part: Extract<TextStreamPart<ToolSet>, {
|
|
75
|
+
type: 'finish';
|
|
76
|
+
}>, acc: StreamAccumulator): TurnEvent | undefined;
|
|
77
|
+
declare function streamTextOptions(req: ProviderCompleteRequest, context: OpenRouterStreamContext): Parameters<typeof streamText>[0];
|
|
78
|
+
declare function missingOpenRouterKey(): TurnEvent;
|
|
79
|
+
declare function finalEvents(req: ProviderCompleteRequest, acc: StreamAccumulator): Generator<TurnEvent>;
|
|
80
|
+
declare function responseFormatFor(req: ProviderCompleteRequest): Record<string, JsonValue> | undefined;
|
|
81
|
+
declare function providerOptionsFor(req: ProviderCompleteRequest): ProviderOptions | undefined;
|
|
82
|
+
declare function openRouterHeaders(config: OpenRouterConfig): Record<string, string> | undefined;
|
|
12
83
|
/** Create a `ModelProvider` backed by OpenRouter through AI SDK Core. */
|
|
13
84
|
declare function createOpenRouterProvider(config?: OpenRouterConfig): ModelProvider;
|
|
14
85
|
export type { OpenRouterConfig };
|
|
15
86
|
export { createOpenRouterProvider, resolveOpenRouterModel, toOpenRouterPayload };
|
|
87
|
+
/** @internal Exported for direct unit testing only. */
|
|
88
|
+
export declare const _internals: {
|
|
89
|
+
trimApiKey: typeof trimApiKey;
|
|
90
|
+
createAccumulator: typeof createAccumulator;
|
|
91
|
+
mediaPart: typeof mediaPart;
|
|
92
|
+
contentFromParts: typeof contentFromParts;
|
|
93
|
+
parseToolInput: typeof parseToolInput;
|
|
94
|
+
stringDefault: typeof stringDefault;
|
|
95
|
+
fallbackToolCallId: typeof fallbackToolCallId;
|
|
96
|
+
toolResultContent: typeof toolResultContent;
|
|
97
|
+
assistantToolCallContent: typeof assistantToolCallContent;
|
|
98
|
+
historyMessage: typeof historyMessage;
|
|
99
|
+
contentFromOptionalParts: typeof contentFromOptionalParts;
|
|
100
|
+
contentHistoryMessage: typeof contentHistoryMessage;
|
|
101
|
+
buildMessages: typeof buildMessages;
|
|
102
|
+
schemaForTool: typeof schemaForTool;
|
|
103
|
+
buildTools: typeof buildTools;
|
|
104
|
+
openRouterSettings: typeof openRouterSettings;
|
|
105
|
+
tokensFromUsage: typeof tokensFromUsage;
|
|
106
|
+
rawRecord: typeof rawRecord;
|
|
107
|
+
stringArray: typeof stringArray;
|
|
108
|
+
metadataRecord: typeof metadataRecord;
|
|
109
|
+
citationCandidates: typeof citationCandidates;
|
|
110
|
+
nestedCitations: typeof nestedCitations;
|
|
111
|
+
metadataAnnotations: typeof metadataAnnotations;
|
|
112
|
+
evidenceFromMetadata: typeof evidenceFromMetadata;
|
|
113
|
+
toolArguments: typeof toolArguments;
|
|
114
|
+
toolResultData: typeof toolResultData;
|
|
115
|
+
rawThoughtEvent: typeof rawThoughtEvent;
|
|
116
|
+
rawChoiceMessageEvidence: typeof rawChoiceMessageEvidence;
|
|
117
|
+
rawEvents: typeof rawEvents;
|
|
118
|
+
toolCallEvent: typeof toolCallEvent;
|
|
119
|
+
toolResultEvent: typeof toolResultEvent;
|
|
120
|
+
tokenEvent: typeof tokenEvent;
|
|
121
|
+
providerMetadataEvent: typeof providerMetadataEvent;
|
|
122
|
+
eventFromPart: typeof eventFromPart;
|
|
123
|
+
primaryEventFromPart: typeof primaryEventFromPart;
|
|
124
|
+
finishEvent: typeof finishEvent;
|
|
125
|
+
sourceEvent: typeof sourceEvent;
|
|
126
|
+
finalEvents: typeof finalEvents;
|
|
127
|
+
responseFormatFor: typeof responseFormatFor;
|
|
128
|
+
providerOptionsFor: typeof providerOptionsFor;
|
|
129
|
+
openRouterHeaders: typeof openRouterHeaders;
|
|
130
|
+
missingOpenRouterKey: typeof missingOpenRouterKey;
|
|
131
|
+
streamTextOptions: typeof streamTextOptions;
|
|
132
|
+
};
|
|
@@ -11,9 +11,8 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
|
11
11
|
import { jsonSchema, streamText, tool, } from 'ai';
|
|
12
12
|
import { isAbortError, toErrorEvent } from '../guardrails/error.js';
|
|
13
13
|
import { tryStructured } from '../kernel/engine/delta.js';
|
|
14
|
-
import {
|
|
15
|
-
import { resolveOpenRouterModel, toOpenRouterPayload, } from './openrouter-payload.js';
|
|
16
|
-
import { takeSsePayloads } from './sse.js';
|
|
14
|
+
import { getStructured } from '../kernel/registry/schemas.js';
|
|
15
|
+
import { resolveOpenRouterModel, resolveOpenRouterPlugins, toOpenRouterPayload, } from './openrouter-payload.js';
|
|
17
16
|
function trimApiKey(explicitKey) {
|
|
18
17
|
if (explicitKey?.trim()) {
|
|
19
18
|
return explicitKey.trim();
|
|
@@ -26,7 +25,6 @@ function createAccumulator() {
|
|
|
26
25
|
evidenceSeen: false,
|
|
27
26
|
emittedTokens: false,
|
|
28
27
|
errored: false,
|
|
29
|
-
toolInputs: new Map(),
|
|
30
28
|
};
|
|
31
29
|
}
|
|
32
30
|
function mediaPart(part) {
|
|
@@ -61,9 +59,6 @@ function parseToolInput(raw) {
|
|
|
61
59
|
function stringDefault(value, fallback) {
|
|
62
60
|
return value === undefined ? fallback : value;
|
|
63
61
|
}
|
|
64
|
-
function optionalSingle(value) {
|
|
65
|
-
return value === undefined ? undefined : [value];
|
|
66
|
-
}
|
|
67
62
|
function fallbackToolCallId(name) {
|
|
68
63
|
return `call_${stringDefault(name, 'tool')}`;
|
|
69
64
|
}
|
|
@@ -111,34 +106,21 @@ function contentHistoryMessage(msg) {
|
|
|
111
106
|
const content = contentFromOptionalParts(msg.parts, msg.content);
|
|
112
107
|
return { role: msg.role, content };
|
|
113
108
|
}
|
|
114
|
-
function sourceValue(part, key) {
|
|
115
|
-
const source = part;
|
|
116
|
-
const value = source[key];
|
|
117
|
-
return typeof value === 'string' ? value : undefined;
|
|
118
|
-
}
|
|
119
|
-
function sourceUrl(part) {
|
|
120
|
-
return sourceValue(part, 'url');
|
|
121
|
-
}
|
|
122
|
-
function sourceTitle(part) {
|
|
123
|
-
return sourceValue(part, 'title') ?? sourceUrl(part);
|
|
124
|
-
}
|
|
125
|
-
function sourceList(title, url) {
|
|
126
|
-
if (title === undefined || url === undefined) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
return [{ title, uri: url, type: 'web' }];
|
|
130
|
-
}
|
|
131
109
|
function sourceEvent(part) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
110
|
+
if (part.sourceType !== 'url') {
|
|
111
|
+
return {
|
|
112
|
+
type: 'evidence',
|
|
113
|
+
evidence: { provider: 'openrouter', raw: part },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const title = part.title ?? part.url;
|
|
135
117
|
return {
|
|
136
118
|
type: 'evidence',
|
|
137
119
|
evidence: {
|
|
138
120
|
provider: 'openrouter',
|
|
139
|
-
raw,
|
|
140
|
-
citations:
|
|
141
|
-
sources:
|
|
121
|
+
raw: part,
|
|
122
|
+
citations: [part.url],
|
|
123
|
+
sources: [{ title, uri: part.url, type: 'web' }],
|
|
142
124
|
},
|
|
143
125
|
};
|
|
144
126
|
}
|
|
@@ -171,19 +153,17 @@ function buildTools(dynamicTools) {
|
|
|
171
153
|
}
|
|
172
154
|
return tools;
|
|
173
155
|
}
|
|
174
|
-
function openRouterPlugins(req) {
|
|
175
|
-
const plugins = req.builtins
|
|
176
|
-
.map((id) => getTool(id)?.openRouterPlugin)
|
|
177
|
-
.filter((id) => Boolean(id))
|
|
178
|
-
.map((id) => ({ id }));
|
|
179
|
-
return plugins.length > 0 ? plugins : undefined;
|
|
180
|
-
}
|
|
181
156
|
function openRouterSettings(req) {
|
|
182
|
-
const plugins =
|
|
183
|
-
if (!
|
|
157
|
+
const { plugins, webSearch } = resolveOpenRouterPlugins(req.builtins);
|
|
158
|
+
if (plugins.length === 0 && !webSearch) {
|
|
184
159
|
return undefined;
|
|
185
160
|
}
|
|
186
|
-
|
|
161
|
+
const settings = {};
|
|
162
|
+
if (plugins.length > 0)
|
|
163
|
+
settings.plugins = plugins;
|
|
164
|
+
if (webSearch)
|
|
165
|
+
settings.web_search_options = {};
|
|
166
|
+
return settings;
|
|
187
167
|
}
|
|
188
168
|
function tokensFromUsage(usage) {
|
|
189
169
|
const input = usage.inputTokens ?? 0;
|
|
@@ -269,17 +249,6 @@ function toolArguments(input) {
|
|
|
269
249
|
}
|
|
270
250
|
return { value: input };
|
|
271
251
|
}
|
|
272
|
-
function isEmptyRecord(input) {
|
|
273
|
-
return Boolean(input) && Object.keys(input ?? {}).length === 0;
|
|
274
|
-
}
|
|
275
|
-
function toolCallArguments(part, acc) {
|
|
276
|
-
const fromInput = toolArguments(part.input);
|
|
277
|
-
const rawInput = acc.toolInputs.get(part.toolCallId);
|
|
278
|
-
if ((!fromInput || isEmptyRecord(fromInput)) && rawInput) {
|
|
279
|
-
return toolArguments(parseToolInput(rawInput));
|
|
280
|
-
}
|
|
281
|
-
return fromInput;
|
|
282
|
-
}
|
|
283
252
|
function toolResultData(output) {
|
|
284
253
|
return rawRecord(output);
|
|
285
254
|
}
|
|
@@ -333,45 +302,12 @@ function rawEvents(raw, acc) {
|
|
|
333
302
|
}
|
|
334
303
|
return events;
|
|
335
304
|
}
|
|
336
|
-
|
|
337
|
-
if (!res.body) {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
const reader = res.body.getReader();
|
|
341
|
-
const decoder = new TextDecoder();
|
|
342
|
-
const events = [];
|
|
343
|
-
const acc = createAccumulator();
|
|
344
|
-
let buffer = '';
|
|
345
|
-
let pendingEvent = '';
|
|
346
|
-
while (true) {
|
|
347
|
-
const { done, value } = await reader.read();
|
|
348
|
-
if (done) {
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
buffer += decoder.decode(value, { stream: true });
|
|
352
|
-
const taken = takeSsePayloads(buffer, pendingEvent);
|
|
353
|
-
buffer = taken.rest;
|
|
354
|
-
pendingEvent = taken.pendingEvent;
|
|
355
|
-
for (const payload of taken.payloads) {
|
|
356
|
-
events.push(...rawEvents(payload, acc));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return events;
|
|
360
|
-
}
|
|
361
|
-
function captureFetch(config, setCapture) {
|
|
362
|
-
const fetchFn = config.fetch ?? fetch;
|
|
363
|
-
return async (input, init) => {
|
|
364
|
-
const res = await fetchFn(input, init);
|
|
365
|
-
setCapture(collectRawEvents(res.clone()).catch(() => []));
|
|
366
|
-
return res;
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function toolCallEvent(part, acc) {
|
|
305
|
+
function toolCallEvent(part) {
|
|
370
306
|
return {
|
|
371
307
|
type: 'tool',
|
|
372
308
|
tool: {
|
|
373
309
|
name: part.toolName,
|
|
374
|
-
arguments:
|
|
310
|
+
arguments: toolArguments(part.input),
|
|
375
311
|
id: part.toolCallId,
|
|
376
312
|
},
|
|
377
313
|
};
|
|
@@ -401,22 +337,7 @@ function providerMetadataEvent(part, acc) {
|
|
|
401
337
|
}
|
|
402
338
|
return evidenceFromMetadata(part.providerMetadata, acc);
|
|
403
339
|
}
|
|
404
|
-
function appendToolInput(part, acc) {
|
|
405
|
-
if (part.type === 'tool-input-start') {
|
|
406
|
-
acc.toolInputs.set(part.id, '');
|
|
407
|
-
return true;
|
|
408
|
-
}
|
|
409
|
-
if (part.type === 'tool-input-delta') {
|
|
410
|
-
const current = acc.toolInputs.get(part.id) ?? '';
|
|
411
|
-
acc.toolInputs.set(part.id, current + part.delta);
|
|
412
|
-
return true;
|
|
413
|
-
}
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
340
|
function eventFromPart(part, acc) {
|
|
417
|
-
if (appendToolInput(part, acc)) {
|
|
418
|
-
return [];
|
|
419
|
-
}
|
|
420
341
|
const mapped = primaryEventFromPart(part, acc);
|
|
421
342
|
if (mapped) {
|
|
422
343
|
return [mapped];
|
|
@@ -432,11 +353,15 @@ function primaryEventFromPart(part, acc) {
|
|
|
432
353
|
case 'reasoning-delta':
|
|
433
354
|
return { type: 'thought', text: part.text };
|
|
434
355
|
case 'tool-call':
|
|
435
|
-
return toolCallEvent(part
|
|
356
|
+
return toolCallEvent(part);
|
|
436
357
|
case 'tool-result':
|
|
437
358
|
return toolResultEvent(part);
|
|
438
|
-
case 'source':
|
|
359
|
+
case 'source': {
|
|
360
|
+
if (acc.evidenceSeen)
|
|
361
|
+
return undefined;
|
|
362
|
+
acc.evidenceSeen = true;
|
|
439
363
|
return sourceEvent(part);
|
|
364
|
+
}
|
|
440
365
|
case 'finish':
|
|
441
366
|
return finishEvent(part, acc);
|
|
442
367
|
case 'error':
|
|
@@ -456,27 +381,12 @@ function finishEvent(part, acc) {
|
|
|
456
381
|
}
|
|
457
382
|
return event;
|
|
458
383
|
}
|
|
459
|
-
function emitRawEvents(rawCapture, acc) {
|
|
460
|
-
return (rawCapture ?? Promise.resolve([])).then((events) => events.filter((event) => {
|
|
461
|
-
if (event.type !== 'evidence') {
|
|
462
|
-
return true;
|
|
463
|
-
}
|
|
464
|
-
if (acc.evidenceSeen) {
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
acc.evidenceSeen = true;
|
|
468
|
-
return true;
|
|
469
|
-
}));
|
|
470
|
-
}
|
|
471
384
|
function createStreamContext(req, config, apiKey) {
|
|
472
|
-
let rawCapture;
|
|
473
385
|
const openrouter = createOpenRouter({
|
|
474
386
|
apiKey,
|
|
475
387
|
baseURL: config.baseUrl,
|
|
476
388
|
headers: openRouterHeaders(config),
|
|
477
|
-
fetch:
|
|
478
|
-
rawCapture = capture;
|
|
479
|
-
}),
|
|
389
|
+
fetch: config.fetch,
|
|
480
390
|
compatibility: 'strict',
|
|
481
391
|
});
|
|
482
392
|
return {
|
|
@@ -485,27 +395,26 @@ function createStreamContext(req, config, apiKey) {
|
|
|
485
395
|
apiId: req.apiId,
|
|
486
396
|
openRouterId: req.openRouterId,
|
|
487
397
|
}),
|
|
488
|
-
rawCapture: () => rawCapture,
|
|
489
398
|
};
|
|
490
399
|
}
|
|
491
400
|
function streamTextOptions(req, context) {
|
|
492
401
|
return {
|
|
493
402
|
model: context.openrouter.chat(context.modelName, openRouterSettings(req)),
|
|
494
|
-
|
|
403
|
+
instructions: req.system,
|
|
495
404
|
messages: buildMessages(req),
|
|
496
405
|
allowSystemInMessages: true,
|
|
497
406
|
temperature: req.temperature,
|
|
498
407
|
maxOutputTokens: req.maxOutputTokens,
|
|
499
408
|
tools: buildTools(req.dynamicTools),
|
|
500
409
|
providerOptions: providerOptionsFor(req),
|
|
501
|
-
|
|
410
|
+
include: { rawChunks: true },
|
|
502
411
|
abortSignal: req.signal,
|
|
503
412
|
onError: () => undefined,
|
|
504
413
|
};
|
|
505
414
|
}
|
|
506
415
|
async function* yieldAiSdkStream(req, acc, context) {
|
|
507
416
|
const result = streamText(streamTextOptions(req, context));
|
|
508
|
-
for await (const part of result.
|
|
417
|
+
for await (const part of result.stream) {
|
|
509
418
|
if (part.type === 'raw') {
|
|
510
419
|
req.tapGemini?.(rawRecord(part.rawValue) ?? { rawValue: part.rawValue });
|
|
511
420
|
for (const event of rawEvents(part.rawValue, acc)) {
|
|
@@ -518,16 +427,6 @@ async function* yieldAiSdkStream(req, acc, context) {
|
|
|
518
427
|
}
|
|
519
428
|
}
|
|
520
429
|
}
|
|
521
|
-
async function* yieldCapturedRawEvents(context, acc) {
|
|
522
|
-
for (const event of await emitRawEvents(context.rawCapture(), acc)) {
|
|
523
|
-
yield event;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
async function* yieldCapturedRawEventsUnchecked(context) {
|
|
527
|
-
for (const event of await (context.rawCapture() ?? Promise.resolve([]))) {
|
|
528
|
-
yield event;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
430
|
function missingOpenRouterKey() {
|
|
532
431
|
return toErrorEvent('missing OpenRouter API key');
|
|
533
432
|
}
|
|
@@ -541,14 +440,12 @@ async function* streamOpenRouter(req, config) {
|
|
|
541
440
|
const context = createStreamContext(req, config, apiKey);
|
|
542
441
|
try {
|
|
543
442
|
yield* yieldAiSdkStream(req, acc, context);
|
|
544
|
-
yield* yieldCapturedRawEvents(context, acc);
|
|
545
443
|
yield* finalEvents(req, acc);
|
|
546
444
|
}
|
|
547
445
|
catch (err) {
|
|
548
446
|
if (isAbortError(err)) {
|
|
549
447
|
throw err;
|
|
550
448
|
}
|
|
551
|
-
yield* yieldCapturedRawEventsUnchecked(context);
|
|
552
449
|
yield toErrorEvent(err);
|
|
553
450
|
}
|
|
554
451
|
}
|
|
@@ -564,16 +461,34 @@ function* finalEvents(req, acc) {
|
|
|
564
461
|
}
|
|
565
462
|
yield { type: 'done' };
|
|
566
463
|
}
|
|
567
|
-
function
|
|
568
|
-
if (req.
|
|
464
|
+
function responseFormatFor(req) {
|
|
465
|
+
if (!req.structured)
|
|
466
|
+
return undefined;
|
|
467
|
+
const spec = getStructured(req.structured);
|
|
468
|
+
if (!spec.jsonSchema)
|
|
569
469
|
return undefined;
|
|
570
|
-
}
|
|
571
470
|
return {
|
|
572
|
-
|
|
573
|
-
|
|
471
|
+
type: 'json_schema',
|
|
472
|
+
json_schema: {
|
|
473
|
+
name: String(req.structured),
|
|
474
|
+
strict: true,
|
|
475
|
+
schema: spec.jsonSchema,
|
|
574
476
|
},
|
|
575
477
|
};
|
|
576
478
|
}
|
|
479
|
+
function providerOptionsFor(req) {
|
|
480
|
+
const openrouter = {};
|
|
481
|
+
if (req.thinking !== 'none') {
|
|
482
|
+
openrouter.reasoning = { effort: req.thinking };
|
|
483
|
+
}
|
|
484
|
+
const responseFormat = responseFormatFor(req);
|
|
485
|
+
if (responseFormat) {
|
|
486
|
+
openrouter.response_format = responseFormat;
|
|
487
|
+
}
|
|
488
|
+
if (Object.keys(openrouter).length === 0)
|
|
489
|
+
return undefined;
|
|
490
|
+
return { openrouter };
|
|
491
|
+
}
|
|
577
492
|
function openRouterHeaders(config) {
|
|
578
493
|
const headers = {};
|
|
579
494
|
if (config.siteUrl) {
|
|
@@ -591,3 +506,49 @@ function createOpenRouterProvider(config = {}) {
|
|
|
591
506
|
};
|
|
592
507
|
}
|
|
593
508
|
export { createOpenRouterProvider, resolveOpenRouterModel, toOpenRouterPayload };
|
|
509
|
+
/** @internal Exported for direct unit testing only. */
|
|
510
|
+
export const _internals = {
|
|
511
|
+
trimApiKey,
|
|
512
|
+
createAccumulator,
|
|
513
|
+
mediaPart,
|
|
514
|
+
contentFromParts,
|
|
515
|
+
parseToolInput,
|
|
516
|
+
stringDefault,
|
|
517
|
+
fallbackToolCallId,
|
|
518
|
+
toolResultContent,
|
|
519
|
+
assistantToolCallContent,
|
|
520
|
+
historyMessage,
|
|
521
|
+
contentFromOptionalParts,
|
|
522
|
+
contentHistoryMessage,
|
|
523
|
+
buildMessages,
|
|
524
|
+
schemaForTool,
|
|
525
|
+
buildTools,
|
|
526
|
+
openRouterSettings,
|
|
527
|
+
tokensFromUsage,
|
|
528
|
+
rawRecord,
|
|
529
|
+
stringArray,
|
|
530
|
+
metadataRecord,
|
|
531
|
+
citationCandidates,
|
|
532
|
+
nestedCitations,
|
|
533
|
+
metadataAnnotations,
|
|
534
|
+
evidenceFromMetadata,
|
|
535
|
+
toolArguments,
|
|
536
|
+
toolResultData,
|
|
537
|
+
rawThoughtEvent,
|
|
538
|
+
rawChoiceMessageEvidence,
|
|
539
|
+
rawEvents,
|
|
540
|
+
toolCallEvent,
|
|
541
|
+
toolResultEvent,
|
|
542
|
+
tokenEvent,
|
|
543
|
+
providerMetadataEvent,
|
|
544
|
+
eventFromPart,
|
|
545
|
+
primaryEventFromPart,
|
|
546
|
+
finishEvent,
|
|
547
|
+
sourceEvent,
|
|
548
|
+
finalEvents,
|
|
549
|
+
responseFormatFor,
|
|
550
|
+
providerOptionsFor,
|
|
551
|
+
openRouterHeaders,
|
|
552
|
+
missingOpenRouterKey,
|
|
553
|
+
streamTextOptions,
|
|
554
|
+
};
|
|
@@ -3,5 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
+
declare function writeAscii(view: DataView, offset: number, str: string): void;
|
|
6
7
|
/** Wrap raw PCM bytes in a RIFF/WAVE container. */
|
|
7
8
|
export declare function wrapPcmAsWav(pcm: Uint8Array, sampleRate?: number): Uint8Array;
|
|
9
|
+
/** @internal Exported for direct unit testing only. */
|
|
10
|
+
export declare const _internals: {
|
|
11
|
+
writeAscii: typeof writeAscii;
|
|
12
|
+
wrapPcmAsWav: typeof wrapPcmAsWav;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
package/esm/src/providers/pcm.js
CHANGED
|
@@ -8,8 +8,36 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import type { ModelProvider } from '../kernel/types.js';
|
|
11
|
+
import type { ModelProvider, ProviderCompleteRequest, TurnEvent } from '../kernel/types.js';
|
|
12
12
|
import { type GeminiTransport } from './keys.js';
|
|
13
|
+
declare function base64ToBytes(data: string): Uint8Array;
|
|
14
|
+
declare function bytesToBase64(bytes: Uint8Array): string;
|
|
15
|
+
declare function isRawPcmMime(mime: string): boolean;
|
|
16
|
+
/** Google TTS returns raw PCM; wrap as WAV for hosts (matches OpenRouter pcm path). */
|
|
17
|
+
declare function normalizeSpeechMedia(event: TurnEvent, speech: boolean): TurnEvent;
|
|
18
|
+
declare function eventType(event: Record<string, unknown>): string;
|
|
19
|
+
declare function isDeltaEvent(kind: string): boolean;
|
|
20
|
+
declare function isCompleteEvent(kind: string): boolean;
|
|
21
|
+
declare function foldDeltaPayload(event: Record<string, unknown>, acc: {
|
|
22
|
+
text: string;
|
|
23
|
+
}): TurnEvent[];
|
|
24
|
+
declare function foldPayload(event: Record<string, unknown>, acc: {
|
|
25
|
+
text: string;
|
|
26
|
+
}): TurnEvent[];
|
|
27
|
+
declare function withTap(req: ProviderCompleteRequest, transport: GeminiTransport): GeminiTransport;
|
|
13
28
|
/** Create a `ModelProvider` backed by Google Interactions streaming. */
|
|
14
29
|
declare function createInteractionsProvider(transport: GeminiTransport): ModelProvider;
|
|
15
30
|
export { createInteractionsProvider };
|
|
31
|
+
/** @internal Exported for direct unit testing only. */
|
|
32
|
+
export declare const _internals: {
|
|
33
|
+
base64ToBytes: typeof base64ToBytes;
|
|
34
|
+
bytesToBase64: typeof bytesToBase64;
|
|
35
|
+
isRawPcmMime: typeof isRawPcmMime;
|
|
36
|
+
normalizeSpeechMedia: typeof normalizeSpeechMedia;
|
|
37
|
+
eventType: typeof eventType;
|
|
38
|
+
isDeltaEvent: typeof isDeltaEvent;
|
|
39
|
+
isCompleteEvent: typeof isCompleteEvent;
|
|
40
|
+
foldDeltaPayload: typeof foldDeltaPayload;
|
|
41
|
+
foldPayload: typeof foldPayload;
|
|
42
|
+
withTap: typeof withTap;
|
|
43
|
+
};
|
|
@@ -161,3 +161,16 @@ function createInteractionsProvider(transport) {
|
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
export { createInteractionsProvider };
|
|
164
|
+
/** @internal Exported for direct unit testing only. */
|
|
165
|
+
export const _internals = {
|
|
166
|
+
base64ToBytes,
|
|
167
|
+
bytesToBase64,
|
|
168
|
+
isRawPcmMime,
|
|
169
|
+
normalizeSpeechMedia,
|
|
170
|
+
eventType,
|
|
171
|
+
isDeltaEvent,
|
|
172
|
+
isCompleteEvent,
|
|
173
|
+
foldDeltaPayload,
|
|
174
|
+
foldPayload,
|
|
175
|
+
withTap,
|
|
176
|
+
};
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
-
import type { ModelProvider, ProviderCompleteRequest, TurnEvent } from '../kernel/types.js';
|
|
9
|
+
import type { InteractionPart, ModelProvider, ProfileSpeechSpec, ProviderCompleteRequest, SpeechAudioFormat, TurnEvent } from '../kernel/types.js';
|
|
10
|
+
declare function bytesToBase64(bytes: Uint8Array): string;
|
|
10
11
|
/** Credentials for the openAi speech path (same shape as OpenRouter chat config + voice). */
|
|
11
12
|
export interface SpeechProviderConfig {
|
|
12
13
|
apiKey?: string;
|
|
@@ -17,7 +18,23 @@ export interface SpeechProviderConfig {
|
|
|
17
18
|
siteName?: string;
|
|
18
19
|
fetch?: typeof fetch;
|
|
19
20
|
}
|
|
21
|
+
declare function extractInputText(input: InteractionPart[]): string;
|
|
22
|
+
declare function resolveSpeechWireModel(req: ProviderCompleteRequest): string;
|
|
23
|
+
declare function buildHeaders(apiKey: string, config: SpeechProviderConfig): Record<string, string>;
|
|
24
|
+
declare function buildPayload(req: ProviderCompleteRequest, text: string, speech: ProfileSpeechSpec | undefined, configVoice?: string): Record<string, unknown>;
|
|
25
|
+
declare function requestSpeech(apiKey: string, text: string, req: ProviderCompleteRequest, config: SpeechProviderConfig): Promise<Response>;
|
|
26
|
+
declare function yieldSpeechSuccess(rawBytes: Uint8Array, text: string, format: SpeechAudioFormat): Generator<TurnEvent>;
|
|
20
27
|
declare function streamSpeech(req: ProviderCompleteRequest, config?: SpeechProviderConfig): AsyncGenerator<TurnEvent>;
|
|
21
28
|
/** Internal ModelProvider for openAi speech roles. */
|
|
22
29
|
declare function createSpeechProvider(config?: SpeechProviderConfig): ModelProvider;
|
|
23
30
|
export { createSpeechProvider, streamSpeech };
|
|
31
|
+
/** @internal Exported for direct unit testing only. */
|
|
32
|
+
export declare const _internals: {
|
|
33
|
+
bytesToBase64: typeof bytesToBase64;
|
|
34
|
+
extractInputText: typeof extractInputText;
|
|
35
|
+
resolveSpeechWireModel: typeof resolveSpeechWireModel;
|
|
36
|
+
buildHeaders: typeof buildHeaders;
|
|
37
|
+
buildPayload: typeof buildPayload;
|
|
38
|
+
requestSpeech: typeof requestSpeech;
|
|
39
|
+
yieldSpeechSuccess: typeof yieldSpeechSuccess;
|
|
40
|
+
};
|
|
@@ -124,3 +124,13 @@ function createSpeechProvider(config = {}) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
export { createSpeechProvider, streamSpeech };
|
|
127
|
+
/** @internal Exported for direct unit testing only. */
|
|
128
|
+
export const _internals = {
|
|
129
|
+
bytesToBase64,
|
|
130
|
+
extractInputText,
|
|
131
|
+
resolveSpeechWireModel,
|
|
132
|
+
buildHeaders,
|
|
133
|
+
buildPayload,
|
|
134
|
+
requestSpeech,
|
|
135
|
+
yieldSpeechSuccess,
|
|
136
|
+
};
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
declare const INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions?alt=sse";
|
|
2
|
+
declare function asObject(parsed: unknown): Record<string, unknown> | undefined;
|
|
3
|
+
declare function dataRecord(raw: string, sseEvent: string): Record<string, unknown>;
|
|
2
4
|
declare function takeSsePayloads(buffer: string, pendingEvent?: string): {
|
|
3
5
|
rest: string;
|
|
4
6
|
payloads: Record<string, unknown>[];
|
|
5
7
|
pendingEvent: string;
|
|
6
8
|
};
|
|
7
9
|
export { INTERACTIONS_URL, takeSsePayloads };
|
|
10
|
+
/** @internal Exported for direct unit testing only. */
|
|
11
|
+
export declare const _internals: {
|
|
12
|
+
asObject: typeof asObject;
|
|
13
|
+
dataRecord: typeof dataRecord;
|
|
14
|
+
takeSsePayloads: typeof takeSsePayloads;
|
|
15
|
+
};
|
package/esm/src/providers/sse.js
CHANGED
|
@@ -51,3 +51,5 @@ function takeSsePayloads(buffer, pendingEvent = '') {
|
|
|
51
51
|
return { rest, payloads, pendingEvent: sseEvent };
|
|
52
52
|
}
|
|
53
53
|
export { INTERACTIONS_URL, takeSsePayloads };
|
|
54
|
+
/** @internal Exported for direct unit testing only. */
|
|
55
|
+
export const _internals = { asObject, dataRecord, takeSsePayloads };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read one string field from incomplete JSON while structured output streams as text deltas.
|
|
3
|
+
*
|
|
4
|
+
* The buffer may lack a closing quote; any decoded prefix is returned for live preview.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readStreamingJsonStringField(jsonText: string, key: string): string | null;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const JSON_ESCAPES = {
|
|
2
|
+
n: '\n',
|
|
3
|
+
t: '\t',
|
|
4
|
+
r: '\r',
|
|
5
|
+
'"': '"',
|
|
6
|
+
'\\': '\\',
|
|
7
|
+
'/': '/',
|
|
8
|
+
};
|
|
9
|
+
function decodeEscapedChar(ch, jsonText, index) {
|
|
10
|
+
if (ch === 'u' && index + 4 < jsonText.length) {
|
|
11
|
+
const hex = jsonText.slice(index + 1, index + 5);
|
|
12
|
+
if (/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
13
|
+
return { text: String.fromCharCode(Number.parseInt(hex, 16)), next: index + 4 };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const mapped = JSON_ESCAPES[ch];
|
|
17
|
+
return { text: mapped ?? ch, next: index };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read one string field from incomplete JSON while structured output streams as text deltas.
|
|
21
|
+
*
|
|
22
|
+
* The buffer may lack a closing quote; any decoded prefix is returned for live preview.
|
|
23
|
+
*/
|
|
24
|
+
export function readStreamingJsonStringField(jsonText, key) {
|
|
25
|
+
const keyPattern = new RegExp(`"${key}"\\s*:\\s*"`);
|
|
26
|
+
const match = keyPattern.exec(jsonText);
|
|
27
|
+
if (!match || match.index === undefined) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
let i = match.index + match[0].length;
|
|
31
|
+
let result = '';
|
|
32
|
+
let escaped = false;
|
|
33
|
+
while (i < jsonText.length) {
|
|
34
|
+
const ch = jsonText[i];
|
|
35
|
+
if (escaped) {
|
|
36
|
+
const decoded = decodeEscapedChar(ch, jsonText, i);
|
|
37
|
+
result += decoded.text;
|
|
38
|
+
i = decoded.next;
|
|
39
|
+
escaped = false;
|
|
40
|
+
i += 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (ch === '\\') {
|
|
44
|
+
escaped = true;
|
|
45
|
+
}
|
|
46
|
+
else if (ch === '"') {
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
result += ch;
|
|
51
|
+
}
|
|
52
|
+
i += 1;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theorum",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "A flat TypeScript agent kernel for typed profiles, deterministic turn execution, dynamic tools, provider adapters, guardrails, and host-injected traces.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"./presets/google": {
|
|
47
47
|
"import": "./esm/src/presets/google.js"
|
|
48
|
+
},
|
|
49
|
+
"./streaming": {
|
|
50
|
+
"import": "./esm/src/streaming/mod.js"
|
|
48
51
|
}
|
|
49
52
|
},
|
|
50
53
|
"scripts": {},
|
|
@@ -54,8 +57,8 @@
|
|
|
54
57
|
"type": "module",
|
|
55
58
|
"sideEffects": false,
|
|
56
59
|
"dependencies": {
|
|
57
|
-
"@openrouter/ai-sdk-provider": "
|
|
58
|
-
"ai": "
|
|
60
|
+
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
61
|
+
"ai": "^7.0.0",
|
|
59
62
|
"@deno/shim-deno": "~0.18.0"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|