mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 05:43:14 +00:00
fix: Better daytona error handling (#429)
This commit is contained in:
parent
13329fe5c1
commit
d4ca73089c
4 changed files with 135 additions and 33 deletions
|
|
@ -22,7 +22,10 @@ import {
|
|||
import { Command } from "@langchain/langgraph";
|
||||
import { truncateOutput } from "../../../utils/truncate-outputs.js";
|
||||
import { getSandboxWithErrorHandling } from "../../../utils/sandbox.js";
|
||||
import { getCodebaseTree } from "../../../utils/tree.js";
|
||||
import {
|
||||
FAILED_TO_GENERATE_TREE_MESSAGE,
|
||||
getCodebaseTree,
|
||||
} from "../../../utils/tree.js";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { createInstallDependenciesTool } from "../../../tools/install-dependencies.js";
|
||||
import { createSearchTool } from "../../../tools/search.js";
|
||||
|
|
@ -180,6 +183,11 @@ export async function takeAction(
|
|||
]);
|
||||
|
||||
const codebaseTree = await getCodebaseTree();
|
||||
// If the codebase tree failed to generate, fallback to the previous codebase tree, or if that's not defined, use the failed to generate message.
|
||||
const codebaseTreeToReturn =
|
||||
codebaseTree === FAILED_TO_GENERATE_TREE_MESSAGE
|
||||
? (state.codebaseTree ?? codebaseTree)
|
||||
: codebaseTree;
|
||||
|
||||
// Prioritize wereDependenciesInstalled over dependenciesInstalled
|
||||
const dependenciesInstalledUpdate =
|
||||
|
|
@ -193,7 +201,7 @@ export async function takeAction(
|
|||
messages: toolCallResults,
|
||||
internalMessages: toolCallResults,
|
||||
...(branchName && { branchName }),
|
||||
codebaseTree,
|
||||
codebaseTree: codebaseTreeToReturn,
|
||||
sandboxSessionId: sandbox.id,
|
||||
...(dependenciesInstalledUpdate !== null && {
|
||||
dependenciesInstalled: dependenciesInstalledUpdate,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ import {
|
|||
configureGitUserInRepo,
|
||||
pullLatestChanges,
|
||||
} from "../../utils/github/git.js";
|
||||
import { getCodebaseTree } from "../../utils/tree.js";
|
||||
import {
|
||||
FAILED_TO_GENERATE_TREE_MESSAGE,
|
||||
getCodebaseTree,
|
||||
} from "../../utils/tree.js";
|
||||
import { DO_NOT_RENDER_ID_PREFIX } from "@open-swe/shared/constants";
|
||||
import {
|
||||
CustomNodeEvent,
|
||||
|
|
@ -171,7 +174,15 @@ export async function initializeSandbox(
|
|||
emitStepEvent(baseGenerateCodebaseTreeAction, "pending");
|
||||
try {
|
||||
const codebaseTree = await getCodebaseTree(existingSandbox.id);
|
||||
emitStepEvent(baseGenerateCodebaseTreeAction, "success");
|
||||
if (codebaseTree === FAILED_TO_GENERATE_TREE_MESSAGE) {
|
||||
emitStepEvent(
|
||||
baseGenerateCodebaseTreeAction,
|
||||
"error",
|
||||
FAILED_TO_GENERATE_TREE_MESSAGE,
|
||||
);
|
||||
} else {
|
||||
emitStepEvent(baseGenerateCodebaseTreeAction, "success");
|
||||
}
|
||||
|
||||
return {
|
||||
sandboxSessionId: existingSandbox.id,
|
||||
|
|
@ -183,9 +194,14 @@ export async function initializeSandbox(
|
|||
emitStepEvent(
|
||||
baseGenerateCodebaseTreeAction,
|
||||
"error",
|
||||
"Failed to generate codebase tree. Please try again later.",
|
||||
FAILED_TO_GENERATE_TREE_MESSAGE,
|
||||
);
|
||||
throw new Error("Failed to generate codebase tree.");
|
||||
return {
|
||||
sandboxSessionId: existingSandbox.id,
|
||||
codebaseTree: FAILED_TO_GENERATE_TREE_MESSAGE,
|
||||
messages: createEventsMessage(),
|
||||
customRules: await getCustomRules(existingSandbox, absoluteRepoDir),
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
emitStepEvent(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { DEFAULT_SANDBOX_CREATE_PARAMS } from "../constants.js";
|
|||
import { getGitHubTokensFromConfig } from "./github-tokens.js";
|
||||
import { cloneRepo, configureGitUserInRepo } from "./github/git.js";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { getCodebaseTree } from "./tree.js";
|
||||
import { FAILED_TO_GENERATE_TREE_MESSAGE, getCodebaseTree } from "./tree.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "Sandbox");
|
||||
|
||||
|
|
@ -65,6 +65,28 @@ export async function deleteSandbox(
|
|||
}
|
||||
}
|
||||
|
||||
async function createSandbox(attempt: number): Promise<Sandbox | null> {
|
||||
try {
|
||||
return await daytonaClient().create(DEFAULT_SANDBOX_CREATE_PARAMS, {
|
||||
timeout: 100, // 100s timeout on creation.
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error("Failed to create sandbox", {
|
||||
attempt,
|
||||
...(e instanceof Error
|
||||
? {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}
|
||||
: {
|
||||
error: e,
|
||||
}),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSandboxWithErrorHandling(
|
||||
sandboxSessionId: string | undefined,
|
||||
targetRepository: TargetRepository,
|
||||
|
|
@ -112,7 +134,19 @@ export async function getSandboxWithErrorHandling(
|
|||
error,
|
||||
});
|
||||
|
||||
const sandbox = await daytonaClient().create(DEFAULT_SANDBOX_CREATE_PARAMS);
|
||||
let sandbox: Sandbox | null = null;
|
||||
let numSandboxCreateAttempts = 0;
|
||||
while (!sandbox && numSandboxCreateAttempts < 3) {
|
||||
sandbox = await createSandbox(numSandboxCreateAttempts);
|
||||
if (!sandbox) {
|
||||
numSandboxCreateAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sandbox) {
|
||||
throw new Error("Failed to create sandbox after 3 attempts");
|
||||
}
|
||||
|
||||
const { githubInstallationToken } = getGitHubTokensFromConfig(config);
|
||||
|
||||
// Clone repository
|
||||
|
|
@ -130,14 +164,16 @@ export async function getSandboxWithErrorHandling(
|
|||
});
|
||||
|
||||
// Get codebase tree
|
||||
const codebaseTree = await getCodebaseTree(sandbox.id);
|
||||
const codebaseTree = await getCodebaseTree(sandbox.id, targetRepository);
|
||||
const codebaseTreeToReturn =
|
||||
codebaseTree === FAILED_TO_GENERATE_TREE_MESSAGE ? null : codebaseTree;
|
||||
|
||||
logger.info("Sandbox created successfully", {
|
||||
sandboxId: sandbox.id,
|
||||
});
|
||||
return {
|
||||
sandbox,
|
||||
codebaseTree,
|
||||
codebaseTree: codebaseTreeToReturn,
|
||||
dependenciesInstalled: false,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,78 @@
|
|||
import { getCurrentTaskInput } from "@langchain/langgraph";
|
||||
import { GraphState } from "@open-swe/shared/open-swe/types";
|
||||
import { GraphState, TargetRepository } from "@open-swe/shared/open-swe/types";
|
||||
import { daytonaClient } from "./sandbox.js";
|
||||
import { createLogger, LogLevel } from "./logger.js";
|
||||
import path from "node:path";
|
||||
import { SANDBOX_ROOT_DIR, TIMEOUT_SEC } from "@open-swe/shared/constants";
|
||||
import { getSandboxErrorFields } from "./sandbox-error-fields.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "Tree");
|
||||
|
||||
export async function getCodebaseTree(sandboxSessionId_?: string) {
|
||||
const command = `git ls-files | tree --fromfile -L 3`;
|
||||
const state = getCurrentTaskInput<GraphState>();
|
||||
const sandboxSessionId = sandboxSessionId_ || state.sandboxSessionId;
|
||||
if (!sandboxSessionId) {
|
||||
logger.error("Failed to generate tree: No sandbox session ID provided");
|
||||
throw new Error("Failed generate tree: No sandbox session ID provided");
|
||||
}
|
||||
const sandbox = await daytonaClient().get(sandboxSessionId);
|
||||
const repoDir = path.join(SANDBOX_ROOT_DIR, state.targetRepository.repo);
|
||||
const response = await sandbox.process.executeCommand(
|
||||
command,
|
||||
repoDir,
|
||||
undefined,
|
||||
TIMEOUT_SEC,
|
||||
);
|
||||
export const FAILED_TO_GENERATE_TREE_MESSAGE =
|
||||
"Failed to generate tree. Please try again.";
|
||||
|
||||
if (response.exitCode !== 0) {
|
||||
export async function getCodebaseTree(
|
||||
sandboxSessionId_?: string,
|
||||
targetRepository_?: TargetRepository,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const command = `git ls-files | tree --fromfile -L 3`;
|
||||
let sandboxSessionId = sandboxSessionId_;
|
||||
let targetRepository = targetRepository_;
|
||||
|
||||
// If sandbox session ID is not provided, try to get it from the current state.
|
||||
if (!sandboxSessionId || !targetRepository) {
|
||||
try {
|
||||
const state = getCurrentTaskInput<GraphState>();
|
||||
// Prefer the provided sandbox session ID and target repository. Fallback to state if defined.
|
||||
sandboxSessionId = sandboxSessionId ?? state.sandboxSessionId;
|
||||
targetRepository = targetRepository ?? state.targetRepository;
|
||||
} catch {
|
||||
// not executed in a LangGraph instance. continue.
|
||||
}
|
||||
}
|
||||
|
||||
if (!sandboxSessionId) {
|
||||
logger.error("Failed to generate tree: No sandbox session ID provided");
|
||||
throw new Error("Failed generate tree: No sandbox session ID provided");
|
||||
}
|
||||
if (!targetRepository) {
|
||||
logger.error("Failed to generate tree: No target repository provided");
|
||||
throw new Error("Failed generate tree: No target repository provided");
|
||||
}
|
||||
|
||||
const sandbox = await daytonaClient().get(sandboxSessionId);
|
||||
const repoDir = path.join(SANDBOX_ROOT_DIR, targetRepository.repo);
|
||||
const response = await sandbox.process.executeCommand(
|
||||
command,
|
||||
repoDir,
|
||||
undefined,
|
||||
TIMEOUT_SEC,
|
||||
);
|
||||
|
||||
if (response.exitCode !== 0) {
|
||||
logger.error("Failed to generate tree", {
|
||||
exitCode: response.exitCode,
|
||||
result: response.result ?? response.artifacts?.stdout,
|
||||
});
|
||||
throw new Error(
|
||||
`Failed to generate tree: ${response.result ?? response.artifacts?.stdout}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.result;
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
logger.error("Failed to generate tree", {
|
||||
error: response.result,
|
||||
error_result: response,
|
||||
...(errorFields ? { errorFields } : {}),
|
||||
...(e instanceof Error
|
||||
? {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
throw new Error(`Failed to generate tree: ${response.result}`);
|
||||
return FAILED_TO_GENERATE_TREE_MESSAGE;
|
||||
}
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue