Skip to content

Commit c842397

Browse files
committed
Fix stagehand.metrics
1 parent ade59cb commit c842397

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

.changeset/sharp-suns-fall.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@browserbasehq/stagehand": minor
3+
---
4+
5+
Update stagehand.metrics to work on every environment

lib/api.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
ExtractResult,
1818
ObserveOptions,
1919
ObserveResult,
20+
StagehandMetrics,
2021
} from "../types/stagehand";
2122
import { AgentExecuteOptions, AgentResult } from ".";
2223
import {
@@ -165,6 +166,99 @@ export class StagehandAPI {
165166
return response;
166167
}
167168

169+
async getReplayMetrics(): Promise<StagehandMetrics> {
170+
if (!this.sessionId) {
171+
throw new Error("sessionId is required to fetch metrics.");
172+
}
173+
174+
const response = await this.request(`/sessions/${this.sessionId}/replay`, {
175+
method: "GET",
176+
});
177+
178+
if (response.status !== 200) {
179+
const errorText = await response.text();
180+
this.logger({
181+
category: "api",
182+
message: `[HTTP ERROR] Failed to fetch metrics. Status ${response.status}: ${errorText}`,
183+
level: 1,
184+
});
185+
throw new Error(
186+
`Failed to fetch metrics with status ${response.status}: ${errorText}`,
187+
);
188+
}
189+
190+
const data = await response.json();
191+
192+
if (!data.success) {
193+
throw new Error(
194+
`Failed to fetch metrics: ${data.error || "Unknown error"}`,
195+
);
196+
}
197+
198+
// Parse the API data into StagehandMetrics format
199+
const apiData = data.data || {};
200+
const metrics: StagehandMetrics = {
201+
actPromptTokens: 0,
202+
actCompletionTokens: 0,
203+
actInferenceTimeMs: 0,
204+
extractPromptTokens: 0,
205+
extractCompletionTokens: 0,
206+
extractInferenceTimeMs: 0,
207+
observePromptTokens: 0,
208+
observeCompletionTokens: 0,
209+
observeInferenceTimeMs: 0,
210+
agentPromptTokens: 0,
211+
agentCompletionTokens: 0,
212+
agentInferenceTimeMs: 0,
213+
totalPromptTokens: 0,
214+
totalCompletionTokens: 0,
215+
totalInferenceTimeMs: 0,
216+
};
217+
218+
// Parse pages and their actions
219+
const pages = apiData.pages || [];
220+
for (const page of pages) {
221+
const actions = page.actions || [];
222+
for (const action of actions) {
223+
// Get method name and token usage
224+
const method = (action.method || "").toLowerCase();
225+
const tokenUsage = action.tokenUsage || {};
226+
227+
if (tokenUsage) {
228+
const inputTokens = tokenUsage.inputTokens || 0;
229+
const outputTokens = tokenUsage.outputTokens || 0;
230+
const timeMs = tokenUsage.timeMs || 0;
231+
232+
// Map method to metrics fields
233+
if (method === "act") {
234+
metrics.actPromptTokens += inputTokens;
235+
metrics.actCompletionTokens += outputTokens;
236+
metrics.actInferenceTimeMs += timeMs;
237+
} else if (method === "extract") {
238+
metrics.extractPromptTokens += inputTokens;
239+
metrics.extractCompletionTokens += outputTokens;
240+
metrics.extractInferenceTimeMs += timeMs;
241+
} else if (method === "observe") {
242+
metrics.observePromptTokens += inputTokens;
243+
metrics.observeCompletionTokens += outputTokens;
244+
metrics.observeInferenceTimeMs += timeMs;
245+
} else if (method === "agent") {
246+
metrics.agentPromptTokens += inputTokens;
247+
metrics.agentCompletionTokens += outputTokens;
248+
metrics.agentInferenceTimeMs += timeMs;
249+
}
250+
251+
// Always update totals for any method with token usage
252+
metrics.totalPromptTokens += inputTokens;
253+
metrics.totalCompletionTokens += outputTokens;
254+
metrics.totalInferenceTimeMs += timeMs;
255+
}
256+
}
257+
}
258+
259+
return metrics;
260+
}
261+
168262
private async execute<T>({
169263
method,
170264
args,

lib/index.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,21 @@ export class Stagehand {
458458
totalInferenceTimeMs: 0,
459459
};
460460

461-
public get metrics(): StagehandMetrics {
462-
return this.stagehandMetrics;
461+
public get metrics(): Promise<StagehandMetrics> {
462+
if (this.usingAPI && this.apiClient) {
463+
// Fetch metrics from the API
464+
return this.apiClient.getReplayMetrics().catch((error) => {
465+
this.logger({
466+
category: "metrics",
467+
message: `Failed to fetch metrics from API: ${error}`,
468+
level: 0,
469+
});
470+
// Fall back to local metrics on error
471+
return this.stagehandMetrics;
472+
});
473+
}
474+
// Return local metrics wrapped in a Promise for consistency
475+
return Promise.resolve(this.stagehandMetrics);
463476
}
464477

465478
public get isClosed(): boolean {

0 commit comments

Comments
 (0)