Spaces:
Sleeping
Sleeping
File size: 2,227 Bytes
26d4279 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | """The agent graph.
prepare -> plan -+-(tool)-> act -+-(steps/time left)-> plan
| `-(budget spent)---> compose -> END
`-(answer)------------------------> compose -> END
Grading, query rewriting and groundedness verification are deliberately not here
yet — this shape is the loop that makes narrative questions answerable, and it is
worth proving on the real corpus before adding more LLM calls per turn.
"""
from __future__ import annotations
import logging
import threading
from langgraph.graph import END, START, StateGraph
from . import nodes
from .checkpoint import get_checkpointer
from .state import AgentState
logger = logging.getLogger("inkference.agent")
_graph = None
_graph_lock = threading.Lock()
def build_graph(store, index, checkpointer=None):
"""Compile the graph.
The corpus store and index are bound into the nodes that need them via closures,
so every node LangGraph sees is a plain (state) -> partial-state callable.
(They are bound as `doc_store`/`rag_index`, never `store`: LangGraph injects its
own BaseStore into a node parameter named `store` and would shadow ours.)
"""
builder = StateGraph(AgentState)
builder.add_node("prepare", lambda s: nodes.prepare(s, store, index))
builder.add_node("plan", nodes.plan)
builder.add_node("act", lambda s: nodes.act(s, store, index))
builder.add_node("compose", nodes.compose)
builder.add_edge(START, "prepare")
builder.add_edge("prepare", "plan")
builder.add_conditional_edges(
"plan", nodes.route_after_plan, {"act": "act", "compose": "compose"}
)
builder.add_conditional_edges(
"act", nodes.route_after_act, {"plan": "plan", "compose": "compose"}
)
builder.add_edge("compose", END)
return builder.compile(checkpointer=checkpointer or get_checkpointer())
def get_graph(store, index):
"""Lazy singleton — compiling is cheap but the checkpointer connection is not."""
global _graph
if _graph is None:
with _graph_lock:
if _graph is None:
_graph = build_graph(store, index)
logger.info("agent: graph compiled")
return _graph
|