open-swe/apps/cli/src/trace_replay.ts
Palash Shah 3caa492ffd
feat: modify cli for deep agents + formatting (#792)
* feat: update cli for local

* feat: file diff formatting

* fix: udate diff format

* fix: show green/red diffs

* fix: modifications

* feat: str based replacement

* feat: update streaming to allow submissions

* feat: traces

* fix: wrap text

* feat: update cli

* fix: remove json

* fix: remove write fill diff

* fix: mods

* fix: updates to logging

* fix: add trace replay service

* feat: update logs

* fix: remove debugging

* fix: lint

* fix: unused vars

* fix: updates

* fix: formatting

* fix: update streaming value

* fix: replace constants

* feat: mods

* nit: change interrupt

* nit: remove question
2025-08-22 16:58:27 -04:00

100 lines
2.8 KiB
TypeScript

import { formatDisplayLog } from "./logger.js";
export interface TraceReplayCallbacks {
setLogs: (updater: (prev: string[]) => string[]) => void; // eslint-disable-line no-unused-vars
setLoadingLogs: (loading: boolean) => void; // eslint-disable-line no-unused-vars
}
export class TraceReplayService {
private callbacks: TraceReplayCallbacks;
private rawLogs: any[] = [];
constructor(callbacks: TraceReplayCallbacks) {
this.callbacks = callbacks;
}
/**
* Get formatted logs for display
*/
getFormattedLogs(): string[] {
const formattedLogs: string[] = [];
for (const chunk of this.rawLogs) {
if (typeof chunk === "string") {
const formatted = formatDisplayLog(chunk);
formattedLogs.push(...formatted);
} else if (chunk && chunk.data) {
// Process all chunks with data, not just "updates" events
const formatted = formatDisplayLog(chunk);
formattedLogs.push(...formatted);
}
}
return formattedLogs;
}
/**
* Update the display with formatted logs
*/
private updateDisplay() {
const formattedLogs = this.getFormattedLogs();
this.callbacks.setLogs(() => formattedLogs);
}
async replayFromTrace(langsmithRun: any, playbackSpeed: number = 500) {
this.rawLogs = [];
this.callbacks.setLogs(() => []);
this.callbacks.setLoadingLogs(true);
try {
const messages = langsmithRun.messages || [];
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
// Convert LangSmith message to the format expected by formatDisplayLog
const mockChunk = {
event: "updates",
data: {
agent: {
messages: [message],
},
},
};
this.rawLogs.push(mockChunk);
this.updateDisplay();
if (this.rawLogs.length === 1) {
this.callbacks.setLoadingLogs(false);
}
// Add delay between messages to simulate streaming
if (i < messages.length - 1) {
await new Promise((resolve) => setTimeout(resolve, playbackSpeed));
}
}
// Check for interrupt data in the trace and add it at the end
if (langsmithRun.__interrupt__ || langsmithRun.interrupt) {
const interruptData =
langsmithRun.__interrupt__ || langsmithRun.interrupt;
const interruptChunk = {
event: "interrupt",
data: {
__interrupt__: Array.isArray(interruptData)
? interruptData
: [interruptData],
},
};
this.rawLogs.push(interruptChunk);
this.updateDisplay();
}
} catch (err: any) {
this.rawLogs.push(`Error during replay: ${err.message}`);
this.updateDisplay();
this.callbacks.setLoadingLogs(false);
} finally {
this.callbacks.setLoadingLogs(false);
}
}
}