update api for subagent protocol and delete hermes agent
This commit is contained in:
parent
ff1799cd98
commit
952b2e7d17
1150 changed files with 704 additions and 458893 deletions
|
|
@ -1,15 +1,60 @@
|
|||
import asyncio
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from typing import Any
|
||||
from typing import Any, Literal
|
||||
from urllib import error, request
|
||||
|
||||
from browser_use import Agent, Browser, ChatOpenAI
|
||||
from pydantic import BaseModel, Field, ValidationError, field_validator
|
||||
|
||||
|
||||
def _json_response(handler, status_code, payload):
|
||||
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
||||
class RunTaskRequest(BaseModel):
|
||||
"""RPC payload для запуска browser-use задачи."""
|
||||
|
||||
task: str = Field(..., min_length=1)
|
||||
|
||||
@field_validator("task")
|
||||
@classmethod
|
||||
def validate_task(cls, value: str) -> str:
|
||||
normalized = value.strip()
|
||||
if not normalized:
|
||||
raise ValueError("Field 'task' is required")
|
||||
return normalized
|
||||
|
||||
|
||||
class HistoryEvent(BaseModel):
|
||||
"""Нормализованное событие из history агента."""
|
||||
|
||||
step: int
|
||||
kind: str
|
||||
content: str | None = None
|
||||
data: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class RunTaskSuccessResponse(BaseModel):
|
||||
"""Успешный ответ RPC раннера."""
|
||||
|
||||
success: Literal[True] = True
|
||||
result: str | None = None
|
||||
history: list[HistoryEvent] = Field(default_factory=list)
|
||||
browser_view: str = ""
|
||||
|
||||
|
||||
class RunTaskErrorResponse(BaseModel):
|
||||
"""Ошибка выполнения задачи в RPC раннере."""
|
||||
|
||||
success: Literal[False] = False
|
||||
error: str
|
||||
|
||||
|
||||
def _json_response(handler, status_code: int, payload: dict[str, Any] | BaseModel) -> None:
|
||||
if isinstance(payload, BaseModel):
|
||||
body = payload.model_dump(mode="json")
|
||||
else:
|
||||
body = payload
|
||||
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
|
||||
handler.send_response(status_code)
|
||||
handler.send_header("Content-Type", "application/json; charset=utf-8")
|
||||
handler.send_header("Content-Length", str(len(data)))
|
||||
|
|
@ -17,7 +62,7 @@ def _json_response(handler, status_code, payload):
|
|||
handler.wfile.write(data)
|
||||
|
||||
|
||||
async def run_browser_task(task):
|
||||
async def run_browser_task(task: str) -> RunTaskSuccessResponse | RunTaskErrorResponse:
|
||||
cdp_url = os.getenv("BROWSER_CDP_URL", "http://127.0.0.1:9222")
|
||||
browser_view_url = os.getenv("BROWSER_VIEW_URL", "")
|
||||
|
||||
|
|
@ -34,17 +79,20 @@ async def run_browser_task(task):
|
|||
|
||||
try:
|
||||
history = await agent.run()
|
||||
return {
|
||||
"success": True,
|
||||
"result": history.final_result(),
|
||||
"history": _extract_history_events(history),
|
||||
"browser_view": browser_view_url,
|
||||
}
|
||||
return RunTaskSuccessResponse(
|
||||
result=history.final_result(),
|
||||
history=[HistoryEvent.model_validate(item) for item in _extract_history_events(history)],
|
||||
browser_view=browser_view_url,
|
||||
)
|
||||
except Exception as err:
|
||||
return {"success": False, "error": f"Browser automation failed: {err}"}
|
||||
return RunTaskErrorResponse(error=f"Browser automation failed: {err}")
|
||||
finally:
|
||||
try:
|
||||
await browser.close()
|
||||
close_method = getattr(browser, "close", None)
|
||||
if callable(close_method):
|
||||
close_result = close_method()
|
||||
if inspect.isawaitable(close_result):
|
||||
await close_result
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
@ -156,20 +204,19 @@ class BrowserUseRPCHandler(BaseHTTPRequestHandler):
|
|||
content_length = int(self.headers.get("Content-Length", "0"))
|
||||
raw = self.rfile.read(content_length)
|
||||
payload = json.loads(raw.decode("utf-8") if raw else "{}")
|
||||
task = payload.get("task", "")
|
||||
if not isinstance(task, str) or not task.strip():
|
||||
_json_response(self, 400, {"success": False, "error": "Field 'task' is required"})
|
||||
return
|
||||
request_model = RunTaskRequest.model_validate(payload)
|
||||
|
||||
result = asyncio.run(run_browser_task(task.strip()))
|
||||
code = 200 if result.get("success") else 500
|
||||
_json_response(self, code, result)
|
||||
result_model = asyncio.run(run_browser_task(request_model.task))
|
||||
code = 200 if result_model.success else 500
|
||||
_json_response(self, code, result_model)
|
||||
except ValidationError as err:
|
||||
_json_response(self, 400, RunTaskErrorResponse(error=f"Invalid request payload: {err.errors()}"))
|
||||
except json.JSONDecodeError:
|
||||
_json_response(self, 400, {"success": False, "error": "Invalid JSON payload"})
|
||||
_json_response(self, 400, RunTaskErrorResponse(error="Invalid JSON payload"))
|
||||
except error.URLError as err:
|
||||
_json_response(self, 503, {"success": False, "error": f"Transport error: {err}"})
|
||||
_json_response(self, 503, RunTaskErrorResponse(error=f"Transport error: {err}"))
|
||||
except Exception as err:
|
||||
_json_response(self, 500, {"success": False, "error": f"Internal error: {err}"})
|
||||
_json_response(self, 500, RunTaskErrorResponse(error=f"Internal error: {err}"))
|
||||
|
||||
def log_message(self, format_str, *args):
|
||||
return
|
||||
|
|
@ -178,7 +225,7 @@ class BrowserUseRPCHandler(BaseHTTPRequestHandler):
|
|||
def main():
|
||||
host = os.getenv("BROWSER_USE_RPC_HOST", "0.0.0.0")
|
||||
port = int(os.getenv("BROWSER_USE_RPC_PORT", "8787"))
|
||||
server = ThreadingHTTPServer((host, port), BrowserUseRPCHandler)
|
||||
server = ThreadingHTTPServer((host, port), BrowserUseRPCHandler) # type: ignore[arg-type]
|
||||
print(f"browser-use RPC listening on {host}:{port}")
|
||||
server.serve_forever()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue