diff --git a/evaluation/benchmarks/swe_bench/run_infer.py b/evaluation/benchmarks/swe_bench/run_infer.py
index dfb71464159c..b0f2c399096e 100644
--- a/evaluation/benchmarks/swe_bench/run_infer.py
+++ b/evaluation/benchmarks/swe_bench/run_infer.py
@@ -32,6 +32,7 @@
codeact_user_response,
codex_user_response,
opencode_user_response,
+ swe_agent_user_response,
get_default_sandbox_config_for_eval,
get_metrics,
get_openhands_config_for_eval,
@@ -108,6 +109,7 @@ def set_dataset_type(dataset_name: str) -> str:
'CodeActAgent': codeact_user_response,
'OpenCodeAgent': opencode_user_response,
'CodexAgent': codex_user_response,
+ 'SWEAgent': swe_agent_user_response,
}
diff --git a/openhands/agenthub/__init__.py b/openhands/agenthub/__init__.py
index 73bb54f2e6f9..0539f75a2299 100644
--- a/openhands/agenthub/__init__.py
+++ b/openhands/agenthub/__init__.py
@@ -11,6 +11,7 @@
loc_agent,
opencode_agent,
readonly_agent,
+ swe_agent,
visualbrowsing_agent,
)
from openhands.controller.agent import Agent # noqa: E402
@@ -25,4 +26,5 @@
'loc_agent',
'opencode_agent',
'codex_agent',
+ 'swe_agent',
]
diff --git a/openhands/agenthub/swe_agent/__init__.py b/openhands/agenthub/swe_agent/__init__.py
new file mode 100644
index 000000000000..0e8364420f1f
--- /dev/null
+++ b/openhands/agenthub/swe_agent/__init__.py
@@ -0,0 +1,4 @@
+from openhands.agenthub.swe_agent.swe_agent import SWEAgent
+from openhands.controller.agent import Agent
+
+Agent.register('SWEAgent', SWEAgent)
diff --git a/openhands/agenthub/swe_agent/function_calling.py b/openhands/agenthub/swe_agent/function_calling.py
new file mode 100644
index 000000000000..7b859a7723c8
--- /dev/null
+++ b/openhands/agenthub/swe_agent/function_calling.py
@@ -0,0 +1,198 @@
+"""Function calling implementation for SWE-Agent.
+
+Maps SWE-Agent tool calls (bash, str_replace_editor, submit) to OpenHands actions.
+"""
+
+import json
+
+from litellm import ModelResponse
+
+from openhands.agenthub.codeact_agent.function_calling import combine_thought
+from openhands.agenthub.swe_agent.tools.bash import SWE_AGENT_BASH_TOOL_NAME
+from openhands.agenthub.swe_agent.tools.str_replace_editor import (
+ SWE_AGENT_STR_REPLACE_EDITOR_TOOL_NAME,
+ StrReplaceEditorTool,
+)
+from openhands.agenthub.swe_agent.tools.submit import SWE_AGENT_SUBMIT_TOOL_NAME
+from openhands.core.exceptions import (
+ FunctionCallNotExistsError,
+ FunctionCallValidationError,
+ LLMContextWindowExceedError,
+)
+from openhands.core.logger import openhands_logger as logger
+from openhands.events.action import (
+ Action,
+ AgentFinishAction,
+ CmdRunAction,
+ FileEditAction,
+ FileReadAction,
+ MessageAction,
+ ValidationFailureAction,
+)
+from openhands.events.action.mcp import MCPAction
+from openhands.events.event import FileEditSource, FileReadSource
+from openhands.events.tool import ToolCallMetadata
+
+
+def response_to_actions(
+ response: ModelResponse, mcp_tool_names: list[str] | None = None
+) -> list[Action]:
+ actions: list[Action] = []
+ assert len(response.choices) == 1
+ choice = response.choices[0]
+ assistant_msg = choice.message
+
+ has_content = assistant_msg.content is not None
+ has_tool_calls = hasattr(assistant_msg, 'tool_calls') and assistant_msg.tool_calls
+
+ if not has_content and not has_tool_calls:
+ raise LLMContextWindowExceedError(
+ 'LLM returned empty response with no content and no tool calls.'
+ )
+
+ if hasattr(assistant_msg, 'tool_calls') and assistant_msg.tool_calls:
+ # Extract thought from content
+ thought = ''
+ if isinstance(assistant_msg.content, str):
+ thought = assistant_msg.content
+ elif isinstance(assistant_msg.content, list):
+ for msg in assistant_msg.content:
+ if msg['type'] == 'text':
+ thought += msg['text']
+
+ for i, tool_call in enumerate(assistant_msg.tool_calls):
+ action: Action
+ logger.debug(f'SWE-Agent tool call: {tool_call}')
+
+ try:
+ try:
+ arguments = json.loads(tool_call.function.arguments)
+ except json.decoder.JSONDecodeError as e:
+ raise FunctionCallValidationError(
+ f'Failed to parse tool call arguments: {tool_call.function.arguments}'
+ ) from e
+
+ # ================================================
+ # Bash tool
+ # ================================================
+ if tool_call.function.name == SWE_AGENT_BASH_TOOL_NAME:
+ if 'command' not in arguments:
+ raise FunctionCallValidationError(
+ f'Missing required argument "command" in tool call {tool_call.function.name}'
+ )
+ action = CmdRunAction(command=arguments['command'])
+
+ # ================================================
+ # str_replace_editor tool
+ # ================================================
+ elif tool_call.function.name == SWE_AGENT_STR_REPLACE_EDITOR_TOOL_NAME:
+ if 'command' not in arguments:
+ raise FunctionCallValidationError(
+ f'Missing required argument "command" in tool call {tool_call.function.name}'
+ )
+ if 'path' not in arguments:
+ raise FunctionCallValidationError(
+ f'Missing required argument "path" in tool call {tool_call.function.name}'
+ )
+
+ path = arguments['path']
+ command = arguments['command']
+ other_kwargs = {
+ k: v
+ for k, v in arguments.items()
+ if k not in ['command', 'path']
+ }
+
+ if command == 'view':
+ action = FileReadAction(
+ path=path,
+ impl_source=FileReadSource.OH_ACI,
+ view_range=other_kwargs.get('view_range', None),
+ )
+ else:
+ if 'view_range' in other_kwargs:
+ other_kwargs.pop('view_range')
+
+ # Filter to valid str_replace_editor params
+ valid_params = set(
+ StrReplaceEditorTool['function']['parameters'][
+ 'properties'
+ ].keys()
+ )
+ valid_kwargs_for_editor = {
+ k: v
+ for k, v in other_kwargs.items()
+ if k in valid_params
+ }
+
+ action = FileEditAction(
+ path=path,
+ command=command,
+ impl_source=FileEditSource.OH_ACI,
+ **valid_kwargs_for_editor,
+ )
+
+ # ================================================
+ # Submit tool
+ # ================================================
+ elif tool_call.function.name == SWE_AGENT_SUBMIT_TOOL_NAME:
+ action = AgentFinishAction()
+
+ # ================================================
+ # MCP tools
+ # ================================================
+ elif mcp_tool_names and tool_call.function.name in mcp_tool_names:
+ action = MCPAction(
+ name=tool_call.function.name,
+ arguments=arguments,
+ )
+ else:
+ raise FunctionCallNotExistsError(
+ f'Tool {tool_call.function.name} is not registered. '
+ f'(arguments: {arguments}). '
+ f'Please check the tool name and retry with an existing tool.'
+ )
+
+ except FunctionCallValidationError as e:
+ action = ValidationFailureAction(
+ function_name=tool_call.function.name,
+ error_message=str(e),
+ thought=thought if i == 0 else '',
+ )
+
+ except FunctionCallNotExistsError as e:
+ action = MessageAction(
+ content=str(e),
+ wait_for_response=False,
+ )
+
+ # Add thought to first action
+ if i == 0 and not isinstance(
+ action, (ValidationFailureAction, MessageAction)
+ ):
+ action = combine_thought(action, thought)
+
+ # Add metadata for tool calling
+ action.tool_call_metadata = ToolCallMetadata(
+ tool_call_id=tool_call.id,
+ function_name=tool_call.function.name,
+ model_response=response,
+ total_calls_in_response=len(assistant_msg.tool_calls),
+ )
+ actions.append(action)
+ else:
+ message_action = MessageAction(
+ content=str(assistant_msg.content) if assistant_msg.content else '',
+ wait_for_response=True,
+ )
+ message_action.tool_call_metadata = ToolCallMetadata(
+ model_response=response,
+ total_calls_in_response=0,
+ )
+ actions.append(message_action)
+
+ for action in actions:
+ action.response_id = response.id
+
+ assert len(actions) >= 1
+ return actions
diff --git a/openhands/agenthub/swe_agent/prompts/additional_info.j2 b/openhands/agenthub/swe_agent/prompts/additional_info.j2
new file mode 100644
index 000000000000..5e0ec14ba00f
--- /dev/null
+++ b/openhands/agenthub/swe_agent/prompts/additional_info.j2
@@ -0,0 +1,53 @@
+{% if repository_info %}
+
+At the user's request, repository {{ repository_info.repo_name }} has been cloned to {{ repository_info.repo_directory }} in the current working directory.
+{% if repository_info.branch_name %}The repository has been checked out to branch "{{ repository_info.branch_name }}".
+
+IMPORTANT: You should work within the current branch "{{ repository_info.branch_name }}" unless:
+ 1. the user explicitly instructs otherwise
+ 2. the current branch is "main", "master", or another default branch where direct pushes may be unsafe
+{% endif %}
+
+{% endif %}
+{% if repository_instructions -%}
+
+{{ repository_instructions }}
+
+{% endif %}
+{% if runtime_info -%}
+
+{% if runtime_info.working_dir %}
+The current working directory is {{ runtime_info.working_dir }}
+{% endif %}
+{% if runtime_info.available_hosts %}
+The user has access to the following hosts for accessing a web application,
+each of which has a corresponding port:
+{% for host, port in runtime_info.available_hosts.items() -%}
+* {{ host }} (port {{ port }})
+{% endfor %}
+When starting a web server, use the corresponding ports. You should also
+set any options to allow iframes and CORS requests, and allow the server to
+be accessed from any host (e.g. 0.0.0.0).
+For example, if you are using vite.config.js, you should set server.host and server.allowedHosts to true
+{% endif %}
+{% if runtime_info.additional_agent_instructions %}
+{{ runtime_info.additional_agent_instructions }}
+{% endif %}
+{% if runtime_info.custom_secrets_descriptions %}
+
+You have access to the following environment variables
+{% for secret_name, secret_description in runtime_info.custom_secrets_descriptions.items() %}
+* **${{ secret_name }}**: {{ secret_description }}
+{% endfor %}
+
+{% endif %}
+{% if runtime_info.date %}
+Today's date is {{ runtime_info.date }} (UTC).
+{% endif %}
+
+{% if conversation_instructions and conversation_instructions.content -%}
+
+{{ conversation_instructions.content }}
+
+{% endif %}
+{% endif %}
diff --git a/openhands/agenthub/swe_agent/prompts/microagent_info.j2 b/openhands/agenthub/swe_agent/prompts/microagent_info.j2
new file mode 100644
index 000000000000..264828fbe206
--- /dev/null
+++ b/openhands/agenthub/swe_agent/prompts/microagent_info.j2
@@ -0,0 +1,8 @@
+{% for agent_info in triggered_agents %}
+
+The following information has been included based on a keyword match for "{{ agent_info.trigger }}".
+It may or may not be relevant to the user's request.
+
+{{ agent_info.content }}
+
+{% endfor %}
diff --git a/openhands/agenthub/swe_agent/prompts/system_prompt.j2 b/openhands/agenthub/swe_agent/prompts/system_prompt.j2
new file mode 100644
index 000000000000..5fe5dce8f609
--- /dev/null
+++ b/openhands/agenthub/swe_agent/prompts/system_prompt.j2
@@ -0,0 +1,4 @@
+You are a helpful assistant that can interact with a computer to solve tasks.
+
+{% include 'additional_info.j2' %}
+{% include 'microagent_info.j2' %}
diff --git a/openhands/agenthub/swe_agent/prompts/system_prompt_long_horizon.j2 b/openhands/agenthub/swe_agent/prompts/system_prompt_long_horizon.j2
new file mode 100644
index 000000000000..6b4b3dd61c60
--- /dev/null
+++ b/openhands/agenthub/swe_agent/prompts/system_prompt_long_horizon.j2
@@ -0,0 +1 @@
+{% include "system_prompt.j2" %}
diff --git a/openhands/agenthub/swe_agent/prompts/user_prompt.j2 b/openhands/agenthub/swe_agent/prompts/user_prompt.j2
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/openhands/agenthub/swe_agent/prompts/user_prompt.j2
@@ -0,0 +1 @@
+
diff --git a/openhands/agenthub/swe_agent/swe_agent.py b/openhands/agenthub/swe_agent/swe_agent.py
new file mode 100644
index 000000000000..9921d10e28b0
--- /dev/null
+++ b/openhands/agenthub/swe_agent/swe_agent.py
@@ -0,0 +1,183 @@
+"""SWE-Agent for OpenHands.
+
+A function-calling agent that uses SWE-Agent's native tools:
+- bash: Execute bash commands
+- str_replace_editor: View/create/edit files
+- submit: Submit the solution
+"""
+
+import os
+from collections import deque
+from typing import TYPE_CHECKING
+
+from openhands.llm.llm_registry import LLMRegistry
+
+if TYPE_CHECKING:
+ from litellm import ChatCompletionToolParam
+
+ from openhands.events.action import Action
+ from openhands.llm.llm import ModelResponse
+
+import openhands.agenthub.swe_agent.function_calling as swe_agent_function_calling
+from openhands.agenthub.swe_agent.tools.bash import BashTool
+from openhands.agenthub.swe_agent.tools.str_replace_editor import StrReplaceEditorTool
+from openhands.agenthub.swe_agent.tools.submit import SubmitTool
+from openhands.controller.agent import Agent
+from openhands.controller.state.state import State
+from openhands.core.config import AgentConfig
+from openhands.core.logger import openhands_logger as logger
+from openhands.core.message import Message
+from openhands.events.action import AgentFinishAction, MessageAction
+from openhands.events.event import Event
+from openhands.llm.llm_utils import check_tools
+from openhands.memory.condenser import Condenser
+from openhands.memory.condenser.condenser import Condensation, View
+from openhands.memory.conversation_memory import ConversationMemory
+from openhands.runtime.plugins import (
+ AgentSkillsRequirement,
+ PluginRequirement,
+)
+from openhands.utils.prompt import PromptManager
+
+
+class SWEAgent(Agent):
+ VERSION = '1.0'
+ """
+ SWE-Agent style agent using function calling with SWE-Agent's native tools.
+
+ This agent provides a simplified tool interface matching the SWE-Agent framework:
+ - bash: Execute bash commands
+ - str_replace_editor: View, create, and edit files
+ - submit: Submit the solution
+
+ The agent uses function calling to invoke tools and maintains conversation
+ history for context-aware assistance.
+ """
+
+ sandbox_plugins: list[PluginRequirement] = [
+ AgentSkillsRequirement(),
+ ]
+
+ def __init__(self, config: AgentConfig, llm_registry: LLMRegistry) -> None:
+ super().__init__(config, llm_registry)
+ self.pending_actions: deque['Action'] = deque()
+ self.reset()
+ self.tools = self._get_tools()
+
+ self.conversation_memory = ConversationMemory(self.config, self.prompt_manager)
+
+ self.condenser = Condenser.from_config(self.config.condenser, llm_registry)
+ logger.debug(f'Using condenser: {type(self.condenser)}')
+
+ self.llm = self.llm_registry.get_router(self.config)
+
+ @property
+ def prompt_manager(self) -> PromptManager:
+ if self._prompt_manager is None:
+ prompt_dir = (
+ self.config.custom_prompt_dir
+ if self.config.custom_prompt_dir
+ else os.path.join(os.path.dirname(__file__), 'prompts')
+ )
+
+ template_overrides = {}
+ if self.config.system_prompt_path:
+ template_overrides['system_prompt.j2'] = self.config.system_prompt_path
+ if self.config.system_prompt_long_horizon_path:
+ template_overrides['system_prompt_long_horizon.j2'] = (
+ self.config.system_prompt_long_horizon_path
+ )
+
+ self._prompt_manager = PromptManager(
+ prompt_dir=prompt_dir,
+ system_prompt_filename=self.config.resolved_system_prompt_filename,
+ template_overrides=template_overrides if template_overrides else None,
+ )
+
+ return self._prompt_manager
+
+ def _get_tools(self) -> list['ChatCompletionToolParam']:
+ return [BashTool, StrReplaceEditorTool, SubmitTool]
+
+ def reset(self) -> None:
+ super().reset()
+ self.pending_actions.clear()
+
+ def step(self, state: State) -> 'Action':
+ if self.pending_actions:
+ return self.pending_actions.popleft()
+
+ latest_user_message = state.get_last_user_message()
+ if latest_user_message and latest_user_message.content.strip() == '/exit':
+ return AgentFinishAction()
+
+ condensed_history: list[Event] = []
+ match self.condenser.condensed_history(state):
+ case View(events=events):
+ condensed_history = events
+
+ case Condensation(action=condensation_action):
+ return condensation_action
+
+ logger.debug(
+ f'Processing {len(condensed_history)} events from a total of {len(state.history)} events'
+ )
+
+ initial_user_message = self._get_initial_user_message(state.history)
+ messages = self._get_messages(condensed_history, initial_user_message)
+ params: dict = {
+ 'messages': messages,
+ }
+ params['tools'] = check_tools(self.tools, self.llm.config)
+ params['extra_body'] = {
+ 'metadata': state.to_llm_metadata(
+ model_name=self.llm.config.model, agent_name=self.name
+ )
+ }
+ response = self.llm.completion(**params)
+ logger.debug(f'Response from LLM: {response}')
+ actions = self.response_to_actions(response)
+ logger.debug(f'Actions after response_to_actions: {actions}')
+ for action in actions:
+ self.pending_actions.append(action)
+ return self.pending_actions.popleft()
+
+ def _get_initial_user_message(self, history: list[Event]) -> MessageAction:
+ initial_user_message: MessageAction | None = None
+ for event in history:
+ if isinstance(event, MessageAction) and event.source == 'user':
+ initial_user_message = event
+ break
+
+ if initial_user_message is None:
+ logger.error(
+ f'CRITICAL: Could not find the initial user MessageAction in the full {len(history)} events history.'
+ )
+ raise ValueError(
+ 'Initial user message not found in history. Please report this issue.'
+ )
+ return initial_user_message
+
+ def _get_messages(
+ self, events: list[Event], initial_user_message: MessageAction
+ ) -> list[Message]:
+ if not self.prompt_manager:
+ raise Exception('Prompt Manager not instantiated.')
+
+ messages = self.conversation_memory.process_events(
+ condensed_history=events,
+ initial_user_action=initial_user_message,
+ max_message_chars=self.llm.config.max_message_chars,
+ vision_is_active=self.llm.vision_is_active(),
+ )
+
+ if self.llm.is_caching_prompt_active():
+ self.conversation_memory.apply_prompt_caching(messages)
+
+ return messages
+
+ def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
+ return swe_agent_function_calling.response_to_actions(
+ response,
+ mcp_tool_names=list(self.mcp_tools.keys()),
+ )
diff --git a/openhands/agenthub/swe_agent/tools/__init__.py b/openhands/agenthub/swe_agent/tools/__init__.py
new file mode 100644
index 000000000000..65512465e365
--- /dev/null
+++ b/openhands/agenthub/swe_agent/tools/__init__.py
@@ -0,0 +1,5 @@
+from openhands.agenthub.swe_agent.tools.bash import BashTool
+from openhands.agenthub.swe_agent.tools.str_replace_editor import StrReplaceEditorTool
+from openhands.agenthub.swe_agent.tools.submit import SubmitTool
+
+__all__ = ['BashTool', 'StrReplaceEditorTool', 'SubmitTool']
diff --git a/openhands/agenthub/swe_agent/tools/bash.py b/openhands/agenthub/swe_agent/tools/bash.py
new file mode 100644
index 000000000000..76dafa36450b
--- /dev/null
+++ b/openhands/agenthub/swe_agent/tools/bash.py
@@ -0,0 +1,21 @@
+from litellm import ChatCompletionToolParam, ChatCompletionToolParamFunctionChunk
+
+SWE_AGENT_BASH_TOOL_NAME = 'bash'
+
+BashTool: ChatCompletionToolParam = ChatCompletionToolParam(
+ type='function',
+ function=ChatCompletionToolParamFunctionChunk(
+ name=SWE_AGENT_BASH_TOOL_NAME,
+ description='runs the given command directly in bash',
+ parameters={
+ 'type': 'object',
+ 'properties': {
+ 'command': {
+ 'type': 'string',
+ 'description': 'The bash command to execute',
+ },
+ },
+ 'required': ['command'],
+ },
+ ),
+)
diff --git a/openhands/agenthub/swe_agent/tools/str_replace_editor.py b/openhands/agenthub/swe_agent/tools/str_replace_editor.py
new file mode 100644
index 000000000000..7e229610ebdf
--- /dev/null
+++ b/openhands/agenthub/swe_agent/tools/str_replace_editor.py
@@ -0,0 +1,69 @@
+from litellm import ChatCompletionToolParam, ChatCompletionToolParamFunctionChunk
+
+SWE_AGENT_STR_REPLACE_EDITOR_TOOL_NAME = 'str_replace_editor'
+
+StrReplaceEditorTool: ChatCompletionToolParam = ChatCompletionToolParam(
+ type='function',
+ function=ChatCompletionToolParamFunctionChunk(
+ name=SWE_AGENT_STR_REPLACE_EDITOR_TOOL_NAME,
+ description=(
+ 'Custom editing tool for viewing, creating and editing files\n'
+ '* State is persistent across command calls and discussions with the user\n'
+ '* If `path` is a file, `view` displays the result of applying `cat -n`. '
+ 'If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep\n'
+ '* The `create` command cannot be used if the specified `path` already exists as a file\n'
+ '* If a `command` generates a long output, it will be truncated and marked with ``\n'
+ '* The `undo_edit` command will revert the last edit made to the file at `path`\n'
+ 'Notes for using the `str_replace` command:\n'
+ '* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. '
+ 'Be mindful of whitespaces!\n'
+ '* If the `old_str` parameter is not unique in the file, the replacement will not be performed. '
+ 'Make sure to include enough context in `old_str` to make it unique\n'
+ '* The `new_str` parameter should contain the edited lines that should replace the `old_str`'
+ ),
+ parameters={
+ 'type': 'object',
+ 'properties': {
+ 'command': {
+ 'type': 'string',
+ 'description': 'The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`.',
+ 'enum': ['view', 'create', 'str_replace', 'insert', 'undo_edit'],
+ },
+ 'path': {
+ 'type': 'string',
+ 'description': 'Absolute path to file or directory, e.g. `/testbed/file.py` or `/testbed`.',
+ },
+ 'file_text': {
+ 'type': 'string',
+ 'description': 'Required parameter of `create` command, with the content of the file to be created.',
+ },
+ 'old_str': {
+ 'type': 'string',
+ 'description': 'Required parameter of `str_replace` command containing the string in `path` to replace.',
+ },
+ 'new_str': {
+ 'type': 'string',
+ 'description': (
+ 'Optional parameter of `str_replace` command containing the new string (if not given, no string will be added). '
+ 'Required parameter of `insert` command containing the string to insert.'
+ ),
+ },
+ 'insert_line': {
+ 'type': 'integer',
+ 'description': 'Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`.',
+ },
+ 'view_range': {
+ 'type': 'array',
+ 'items': {'type': 'integer'},
+ 'description': (
+ 'Optional parameter of `view` command when `path` points to a file. '
+ 'If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, '
+ 'e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. '
+ 'Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file.'
+ ),
+ },
+ },
+ 'required': ['command', 'path'],
+ },
+ ),
+)
diff --git a/openhands/agenthub/swe_agent/tools/submit.py b/openhands/agenthub/swe_agent/tools/submit.py
new file mode 100644
index 000000000000..c9db1f8bcc09
--- /dev/null
+++ b/openhands/agenthub/swe_agent/tools/submit.py
@@ -0,0 +1,16 @@
+from litellm import ChatCompletionToolParam, ChatCompletionToolParamFunctionChunk
+
+SWE_AGENT_SUBMIT_TOOL_NAME = 'submit'
+
+SubmitTool: ChatCompletionToolParam = ChatCompletionToolParam(
+ type='function',
+ function=ChatCompletionToolParamFunctionChunk(
+ name=SWE_AGENT_SUBMIT_TOOL_NAME,
+ description='submits the current file',
+ parameters={
+ 'type': 'object',
+ 'properties': {},
+ 'required': [],
+ },
+ ),
+)