diff --git a/apps/open-swe/package.json b/apps/open-swe/package.json index 5b38bc4c..a9d79135 100644 --- a/apps/open-swe/package.json +++ b/apps/open-swe/package.json @@ -20,6 +20,7 @@ "test:int": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.config.js --testPathPattern=int.test.ts", "test:single": "NODE_OPTIONS=--experimental-vm-modules yarn run jest --config jest.config.js --testTimeout 100000", "eval:single": "NODE_OPTIONS=--experimental-vm-modules yarn run vitest --config ls.vitest.config.ts --run", + "get-trace-urls": "tsx scripts/get-trace-urls.ts", "postinstall": "turbo build" }, "dependencies": { diff --git a/apps/open-swe/scripts/get-trace-urls.ts b/apps/open-swe/scripts/get-trace-urls.ts new file mode 100644 index 00000000..482d2919 --- /dev/null +++ b/apps/open-swe/scripts/get-trace-urls.ts @@ -0,0 +1,110 @@ +/* eslint-disable no-console */ +import "dotenv/config"; +import { Client } from "@langchain/langgraph-sdk"; +import { ManagerGraphState } from "@open-swe/shared/open-swe/manager/types"; +import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types"; + +interface TraceUrls { + managerTraceUrl: string; + plannerTraceUrl: string; + programmerTraceUrl: string; +} + +/** + * Get the trace URLs for a given manager thread ID. + * @param managerThreadId The ID of the manager thread. + * @returns The trace URLs for the manager, planner, and programmer. + */ +async function getTraceUrls(managerThreadId: string): Promise { + const { + LANGGRAPH_API_URL: apiUrl, + LANGCHAIN_API_KEY: apiKey, + LANGSMITH_WORKSPACE_ID: orgId, + LANGSMITH_PROJECT_ID: projectId, + } = process.env; + + const missing = [apiUrl, apiKey, orgId, projectId] + .map((val, i) => + !val + ? [ + "LANGGRAPH_API_URL", + "LANGCHAIN_API_KEY", + "LANGSMITH_WORKSPACE_ID", + "LANGSMITH_PROJECT_ID", + ][i] + : null, + ) + .filter(Boolean); + + if (missing.length) { + throw new Error( + `Missing required environment variables: ${missing.join(", ")}`, + ); + } + + const client = new Client({ + apiUrl: apiUrl!, + apiKey: apiKey!, + defaultHeaders: { + "x-auth-scheme": "langsmith", + }, + }); + const constructUrl = (runId: string) => + `https://smith.langchain.com/o/${orgId}/projects/p/${projectId}/r/${runId}`; + + const [managerRuns, managerState] = await Promise.all([ + client.runs.list(managerThreadId), + client.threads.getState(managerThreadId), + ]); + + const managerRunId = managerRuns?.[0]?.run_id; + if (!managerRunId) { + throw new Error("Unable to find run ID for manager thread."); + } + + const result: TraceUrls = { + managerTraceUrl: constructUrl(managerRunId), + plannerTraceUrl: "", + programmerTraceUrl: "", + }; + + const plannerSession = managerState.values.plannerSession; + if (!plannerSession?.runId || !plannerSession?.threadId) { + return result; + } + + result.plannerTraceUrl = constructUrl(plannerSession.runId); + + const plannerState = await client.threads.getState( + plannerSession.threadId, + ); + const programmerSession = plannerState.values.programmerSession; + + if (programmerSession?.runId && programmerSession?.threadId) { + result.programmerTraceUrl = constructUrl(programmerSession.runId); + } + + return result; +} + +// Make script executable +if (import.meta.url === `file://${process.argv[1]}`) { + const managerThreadId = process.argv[2]; + + if (!managerThreadId) { + console.error("Usage: yarn get-trace-urls "); + process.exit(1); + } + + getTraceUrls(managerThreadId) + .then((urls) => { + console.log("\nšŸ”— Trace URLs:"); + console.log(`Manager: ${urls.managerTraceUrl}`); + console.log(`Planner: ${urls.plannerTraceUrl || "Not available"}`); + console.log(`Programmer: ${urls.programmerTraceUrl || "Not available"}`); + }) + .catch((error) => { + console.error("āŒ Error:", error.message); + process.exit(1); + }); +}