mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 13:53:15 +00:00
fix: Implement logger (#17)
* fix: Docs and scripts * cr * fix: better err handling * cr * cr * fix writing * fix types * fix: Implement logger
This commit is contained in:
parent
04b14cae32
commit
20c483f327
11 changed files with 299 additions and 109 deletions
|
|
@ -3,6 +3,9 @@ import { loadModel, Task } from "../utils/load-model.js";
|
|||
import { shellTool, applyPatchTool } from "../tools/index.js";
|
||||
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
||||
import { pauseSandbox } from "../utils/sandbox.js";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GenerateMessageNode");
|
||||
|
||||
const systemPrompt = `You are operating as a terminal-based agentic coding assistant built by LangChain. It wraps LLM models to enable natural language interaction with a local codebase. You are expected to be precise, safe, and helpful.
|
||||
|
||||
|
|
@ -81,10 +84,14 @@ export async function generateAction(
|
|||
// No tool calls means the graph is going to end. Pause the sandbox.
|
||||
let newSandboxSessionId: string | undefined;
|
||||
if (!hasToolCalls && state.sandboxSessionId) {
|
||||
console.log("No tool calls found. Pausing sandbox...");
|
||||
logger.info("No tool calls found. Pausing sandbox...");
|
||||
newSandboxSessionId = await pauseSandbox(state.sandboxSessionId);
|
||||
}
|
||||
|
||||
logger.info("Generated action", {
|
||||
name: response.tool_calls?.[0].name,
|
||||
args: response.tool_calls?.[0].args,
|
||||
});
|
||||
return {
|
||||
messages: [response],
|
||||
...(newSandboxSessionId && { sandboxSessionId: newSandboxSessionId }),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Sandbox } from "@e2b/code-interpreter";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
import {
|
||||
GraphState,
|
||||
GraphConfig,
|
||||
|
|
@ -14,6 +15,8 @@ import {
|
|||
} from "../utils/git/index.js";
|
||||
import { getSandboxErrorFields } from "../utils/sandbox-error-fields.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "Initialize");
|
||||
|
||||
const SANDBOX_TEMPLATE_ID = "eh0860emqx28qyxmbctu";
|
||||
|
||||
async function cloneRepo(sandbox: Sandbox, targetRepository: TargetRepository) {
|
||||
|
|
@ -32,7 +35,7 @@ async function cloneRepo(sandbox: Sandbox, targetRepository: TargetRepository) {
|
|||
gitCloneCommand.push(repoUrlWithToken);
|
||||
}
|
||||
|
||||
console.log("Cloning repository...", {
|
||||
logger.info("Cloning repository", {
|
||||
command: gitCloneCommand.join(" "),
|
||||
});
|
||||
return await sandbox.commands.run(
|
||||
|
|
@ -41,7 +44,7 @@ async function cloneRepo(sandbox: Sandbox, targetRepository: TargetRepository) {
|
|||
);
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
console.error("Failed to clone repository", errorFields ?? e);
|
||||
logger.error("Failed to clone repository", errorFields ?? e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +66,7 @@ export async function initialize(
|
|||
|
||||
if (sandboxSessionId) {
|
||||
try {
|
||||
console.log("Sandbox session ID exists. Resuming...", {
|
||||
logger.info("Sandbox session ID exists. Resuming", {
|
||||
sandboxSessionId,
|
||||
});
|
||||
// Resume the sandbox if the session ID is in the config.
|
||||
|
|
@ -76,7 +79,7 @@ export async function initialize(
|
|||
};
|
||||
} catch (e) {
|
||||
// Error thrown, log it and continue. Will create a new sandbox session since the resumption failed.
|
||||
console.error("Failed to get sandbox session.", e);
|
||||
logger.error("Failed to get sandbox session", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +91,7 @@ export async function initialize(
|
|||
);
|
||||
}
|
||||
|
||||
console.log("Creating sandbox...");
|
||||
logger.info("Creating sandbox...");
|
||||
const sandbox = await Sandbox.create(
|
||||
SANDBOX_TEMPLATE_ID,
|
||||
TIMEOUT_EXTENSION_OPT,
|
||||
|
|
@ -97,16 +100,16 @@ export async function initialize(
|
|||
const res = await cloneRepo(sandbox, target_repository);
|
||||
if (res.error) {
|
||||
// TODO: This should probably be an interrupt.
|
||||
console.error("Failed to clone repository.", res.error);
|
||||
logger.error("Failed to clone repository", res.error);
|
||||
throw new Error(`Failed to clone repository.\n${res.error}`);
|
||||
}
|
||||
console.log("Repository cloned successfully.");
|
||||
logger.info("Repository cloned successfully.");
|
||||
|
||||
const absoluteRepoDir = getRepoAbsolutePath(config);
|
||||
|
||||
console.log(`Configuring git user for repository at "${absoluteRepoDir}"...`);
|
||||
logger.info(`Configuring git user for repository at "${absoluteRepoDir}"...`);
|
||||
await configureGitUserInRepo(absoluteRepoDir, sandbox);
|
||||
console.log("Git user configured successfully.");
|
||||
logger.info("Git user configured successfully.");
|
||||
|
||||
const checkoutBranchRes = await checkoutBranch(
|
||||
absoluteRepoDir,
|
||||
|
|
@ -116,7 +119,7 @@ export async function initialize(
|
|||
|
||||
if (!checkoutBranchRes) {
|
||||
// TODO: This should probably be an interrupt.
|
||||
console.error("\nFailed to checkout branch.");
|
||||
logger.error("Failed to checkout branch.");
|
||||
throw new Error("Failed to checkout branch");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { z } from "zod";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
import { GraphConfig, GraphState, GraphUpdate, PlanItem } from "../types.js";
|
||||
import { loadModel, Task } from "../utils/load-model.js";
|
||||
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ProgressPlanStep");
|
||||
|
||||
const systemPrompt = `You are operating as a terminal-based agentic coding assistant built by LangChain. It wraps LLM models to enable natural language interaction with a local codebase. You are expected to be precise, safe, and helpful.
|
||||
|
||||
In your workflow, you generate a plan, then act on said plan. It may take many actions to complete a single step, or a single action to complete the step.
|
||||
|
|
@ -65,7 +68,7 @@ export async function progressPlanStep(
|
|||
const remainingTask = state.plan.find((p) => !p.completed);
|
||||
if (!remainingTask) {
|
||||
// No remaining tasks, end the process
|
||||
console.log(
|
||||
logger.info(
|
||||
"Found no remaining tasks in the plan during the check plan step.",
|
||||
);
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { isAIMessage, ToolMessage } from "@langchain/core/messages";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
import { applyPatchTool, shellTool } from "../tools/index.js";
|
||||
import { GraphState, GraphConfig, GraphUpdate } from "../types.js";
|
||||
import {
|
||||
|
|
@ -8,6 +9,8 @@ import {
|
|||
} from "../utils/git/index.js";
|
||||
import { Sandbox } from "@e2b/code-interpreter";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "TakeAction");
|
||||
|
||||
export async function takeAction(
|
||||
state: GraphState,
|
||||
config: GraphConfig,
|
||||
|
|
@ -45,7 +48,11 @@ export async function takeAction(
|
|||
// @ts-expect-error tool.invoke types are weird here...
|
||||
result = await tool.invoke(toolCall.args);
|
||||
} catch (e) {
|
||||
console.error("\nFailed to call tool", e);
|
||||
logger.error("Failed to call tool", {
|
||||
...(e instanceof Error
|
||||
? { name: e.name, message: e.message, stack: e.stack }
|
||||
: { error: e }),
|
||||
});
|
||||
const errMessage = e instanceof Error ? e.message : "Unknown error";
|
||||
result = `FAILED TO CALL TOOL: "${toolCall.name}"\n\nError: ${errMessage}`;
|
||||
}
|
||||
|
|
@ -66,7 +73,7 @@ export async function takeAction(
|
|||
|
||||
let branchName: string | undefined = state.branchName;
|
||||
if (changedFiles.length > 0) {
|
||||
console.log(`\nHas ${changedFiles.length} changed files. Committing...`, {
|
||||
logger.info(`Has ${changedFiles.length} changed files. Committing.`, {
|
||||
changedFiles,
|
||||
});
|
||||
branchName = await checkoutBranchAndCommit(config, sandbox, {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import { Sandbox } from "@e2b/code-interpreter";
|
|||
import { readFile, writeFile } from "../utils/read-write.js";
|
||||
import { getCurrentTaskInput } from "@langchain/langgraph";
|
||||
import { fixGitPatch } from "../utils/diff.js";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ApplyPatchTool");
|
||||
|
||||
const applyPatchToolSchema = z.object({
|
||||
diff: z.string().describe("The diff to apply. Use a standard diff format."),
|
||||
|
|
@ -17,7 +20,7 @@ export const applyPatchTool = tool(
|
|||
const state = getCurrentTaskInput<GraphState>();
|
||||
const { sandboxSessionId } = state;
|
||||
if (!sandboxSessionId) {
|
||||
console.error("FAILED TO RUN COMMAND: No sandbox session ID provided", {
|
||||
logger.error("FAILED TO RUN COMMAND: No sandbox session ID provided", {
|
||||
input,
|
||||
});
|
||||
throw new Error("FAILED TO RUN COMMAND: No sandbox session ID provided");
|
||||
|
|
@ -32,37 +35,46 @@ export const applyPatchTool = tool(
|
|||
file_path,
|
||||
);
|
||||
if (!readFileSuccess) {
|
||||
logger.error("Failed to read file", readFileOutput);
|
||||
return readFileOutput;
|
||||
}
|
||||
|
||||
console.log(`\nApplying patch to file ${file_path}\n`);
|
||||
console.log("\nreadFileOutput\n", readFileOutput);
|
||||
console.log("\ndiff\n", diff);
|
||||
|
||||
let patchedContent: string | false;
|
||||
try {
|
||||
logger.info(`Applying patch to file ${file_path}`);
|
||||
const fixedDiff = fixGitPatch(diff, {
|
||||
[file_path]: readFileOutput,
|
||||
});
|
||||
console.log("\n\nfixedDiff\n\n", fixedDiff);
|
||||
patchedContent = applyPatch(readFileOutput, fixedDiff);
|
||||
} catch (e) {
|
||||
console.error("Failed to apply patch", e);
|
||||
logger.error("Failed to apply patch", {
|
||||
...(e instanceof Error
|
||||
? { name: e.name, message: e.message, stack: e.stack }
|
||||
: { error: e }),
|
||||
});
|
||||
const errMessage = e instanceof Error ? e.message : "Unknown error";
|
||||
return `FAILED TO APPLY PATCH: The diff could not be applied to file '${file_path}'.\n\nError: ${errMessage}`;
|
||||
}
|
||||
|
||||
if (patchedContent === false) {
|
||||
logger.error(
|
||||
`FAILED TO APPLY PATCH: The diff could not be applied to file '${file_path}'. This may be due to an invalid diff format or conflicting changes with the file's current content. Original content length: ${readFileOutput.length}, Diff: ${diff.substring(0, 100)}...`,
|
||||
);
|
||||
return `FAILED TO APPLY PATCH: The diff could not be applied to file '${file_path}'. This may be due to an invalid diff format or conflicting changes with the file's current content. Original content length: ${readFileOutput.length}, Diff: ${diff.substring(0, 100)}...`;
|
||||
}
|
||||
|
||||
// TODO: Should we be committing every time we apply a diff?
|
||||
const { success: writeFileSuccess, output: writeFileOutput } =
|
||||
await writeFile(sandbox, file_path, patchedContent);
|
||||
if (!writeFileSuccess) {
|
||||
logger.error("Failed to write file", {
|
||||
writeFileOutput,
|
||||
});
|
||||
return writeFileOutput;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Successfully applied diff to \`${file_path}\` and saved changes.`,
|
||||
);
|
||||
return `Successfully applied diff to \`${file_path}\` and saved changes.`;
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { Sandbox } from "@e2b/code-interpreter";
|
|||
import { GraphState } from "../types.js";
|
||||
import { getCurrentTaskInput } from "@langchain/langgraph";
|
||||
import { TIMEOUT_MS } from "../constants.js";
|
||||
import { getSandboxErrorFields } from "../utils/sandbox-error-fields.js";
|
||||
|
||||
const DEFAULT_COMMAND_TIMEOUT = 120_000; // 2 minutes
|
||||
const DEFAULT_COMMAND_TIMEOUT = 60_000; // 1 minute
|
||||
|
||||
const shellToolSchema = z.object({
|
||||
command: z.array(z.string()).describe("The command to run"),
|
||||
|
|
@ -55,26 +56,28 @@ export const shellTool = tool(
|
|||
}
|
||||
|
||||
return result.stdout;
|
||||
} catch (e: any) {
|
||||
if (typeof e === "object" && "result" in e && e.result) {
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
if (errorFields) {
|
||||
console.error("Failed to run command", {
|
||||
error: e.message,
|
||||
error_result: e.result,
|
||||
input,
|
||||
error: errorFields,
|
||||
});
|
||||
return (
|
||||
"FAILED TO RUN COMMAND: " +
|
||||
e.message +
|
||||
"\n" +
|
||||
JSON.stringify(e.result, null, 2)
|
||||
);
|
||||
return `Command failed. Exit code: ${errorFields.exitCode}\nError: ${errorFields.error}\nStderr:\n${errorFields.stderr}\nStdout:\n${errorFields.stdout}`;
|
||||
}
|
||||
|
||||
console.error("Failed to run command: " + e.message, {
|
||||
error: e,
|
||||
input,
|
||||
});
|
||||
throw new Error("FAILED TO RUN COMMAND: " + e.message);
|
||||
console.error(
|
||||
"Failed to run command: " +
|
||||
(e instanceof Error ? e.message : "Unknown error"),
|
||||
{
|
||||
error: e,
|
||||
input,
|
||||
},
|
||||
);
|
||||
throw new Error(
|
||||
"FAILED TO RUN COMMAND: " +
|
||||
(e instanceof Error ? e.message : "Unknown error"),
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import { createLogger, LogLevel } from "./logger.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "DiffUtil");
|
||||
|
||||
interface Hunk {
|
||||
oldStart: number;
|
||||
oldLines: number;
|
||||
|
|
@ -386,8 +390,12 @@ export function fixGitPatch(
|
|||
}
|
||||
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
console.error("Error fixing patch:", error);
|
||||
} catch (e) {
|
||||
logger.error(`Error fixing patch:`, {
|
||||
...(e instanceof Error
|
||||
? { name: e.name, message: e.message, stack: e.stack }
|
||||
: { error: e }),
|
||||
});
|
||||
return patchString;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { CommandResult, Sandbox } from "@e2b/code-interpreter";
|
||||
import { createLogger, LogLevel } from "../logger.js";
|
||||
import { GraphConfig } from "../../types.js";
|
||||
import { TIMEOUT_MS } from "../../constants.js";
|
||||
import { getSandboxErrorFields } from "../sandbox-error-fields.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GitUtil");
|
||||
|
||||
export function getRepoAbsolutePath(config: GraphConfig): string {
|
||||
const repoName = config.configurable?.target_repository.repo;
|
||||
if (!repoName) {
|
||||
|
|
@ -26,9 +29,7 @@ export async function checkoutBranch(
|
|||
branchName: string,
|
||||
sandbox: Sandbox,
|
||||
): Promise<CommandResult | false> {
|
||||
console.log("\nChecking out branch...", {
|
||||
branchName,
|
||||
});
|
||||
logger.info(`Checking out branch '${branchName}'...`);
|
||||
|
||||
try {
|
||||
const getCurrentBranchOutput = await sandbox.commands.run(
|
||||
|
|
@ -38,11 +39,13 @@ export async function checkoutBranch(
|
|||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
|
||||
if (getCurrentBranchOutput.exitCode !== 0) {
|
||||
console.error("Failed to get current branch", getCurrentBranchOutput);
|
||||
logger.error(`Failed to get current branch`, {
|
||||
getCurrentBranchOutput,
|
||||
});
|
||||
} else {
|
||||
const currentBranch = getCurrentBranchOutput.stdout.trim();
|
||||
if (currentBranch === branchName) {
|
||||
console.log(`\nAlready on branch '${branchName}'. No checkout needed.`);
|
||||
logger.info(`Already on branch '${branchName}'. No checkout needed.`);
|
||||
return {
|
||||
stdout: `Already on branch ${branchName}`,
|
||||
stderr: "",
|
||||
|
|
@ -52,15 +55,22 @@ export async function checkoutBranch(
|
|||
}
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
console.error("Failed to get current branch", errorFields ?? e);
|
||||
logger.error(`Failed to get current branch`, {
|
||||
...(errorFields && { errorFields }),
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
let checkoutCommand: string;
|
||||
try {
|
||||
console.log("\nChecking if branch exists...", {
|
||||
command: `git rev-parse --verify --quiet "refs/heads/${branchName}"`,
|
||||
});
|
||||
logger.info(
|
||||
`Checking if branch 'refs/heads/${branchName}' exists using 'git rev-parse --verify --quiet'`,
|
||||
);
|
||||
// Check if branch exists using git rev-parse for robustness
|
||||
const checkBranchExistsOutput = await sandbox.commands.run(
|
||||
`git rev-parse --verify --quiet "refs/heads/${branchName}"`,
|
||||
|
|
@ -85,7 +95,13 @@ export async function checkoutBranch(
|
|||
) {
|
||||
checkoutCommand = `git checkout -b "${branchName}"`;
|
||||
} else {
|
||||
console.error("\nError checking if branch exists", e);
|
||||
logger.error(`Error checking if branch exists`, {
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -96,19 +112,27 @@ export async function checkoutBranch(
|
|||
});
|
||||
|
||||
if (gitCheckoutOutput.exitCode !== 0) {
|
||||
console.error("\nFailed to checkout branch", gitCheckoutOutput);
|
||||
logger.error(`Failed to checkout branch`, {
|
||||
gitCheckoutOutput,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log("\nChecked out branch successfully.", {
|
||||
branchName,
|
||||
gitCheckoutOutput: gitCheckoutOutput.stdout,
|
||||
logger.info(`Checked out branch '${branchName}' successfully.`, {
|
||||
gitCheckoutOutput,
|
||||
});
|
||||
|
||||
return gitCheckoutOutput;
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
console.error("Error checking out branch", errorFields ?? e);
|
||||
logger.error(`Error checking out branch`, {
|
||||
...(errorFields && { errorFields }),
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +150,7 @@ async function getGitUserDetailsFromGitHub(): Promise<{
|
|||
}> {
|
||||
const githubToken = process.env.GITHUB_PAT;
|
||||
if (!githubToken) {
|
||||
console.warn(
|
||||
logger.warn(
|
||||
"GITHUB_PAT environment variable is not set. Cannot fetch user details from GitHub.",
|
||||
);
|
||||
return {};
|
||||
|
|
@ -141,9 +165,9 @@ async function getGitUserDetailsFromGitHub(): Promise<{
|
|||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(
|
||||
`Failed to fetch GitHub user info: ${response.status} ${response.statusText}. Response: ${await response.text()}`,
|
||||
);
|
||||
logger.error(`Failed to fetch GitHub user info`, {
|
||||
response,
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -161,14 +185,20 @@ async function getGitUserDetailsFromGitHub(): Promise<{
|
|||
const finalUserEmail = fetchedUserEmail || undefined;
|
||||
|
||||
if (!finalUserName) {
|
||||
console.warn("Could not determine GitHub username from API response.");
|
||||
logger.warn("Could not determine GitHub username from API response.");
|
||||
}
|
||||
if (!finalUserEmail) {
|
||||
console.warn("Could not determine GitHub user email from API response.");
|
||||
logger.warn("Could not determine GitHub user email from API response.");
|
||||
}
|
||||
return { userName: finalUserName, userEmail: finalUserEmail };
|
||||
} catch (e) {
|
||||
console.error("Error fetching GitHub user info:", e);
|
||||
logger.error(`Error fetching GitHub user info`, {
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
@ -197,10 +227,13 @@ export async function configureGitUserInRepo(
|
|||
needsGitConfig = true;
|
||||
}
|
||||
} catch (checkError) {
|
||||
console.warn(
|
||||
"Could not check existing git config, will attempt to set it:",
|
||||
checkError,
|
||||
);
|
||||
logger.warn(`Could not check existing git config, will attempt to set it`, {
|
||||
...(checkError instanceof Error && {
|
||||
name: checkError.name,
|
||||
message: checkError.message,
|
||||
stack: checkError.stack,
|
||||
}),
|
||||
});
|
||||
needsGitConfig = true;
|
||||
}
|
||||
|
||||
|
|
@ -214,14 +247,11 @@ export async function configureGitUserInRepo(
|
|||
);
|
||||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
if (configUserNameOutput.exitCode !== 0) {
|
||||
console.error(
|
||||
"Failed to set git user.name:",
|
||||
configUserNameOutput.stderr || configUserNameOutput.stdout,
|
||||
);
|
||||
} else {
|
||||
console.log("\nSet git user.name successfully.", {
|
||||
userName,
|
||||
logger.error(`Failed to set git user.name`, {
|
||||
configUserNameOutput,
|
||||
});
|
||||
} else {
|
||||
logger.info(`Set git user.name to '${userName}' successfully.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,18 +262,15 @@ export async function configureGitUserInRepo(
|
|||
);
|
||||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
if (configUserEmailOutput.exitCode !== 0) {
|
||||
console.error(
|
||||
"Failed to set git user.email:",
|
||||
configUserEmailOutput.stderr || configUserEmailOutput.stdout,
|
||||
);
|
||||
} else {
|
||||
console.log("\nSet git user.email successfully.", {
|
||||
userEmail,
|
||||
logger.error(`Failed to set git user.email`, {
|
||||
configUserEmailOutput,
|
||||
});
|
||||
} else {
|
||||
logger.info(`Set git user.email to '${userEmail}' successfully.`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
logger.info(
|
||||
"Git user.name and user.email are already configured in this repository.",
|
||||
);
|
||||
}
|
||||
|
|
@ -262,14 +289,21 @@ export async function commitAll(
|
|||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
|
||||
if (gitAddOutput.exitCode !== 0) {
|
||||
console.error(
|
||||
"Failed to commit all changes to git repository",
|
||||
logger.error(`Failed to commit all changes to git repository`, {
|
||||
gitAddOutput,
|
||||
);
|
||||
});
|
||||
}
|
||||
return gitAddOutput;
|
||||
} catch (e) {
|
||||
console.error("Failed to commit all changes to git repository", e);
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
logger.error(`Failed to commit all changes to git repository`, {
|
||||
...(errorFields && { errorFields }),
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -295,13 +329,23 @@ export async function commitAllAndPush(
|
|||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
|
||||
if (gitPushOutput.exitCode !== 0) {
|
||||
console.error("Failed to push changes to git repository", gitPushOutput);
|
||||
logger.error(`Failed to push changes to git repository`, {
|
||||
gitPushOutput,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return gitPushOutput;
|
||||
} catch (e) {
|
||||
console.error("Failed to commit all and push changes to git repository", e);
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
logger.error(`Failed to commit all and push changes to git repository`, {
|
||||
...(errorFields && { errorFields }),
|
||||
...(e instanceof Error && {
|
||||
name: e.name,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -315,7 +359,9 @@ export async function getChangedFilesStatus(
|
|||
});
|
||||
|
||||
if (gitStatusOutput.exitCode !== 0) {
|
||||
console.error("Failed to get changed files status", gitStatusOutput);
|
||||
logger.error(`Failed to get changed files status`, {
|
||||
gitStatusOutput,
|
||||
});
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -332,15 +378,15 @@ export async function checkoutBranchAndCommit(
|
|||
branchName?: string;
|
||||
},
|
||||
): Promise<string> {
|
||||
console.log("\nChecking out branch and committing changes...");
|
||||
logger.info("Checking out branch and committing changes...");
|
||||
const absoluteRepoDir = getRepoAbsolutePath(config);
|
||||
const branchName = options?.branchName || getBranchName(config);
|
||||
|
||||
await checkoutBranch(absoluteRepoDir, branchName, sandbox);
|
||||
|
||||
console.log(`Committing changes to branch ${branchName}`);
|
||||
logger.info(`Committing changes to branch ${branchName}`);
|
||||
await commitAllAndPush(absoluteRepoDir, "Apply patch", sandbox);
|
||||
console.log("Successfully checked out & committed changes.\n");
|
||||
logger.info("Successfully checked out & committed changes.");
|
||||
|
||||
return branchName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,11 +39,9 @@ export async function loadModel(config: GraphConfig, task: Task) {
|
|||
|
||||
let thinkingModel = false;
|
||||
if (modelNameParts[0] === "extended-thinking") {
|
||||
console.log("THINKING ENABLED", modelNameParts);
|
||||
// Using a thinking model. Remove it from the model name.
|
||||
modelNameParts.shift();
|
||||
thinkingModel = true;
|
||||
console.log("THINKING ENABLED", modelNameParts);
|
||||
}
|
||||
|
||||
const modelName = modelNameParts.join(":");
|
||||
|
|
|
|||
94
src/utils/logger.ts
Normal file
94
src/utils/logger.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
export enum LogLevel {
|
||||
DEBUG = "debug",
|
||||
INFO = "info",
|
||||
WARN = "warn",
|
||||
ERROR = "error",
|
||||
}
|
||||
|
||||
// ANSI escape codes
|
||||
const RESET = "\x1b[0m";
|
||||
const BOLD = "\x1b[1m";
|
||||
|
||||
// Define a list of colors (foreground)
|
||||
const COLORS = [
|
||||
"\x1b[31m", // Red
|
||||
"\x1b[32m", // Green
|
||||
"\x1b[33m", // Yellow
|
||||
"\x1b[34m", // Blue
|
||||
"\x1b[35m", // Magenta
|
||||
"\x1b[36m", // Cyan
|
||||
"\x1b[91m", // Bright Red
|
||||
"\x1b[92m", // Bright Green
|
||||
"\x1b[93m", // Bright Yellow
|
||||
"\x1b[94m", // Bright Blue
|
||||
"\x1b[95m", // Bright Magenta
|
||||
"\x1b[96m", // Bright Cyan
|
||||
];
|
||||
|
||||
// Simple hashing function to get a positive integer
|
||||
function simpleHash(str: string): number {
|
||||
let hash = 0;
|
||||
if (str.length === 0) {
|
||||
return hash;
|
||||
}
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash); // Ensure positive for modulo index
|
||||
}
|
||||
|
||||
export function createLogger(level: LogLevel, prefix: string) {
|
||||
const hash = simpleHash(prefix);
|
||||
const color = COLORS[hash % COLORS.length];
|
||||
const styledPrefix = `${BOLD}${color}[${prefix}]${RESET}`; // Apply bold and color
|
||||
|
||||
return {
|
||||
debug: (message: string, data?: any) => {
|
||||
if (level === LogLevel.DEBUG) {
|
||||
if (data !== undefined) {
|
||||
console.log(`${styledPrefix} ${message}`, data);
|
||||
} else {
|
||||
console.log(`${styledPrefix} ${message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
info: (message: string, data?: any) => {
|
||||
if (level === LogLevel.INFO || level === LogLevel.DEBUG) {
|
||||
if (data !== undefined) {
|
||||
console.log(`${styledPrefix} ${message}`, data);
|
||||
} else {
|
||||
console.log(`${styledPrefix} ${message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
warn: (message: string, data?: any) => {
|
||||
if (
|
||||
level === LogLevel.WARN ||
|
||||
level === LogLevel.INFO ||
|
||||
level === LogLevel.DEBUG
|
||||
) {
|
||||
if (data !== undefined) {
|
||||
console.log(`${styledPrefix} ${message}`, data);
|
||||
} else {
|
||||
console.log(`${styledPrefix} ${message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (message: string, data?: any) => {
|
||||
if (
|
||||
level === LogLevel.ERROR ||
|
||||
level === LogLevel.WARN ||
|
||||
level === LogLevel.INFO ||
|
||||
level === LogLevel.DEBUG
|
||||
) {
|
||||
if (data !== undefined) {
|
||||
console.log(`${styledPrefix} ${message}`, data);
|
||||
} else {
|
||||
console.log(`${styledPrefix} ${message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
import { Sandbox } from "@e2b/code-interpreter";
|
||||
import { createLogger, LogLevel } from "./logger.js";
|
||||
import { TIMEOUT_MS } from "../constants.js";
|
||||
import { getSandboxErrorFields } from "./sandbox-error-fields.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ReadWriteUtil");
|
||||
|
||||
export async function readFile(
|
||||
sandbox: Sandbox,
|
||||
filePath: string,
|
||||
|
|
@ -15,18 +18,17 @@ export async function readFile(
|
|||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
|
||||
if (readOutput.exitCode !== 0) {
|
||||
console.error(
|
||||
`\nError reading file '${filePath}' from sandbox via cat:`,
|
||||
logger.error(`Error reading file '${filePath}' from sandbox via cat:`, {
|
||||
readOutput,
|
||||
);
|
||||
});
|
||||
return {
|
||||
success: false,
|
||||
output: `FAILED TO READ FILE from sandbox '${filePath}'. Exit code: ${readOutput.exitCode}.\nStderr: ${readOutput.stderr}.\nStdout: ${readOutput.stdout}`,
|
||||
};
|
||||
}
|
||||
if (readOutput.stderr) {
|
||||
console.warn(
|
||||
`\nStderr while reading file '${filePath}' from sandbox via cat: ${readOutput.stderr}`,
|
||||
logger.warn(
|
||||
`Stderr while reading file '${filePath}' from sandbox via cat: ${readOutput.stderr}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
|
@ -34,9 +36,13 @@ export async function readFile(
|
|||
output: readOutput.stdout,
|
||||
};
|
||||
} catch (e: any) {
|
||||
console.error(
|
||||
`\nException while trying to read file '${filePath}' from sandbox via cat:`,
|
||||
e,
|
||||
logger.error(
|
||||
`Exception while trying to read file '${filePath}' from sandbox via cat:`,
|
||||
{
|
||||
...(e instanceof Error
|
||||
? { name: e.name, message: e.message, stack: e.stack }
|
||||
: { error: e }),
|
||||
},
|
||||
);
|
||||
let outputMessage = `FAILED TO EXECUTE READ COMMAND for sandbox '${filePath}'.`;
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
|
|
@ -71,18 +77,17 @@ ${delimiter}`;
|
|||
await sandbox.setTimeout(TIMEOUT_MS);
|
||||
|
||||
if (writeOutput.exitCode !== 0) {
|
||||
console.error(
|
||||
`\nError writing file '${filePath}' to sandbox via printf:`,
|
||||
logger.error(`Error writing file '${filePath}' to sandbox via printf:`, {
|
||||
writeOutput,
|
||||
);
|
||||
});
|
||||
return {
|
||||
success: false,
|
||||
output: `FAILED TO WRITE FILE to sandbox '${filePath}'. Exit code: ${writeOutput.exitCode}. Stderr: ${writeOutput.stderr}. Stdout: ${writeOutput.stdout}`,
|
||||
};
|
||||
}
|
||||
if (writeOutput.stderr) {
|
||||
console.warn(
|
||||
`\nStderr while writing file '${filePath}' to sandbox via printf: ${writeOutput.stderr}`,
|
||||
logger.warn(
|
||||
`Stderr while writing file '${filePath}' to sandbox via printf: ${writeOutput.stderr}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
|
@ -90,9 +95,13 @@ ${delimiter}`;
|
|||
output: `Successfully wrote file '${filePath}' to sandbox via printf.`,
|
||||
};
|
||||
} catch (e: any) {
|
||||
console.error(
|
||||
`\nException while trying to write file '${filePath}' to sandbox via printf:`,
|
||||
e,
|
||||
logger.error(
|
||||
`Exception while trying to write file '${filePath}' to sandbox via printf:`,
|
||||
{
|
||||
...(e instanceof Error
|
||||
? { name: e.name, message: e.message, stack: e.stack }
|
||||
: { error: e }),
|
||||
},
|
||||
);
|
||||
|
||||
let outputMessage = `FAILED TO EXECUTE WRITE COMMAND for sandbox '${filePath}'.`;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue