deploy
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"language": "python",
|
||||
"name": "seleniumbase-website-scraper",
|
||||
"description": "SeleniumBase-style browser agent that turns observed website traffic into OpenAPI specs, coverage reports, samples, and a replay client.",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"entrypoint": {
|
||||
"module": "agent",
|
||||
"class_name": "BrowserToApiAgent",
|
||||
@@ -381,7 +381,7 @@
|
||||
"source": "python-a2a-pack",
|
||||
"project_manifest": {
|
||||
"name": "seleniumbase-website-scraper",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"entrypoint": "agent:BrowserToApiAgent"
|
||||
}
|
||||
}
|
||||
|
||||
2
a2a.yaml
2
a2a.yaml
@@ -1,5 +1,5 @@
|
||||
name: seleniumbase-website-scraper
|
||||
version: 0.2.7
|
||||
version: 0.2.8
|
||||
entrypoint: agent:BrowserToApiAgent
|
||||
expose:
|
||||
public: true
|
||||
|
||||
64
agent.py
64
agent.py
@@ -129,7 +129,7 @@ class BrowserToApiAgent(A2AAgent[BrowserToApiConfig, NoAuth]):
|
||||
"SeleniumBase-style browser agent that turns observed website traffic "
|
||||
"into OpenAPI specs, coverage reports, samples, and a replay client."
|
||||
)
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
|
||||
config_model = BrowserToApiConfig
|
||||
auth_model = NoAuth
|
||||
@@ -383,6 +383,11 @@ def capture_browser_traffic(
|
||||
|
||||
page.wait_for_timeout(wait_seconds * 1000)
|
||||
if ai_explore and ai_steps > 0 and llm_config:
|
||||
log.append(
|
||||
"ai: using ctx.llm "
|
||||
f"model={llm_config.get('model', 'unknown')} "
|
||||
f"source={llm_config.get('source', 'unknown')}"
|
||||
)
|
||||
_ai_explore_page(
|
||||
page,
|
||||
llm_config=llm_config,
|
||||
@@ -414,7 +419,13 @@ def _llm_config_from_ctx(ctx: RunContext[NoAuth]) -> dict[str, Any] | None:
|
||||
model = str(getattr(creds, "model", "") or "")
|
||||
if not (api_key and base_url and model):
|
||||
return None
|
||||
return {"api_key": api_key, "base_url": base_url, "model": model}
|
||||
return {
|
||||
"api_key": api_key,
|
||||
"base_url": base_url,
|
||||
"model": model,
|
||||
"source": str(getattr(creds, "source", "") or ""),
|
||||
"extra_body": dict(getattr(creds, "extra_body", None) or {}),
|
||||
}
|
||||
|
||||
|
||||
def _default_ai_goal(url: str) -> str:
|
||||
@@ -558,15 +569,48 @@ def _llm_choose_action(
|
||||
|
||||
def _chat_completion_json(llm_config: dict[str, Any], *, system: str, user: str) -> str:
|
||||
url = f"{llm_config['base_url'].rstrip('/')}/chat/completions"
|
||||
payload = {
|
||||
base_payload = {
|
||||
"model": llm_config["model"],
|
||||
"messages": [
|
||||
{"role": "system", "content": system},
|
||||
{"role": "user", "content": user},
|
||||
],
|
||||
"max_tokens": 320,
|
||||
"response_format": {"type": "json_object"},
|
||||
}
|
||||
extra_body = dict(llm_config.get("extra_body") or {})
|
||||
attempts = [
|
||||
{
|
||||
**base_payload,
|
||||
"max_completion_tokens": 1200,
|
||||
"reasoning_effort": "minimal",
|
||||
**extra_body,
|
||||
},
|
||||
{
|
||||
**base_payload,
|
||||
"max_tokens": 800,
|
||||
**extra_body,
|
||||
},
|
||||
{
|
||||
**base_payload,
|
||||
"max_completion_tokens": 1200,
|
||||
"response_format": {"type": "json_object"},
|
||||
**extra_body,
|
||||
},
|
||||
]
|
||||
last_error: Exception | None = None
|
||||
for payload in attempts:
|
||||
try:
|
||||
content = _post_chat_completion(url, llm_config["api_key"], payload)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
last_error = exc
|
||||
continue
|
||||
if content.strip():
|
||||
return content
|
||||
if last_error is not None:
|
||||
raise last_error
|
||||
return ""
|
||||
|
||||
|
||||
def _post_chat_completion(url: str, api_key: str, payload: dict[str, Any]) -> str:
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(payload).encode("utf-8"),
|
||||
@@ -586,7 +630,15 @@ def _chat_completion_json(llm_config: dict[str, Any], *, system: str, user: str)
|
||||
if not choices:
|
||||
return ""
|
||||
message = choices[0].get("message") if isinstance(choices[0], dict) else {}
|
||||
return str(message.get("content") or "")
|
||||
content = message.get("content")
|
||||
if isinstance(content, list):
|
||||
parts = [
|
||||
str(item.get("text") or item.get("content") or "")
|
||||
for item in content
|
||||
if isinstance(item, dict)
|
||||
]
|
||||
return "\n".join(part for part in parts if part)
|
||||
return str(content or "")
|
||||
|
||||
|
||||
def _apply_ai_action(
|
||||
|
||||
Reference in New Issue
Block a user