mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
Rename service (#8791)
This commit is contained in:
@@ -266,5 +266,6 @@ class CodeActAgent(Agent):
|
||||
|
||||
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
|
||||
return codeact_function_calling.response_to_actions(
|
||||
response, mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
response,
|
||||
mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
)
|
||||
|
||||
@@ -5,14 +5,13 @@ This is similar to the functionality of `CodeActResponseParser`.
|
||||
|
||||
import json
|
||||
|
||||
|
||||
from litellm import (
|
||||
ChatCompletionToolParam,
|
||||
ModelResponse,
|
||||
)
|
||||
|
||||
from openhands.agenthub.codeact_agent.tools import FinishTool
|
||||
from openhands.agenthub.codeact_agent.function_calling import combine_thought
|
||||
from openhands.agenthub.codeact_agent.tools import FinishTool
|
||||
from openhands.agenthub.loc_agent.tools import (
|
||||
SearchEntityTool,
|
||||
SearchRepoTool,
|
||||
@@ -32,7 +31,8 @@ from openhands.events.tool import ToolCallMetadata
|
||||
|
||||
|
||||
def response_to_actions(
|
||||
response: ModelResponse, mcp_tool_names: list[str] | None = None,
|
||||
response: ModelResponse,
|
||||
mcp_tool_names: list[str] | None = None,
|
||||
) -> list[Action]:
|
||||
actions: list[Action] = []
|
||||
assert len(response.choices) == 1, 'Only one choice is supported for now'
|
||||
@@ -87,7 +87,7 @@ def response_to_actions(
|
||||
raise FunctionCallNotExistsError(
|
||||
f'Tool {tool_call.function.name} is not registered. (arguments: {arguments}). Please check the tool name and retry with an existing tool.'
|
||||
)
|
||||
|
||||
|
||||
# We only add thought to the first action
|
||||
if i == 0:
|
||||
action = combine_thought(action, thought)
|
||||
@@ -106,7 +106,7 @@ def response_to_actions(
|
||||
wait_for_response=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Add response id to actions
|
||||
# This will ensure we can match both actions without tool calls (e.g. MessageAction)
|
||||
# and actions with tool calls (e.g. CmdRunAction, IPythonRunCellAction, etc.)
|
||||
@@ -116,7 +116,7 @@ def response_to_actions(
|
||||
|
||||
assert len(actions) >= 1
|
||||
return actions
|
||||
|
||||
|
||||
|
||||
def get_tools() -> list[ChatCompletionToolParam]:
|
||||
tools = [FinishTool]
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from openhands.agenthub.codeact_agent import CodeActAgent
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import openhands.agenthub.loc_agent.function_calling as locagent_function_calling
|
||||
from openhands.agenthub.codeact_agent import CodeActAgent
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.llm.llm import LLM
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
from openhands.events.action import Action
|
||||
from openhands.llm.llm import ModelResponse
|
||||
|
||||
@@ -35,5 +34,6 @@ class LocAgent(CodeActAgent):
|
||||
|
||||
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
|
||||
return locagent_function_calling.response_to_actions(
|
||||
response, mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
response,
|
||||
mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
)
|
||||
|
||||
@@ -11,7 +11,9 @@ class MCPObservation(Observation):
|
||||
|
||||
observation: str = ObservationType.MCP
|
||||
name: str = '' # The name of the MCP tool that was called
|
||||
arguments: dict[str, Any] = field(default_factory=dict) # The arguments passed to the MCP tool
|
||||
arguments: dict[str, Any] = field(
|
||||
default_factory=dict
|
||||
) # The arguments passed to the MCP tool
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
|
||||
@@ -1 +1 @@
|
||||
{{ issue_comment }}
|
||||
{{ issue_comment }}
|
||||
|
||||
@@ -1 +1 @@
|
||||
Please fix issue number #{{ issue_number }} in your repository.
|
||||
Please fix issue number #{{ issue_number }} in your repository.
|
||||
|
||||
@@ -1 +1 @@
|
||||
{{ pr_comment }}
|
||||
{{ pr_comment }}
|
||||
|
||||
@@ -4,4 +4,4 @@ You SHOULD INCLUDE PROPER INDENTATION in your edit commands.{% if repo_instructi
|
||||
Some basic information about this repository:
|
||||
{{ repo_instruction }}{% endif %}
|
||||
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
|
||||
@@ -13,4 +13,4 @@ You SHOULD INCLUDE PROPER INDENTATION in your edit commands.{% if repo_instructi
|
||||
Some basic information about this repository:
|
||||
{{ repo_instruction }}{% endif %}
|
||||
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
|
||||
@@ -2,4 +2,4 @@ Please fix the following issue for the repository in /workspace.
|
||||
An environment has been set up for you to start working. You may assume all necessary tools are installed.
|
||||
|
||||
# Problem Statement
|
||||
{{ body }}
|
||||
{{ body }}
|
||||
|
||||
@@ -2,4 +2,4 @@ Please fix the following issue for the repository in /workspace.
|
||||
An environment has been set up for you to start working. You may assume all necessary tools are installed.
|
||||
|
||||
# Problem Statement
|
||||
{{ body }}
|
||||
{{ body }}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import io
|
||||
import base64
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def image_to_png_base64_url(
|
||||
image: np.ndarray | Image.Image, add_data_prefix: bool = False
|
||||
@@ -21,6 +23,7 @@ def image_to_png_base64_url(
|
||||
else f'{image_base64}'
|
||||
)
|
||||
|
||||
|
||||
def png_base64_url_to_image(png_base64_url: str) -> Image.Image:
|
||||
"""Convert a base64 encoded png image url to a PIL Image."""
|
||||
splited = png_base64_url.split(',')
|
||||
|
||||
@@ -12,13 +12,14 @@ from browsergym.utils.obs import flatten_dom_to_str, overlay_som
|
||||
|
||||
from openhands.core.exceptions import BrowserInitException
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.runtime.browser.base64 import image_to_png_base64_url
|
||||
from openhands.utils.shutdown_listener import should_continue, should_exit
|
||||
from openhands.utils.tenacity_stop import stop_if_should_exit
|
||||
from openhands.runtime.browser.base64 import image_to_png_base64_url
|
||||
|
||||
BROWSER_EVAL_GET_GOAL_ACTION = 'GET_EVAL_GOAL'
|
||||
BROWSER_EVAL_GET_REWARDS_ACTION = 'GET_EVAL_REWARDS'
|
||||
|
||||
|
||||
class BrowserEnv:
|
||||
def __init__(self, browsergym_eval_env: str | None = None):
|
||||
self.html_text_converter = self.get_html_text_converter()
|
||||
|
||||
@@ -6,22 +6,22 @@ from contextlib import asynccontextmanager
|
||||
@asynccontextmanager
|
||||
async def capture_logs(logger_name, level=logging.ERROR):
|
||||
logger = logging.getLogger(logger_name)
|
||||
|
||||
|
||||
# Store original handlers and level
|
||||
original_handlers = logger.handlers[:]
|
||||
original_level = logger.level
|
||||
|
||||
|
||||
# Set up capture
|
||||
log_capture = io.StringIO()
|
||||
handler = logging.StreamHandler(log_capture)
|
||||
handler.setLevel(level)
|
||||
|
||||
|
||||
logger.handlers = [handler]
|
||||
logger.setLevel(level)
|
||||
|
||||
|
||||
try:
|
||||
yield log_capture
|
||||
finally:
|
||||
# Restore original configuration
|
||||
logger.handlers = original_handlers
|
||||
logger.setLevel(original_level)
|
||||
logger.setLevel(original_level)
|
||||
|
||||
@@ -22,7 +22,7 @@ class ServerConfig(ServerConfigInterface):
|
||||
'openhands.storage.conversation.file_conversation_store.FileConversationStore'
|
||||
)
|
||||
conversation_manager_class: str = os.environ.get(
|
||||
"CONVERSATION_MANAGER_CLASS",
|
||||
'CONVERSATION_MANAGER_CLASS',
|
||||
'openhands.server.conversation_manager.standalone_conversation_manager.StandaloneConversationManager',
|
||||
)
|
||||
monitoring_listener_class: str = 'openhands.server.monitoring.MonitoringListener'
|
||||
|
||||
@@ -9,6 +9,7 @@ class AgentLoopInfo:
|
||||
"""
|
||||
Information about an agent loop - the URL on which to locate it and the event store
|
||||
"""
|
||||
|
||||
conversation_id: str
|
||||
url: str | None
|
||||
session_api_key: str | None
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import time
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
|
||||
from openhands.runtime.utils.system_stats import get_system_stats
|
||||
@@ -6,17 +7,16 @@ from openhands.runtime.utils.system_stats import get_system_stats
|
||||
start_time = time.time()
|
||||
last_execution_time = start_time
|
||||
|
||||
|
||||
def add_health_endpoints(app: FastAPI):
|
||||
@app.get('/alive')
|
||||
async def alive():
|
||||
return {'status': 'ok'}
|
||||
|
||||
|
||||
@app.get('/health')
|
||||
async def health() -> str:
|
||||
return 'OK'
|
||||
|
||||
|
||||
@app.get('/server_info')
|
||||
async def get_server_info():
|
||||
current_time = time.time()
|
||||
@@ -29,9 +29,8 @@ def add_health_endpoints(app: FastAPI):
|
||||
'resources': get_system_stats(),
|
||||
}
|
||||
return response
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
@app.middleware('http')
|
||||
async def update_last_execution_time(request: Request, call_next):
|
||||
global last_execution_time
|
||||
response = await call_next(request)
|
||||
|
||||
@@ -23,7 +23,7 @@ from openhands.server.data_models.conversation_info_result_set import (
|
||||
ConversationInfoResultSet,
|
||||
)
|
||||
from openhands.server.dependencies import get_dependencies
|
||||
from openhands.server.services.conversation import create_new_conversation
|
||||
from openhands.server.services.conversation_service import create_new_conversation
|
||||
from openhands.server.shared import (
|
||||
ConversationStoreImpl,
|
||||
config,
|
||||
@@ -103,8 +103,10 @@ async def new_conversation(
|
||||
if auth_type == AuthType.BEARER:
|
||||
conversation_trigger = ConversationTrigger.REMOTE_API_KEY
|
||||
|
||||
|
||||
if conversation_trigger == ConversationTrigger.REMOTE_API_KEY and not initial_user_msg:
|
||||
if (
|
||||
conversation_trigger == ConversationTrigger.REMOTE_API_KEY
|
||||
and not initial_user_msg
|
||||
):
|
||||
return JSONResponse(
|
||||
content={
|
||||
'status': 'error',
|
||||
@@ -193,19 +195,27 @@ async def search_conversations(
|
||||
conversation_ids = set(
|
||||
conversation.conversation_id for conversation in filtered_results
|
||||
)
|
||||
connection_ids_to_conversation_ids = await conversation_manager.get_connections(filter_to_sids=conversation_ids)
|
||||
agent_loop_info = await conversation_manager.get_agent_loop_info(filter_to_sids=conversation_ids)
|
||||
agent_loop_info_by_conversation_id = {info.conversation_id: info for info in agent_loop_info}
|
||||
connection_ids_to_conversation_ids = await conversation_manager.get_connections(
|
||||
filter_to_sids=conversation_ids
|
||||
)
|
||||
agent_loop_info = await conversation_manager.get_agent_loop_info(
|
||||
filter_to_sids=conversation_ids
|
||||
)
|
||||
agent_loop_info_by_conversation_id = {
|
||||
info.conversation_id: info for info in agent_loop_info
|
||||
}
|
||||
result = ConversationInfoResultSet(
|
||||
results=await wait_all(
|
||||
_get_conversation_info(
|
||||
conversation=conversation,
|
||||
num_connections=sum(
|
||||
1 for conversation_id in connection_ids_to_conversation_ids.values()
|
||||
1
|
||||
for conversation_id in connection_ids_to_conversation_ids.values()
|
||||
if conversation_id == conversation.conversation_id
|
||||
),
|
||||
agent_loop_info=agent_loop_info_by_conversation_id.get(conversation.conversation_id),
|
||||
|
||||
agent_loop_info=agent_loop_info_by_conversation_id.get(
|
||||
conversation.conversation_id
|
||||
),
|
||||
)
|
||||
for conversation in filtered_results
|
||||
),
|
||||
@@ -221,10 +231,16 @@ async def get_conversation(
|
||||
) -> ConversationInfo | None:
|
||||
try:
|
||||
metadata = await conversation_store.get_metadata(conversation_id)
|
||||
num_connections = len(await conversation_manager.get_connections(filter_to_sids={conversation_id}))
|
||||
agent_loop_infos = await conversation_manager.get_agent_loop_info(filter_to_sids={conversation_id})
|
||||
num_connections = len(
|
||||
await conversation_manager.get_connections(filter_to_sids={conversation_id})
|
||||
)
|
||||
agent_loop_infos = await conversation_manager.get_agent_loop_info(
|
||||
filter_to_sids={conversation_id}
|
||||
)
|
||||
agent_loop_info = agent_loop_infos[0] if agent_loop_infos else None
|
||||
conversation_info = await _get_conversation_info(metadata, num_connections, agent_loop_info)
|
||||
conversation_info = await _get_conversation_info(
|
||||
metadata, num_connections, agent_loop_info
|
||||
)
|
||||
return conversation_info
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
@@ -268,11 +284,15 @@ async def _get_conversation_info(
|
||||
selected_branch=conversation.selected_branch,
|
||||
git_provider=conversation.git_provider,
|
||||
status=(
|
||||
agent_loop_info.status if agent_loop_info else ConversationStatus.STOPPED
|
||||
agent_loop_info.status
|
||||
if agent_loop_info
|
||||
else ConversationStatus.STOPPED
|
||||
),
|
||||
num_connections=num_connections,
|
||||
url=agent_loop_info.url if agent_loop_info else None,
|
||||
session_api_key=agent_loop_info.session_api_key if agent_loop_info else None,
|
||||
session_api_key=agent_loop_info.session_api_key
|
||||
if agent_loop_info
|
||||
else None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
|
||||
@@ -218,7 +218,9 @@ async def create_custom_secret(
|
||||
) -> JSONResponse:
|
||||
try:
|
||||
existing_secrets = await secrets_store.load()
|
||||
custom_secrets = dict(existing_secrets.custom_secrets) if existing_secrets else {}
|
||||
custom_secrets = (
|
||||
dict(existing_secrets.custom_secrets) if existing_secrets else {}
|
||||
)
|
||||
|
||||
secret_name = incoming_secret.name
|
||||
secret_value = incoming_secret.value
|
||||
@@ -238,7 +240,9 @@ async def create_custom_secret(
|
||||
# Create a new UserSecrets that preserves provider tokens
|
||||
updated_user_secrets = UserSecrets(
|
||||
custom_secrets=custom_secrets,
|
||||
provider_tokens=existing_secrets.provider_tokens if existing_secrets else {},
|
||||
provider_tokens=existing_secrets.provider_tokens
|
||||
if existing_secrets
|
||||
else {},
|
||||
)
|
||||
|
||||
await secrets_store.store(updated_user_secrets)
|
||||
|
||||
@@ -7,8 +7,8 @@ from fastapi import Request
|
||||
from pydantic import SecretStr
|
||||
|
||||
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
|
||||
from openhands.server.shared import server_config
|
||||
from openhands.server.settings import Settings
|
||||
from openhands.server.shared import server_config
|
||||
from openhands.storage.data_models.user_secrets import UserSecrets
|
||||
from openhands.storage.secrets.secrets_store import SecretsStore
|
||||
from openhands.storage.settings.settings_store import SettingsStore
|
||||
|
||||
@@ -137,7 +137,6 @@ class UserSecrets(BaseModel):
|
||||
new_data['custom_secrets'] = secrets
|
||||
|
||||
return new_data
|
||||
|
||||
|
||||
def set_event_stream_secrets(self, event_stream: EventStream) -> None:
|
||||
"""
|
||||
@@ -158,10 +157,9 @@ class UserSecrets(BaseModel):
|
||||
|
||||
return secrets
|
||||
|
||||
|
||||
def get_custom_secrets_descriptions(self) -> dict[str, str]:
|
||||
secrets = {}
|
||||
for secret_name, secret in self.custom_secrets.items():
|
||||
secrets[secret_name] = secret.description
|
||||
|
||||
return secrets
|
||||
return secrets
|
||||
|
||||
Reference in New Issue
Block a user