Skip to content

Commit f1ae52e

Browse files
committed
better retry util, retry on fail to get full page content in extract
1 parent 101a47b commit f1ae52e

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

.changeset/shy-ants-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"magnitude-core": patch
3+
---
4+
5+
retry get full page content in extract

packages/magnitude-core/src/agent/browserAgent.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { renderMinimalAccessibilityTree } from "@/web/util";
99
import { narrateAgent, narrateBrowserAgent } from "./narrator";
1010
import { PartitionOptions, partitionHtml, MarkdownSerializerOptions, serializeToMarkdown } from 'magnitude-extract';
1111
import EventEmitter from "eventemitter3";
12+
import { retry } from "@/common/retry";
1213

1314
// export interface StartAgentWithWebOptions {
1415
// agentBaseOptions?: Partial<AgentOptions>;
@@ -119,7 +120,10 @@ export class BrowserAgent extends Agent {
119120
async extract<T extends Schema>(instructions: string, schema: T): Promise<z.infer<T>> {
120121
this.browserAgentEvents.emit('extractStarted', instructions, schema);
121122
//const htmlContent = await this.page.content();
122-
const htmlContent = await getFullPageContent(this.page);
123+
const htmlContent = await retry(
124+
async () => await getFullPageContent(this.page),
125+
{ retries: 5, delay: 200, exponential: true }
126+
);
123127
// const accessibilityTree = await this.page.accessibility.snapshot({ interestingOnly: true });
124128
// const pageRepr = renderMinimalAccessibilityTree(accessibilityTree);
125129

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
type RetryOptions = {
2+
retries?: number;
3+
delay?: number;
4+
maxDelay?: number;
5+
exponential?: boolean | number; // true = 2x, or custom multiplier
6+
throwOnExhaustion?: boolean;
7+
retryIf?: (error: Error) => boolean;
8+
onRetry?: (error: Error, attempt: number) => void;
9+
};
10+
11+
export async function retry<T>(
12+
fn: () => Promise<T>,
13+
options: RetryOptions & { throwOnExhaustion: false }
14+
): Promise<T | null>;
15+
16+
export async function retry<T>(
17+
fn: () => Promise<T>,
18+
options?: RetryOptions & { throwOnExhaustion?: true }
19+
): Promise<T>;
20+
21+
export async function retry<T>(
22+
fn: () => Promise<T>,
23+
options: RetryOptions = {}
24+
): Promise<T | null> {
25+
const {
26+
retries = 3,
27+
delay = 0,
28+
maxDelay = Infinity,
29+
exponential = false,
30+
throwOnExhaustion = true, // Now defaults to true
31+
retryIf = () => true,
32+
onRetry
33+
} = options;
34+
35+
const multiplier = exponential === true ? 2 : exponential || 1;
36+
37+
for (let attempt = 0; attempt <= retries; attempt++) {
38+
try {
39+
return await fn();
40+
} catch (error) {
41+
if (!(error instanceof Error)) {
42+
throw new Error(`Non-Error thrown: ${String(error)}`);
43+
}
44+
45+
if (attempt === retries || !retryIf(error)) {
46+
if (throwOnExhaustion) {
47+
throw error;
48+
}
49+
return null;
50+
}
51+
52+
if (onRetry) {
53+
onRetry(error, attempt + 1);
54+
}
55+
56+
if (delay > 0) {
57+
const currentDelay = Math.min(
58+
delay * Math.pow(multiplier, attempt),
59+
maxDelay
60+
);
61+
await new Promise(resolve => setTimeout(resolve, currentDelay));
62+
}
63+
}
64+
}
65+
66+
// Unreachable
67+
return null as any;
68+
}
69+

0 commit comments

Comments
 (0)