On-demand Scout service (replaces the nightly aggregator): - FastAPI agent-mesh service; card name "matchmaking-service" (HTTP /a2a/tasks + Redis-stream worker matching the sibling-service pattern) - run_search: parallel multi-board sweep (Naukri/blackfalcondata + Foundit + LinkedIn), city-filtered at the board, cheapest-per-result first - per-board adapters + normalizers -> ScoutJob with rich read-only details and offsite apply links; recall mode + MVQ guard - result cache for dev replay ($0); per-board cost knobs; retry-on-5xx - research/: engine design, board cost economics, India-actor shortlist, POC
61 lines
2.0 KiB
Python
61 lines
2.0 KiB
Python
"""A2A task endpoint — the single HTTP entry the orchestrator calls per action.
|
|
|
|
Collects the messages a Session pushes and returns them; the orchestrator forwards
|
|
each to the frontend WebSocket. (Same proven adapter as the rest of the mesh.)
|
|
"""
|
|
import logging
|
|
from uuid import uuid4
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.a2a.auth import verify_a2a_key
|
|
from app.agent.session import MatchmakingAgentSession
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
|
|
class TaskRequest(BaseModel):
|
|
task_id: str = Field(default_factory=lambda: str(uuid4()))
|
|
action: str = ""
|
|
params: dict = Field(default_factory=dict)
|
|
user_id: str = ""
|
|
user_context: dict | None = None
|
|
session_start: bool = False
|
|
|
|
|
|
class TaskCollector:
|
|
"""WebSocket-shaped sink that captures pushed messages into a list."""
|
|
|
|
def __init__(self):
|
|
self.messages: list[dict] = []
|
|
|
|
async def accept(self):
|
|
pass
|
|
|
|
async def send_json(self, data: dict):
|
|
self.messages.append(data)
|
|
|
|
|
|
@router.post("/a2a/tasks")
|
|
async def handle_a2a_task(req: TaskRequest, _token: str = Depends(verify_a2a_key)):
|
|
collector = TaskCollector()
|
|
session = MatchmakingAgentSession(websocket=collector, user_id=req.user_id)
|
|
|
|
params = {**req.params}
|
|
if req.user_context:
|
|
params["user_context"] = req.user_context
|
|
|
|
try:
|
|
if req.session_start:
|
|
await session.on_session_start(params)
|
|
elif req.action:
|
|
await session.on_user_action(req.action, params)
|
|
else:
|
|
return {"task_id": req.task_id, "status": "error", "messages": [], "error": "No action or session_start"}
|
|
return {"task_id": req.task_id, "status": "completed", "messages": collector.messages}
|
|
except Exception as e: # noqa: BLE001
|
|
logger.error("A2A task %s failed: %s", req.task_id, e, exc_info=True)
|
|
return {"task_id": req.task_id, "status": "error", "messages": collector.messages, "error": str(e)}
|