This commit is contained in:
a2a-platform
2026-06-27 17:04:31 +00:00
parent 116dc861bd
commit 94e5b42223
3 changed files with 61 additions and 9 deletions

View File

@@ -3,7 +3,7 @@
"language": "python", "language": "python",
"name": "seleniumbase-website-scraper", "name": "seleniumbase-website-scraper",
"description": "SeleniumBase-style browser agent that turns observed website traffic into OpenAPI specs, coverage reports, samples, and a replay client.", "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": { "entrypoint": {
"module": "agent", "module": "agent",
"class_name": "BrowserToApiAgent", "class_name": "BrowserToApiAgent",
@@ -381,7 +381,7 @@
"source": "python-a2a-pack", "source": "python-a2a-pack",
"project_manifest": { "project_manifest": {
"name": "seleniumbase-website-scraper", "name": "seleniumbase-website-scraper",
"version": "0.2.7", "version": "0.2.8",
"entrypoint": "agent:BrowserToApiAgent" "entrypoint": "agent:BrowserToApiAgent"
} }
} }

View File

@@ -1,5 +1,5 @@
name: seleniumbase-website-scraper name: seleniumbase-website-scraper
version: 0.2.7 version: 0.2.8
entrypoint: agent:BrowserToApiAgent entrypoint: agent:BrowserToApiAgent
expose: expose:
public: true public: true

View File

@@ -129,7 +129,7 @@ class BrowserToApiAgent(A2AAgent[BrowserToApiConfig, NoAuth]):
"SeleniumBase-style browser agent that turns observed website traffic " "SeleniumBase-style browser agent that turns observed website traffic "
"into OpenAPI specs, coverage reports, samples, and a replay client." "into OpenAPI specs, coverage reports, samples, and a replay client."
) )
version = "0.2.7" version = "0.2.8"
config_model = BrowserToApiConfig config_model = BrowserToApiConfig
auth_model = NoAuth auth_model = NoAuth
@@ -383,6 +383,11 @@ def capture_browser_traffic(
page.wait_for_timeout(wait_seconds * 1000) page.wait_for_timeout(wait_seconds * 1000)
if ai_explore and ai_steps > 0 and llm_config: 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( _ai_explore_page(
page, page,
llm_config=llm_config, 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 "") model = str(getattr(creds, "model", "") or "")
if not (api_key and base_url and model): if not (api_key and base_url and model):
return None 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: 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: def _chat_completion_json(llm_config: dict[str, Any], *, system: str, user: str) -> str:
url = f"{llm_config['base_url'].rstrip('/')}/chat/completions" url = f"{llm_config['base_url'].rstrip('/')}/chat/completions"
payload = { base_payload = {
"model": llm_config["model"], "model": llm_config["model"],
"messages": [ "messages": [
{"role": "system", "content": system}, {"role": "system", "content": system},
{"role": "user", "content": user}, {"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( req = urllib.request.Request(
url, url,
data=json.dumps(payload).encode("utf-8"), 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: if not choices:
return "" return ""
message = choices[0].get("message") if isinstance(choices[0], dict) else {} 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( def _apply_ai_action(