Concepts
In this sectionHow 10xGraph fits together: a Python graph engine, a generated production server and a typed TypeScript client, plus the core execution model.
- 20 pages
- About 87 min to read all
10xGraph is a Python framework that gives you the agent graph and the production server around it. You wire Python functions and LLMs into a graph and compile it once. The server layer is generated from that graph, and the runtime keeps runs correct under failure: tool calls are replay-safe, durable writes are versioned, and nodes and tools have timeouts.
Three layers
flowchart TB
subgraph "Python library (10xgraph)"
Graph[StateGraph · Agent · ToolNode]
Storage[Checkpointer · Memory Store · Media Store]
end
subgraph "API and CLI (10xgraph-api)"
CLI[10xgraph CLI]
API[FastAPI Server]
end
subgraph "TypeScript client (10xgraph-client)"
SDK[AgentFlowClient]
end
SDK -->|HTTP / SSE / WS| API
CLI --> API
API --> Graph
Graph <--> Storage
| Layer | Package | Role |
|---|---|---|
| Core library | 10xgraph |
Graph engine, agents, tools, state, storage |
| API / CLI | 10xgraph-api |
FastAPI server, 10xgraph CLI, auth, authorization, rate limits, publishers |
| TypeScript client | 10xgraph-client |
Typed HTTP wrapper for browser and Node.js |
Python code imports from tenxgraph: from tenxgraph.... The old agentflow module name remains a deprecated alias until 2.0.
The execution model
Four concepts form the foundation. Everything else builds on these.
Message
The unit of all communication. Every piece of information flowing through a graph is a Message.
from tenxgraph.core.state import Message
Message.text_message("Hello") # role="user" (default)
Message.text_message("Hello", role="user") # explicit user message
Message.text_message("Hi", role="assistant") # assistant message
Message.text_message("...", role="system") # system messageA message carries one or more content blocks: TextBlock, ToolCallBlock, ToolResultBlock, ImageBlock, AudioBlock, VideoBlock, DocumentBlock, ReasoningBlock, ErrorBlock.
AgentState
The moving container passed from node to node. AgentState has three built-in fields; subclass it and add your own on top.
| Field | Type | Purpose |
|---|---|---|
context |
list[Message] |
Live message list; appended to by every node via the add_messages reducer |
context_summary |
str | None |
Optional summary text written by SummaryContextManager when old messages are trimmed |
execution_meta |
ExecMeta |
Internal runtime bookkeeping (current node, step count, interrupt status), managed by the framework, not by user code |
from tenxgraph.core.state import AgentState
from pydantic import Field
class MyState(AgentState):
# context, context_summary, and execution_meta are already defined
user_name: str = "Guest"
data: dict = Field(default_factory=dict)Fields use annotated reducers to control how values merge across node executions. context is already wired this way in AgentState:
from typing import Annotated
from tenxgraph.core.state import add_messages, Message
context: Annotated[list[Message], add_messages] # appends new messages; deduplicates by idNode
Any Python function that receives AgentState and returns a message or a state update. Nodes are the unit of work.
async def my_node(state: MyState) -> Message:
return Message.text_message(f"Hello {state.user_name}", role="assistant")The graph injects state, config, and any Inject[T] dependencies automatically. You never construct a node manually.
Message → State → Node → State
Each node receives the full state, does its work, and returns a message or partial update. The graph merges it back via reducers, checkpoints, then routes to the next node.
flowchart LR
MSG["Message\n(role + content blocks)"] --> STATE["AgentState\n(context = Message list)"]
STATE --> NODE[Node function]
NODE --> NEW_MSG[New Message\nappended to context]
NEW_MSG --> STATE
Edge
Edges connect nodes. Two kinds:
graph.add_edge("A", "B") # static: always goes to B
graph.add_conditional_edges("A", route_fn) # dynamic: route_fn(state) returns node name
graph.add_conditional_edges("A", route_fn, { # mapped: route_fn returns a key
"tool": "TOOL",
"done": END,
})ToolNode and the ReAct loop
ToolNode is a built-in node that dispatches tool-call messages, runs the registered functions, and returns results. Pair it with an Agent to get a ReAct loop:
flowchart LR
START --> Agent
Agent -->|tool_call message| Tools[ToolNode]
Tools -->|tool_result message| Agent
Agent -->|no more tools| END
from tenxgraph.core.graph import ToolNode
tool_node = ToolNode([lookup_order, refund_order])Agent
Agent is a built-in node that wraps an LLM call. It handles provider selection, retries, structured output, reasoning, context trimming, and the tool loop.
from tenxgraph.core.graph import Agent
agent = Agent(
model="gpt-4o",
system_prompt=[{"role": "system", "content": "You are a helpful assistant."}],
tool_node=tool_node,
)Agent extends BaseAgent. You can subclass it to bring your own LLM or override the call logic entirely. See Extensibility.
Define → Compile → Run
START and END are special sentinel strings ("__start__" and "__end__") that mark the entry and exit points of the graph. Import them from tenxgraph.utils.
flowchart LR
subgraph Define
N1[add_node] --> N2[add_edge]
end
subgraph Compile
C[graph.compile\nwires DI, checkpointer, store]
end
subgraph Run
R1[invoke] & R2[stream] & R3[astream]
end
Define --> Compile --> Run
The snippet below is illustrative: route_fn, lookup_order, and refund_order are placeholders for your own routing logic and tool functions:
from tenxgraph.core.graph import StateGraph, Agent, ToolNode
from tenxgraph.utils import START, END
# route_fn receives state and returns "tool" or "done"
def route_fn(state: MyState) -> str:
last = state.context[-1] if state.context else None
if last and last.tools_calls: # tools_calls is the real attribute name
return "tool"
return "done"
graph = StateGraph()
graph.add_node("MAIN", agent) # agent defined above
graph.add_node("TOOL", tool_node) # tool_node defined above
graph.add_edge(START, "MAIN") # START → first node
graph.add_conditional_edges("MAIN", route_fn, {"tool": "TOOL", "done": END})
graph.add_edge("TOOL", "MAIN") # tool results loop back to agent
compiled = graph.compile()Execution: pass messages as the initial message list:
from tenxgraph.core.state import Message
input_state = {"messages": [Message.text_message("Where is order 1042?", role="user")]}
config = {"thread_id": "abc"}
# sync
result = compiled.invoke(input_state, config)
# async
result = await compiled.ainvoke(input_state, config)
# streaming (async)
async for chunk in compiled.astream(input_state, config):
print(chunk)Pass the same thread_id on the next call and the graph resumes where it left off. The checkpointer handles it, and it also records finished tool calls so a resumed run does not repeat them (see Replay-safe tools).
Prebuilt agents
For common patterns you don’t need to wire the graph manually. 10xGraph ships six prebuilt agents (ReactAgent, RAGAgent, PlanActReflectAgent, StructuredOutputAgent, SupervisorTeamAgent, and SwarmAgent), each exposing .compile() and returning a ready CompiledGraph. Full details and examples are on Agents and Tools.
from tenxgraph.prebuilt.agent import ReactAgent
# compile() returns a CompiledGraph, same API as the manual graph above
compiled = ReactAgent(
model="gpt-4o",
tools=[lookup_order, refund_order], # your tool functions
).compile()What’s next
| Page | What it covers |
|---|---|
| Agents and Tools | ReAct loop, tool authoring, prebuilt agents, callbacks, validators, Command |
| Memory | Three memory layers: running state, per-thread checkpointing, long-term vector store |
| Serving Agents | FastAPI server, CLI, auth, authorization, publishers, production runtime |
| Connecting Clients | TypeScript SDK, streaming, remote tools |
| Replay-safe tools | How a crashed run avoids executing a finished tool twice |
| Extensibility | Every ABC you can subclass |
| Quality & Observability | Unit testing, evaluation criteria, user simulation, observability hooks |
All pages in Concepts
Graphs and agents
- ArchitectureAn overview of how 10xGraph packages fit together and how requests flow from client to graph.2 min
- StateGraphHow StateGraph models an agent as nodes, edges and shared state, how compile() produces a runnable graph, and when to use invoke or stream.4 min
- Agents and ToolsHow Agent wraps a language model, how ToolNode dispatches tool calls, and all constructor options.6 min
- State and MessagesAgentState fields, Message structure, all content block types, ToolResult, and the add_messages reducer.5 min
- Prebuilt Agents and ToolsReady-made 10xGraph graph patterns, common tools, and handoff helpers.2 min
- Callbacks and CommandHook into invocations, validate inputs, recover from errors, and route from inside nodes.3 min
- Dependency InjectionInjectable parameters, injectq service containers, and how to wire custom services into 10xGraph nodes and tools.3 min
Memory and reliability
- Memory: hot and coldHow PgCheckpointer keeps thread state in a Redis hot cache and PostgreSQL durable history, what is written when, and how the long-term store fits in.5 min
- Replay-safe toolsReplay-safe tools let a resumed 10xGraph run skip tool calls that already finished, so a crash does not charge a card or send an email twice.4 min
- Checkpointing and ThreadsHow checkpointers save and restore conversation state across calls using thread IDs.7 min
- Memory and StoreHow long-term memory works in 10xGraph — memory_tool, retrieval modes, MemoryIntegration, and MemoryConfig.5 min
Serving and clients
- Serving AgentsHow 10xgraph.json wires a compiled graph to the API server, plus authentication, authorization, and publisher configuration for production.9 min
- Connecting ClientsHow the 10xgraph-client TypeScript SDK connects browser and Node.js apps to a 10xGraph API over REST, SSE, and WebSockets.4 min
- StreamingHow invoke, stream, and astream work; StreamChunk fields; ResponseGranularity; and how to consume SSE in TypeScript.4 min
- Remote ToolsHow 10xGraph lets a Python graph request tools that execute in a TypeScript client or browser.2 min
- Media and FilesHow to build multimodal messages with images, audio, video, and documents using MediaRef, content blocks, and media stores.7 min
Production
- Production RuntimeHow 10xGraph serves agents in production, including async execution, publisher adapters, and multi-worker deployments.3 min
- Security and ValidatorsHow input validators and PromptInjectionValidator work in 10xGraph, what the production template enables, and why they reduce prompt-injection risk.3 min
- Context, IDs, and Background TasksHow 10xGraph trims model context with MessageContextManager, generates thread and run IDs, and tracks background tasks with BackgroundTaskManager.2 min
- ExtensibilityThe abstract base classes — BaseCheckpointer, BaseStore, BaseAuth, BasePublisher, and more — used to extend 10xGraph's storage, auth, and event layers.7 min