Skip to content

research_router

research_router

HTTP route: POST /api/research — agentic research over the knowledge store.

Drives :class:openjarvis.agents.research_loop.ResearchAgent and streams a custom SSE event schema back to the client:

  • search_call — about to invoke HybridSearch.search (with arguments)
  • search_result — search returned (num_hits, top_titles)
  • synthesis — final answer, emitted in word-window chunks for an incremental UX (the agent itself returns the full string in one shot; chunking happens in the router so we don't need to rewire the loop)
  • done — sentinel marking the end of the stream

Clarify is disabled for the web session — the agent's clarify_handler is overridden to return a fixed "no clarification available" string so the loop never blocks waiting for terminal stdin. Bringing real clarify back to the browser will require a two-step session protocol; that's a future endpoint, not this one.

Classes

Functions

research async

research(req: ResearchRequest) -> StreamingResponse

Run a research query and stream the agent's trace + synthesis via SSE.

Response is text/event-stream with one JSON event per frame. See the module docstring for the schema; a final {"type": "done"} always terminates the stream so clients can detect end-of-response without parsing the underlying [DONE] sentinel used by OpenAI-style routes.

Source code in src/openjarvis/server/research_router.py
@router.post("/research")
async def research(req: ResearchRequest) -> StreamingResponse:
    """Run a research query and stream the agent's trace + synthesis via SSE.

    Response is ``text/event-stream`` with one JSON event per frame. See the
    module docstring for the schema; a final ``{"type": "done"}`` always
    terminates the stream so clients can detect end-of-response without
    parsing the underlying ``[DONE]`` sentinel used by OpenAI-style routes.
    """
    if req.model and req.model != DEFAULT_PLANNER_MODEL:
        logger.info(
            "research: ignoring client model=%r; using DEFAULT_PLANNER_MODEL=%r",
            req.model,
            DEFAULT_PLANNER_MODEL,
        )
    return StreamingResponse(
        _stream_research(req.query, DEFAULT_PLANNER_MODEL),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )