theorum 0.1.2 → 0.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 +62 -27
- package/docs/AGENT_PROFILE_CONTRACT.md +45 -18
- package/docs/CLI_SPEC.md +2 -2
- package/docs/SECRETS.md +27 -22
- package/esm/_dnt.polyfills.d.ts +99 -0
- package/esm/_dnt.polyfills.js +130 -1
- package/esm/mod.d.ts +19 -3
- package/esm/mod.js +17 -3
- package/esm/src/cli/commands/profile.d.ts +2 -0
- package/esm/src/cli/commands/profile.js +53 -0
- package/esm/src/cli/commands/run.d.ts +10 -0
- package/esm/src/cli/commands/run.js +55 -0
- package/esm/src/cli/commands/test.d.ts +15 -0
- package/esm/src/cli/commands/test.js +140 -0
- package/esm/src/cli/index.d.ts +4 -0
- package/esm/src/cli/index.js +148 -0
- package/esm/src/cli/matrix/fixtures.d.ts +13 -0
- package/esm/src/cli/matrix/fixtures.js +77 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +29 -0
- package/esm/src/cli/matrix/synthesizer.js +187 -0
- package/esm/src/guardrails/error.d.ts +1 -3
- package/esm/src/guardrails/error.js +4 -6
- package/esm/src/guardrails/mod.d.ts +1 -1
- package/esm/src/guardrails/mod.js +1 -1
- package/esm/src/guardrails/sanitize.js +1 -1
- package/esm/src/host/mint-trace.d.ts +27 -0
- package/esm/src/host/mint-trace.js +33 -0
- package/esm/src/host/mod.d.ts +13 -0
- package/esm/src/host/mod.js +12 -0
- package/esm/src/host/reply.d.ts +14 -0
- package/esm/src/host/reply.js +27 -0
- package/esm/src/kernel/engine/delta.js +31 -4
- package/esm/src/kernel/engine/repair.d.ts +1 -3
- package/esm/src/kernel/engine/repair.js +1 -1
- package/esm/src/kernel/engine/runner/gates.d.ts +4 -0
- package/esm/src/kernel/engine/runner/gates.js +206 -0
- package/esm/src/kernel/engine/runner/mod.d.ts +14 -0
- package/esm/src/kernel/engine/runner/mod.js +103 -0
- package/esm/src/kernel/engine/runner/state.d.ts +16 -0
- package/esm/src/kernel/engine/runner/state.js +8 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +15 -0
- package/esm/src/kernel/engine/runner/steps.js +119 -0
- package/esm/src/kernel/engine/runner/stream.d.ts +11 -0
- package/esm/src/kernel/engine/runner/stream.js +65 -0
- package/esm/src/kernel/engine/runner/tokens.d.ts +3 -0
- package/esm/src/kernel/engine/runner/tokens.js +38 -0
- package/esm/src/kernel/engine/runner/tools.d.ts +13 -0
- package/esm/src/kernel/engine/runner/tools.js +197 -0
- package/esm/src/kernel/engine/runner.d.ts +1 -9
- package/esm/src/kernel/engine/runner.js +1 -726
- package/esm/src/kernel/mod.d.ts +3 -1
- package/esm/src/kernel/mod.js +2 -1
- package/esm/src/{providers → kernel/registry}/attachments.d.ts +2 -3
- package/esm/src/{providers → kernel/registry}/attachments.js +6 -6
- package/esm/src/kernel/registry/catalog.d.ts +28 -21
- package/esm/src/kernel/registry/catalog.js +55 -133
- package/esm/src/kernel/registry/ingress.d.ts +13 -0
- package/esm/src/kernel/registry/ingress.js +163 -0
- package/esm/src/kernel/registry/profiles.d.ts +2 -4
- package/esm/src/kernel/registry/profiles.js +14 -5
- package/esm/src/kernel/registry/provider-request.d.ts +4 -0
- package/esm/src/kernel/registry/provider-request.js +25 -0
- package/esm/src/kernel/registry/resolve.d.ts +0 -1
- package/esm/src/kernel/registry/resolve.js +35 -53
- package/esm/src/kernel/registry/tools.js +2 -2
- package/esm/src/kernel/registry/vault.d.ts +11 -0
- package/esm/src/kernel/registry/vault.js +18 -0
- package/esm/src/kernel/types.d.ts +102 -78
- package/esm/src/observability/trace-attach.js +6 -20
- package/esm/src/presets/google.d.ts +50 -0
- package/esm/src/presets/google.js +96 -0
- package/esm/src/presets/mod.d.ts +11 -0
- package/esm/src/presets/mod.js +10 -0
- package/esm/src/providers/create-provider.d.ts +29 -0
- package/esm/src/providers/create-provider.js +38 -0
- package/esm/src/providers/gemini-tape.d.ts +1 -2
- package/esm/src/providers/gemini-tape.js +1 -1
- package/esm/src/providers/interactions.js +41 -16
- package/esm/src/{guardrails → providers}/keys.d.ts +10 -3
- package/esm/src/{guardrails → providers}/keys.js +10 -13
- package/esm/src/providers/mod.d.ts +4 -7
- package/esm/src/providers/mod.js +2 -5
- package/esm/src/providers/openrouter-mod.d.ts +13 -0
- package/esm/src/providers/openrouter-mod.js +12 -0
- package/esm/src/providers/openrouter-payload.d.ts +11 -3
- package/esm/src/providers/openrouter-payload.js +28 -26
- package/esm/src/providers/openrouter.d.ts +5 -7
- package/esm/src/providers/openrouter.js +488 -231
- package/esm/src/providers/pcm.d.ts +7 -0
- package/esm/src/providers/pcm.js +35 -0
- package/esm/src/providers/provider.d.ts +3 -1
- package/esm/src/providers/provider.js +45 -2
- package/esm/src/providers/speech.d.ts +23 -0
- package/esm/src/providers/speech.js +125 -0
- package/package.json +20 -3
- package/esm/src/providers/media.d.ts +0 -5
- package/esm/src/providers/media.js +0 -125
- package/esm/src/providers/tts.d.ts +0 -24
- package/esm/src/providers/tts.js +0 -144
package/esm/src/kernel/mod.d.ts
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import "../../_dnt.polyfills.js";
|
|
11
11
|
export { runTurn } from './engine/runner.js';
|
|
12
|
-
export {
|
|
12
|
+
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, getTool, listBuiltinIds, modelEntryByApiId, registerTools, requireModelSpec, resetTools, } from './registry/catalog.js';
|
|
13
|
+
export type { ProfileDefinition } from './registry/profiles.js';
|
|
14
|
+
export { clearProfiles, defineProfile, getProfile, hasProfile, listProfiles, registerProfile, registerProfiles, } from './registry/profiles.js';
|
|
13
15
|
export { projectProfile, resolveTurn } from './registry/resolve.js';
|
|
14
16
|
export { getStructured, registerStructured } from './registry/schemas.js';
|
|
15
17
|
export { executeTool } from './registry/tools.js';
|
package/esm/src/kernel/mod.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import "../../_dnt.polyfills.js";
|
|
11
11
|
export { runTurn } from './engine/runner.js';
|
|
12
|
-
export {
|
|
12
|
+
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, getTool, listBuiltinIds, modelEntryByApiId, registerTools, requireModelSpec, resetTools, } from './registry/catalog.js';
|
|
13
|
+
export { clearProfiles, defineProfile, getProfile, hasProfile, listProfiles, registerProfile, registerProfiles, } from './registry/profiles.js';
|
|
13
14
|
export { projectProfile, resolveTurn } from './registry/resolve.js';
|
|
14
15
|
export { getStructured, registerStructured } from './registry/schemas.js';
|
|
15
16
|
export { executeTool } from './registry/tools.js';
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { MediaLimits,
|
|
1
|
+
import type { MediaLimits, Profile, TurnBlob } from '../types.js';
|
|
2
2
|
declare function tooManyFilesMessage(maxFiles: number): string;
|
|
3
3
|
declare function fileTooLargeMessage(maxBytes: number): string;
|
|
4
4
|
declare function turnTooLargeMessage(maxTurnBytes: number): string;
|
|
5
|
-
declare function mediaLimits(inputs: MimeInputs): MediaLimits | undefined;
|
|
6
5
|
declare function requireMediaLimits(profile: Profile): MediaLimits;
|
|
7
6
|
declare function sanitizeCsvText(text: string): string;
|
|
8
7
|
declare function assertAttachmentLimits(blobs: TurnBlob[], limits: MediaLimits): void;
|
|
@@ -14,4 +13,4 @@ declare function sanitizeTurnBlobsForProfile(profileId: string, attachments: Tur
|
|
|
14
13
|
attachments?: TurnBlob[];
|
|
15
14
|
voice?: TurnBlob[];
|
|
16
15
|
};
|
|
17
|
-
export { assertAttachmentLimits, fileTooLargeMessage,
|
|
16
|
+
export { assertAttachmentLimits, fileTooLargeMessage, requireMediaLimits, sanitizeCsvText, sanitizeTurnBlobs, sanitizeTurnBlobsForProfile, tooManyFilesMessage, turnTooLargeMessage, };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { TheorumError } from '
|
|
2
|
-
import { injectionSpans } from '
|
|
3
|
-
import { sensitiveSpans } from '
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { TheorumError } from '../../guardrails/error.js';
|
|
2
|
+
import { injectionSpans } from '../../guardrails/injection.js';
|
|
3
|
+
import { sensitiveSpans } from '../../guardrails/sensitive.js';
|
|
4
|
+
import { applySpans } from '../../observability/spans.js';
|
|
5
|
+
import { getProfile } from './profiles.js';
|
|
6
6
|
const B64_PAD = 2;
|
|
7
7
|
const B64_WORD = 4;
|
|
8
8
|
const B64_TRIPLET = 3;
|
|
@@ -153,4 +153,4 @@ function sanitizeTurnBlobsForProfile(profileId, attachments, voice) {
|
|
|
153
153
|
const limits = requireMediaLimits(getProfile(profileId));
|
|
154
154
|
return sanitizeTurnBlobs(attachments, voice, limits);
|
|
155
155
|
}
|
|
156
|
-
export { assertAttachmentLimits, fileTooLargeMessage,
|
|
156
|
+
export { assertAttachmentLimits, fileTooLargeMessage, requireMediaLimits, sanitizeCsvText, sanitizeTurnBlobs, sanitizeTurnBlobsForProfile, tooManyFilesMessage, turnTooLargeMessage, };
|
|
@@ -1,24 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
declare const
|
|
1
|
+
/**
|
|
2
|
+
* Tool catalog and MIME helpers.
|
|
3
|
+
*
|
|
4
|
+
* Model wire metadata is host-owned on `profile.model.config`. This module only
|
|
5
|
+
* keeps builtin/custom tool descriptors and shared MIME utilities.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { BuiltinToolId, Catalog, GeminiInputKind, ModelId, ModelSpec, Profile, ThinkingLevel, ToolCatalogEntry, ToolId } from '../types.js';
|
|
10
|
+
/** Live tool catalog. Starts with harness tools; presets/hosts register more. */
|
|
11
|
+
declare const CATALOG: Catalog;
|
|
12
|
+
/** Register or replace tool descriptors (idempotent per id). */
|
|
13
|
+
declare function registerTools(entries: Record<string, ToolCatalogEntry>): void;
|
|
14
|
+
/** Look up one registered tool descriptor. */
|
|
15
|
+
declare function getTool(id: ToolId): ToolCatalogEntry | undefined;
|
|
16
|
+
/** Ids of all registered provider builtins. */
|
|
17
|
+
declare function listBuiltinIds(): BuiltinToolId[];
|
|
18
|
+
/** Restore harness-only tools (tests / host reloads). */
|
|
19
|
+
declare function resetTools(): void;
|
|
12
20
|
declare function mimeEssence(mime: string): string;
|
|
13
21
|
declare function mimeAllowed(accept: string[], mime: string): boolean;
|
|
14
22
|
declare function geminiKindForMime(mime: string): GeminiInputKind | undefined;
|
|
15
|
-
|
|
16
|
-
declare
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/** Clamp a
|
|
22
|
-
|
|
23
|
-
export
|
|
24
|
-
export declare function clampThinkingLevelForApiId(apiId: string, level: ThinkingLevel): ThinkingLevel;
|
|
23
|
+
/** Require a host-declared model spec for an allowed profile model id. */
|
|
24
|
+
declare function requireModelSpec(profile: Profile, modelId: ModelId): ModelSpec;
|
|
25
|
+
/** Clamp a requested thinking level to what the model spec accepts. */
|
|
26
|
+
declare function clampThinkingLevel(spec: ModelSpec, level: ThinkingLevel): ThinkingLevel;
|
|
27
|
+
/** Look up a model spec by provider-native API id within a host specs map. */
|
|
28
|
+
declare function modelEntryByApiId(specs: Record<string, ModelSpec>, apiId: string): ModelSpec | undefined;
|
|
29
|
+
/** Clamp thinking level using a provider-native API id within a host specs map. */
|
|
30
|
+
declare function clampThinkingLevelForApiId(specs: Record<string, ModelSpec>, apiId: string, level: ThinkingLevel): ThinkingLevel;
|
|
31
|
+
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, geminiKindForMime, getTool, listBuiltinIds, mimeAllowed, mimeEssence, modelEntryByApiId, registerTools, requireModelSpec, resetTools, };
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool catalog and MIME helpers.
|
|
3
|
+
*
|
|
4
|
+
* Model wire metadata is host-owned on `profile.model.config`. This module only
|
|
5
|
+
* keeps builtin/custom tool descriptors and shared MIME utilities.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { TheorumError } from '../../guardrails/error.js';
|
|
1
10
|
const ASK_USER_SCHEMA = {
|
|
2
11
|
type: 'object',
|
|
3
12
|
properties: {
|
|
@@ -7,23 +16,36 @@ const ASK_USER_SCHEMA = {
|
|
|
7
16
|
},
|
|
8
17
|
required: ['kind', 'prompt'],
|
|
9
18
|
};
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const IMAGE_FLASH_LITE_MAX_OUTPUT_TOKENS = 4096;
|
|
14
|
-
const TTS_FLASH_MAX_OUTPUT_TOKENS = 2048;
|
|
15
|
-
const SONAR_MAX_OUTPUT_TOKENS = 8192;
|
|
16
|
-
const IMAGE_INPUT_MIMES = ['image/png', 'image/jpeg', 'image/webp', 'image/heic', 'image/heif'];
|
|
17
|
-
const VOICE_INPUT_MIMES = ['audio/webm', 'audio/wav', 'audio/mpeg', 'audio/mp4'];
|
|
18
|
-
const KIB = 1024;
|
|
19
|
-
const MIB = KIB * KIB;
|
|
20
|
-
/** Default chat media caps. Host profiles may copy or override these limits. */
|
|
21
|
-
const CHAT_MEDIA_LIMITS = {
|
|
22
|
-
maxFiles: 10,
|
|
23
|
-
maxBytes: 8 * MIB,
|
|
24
|
-
maxTurnBytes: 32 * MIB,
|
|
19
|
+
/** Harness tools that always ship with THEORUM. */
|
|
20
|
+
const HARNESS_TOOLS = {
|
|
21
|
+
askUser: { kind: 'custom', ui: true, schema: ASK_USER_SCHEMA },
|
|
25
22
|
};
|
|
26
|
-
/**
|
|
23
|
+
/** Live tool catalog. Starts with harness tools; presets/hosts register more. */
|
|
24
|
+
const CATALOG = {
|
|
25
|
+
tools: { ...HARNESS_TOOLS },
|
|
26
|
+
};
|
|
27
|
+
/** Register or replace tool descriptors (idempotent per id). */
|
|
28
|
+
function registerTools(entries) {
|
|
29
|
+
Object.assign(CATALOG.tools, entries);
|
|
30
|
+
}
|
|
31
|
+
/** Look up one registered tool descriptor. */
|
|
32
|
+
function getTool(id) {
|
|
33
|
+
return CATALOG.tools[id];
|
|
34
|
+
}
|
|
35
|
+
/** Ids of all registered provider builtins. */
|
|
36
|
+
function listBuiltinIds() {
|
|
37
|
+
return Object.entries(CATALOG.tools)
|
|
38
|
+
.filter(([, entry]) => entry.kind === 'builtin')
|
|
39
|
+
.map(([id]) => id);
|
|
40
|
+
}
|
|
41
|
+
/** Restore harness-only tools (tests / host reloads). */
|
|
42
|
+
function resetTools() {
|
|
43
|
+
for (const id of Object.keys(CATALOG.tools)) {
|
|
44
|
+
delete CATALOG.tools[id];
|
|
45
|
+
}
|
|
46
|
+
Object.assign(CATALOG.tools, HARNESS_TOOLS);
|
|
47
|
+
}
|
|
48
|
+
/** Gemini Interactions inline MIME → part type (adapter wire map). */
|
|
27
49
|
const GEMINI_INPUT_KINDS = {
|
|
28
50
|
'image/png': 'image',
|
|
29
51
|
'image/jpeg': 'image',
|
|
@@ -76,115 +98,13 @@ function mimeAllowed(accept, mime) {
|
|
|
76
98
|
function geminiKindForMime(mime) {
|
|
77
99
|
return GEMINI_INPUT_KINDS[mimeEssence(mime)];
|
|
78
100
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'5:4',
|
|
87
|
-
'9:16',
|
|
88
|
-
'16:9',
|
|
89
|
-
'21:9',
|
|
90
|
-
];
|
|
91
|
-
const IMAGE_FLASH_LITE_SIZES = ['1K'];
|
|
92
|
-
const FLASH_LITE_THINKING_LEVELS = ['minimal', 'low', 'medium', 'high'];
|
|
93
|
-
const PRO_PREVIEW_THINKING_LEVELS = ['low', 'medium', 'high'];
|
|
94
|
-
const IMAGE_FLASH_LITE_THINKING_LEVELS = ['minimal', 'high'];
|
|
95
|
-
const TTS_THINKING_LEVELS = ['minimal'];
|
|
96
|
-
const OPENROUTER_SEARCH_THINKING_LEVELS = ['low', 'medium', 'high'];
|
|
97
|
-
const GENERIC_MODEL_ENTRY = {
|
|
98
|
-
apiId: '',
|
|
99
|
-
thinking: { on: 'high', off: 'low' },
|
|
100
|
-
thinkingLevels: ['low', 'medium', 'high'],
|
|
101
|
-
summaries: { on: 'auto', off: 'none' },
|
|
102
|
-
maxOutputTokens: FLASH_LITE_MAX_OUTPUT_TOKENS,
|
|
103
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
104
|
-
freeBuiltins: [],
|
|
105
|
-
};
|
|
106
|
-
const CATALOG = {
|
|
107
|
-
models: {
|
|
108
|
-
gemini31FlashLite: {
|
|
109
|
-
apiId: 'gemini-3.1-flash-lite',
|
|
110
|
-
thinking: { on: 'high', off: 'minimal' },
|
|
111
|
-
thinkingLevels: FLASH_LITE_THINKING_LEVELS,
|
|
112
|
-
summaries: { on: 'auto', off: 'none' },
|
|
113
|
-
maxOutputTokens: FLASH_LITE_MAX_OUTPUT_TOKENS,
|
|
114
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
115
|
-
freeBuiltins: ['googleMaps', 'urlContext'],
|
|
116
|
-
},
|
|
117
|
-
gemini31ProPreview: {
|
|
118
|
-
apiId: 'gemini-3.1-pro-preview',
|
|
119
|
-
thinking: { on: 'high', off: 'low' },
|
|
120
|
-
thinkingLevels: PRO_PREVIEW_THINKING_LEVELS,
|
|
121
|
-
summaries: { on: 'auto', off: 'none' },
|
|
122
|
-
maxOutputTokens: PRO_PREVIEW_MAX_OUTPUT_TOKENS,
|
|
123
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
124
|
-
freeBuiltins: [],
|
|
125
|
-
},
|
|
126
|
-
gemini35FlashLite: {
|
|
127
|
-
apiId: 'gemini-3.5-flash-lite',
|
|
128
|
-
thinking: { on: 'high', off: 'minimal' },
|
|
129
|
-
thinkingLevels: FLASH_LITE_THINKING_LEVELS,
|
|
130
|
-
summaries: { on: 'auto', off: 'none' },
|
|
131
|
-
maxOutputTokens: FLASH_LITE_MAX_OUTPUT_TOKENS,
|
|
132
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
133
|
-
freeBuiltins: ['googleMaps', 'urlContext'],
|
|
134
|
-
},
|
|
135
|
-
gemini31FlashLiteImage: {
|
|
136
|
-
apiId: 'gemini-3.1-flash-lite-image',
|
|
137
|
-
thinking: { on: 'high', off: 'minimal' },
|
|
138
|
-
thinkingLevels: IMAGE_FLASH_LITE_THINKING_LEVELS,
|
|
139
|
-
summaries: { on: 'none', off: 'none' },
|
|
140
|
-
maxOutputTokens: IMAGE_FLASH_LITE_MAX_OUTPUT_TOKENS,
|
|
141
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
142
|
-
freeBuiltins: [],
|
|
143
|
-
image: {
|
|
144
|
-
maxInputImages: 14,
|
|
145
|
-
inputMimes: IMAGE_INPUT_MIMES,
|
|
146
|
-
sizes: IMAGE_FLASH_LITE_SIZES,
|
|
147
|
-
aspectRatios: IMAGE_FLASH_LITE_ASPECT_RATIOS,
|
|
148
|
-
outputMime: 'image/jpeg',
|
|
149
|
-
allowsGrounding: false,
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
gemini31FlashTts: {
|
|
153
|
-
apiId: 'gemini-3.1-flash-tts-preview',
|
|
154
|
-
thinking: { on: 'minimal', off: 'minimal' },
|
|
155
|
-
thinkingLevels: TTS_THINKING_LEVELS,
|
|
156
|
-
summaries: { on: 'none', off: 'none' },
|
|
157
|
-
maxOutputTokens: TTS_FLASH_MAX_OUTPUT_TOKENS,
|
|
158
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
159
|
-
freeBuiltins: [],
|
|
160
|
-
},
|
|
161
|
-
sonar: {
|
|
162
|
-
apiId: 'sonar',
|
|
163
|
-
openRouterId: 'perplexity/sonar',
|
|
164
|
-
thinking: { on: 'high', off: 'low' },
|
|
165
|
-
thinkingLevels: OPENROUTER_SEARCH_THINKING_LEVELS,
|
|
166
|
-
summaries: { on: 'none', off: 'none' },
|
|
167
|
-
maxOutputTokens: SONAR_MAX_OUTPUT_TOKENS,
|
|
168
|
-
temperature: DEFAULT_TEMPERATURE,
|
|
169
|
-
freeBuiltins: [],
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
// Off until the turn sets tools[id]. Profile allow is only the ceiling.
|
|
173
|
-
tools: {
|
|
174
|
-
googleSearch: { kind: 'builtin', ui: true },
|
|
175
|
-
googleMaps: { kind: 'builtin', ui: true },
|
|
176
|
-
urlContext: { kind: 'builtin', ui: true },
|
|
177
|
-
askUser: { kind: 'custom', ui: true, schema: ASK_USER_SCHEMA },
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
export { CATALOG, CHAT_MEDIA_LIMITS, GEMINI_INPUT_KINDS, geminiKindForMime, IMAGE_FLASH_LITE_ASPECT_RATIOS, IMAGE_FLASH_LITE_SIZES, IMAGE_INPUT_MIMES, mimeAllowed, mimeEssence, VOICE_INPUT_MIMES, };
|
|
181
|
-
export const MODEL_CATALOG = CATALOG.models;
|
|
182
|
-
export function modelEntry(modelId) {
|
|
183
|
-
return (MODEL_CATALOG[modelId] ?? {
|
|
184
|
-
...GENERIC_MODEL_ENTRY,
|
|
185
|
-
apiId: modelId,
|
|
186
|
-
openRouterId: modelId,
|
|
187
|
-
});
|
|
101
|
+
/** Require a host-declared model spec for an allowed profile model id. */
|
|
102
|
+
function requireModelSpec(profile, modelId) {
|
|
103
|
+
const spec = profile.model.config[modelId];
|
|
104
|
+
if (!spec) {
|
|
105
|
+
throw new TheorumError(`Profile ${profile.id} has no model spec for '${modelId}'`);
|
|
106
|
+
}
|
|
107
|
+
return spec;
|
|
188
108
|
}
|
|
189
109
|
function clampLevels(entry, level) {
|
|
190
110
|
if (!entry?.thinkingLevels || entry.thinkingLevels.length === 0) {
|
|
@@ -200,14 +120,16 @@ function clampLevels(entry, level) {
|
|
|
200
120
|
const first = entry.thinkingLevels[0];
|
|
201
121
|
return first ?? level;
|
|
202
122
|
}
|
|
203
|
-
/** Clamp a requested thinking level to what the
|
|
204
|
-
|
|
205
|
-
return clampLevels(
|
|
123
|
+
/** Clamp a requested thinking level to what the model spec accepts. */
|
|
124
|
+
function clampThinkingLevel(spec, level) {
|
|
125
|
+
return clampLevels(spec, level);
|
|
206
126
|
}
|
|
207
|
-
|
|
208
|
-
|
|
127
|
+
/** Look up a model spec by provider-native API id within a host specs map. */
|
|
128
|
+
function modelEntryByApiId(specs, apiId) {
|
|
129
|
+
return Object.values(specs).find((m) => m.apiId === apiId);
|
|
209
130
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return clampLevels(
|
|
131
|
+
/** Clamp thinking level using a provider-native API id within a host specs map. */
|
|
132
|
+
function clampThinkingLevelForApiId(specs, apiId, level) {
|
|
133
|
+
return clampLevels(modelEntryByApiId(specs, apiId), level);
|
|
213
134
|
}
|
|
135
|
+
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, geminiKindForMime, getTool, listBuiltinIds, mimeAllowed, mimeEssence, modelEntryByApiId, registerTools, requireModelSpec, resetTools, };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel ingress: input parts, image pins, and speech-role checks.
|
|
3
|
+
*
|
|
4
|
+
* Owned by the kernel so `resolveTurn` does not import provider adapters.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import type { BuiltinToolId, ImageResponseFormat, InteractionPart, ModelId, Profile, TurnRequest } from '../types.js';
|
|
9
|
+
declare function assertSpeechRole(profile: Profile): void;
|
|
10
|
+
declare function resolveImageFormat(profile: Profile, _model: ModelId, slots?: Record<string, string>): ImageResponseFormat | null;
|
|
11
|
+
declare function resolveInputParts(profile: Profile, model: ModelId, req: TurnRequest): InteractionPart[];
|
|
12
|
+
declare function assertImageGrounding(profile: Profile, model: ModelId, builtins: BuiltinToolId[]): void;
|
|
13
|
+
export { assertImageGrounding, assertSpeechRole, resolveImageFormat, resolveInputParts };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel ingress: input parts, image pins, and speech-role checks.
|
|
3
|
+
*
|
|
4
|
+
* Owned by the kernel so `resolveTurn` does not import provider adapters.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { TheorumError } from '../../guardrails/error.js';
|
|
9
|
+
import { wrapUserData } from '../engine/boundary.js';
|
|
10
|
+
import { synthesizeRepairPrompt } from '../engine/repair.js';
|
|
11
|
+
import { assertAttachmentLimits, requireMediaLimits } from './attachments.js';
|
|
12
|
+
import { geminiKindForMime, mimeAllowed, mimeEssence } from './catalog.js';
|
|
13
|
+
function listedValue(allowed, value) {
|
|
14
|
+
if (!value) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (!allowed || allowed.length === 0) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (allowed.includes(value)) {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
function resolveSlotOrPin(profileId, label, slotValue, pin, allow) {
|
|
26
|
+
if (slotValue !== undefined) {
|
|
27
|
+
const fromSlot = listedValue(allow, slotValue);
|
|
28
|
+
if (!fromSlot) {
|
|
29
|
+
throw new TheorumError(`Unknown image ${label} for ${profileId}`);
|
|
30
|
+
}
|
|
31
|
+
return fromSlot;
|
|
32
|
+
}
|
|
33
|
+
if (pin === undefined) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const fromPin = listedValue(allow, pin);
|
|
37
|
+
if (!fromPin) {
|
|
38
|
+
throw new TheorumError(`Unknown image ${label} for ${profileId}`);
|
|
39
|
+
}
|
|
40
|
+
return fromPin;
|
|
41
|
+
}
|
|
42
|
+
function assertImageRole(profile) {
|
|
43
|
+
const pins = profile.outputs.image;
|
|
44
|
+
if (!pins) {
|
|
45
|
+
throw new TheorumError(`Profile ${profile.id} requests image output but does not set outputs.image`);
|
|
46
|
+
}
|
|
47
|
+
assertExclusiveNativeOutput(profile, 'image');
|
|
48
|
+
return pins;
|
|
49
|
+
}
|
|
50
|
+
function assertSpeechRole(profile) {
|
|
51
|
+
if (!profile.outputs.speech) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
assertExclusiveNativeOutput(profile, 'speech');
|
|
55
|
+
if (profile.outputs.speech.format === 'mp3' && profile.model.protocol === 'geminiInteractions') {
|
|
56
|
+
throw new TheorumError(`Profile ${profile.id}: outputs.speech.format 'mp3' requires protocol 'openAi' ` +
|
|
57
|
+
`(geminiInteractions speech returns PCM and emits WAV)`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function assertExclusiveNativeOutput(profile, kind) {
|
|
61
|
+
const other = kind === 'image' ? 'speech' : 'image';
|
|
62
|
+
if (profile.outputs[other]) {
|
|
63
|
+
throw new TheorumError(`Profile ${profile.id} cannot mix outputs.${kind} with outputs.${other}`);
|
|
64
|
+
}
|
|
65
|
+
if (profile.outputs.structured !== null && profile.outputs.structured !== undefined) {
|
|
66
|
+
throw new TheorumError(`Profile ${profile.id} cannot mix structured JSON with native ${kind} output`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function resolveImageFormat(profile, _model, slots) {
|
|
70
|
+
if (!profile.outputs.image) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const pins = assertImageRole(profile);
|
|
74
|
+
const aspectRatio = resolveSlotOrPin(profile.id, 'aspect', slots?.aspectRatio, pins.aspectRatio, profile.inputs.slots?.aspectRatio);
|
|
75
|
+
const size = resolveSlotOrPin(profile.id, 'size', slots?.size, pins.size, profile.inputs.slots?.size);
|
|
76
|
+
if (!(aspectRatio && size)) {
|
|
77
|
+
throw new TheorumError(`Unknown image aspect or size for ${profile.id}`);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
type: 'image',
|
|
81
|
+
mimeType: pins.mimeType ?? 'image/jpeg',
|
|
82
|
+
aspectRatio,
|
|
83
|
+
size,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function assertGeminiMime(mime) {
|
|
87
|
+
const kind = geminiKindForMime(mime);
|
|
88
|
+
if (!kind) {
|
|
89
|
+
throw new TheorumError(`MIME '${mime}' is not a Gemini input type`);
|
|
90
|
+
}
|
|
91
|
+
return kind;
|
|
92
|
+
}
|
|
93
|
+
function mediaParts(profile, model, blobs, channel) {
|
|
94
|
+
const accept = channel === 'voice' ? profile.inputs.voice?.accept : profile.inputs.attachments?.accept;
|
|
95
|
+
if (!accept) {
|
|
96
|
+
throw new TheorumError(`Profile ${profile.id} does not accept ${channel}`);
|
|
97
|
+
}
|
|
98
|
+
const maxInputImages = profile.outputs.image?.maxInputImages;
|
|
99
|
+
const imageCount = blobs.filter((blob) => geminiKindForMime(blob.mimeType) === 'image').length;
|
|
100
|
+
if (maxInputImages !== undefined && imageCount > maxInputImages) {
|
|
101
|
+
throw new TheorumError(`At most ${maxInputImages} reference images on ${model}`);
|
|
102
|
+
}
|
|
103
|
+
return blobs.map((blob) => {
|
|
104
|
+
const kind = assertGeminiMime(blob.mimeType);
|
|
105
|
+
if (!mimeAllowed(accept, blob.mimeType)) {
|
|
106
|
+
throw new TheorumError(`MIME '${blob.mimeType}' is not accepted on ${profile.id}`);
|
|
107
|
+
}
|
|
108
|
+
const essence = mimeEssence(blob.mimeType);
|
|
109
|
+
return {
|
|
110
|
+
type: kind,
|
|
111
|
+
mimeType: essence === 'image/jpg' ? 'image/jpeg' : essence,
|
|
112
|
+
data: blob.data,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function extractTextPart(profile, req) {
|
|
117
|
+
const { text, repair, history } = req.input ?? {};
|
|
118
|
+
if (profile.inputs.text === false) {
|
|
119
|
+
if (text) {
|
|
120
|
+
throw new TheorumError(`Profile ${profile.id} does not accept text input`);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
let promptText = text;
|
|
125
|
+
if (repair) {
|
|
126
|
+
promptText = synthesizeRepairPrompt({ profile, repair, history });
|
|
127
|
+
}
|
|
128
|
+
if (!promptText) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return { type: 'text', text: wrapUserData(promptText) };
|
|
132
|
+
}
|
|
133
|
+
function extractMediaParts(profile, model, req) {
|
|
134
|
+
const { attachments, voice } = req.input ?? {};
|
|
135
|
+
const files = attachments ?? [];
|
|
136
|
+
const clips = voice ?? [];
|
|
137
|
+
if (files.length + clips.length > 0) {
|
|
138
|
+
assertAttachmentLimits([...files, ...clips], requireMediaLimits(profile));
|
|
139
|
+
}
|
|
140
|
+
const parts = [];
|
|
141
|
+
if (files.length > 0) {
|
|
142
|
+
parts.push(...mediaParts(profile, model, files, 'attachments'));
|
|
143
|
+
}
|
|
144
|
+
if (clips.length > 0) {
|
|
145
|
+
parts.push(...mediaParts(profile, model, clips, 'voice'));
|
|
146
|
+
}
|
|
147
|
+
return parts;
|
|
148
|
+
}
|
|
149
|
+
function resolveInputParts(profile, model, req) {
|
|
150
|
+
const parts = [];
|
|
151
|
+
const textPart = extractTextPart(profile, req);
|
|
152
|
+
if (textPart) {
|
|
153
|
+
parts.push(textPart);
|
|
154
|
+
}
|
|
155
|
+
parts.push(...extractMediaParts(profile, model, req));
|
|
156
|
+
return parts;
|
|
157
|
+
}
|
|
158
|
+
function assertImageGrounding(profile, model, builtins) {
|
|
159
|
+
if (profile.outputs.image?.allowsGrounding === false && builtins.length > 0) {
|
|
160
|
+
throw new TheorumError(`Grounding tools are not valid on ${model}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export { assertImageGrounding, assertSpeechRole, resolveImageFormat, resolveInputParts };
|
|
@@ -11,16 +11,14 @@ import type { Profile } from '../types.js';
|
|
|
11
11
|
export type ProfileDefinition = {
|
|
12
12
|
id: Profile['id'];
|
|
13
13
|
identity?: Partial<Profile['identity']>;
|
|
14
|
-
model: Partial<Profile['model']> & Pick<Profile['model'], 'allow'>;
|
|
14
|
+
model: Partial<Profile['model']> & Pick<Profile['model'], 'allow' | 'config'>;
|
|
15
15
|
tools?: Partial<Profile['tools']>;
|
|
16
16
|
inputs?: Partial<Profile['inputs']>;
|
|
17
17
|
outputs?: Partial<Profile['outputs']>;
|
|
18
18
|
guardrails?: Partial<Profile['guardrails']>;
|
|
19
19
|
};
|
|
20
|
-
/** Backwards-compatible alias for callers using the older name. */
|
|
21
|
-
export type DefineProfileInput = ProfileDefinition;
|
|
22
20
|
/** Define a typed profile with stable defaults for optional properties. */
|
|
23
|
-
declare function defineProfile(input:
|
|
21
|
+
declare function defineProfile(input: ProfileDefinition): Profile;
|
|
24
22
|
/** Register one host-owned profile in the process-local registry. */
|
|
25
23
|
declare function registerProfile(profileInput: Profile | ProfileDefinition): void;
|
|
26
24
|
/** Register several host-owned profiles in order. */
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { TheorumError } from '../../guardrails/error.js';
|
|
10
10
|
const profiles = new Map();
|
|
11
|
+
function assertModelSpecs(profileId, allow, config) {
|
|
12
|
+
for (const id of allow) {
|
|
13
|
+
if (!config[id]) {
|
|
14
|
+
throw new TheorumError(`Profile ${profileId} allowlists '${id}' without a model spec`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
11
18
|
function buildDefaultIdentity(id, identity) {
|
|
12
19
|
return {
|
|
13
20
|
handle: identity?.handle ?? id,
|
|
@@ -16,17 +23,18 @@ function buildDefaultIdentity(id, identity) {
|
|
|
16
23
|
systemByRole: identity?.systemByRole,
|
|
17
24
|
};
|
|
18
25
|
}
|
|
19
|
-
function buildDefaultModel(model) {
|
|
26
|
+
function buildDefaultModel(profileId, model) {
|
|
27
|
+
assertModelSpecs(profileId, model.allow, model.config);
|
|
20
28
|
return {
|
|
21
29
|
protocol: model.protocol ?? 'geminiInteractions',
|
|
22
30
|
provider: model.provider ?? 'google',
|
|
23
31
|
allow: model.allow,
|
|
32
|
+
config: model.config,
|
|
24
33
|
thinking: model.thinking ?? 'minimal',
|
|
25
34
|
controls: model.controls ?? [],
|
|
26
35
|
maxSteps: model.maxSteps ?? 1,
|
|
27
36
|
key: model.key ?? 'freeA',
|
|
28
37
|
select: model.select,
|
|
29
|
-
override: model.override,
|
|
30
38
|
};
|
|
31
39
|
}
|
|
32
40
|
function buildDefaultInputs(inputs) {
|
|
@@ -44,8 +52,8 @@ function buildDefaultInputs(inputs) {
|
|
|
44
52
|
function buildDefaultOutputs(outputs) {
|
|
45
53
|
return {
|
|
46
54
|
structured: outputs?.structured ?? null,
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
image: outputs?.image,
|
|
56
|
+
speech: outputs?.speech,
|
|
49
57
|
validation: outputs?.validation,
|
|
50
58
|
streaming: outputs?.streaming,
|
|
51
59
|
};
|
|
@@ -64,7 +72,7 @@ function defineProfile(input) {
|
|
|
64
72
|
return {
|
|
65
73
|
id: input.id,
|
|
66
74
|
identity: buildDefaultIdentity(input.id, input.identity),
|
|
67
|
-
model: buildDefaultModel(input.model),
|
|
75
|
+
model: buildDefaultModel(input.id, input.model),
|
|
68
76
|
tools: { allow: input.tools?.allow ?? [] },
|
|
69
77
|
inputs: buildDefaultInputs(input.inputs),
|
|
70
78
|
outputs: buildDefaultOutputs(input.outputs),
|
|
@@ -74,6 +82,7 @@ function defineProfile(input) {
|
|
|
74
82
|
/** Register one host-owned profile in the process-local registry. */
|
|
75
83
|
function registerProfile(profileInput) {
|
|
76
84
|
const profile = defineProfile(profileInput);
|
|
85
|
+
assertModelSpecs(profile.id, profile.model.allow, profile.model.config);
|
|
77
86
|
const { attachments, voice, maxFiles, maxBytes, maxTurnBytes } = profile.inputs;
|
|
78
87
|
if (attachments || voice) {
|
|
79
88
|
if (!(maxFiles && maxBytes && maxTurnBytes)) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderCompleteRequest, ResolvedGeneration } from '../types.js';
|
|
2
|
+
/** Build the provider request projection shared by execution and tracing. */
|
|
3
|
+
declare function providerCompleteRequest(generation: ResolvedGeneration, system: string): ProviderCompleteRequest;
|
|
4
|
+
export { providerCompleteRequest };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Build the provider request projection shared by execution and tracing. */
|
|
2
|
+
function providerCompleteRequest(generation, system) {
|
|
3
|
+
return {
|
|
4
|
+
model: generation.model,
|
|
5
|
+
apiId: generation.apiId,
|
|
6
|
+
openRouterId: generation.openRouterId,
|
|
7
|
+
previousInteractionId: generation.previousInteractionId,
|
|
8
|
+
store: generation.store,
|
|
9
|
+
thinking: generation.thinking,
|
|
10
|
+
summaries: generation.summaries,
|
|
11
|
+
maxOutputTokens: generation.maxOutputTokens,
|
|
12
|
+
temperature: generation.temperature,
|
|
13
|
+
builtins: generation.builtins,
|
|
14
|
+
system,
|
|
15
|
+
input: generation.input,
|
|
16
|
+
history: generation.history,
|
|
17
|
+
dynamicTools: generation.dynamicTools,
|
|
18
|
+
dynamicToolLoader: generation.dynamicToolLoader,
|
|
19
|
+
structured: generation.structured,
|
|
20
|
+
image: generation.image,
|
|
21
|
+
speech: generation.speech,
|
|
22
|
+
geminiBucket: generation.geminiBucket,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export { providerCompleteRequest };
|
|
@@ -13,7 +13,6 @@ declare function resolveTurn(req: TurnRequest): {
|
|
|
13
13
|
profile: Profile;
|
|
14
14
|
generation: ResolvedGeneration;
|
|
15
15
|
};
|
|
16
|
-
/** UI projection: catalog ∩ profile. Swatches are not included. */
|
|
17
16
|
/** Project a registered profile into a safe host/UI inspection object. */
|
|
18
17
|
declare function projectProfile(id: Profile['id']): ProjectedProfile;
|
|
19
18
|
declare function pickSystemRole(profile: Profile, requested?: string): string;
|