feat: langgraph engineer mode (#803)

* feat: langeng button

* fix: default ui mcp value

* feat: plan generate plan prompt update

* feat: planner generate message sys prompt

* feat: programmer sys prompt updated

* feat: review sys prompt added

* chore: code cleaning

* chore: code cleaning

* chore: code cleaning

* chore: code formatting

* chore: prompt formatting

* chore: var naming

* chore: args formatting

* chore: icon renaming

* fix: made custom framework additions generalizable

* chore: code formatting

* chore: import issue fixes

* feat: doc updated

* chore: prompt fix

* chore: removed fies

* fix: doc updates

* chore: code cleaning

* chore: doc fix

* chore: docs update
This commit is contained in:
Aliyan Ishfaq 2025-08-27 17:12:56 -07:00 • committed by GitHub
parent d81080e71e
commit 59db158190
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 976 additions and 24 deletions

View file

@ -35,6 +35,7 @@
"setup/intro",
"setup/development",
"setup/authentication",
"setup/customization",
"setup/monorepo",
"setup/ci"
]

View file

@ -0,0 +1,108 @@
---
title: "Custom Framework Configuration"
description: "How to configure and customize OpenSWE for custom libraries"
---
Open SWE includes support for LangGraph development through the **LangGraph Engineer** toggle. This feature adds framework-specific prompts and MCP tools for building LangGraph agents and workflows.
You can use a similar approach to build your own customization.
## LangGraph Engineer Configuration
### Setting Configuration in the UI
Enabling the **LangGraph Engineer** toggle in the main input area adds specific LangGraph prompts and allows LangGraph documentation access.
This sets the following configuration:
```typescript
const config = {
configurable: {
customFramework: true
}
}
```
## How Custom Framework Configuration Works
The configuration is passed to all agent nodes and used to conditionally include specialized prompts:
```typescript
// In prompt formatting functions
.replace(
"{CUSTOM_FRAMEWORK_PROMPT}",
shouldUseCustomFramework(config) ? CUSTOM_FRAMEWORK_PROMPT : "",
)
```
The `shouldUseCustomFramework(config)` function checks if `config.configurable?.customFramework === true`.
## Custom Framework Files
When `customFramework` is `true`, we inject a series of custom prompts and tools into the agent. The table below shows every place in the codebase that needs updating if you're customizing it for a framework other than LangGraph.
### Prompt Files
| Component | File | Prompt Constant | Description |
|-----------|------|-----------------|-------------|
| Planner | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/planner/nodes/generate-message/prompt.ts) | `EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT` | Adds framework documentation access instructions |
| Planner | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/planner/nodes/generate-message/prompt.ts) | `EXTERNAL_FRAMEWORK_PLAN_PROMPT` | Provides framework-specific planning requirements |
| Programmer | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/programmer/nodes/generate-message/prompt.ts) | `CUSTOM_FRAMEWORK_PROMPT` | Comprehensive framework implementation patterns and best practices |
| Reviewer | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/reviewer/nodes/generate-review-actions/prompt.ts) | `CUSTOM_FRAMEWORK_PROMPT` | Framework validation and testing requirements |
### Agent & UI Configuration Files
| Component | File | Description |
|-----------|------|-------------|
| Toggle Button | [default-view.tsx](https://github.com/langchain-ai/open-swe/tree/main/apps/web/src/components/v2/default-view.tsx) | Contains the LangGraph Engineer toggle button - modify this to add your own button or change the existing one |
| Framework Logic | [should-use-custom-framework.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/utils/should-use-custom-framework.ts) | Logic function that determines when to enable custom framework features. Currently a boolean based on the UI toggle |
| Documentation | [constants.ts](https://github.com/langchain-ai/open-swe/tree/main/packages/shared/src/constants.ts) | MCP server configuration for framework documentation access |
## Customizing for Your Own Framework
Follow these steps to adapt Open SWE for your own framework:
### Modify Prompt Constants
1. **Update prompt files** from the table above to replace LangGraph-specific content:
- Replace `CUSTOM_FRAMEWORK_PROMPT` with your framework's patterns
- Update `EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT` with your docs access instructions
- Modify `EXTERNAL_FRAMEWORK_PLAN_PROMPT` with your planning requirements
### Update UI Configuration
1. **Modify the toggle button** in [`default-view.tsx`](https://github.com/langchain-ai/open-swe/tree/main/apps/web/src/components/v2/default-view.tsx):
- Change button text from "LangGraph Engineer" to your framework name
- Update tooltip text and descriptions
2. **Optional**: Create a new toggle button for your framework while keeping the LangGraph one
### Configure Framework Logic
You can either:
- **Reuse the existing `customFramework: true` configuration** and modify the prompts to match your framework instead of LangGraph (no additional code changes needed)
- **Create a separate config variable** by adding a new field in your graph configuration (e.g., `yourFramework: true`) and adding the detection logic similar to [`should-use-custom-framework.ts`](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/utils/should-use-custom-framework.ts)
### Add Documentation Access
1. **Configure MCP servers** in [`constants.ts`](https://github.com/langchain-ai/open-swe/tree/main/packages/shared/src/constants.ts):
```typescript
export const DEFAULT_MCP_SERVERS = {
"your-framework-docs-mcp": {
command: "uvx",
args: ["your-framework-docs-mcp"],
},
};
```
### Build and Test
1. **Rebuild the application**:
```bash
cd apps/open-swe
yarn build
```
2. **Test your customization** by enabling the toggle and verifying framework-specific prompts are used

View file

@ -173,6 +173,30 @@ Once a plan is accepted, the Programmer graph executes the implementation.
work is preserved even if the session is interrupted.
</Note>
## LangGraph Engineer Toggle
The UI includes a **LangGraph Engineer** toggle button that optimizes the agent's performance when working with LangGraph code:
<Note>
The LangGraph Engineer toggle is located in the main input area, represented by the Open SWE icon.
</Note>
### When to Enable LangGraph Engineer
Enable this toggle when your request involves creating or modifying LangGraph agents or workflows. It is very useful for building LLM apps from scratch.
### What LangGraph Engineer Does
When enabled, the toggle:
- **Provides specific prompts**: Adds LangGraph-specific guidance and best practices to all agent prompts
- **Includes documentation access**: Agents can automatically fetch up-to-date LangGraph documentation during planning and implementation through an MCP server.
- **Follows LangGraph patterns**: Ensures agents use proper LangGraph structure, primitives, and deployment methods.
<Tip>
Enable LangGraph Engineer for any project that imports from `@langchain/langgraph` or `langgraph` to get the best results.
</Tip>
## Getting Started
To begin using the Open SWE UI:

View file

@ -20,7 +20,11 @@ import {
formatFollowupMessagePrompt,
isFollowupRequest,
} from "../../utils/followup.js";
import { SYSTEM_PROMPT } from "./prompt.js";
import {
SYSTEM_PROMPT,
EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT,
EXTERNAL_FRAMEWORK_PLAN_PROMPT,
} from "./prompt.js";
import { getRepoAbsolutePath } from "@openswe/shared/git";
import {
isLocalMode,
@ -41,6 +45,7 @@ import {
} from "../../../../utils/caching.js";
import { createViewTool } from "../../../../tools/builtin-tools/view.js";
import { shouldCreateIssue } from "../../../../utils/should-create-issue.js";
import { shouldUseCustomFramework } from "../../../../utils/should-use-custom-framework.js";
const logger = createLogger(LogLevel.INFO, "GeneratePlanningMessageNode");
@ -80,7 +85,18 @@ function formatSystemPrompt(
state.codebaseTree || "No codebase tree generated yet.",
)
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules))
.replace("{USER_REQUEST_PROMPT}", formatUserRequestPrompt(state.messages));
.replace("{USER_REQUEST_PROMPT}", formatUserRequestPrompt(state.messages))
.replace(
"{EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT}",
shouldUseCustomFramework(config)
? EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT
: "",
)
.replace(
"{EXTERNAL_FRAMEWORK_PLAN_PROMPT}",
shouldUseCustomFramework(config) ? EXTERNAL_FRAMEWORK_PLAN_PROMPT : "",
)
.replace("{DEV_SERVER_PROMPT}", ""); // Always empty until we add dev server tool
}
export async function generateAction(

View file

@ -34,8 +34,11 @@ Your sole objective in this phase is to gather comprehensive context about the c
- You will always be able to gather more context after the planning phase, so ensure that the actions you perform in this planning phase are only the most necessary and targeted actions to gather context.
- Avoid rabbit holes for gathering context. You should always first consider whether or not the action you're about to take is necessary to generate a plan for the user's request. If it is not, do not take it.
9. Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of cd. You may use cd if the User explicitly requests it.
{EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT}
</context_gathering_guidelines>
{EXTERNAL_FRAMEWORK_PLAN_PROMPT}
<tool_usage>
### Grep search tool
- Use the \`grep\` tool for all file searches. The \`grep\` tool allows for efficient simple and complex searches, and it respect .gitignore patterns.
@ -74,6 +77,8 @@ Your sole objective in this phase is to gather comprehensive context about the c
Parameters:
- \`url\`: The URL to fetch the contents of
- \`query\`: The query to search for within the document. This should be a natural language query. The query will be passed to a separate LLM and prompted to extract context from the document which answers this query.
{DEV_SERVER_PROMPT}
</tool_usage>
<workspace_information>
@ -96,3 +101,115 @@ Your sole objective in this phase is to gather comprehensive context about the c
{USER_REQUEST_PROMPT}
</user_request>
</task_context>`;
export const EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT = `
10. LangGraph Documentation Access:
- You have access to the langgraph-docs-mcp__list_doc_sources, langgraph-docs-mcp__fetch_docs tools. Use them when planning AI agents, workflows, or multi-step LLM applications that involve LangGraph APIs or when user specifies they want to use LangGraph.
- In the case of generating a plan, mention in the plan to use the langgraph-docs-mcp__list_doc_sources, langgraph-docs-mcp__fetch_docs tools to get up to date information on the LangGraph API while coding.
- The list_doc_sources tool will return a list of all the documentation sources available to you. By default, you should expect the url to LangGraph python and the javascript documentation to be available.
- The fetch_docs tool will fetch the documentation for the given source. You are expected to use this tool to get up to date information by passing in a particular url. It returns the documentation as a markdown string.
- [Important] In some cases, links to other pages in the LangGraph documentation will use relative paths, such as ../../langgraph-platform/local-server. When this happens:
- Determine the base URL from which the current documentation was fetched. It should be the url of the page you you read the relative path from.
- For ../, go one level up in the URL hierarchy.
- For ../../, go two levels up, then append the relative path.
- If the current page is: https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/setup/ And you encounter a relative link: ../../langgraph-platform/local-server,
- Go up two levels: https://langchain-ai.github.io/langgraph/tutorials/get-started/
- Append the relative path to form the full URL: https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/local-server
- If you get a response like Encountered an HTTP error: Client error '404' for url, it probably means that the url you created with relative path is incorrect so you should try constructing it again.
`;
export const EXTERNAL_FRAMEWORK_PLAN_PROMPT = `
<external_libraries_and_frameworks_planning>
<langgraph_planning_requirements>
When planning LangGraph agents, ensure tasks include:
**Structure Requirements:**
- If any LangGraph-related files exist in the codebase (graph.py, main.py, app.py, or any files with graph imports/exports), do not create a newagent.py. Always work with existing files and follow the established patterns.
- Create agent.py when building a new LangGraph project from an empty directory with zero existing graph-related files.
- For existing projects, always follow the existing structure and never impose new patterns.
- Proper state management with TypedDict or Pydantic BaseModel
- Never add a checkpointer unless explicitly requested by user
**Deployment-First Planning:**
- Plan to use prebuilt components: create_react_agent, supervisor patterns, swarm patterns
- Only plan to use custom StateGraph when prebuilt components don't fit the use case
- Always include tasks for runtime testing with dev_server
- Plan for \`langgraph dev\` testing after implementation
**Critical Error Prevention in Plans:**
- State updates must return dictionaries, not full state objects
- Message objects are not strings - plan for .content property extraction
- Always plan for exporting compiled graph as 'app' variable
- Plan for type safety verification before chaining operations
**Required Testing Tasks:**
- Include dev_server task after any LangGraph implementation
- Plan for \`langgraph dev\` command testing
- Plan for sending test requests to verify agent responses
- Plan for reviewing server logs for initialization issues
</langgraph_planning_requirements>
<framework_integration_planning>
**Streamlit + LangGraph Integration:**
- Plan for nest_asyncio setup tasks
- Plan for session state management tasks
- Plan for form widget constraints handling
**FastAPI + LangGraph Integration:**
- Plan for async endpoint patterns
- Plan for proper event loop management
**Multi-Framework Integration:**
- Plan debugging verification tasks with test markers
- Plan for config propagation verification
- Plan for integration point testing
</framework_integration_planning>
<important_documentation>
**LangGraph Core Concepts:**
- https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/
- https://langchain-ai.github.io/langgraph/how-tos/pass-config-to-tools/
**LangGraph Patterns:**
- https://langchain-ai.github.io/langgraph/reference/supervisor/
- https://langchain-ai.github.io/langgraph/reference/swarm/
**LangGraph Streaming & Interrupts (needed when user input required):**
- https://langchain-ai.github.io/langgraph/how-tos/stream-updates/
- https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#stream
- https://langchain-ai.github.io/langgraph/concepts/streaming/#whats-possible-with-langgraph-streaming
- https://docs.langchain.com/langgraph-platform/interrupt-concurrent
**Framework Integration:**
- https://docs.streamlit.io/library/api-reference/session-state
- https://docs.streamlit.io/knowledge-base/using-streamlit/how-to-use-async-await
- https://docs.python.org/3/library/asyncio-dev.html#common-mistakes
- https://github.com/erdewit/nest_asyncio
</important_documentation>
</external_libraries_and_frameworks_planning>`;
export const DEV_SERVER_PROMPT = `
### Dev server tool
The \`dev_server\` tool allows you to start development servers and monitor their behavior for debugging purposes.
You SHOULD use this tool when reviewing any changes to web applications, APIs, or services.
Static code review is insufficient - you must verify runtime behavior when creating langgraph agents.
**You should always use this tool when:**
- Reviewing API modifications (verify endpoints respond properly)
- Investigating server startup issues or runtime errors
Common development server commands by technology:
- **Python/LangGraph**: \`langgraph dev\` (for LangGraph applications)
- **Node.js/React**: \`npm start\`, \`npm run dev\`, \`yarn start\`, \`yarn dev\`
- **Python/Django**: \`python manage.py runserver\`
- **Python/Flask**: \`python app.py\`, \`flask run\`
- **Python/FastAPI**: \`uvicorn main:app --reload\`
- **Go**: \`go run .\`, \`go run main.go\`
- **Ruby/Rails**: \`rails server\`, \`bundle exec rails server\`
Parameters:
- \`command\`: The development server command to execute (e.g., ["langgraph", "dev"] or ["npm", "start"])
- \`request\`: HTTP request to send to the server for testing (JSON format with url, method, headers, body)
- \`workdir\`: Working directory for the command
- \`wait_time\`: Time to wait in seconds before sending request (default: 10)
The tool will start the server, send a test request, capture logs, and return the results for your review.`;

View file

@ -20,14 +20,22 @@ import { stopSandbox } from "../../../../utils/sandbox.js";
import { z } from "zod";
import { formatCustomRulesPrompt } from "../../../../utils/custom-rules.js";
import { getScratchpad } from "../../utils/scratchpad-notes.js";
import { SCRATCHPAD_PROMPT, SYSTEM_PROMPT } from "./prompt.js";
import {
SCRATCHPAD_PROMPT,
SYSTEM_PROMPT,
CUSTOM_FRAMEWORK_PROMPT,
} from "./prompt.js";
import { shouldUseCustomFramework } from "../../../../utils/should-use-custom-framework.js";
import { DO_NOT_RENDER_ID_PREFIX } from "@openswe/shared/constants";
import { filterMessagesWithoutContent } from "../../../../utils/message/content.js";
import { getModelManager } from "../../../../utils/llms/model-manager.js";
import { trackCachePerformance } from "../../../../utils/caching.js";
import { isLocalMode } from "@openswe/shared/open-swe/local-mode";
function formatSystemPrompt(state: PlannerGraphState): string {
function formatSystemPrompt(
state: PlannerGraphState,
config: GraphConfig,
): string {
// It's a followup if there's more than one human message.
const isFollowup = isFollowupRequest(state.taskPlan, state.proposedPlan);
const scratchpad = getScratchpad(state.messages)
@ -48,6 +56,10 @@ function formatSystemPrompt(state: PlannerGraphState): string {
scratchpad.length
? SCRATCHPAD_PROMPT.replace("{SCRATCHPAD}", scratchpad)
: "",
)
.replace(
"{ADDITIONAL_INSTRUCTIONS}",
shouldUseCustomFramework(config) ? CUSTOM_FRAMEWORK_PROMPT : "",
);
}
@ -97,7 +109,7 @@ export async function generatePlan(
.invoke([
{
role: "system",
content: formatSystemPrompt(state),
content: formatSystemPrompt(state, config),
},
...inputMessages,
]);

View file

@ -54,6 +54,8 @@ Create your plan following these guidelines:
- If you have multiple simple steps that are related, and should be executed one after the other, combine them into a single step.
- For example, if you have multiple steps to run a linter, formatter, etc., combine them into a single step. The same goes for passing arguments, or editing files.
{ADDITIONAL_INSTRUCTIONS}
${GITHUB_WORKFLOWS_PERMISSIONS_PROMPT}
</instructions>
@ -72,3 +74,25 @@ Always format your plan items with proper markdown. Avoid large headers, but you
{SCRATCHPAD}
Remember: Your goal is to create a focused, executable plan that efficiently accomplishes the user's request using the context you've already gathered.`;
export const CUSTOM_FRAMEWORK_PROMPT = `
7. **LangGraph-specific planning:**
- When the user's request involves LangGraph code generation, editing, or bug fixing, ensure the execution agent will have access to up-to-date LangGraph documentation
- If the codebase contains any existing LangGraph files (such as graph.py, main.py, app.py) or any files that import/export graphs, do NOT plan new agent files unless asked. Always work with the existing file structure.
- Create agent.py when building a completely new LangGraph project from an empty directory with zero existing graph-related files.
- When LangGraph is involved, include a plan item to reference the langgraph-docs-mcp tools for current API information during implementation
8. **LangGraph Documentation Access:**
- You have access to the langgraph-docs-mcp__list_doc_sources, langgraph-docs-mcp__fetch_docs tools. Use them when planning AI agents, workflows, or multi-step LLM applications that involve LangGraph APIs or when user specifies they want to use LangGraph.
- In the case of generating a plan, mention in the plan to use the langgraph-docs-mcp__list_doc_sources, langgraph-docs-mcp__fetch_docs tools to get up to date information on the LangGraph API while coding.
- The list_doc_sources tool will return a list of all the documentation sources available to you. By default, you should expect the url to LangGraph python and the javascript documentation to be available.
- The fetch_docs tool will fetch the documentation for the given source. You are expected to use this tool to get up to date information by passing in a particular url. It returns the documentation as a markdown string.
- [Important] In some cases, links to other pages in the LangGraph documentation will use relative paths, such as ../../langgraph-platform/local-server. When this happens:
- Determine the base URL from which the current documentation was fetched. It should be the url of the page you you read the relative path from.
- For ../, go one level up in the URL hierarchy.
- For ../../, go two levels up, then append the relative path.
- If the current page is: https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/setup/ And you encounter a relative link: ../../langgraph-platform/local-server,
- Go up two levels: https://langchain-ai.github.io/langgraph/tutorials/get-started/
- Append the relative path to form the full URL: https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/local-server
- If you get a response like Encountered an HTTP error: Client error '404' for url, it probably means that the url you created with relative path is incorrect so you should try constructing it again.
`;

View file

@ -34,6 +34,7 @@ import {
DYNAMIC_SYSTEM_PROMPT,
STATIC_ANTHROPIC_SYSTEM_INSTRUCTIONS,
STATIC_SYSTEM_INSTRUCTIONS,
CUSTOM_FRAMEWORK_PROMPT,
} from "./prompt.js";
import { getRepoAbsolutePath } from "@openswe/shared/git";
import { getMissingMessages } from "../../../../utils/github/issue-messages.js";
@ -66,6 +67,7 @@ import {
shouldIncludeReviewCommentTool,
createReplyToReviewTool,
} from "../../../../tools/reply-to-review-comment.js";
import { shouldUseCustomFramework } from "../../../../utils/should-use-custom-framework.js";
const logger = createLogger(LogLevel.INFO, "GenerateMessageNode");
@ -93,6 +95,7 @@ const formatDynamicContextPrompt = (state: GraphState) => {
const formatStaticInstructionsPrompt = (
state: GraphState,
config: GraphConfig,
isAnthropicModel: boolean,
) => {
return (
@ -101,11 +104,17 @@ const formatStaticInstructionsPrompt = (
: STATIC_SYSTEM_INSTRUCTIONS
)
.replaceAll("{REPO_DIRECTORY}", getRepoAbsolutePath(state.targetRepository))
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules));
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules))
.replace(
"{CUSTOM_FRAMEWORK_PROMPT}",
shouldUseCustomFramework(config) ? CUSTOM_FRAMEWORK_PROMPT : "",
)
.replace("{DEV_SERVER_PROMPT}", ""); // Always empty until we add dev server tool
};
const formatCacheablePrompt = (
state: GraphState,
config: GraphConfig,
args?: {
isAnthropicModel?: boolean;
excludeCacheControl?: boolean;
@ -117,7 +126,11 @@ const formatCacheablePrompt = (
// Cache Breakpoint 2: Static Instructions
{
type: "text",
text: formatStaticInstructionsPrompt(state, !!args?.isAnthropicModel),
text: formatStaticInstructionsPrompt(
state,
config,
!!args?.isAnthropicModel,
),
...(!args?.excludeCacheControl
? { cache_control: { type: "ephemeral" } }
: {}),
@ -236,6 +249,7 @@ async function createToolsAndPrompt(
...state,
taskPlan: options.latestTaskPlan ?? state.taskPlan,
},
config,
{
isAnthropicModel: true,
excludeCacheControl: false,
@ -254,6 +268,7 @@ async function createToolsAndPrompt(
...state,
taskPlan: options.latestTaskPlan ?? state.taskPlan,
},
config,
{
isAnthropicModel: false,
excludeCacheControl: true,

View file

@ -188,12 +188,16 @@ ${CORE_BEHAVIOR_PROMPT}
The \`mark_task_completed\` tool allows Claude to mark a task as completed.
Parameters:
- \`completed_task_summary\`: A summary of the completed task. This summary should include high level context about the actions you took to complete the task, and any other context which would be useful to another developer reviewing the actions you took. Ensure this is properly formatted using markdown.
{DEV_SERVER_PROMPT}
</tool_usage>
${TOOL_USE_BEST_PRACTICES_PROMPT}
${CODING_STANDARDS_PROMPT}
{CUSTOM_FRAMEWORK_PROMPT}
${COMMUNICATION_GUIDELINES_PROMPT}
${SPECIAL_TOOLS_PROMPT}
@ -219,6 +223,8 @@ ${CORE_BEHAVIOR_PROMPT}
${CODING_STANDARDS_PROMPT}
{CUSTOM_FRAMEWORK_PROMPT}
${COMMUNICATION_GUIDELINES_PROMPT}
${SPECIAL_TOOLS_PROMPT}
@ -273,3 +279,506 @@ These are notes you took while gathering context for the plan:
</context>
`;
export const DEV_SERVER_PROMPT = `
### Dev server tool
The \`dev_server\` tool allows you to start development servers and monitor their behavior for debugging purposes.
You SHOULD use this tool when reviewing any changes to web applications, APIs, or services.
Static code review is insufficient - you must verify runtime behavior when creating langgraph agents.
**You should always use this tool when:**
- Reviewing API modifications (verify endpoints respond properly)
- Investigating server startup issues or runtime errors
Common development server commands by technology:
- **Python/LangGraph**: \`langgraph dev\` (for LangGraph applications)
- **Node.js/React**: \`npm start\`, \`npm run dev\`, \`yarn start\`, \`yarn dev\`
- **Python/Django**: \`python manage.py runserver\`
- **Python/Flask**: \`python app.py\`, \`flask run\`
- **Python/FastAPI**: \`uvicorn main:app --reload\`
- **Go**: \`go run .\`, \`go run main.go\`
- **Ruby/Rails**: \`rails server\`, \`bundle exec rails server\`
Parameters:
- \`command\`: The development server command to execute (e.g., ["langgraph", "dev"] or ["npm", "start"])
- \`request\`: HTTP request to send to the server for testing (JSON format with url, method, headers, body)
- \`workdir\`: Working directory for the command
- \`wait_time\`: Time to wait in seconds before sending request (default: 10)
The tool will start the server, send a test request, capture logs, and return the results for your review.`;
export const CUSTOM_FRAMEWORK_PROMPT = `
<langgraph_specific_patterns>
<critical_structure>
**MANDATORY FIRST STEP**: Before creating any files, search the codebase for existing LangGraph-related files. Look for:
- Files with names like: graph.py, main.py, app.py, agent.py, workflow.py
- Files containing: ".compile()", "StateGraph", "create_react_agent", "app =", graph exports
- Any existing LangGraph imports or patterns
**If any LangGraph files exist**: Follow the existing structure exactly. Do not create new agent.py files.
**Only create agent.py when**: Building from completely empty directory with zero existing LangGraph files:
1. agent.py at project root with compiled graph exported as 'app'
2. langgraph.json configuration file in same directory as the graph
3. Proper state management with TypedDict or Pydantic BaseModel
Example structure:
\`\`\`python
from langgraph.graph import StateGraph, START, END
# ... your state and node definitions ...
# Build your graph
graph_builder = StateGraph(YourState)
# ... add nodes and edges ...
# Export as 'app' for new agents from scratch
graph = graph_builder.compile()
app = graph # Required for new LangGraph agents. For existing projects, follow established patterns.
\`\`\`
4. Test small components before building complex graphs
</critical_structure>
<common_langgraph_errors>
- Incorrect interrupt() usage: It pauses execution, doesn't return values.
- Refer to documentation to refer to best interrupt handling practcies, including waiting for user input and proper handling of it.
- Wrong state update patterns: Return updates, not full state.
- Missing state type annotations.
- Missing state fields (current_field, user_input).
- Invalid edge conditions: Ensure all paths have valid transitions.
- Not handling error states properly.
- Not exporting graph as 'app' when creating new LangGraph agents from scratch. For existing projects, follow the established structure.
- Forgetting langgraph.json configuration.
- **Type assumption errors**: Assuming message objects are strings, or that state fields are certain types
- **Chain operations without type checking**: Like \`state.get("field", "")[-1].method()\` without verifying types
</common_langgraph_errors>
<message_and_state_handling>
**CRITICAL**: LangGraph state and message handling patterns:
\`\`\`python
# CORRECT: Extract message content properly
result = agent.invoke({"messages": state["messages"]})
if result.get("messages"):
final_message = result["messages"][-1] # This is a message object
content = final_message.content # This is the string content
# WRONG: Treating message objects as strings
content = result["messages"][-1] # This is an object, not a string!
if content.startswith("Error"): # Will fail - objects don't have startswith()
\`\`\`
**State Updates Must Be Dictionaries**:
\`\`\`python
def my_node(state: State) -> Dict[str, Any]:
# Do work...
return {
"field_name": extracted_string, # Always return dict updates
"messages": updated_message_list # Not the raw messages
}
\`\`\`
</message_and_state_handling>
<langgraph_streaming_and_interrupts_patterns>
- Interrupts only work with stream_mode="updates", not stream_mode="values"
- In "updates" mode, events are structured as {node_name: node_data, ...}
- Check for "__interrupt__" key directly in the event object
- Iterate through event.items() to access individual node outputs
- Interrupts appear as event["__interrupt__"] containing tuple of Interrupt objects
- Access interrupt data via interrupt_obj.value where interrupt_obj = event["__interrupt__"][0]
<important_documentation>
- LangGraph Streaming: https://langchain-ai.github.io/langgraph/how-tos/stream-updates/
- SDK Streaming: https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#stream
- Concurrent Interrupts: https://docs.langchain.com/langgraph-platform/interrupt-concurrent
</important_documentation>
</langgraph_streaming_and_interrupts_patterns>
<when_to_use_interrupts>
**Use interrupt() when you need:**
- User approval for generated plans or proposed changes
- Human confirmation before executing potentially risky operations
- Additional clarification when the task is ambiguous
- User input for decision points that require human judgment
- Feedback on partially completed work before proceeding
</when_to_use_interrupts>
<framework_integration_patterns>
<integration_debugging>
**When building integrations, always start with debugging**:
**Log Everything Initially**:
Use temporary print statements to understand the data flowing through your integration.
\`\`\`python
# Temporary debugging for new integrations
def my_integration_function(input_data, config):
print(f"=== DEBUG START ===")
print(f"Input type: {type(input_data)}")
print(f"Input data: {input_data}")
print(f"Config type: {type(config)}")
print(f"Config data: {config}")
# Process...
result = process(input_data, config)
print(f"Result type: {type(result)}")
print(f"Result data: {result}")
print(f"=== DEBUG END ===")
return result
\`\`\`
</integration_debugging>
<config_propagation_verification>
- **Backend Verification Pattern**: Always verify the receiving end actually uses configuration:
\`\`\`python
# WRONG: Assuming config is used
def my_node(state: State) -> Dict[str, Any]:
response = llm.invoke(state["messages"])
return {"messages": [response]}
# CORRECT: Actually using config
def my_node(state: State, config: RunnableConfig) -> Dict[str, Any]:
# Extract configuration
configurable = config.get("configurable", {})
system_prompt = configurable.get("system_prompt", "Default prompt")
# Use configuration in messages
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
\`\`\`
</config_propagation_verification>
<important_documentation>
- LangGraph Config: https://langchain-ai.github.io/langgraph/how-tos/pass-config-to-tools/
- Streamlit Session State: https://docs.streamlit.io/library/api-reference/session-state
- Asyncio with Web Frameworks: https://docs.python.org/3/library/asyncio-eventloop.html#running-and-stopping-the-loop
</important_documentation>
</framework_integration_patterns>
<langgraph_specific_coding_standards>
- Test small components before building complex graphs
- **Avoid unnecessary complexity**: Before adding complex solutions, consider if simpler approaches with prebuilt components would achieve the same goals:
- Don't create redundant graph nodes that could be combined or simplified
- Check for duplicate processing or validation that could be consolidated
- Question whether additional nodes actually improve the workflow or just add complexity
- Prefer fewer, well-designed nodes over many small, redundant ones
- **Structured LLM Calls and Validation**: When working with LangGraph nodes that involve LLM calls, always use structured output with Pydantic dataclasses for validation and parsing:
- Use \`with_structured_output()\` method for LLM calls that need specific response formats
- Define Pydantic BaseModel classes for all structured data (state schemas, LLM responses, tool inputs/outputs)
- Validate and parse LLM responses using Pydantic models to ensure type safety and data integrity
- For conditional nodes relying on LLM decisions, use structured output to ensure the LLM returns the correct type of data
- Example: \`llm.with_structured_output(MyPydanticModel).invoke(messages)\` instead of raw string parsing
</langgraph_specific_coding_standards>
</langgraph_specific_patterns>
<deployment_first_principles>
**CRITICAL**: All LangGraph agents should be written for DEPLOYMENT unless otherwise specified by the user.
**Core Requirements:**
- NEVER ADD A CHECKPOINTER unless explicitly requested by user.
- Always export compiled graph as 'app'.
- Use prebuilt components when possible.
- Follow model preference hierarchy: Anthropic > OpenAI > Google.
- Keep state minimal (MessagesState usually sufficient).
**AVOID unless user specifically requests:**
\`\`\`python
# Don't do this unless asked!
from langgraph.checkpoint.memory import MemorySaver
graph = create_react_agent(model, tools, checkpointer=MemorySaver())
\`\`\`
**For existing codebases**:
- Always search for existing graph export patterns first
- Work within the established structure rather than imposing new patterns
- Do not create agent.py if graphs are already exported elsewhere
</deployment_first_principles>
<prefer_prebuilt_components>
**Always use prebuilt components when possible** They are deployment-ready and well-tested.
**Basic agents** - use create_react_agent:
\`\`\`python
from langgraph.prebuilt import create_react_agent
# Simple, deployment-ready agent
graph = create_react_agent(
model=model,
tools=tools,
prompt="Your agent instructions here"
)
app = graph
\`\`\`
**Multi-agent systems** - use prebuilt patterns:
**Supervisor pattern** (central coordination):
\`\`\`python
from langgraph_supervisor import create_supervisor
supervisor = create_supervisor(
agents=[agent1, agent2],
model=model,
prompt="You coordinate between agents..."
)
app = supervisor.compile()
\`\`\`
<important_documentation>https://langchain-ai.github.io/langgraph/reference/supervisor/</important_documentation>
**Swarm pattern** (dynamic handoffs):
\`\`\`python
from langgraph_swarm import create_swarm, create_handoff_tool
alice = create_react_agent(
model,
[tools, create_handoff_tool(agent_name="Bob")],
prompt="You are Alice.",
name="Alice",
)
workflow = create_swarm([alice, bob], default_active_agent="Alice")
app = workflow.compile()
\`\`\`
<important_documentation>https://langchain-ai.github.io/langgraph/reference/swarm/</important_documentation>
**Only build custom StateGraph when:**
- Prebuilt components don't fit the specific use case.
- User explicitly asks for custom workflow.
- Complex branching logic required.
- Advanced streaming patterns needed.
<important_documentation>https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/</important_documentation>
</prefer_prebuilt_components>
<patterns_to_avoid>
**AVOID these patterns:**
**Mixing responsibilities in single nodes:**
\`\`\`python
# AVOID: LLM call + tool execution in same node
def bad_node(state):
ai_response = model.invoke(state["messages"]) # LLM call
tool_result = tool_node.invoke({"messages": [ai_response]}) # Tool execution
return {"messages": [...]} # Mixed concerns!
\`\`\`
**PREFER: Separate nodes for separate concerns:**
\`\`\`python
# GOOD: LLM node only calls model
def llm_node(state):
return {"messages": [model.invoke(state["messages"])]}
# GOOD: Tool node only executes tools
def tool_node(state):
return ToolNode(tools).invoke(state)
# Connect with edges
workflow.add_edge("llm", "tools")
\`\`\`
**Overly complex agents when simple ones suffice:**
\`\`\`python
# AVOID: Unnecessary complexity
workflow = StateGraph(ComplexState)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
# ... 20 lines of manual setup when create_react_agent would work
\`\`\`
**Overly complex state:**
\`\`\`python
# AVOID: Too many state fields
class State(TypedDict):
messages: List[BaseMessage]
user_input: str
current_step: int
metadata: Dict[str, Any]
history: List[Dict]
# ... many more fields
\`\`\`
**Wrong export patterns:**
\`\`\`python
# AVOID: Wrong variable names or missing export
compiled_graph = workflow.compile() # Wrong name
# Missing: app = compiled_graph
\`\`\`
**Incorrect interrupt() usage:**
\`\`\`python
# AVOID: Treating interrupt() as synchronous
result = interrupt("Please confirm action") # Wrong - doesn't return values
if result == "yes": # This won't work
proceed()
\`\`\`
**CORRECT**: interrupt() pauses execution for human input
\`\`\`python
interrupt("Please confirm action")
# Execution resumes after human provides input through platform
\`\`\`
<important_documentation>https://langchain-ai.github.io/langgraph/concepts/streaming/#whats-possible-with-langgraph-streaming</important_documentation>
</patterns_to_avoid>
<async_event_loop_patterns>
<web_framework_async_rules>
**Framework-Specific Async Patterns**:
1. **Streamlit** (has its own event loop):
\`\`\`python
# WRONG: Creating new event loops
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# WRONG: Using ThreadPoolExecutor
with ThreadPoolExecutor() as executor:
future = executor.submit(async_func)
# CORRECT: Use nest_asyncio
import nest_asyncio
nest_asyncio.apply()
# Then simple asyncio.run()
result = asyncio.run(async_function())
\`\`\`
2. **FastAPI** (manages its own event loop):
\`\`\`python
# CORRECT: Use async endpoints directly
@app.post("/run")
async def run_agent(request: Request):
result = await agent.ainvoke(...)
return result
\`\`\`
3. **Jupyter** (IPython event loop):
\`\`\`python
# CORRECT: Use await directly in cells
result = await agent.ainvoke(...)
\`\`\`
</web_framework_async_rules>
<async_error_patterns>
Common errors and solutions:
- \`RuntimeError: Event loop is closed\` → Use nest_asyncio
- \`RuntimeError: This event loop is already running\` → Use nest_asyncio or await directly
- \`asyncio.locks.Event object is bound to a different event loop\` → Don't create new loops
</async_error_patterns>
<important_documentation>
- nest_asyncio: https://github.com/erdewit/nest_asyncio
- Streamlit async: https://docs.streamlit.io/knowledge-base/using-streamlit/how-to-use-async-await
- Python asyncio: https://docs.python.org/3/library/asyncio-dev.html#common-mistakes
</important_documentation>
</async_event_loop_patterns>
<streamlit_specific_patterns>
<session_state_management>
**Centralized State Pattern**:
\`\`\`python
def init_session_state():
"""Initialize all session state variables at once"""
defaults = {
# Static values
"messages": [],
"client": None,
"thread_id": None,
# Dynamic tracking - prefix with 'current_'
"current_system_prompt": "Default prompt",
"current_config": {},
# UI state
"show_feedback": False,
"last_user_input": None,
}
for key, default_value in defaults.items():
if key not in st.session_state:
st.session_state[key] = default_value
# Call at app start
init_session_state()
\`\`\`
</session_state_management>
<form_widget_rules>
**Form API Constraints**:
\`\`\`python
# WRONG: Regular widgets in forms
with st.form("my_form"):
st.text_input("Input")
if st.button("Action"): # Not allowed
process()
# CORRECT: Only form widgets in forms
with st.form("my_form"):
user_input = st.text_input("Input")
submitted = st.form_submit_button("Submit")
# Process outside form
if submitted:
process(user_input)
# Other actions outside form
if st.button("Other Action"):
other_process()
\`\`\`
</form_widget_rules>
<rerun_patterns>
**Avoiding Infinite Reruns**:
\`\`\`python
# WRONG: Modifying state in main flow
st.session_state.counter += 1 # Causes rerun loop
# CORRECT: Modify state in callbacks or conditionally
if st.button("Increment"):
st.session_state.counter += 1
\`\`\`
</rerun_patterns>
<reference_docs>
- Session State API: https://docs.streamlit.io/library/api-reference/session-state
- Forms reference: https://docs.streamlit.io/library/api-reference/control-flow/st.form
- Widget behavior: https://docs.streamlit.io/library/advanced-features/widget-behavior
</reference_docs>
</streamlit_specific_patterns>
<model_preferences>
**LLM MODEL PRIORITY** (follow this order):
\`\`\`python
# 1. PREFER: Anthropic
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model="claude-3-5-sonnet-20241022")
# 2. SECOND CHOICE: OpenAI
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
# 3. THIRD CHOICE: Google
from langchain_google_genai import ChatGoogleGenerativeAI
model = ChatGoogleGenerativeAI(model="gemini-1.5-pro")
\`\`\`
**NOTE**: Assume API keys are available in environment - ignore missing key errors during development.
</model_preferences>
<documentation_guidelines>
<when_to_consult_documentation>
Always use the documentation tools before implementing LangGraph code rather than relying on internal knowledge, as the API evolves rapidly. Specifically:
- Before creating new graph nodes or modifying existing ones.
- When implementing state schemas or message passing patterns.
- Before using LangGraph-specific decorators, annotations, or utilities.
- When working with conditional edges, dynamic routing, or subgraphs.
- Before implementing tool calling patterns within graph nodes.
Whenever you are building applications that require multiple frameworks and their integrations for e.g., LangGraph + Streamlit, LangGraph + Next.js, LangGraph + React, etc., you should consult the documentation of the framework you are using to ensure you are using the correct syntax and patterns.
</when_to_consult_documentation>
<documentation_navigation>
- Determine the base URL from the current documentation page.
- For ../, go one level up in the URL hierarchy.
- For ../../, go two levels up, then append the relative path.
- Example: From https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/setup/ with link ../../langgraph-platform/local-server
- Go up two levels: https://langchain-ai.github.io/langgraph/tutorials/get-started/
- Append path: https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/local-server
- If you get a response like Encountered an HTTP error: Client error '404' for url, it probably means that the url you created with relative path is incorrect so you should try constructing it again.
</documentation_navigation>
</documentation_guidelines>
`;

View file

@ -12,7 +12,12 @@ import {
import { GraphConfig } from "@openswe/shared/open-swe/types";
import { createLogger, LogLevel } from "../../../../utils/logger.js";
import { getMessageContentString } from "@openswe/shared/messages";
import { PREVIOUS_REVIEW_PROMPT, SYSTEM_PROMPT } from "./prompt.js";
import {
PREVIOUS_REVIEW_PROMPT,
SYSTEM_PROMPT,
CUSTOM_FRAMEWORK_PROMPT,
} from "./prompt.js";
import { shouldUseCustomFramework } from "../../../../utils/should-use-custom-framework.js";
import { getRepoAbsolutePath } from "@openswe/shared/git";
import {
createGrepTool,
@ -40,7 +45,10 @@ import { BindToolsInput } from "@langchain/core/language_models/chat_models";
const logger = createLogger(LogLevel.INFO, "GenerateReviewActionsNode");
function formatSystemPrompt(state: ReviewerGraphState): string {
function formatSystemPrompt(
state: ReviewerGraphState,
config: GraphConfig,
): string {
const activePlan = getActivePlanItems(state.taskPlan);
const tasksString = formatPlanPromptWithSummaries(activePlan);
@ -55,6 +63,10 @@ function formatSystemPrompt(state: ReviewerGraphState): string {
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules))
.replaceAll("{CHANGED_FILES}", state.changedFiles)
.replaceAll("{BASE_BRANCH_NAME}", state.baseBranchName)
.replace(
"{CUSTOM_FRAMEWORK_PROMPT}",
shouldUseCustomFramework(config) ? CUSTOM_FRAMEWORK_PROMPT : "",
)
.replaceAll("{COMPLETED_TASKS_AND_SUMMARIES}", tasksString)
.replaceAll(
"{DEPENDENCIES_INSTALLED}",
@ -68,6 +80,7 @@ function formatSystemPrompt(state: ReviewerGraphState): string {
const formatCacheablePrompt = (
state: ReviewerGraphState,
config: GraphConfig,
args?: {
excludeCacheControl?: boolean;
},
@ -77,7 +90,7 @@ const formatCacheablePrompt = (
const segments: CacheablePromptSegment[] = [
{
type: "text",
text: formatSystemPrompt(state),
text: formatSystemPrompt(state, config),
...(!args?.excludeCacheControl
? { cache_control: { type: "ephemeral" } }
: {}),
@ -146,7 +159,9 @@ function createToolsAndPrompt(
const anthropicMessages = [
{
role: "system",
content: formatCacheablePrompt(state, { excludeCacheControl: false }),
content: formatCacheablePrompt(state, config, {
excludeCacheControl: false,
}),
},
{
role: "user",
@ -159,7 +174,9 @@ function createToolsAndPrompt(
const nonAnthropicMessages = [
{
role: "system",
content: formatCacheablePrompt(state, { excludeCacheControl: true }),
content: formatCacheablePrompt(state, config, {
excludeCacheControl: true,
}),
},
{
role: "user",

View file

@ -106,6 +106,8 @@ By reviewing these actions, and comparing them to the plan and original user req
**REMINDER**:
You are ONLY gathering context. Any non-read actions you believe are necessary to take can be executed after you've provided your final review.
Only gather context right now in order to inform your final review, and to provide any additional steps to take after the review.
{CUSTOM_FRAMEWORK_PROMPT}
</instructions>
<tool_usage>
@ -170,3 +172,32 @@ By reviewing these actions, and comparing them to the plan and original user req
<task_context>
{USER_REQUEST_PROMPT}
</task_context>`;
export const CUSTOM_FRAMEWORK_PROMPT = `
<langgraph_validation>
When reviewing LangGraph implementations:
**1. Structure Validation**:
- Search for existing graph exports first (app =, .compile(), graph exports)
- Validate existing structure rather than expecting new agent.py files
- Only validate agent.py if no existing exports found
**2. Quality Checks**:
- Verify structured outputs with Pydantic models for LLM calls
- Check for unnecessary complexity or duplicate nodes
- Ensure proper use of with_structured_output() for type safety
- Validate state management patterns
**3. Compilation Testing**:
- Test basic import: python3 -c "import [module]; print('Success')"
- Test graph compilation: python3 -c "from [module] import app; print('Compiled')"
- Check langgraph.json validity if present
- Run available linters (ruff, mypy) but don't block on warnings
**4. Success Criteria**:
- Module imports without errors
- Graph compiles successfully
- No blocking syntax/import issues
- Follows established patterns in codebase
</langgraph_validation>
`;

View file

@ -8,6 +8,7 @@ import {
} from "@openswe/shared/open-swe/mcp";
import { createLogger, LogLevel } from "./logger.js";
import { DEFAULT_MCP_SERVERS } from "@openswe/shared/constants";
import { shouldUseCustomFramework } from "./should-use-custom-framework.js";
const logger = createLogger(LogLevel.INFO, "MCP Client");
@ -86,8 +87,10 @@ export async function getMcpTools(
config: GraphConfig,
): Promise<StructuredToolInterface[]> {
try {
// TODO: Remove default MCP servers obj once UI is implemented
const mergedServers: McpServers = { ...DEFAULT_MCP_SERVERS };
let mergedServers: McpServers = {};
if (shouldUseCustomFramework(config)) {
mergedServers = { ...DEFAULT_MCP_SERVERS };
}
const mcpServersConfig = config?.configurable?.["mcpServers"];
if (mcpServersConfig) {

View file

@ -0,0 +1,5 @@
import { GraphConfig } from "@openswe/shared/open-swe/types";
export function shouldUseCustomFramework(config: GraphConfig): boolean {
return config.configurable?.customFramework === true;
}

View file

@ -17,7 +17,7 @@ import { useThreadsStatus } from "@/hooks/useThreadsStatus";
import { cn } from "@/lib/utils";
import { threadsToMetadata } from "@/lib/thread-utils";
import { UserPopover } from "@/components/user-popover";
import { OpenSWELogoSVG } from "@/components/icons/openswe";
import { OpenSWELogo } from "@/components/icons/openswe-logo";
type FilterStatus =
| "all"
@ -128,7 +128,7 @@ function AllThreadsPageContent() {
<ArrowLeft className="h-3 w-3" />
</Button>
<div className="flex items-center gap-2">
<OpenSWELogoSVG
<OpenSWELogo
width={120}
height={18}
/>

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,6 @@
export function OpenSWELogoSVG({
import { cn } from "@/lib/utils";
export function OpenSWELogo({
className,
width = 130,
height = 20,
@ -16,7 +18,7 @@ export function OpenSWELogoSVG({
viewBox="0 0 1625 250"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
className={cn(className)}
style={style}
>
<path

View file

@ -31,7 +31,8 @@ import { useState, useMemo } from "react";
import { threadsToMetadata } from "@/lib/thread-utils";
import { Settings, BookOpen } from "lucide-react";
import NextLink from "next/link";
import { OpenSWELogoSVG } from "../icons/openswe";
import { OpenSWELogo } from "../icons/openswe-logo";
import { OpenSWEIcon } from "../icons/openswe-icon";
import { DEFAULT_CONFIG_KEY, useConfigStore } from "@/hooks/useConfigStore";
function OpenSettingsButton() {
@ -101,6 +102,9 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
const [shouldCreateIssue, setShouldCreateIssue] = useState(
config?.shouldCreateIssue != null ? !!config.shouldCreateIssue : true,
);
const [customFramework, setCustomFramework] = useState(
config?.customFramework != null ? !!config.customFramework : false,
);
const threadsMetadata = useMemo(() => threadsToMetadata(threads), [threads]);
const displayThreads = threadsMetadata.slice(0, 4);
@ -126,7 +130,7 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
<div className="border-border bg-card border-b px-4 py-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<OpenSWELogoSVG
<OpenSWELogo
width={120}
height={18}
/>
@ -180,6 +184,8 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
setAutoAcceptPlan={setAutoAccept}
shouldCreateIssue={shouldCreateIssue}
setShouldCreateIssue={setShouldCreateIssue}
customFramework={customFramework}
setCustomFramework={setCustomFramework}
/>
<div className="flex items-center gap-2">
<TooltipIconButton
@ -218,6 +224,24 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
<EyeOff className="size-4" />
)}
</TooltipIconButton>
<TooltipIconButton
variant={customFramework ? "default" : "ghost"}
tooltip="Use LangGraph Engineer when writing LangGraph code for better results"
className={cn(
"transition-colors duration-200",
customFramework
? "bg-primary hover:bg-primary/90"
: "text-muted-foreground hover:text-foreground",
)}
onClick={() => setCustomFramework((prev) => !prev)}
side="bottom"
>
{customFramework ? (
<OpenSWEIcon className="size-5" />
) : (
<OpenSWEIcon className="size-5" />
)}
</TooltipIconButton>
</div>
</div>
</CardContent>

View file

@ -41,6 +41,8 @@ interface TerminalInputProps {
shouldCreateIssue: boolean;
setShouldCreateIssue: Dispatch<SetStateAction<boolean>>;
draftToLoad?: string;
customFramework: boolean;
setCustomFramework: Dispatch<SetStateAction<boolean>>;
}
const MISSING_API_KEYS_TOAST_CONTENT = (
@ -76,6 +78,8 @@ export function TerminalInput({
shouldCreateIssue,
setShouldCreateIssue,
draftToLoad,
customFramework,
setCustomFramework,
}: TerminalInputProps) {
const { push } = useRouter();
const { message, setMessage, clearCurrentDraft } = useDraftStorage();
@ -169,6 +173,7 @@ export function TerminalInput({
configurable: {
...defaultConfig,
shouldCreateIssue,
customFramework,
[GITHUB_USER_LOGIN_HEADER]: user.login,
},
},
@ -209,6 +214,11 @@ export function TerminalInput({
? !!defaultConfig.shouldCreateIssue
: true,
);
setCustomFramework(
defaultConfig?.customFramework != null
? !!defaultConfig.customFramework
: false,
);
} catch (e) {
if (
typeof e === "object" &&

View file

@ -15,7 +15,6 @@ import {
GITHUB_USER_ID_HEADER,
GITHUB_USER_LOGIN_HEADER,
GITHUB_PAT,
DEFAULT_MCP_SERVERS,
GITHUB_INSTALLATION_ID,
} from "../constants.js";
import { withLangGraph } from "@langchain/langgraph/zod";
@ -429,9 +428,9 @@ export const GraphConfigurationMetadata: {
mcpServers: {
x_open_swe_ui_config: {
type: "json",
default: JSON.stringify(DEFAULT_MCP_SERVERS, null, 2),
default: "{}",
description:
"JSON configuration for custom MCP servers. LangGraph docs server is set by default. See the `mcpServers` field of the LangChain MCP Adapters `ClientConfig` type for information on this schema. [Documentation here](https://v03.api.js.langchain.com/types/_langchain_mcp_adapters.ClientConfig.html).",
"JSON configuration for custom MCP servers. LangGraph docs server is automatically added when custom LangGraph prompts are enabled. See the `mcpServers` field of the LangChain MCP Adapters `ClientConfig` type for information on this schema. [Documentation here](https://v03.api.js.langchain.com/types/_langchain_mcp_adapters.ClientConfig.html).",
},
},
shouldCreateIssue: {
@ -442,6 +441,11 @@ export const GraphConfigurationMetadata: {
"Whether or not to create GitHub issues for all requests. Can be overridden on a per-request basis via the 'eye' icon in the chat input area.",
},
},
customFramework: {
x_open_swe_ui_config: {
type: "hidden",
},
},
reviewPullNumber: {
x_open_swe_ui_config: {
type: "hidden",
@ -619,6 +623,13 @@ export const GraphConfiguration = z.object({
shouldCreateIssue: withLangGraph(z.boolean().optional(), {
metadata: GraphConfigurationMetadata.shouldCreateIssue,
}),
/**
* Whether or not to use the custom framework for the request.
* @default false
*/
customFramework: withLangGraph(z.boolean().optional(), {
metadata: GraphConfigurationMetadata.customFramework,
}),
/**
* The pull request number that this run is associated with.
* @default undefined

View file

@ -16,7 +16,7 @@ export function getCustomConfigurableFields(
if (key in config.configurable) {
if (
metadataValue.x_open_swe_ui_config.type !== "hidden" ||
["apiKeys", "reviewPullNumber"].includes(key)
["apiKeys", "reviewPullNumber", "customFramework"].includes(key)
) {
result[key as keyof GraphConfig["configurable"]] =
config.configurable[key as keyof GraphConfig["configurable"]];