@ixo/oracle-runtime-workers 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +4 -2
- package/src/channels/auth.ts +54 -11
- package/src/channels/contract.ts +122 -13
- package/src/channels/turns.ts +12 -2
- package/src/core/budgeted-llm.ts +115 -6
- package/src/core/context-budget.ts +24 -26
- package/src/core/context-window.ts +39 -12
- package/src/core/domain-context/document.ts +100 -0
- package/src/core/domain-context/findings.ts +25 -0
- package/src/core/domain-context/index.ts +28 -0
- package/src/core/domain-context/integrity.ts +60 -0
- package/src/core/domain-context/lru.ts +63 -0
- package/src/core/domain-context/resolver.ts +651 -0
- package/src/core/domain-context/transport.ts +340 -0
- package/src/core/domain-context/turn-context.ts +443 -0
- package/src/core/domain-context/types.ts +119 -0
- package/src/core/domain-context/validator.ts +41 -0
- package/src/core/env.ts +60 -6
- package/src/core/index.ts +2 -0
- package/src/core/llm.ts +225 -4
- package/src/core/main-agent-types.ts +28 -0
- package/src/core/main-agent.ts +61 -4
- package/src/core/middlewares/tool-execution.ts +19 -2
- package/src/core/registries.ts +14 -3
- package/src/core/state.ts +2 -1
- package/src/core/sub-agent-fallback.ts +14 -2
- package/src/core/turn-budget.ts +19 -22
- package/src/do/contracts.ts +23 -2
- package/src/do/domain-context-store.ts +123 -0
- package/src/do/run-request.ts +6 -0
- package/src/do/sse-stream.ts +1 -1
- package/src/do/transcript.ts +9 -0
- package/src/do/turn-body.ts +2 -1
- package/src/do/turn-metadata.ts +13 -6
- package/src/do/user-oracle-do.ts +237 -60
- package/src/index.ts +16 -0
- package/src/llm/byo-adapter.ts +6 -0
- package/src/llm/byo-catalog.ts +7 -4
- package/src/llm/byo-client.ts +42 -7
- package/src/llm/provider-error.ts +21 -3
- package/src/llm/stream-liveness.ts +498 -0
- package/src/plugins/firecrawl/firecrawl-tools.ts +6 -0
- package/src/shell/app.ts +41 -8
package/src/llm/byo-catalog.ts
CHANGED
|
@@ -382,6 +382,8 @@ export const BYO_DEFAULT_MODEL: Record<ByoProvider, string> = {
|
|
|
382
382
|
* A role absent here falls back to the platform provider (deliberately:
|
|
383
383
|
* `embedding` everywhere — none of these providers is wired for the runtime's
|
|
384
384
|
* embedding path — and `vision` on DeepSeek, whose chat models are text-only).
|
|
385
|
+
* `main` and `summarizer` are not listed: both are the turn's selected model
|
|
386
|
+
* (`byoModelForRole`), so the history is summarized on the user's provider.
|
|
385
387
|
*/
|
|
386
388
|
export const BYO_ROLE_MODELS: Record<
|
|
387
389
|
ByoProvider,
|
|
@@ -447,6 +449,7 @@ export const BYO_ROLE_MODELS: Record<
|
|
|
447
449
|
*/
|
|
448
450
|
const KNOWN_MODEL_ROLES: ReadonlySet<string> = new Set([
|
|
449
451
|
'main',
|
|
452
|
+
'summarizer',
|
|
450
453
|
'skills',
|
|
451
454
|
'subagent',
|
|
452
455
|
'vision',
|
|
@@ -460,16 +463,16 @@ const KNOWN_MODEL_ROLES: ReadonlySet<string> = new Set([
|
|
|
460
463
|
|
|
461
464
|
/**
|
|
462
465
|
* Resolve the provider-native model id for a role on a BYO turn.
|
|
463
|
-
* `main`
|
|
464
|
-
* table. `null` → the role is not served by this provider and
|
|
465
|
-
* must fall back to the platform adapter.
|
|
466
|
+
* `main` and `summarizer` use the turn's selected model; other roles use the
|
|
467
|
+
* translation table. `null` → the role is not served by this provider and
|
|
468
|
+
* the caller must fall back to the platform adapter.
|
|
466
469
|
*/
|
|
467
470
|
export function byoModelForRole(
|
|
468
471
|
provider: ByoProvider,
|
|
469
472
|
role: ProviderModelRole | string,
|
|
470
473
|
mainModelId: string,
|
|
471
474
|
): string | null {
|
|
472
|
-
if (role === 'main') return mainModelId;
|
|
475
|
+
if (role === 'main' || role === 'summarizer') return mainModelId;
|
|
473
476
|
const map = BYO_ROLE_MODELS[provider];
|
|
474
477
|
const direct = map[role as ProviderModelRole];
|
|
475
478
|
if (direct) return direct;
|
package/src/llm/byo-client.ts
CHANGED
|
@@ -17,8 +17,13 @@
|
|
|
17
17
|
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
18
18
|
import { ChatOpenAI } from '@langchain/openai';
|
|
19
19
|
import type { ChatOpenAIFields, Logger } from '../plugin-api/types';
|
|
20
|
-
import type
|
|
20
|
+
import { streamLivenessFromEnv, type ProviderModelRole } from '../core/llm';
|
|
21
21
|
import type { ByoCredential } from './byo-catalog';
|
|
22
|
+
import {
|
|
23
|
+
livenessFetchFor,
|
|
24
|
+
livenessModelFields,
|
|
25
|
+
type StreamLivenessSettings,
|
|
26
|
+
} from './stream-liveness';
|
|
22
27
|
|
|
23
28
|
/** OpenAI-compatible base URLs for the API-key providers that need one. */
|
|
24
29
|
export const DEEPSEEK_BASE_URL = 'https://api.deepseek.com/v1';
|
|
@@ -133,6 +138,11 @@ export interface CreateByoChatModelArgs {
|
|
|
133
138
|
logger?: Logger;
|
|
134
139
|
/** ChatGPT lane only: where requests go (default: the real backend). */
|
|
135
140
|
chatGptBackend?: ChatGptBackendConfig;
|
|
141
|
+
/**
|
|
142
|
+
* Liveness budgets for this model's calls (default: the BYO lane's
|
|
143
|
+
* defaults, see `streamLivenessFromEnv`).
|
|
144
|
+
*/
|
|
145
|
+
streamLiveness?: StreamLivenessSettings;
|
|
136
146
|
}
|
|
137
147
|
|
|
138
148
|
const NOOP: Logger = {
|
|
@@ -164,17 +174,35 @@ export function createByoChatModel(
|
|
|
164
174
|
// "Thinking..." for a minute before any error surfaces. Two attempts keep
|
|
165
175
|
// transient-blip resilience while failing fast enough to report.
|
|
166
176
|
const maxRetries = 2;
|
|
177
|
+
// Every call runs behind the stream liveness guard with the BYO lane's
|
|
178
|
+
// budgets: a direct provider may stay silent while a model reasons, so
|
|
179
|
+
// its idle budget is longer than the platform lane's.
|
|
180
|
+
const liveness = args.streamLiveness ?? streamLivenessFromEnv({}, 'byo');
|
|
181
|
+
const callerConfiguration = asRecord(params?.configuration);
|
|
182
|
+
const guardedFetch = (base?: typeof fetch): typeof fetch =>
|
|
183
|
+
livenessFetchFor({
|
|
184
|
+
settings: liveness,
|
|
185
|
+
label: `byo:${credential.provider} ${String(role)} ${modelId}`,
|
|
186
|
+
logger,
|
|
187
|
+
callerFetch: callerConfiguration.fetch,
|
|
188
|
+
...(base ? { base } : {}),
|
|
189
|
+
});
|
|
167
190
|
|
|
168
191
|
switch (credential.provider) {
|
|
169
192
|
case 'openai':
|
|
170
193
|
return new ChatOpenAI({
|
|
171
194
|
__includeRawResponse: true,
|
|
172
195
|
maxRetries,
|
|
196
|
+
...livenessModelFields(liveness),
|
|
173
197
|
...params,
|
|
174
198
|
model: modelId,
|
|
175
199
|
apiKey: credential.apiKey,
|
|
176
200
|
temperature: undefined,
|
|
177
201
|
topP: undefined,
|
|
202
|
+
configuration: {
|
|
203
|
+
...callerConfiguration,
|
|
204
|
+
fetch: guardedFetch(),
|
|
205
|
+
},
|
|
178
206
|
});
|
|
179
207
|
|
|
180
208
|
case 'deepseek':
|
|
@@ -182,12 +210,14 @@ export function createByoChatModel(
|
|
|
182
210
|
temperature,
|
|
183
211
|
__includeRawResponse: true,
|
|
184
212
|
maxRetries,
|
|
213
|
+
...livenessModelFields(liveness),
|
|
185
214
|
...params,
|
|
186
215
|
model: modelId,
|
|
187
216
|
apiKey: credential.apiKey,
|
|
188
217
|
configuration: {
|
|
189
218
|
baseURL: DEEPSEEK_BASE_URL,
|
|
190
|
-
...
|
|
219
|
+
...callerConfiguration,
|
|
220
|
+
fetch: guardedFetch(),
|
|
191
221
|
},
|
|
192
222
|
});
|
|
193
223
|
|
|
@@ -196,12 +226,14 @@ export function createByoChatModel(
|
|
|
196
226
|
temperature,
|
|
197
227
|
__includeRawResponse: true,
|
|
198
228
|
maxRetries,
|
|
229
|
+
...livenessModelFields(liveness),
|
|
199
230
|
...params,
|
|
200
231
|
model: modelId,
|
|
201
232
|
apiKey: credential.apiKey,
|
|
202
233
|
configuration: {
|
|
203
234
|
baseURL: GEMINI_OPENAI_COMPAT_BASE_URL,
|
|
204
|
-
...
|
|
235
|
+
...callerConfiguration,
|
|
236
|
+
fetch: guardedFetch(),
|
|
205
237
|
},
|
|
206
238
|
});
|
|
207
239
|
|
|
@@ -211,6 +243,7 @@ export function createByoChatModel(
|
|
|
211
243
|
return new ChatOpenAI({
|
|
212
244
|
__includeRawResponse: true,
|
|
213
245
|
maxRetries,
|
|
246
|
+
...livenessModelFields(liveness),
|
|
214
247
|
...params,
|
|
215
248
|
model: modelId,
|
|
216
249
|
apiKey: credential.apiKey,
|
|
@@ -218,7 +251,8 @@ export function createByoChatModel(
|
|
|
218
251
|
topP: undefined,
|
|
219
252
|
configuration: {
|
|
220
253
|
baseURL: ANTHROPIC_OPENAI_COMPAT_BASE_URL,
|
|
221
|
-
...
|
|
254
|
+
...callerConfiguration,
|
|
255
|
+
fetch: guardedFetch(),
|
|
222
256
|
},
|
|
223
257
|
});
|
|
224
258
|
|
|
@@ -232,7 +266,7 @@ export function createByoChatModel(
|
|
|
232
266
|
// distinct headers upstream — send both. The backend rejects sampling
|
|
233
267
|
// params (`temperature`/`top_p`) the way the standard API does for
|
|
234
268
|
// reasoning models, and reports request errors with an EMPTY body —
|
|
235
|
-
// hence the diagnostic fetch.
|
|
269
|
+
// hence the diagnostic fetch, inside the liveness guard.
|
|
236
270
|
const sessionId = crypto.randomUUID();
|
|
237
271
|
return new ChatOpenAI({
|
|
238
272
|
__includeRawResponse: true,
|
|
@@ -249,6 +283,7 @@ export function createByoChatModel(
|
|
|
249
283
|
// Effort + human-readable reasoning summaries, exactly the pair the
|
|
250
284
|
// Codex clients send — summaries feed the portal's thinking stream.
|
|
251
285
|
reasoning: { effort: 'medium', summary: 'auto' },
|
|
286
|
+
...livenessModelFields(liveness),
|
|
252
287
|
...params,
|
|
253
288
|
model: modelId,
|
|
254
289
|
apiKey: credential.oauth.accessToken,
|
|
@@ -257,8 +292,8 @@ export function createByoChatModel(
|
|
|
257
292
|
topP: undefined,
|
|
258
293
|
configuration: {
|
|
259
294
|
baseURL: backend.baseUrl,
|
|
260
|
-
|
|
261
|
-
|
|
295
|
+
...callerConfiguration,
|
|
296
|
+
fetch: guardedFetch(chatGptDiagnosticFetch(logger)),
|
|
262
297
|
defaultHeaders: {
|
|
263
298
|
...chatGptBackendHeaders(backend),
|
|
264
299
|
'ChatGPT-Account-ID': credential.oauth.accountId,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
isByoProvider,
|
|
15
15
|
type ByoProvider,
|
|
16
16
|
} from './byo-catalog';
|
|
17
|
+
import { findProviderStall, type ProviderStallError } from './stream-liveness';
|
|
17
18
|
|
|
18
19
|
/** `kind` value for the BYO degradation notice. */
|
|
19
20
|
export const BYO_FALLBACK_KIND = 'byo_fallback';
|
|
@@ -283,12 +284,27 @@ export interface ClassifyLlmErrorContext {
|
|
|
283
284
|
byoProvider?: ByoProvider | string | null;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
/**
|
|
288
|
+
* What the user is told about a stalled call (`ProviderStallError`): a stall
|
|
289
|
+
* mid-reply reads differently from a model that never started.
|
|
290
|
+
*/
|
|
291
|
+
function stallMessage(stall: ProviderStallError): string {
|
|
292
|
+
return stall.phase === 'stream'
|
|
293
|
+
? 'The model stopped responding mid-reply. Please try again.'
|
|
294
|
+
: 'The model did not start responding. Please try again.';
|
|
295
|
+
}
|
|
296
|
+
|
|
286
297
|
export function classifyLlmError(
|
|
287
298
|
error: unknown,
|
|
288
299
|
ctx?: ClassifyLlmErrorContext,
|
|
289
300
|
): ClassifiedLlmError {
|
|
290
|
-
|
|
291
|
-
|
|
301
|
+
// A stall is found through the wrappers (the OpenAI SDK's
|
|
302
|
+
// `APIConnectionError`, LangChain's middleware errors) by its `cause`.
|
|
303
|
+
const stall = findProviderStall(error);
|
|
304
|
+
const parts: ErrorParts = stall
|
|
305
|
+
? { text: stall.message }
|
|
306
|
+
: extractErrorParts(error);
|
|
307
|
+
const kind: LlmErrorKind = stall ? 'timeout' : detectKind(parts);
|
|
292
308
|
const provider =
|
|
293
309
|
typeof ctx?.byoProvider === 'string' &&
|
|
294
310
|
isByoProvider(ctx.byoProvider) &&
|
|
@@ -305,7 +321,9 @@ export function classifyLlmError(
|
|
|
305
321
|
...(providerLabel && { providerLabel }),
|
|
306
322
|
...(parts.status !== undefined && { status: parts.status }),
|
|
307
323
|
retryable: RETRYABLE_KINDS.has(kind),
|
|
308
|
-
message:
|
|
324
|
+
message: stall
|
|
325
|
+
? stallMessage(stall)
|
|
326
|
+
: fallbackMessage(kind, provider, providerLabel),
|
|
309
327
|
detail: parts.text,
|
|
310
328
|
};
|
|
311
329
|
}
|