@ddwang/magnitude-core 0.3.1-ddwang.17 → 0.3.1-ddwang.18
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/dist/agent/errors.d.ts +4 -0
- package/dist/agent/errors.js +8 -0
- package/dist/agent/index.d.ts +4 -2
- package/dist/agent/index.js +1 -1
- package/dist/ai/modelHarness.d.ts +9 -2
- package/dist/ai/modelHarness.js +29 -17
- package/dist/ai/multiModelHarness.d.ts +5 -2
- package/dist/ai/multiModelHarness.js +2 -2
- package/dist/ai/types.d.ts +5 -3
- package/dist/index.cjs +54 -26
- package/dist/index.d.cts +18 -7
- package/dist/index.mjs +54 -27
- package/package.json +1 -1
package/dist/agent/errors.d.ts
CHANGED
|
@@ -26,3 +26,7 @@ export declare class PlannerTimeoutError extends AgentError {
|
|
|
26
26
|
readonly timeoutMs: number;
|
|
27
27
|
constructor(timeoutMs: number);
|
|
28
28
|
}
|
|
29
|
+
export declare class ExtractTimeoutError extends AgentError {
|
|
30
|
+
readonly timeoutMs: number;
|
|
31
|
+
constructor(timeoutMs: number);
|
|
32
|
+
}
|
package/dist/agent/errors.js
CHANGED
|
@@ -63,6 +63,14 @@ export class PlannerTimeoutError extends AgentError {
|
|
|
63
63
|
this.name = 'PlannerTimeoutError';
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
export class ExtractTimeoutError extends AgentError {
|
|
67
|
+
timeoutMs;
|
|
68
|
+
constructor(timeoutMs) {
|
|
69
|
+
super(`Extract call exceeded its ${timeoutMs} ms request budget`, { variant: 'extract_timeout' });
|
|
70
|
+
this.timeoutMs = timeoutMs;
|
|
71
|
+
this.name = 'ExtractTimeoutError';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
66
74
|
// // Agent will only throw these types of errors
|
|
67
75
|
// import { ActionIngredient } from "./recipe/types";
|
|
68
76
|
// import { WebAction } from "./web/types";
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Action } from "@/actions/types";
|
|
|
4
4
|
import { AgentEvents } from "@/common/events";
|
|
5
5
|
import { AgentConnector } from '@/connectors';
|
|
6
6
|
import { RenderableContent } from '@/memory/observation';
|
|
7
|
-
import { LLMClient, type
|
|
7
|
+
import { LLMClient, type TimeoutPolicy } from "@/ai/types";
|
|
8
8
|
import { type OperationDiagnostics, type OperationKind, type OperationOptions } from '@/common/operation';
|
|
9
9
|
import { AgentMemory, MemoryRenderOptions } from "@/memory";
|
|
10
10
|
import { ActionDefinition } from "@/actions";
|
|
@@ -17,7 +17,9 @@ export interface AgentOptions {
|
|
|
17
17
|
telemetry?: boolean;
|
|
18
18
|
maxActions?: number;
|
|
19
19
|
/** Omitted preserves existing planner retry behavior without a per-call timeout. */
|
|
20
|
-
planner?:
|
|
20
|
+
planner?: TimeoutPolicy;
|
|
21
|
+
/** Omitted runs extract() without a per-call timeout. */
|
|
22
|
+
extract?: TimeoutPolicy;
|
|
21
23
|
}
|
|
22
24
|
export interface ActOptions extends OperationOptions {
|
|
23
25
|
prompt?: string;
|
package/dist/agent/index.js
CHANGED
|
@@ -98,7 +98,7 @@ export class Agent {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
//this.model = new ModelHarness({ llm: this.options.llm });
|
|
101
|
-
this.models = new MultiModelHarness(llms, this.options.planner);
|
|
101
|
+
this.models = new MultiModelHarness(llms, { planner: this.options.planner, extract: this.options.extract });
|
|
102
102
|
this.models.events.on('tokensUsed', (usage) => this.events.emit('tokensUsed', usage), this);
|
|
103
103
|
this.doneActing = false;
|
|
104
104
|
this._paused = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentContext } from "@/ai/baml_client";
|
|
2
2
|
import { ClientRegistry } from "@boundaryml/baml";
|
|
3
|
-
import { LLMClient, ModelUsage, type
|
|
3
|
+
import { LLMClient, ModelUsage, type TimeoutPolicy } from "@/ai/types";
|
|
4
4
|
import { ActionDefinition } from "@/actions";
|
|
5
5
|
import { Schema, z } from 'zod';
|
|
6
6
|
import { Image } from '@/memory/image';
|
|
@@ -9,7 +9,8 @@ import { MultiMediaContentPart } from "@/memory/rendering";
|
|
|
9
9
|
import { type PlannerResponse } from './plannerResponse';
|
|
10
10
|
interface ModelHarnessOptions {
|
|
11
11
|
llm: LLMClient;
|
|
12
|
-
planner?:
|
|
12
|
+
planner?: TimeoutPolicy;
|
|
13
|
+
extract?: TimeoutPolicy;
|
|
13
14
|
}
|
|
14
15
|
export interface ModelHarnessEvents {
|
|
15
16
|
'tokensUsed': (usage: ModelUsage) => {};
|
|
@@ -33,6 +34,12 @@ export declare class ModelHarness {
|
|
|
33
34
|
private _withUsage;
|
|
34
35
|
private _reportCallUsage;
|
|
35
36
|
partialAct<T>(context: AgentContext, task: string, data: MultiMediaContentPart[], actionVocabulary: ActionDefinition<T>[]): Promise<PlannerResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Runs one model call under an optional timeout policy: abort at timeoutMs, wait for the request to
|
|
39
|
+
* settle, then retry up to maxRetries times. Caller cancellation and deadlines take precedence.
|
|
40
|
+
* Planner calls are also recorded in the operation's plannerCalls.
|
|
41
|
+
*/
|
|
42
|
+
private withTimeout;
|
|
36
43
|
private partialActAttempt;
|
|
37
44
|
extract<T extends Schema>(instructions: string, schema: T, screenshot: Image, domContent: string): Promise<z.infer<T>>;
|
|
38
45
|
query<T extends Schema>(context: AgentContext, query: string, schema: T): Promise<z.infer<T>>;
|
package/dist/ai/modelHarness.js
CHANGED
|
@@ -12,7 +12,7 @@ import { anthropicOutputFormat, BASETEN_ENFORCED, plannerSchema, usesStructuredO
|
|
|
12
12
|
import { ModelResponseError } from './modelResponseError';
|
|
13
13
|
import { DEFAULT_BASETEN_MODEL } from './baseten';
|
|
14
14
|
import { beginOperationPhase, checkOperation, currentOperation, operationOptions } from '@/common/operation';
|
|
15
|
-
import { OperationDeadlineError, PlannerTimeoutError } from '@/agent/errors';
|
|
15
|
+
import { ExtractTimeoutError, OperationDeadlineError, PlannerTimeoutError } from '@/agent/errors';
|
|
16
16
|
export class ModelHarness {
|
|
17
17
|
/**
|
|
18
18
|
* Strong reasoning agent for high level strategy and planning.
|
|
@@ -25,17 +25,21 @@ export class ModelHarness {
|
|
|
25
25
|
logger;
|
|
26
26
|
planner;
|
|
27
27
|
constructor(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
for (const name of ['planner', 'extract']) {
|
|
29
|
+
const policy = options[name];
|
|
30
|
+
if (!policy)
|
|
31
|
+
continue;
|
|
32
|
+
if (!Number.isInteger(policy.timeoutMs) || policy.timeoutMs <= 0 || policy.timeoutMs > 2_147_483_647) {
|
|
33
|
+
throw new TypeError(`${name}.timeoutMs must be an integer between 1 and 2147483647`);
|
|
31
34
|
}
|
|
32
|
-
if (!Number.isSafeInteger(
|
|
33
|
-
throw new TypeError(
|
|
35
|
+
if (!Number.isSafeInteger(policy.maxRetries ?? 1) || (policy.maxRetries ?? 1) < 0) {
|
|
36
|
+
throw new TypeError(`${name}.maxRetries must be a non-negative safe integer`);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
this.options = {
|
|
37
40
|
llm: options.llm,
|
|
38
41
|
planner: options.planner && { ...options.planner },
|
|
42
|
+
extract: options.extract && { ...options.extract },
|
|
39
43
|
};
|
|
40
44
|
this.logger = logger.child({ name: 'llm' });
|
|
41
45
|
}
|
|
@@ -279,11 +283,19 @@ export class ModelHarness {
|
|
|
279
283
|
return usage;
|
|
280
284
|
}
|
|
281
285
|
async partialAct(context, task, data, actionVocabulary) {
|
|
282
|
-
|
|
286
|
+
return this.withTimeout(this.options.planner, timeoutMs => new PlannerTimeoutError(timeoutMs), (checkRequest, callId) => this.partialActAttempt(context, task, data, actionVocabulary, checkRequest, callId), true);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Runs one model call under an optional timeout policy: abort at timeoutMs, wait for the request to
|
|
290
|
+
* settle, then retry up to maxRetries times. Caller cancellation and deadlines take precedence.
|
|
291
|
+
* Planner calls are also recorded in the operation's plannerCalls.
|
|
292
|
+
*/
|
|
293
|
+
async withTimeout(policy, timeoutError, call, plannerCall = false) {
|
|
283
294
|
for (let attempt = 1;; attempt++) {
|
|
284
295
|
checkOperation();
|
|
285
296
|
const operation = currentOperation();
|
|
286
297
|
const controller = new AbortController();
|
|
298
|
+
const timeout = policy && timeoutError(policy.timeoutMs);
|
|
287
299
|
const abortFromParent = () => controller.abort(operation.signal.reason);
|
|
288
300
|
const started = performance.now();
|
|
289
301
|
const checkRequest = () => {
|
|
@@ -291,17 +303,17 @@ export class ModelHarness {
|
|
|
291
303
|
// Synchronous validation can occupy the event loop past the
|
|
292
304
|
// timer's due time, on either success or failure.
|
|
293
305
|
if (policy && performance.now() - started >= policy.timeoutMs)
|
|
294
|
-
controller.abort(
|
|
306
|
+
controller.abort(timeout);
|
|
295
307
|
controller.signal.throwIfAborted();
|
|
296
308
|
return controller.signal;
|
|
297
309
|
};
|
|
298
310
|
let timer;
|
|
299
|
-
const callId = operation?.startPlannerCall({
|
|
311
|
+
const callId = plannerCall ? operation?.startPlannerCall({
|
|
300
312
|
attempt, provider: this.options.llm.provider,
|
|
301
313
|
model: (this.options.llm.options.model ?? 'unknown').slice(0, 200),
|
|
302
314
|
...(policy ? { timeoutMs: policy.timeoutMs } : {}),
|
|
303
|
-
});
|
|
304
|
-
const abortOutcome = () => controller.signal.reason
|
|
315
|
+
}) : undefined;
|
|
316
|
+
const abortOutcome = () => controller.signal.reason === timeout ? 'timeout'
|
|
305
317
|
: controller.signal.reason instanceof OperationDeadlineError ? 'deadline' : 'cancelled';
|
|
306
318
|
controller.signal.addEventListener('abort', () => {
|
|
307
319
|
if (callId)
|
|
@@ -311,28 +323,28 @@ export class ModelHarness {
|
|
|
311
323
|
if (operation?.signal.aborted)
|
|
312
324
|
abortFromParent();
|
|
313
325
|
if (policy)
|
|
314
|
-
timer = setTimeout(() => controller.abort(
|
|
326
|
+
timer = setTimeout(() => controller.abort(timeout), policy.timeoutMs);
|
|
315
327
|
let outcome = 'failed';
|
|
316
328
|
try {
|
|
317
329
|
checkRequest();
|
|
318
330
|
// Await native settlement, not a Promise.race: a timeout must
|
|
319
331
|
// never release ownership or overlap a still-running request.
|
|
320
|
-
let
|
|
332
|
+
let result;
|
|
321
333
|
try {
|
|
322
|
-
|
|
334
|
+
result = await call(checkRequest, callId);
|
|
323
335
|
}
|
|
324
336
|
finally {
|
|
325
337
|
checkRequest();
|
|
326
338
|
}
|
|
327
339
|
outcome = 'succeeded';
|
|
328
|
-
return
|
|
340
|
+
return result;
|
|
329
341
|
}
|
|
330
342
|
catch (error) {
|
|
331
343
|
checkOperation();
|
|
332
344
|
if (!controller.signal.aborted)
|
|
333
345
|
throw error;
|
|
334
346
|
outcome = abortOutcome();
|
|
335
|
-
if (
|
|
347
|
+
if (controller.signal.reason !== timeout || attempt > (policy?.maxRetries ?? 1)) {
|
|
336
348
|
throw controller.signal.reason;
|
|
337
349
|
}
|
|
338
350
|
}
|
|
@@ -419,7 +431,7 @@ export class ModelHarness {
|
|
|
419
431
|
// }
|
|
420
432
|
const clientRegistry = this.clientForSchema(schema instanceof z.ZodObject ? schema : z.object({ data: schema }));
|
|
421
433
|
const bamlScreenshot = await screenshot.toBaml();
|
|
422
|
-
const resp = await this._withUsage(collector => this.baml.ExtractData(instructions, bamlScreenshot, domContent, this.options.llm.provider === 'claude-code', { tb, collector, clientRegistry, signal:
|
|
434
|
+
const resp = await this.withTimeout(this.options.extract, timeoutMs => new ExtractTimeoutError(timeoutMs), checkRequest => this._withUsage(collector => this.baml.ExtractData(instructions, bamlScreenshot, domContent, this.options.llm.provider === 'claude-code', { tb, collector, clientRegistry, signal: checkRequest() })));
|
|
423
435
|
return schema.parse(schema instanceof z.ZodObject ? resp : resp.data);
|
|
424
436
|
}
|
|
425
437
|
// ^ extract could prob be a subset of query w trimmed mem
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MultiMediaContentPart } from "@/memory/rendering";
|
|
2
2
|
import { ModelHarnessEvents } from "./modelHarness";
|
|
3
|
-
import { LLMClient, type
|
|
3
|
+
import { LLMClient, type TimeoutPolicy } from "./types";
|
|
4
4
|
import { ActionDefinition } from "@/actions";
|
|
5
5
|
import { AgentContext } from "./baml_client";
|
|
6
6
|
import { Image } from '@/memory/image';
|
|
@@ -14,7 +14,10 @@ export declare class MultiModelHarness {
|
|
|
14
14
|
private roles;
|
|
15
15
|
private uniqueModels;
|
|
16
16
|
readonly events: EventEmitter<ModelHarnessEvents>;
|
|
17
|
-
constructor(clients: LLMClient[],
|
|
17
|
+
constructor(clients: LLMClient[], policies?: {
|
|
18
|
+
planner?: TimeoutPolicy;
|
|
19
|
+
extract?: TimeoutPolicy;
|
|
20
|
+
});
|
|
18
21
|
setup(): Promise<void>;
|
|
19
22
|
describe(): string;
|
|
20
23
|
partialAct<T>(context: AgentContext, task: string, data: MultiMediaContentPart[], actionVocabulary: ActionDefinition<T>[]): Promise<PlannerResponse>;
|
|
@@ -10,11 +10,11 @@ export class MultiModelHarness {
|
|
|
10
10
|
roles = {};
|
|
11
11
|
uniqueModels = [];
|
|
12
12
|
events = new EventEmitter();
|
|
13
|
-
constructor(clients,
|
|
13
|
+
constructor(clients, policies = {}) {
|
|
14
14
|
// Sort by specificity (from least specific to most specific)
|
|
15
15
|
const sortedClients = [...clients].sort((a, b) => (b.roles ? b.roles.length : 9999) - (a.roles ? a.roles.length : 9999));
|
|
16
16
|
for (const client of sortedClients) {
|
|
17
|
-
const harness = new ModelHarness({ llm: client,
|
|
17
|
+
const harness = new ModelHarness({ llm: client, ...policies });
|
|
18
18
|
this.uniqueModels.push(harness);
|
|
19
19
|
if (client.roles) {
|
|
20
20
|
for (const role of client.roles) {
|
package/dist/ai/types.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export type BrowserAgentRole = 'act' | 'extract' | 'query';
|
|
2
2
|
export declare const allBrowserAgentRoles: BrowserAgentRole[];
|
|
3
|
-
/** Optional request budget for act() planning
|
|
4
|
-
export interface
|
|
5
|
-
/** Includes transport retries and format correction within one
|
|
3
|
+
/** Optional request budget for one model call, used for act() planning and for extract(). */
|
|
4
|
+
export interface TimeoutPolicy {
|
|
5
|
+
/** Includes transport retries and format correction within one invocation. */
|
|
6
6
|
timeoutMs: number;
|
|
7
7
|
/** Retries after timeout, once the previous invocation settles. Default: 1. */
|
|
8
8
|
maxRetries?: number;
|
|
9
9
|
}
|
|
10
|
+
/** The published name for the planner's policy, kept for existing imports. */
|
|
11
|
+
export type PlannerOptions = TimeoutPolicy;
|
|
10
12
|
export type LLMClient = (AnthropicClient | ClaudeCodeClient | BedrockClient | GoogleAIClient | GoogleVertexClient | OpenAIClient | BasetenClient | OpenAIGenericClient | AzureOpenAIClient) & {
|
|
11
13
|
roles?: BrowserAgentRole[];
|
|
12
14
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1157,6 +1157,13 @@ class PlannerTimeoutError extends AgentError {
|
|
|
1157
1157
|
this.name = "PlannerTimeoutError";
|
|
1158
1158
|
}
|
|
1159
1159
|
}
|
|
1160
|
+
class ExtractTimeoutError extends AgentError {
|
|
1161
|
+
constructor(timeoutMs) {
|
|
1162
|
+
super(`Extract call exceeded its ${timeoutMs} ms request budget`, { variant: "extract_timeout" });
|
|
1163
|
+
this.timeoutMs = timeoutMs;
|
|
1164
|
+
this.name = "ExtractTimeoutError";
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1160
1167
|
|
|
1161
1168
|
function attachOperationDiagnostics(error, diagnostics) {
|
|
1162
1169
|
if (!(error instanceof Error)) return;
|
|
@@ -3628,17 +3635,20 @@ class ModelHarness {
|
|
|
3628
3635
|
logger;
|
|
3629
3636
|
planner;
|
|
3630
3637
|
constructor(options) {
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3638
|
+
for (const name of ["planner", "extract"]) {
|
|
3639
|
+
const policy = options[name];
|
|
3640
|
+
if (!policy) continue;
|
|
3641
|
+
if (!Number.isInteger(policy.timeoutMs) || policy.timeoutMs <= 0 || policy.timeoutMs > 2147483647) {
|
|
3642
|
+
throw new TypeError(`${name}.timeoutMs must be an integer between 1 and 2147483647`);
|
|
3634
3643
|
}
|
|
3635
|
-
if (!Number.isSafeInteger(
|
|
3636
|
-
throw new TypeError(
|
|
3644
|
+
if (!Number.isSafeInteger(policy.maxRetries ?? 1) || (policy.maxRetries ?? 1) < 0) {
|
|
3645
|
+
throw new TypeError(`${name}.maxRetries must be a non-negative safe integer`);
|
|
3637
3646
|
}
|
|
3638
3647
|
}
|
|
3639
3648
|
this.options = {
|
|
3640
3649
|
llm: options.llm,
|
|
3641
|
-
planner: options.planner && { ...options.planner }
|
|
3650
|
+
planner: options.planner && { ...options.planner },
|
|
3651
|
+
extract: options.extract && { ...options.extract }
|
|
3642
3652
|
};
|
|
3643
3653
|
this.logger = logger.child({ name: "llm" });
|
|
3644
3654
|
}
|
|
@@ -3842,49 +3852,62 @@ class ModelHarness {
|
|
|
3842
3852
|
return usage;
|
|
3843
3853
|
}
|
|
3844
3854
|
async partialAct(context, task, data, actionVocabulary) {
|
|
3845
|
-
|
|
3855
|
+
return this.withTimeout(
|
|
3856
|
+
this.options.planner,
|
|
3857
|
+
(timeoutMs) => new PlannerTimeoutError(timeoutMs),
|
|
3858
|
+
(checkRequest, callId) => this.partialActAttempt(context, task, data, actionVocabulary, checkRequest, callId),
|
|
3859
|
+
true
|
|
3860
|
+
);
|
|
3861
|
+
}
|
|
3862
|
+
/**
|
|
3863
|
+
* Runs one model call under an optional timeout policy: abort at timeoutMs, wait for the request to
|
|
3864
|
+
* settle, then retry up to maxRetries times. Caller cancellation and deadlines take precedence.
|
|
3865
|
+
* Planner calls are also recorded in the operation's plannerCalls.
|
|
3866
|
+
*/
|
|
3867
|
+
async withTimeout(policy, timeoutError, call, plannerCall = false) {
|
|
3846
3868
|
for (let attempt = 1; ; attempt++) {
|
|
3847
3869
|
checkOperation();
|
|
3848
3870
|
const operation = currentOperation();
|
|
3849
3871
|
const controller = new AbortController();
|
|
3872
|
+
const timeout = policy && timeoutError(policy.timeoutMs);
|
|
3850
3873
|
const abortFromParent = () => controller.abort(operation.signal.reason);
|
|
3851
3874
|
const started = performance.now();
|
|
3852
3875
|
const checkRequest = () => {
|
|
3853
3876
|
checkOperation();
|
|
3854
|
-
if (policy && performance.now() - started >= policy.timeoutMs) controller.abort(
|
|
3877
|
+
if (policy && performance.now() - started >= policy.timeoutMs) controller.abort(timeout);
|
|
3855
3878
|
controller.signal.throwIfAborted();
|
|
3856
3879
|
return controller.signal;
|
|
3857
3880
|
};
|
|
3858
3881
|
let timer;
|
|
3859
|
-
const callId = operation?.startPlannerCall({
|
|
3882
|
+
const callId = plannerCall ? operation?.startPlannerCall({
|
|
3860
3883
|
attempt,
|
|
3861
3884
|
provider: this.options.llm.provider,
|
|
3862
3885
|
model: (this.options.llm.options.model ?? "unknown").slice(0, 200),
|
|
3863
3886
|
...policy ? { timeoutMs: policy.timeoutMs } : {}
|
|
3864
|
-
});
|
|
3865
|
-
const abortOutcome = () => controller.signal.reason
|
|
3887
|
+
}) : void 0;
|
|
3888
|
+
const abortOutcome = () => controller.signal.reason === timeout ? "timeout" : controller.signal.reason instanceof OperationDeadlineError ? "deadline" : "cancelled";
|
|
3866
3889
|
controller.signal.addEventListener("abort", () => {
|
|
3867
3890
|
if (callId) operation.updatePlannerCall(callId, abortOutcome(), "draining");
|
|
3868
3891
|
}, { once: true });
|
|
3869
3892
|
operation?.signal.addEventListener("abort", abortFromParent, { once: true });
|
|
3870
3893
|
if (operation?.signal.aborted) abortFromParent();
|
|
3871
|
-
if (policy) timer = setTimeout(() => controller.abort(
|
|
3894
|
+
if (policy) timer = setTimeout(() => controller.abort(timeout), policy.timeoutMs);
|
|
3872
3895
|
let outcome = "failed";
|
|
3873
3896
|
try {
|
|
3874
3897
|
checkRequest();
|
|
3875
|
-
let
|
|
3898
|
+
let result;
|
|
3876
3899
|
try {
|
|
3877
|
-
|
|
3900
|
+
result = await call(checkRequest, callId);
|
|
3878
3901
|
} finally {
|
|
3879
3902
|
checkRequest();
|
|
3880
3903
|
}
|
|
3881
3904
|
outcome = "succeeded";
|
|
3882
|
-
return
|
|
3905
|
+
return result;
|
|
3883
3906
|
} catch (error) {
|
|
3884
3907
|
checkOperation();
|
|
3885
3908
|
if (!controller.signal.aborted) throw error;
|
|
3886
3909
|
outcome = abortOutcome();
|
|
3887
|
-
if (
|
|
3910
|
+
if (controller.signal.reason !== timeout || attempt > (policy?.maxRetries ?? 1)) {
|
|
3888
3911
|
throw controller.signal.reason;
|
|
3889
3912
|
}
|
|
3890
3913
|
} finally {
|
|
@@ -3957,13 +3980,17 @@ class ModelHarness {
|
|
|
3957
3980
|
}
|
|
3958
3981
|
const clientRegistry = this.clientForSchema(schema instanceof z.z.ZodObject ? schema : z.z.object({ data: schema }));
|
|
3959
3982
|
const bamlScreenshot = await screenshot.toBaml();
|
|
3960
|
-
const resp = await this.
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3983
|
+
const resp = await this.withTimeout(
|
|
3984
|
+
this.options.extract,
|
|
3985
|
+
(timeoutMs) => new ExtractTimeoutError(timeoutMs),
|
|
3986
|
+
(checkRequest) => this._withUsage((collector) => this.baml.ExtractData(
|
|
3987
|
+
instructions,
|
|
3988
|
+
bamlScreenshot,
|
|
3989
|
+
domContent,
|
|
3990
|
+
this.options.llm.provider === "claude-code",
|
|
3991
|
+
{ tb, collector, clientRegistry, signal: checkRequest() }
|
|
3992
|
+
))
|
|
3993
|
+
);
|
|
3967
3994
|
return schema.parse(schema instanceof z.z.ZodObject ? resp : resp.data);
|
|
3968
3995
|
}
|
|
3969
3996
|
// ^ extract could prob be a subset of query w trimmed mem
|
|
@@ -4066,10 +4093,10 @@ class MultiModelHarness {
|
|
|
4066
4093
|
roles = {};
|
|
4067
4094
|
uniqueModels = [];
|
|
4068
4095
|
events = new EventEmitter();
|
|
4069
|
-
constructor(clients,
|
|
4096
|
+
constructor(clients, policies = {}) {
|
|
4070
4097
|
const sortedClients = [...clients].sort((a, b) => (b.roles ? b.roles.length : 9999) - (a.roles ? a.roles.length : 9999));
|
|
4071
4098
|
for (const client of sortedClients) {
|
|
4072
|
-
const harness = new ModelHarness({ llm: client,
|
|
4099
|
+
const harness = new ModelHarness({ llm: client, ...policies });
|
|
4073
4100
|
this.uniqueModels.push(harness);
|
|
4074
4101
|
if (client.roles) {
|
|
4075
4102
|
for (const role of client.roles) {
|
|
@@ -4549,7 +4576,7 @@ class Agent {
|
|
|
4549
4576
|
}
|
|
4550
4577
|
}
|
|
4551
4578
|
}
|
|
4552
|
-
this.models = new MultiModelHarness(llms, this.options.planner);
|
|
4579
|
+
this.models = new MultiModelHarness(llms, { planner: this.options.planner, extract: this.options.extract });
|
|
4553
4580
|
this.models.events.on("tokensUsed", (usage) => this.events.emit("tokensUsed", usage), this);
|
|
4554
4581
|
this.doneActing = false;
|
|
4555
4582
|
this._paused = false;
|
|
@@ -8292,6 +8319,7 @@ exports.BrowserBlockedError = BrowserBlockedError;
|
|
|
8292
8319
|
exports.BrowserConnector = BrowserConnector;
|
|
8293
8320
|
exports.BrowserProvider = BrowserProvider;
|
|
8294
8321
|
exports.DesktopConnector = DesktopConnector;
|
|
8322
|
+
exports.ExtractTimeoutError = ExtractTimeoutError;
|
|
8295
8323
|
exports.Observation = Observation;
|
|
8296
8324
|
exports.OperationCancelledError = OperationCancelledError;
|
|
8297
8325
|
exports.OperationDeadlineError = OperationDeadlineError;
|
package/dist/index.d.cts
CHANGED
|
@@ -83,13 +83,15 @@ interface CheckIntent {
|
|
|
83
83
|
|
|
84
84
|
type BrowserAgentRole = 'act' | 'extract' | 'query';
|
|
85
85
|
declare const allBrowserAgentRoles: BrowserAgentRole[];
|
|
86
|
-
/** Optional request budget for act() planning
|
|
87
|
-
interface
|
|
88
|
-
/** Includes transport retries and format correction within one
|
|
86
|
+
/** Optional request budget for one model call, used for act() planning and for extract(). */
|
|
87
|
+
interface TimeoutPolicy {
|
|
88
|
+
/** Includes transport retries and format correction within one invocation. */
|
|
89
89
|
timeoutMs: number;
|
|
90
90
|
/** Retries after timeout, once the previous invocation settles. Default: 1. */
|
|
91
91
|
maxRetries?: number;
|
|
92
92
|
}
|
|
93
|
+
/** The published name for the planner's policy, kept for existing imports. */
|
|
94
|
+
type PlannerOptions = TimeoutPolicy;
|
|
93
95
|
type LLMClient = (AnthropicClient | ClaudeCodeClient | BedrockClient | GoogleAIClient | GoogleVertexClient | OpenAIClient | BasetenClient | OpenAIGenericClient | AzureOpenAIClient) & {
|
|
94
96
|
roles?: BrowserAgentRole[];
|
|
95
97
|
};
|
|
@@ -558,7 +560,10 @@ declare class MultiModelHarness {
|
|
|
558
560
|
private roles;
|
|
559
561
|
private uniqueModels;
|
|
560
562
|
readonly events: EventEmitter<ModelHarnessEvents>;
|
|
561
|
-
constructor(clients: LLMClient[],
|
|
563
|
+
constructor(clients: LLMClient[], policies?: {
|
|
564
|
+
planner?: TimeoutPolicy;
|
|
565
|
+
extract?: TimeoutPolicy;
|
|
566
|
+
});
|
|
562
567
|
setup(): Promise<void>;
|
|
563
568
|
describe(): string;
|
|
564
569
|
partialAct<T>(context: AgentContext, task: string, data: MultiMediaContentPart[], actionVocabulary: ActionDefinition<T>[]): Promise<PlannerResponse>;
|
|
@@ -575,7 +580,9 @@ interface AgentOptions {
|
|
|
575
580
|
telemetry?: boolean;
|
|
576
581
|
maxActions?: number;
|
|
577
582
|
/** Omitted preserves existing planner retry behavior without a per-call timeout. */
|
|
578
|
-
planner?:
|
|
583
|
+
planner?: TimeoutPolicy;
|
|
584
|
+
/** Omitted runs extract() without a per-call timeout. */
|
|
585
|
+
extract?: TimeoutPolicy;
|
|
579
586
|
}
|
|
580
587
|
interface ActOptions extends OperationOptions {
|
|
581
588
|
prompt?: string;
|
|
@@ -1012,6 +1019,10 @@ declare class PlannerTimeoutError extends AgentError {
|
|
|
1012
1019
|
readonly timeoutMs: number;
|
|
1013
1020
|
constructor(timeoutMs: number);
|
|
1014
1021
|
}
|
|
1022
|
+
declare class ExtractTimeoutError extends AgentError {
|
|
1023
|
+
readonly timeoutMs: number;
|
|
1024
|
+
constructor(timeoutMs: number);
|
|
1025
|
+
}
|
|
1015
1026
|
|
|
1016
1027
|
/**
|
|
1017
1028
|
* Represents any reason why a test case could have failed, for example:
|
|
@@ -1151,5 +1162,5 @@ declare function buildDefaultBrowserAgentOptions({ agentOptions, browserOptions
|
|
|
1151
1162
|
|
|
1152
1163
|
declare const logger: pino.Logger<never, boolean>;
|
|
1153
1164
|
|
|
1154
|
-
export { ActionLimitError, Agent, AgentBusyError, AgentError, AgentMemory, BrowserAgent, BrowserBlockedError, BrowserConnector, BrowserProvider, DesktopConnector, Observation, OperationCancelledError, OperationDeadlineError, PlannerTimeoutError, WebHarness, allBrowserAgentRoles, buildDefaultBrowserAgentOptions, createAction, createId, deepEquals, getCodebaseId, getMachineId, logger, mergeMessages, posthog, retryOnError, retryOnErrorIsSuccess, sendTelemetry, startBrowserAgent };
|
|
1155
|
-
export type { ActOptions, Action, ActionContext, ActionDefinition, ActionIntent, ActionPayload, AgentConnector, AgentErrorOptions, AgentEvents, AgentMemoryOptions, AgentOptions, AnthropicClient, ApiKeyFailure, AzureOpenAIClient, Base64Image, BasetenClient, BedrockClient, BlockReason, BrowserAgentRole, BrowserBlock, BrowserClickDiagnostics, BrowserConnectorOptions, BrowserConnectorStateData, BrowserFailure, BrowserOptions, BugDetectedFailure, BugSeverity, CancelledFailure, CheckIntent, ClaudeCodeClient, ClickIntent, ClickWebAction, DesktopConnectorOptions, DesktopInterface, FailedTestCaseResult, FailureDescriptor, GoogleAIClient, GoogleVertexClient, HoverIntent, HoverWebAction, HttpDiagnostic, Intent, LLMClient, LLMClientIdentifier, MemoryRenderOptions, MisalignmentFailure, ModelUsage, NavigateWebAction, NetworkFailure, ObservableDataArray, ObservableDataObject, ObservableDataPrimitive, ObservationRetentionOptions, ObservationRole, ObservationSource, OpenAIClient, OpenAIGenericClient, OperationDiagnostics, OperationKind, OperationOptions, OperationPhase, OperationTiming, PixelCoordinate, PlannerCallDiagnostics, PlannerOptions, ProviderAttemptDiagnostics, RateLimitFailure, RecoveryOptions, RenderableContent, RetryMode, RetryOptions, RetryParams, ScrollIntent, ScrollWebAction, SerializedAgentMemory, SuccessfulTestCaseResult, SwitchTabIntent, SwitchTabWebAction, TestCaseDefinition, TestCaseResult, TestData, TestDataEntry, TestStepDefinition, TypeIntent, TypeWebAction, UnknownFailure, WebAction, WebHarnessEvents, WebHarnessOptions };
|
|
1165
|
+
export { ActionLimitError, Agent, AgentBusyError, AgentError, AgentMemory, BrowserAgent, BrowserBlockedError, BrowserConnector, BrowserProvider, DesktopConnector, ExtractTimeoutError, Observation, OperationCancelledError, OperationDeadlineError, PlannerTimeoutError, WebHarness, allBrowserAgentRoles, buildDefaultBrowserAgentOptions, createAction, createId, deepEquals, getCodebaseId, getMachineId, logger, mergeMessages, posthog, retryOnError, retryOnErrorIsSuccess, sendTelemetry, startBrowserAgent };
|
|
1166
|
+
export type { ActOptions, Action, ActionContext, ActionDefinition, ActionIntent, ActionPayload, AgentConnector, AgentErrorOptions, AgentEvents, AgentMemoryOptions, AgentOptions, AnthropicClient, ApiKeyFailure, AzureOpenAIClient, Base64Image, BasetenClient, BedrockClient, BlockReason, BrowserAgentRole, BrowserBlock, BrowserClickDiagnostics, BrowserConnectorOptions, BrowserConnectorStateData, BrowserFailure, BrowserOptions, BugDetectedFailure, BugSeverity, CancelledFailure, CheckIntent, ClaudeCodeClient, ClickIntent, ClickWebAction, DesktopConnectorOptions, DesktopInterface, FailedTestCaseResult, FailureDescriptor, GoogleAIClient, GoogleVertexClient, HoverIntent, HoverWebAction, HttpDiagnostic, Intent, LLMClient, LLMClientIdentifier, MemoryRenderOptions, MisalignmentFailure, ModelUsage, NavigateWebAction, NetworkFailure, ObservableDataArray, ObservableDataObject, ObservableDataPrimitive, ObservationRetentionOptions, ObservationRole, ObservationSource, OpenAIClient, OpenAIGenericClient, OperationDiagnostics, OperationKind, OperationOptions, OperationPhase, OperationTiming, PixelCoordinate, PlannerCallDiagnostics, PlannerOptions, ProviderAttemptDiagnostics, RateLimitFailure, RecoveryOptions, RenderableContent, RetryMode, RetryOptions, RetryParams, ScrollIntent, ScrollWebAction, SerializedAgentMemory, SuccessfulTestCaseResult, SwitchTabIntent, SwitchTabWebAction, TestCaseDefinition, TestCaseResult, TestData, TestDataEntry, TestStepDefinition, TimeoutPolicy, TypeIntent, TypeWebAction, UnknownFailure, WebAction, WebHarnessEvents, WebHarnessOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1134,6 +1134,13 @@ class PlannerTimeoutError extends AgentError {
|
|
|
1134
1134
|
this.name = "PlannerTimeoutError";
|
|
1135
1135
|
}
|
|
1136
1136
|
}
|
|
1137
|
+
class ExtractTimeoutError extends AgentError {
|
|
1138
|
+
constructor(timeoutMs) {
|
|
1139
|
+
super(`Extract call exceeded its ${timeoutMs} ms request budget`, { variant: "extract_timeout" });
|
|
1140
|
+
this.timeoutMs = timeoutMs;
|
|
1141
|
+
this.name = "ExtractTimeoutError";
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1137
1144
|
|
|
1138
1145
|
function attachOperationDiagnostics(error, diagnostics) {
|
|
1139
1146
|
if (!(error instanceof Error)) return;
|
|
@@ -3605,17 +3612,20 @@ class ModelHarness {
|
|
|
3605
3612
|
logger;
|
|
3606
3613
|
planner;
|
|
3607
3614
|
constructor(options) {
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3615
|
+
for (const name of ["planner", "extract"]) {
|
|
3616
|
+
const policy = options[name];
|
|
3617
|
+
if (!policy) continue;
|
|
3618
|
+
if (!Number.isInteger(policy.timeoutMs) || policy.timeoutMs <= 0 || policy.timeoutMs > 2147483647) {
|
|
3619
|
+
throw new TypeError(`${name}.timeoutMs must be an integer between 1 and 2147483647`);
|
|
3611
3620
|
}
|
|
3612
|
-
if (!Number.isSafeInteger(
|
|
3613
|
-
throw new TypeError(
|
|
3621
|
+
if (!Number.isSafeInteger(policy.maxRetries ?? 1) || (policy.maxRetries ?? 1) < 0) {
|
|
3622
|
+
throw new TypeError(`${name}.maxRetries must be a non-negative safe integer`);
|
|
3614
3623
|
}
|
|
3615
3624
|
}
|
|
3616
3625
|
this.options = {
|
|
3617
3626
|
llm: options.llm,
|
|
3618
|
-
planner: options.planner && { ...options.planner }
|
|
3627
|
+
planner: options.planner && { ...options.planner },
|
|
3628
|
+
extract: options.extract && { ...options.extract }
|
|
3619
3629
|
};
|
|
3620
3630
|
this.logger = logger.child({ name: "llm" });
|
|
3621
3631
|
}
|
|
@@ -3819,49 +3829,62 @@ class ModelHarness {
|
|
|
3819
3829
|
return usage;
|
|
3820
3830
|
}
|
|
3821
3831
|
async partialAct(context, task, data, actionVocabulary) {
|
|
3822
|
-
|
|
3832
|
+
return this.withTimeout(
|
|
3833
|
+
this.options.planner,
|
|
3834
|
+
(timeoutMs) => new PlannerTimeoutError(timeoutMs),
|
|
3835
|
+
(checkRequest, callId) => this.partialActAttempt(context, task, data, actionVocabulary, checkRequest, callId),
|
|
3836
|
+
true
|
|
3837
|
+
);
|
|
3838
|
+
}
|
|
3839
|
+
/**
|
|
3840
|
+
* Runs one model call under an optional timeout policy: abort at timeoutMs, wait for the request to
|
|
3841
|
+
* settle, then retry up to maxRetries times. Caller cancellation and deadlines take precedence.
|
|
3842
|
+
* Planner calls are also recorded in the operation's plannerCalls.
|
|
3843
|
+
*/
|
|
3844
|
+
async withTimeout(policy, timeoutError, call, plannerCall = false) {
|
|
3823
3845
|
for (let attempt = 1; ; attempt++) {
|
|
3824
3846
|
checkOperation();
|
|
3825
3847
|
const operation = currentOperation();
|
|
3826
3848
|
const controller = new AbortController();
|
|
3849
|
+
const timeout = policy && timeoutError(policy.timeoutMs);
|
|
3827
3850
|
const abortFromParent = () => controller.abort(operation.signal.reason);
|
|
3828
3851
|
const started = performance.now();
|
|
3829
3852
|
const checkRequest = () => {
|
|
3830
3853
|
checkOperation();
|
|
3831
|
-
if (policy && performance.now() - started >= policy.timeoutMs) controller.abort(
|
|
3854
|
+
if (policy && performance.now() - started >= policy.timeoutMs) controller.abort(timeout);
|
|
3832
3855
|
controller.signal.throwIfAborted();
|
|
3833
3856
|
return controller.signal;
|
|
3834
3857
|
};
|
|
3835
3858
|
let timer;
|
|
3836
|
-
const callId = operation?.startPlannerCall({
|
|
3859
|
+
const callId = plannerCall ? operation?.startPlannerCall({
|
|
3837
3860
|
attempt,
|
|
3838
3861
|
provider: this.options.llm.provider,
|
|
3839
3862
|
model: (this.options.llm.options.model ?? "unknown").slice(0, 200),
|
|
3840
3863
|
...policy ? { timeoutMs: policy.timeoutMs } : {}
|
|
3841
|
-
});
|
|
3842
|
-
const abortOutcome = () => controller.signal.reason
|
|
3864
|
+
}) : void 0;
|
|
3865
|
+
const abortOutcome = () => controller.signal.reason === timeout ? "timeout" : controller.signal.reason instanceof OperationDeadlineError ? "deadline" : "cancelled";
|
|
3843
3866
|
controller.signal.addEventListener("abort", () => {
|
|
3844
3867
|
if (callId) operation.updatePlannerCall(callId, abortOutcome(), "draining");
|
|
3845
3868
|
}, { once: true });
|
|
3846
3869
|
operation?.signal.addEventListener("abort", abortFromParent, { once: true });
|
|
3847
3870
|
if (operation?.signal.aborted) abortFromParent();
|
|
3848
|
-
if (policy) timer = setTimeout(() => controller.abort(
|
|
3871
|
+
if (policy) timer = setTimeout(() => controller.abort(timeout), policy.timeoutMs);
|
|
3849
3872
|
let outcome = "failed";
|
|
3850
3873
|
try {
|
|
3851
3874
|
checkRequest();
|
|
3852
|
-
let
|
|
3875
|
+
let result;
|
|
3853
3876
|
try {
|
|
3854
|
-
|
|
3877
|
+
result = await call(checkRequest, callId);
|
|
3855
3878
|
} finally {
|
|
3856
3879
|
checkRequest();
|
|
3857
3880
|
}
|
|
3858
3881
|
outcome = "succeeded";
|
|
3859
|
-
return
|
|
3882
|
+
return result;
|
|
3860
3883
|
} catch (error) {
|
|
3861
3884
|
checkOperation();
|
|
3862
3885
|
if (!controller.signal.aborted) throw error;
|
|
3863
3886
|
outcome = abortOutcome();
|
|
3864
|
-
if (
|
|
3887
|
+
if (controller.signal.reason !== timeout || attempt > (policy?.maxRetries ?? 1)) {
|
|
3865
3888
|
throw controller.signal.reason;
|
|
3866
3889
|
}
|
|
3867
3890
|
} finally {
|
|
@@ -3934,13 +3957,17 @@ class ModelHarness {
|
|
|
3934
3957
|
}
|
|
3935
3958
|
const clientRegistry = this.clientForSchema(schema instanceof z.ZodObject ? schema : z.object({ data: schema }));
|
|
3936
3959
|
const bamlScreenshot = await screenshot.toBaml();
|
|
3937
|
-
const resp = await this.
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3960
|
+
const resp = await this.withTimeout(
|
|
3961
|
+
this.options.extract,
|
|
3962
|
+
(timeoutMs) => new ExtractTimeoutError(timeoutMs),
|
|
3963
|
+
(checkRequest) => this._withUsage((collector) => this.baml.ExtractData(
|
|
3964
|
+
instructions,
|
|
3965
|
+
bamlScreenshot,
|
|
3966
|
+
domContent,
|
|
3967
|
+
this.options.llm.provider === "claude-code",
|
|
3968
|
+
{ tb, collector, clientRegistry, signal: checkRequest() }
|
|
3969
|
+
))
|
|
3970
|
+
);
|
|
3944
3971
|
return schema.parse(schema instanceof z.ZodObject ? resp : resp.data);
|
|
3945
3972
|
}
|
|
3946
3973
|
// ^ extract could prob be a subset of query w trimmed mem
|
|
@@ -4043,10 +4070,10 @@ class MultiModelHarness {
|
|
|
4043
4070
|
roles = {};
|
|
4044
4071
|
uniqueModels = [];
|
|
4045
4072
|
events = new EventEmitter();
|
|
4046
|
-
constructor(clients,
|
|
4073
|
+
constructor(clients, policies = {}) {
|
|
4047
4074
|
const sortedClients = [...clients].sort((a, b) => (b.roles ? b.roles.length : 9999) - (a.roles ? a.roles.length : 9999));
|
|
4048
4075
|
for (const client of sortedClients) {
|
|
4049
|
-
const harness = new ModelHarness({ llm: client,
|
|
4076
|
+
const harness = new ModelHarness({ llm: client, ...policies });
|
|
4050
4077
|
this.uniqueModels.push(harness);
|
|
4051
4078
|
if (client.roles) {
|
|
4052
4079
|
for (const role of client.roles) {
|
|
@@ -4526,7 +4553,7 @@ class Agent {
|
|
|
4526
4553
|
}
|
|
4527
4554
|
}
|
|
4528
4555
|
}
|
|
4529
|
-
this.models = new MultiModelHarness(llms, this.options.planner);
|
|
4556
|
+
this.models = new MultiModelHarness(llms, { planner: this.options.planner, extract: this.options.extract });
|
|
4530
4557
|
this.models.events.on("tokensUsed", (usage) => this.events.emit("tokensUsed", usage), this);
|
|
4531
4558
|
this.doneActing = false;
|
|
4532
4559
|
this._paused = false;
|
|
@@ -8255,4 +8282,4 @@ class DesktopConnector {
|
|
|
8255
8282
|
process.env.BAML_LOG = "off";
|
|
8256
8283
|
setLogLevel("OFF");
|
|
8257
8284
|
|
|
8258
|
-
export { ActionLimitError, Agent, AgentBusyError, AgentError, AgentMemory, BrowserAgent, BrowserBlockedError, BrowserConnector, BrowserProvider, DesktopConnector, Observation, OperationCancelledError, OperationDeadlineError, PlannerTimeoutError, WebHarness, allBrowserAgentRoles, buildDefaultBrowserAgentOptions, createAction, createId, deepEquals, getCodebaseId, getMachineId, logger, mergeMessages, posthog, retryOnError, retryOnErrorIsSuccess, sendTelemetry, startBrowserAgent };
|
|
8285
|
+
export { ActionLimitError, Agent, AgentBusyError, AgentError, AgentMemory, BrowserAgent, BrowserBlockedError, BrowserConnector, BrowserProvider, DesktopConnector, ExtractTimeoutError, Observation, OperationCancelledError, OperationDeadlineError, PlannerTimeoutError, WebHarness, allBrowserAgentRoles, buildDefaultBrowserAgentOptions, createAction, createId, deepEquals, getCodebaseId, getMachineId, logger, mergeMessages, posthog, retryOnError, retryOnErrorIsSuccess, sendTelemetry, startBrowserAgent };
|