diff --git a/.gitignore b/.gitignore index 347cd37a..4b61765b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,11 @@ # misc .DS_Store *.pem +*.key +*.crt +*.p12 +*.pfx +*.jks # debug npm-debug.log* @@ -33,6 +38,7 @@ yarn-error.log* # local env files .env*.local .env +.env.* # vercel .vercel @@ -52,6 +58,7 @@ credentials.json apps/cli/test_traces/ # Python +.venv/ __pycache__/ **/__pycache__/ *.py[cod] diff --git a/CUSTOMIZATION.md b/CUSTOMIZATION.md index 404ec00d..4e45a2c9 100644 --- a/CUSTOMIZATION.md +++ b/CUSTOMIZATION.md @@ -4,8 +4,13 @@ Open SWE is designed to be forked and customized for your org. The core agent is ```python # agent/server.py — the key lines +model_id = os.environ.get("LLM_MODEL_ID", DEFAULT_LLM_MODEL_ID) +model_kwargs = {"max_tokens": DEFAULT_LLM_MAX_TOKENS} +if model_id == DEFAULT_LLM_MODEL_ID: + model_kwargs["reasoning"] = DEFAULT_LLM_REASONING + return create_deep_agent( - model=make_model(os.environ.get("LLM_MODEL_ID", DEFAULT_LLM_MODEL_ID), temperature=0, max_tokens=20_000), + model=make_model(model_id, **model_kwargs), system_prompt=construct_system_prompt(...), tools=[http_request, fetch_url, list_repos, get_branch_name, commit_and_open_pr, linear_comment, slack_thread_reply], backend=sandbox_backend, @@ -119,14 +124,16 @@ See `deepagents.backends.LangSmithSandbox` and `agent/integrations/langsmith.py` ## 2. Model -The model is configured in the `get_agent()` function in `agent/server.py`. By default it uses `anthropic:claude-opus-4-6`, but you can override it with the `LLM_MODEL_ID` environment variable: +The model is configured in the `get_agent()` function in `agent/server.py`. By default it uses `openai:gpt-5.5` with medium reasoning effort, but you can override the model with the `LLM_MODEL_ID` environment variable: ```bash # Set the model via environment variable (uses provider:model format) LLM_MODEL_ID="anthropic:claude-sonnet-4-6" ``` -If `LLM_MODEL_ID` is not set, the default model (`anthropic:claude-opus-4-6`) is used. +If `LLM_MODEL_ID` is not set, the default model (`openai:gpt-5.5`) is used. + +`max_tokens` is a maximum completion/output token budget, not the model's total context window. For OpenAI reasoning models, this budget can include both internal reasoning tokens and final response tokens. ### Switching models @@ -137,7 +144,7 @@ Use the `provider:model` format: model=make_model("anthropic:claude-sonnet-4-6", temperature=0, max_tokens=16_000) # OpenAI (uses Responses API by default) -model=make_model("openai:gpt-4o", temperature=0, max_tokens=16_000) +model=make_model("openai:gpt-5.5", max_tokens=128_000, reasoning={"effort": "medium"}) # Google model=make_model("google_genai:gemini-2.5-pro", temperature=0, max_tokens=16_000) @@ -169,7 +176,7 @@ async def get_agent(config: RunnableConfig) -> Pregel: model = make_model("anthropic:claude-sonnet-4-6", temperature=0, max_tokens=16_000) else: # Full model for code changes from Linear - model = make_model("anthropic:claude-opus-4-6", temperature=0, max_tokens=20_000) + model = make_model("openai:gpt-5.5", max_tokens=128_000, reasoning={"effort": "medium"}) return create_deep_agent(model=model, ...) ``` diff --git a/README.md b/README.md index d37a89c5..f80c6268 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Rather than forking an existing agent or building from scratch, Open SWE **compo ```python create_deep_agent( - model="anthropic:claude-opus-4-6", + model="openai:gpt-5.5", system_prompt=construct_system_prompt(...), tools=[http_request, fetch_url, list_repos, get_branch_name, commit_and_open_pr, linear_comment, slack_thread_reply], backend=sandbox_backend, diff --git a/agent/server.py b/agent/server.py index f5d7f083..f38abfea 100644 --- a/agent/server.py +++ b/agent/server.py @@ -59,7 +59,7 @@ from .tools import ( ) from .utils.auth import resolve_github_token from .utils.github_app import get_github_app_installation_token -from .utils.model import make_model +from .utils.model import ModelKwargs, OpenAIReasoning, make_model from .utils.sandbox import create_sandbox from .utils.sandbox_paths import aresolve_sandbox_work_dir @@ -183,8 +183,10 @@ def graph_loaded_for_execution(config: RunnableConfig) -> bool: ) -DEFAULT_LLM_MODEL_ID = "anthropic:claude-opus-4-6" -DEFAULT_RECURSION_LIMIT = 1_000 +DEFAULT_LLM_MODEL_ID = "openai:gpt-5.5" +DEFAULT_LLM_REASONING: OpenAIReasoning = {"effort": "medium"} +DEFAULT_LLM_MAX_TOKENS = 64_000 +DEFAULT_RECURSION_LIMIT = 9_999 async def get_agent(config: RunnableConfig) -> Pregel: @@ -273,12 +275,14 @@ async def get_agent(config: RunnableConfig) -> Pregel: work_dir = await aresolve_sandbox_work_dir(sandbox_backend) + model_id = os.environ.get("LLM_MODEL_ID", DEFAULT_LLM_MODEL_ID) + model_kwargs: ModelKwargs = {"max_tokens": DEFAULT_LLM_MAX_TOKENS} + if model_id == DEFAULT_LLM_MODEL_ID: + model_kwargs["reasoning"] = DEFAULT_LLM_REASONING + logger.info("Returning agent with sandbox for thread %s", thread_id) return create_deep_agent( - model=make_model( - os.environ.get("LLM_MODEL_ID", DEFAULT_LLM_MODEL_ID), - max_tokens=20_000, - ), + model=make_model(model_id, **model_kwargs), system_prompt=construct_system_prompt( working_dir=work_dir, linear_project_id=linear_project_id, diff --git a/agent/utils/model.py b/agent/utils/model.py index c9cca8a7..d5f70dec 100644 --- a/agent/utils/model.py +++ b/agent/utils/model.py @@ -1,10 +1,25 @@ +from typing import Literal, TypedDict, Unpack + from langchain.chat_models import init_chat_model OPENAI_RESPONSES_WS_BASE_URL = "wss://api.openai.com/v1" -def make_model(model_id: str, **kwargs: dict): - model_kwargs = kwargs.copy() +OpenAIReasoningEffort = Literal["none", "low", "medium", "high", "xhigh"] + + +class OpenAIReasoning(TypedDict, total=False): + effort: OpenAIReasoningEffort + + +class ModelKwargs(TypedDict, total=False): + max_tokens: int | None + reasoning: OpenAIReasoning | None + temperature: float | None + + +def make_model(model_id: str, **kwargs: Unpack[ModelKwargs]): + model_kwargs: dict[str, object] = kwargs.copy() if model_id.startswith("openai:"): model_kwargs["base_url"] = OPENAI_RESPONSES_WS_BASE_URL