deploy
This commit is contained in:
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
a2a.yaml
2
a2a.yaml
@@ -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
|
||||||
|
|||||||
64
agent.py
64
agent.py
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user