mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-03 11:33:27 +00:00
fix: Allow different model names depending on task (#9)
* fix: Allow different model names depending on task * add gemini 2.5 flash and pro * fix anthropic thinking models
This commit is contained in:
parent
516582b81d
commit
6e470e6bd8
6 changed files with 182 additions and 67 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import { GraphState, GraphConfig, GraphUpdate, PlanItem } from "../types.js";
|
import { GraphState, GraphConfig, GraphUpdate, PlanItem } from "../types.js";
|
||||||
import { loadModel } from "../utils/load-model.js";
|
import { loadModel, Task } from "../utils/load-model.js";
|
||||||
import { shellTool, applyPatchTool } from "../tools/index.js";
|
import { shellTool, applyPatchTool } from "../tools/index.js";
|
||||||
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ export async function generateAction(
|
||||||
state: GraphState,
|
state: GraphState,
|
||||||
config: GraphConfig,
|
config: GraphConfig,
|
||||||
): Promise<GraphUpdate> {
|
): Promise<GraphUpdate> {
|
||||||
const model = await loadModel(config);
|
const model = await loadModel(config, Task.ACTION_GENERATOR);
|
||||||
const tools = [shellTool, applyPatchTool];
|
const tools = [shellTool, applyPatchTool];
|
||||||
const modelWithTools = model.bindTools(tools, { tool_choice: "auto" });
|
const modelWithTools = model.bindTools(tools, { tool_choice: "auto" });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { sessionPlanTool } from "../tools/index.js";
|
import { sessionPlanTool } from "../tools/index.js";
|
||||||
import { GraphState, GraphConfig, GraphUpdate } from "../types.js";
|
import { GraphState, GraphConfig, GraphUpdate } from "../types.js";
|
||||||
import { loadModel } from "../utils/load-model.js";
|
import { loadModel, Task } from "../utils/load-model.js";
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ export async function generatePlan(
|
||||||
state: GraphState,
|
state: GraphState,
|
||||||
config: GraphConfig,
|
config: GraphConfig,
|
||||||
): Promise<GraphUpdate> {
|
): Promise<GraphUpdate> {
|
||||||
const model = await loadModel(config);
|
const model = await loadModel(config, Task.PLANNER);
|
||||||
const modelWithTools = model.bindTools([sessionPlanTool], {
|
const modelWithTools = model.bindTools([sessionPlanTool], {
|
||||||
tool_choice: "auto",
|
tool_choice: "auto",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { GraphConfig, GraphState, GraphUpdate, PlanItem } from "../types.js";
|
import { GraphConfig, GraphState, GraphUpdate, PlanItem } from "../types.js";
|
||||||
import { loadModel } from "../utils/load-model.js";
|
import { loadModel, Task } from "../utils/load-model.js";
|
||||||
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
import { formatPlanPrompt } from "../utils/plan-prompt.js";
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
@ -35,7 +35,7 @@ export async function progressPlanStep(
|
||||||
state: GraphState,
|
state: GraphState,
|
||||||
config: GraphConfig,
|
config: GraphConfig,
|
||||||
): Promise<GraphUpdate> {
|
): Promise<GraphUpdate> {
|
||||||
const model = await loadModel(config);
|
const model = await loadModel(config, Task.PROGRESS_PLAN_CHECKER);
|
||||||
const modelWithTools = model.bindTools([confirmTaskCompletionTool], {
|
const modelWithTools = model.bindTools([confirmTaskCompletionTool], {
|
||||||
tool_choice: confirmTaskCompletionTool.name,
|
tool_choice: confirmTaskCompletionTool.name,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { GraphState, GraphConfig, GraphUpdate } from "../types.js";
|
import { GraphState, GraphConfig, GraphUpdate } from "../types.js";
|
||||||
import { loadModel } from "../utils/load-model.js";
|
import { loadModel, Task } from "../utils/load-model.js";
|
||||||
import { sessionPlanTool } from "../tools/index.js";
|
import { sessionPlanTool } from "../tools/index.js";
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
@ -36,7 +36,7 @@ export async function rewritePlan(
|
||||||
throw new Error("No plan change request found.");
|
throw new Error("No plan change request found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const model = await loadModel(config);
|
const model = await loadModel(config, Task.PLANNER);
|
||||||
const modelWithTools = model.bindTools([sessionPlanTool], {
|
const modelWithTools = model.bindTools([sessionPlanTool], {
|
||||||
// The model should always call the tool when rewriting the plan.
|
// The model should always call the tool when rewriting the plan.
|
||||||
tool_choice: sessionPlanTool.name,
|
tool_choice: sessionPlanTool.name,
|
||||||
|
|
|
||||||
185
src/types.ts
185
src/types.ts
|
|
@ -47,16 +47,64 @@ export const GraphAnnotation = Annotation.Root({
|
||||||
export type GraphState = typeof GraphAnnotation.State;
|
export type GraphState = typeof GraphAnnotation.State;
|
||||||
export type GraphUpdate = typeof GraphAnnotation.Update;
|
export type GraphUpdate = typeof GraphAnnotation.Update;
|
||||||
|
|
||||||
export const MCPConfig = z.object({
|
const MODEL_OPTIONS = [
|
||||||
/**
|
{
|
||||||
* The MCP server URL.
|
label: "Claude Sonnet 4 (Extended Thinking)",
|
||||||
*/
|
value: "anthropic:extended-thinking:claude-sonnet-4-0",
|
||||||
url: z.string(),
|
},
|
||||||
/**
|
{
|
||||||
* The list of tools to provide to the LLM.
|
label: "Claude Opus 4 (Extended Thinking)",
|
||||||
*/
|
value: "anthropic:extended-thinking:claude-opus-4-0",
|
||||||
tools: z.array(z.string()),
|
},
|
||||||
});
|
{
|
||||||
|
label: "Claude Sonnet 4",
|
||||||
|
value: "anthropic:claude-sonnet-4-0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Claude Opus 4",
|
||||||
|
value: "anthropic:claude-opus-4-0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Claude 3.7 Sonnet",
|
||||||
|
value: "anthropic:claude-3-7-sonnet-latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Claude 3.5 Sonnet",
|
||||||
|
value: "anthropic:claude-3-5-sonnet-latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "o4",
|
||||||
|
value: "openai:o4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "o4 mini",
|
||||||
|
value: "openai:o4-mini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "o3",
|
||||||
|
value: "openai:o3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "o3 mini",
|
||||||
|
value: "openai:o3-mini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "GPT 4o",
|
||||||
|
value: "openai:gpt-4o",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "GPT 4.1",
|
||||||
|
value: "openai:gpt-4.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Gemini 2.5 Pro Preview",
|
||||||
|
value: "google-genai:gemini-2.5-pro-preview-05-06",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Gemini 2.5 Flash Preview",
|
||||||
|
value: "google-genai:gemini-2.5-flash-preview-05-20",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const GraphConfiguration = z.object({
|
export const GraphConfiguration = z.object({
|
||||||
/**
|
/**
|
||||||
|
|
@ -85,55 +133,28 @@ export const GraphConfiguration = z.object({
|
||||||
*/
|
*/
|
||||||
sandbox_language: z.enum(["js", "python"]).optional().langgraph.metadata({}),
|
sandbox_language: z.enum(["js", "python"]).optional().langgraph.metadata({}),
|
||||||
/**
|
/**
|
||||||
* The model ID to use for the reflection generation.
|
* The model ID to use for the planning step.
|
||||||
* Should be in the format `provider:model_name`.
|
* This includes initial planning, and rewriting.
|
||||||
* Defaults to `anthropic:claude-3-7-sonnet-latest`.
|
* @default "anthropic:extended-thinking:claude-sonnet-4-0"
|
||||||
*/
|
*/
|
||||||
modelName: z
|
plannerModelName: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.langgraph.metadata({
|
.langgraph.metadata({
|
||||||
x_lg_ui_config: {
|
x_lg_ui_config: {
|
||||||
type: "select",
|
type: "select",
|
||||||
default: "anthropic:claude-3-7-sonnet-latest",
|
default: "anthropic:extended-thinking:claude-sonnet-4-0",
|
||||||
description: "The model to use in all generations",
|
description: "The model to use for planning",
|
||||||
options: [
|
options: MODEL_OPTIONS,
|
||||||
{
|
|
||||||
label: "Claude 3.7 Sonnet",
|
|
||||||
value: "anthropic:claude-3-7-sonnet-latest",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Claude 3.5 Sonnet",
|
|
||||||
value: "anthropic:claude-3-5-sonnet-latest",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "GPT 4o",
|
|
||||||
value: "openai:gpt-4o",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "GPT 4.1",
|
|
||||||
value: "openai:gpt-4.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "o3",
|
|
||||||
value: "openai:o3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "o3 mini",
|
|
||||||
value: "openai:o3-mini",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "o4",
|
|
||||||
value: "openai:o4",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* The temperature to use for the reflection generation.
|
* The temperature to use for the planning step.
|
||||||
* Defaults to `0.7`.
|
* This includes initial planning, and rewriting.
|
||||||
|
* If selecting a reasoning model, this will be ignored.
|
||||||
|
* @default 0
|
||||||
*/
|
*/
|
||||||
temperature: z
|
plannerTemperature: z
|
||||||
.number()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
.langgraph.metadata({
|
.langgraph.metadata({
|
||||||
|
|
@ -146,18 +167,72 @@ export const GraphConfiguration = z.object({
|
||||||
description: "Controls randomness (0 = deterministic, 2 = creative)",
|
description: "Controls randomness (0 = deterministic, 2 = creative)",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of tokens to generate.
|
* The model ID to use for action generation.
|
||||||
* Defaults to `1000`.
|
* @default "anthropic:claude-sonnet-4-0"
|
||||||
*/
|
*/
|
||||||
maxTokens: z
|
actionGeneratorModelName: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.langgraph.metadata({
|
||||||
|
x_lg_ui_config: {
|
||||||
|
type: "select",
|
||||||
|
default: "anthropic:claude-sonnet-4-0",
|
||||||
|
description: "The model to use for action generation",
|
||||||
|
options: MODEL_OPTIONS,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* The temperature to use for action generation.
|
||||||
|
* If selecting a reasoning model, this will be ignored.
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
actionGeneratorTemperature: z
|
||||||
.number()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
.langgraph.metadata({
|
.langgraph.metadata({
|
||||||
x_lg_ui_config: {
|
x_lg_ui_config: {
|
||||||
type: "number",
|
type: "slider",
|
||||||
min: 1,
|
default: 0,
|
||||||
description: "The maximum number of tokens to generate",
|
min: 0,
|
||||||
|
max: 2,
|
||||||
|
step: 0.1,
|
||||||
|
description: "Controls randomness (0 = deterministic, 2 = creative)",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model ID to use for progress plan checking.
|
||||||
|
* @default "anthropic:claude-sonnet-4-0"
|
||||||
|
*/
|
||||||
|
progressPlanCheckerModelName: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.langgraph.metadata({
|
||||||
|
x_lg_ui_config: {
|
||||||
|
type: "select",
|
||||||
|
default: "anthropic:claude-sonnet-4-0",
|
||||||
|
description: "The model to use for progress plan checking",
|
||||||
|
options: MODEL_OPTIONS,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* The temperature to use for progress plan checking.
|
||||||
|
* If selecting a reasoning model, this will be ignored.
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
progressPlanCheckerTemperature: z
|
||||||
|
.number()
|
||||||
|
.optional()
|
||||||
|
.langgraph.metadata({
|
||||||
|
x_lg_ui_config: {
|
||||||
|
type: "slider",
|
||||||
|
default: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 2,
|
||||||
|
step: 0.1,
|
||||||
|
description: "Controls randomness (0 = deterministic, 2 = creative)",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,55 @@
|
||||||
import { initChatModel } from "langchain/chat_models/universal";
|
import { initChatModel } from "langchain/chat_models/universal";
|
||||||
import { GraphConfig } from "../types.js";
|
import { GraphConfig } from "../types.js";
|
||||||
|
|
||||||
export async function loadModel(config: GraphConfig) {
|
export enum Task {
|
||||||
|
PLANNER = "planner",
|
||||||
|
ACTION_GENERATOR = "actionGenerator",
|
||||||
|
PROGRESS_PLAN_CHECKER = "progressPlanChecker",
|
||||||
|
}
|
||||||
|
|
||||||
|
const TASK_TO_CONFIG_DEFAULTS_MAP = {
|
||||||
|
[Task.PLANNER]: {
|
||||||
|
modelName: "anthropic:extended-thinking:claude-sonnet-4-0",
|
||||||
|
temperature: 0,
|
||||||
|
},
|
||||||
|
[Task.ACTION_GENERATOR]: {
|
||||||
|
modelName: "anthropic:claude-sonnet-4-0",
|
||||||
|
temperature: 0,
|
||||||
|
},
|
||||||
|
[Task.PROGRESS_PLAN_CHECKER]: {
|
||||||
|
modelName: "anthropic:claude-sonnet-4-0",
|
||||||
|
temperature: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function loadModel(config: GraphConfig, task: Task) {
|
||||||
const modelStr =
|
const modelStr =
|
||||||
config.configurable?.modelName ?? "anthropic:claude-3-7-sonnet-latest";
|
config.configurable?.[`${task}ModelName`] ??
|
||||||
|
TASK_TO_CONFIG_DEFAULTS_MAP[task].modelName;
|
||||||
|
const temperature =
|
||||||
|
config.configurable?.[`${task}Temperature`] ??
|
||||||
|
TASK_TO_CONFIG_DEFAULTS_MAP[task].temperature;
|
||||||
|
|
||||||
const [modelProvider, ...modelNameParts] = modelStr.split(":");
|
const [modelProvider, ...modelNameParts] = modelStr.split(":");
|
||||||
|
|
||||||
|
let thinkingModel = false;
|
||||||
|
if (modelNameParts[0] === "extended-thinking") {
|
||||||
|
// Using a thinking model. Remove it from the model name.
|
||||||
|
modelNameParts.shift();
|
||||||
|
thinkingModel = true;
|
||||||
|
}
|
||||||
|
|
||||||
const modelName = modelNameParts.join(":");
|
const modelName = modelNameParts.join(":");
|
||||||
|
if (modelProvider === "openai" && modelName.startsWith("o")) {
|
||||||
|
thinkingModel = true;
|
||||||
|
}
|
||||||
|
|
||||||
const model = await initChatModel(modelName, {
|
const model = await initChatModel(modelName, {
|
||||||
modelProvider,
|
modelProvider,
|
||||||
temperature: config.configurable?.temperature ?? 0,
|
temperature: thinkingModel ? undefined : temperature,
|
||||||
maxTokens: config.configurable?.maxTokens ?? undefined,
|
...(thinkingModel && modelProvider === "anthropic"
|
||||||
|
? { thinking: { budgetTokens: 5000, type: "enabled" } }
|
||||||
|
: {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue