10xgraph-api 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. 10xgraph_api-0.7.0.dist-info/METADATA +618 -0
  2. 10xgraph_api-0.7.0.dist-info/RECORD +203 -0
  3. 10xgraph_api-0.7.0.dist-info/WHEEL +5 -0
  4. 10xgraph_api-0.7.0.dist-info/entry_points.txt +3 -0
  5. 10xgraph_api-0.7.0.dist-info/licenses/LICENSE +21 -0
  6. 10xgraph_api-0.7.0.dist-info/top_level.txt +2 -0
  7. agentflow_cli/__init__.py +69 -0
  8. tenxgraph_api/__init__.py +23 -0
  9. tenxgraph_api/cli/__init__.py +6 -0
  10. tenxgraph_api/cli/capabilities.py +127 -0
  11. tenxgraph_api/cli/commands/__init__.py +49 -0
  12. tenxgraph_api/cli/commands/api.py +262 -0
  13. tenxgraph_api/cli/commands/audit.py +213 -0
  14. tenxgraph_api/cli/commands/build.py +305 -0
  15. tenxgraph_api/cli/commands/config.py +71 -0
  16. tenxgraph_api/cli/commands/demo.py +93 -0
  17. tenxgraph_api/cli/commands/eval.py +909 -0
  18. tenxgraph_api/cli/commands/init.py +531 -0
  19. tenxgraph_api/cli/commands/skills.py +532 -0
  20. tenxgraph_api/cli/commands/test.py +113 -0
  21. tenxgraph_api/cli/commands/version.py +47 -0
  22. tenxgraph_api/cli/config_editor/__init__.py +15 -0
  23. tenxgraph_api/cli/config_editor/schema.py +566 -0
  24. tenxgraph_api/cli/config_editor/server.py +240 -0
  25. tenxgraph_api/cli/config_editor/static/app.css +2 -0
  26. tenxgraph_api/cli/config_editor/static/app.js +3 -0
  27. tenxgraph_api/cli/config_editor/static/favicon.svg +11 -0
  28. tenxgraph_api/cli/config_editor/static/index.html +20 -0
  29. tenxgraph_api/cli/config_editor/store.py +120 -0
  30. tenxgraph_api/cli/config_editor/validation.py +377 -0
  31. tenxgraph_api/cli/constants.py +112 -0
  32. tenxgraph_api/cli/context.py +46 -0
  33. tenxgraph_api/cli/core/__init__.py +0 -0
  34. tenxgraph_api/cli/core/animations.py +550 -0
  35. tenxgraph_api/cli/core/config.py +279 -0
  36. tenxgraph_api/cli/core/output.py +625 -0
  37. tenxgraph_api/cli/core/prompts.py +220 -0
  38. tenxgraph_api/cli/core/screen.py +287 -0
  39. tenxgraph_api/cli/core/steps.py +621 -0
  40. tenxgraph_api/cli/core/theme.py +242 -0
  41. tenxgraph_api/cli/core/validation.py +251 -0
  42. tenxgraph_api/cli/exceptions.py +129 -0
  43. tenxgraph_api/cli/logger.py +84 -0
  44. tenxgraph_api/cli/main.py +936 -0
  45. tenxgraph_api/cli/templates/__init__.py +0 -0
  46. tenxgraph_api/cli/templates/defaults.py +844 -0
  47. tenxgraph_api/cli/templates/dev/.env.example +1 -0
  48. tenxgraph_api/cli/templates/dev/10xgraph.json +16 -0
  49. tenxgraph_api/cli/templates/dev/graph/__init__.py +0 -0
  50. tenxgraph_api/cli/templates/dev/graph/agent.py +63 -0
  51. tenxgraph_api/cli/templates/prod/.env.example +75 -0
  52. tenxgraph_api/cli/templates/prod/.pre-commit-config.yaml +50 -0
  53. tenxgraph_api/cli/templates/prod/.python-version +1 -0
  54. tenxgraph_api/cli/templates/prod/10xgraph.json +42 -0
  55. tenxgraph_api/cli/templates/prod/auth/__init__.py +0 -0
  56. tenxgraph_api/cli/templates/prod/auth/agent_auth.py +46 -0
  57. tenxgraph_api/cli/templates/prod/evals/__init__.py +0 -0
  58. tenxgraph_api/cli/templates/prod/evals/user_simulator_eval.py +76 -0
  59. tenxgraph_api/cli/templates/prod/evals/weather_agents_eval.py +53 -0
  60. tenxgraph_api/cli/templates/prod/graph/__init__.py +0 -0
  61. tenxgraph_api/cli/templates/prod/graph/agent.py +50 -0
  62. tenxgraph_api/cli/templates/prod/graph/state.py +5 -0
  63. tenxgraph_api/cli/templates/prod/graph/thread_name_generator.py +7 -0
  64. tenxgraph_api/cli/templates/prod/graph/tools/__init__.py +0 -0
  65. tenxgraph_api/cli/templates/prod/graph/tools/weather_tool.py +43 -0
  66. tenxgraph_api/cli/templates/prod/graph/validators/__init__.py +0 -0
  67. tenxgraph_api/cli/templates/prod/graph/validators/lifecyle.py +107 -0
  68. tenxgraph_api/cli/templates/prod/graph/validators/manager.py +9 -0
  69. tenxgraph_api/cli/templates/prod/graph/validators/validators.py +21 -0
  70. tenxgraph_api/cli/templates/prod/pyproject.toml +79 -0
  71. tenxgraph_api/cli/templates/prod/tests/__init__.py +0 -0
  72. tenxgraph_api/cli/templates/prod/tests/conftest.py +6 -0
  73. tenxgraph_api/cli/templates/prod/tests/test_agent_eval.py +78 -0
  74. tenxgraph_api/cli/templates/prod/tests/test_catalog_tools.py +47 -0
  75. tenxgraph_api/cli/templates/prod/tests/test_graph_nodes.py +57 -0
  76. tenxgraph_api/cli/templates/skills/10xgraph/SKILL.md +92 -0
  77. tenxgraph_api/cli/templates/skills/10xgraph/references/agents-and-tools.md +389 -0
  78. tenxgraph_api/cli/templates/skills/10xgraph/references/api-client.md +61 -0
  79. tenxgraph_api/cli/templates/skills/10xgraph/references/api-configuration.md +75 -0
  80. tenxgraph_api/cli/templates/skills/10xgraph/references/api-settings-and-middleware.md +89 -0
  81. tenxgraph_api/cli/templates/skills/10xgraph/references/architecture.md +91 -0
  82. tenxgraph_api/cli/templates/skills/10xgraph/references/auth-and-authorization.md +149 -0
  83. tenxgraph_api/cli/templates/skills/10xgraph/references/callbacks-and-command.md +283 -0
  84. tenxgraph_api/cli/templates/skills/10xgraph/references/checkpointing-and-threads.md +63 -0
  85. tenxgraph_api/cli/templates/skills/10xgraph/references/cli-commands.md +73 -0
  86. tenxgraph_api/cli/templates/skills/10xgraph/references/client-auth-and-errors.md +94 -0
  87. tenxgraph_api/cli/templates/skills/10xgraph/references/client-messages-invoke-stream.md +115 -0
  88. tenxgraph_api/cli/templates/skills/10xgraph/references/client-threads-memory-files.md +80 -0
  89. tenxgraph_api/cli/templates/skills/10xgraph/references/context-id-background.md +80 -0
  90. tenxgraph_api/cli/templates/skills/10xgraph/references/dependency-injection.md +58 -0
  91. tenxgraph_api/cli/templates/skills/10xgraph/references/evaluation.md +500 -0
  92. tenxgraph_api/cli/templates/skills/10xgraph/references/id-and-thread-name-generators.md +69 -0
  93. tenxgraph_api/cli/templates/skills/10xgraph/references/media-and-files.md +66 -0
  94. tenxgraph_api/cli/templates/skills/10xgraph/references/memory-and-store.md +70 -0
  95. tenxgraph_api/cli/templates/skills/10xgraph/references/prebuilt-agents-and-tools.md +394 -0
  96. tenxgraph_api/cli/templates/skills/10xgraph/references/production-runtime.md +56 -0
  97. tenxgraph_api/cli/templates/skills/10xgraph/references/providers-and-adapters.md +89 -0
  98. tenxgraph_api/cli/templates/skills/10xgraph/references/publishers-and-runtime-protocols.md +84 -0
  99. tenxgraph_api/cli/templates/skills/10xgraph/references/rate-limiting.md +194 -0
  100. tenxgraph_api/cli/templates/skills/10xgraph/references/realtime.md +172 -0
  101. tenxgraph_api/cli/templates/skills/10xgraph/references/remote-tools.md +55 -0
  102. tenxgraph_api/cli/templates/skills/10xgraph/references/rest-api-and-errors.md +97 -0
  103. tenxgraph_api/cli/templates/skills/10xgraph/references/security-and-validators.md +86 -0
  104. tenxgraph_api/cli/templates/skills/10xgraph/references/state-and-messages.md +68 -0
  105. tenxgraph_api/cli/templates/skills/10xgraph/references/state-graph.md +232 -0
  106. tenxgraph_api/cli/templates/skills/10xgraph/references/stream-emitter.md +144 -0
  107. tenxgraph_api/cli/templates/skills/10xgraph/references/streaming.md +53 -0
  108. tenxgraph_api/cli/templates/skills/10xgraph/references/testing-and-evaluation.md +64 -0
  109. tenxgraph_api/cli/templates/skills/10xgraph/references/unit-testing.md +202 -0
  110. tenxgraph_api/cli/templates/skills/copilot/10xgraph.instructions.md +108 -0
  111. tenxgraph_api/py.typed +0 -0
  112. tenxgraph_api/src/__init__.py +0 -0
  113. tenxgraph_api/src/app/__init__.py +13 -0
  114. tenxgraph_api/src/app/core/__init__.py +15 -0
  115. tenxgraph_api/src/app/core/auth/__init__.py +0 -0
  116. tenxgraph_api/src/app/core/auth/auth_backend.py +38 -0
  117. tenxgraph_api/src/app/core/auth/authorization.py +379 -0
  118. tenxgraph_api/src/app/core/auth/base_auth.py +27 -0
  119. tenxgraph_api/src/app/core/auth/jwt_auth.py +122 -0
  120. tenxgraph_api/src/app/core/auth/ownership_resolver.py +170 -0
  121. tenxgraph_api/src/app/core/auth/permissions.py +354 -0
  122. tenxgraph_api/src/app/core/auth/request_config.py +130 -0
  123. tenxgraph_api/src/app/core/auth/route_guard.py +105 -0
  124. tenxgraph_api/src/app/core/config/__init__.py +0 -0
  125. tenxgraph_api/src/app/core/config/graph_config.py +532 -0
  126. tenxgraph_api/src/app/core/config/media_settings.py +72 -0
  127. tenxgraph_api/src/app/core/config/sentry_config.py +60 -0
  128. tenxgraph_api/src/app/core/config/settings.py +202 -0
  129. tenxgraph_api/src/app/core/config/setup_logs.py +65 -0
  130. tenxgraph_api/src/app/core/config/setup_middleware.py +483 -0
  131. tenxgraph_api/src/app/core/config/worker_middleware.py +99 -0
  132. tenxgraph_api/src/app/core/exceptions/__init__.py +17 -0
  133. tenxgraph_api/src/app/core/exceptions/general_exception.py +58 -0
  134. tenxgraph_api/src/app/core/exceptions/handle_errors.py +363 -0
  135. tenxgraph_api/src/app/core/exceptions/resources_exceptions.py +72 -0
  136. tenxgraph_api/src/app/core/exceptions/user_exception.py +58 -0
  137. tenxgraph_api/src/app/core/middleware/__init__.py +25 -0
  138. tenxgraph_api/src/app/core/middleware/rate_limit/__init__.py +15 -0
  139. tenxgraph_api/src/app/core/middleware/rate_limit/base.py +27 -0
  140. tenxgraph_api/src/app/core/middleware/rate_limit/factory.py +98 -0
  141. tenxgraph_api/src/app/core/middleware/rate_limit/keying.py +133 -0
  142. tenxgraph_api/src/app/core/middleware/rate_limit/memory.py +98 -0
  143. tenxgraph_api/src/app/core/middleware/rate_limit/middleware.py +90 -0
  144. tenxgraph_api/src/app/core/middleware/rate_limit/redis.py +115 -0
  145. tenxgraph_api/src/app/core/middleware/request_limits.py +154 -0
  146. tenxgraph_api/src/app/core/middleware/security_headers.py +239 -0
  147. tenxgraph_api/src/app/core/utils/__init__.py +14 -0
  148. tenxgraph_api/src/app/core/utils/log_sanitizer.py +216 -0
  149. tenxgraph_api/src/app/loader.py +516 -0
  150. tenxgraph_api/src/app/main.py +180 -0
  151. tenxgraph_api/src/app/routers/__init__.py +4 -0
  152. tenxgraph_api/src/app/routers/ag_ui/__init__.py +10 -0
  153. tenxgraph_api/src/app/routers/ag_ui/converter.py +195 -0
  154. tenxgraph_api/src/app/routers/ag_ui/event_mapper.py +353 -0
  155. tenxgraph_api/src/app/routers/ag_ui/router.py +90 -0
  156. tenxgraph_api/src/app/routers/ag_ui/service.py +193 -0
  157. tenxgraph_api/src/app/routers/checkpointer/__init__.py +1 -0
  158. tenxgraph_api/src/app/routers/checkpointer/router.py +482 -0
  159. tenxgraph_api/src/app/routers/checkpointer/schemas/__init__.py +1 -0
  160. tenxgraph_api/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +121 -0
  161. tenxgraph_api/src/app/routers/checkpointer/services/__init__.py +1 -0
  162. tenxgraph_api/src/app/routers/checkpointer/services/checkpointer_service.py +300 -0
  163. tenxgraph_api/src/app/routers/evals/__init__.py +6 -0
  164. tenxgraph_api/src/app/routers/evals/router.py +38 -0
  165. tenxgraph_api/src/app/routers/evals/services/__init__.py +1 -0
  166. tenxgraph_api/src/app/routers/evals/services/eval_report_service.py +354 -0
  167. tenxgraph_api/src/app/routers/graph/__init__.py +6 -0
  168. tenxgraph_api/src/app/routers/graph/realtime_guard.py +161 -0
  169. tenxgraph_api/src/app/routers/graph/router.py +821 -0
  170. tenxgraph_api/src/app/routers/graph/schemas/__init__.py +1 -0
  171. tenxgraph_api/src/app/routers/graph/schemas/graph_schemas.py +386 -0
  172. tenxgraph_api/src/app/routers/graph/services/__init__.py +1 -0
  173. tenxgraph_api/src/app/routers/graph/services/graph_service.py +1219 -0
  174. tenxgraph_api/src/app/routers/graph/services/multimodal_preprocessor.py +164 -0
  175. tenxgraph_api/src/app/routers/media/__init__.py +506 -0
  176. tenxgraph_api/src/app/routers/media/router.py +242 -0
  177. tenxgraph_api/src/app/routers/media/schemas.py +39 -0
  178. tenxgraph_api/src/app/routers/ping/__init__.py +4 -0
  179. tenxgraph_api/src/app/routers/ping/router.py +28 -0
  180. tenxgraph_api/src/app/routers/setup_router.py +60 -0
  181. tenxgraph_api/src/app/routers/store/__init__.py +4 -0
  182. tenxgraph_api/src/app/routers/store/router.py +200 -0
  183. tenxgraph_api/src/app/routers/store/schemas/__init__.py +28 -0
  184. tenxgraph_api/src/app/routers/store/schemas/store_schemas.py +200 -0
  185. tenxgraph_api/src/app/routers/store/services/__init__.py +4 -0
  186. tenxgraph_api/src/app/routers/store/services/store_service.py +203 -0
  187. tenxgraph_api/src/app/tasks/__init__.py +0 -0
  188. tenxgraph_api/src/app/tasks/user_tasks.py +38 -0
  189. tenxgraph_api/src/app/utils/__init__.py +12 -0
  190. tenxgraph_api/src/app/utils/callable_helper.py +26 -0
  191. tenxgraph_api/src/app/utils/media/__init__.py +15 -0
  192. tenxgraph_api/src/app/utils/media/extractor.py +172 -0
  193. tenxgraph_api/src/app/utils/media/pipeline.py +83 -0
  194. tenxgraph_api/src/app/utils/parse_output.py +17 -0
  195. tenxgraph_api/src/app/utils/response_helper.py +129 -0
  196. tenxgraph_api/src/app/utils/schemas/__init__.py +11 -0
  197. tenxgraph_api/src/app/utils/schemas/output_schemas.py +28 -0
  198. tenxgraph_api/src/app/utils/schemas/user_schemas.py +13 -0
  199. tenxgraph_api/src/app/utils/snowflake_id_generator.py +90 -0
  200. tenxgraph_api/src/app/utils/swagger_helper.py +160 -0
  201. tenxgraph_api/src/app/utils/telemetry_store.py +123 -0
  202. tenxgraph_api/src/app/utils/thread_name_generator.py +304 -0
  203. tenxgraph_api/src/app/worker.py +91 -0
@@ -0,0 +1,232 @@
1
+ # StateGraph and Nodes
2
+
3
+ Use this when adding workflows, nodes, edges, interrupts, graph compilation, or execution behavior.
4
+
5
+ ---
6
+
7
+ ## Core model
8
+
9
+ `StateGraph` is the workflow engine. A graph describes a directed set of nodes and edges executed over an `AgentState` subclass. The compiled graph manages state loading, routing, checkpointing, streaming, interrupts, recursion limits, and shutdown automatically.
10
+
11
+ ---
12
+
13
+ ## Creating a StateGraph
14
+
15
+ ### Minimal
16
+
17
+ ```python
18
+ from tenxgraph.core.graph import StateGraph
19
+
20
+ graph = StateGraph() # defaults to AgentState
21
+ ```
22
+
23
+ ### Custom state class
24
+
25
+ ```python
26
+ from tenxgraph.core.state import AgentState
27
+
28
+ class OrderState(AgentState):
29
+ order_id: str = ""
30
+ total: float = 0.0
31
+
32
+ graph = StateGraph(OrderState) # pass class or instance — both work
33
+ ```
34
+
35
+ ### Full constructor
36
+
37
+ ```python
38
+ StateGraph(
39
+ state: StateT | None = None,
40
+ context_manager: BaseContextManager | None = None,
41
+ publisher: BasePublisher | None = None,
42
+ id_generator: BaseIDGenerator = DefaultIDGenerator(),
43
+ container: InjectQ | None = None,
44
+ )
45
+ ```
46
+
47
+ | Parameter | Description |
48
+ |---|---|
49
+ | `state` | State class or instance. Defaults to `AgentState()` |
50
+ | `context_manager` | Optional cross-node state transformer (trim context, summarise, etc.) |
51
+ | `publisher` | Optional publisher for emitting lifecycle events (Kafka, Redis Pub/Sub, RabbitMQ) |
52
+ | `id_generator` | Strategy for generating message and run IDs |
53
+ | `container` | `InjectQ` container for DI. Defaults to the global singleton |
54
+
55
+ ---
56
+
57
+ ## Adding nodes
58
+
59
+ A node is any callable that receives `AgentState` (or subclass) and returns a `Message`, `ToolResult`, or state `dict`.
60
+
61
+ ```python
62
+ graph.add_node("MAIN", agent) # explicit name
63
+ graph.add_node("TOOL", tool_node)
64
+ graph.add_node(process) # auto-name: "process"
65
+ graph.override_node("MAIN", mock) # replace existing node — useful in tests
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Edges
71
+
72
+ ```python
73
+ graph.add_edge("TOOL", "MAIN") # static: always TOOL → MAIN
74
+ graph.set_entry_point("MAIN") # shorthand for add_edge(START, "MAIN")
75
+ ```
76
+
77
+ ### Conditional edges
78
+
79
+ The routing function receives `AgentState` and returns a string key. `path_map` maps that key to a node name:
80
+
81
+ ```python
82
+ from tenxgraph.utils.constants import END
83
+
84
+ def route(state: AgentState) -> str:
85
+ last = state.context[-1]
86
+ if hasattr(last, "tools_calls") and last.tools_calls and last.role == "assistant":
87
+ return "TOOL"
88
+ if last.role == "tool":
89
+ return "MAIN"
90
+ return END
91
+
92
+ graph.add_conditional_edges("MAIN", route, {"TOOL": "TOOL", END: END})
93
+ # If path_map is omitted, the function must return the destination node name directly.
94
+ ```
95
+
96
+ Use `Command(goto=...)` for runtime jumps only when static or conditional edges are insufficient. See `callbacks-and-command.md`.
97
+
98
+ ---
99
+
100
+ ## Compiling
101
+
102
+ ```python
103
+ from tenxgraph.utils import CallbackManager
104
+
105
+ app = graph.compile(
106
+ checkpointer=checkpointer, # BaseCheckpointer | None
107
+ store=store, # BaseStore | None
108
+ media_store=media_store, # BaseMediaStore | None
109
+ interrupt_before=["VALIDATOR"], # pause before these node names
110
+ interrupt_after=["TOOL"], # pause after these node names
111
+ callback_manager=CallbackManager(),
112
+ shutdown_timeout=30.0,
113
+ )
114
+ ```
115
+
116
+ | Parameter | Type | Description |
117
+ |---|---|---|
118
+ | `checkpointer` | `BaseCheckpointer \| None` | State persistence. Defaults to `InMemoryCheckpointer` if not provided |
119
+ | `store` | `BaseStore \| None` | Long-term cross-thread memory store |
120
+ | `media_store` | `BaseMediaStore \| None` | Media file storage for multimodal content |
121
+ | `interrupt_before` | `list[str] \| None` | Pause before these node names |
122
+ | `interrupt_after` | `list[str] \| None` | Pause after these node names |
123
+ | `callback_manager` | `CallbackManager` | Hooks for observability and lifecycle events |
124
+ | `shutdown_timeout` | `float` | Seconds to wait for graceful shutdown (default `30.0`) |
125
+
126
+ `compile()` raises `GraphError` if no entry point is set or if interrupt nodes do not exist.
127
+
128
+ ---
129
+
130
+ ## Invoking
131
+
132
+ ```python
133
+ from tenxgraph.core.state import Message
134
+ from tenxgraph.utils import ResponseGranularity
135
+
136
+ result = app.invoke(
137
+ {"messages": [Message.text_message("Hello!")]},
138
+ config={"thread_id": "t1", "recursion_limit": 25},
139
+ response_granularity=ResponseGranularity.LOW,
140
+ )
141
+ messages = result["messages"]
142
+ ```
143
+
144
+ `ainvoke()` is the async equivalent. Returns the full state dict.
145
+
146
+ ### Lifecycle / async context manager
147
+
148
+ `CompiledGraph` supports `async with`; `aclose()` runs on exit even if the body raises, and is
149
+ idempotent (a second call returns `{"status": "already_closed"}`).
150
+
151
+ ```python
152
+ async with await build_and_compile_graph() as graph:
153
+ await graph.ainvoke(input_data)
154
+ # aclose() runs automatically here
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Streaming
160
+
161
+ ```python
162
+ from tenxgraph.core.state.stream_chunks import StreamEvent
163
+
164
+ for chunk in app.stream(
165
+ {"messages": [Message.text_message("Hello!")]},
166
+ config={"thread_id": "t2"},
167
+ ):
168
+ if chunk.event == StreamEvent.MESSAGE and chunk.message:
169
+ print(chunk.message.text())
170
+ ```
171
+
172
+ `astream()` is the async equivalent. See `streaming.md` for full details on `StreamChunk` fields and `StreamEvent` values.
173
+
174
+ ### Realtime (audio) graphs
175
+
176
+ A graph rooted at a `LiveAgent` is driven by `arealtime(input_queue, config=None, state=None)` (async
177
+ generator of `RealtimeEvent`s) or the sync `realtime(...)` wrapper, not `invoke` / `stream`. The
178
+ forcing rule is mutual: a graph with a `LiveAgent` must use `arealtime()` (`invoke` / `stream`
179
+ raise), and `arealtime()` requires exactly one `LiveAgent` (ordinary graphs raise). See
180
+ `realtime.md`.
181
+
182
+ ---
183
+
184
+ ## Config keys
185
+
186
+ Passed in the `config` dict to `invoke` / `stream`:
187
+
188
+ | Key | Type | Description |
189
+ |---|---|---|
190
+ | `thread_id` | `str` | Conversation thread identifier. Required for checkpointing |
191
+ | `user_id` | `str` | Optional user identifier; injected into tool `config` param |
192
+ | `run_id` | `str` | Optional run identifier; auto-generated if omitted |
193
+ | `recursion_limit` | `int` | Max node executions before stopping (default `25`) |
194
+
195
+ ---
196
+
197
+ ## Interrupts
198
+
199
+ Compile with `interrupt_before` or `interrupt_after`. When execution reaches an interrupt node, the graph pauses and persists state. Resume by calling `invoke` again with the same `thread_id` and an empty input:
200
+
201
+ ```python
202
+ app = graph.compile(checkpointer=checkpointer, interrupt_before=["VALIDATOR"])
203
+
204
+ # First call — pauses before VALIDATOR
205
+ app.invoke({"messages": [Message.text_message("Start")]}, config={"thread_id": "t3"})
206
+
207
+ # Resume after review
208
+ app.invoke({}, config={"thread_id": "t3"})
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Execution lifecycle
214
+
215
+ ```
216
+ app.invoke → Load or create AgentState (from checkpointer if thread_id exists)
217
+ → Run entry node
218
+ → Evaluate conditional or static edge
219
+ → Run next node
220
+ → Check for interrupt → pause and save state (if configured)
221
+ → Repeat until END
222
+ → Save final state → Return result dict
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Source map
228
+
229
+ - StateGraph: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/state_graph.py
230
+ - CompiledGraph: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/compiled_graph.py
231
+ - Constants (END, START): https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/utils/constants.py
232
+ - How-to (build a graph): https://10xgraph.com/how-to/python/build-a-graph
@@ -0,0 +1,144 @@
1
+ # StreamEmitter
2
+
3
+ Emit live progress, errors, and status updates from tools during streaming execution.
4
+
5
+ ## Overview
6
+
7
+ `StreamEmitter` allows tools to send **live progress updates** back to the caller during `app.stream(...)` / `app.astream(...)` execution. Tools can report intermediate steps, retries, and errors in real-time without using an external publisher.
8
+
9
+ ## Key Points
10
+
11
+ - **Streaming only**: Automatically injected during `app.stream()` / `app.astream()`, not during `invoke()` / `ainvoke()`
12
+ - **Optional parameter**: Declare as `emit: StreamEmitter | None = None` in tool functions
13
+ - **Thread-safe**: Works with both sync and async tools
14
+ - **Built-in**: Uses the same streaming pipeline; no external publisher required
15
+
16
+ ## Common Methods
17
+
18
+ ### `progress(message: str, data: dict | None = None)`
19
+
20
+ Emit a progress update showing intermediate steps or status changes.
21
+
22
+ ```python
23
+ if emit:
24
+ emit.progress("Fetching data...", data={"attempt": 1, "max_attempts": 3})
25
+ ```
26
+
27
+ ### `error(message: str, data: dict | None = None)`
28
+
29
+ Emit an error update (informational; doesn't interrupt execution).
30
+
31
+ ```python
32
+ if emit:
33
+ emit.error("API timeout, using cache", data={"retry_count": 3})
34
+ ```
35
+
36
+ ### `message(message: str, data: dict | None = None)`
37
+
38
+ Emit a plain informational message.
39
+
40
+ ```python
41
+ if emit:
42
+ emit.message("Processing complete", data={"items_processed": 1000})
43
+ ```
44
+
45
+ ### `update(data: dict)`
46
+
47
+ Emit a generic data update without a message.
48
+
49
+ ```python
50
+ if emit:
51
+ emit.update({
52
+ "status": "batch_progress",
53
+ "processed": 50,
54
+ "total": 100,
55
+ "percentage": 50.0,
56
+ })
57
+ ```
58
+
59
+ ## Usage Pattern
60
+
61
+ ```python
62
+ from tenxgraph.core.state.stream_emitter import StreamEmitter
63
+
64
+ def my_tool(
65
+ param: str,
66
+ emit: StreamEmitter | None = None,
67
+ ) -> str:
68
+ """Tool that reports progress during streaming."""
69
+ if emit:
70
+ emit.progress("Starting work...")
71
+
72
+ # ... do work ...
73
+
74
+ if emit:
75
+ emit.progress("Finalizing...", data={"step": 2})
76
+
77
+ return "result"
78
+ ```
79
+
80
+ ## When to Use
81
+
82
+ ✅ **Use for:**
83
+ - Long-running operations (API calls, file processing)
84
+ - Retries with multiple attempts
85
+ - Multi-step processes
86
+ - Batch processing with progress tracking
87
+
88
+ ❌ **Don't use for:**
89
+ - Fast operations (<100ms)
90
+ - Non-streaming paths (emit will be None anyway)
91
+ - Critical control flow (always return a result regardless)
92
+
93
+ ## Behavior
94
+
95
+ | Execution Mode | Emit Parameter | Output |
96
+ |---|---|---|
97
+ | `app.stream()` | `StreamEmitter` | Progress updates in stream |
98
+ | `app.astream()` | `StreamEmitter` | Progress updates in stream |
99
+ | `app.invoke()` | `None` | No progress updates |
100
+ | `app.ainvoke()` | `None` | No progress updates |
101
+
102
+ ## Stream Output
103
+
104
+ Emitted chunks appear in the stream output with structure:
105
+
106
+ ```python
107
+ {
108
+ "event": "message" | "error" | "update",
109
+ "data": {
110
+ "status": "tool_progress" | "tool_failed" | "tool_message" | ...,
111
+ "tool_name": "my_tool",
112
+ "tool_call_id": "call_abc123",
113
+ "node": "TOOL",
114
+ "message": "...",
115
+ "thread_id": "...",
116
+ "run_id": "...",
117
+ # ... plus any extra data passed
118
+ },
119
+ }
120
+ ```
121
+
122
+ ## Performance Tips
123
+
124
+ - Emit at meaningful intervals, not every iteration
125
+ - For batch work: emit every N items, not on every item
126
+ - Avoid thousands of updates per second
127
+
128
+ ```python
129
+ # ❌ Too frequent
130
+ for item in items:
131
+ if emit:
132
+ emit.progress(f"Processing {item}")
133
+
134
+ # ✅ Batched
135
+ for i, item in enumerate(items):
136
+ if (i + 1) % 100 == 0 and emit:
137
+ emit.progress(f"Processed {i + 1} of {len(items)}")
138
+ ```
139
+
140
+ ## See Also
141
+
142
+ - [Tools](agents-and-tools.md) — Defining and registering tools
143
+ - [Streaming](streaming.md) — Overview of streaming chunks
144
+ - [Dependency Injection](dependency-injection.md) — How parameters like `emit` are injected
@@ -0,0 +1,53 @@
1
+ # Streaming
2
+
3
+ Use this when working on incremental output, SSE, cancellation, response granularity, or TypeScript async iteration.
4
+
5
+ ## Modes
6
+
7
+ - `invoke`: run to completion and return a final result dict.
8
+ - `ainvoke`: async invoke.
9
+ - `stream`: sync generator yielding `StreamChunk`.
10
+ - `astream`: async generator yielding `StreamChunk`.
11
+
12
+ Use streaming for chat UIs and any caller that needs partial output.
13
+
14
+ ## StreamChunk
15
+
16
+ Important fields:
17
+
18
+ - `event`: `"message"`, `"state"`, `"error"`, or `"updates"`.
19
+ - `message`: populated for message events.
20
+ - `state`: populated for state events.
21
+ - `data`: populated for errors/updates.
22
+ - `thread_id`, `run_id`, `metadata`, `timestamp`.
23
+
24
+ ## ResponseGranularity
25
+
26
+ - `LOW`: latest messages only; good default for UI streaming.
27
+ - `PARTIAL`: context, summary, and latest messages.
28
+ - `FULL`: complete state and messages.
29
+
30
+ Use lower granularity for client performance and privacy unless full state is needed.
31
+
32
+ ## Stop
33
+
34
+ Use `app.stop()` / `app.astop()` or `POST /v1/graph/stop` to request cancellation for a thread. The graph exits cleanly after node boundaries.
35
+
36
+ ## REST and Client
37
+
38
+ REST:
39
+
40
+ - `POST /v1/graph/stream` returns server-sent events.
41
+ - Each SSE data payload is a serialized `StreamChunk`.
42
+
43
+ TypeScript:
44
+
45
+ - `AgentFlowClient.stream(messages, options)` returns an async generator.
46
+ - Handle `chunk.event === "message"` for incremental text.
47
+
48
+ ## Source Map
49
+
50
+ - Stream chunks: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/state/stream_chunks.py
51
+ - Compiled graph streaming: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/compiled_graph.py
52
+ - Graph API router/service: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/src/app/routers/graph
53
+ - TS stream endpoint: https://github.com/10xHub/agentflow-client/blob/main/src/endpoints/stream.ts
@@ -0,0 +1,64 @@
1
+ # Testing and Evaluation
2
+
3
+ Use this when writing tests, mocks, eval datasets, automated quality checks, or trajectory assertions.
4
+
5
+ ## Testing Helpers
6
+
7
+ Import from `tenxgraph.qa.testing` or `tenxgraph.qa`.
8
+
9
+ Key helpers:
10
+
11
+ - `TestAgent`: model-free agent double that returns predefined responses and can simulate tool calls.
12
+ - `QuickTest`: quick single-turn, multi-turn, tool, and custom graph tests.
13
+ - `TestResult`: fluent assertions for output text, tool calls, message counts, and errors.
14
+ - `TestContext`: context manager for creating graphs, agents, stores, and mocks.
15
+ - `MockToolRegistry`: register sync/async mock tools and assert calls.
16
+ - `MockMCPClient`: mock MCP tool listing/calls.
17
+ - `MockComposioAdapter` and `MockLangChainAdapter`: test third-party tool adapter behavior.
18
+ - `InMemoryStore`: deterministic memory store for tests.
19
+
20
+ Use these instead of real model/provider calls in unit tests.
21
+
22
+ ## Evaluation Framework
23
+
24
+ Import from `tenxgraph.qa.evaluation` or `tenxgraph.qa`.
25
+
26
+ Core dataset/results types:
27
+
28
+ - `EvalCase`, `EvalSet`, `EvalSetBuilder`
29
+ - `ToolCall`, `Invocation`, `TrajectoryStep`, `StepType`
30
+ - `EvalConfig`, `CriterionConfig`, `MatchType`, `Rubric`
31
+ - `EvalReport`, `EvalCaseResult`, `CriterionResult`
32
+
33
+ Runner and shortcuts:
34
+
35
+ - `AgentEvaluator`
36
+ - `QuickEval`
37
+ - `run_eval`
38
+ - `create_eval_app`
39
+ - `create_simple_eval_set`
40
+ - `eval_test`
41
+ - `parametrize_eval_cases`
42
+
43
+ Criteria/reporters:
44
+
45
+ - Tool, trajectory, node-order, response, exact-match, keyword, ROUGE, rubric, safety, hallucination, factual accuracy, LLM judge, and simulation criteria.
46
+ - Console, JSON, HTML, JUnit XML, and reporter manager outputs.
47
+
48
+ ## Trajectory Collection
49
+
50
+ Use `TrajectoryCollector` plus `make_trajectory_callback` to record node/tool execution through the callback system. Compile the graph once with the callback manager and reuse it for eval runs.
51
+
52
+ ## Rules
53
+
54
+ - Keep unit tests model-free with `TestAgent` and mocks.
55
+ - Use evals for behavior quality, trajectory matching, safety, and regression checks.
56
+ - Avoid mixing live providers into fast unit tests; isolate them as integration tests.
57
+ - For trajectory evals, compile once with the collector callback to avoid losing callback state.
58
+
59
+ ## Source Map
60
+
61
+ - Testing package: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/qa/testing
62
+ - Evaluation package: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/qa/evaluation
63
+ - Public exports: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/qa/__init__.py
64
+ - Docs: https://10xgraph.com/
@@ -0,0 +1,202 @@
1
+ # Unit Testing
2
+
3
+ Use this when writing fast, deterministic tests for 10xGraph agent graphs without making real LLM API calls. All helpers live in `tenxgraph.qa.testing`.
4
+
5
+ ## TestAgent
6
+
7
+ Drop-in replacement for `Agent`. Returns predefined responses from a list, cycling when called more than once. Never calls an LLM.
8
+
9
+ ```python
10
+ from tenxgraph.qa.testing import TestAgent
11
+
12
+ agent = TestAgent(
13
+ model="test-model", # compatibility only; ignored
14
+ responses=["Response 1", "Response 2"], # cycles on repeated calls
15
+ tools=["get_weather"], # optional; enables simulate_tool_calls
16
+ simulate_tool_calls=False, # True → first call returns tool-call message
17
+ )
18
+ ```
19
+
20
+ Swap into an existing graph:
21
+
22
+ ```python
23
+ test_agent = TestAgent(responses=["Mocked"])
24
+ graph.override_node("MAIN", test_agent)
25
+ app = graph.compile()
26
+ ```
27
+
28
+ Build a minimal graph inline:
29
+
30
+ ```python
31
+ from tenxgraph.core.graph import StateGraph
32
+ from tenxgraph.utils.constants import END
33
+
34
+ graph = StateGraph()
35
+ graph.add_node("MAIN", test_agent)
36
+ graph.set_entry_point("MAIN")
37
+ graph.add_edge("MAIN", END)
38
+ app = graph.compile()
39
+ ```
40
+
41
+ Tool-call simulation — set `simulate_tool_calls=True` (or pass `tools`). First invocation returns a `tools_calls` message; subsequent invocations return responses:
42
+
43
+ ```python
44
+ agent = TestAgent(responses=["Final answer."], tools=["get_weather"])
45
+ # call 1 → tool-call message requesting get_weather
46
+ # call 2 → "Final answer."
47
+ ```
48
+
49
+ Assertion helpers:
50
+
51
+ ```python
52
+ agent.assert_called() # called at least once
53
+ agent.assert_called_times(2) # called exactly n times
54
+ agent.assert_not_called()
55
+ agent.get_last_messages() # message list from last call
56
+ agent.get_last_tools() # tool specs from last call
57
+ agent.reset() # clear call_count and call_history
58
+ ```
59
+
60
+ Attributes: `call_count: int`, `call_history: list[dict]` (each has `messages`, `tools`, `kwargs`).
61
+
62
+ ## QuickTest
63
+
64
+ One-liner async factory for common test scenarios. All methods are `async` — require `@pytest.mark.asyncio`.
65
+
66
+ ```python
67
+ from tenxgraph.qa.testing import QuickTest
68
+ ```
69
+
70
+ **single_turn** — single user/agent exchange:
71
+
72
+ ```python
73
+ result = await QuickTest.single_turn(
74
+ agent_response="Paris.",
75
+ user_message="Capital of France?",
76
+ config=None,
77
+ )
78
+ ```
79
+
80
+ **multi_turn** — multiple turns with accumulated history:
81
+
82
+ ```python
83
+ result = await QuickTest.multi_turn(
84
+ conversation=[
85
+ ("Hello", "Hi there!"),
86
+ ("What can you do?", "Many things."),
87
+ ]
88
+ )
89
+ ```
90
+
91
+ **with_tools** — agent that issues tool calls, then responds:
92
+
93
+ ```python
94
+ result = await QuickTest.with_tools(
95
+ query="Weather in London?",
96
+ response="It is sunny.",
97
+ tools=["get_weather"],
98
+ tool_responses={"get_weather": "22°C"},
99
+ )
100
+ ```
101
+
102
+ Builds a `MAIN → TOOL → MAIN → END` graph internally using `TestAgent(simulate_tool_calls=True)`.
103
+
104
+ **custom** — bring your own agent and optional graph setup:
105
+
106
+ ```python
107
+ result = await QuickTest.custom(
108
+ agent=my_test_agent,
109
+ user_message="Run it",
110
+ graph_setup=None, # callable(graph) → graph for extra wiring
111
+ )
112
+ ```
113
+
114
+ ## TestResult
115
+
116
+ Returned by every `QuickTest` method. All assertion methods return `self` for chaining.
117
+
118
+ ```python
119
+ result.assert_contains("Paris")
120
+ result.assert_not_contains("London")
121
+ result.assert_equals("Paris is the capital.")
122
+ result.assert_tool_called("get_weather")
123
+ result.assert_tool_called("get_weather", city="London") # with specific kwargs
124
+ result.assert_tool_not_called("send_email")
125
+ result.assert_message_count(3)
126
+ result.assert_no_errors()
127
+ ```
128
+
129
+ Attributes: `final_response: str`, `messages: list`, `tool_calls: list[dict]`, `state: dict`, `passed: bool`.
130
+
131
+ ## MockToolRegistry
132
+
133
+ Registers sync or async mock tools and tracks every invocation. Pass `tools.get_tool_list()` to `ToolNode`.
134
+
135
+ ```python
136
+ from tenxgraph.qa.testing import MockToolRegistry
137
+ from tenxgraph.core.graph import ToolNode
138
+
139
+ tools = MockToolRegistry()
140
+ tools.register("get_weather", lambda city: f"22°C in {city}")
141
+ tools.register_async("search_web", async_fn)
142
+
143
+ tool_node = ToolNode(tools.get_tool_list())
144
+ ```
145
+
146
+ Call tracking:
147
+
148
+ ```python
149
+ tools.was_called("get_weather") # bool
150
+ tools.call_count("get_weather") # int
151
+ tools.get_calls("get_weather") # list[dict] with "args" and "kwargs"
152
+ tools.get_last_call("get_weather") # dict or None
153
+
154
+ tools.assert_called("get_weather")
155
+ tools.assert_called_with("get_weather", city="London")
156
+ tools.assert_call_count("get_weather", 2)
157
+
158
+ tools.reset() # clear history, keep functions
159
+ tools.clear() # clear history and functions
160
+ ```
161
+
162
+ ## 10xgraph test CLI
163
+
164
+ Thin pytest wrapper. Reads defaults from `10xgraph.json → "test"` section; CLI flags override config.
165
+
166
+ ```bash
167
+ 10xgraph test # pytest auto-discovery from project root
168
+ 10xgraph test tests/unit # target path
169
+ 10xgraph test --coverage # adds --cov=. --cov-report=term-missing --cov-report=html:htmlcov
170
+ 10xgraph test --coverage --html # opens htmlcov/index.html after run
171
+ 10xgraph test -k "weather" # keyword filter forwarded to pytest
172
+ 10xgraph test -- -m "not integration" --tb=short # raw pytest flags after --
173
+ ```
174
+
175
+ `10xgraph.json` configuration:
176
+
177
+ ```json
178
+ {
179
+ "test": {
180
+ "path": "tests",
181
+ "coverage": true,
182
+ "coverage_threshold": 80
183
+ }
184
+ }
185
+ ```
186
+
187
+ Exit code mirrors pytest: 0 = all pass, 1 = failures or coverage below threshold.
188
+
189
+ ## Rules
190
+
191
+ - Never use real `Agent` in unit tests; always swap with `TestAgent`.
192
+ - All `QuickTest` methods are async — use `@pytest.mark.asyncio`.
193
+ - Call `agent.reset()` and `tools.reset()` between test cases to prevent state bleed.
194
+ - Mark slow or live-provider tests with `@pytest.mark.integration` so they are excluded from fast CI runs (`10xgraph test -- -m "not integration"`).
195
+ - `TestAgent(tools=[...])` auto-enables `simulate_tool_calls`; pair it with a real `ToolNode(tools.get_tool_list())` to exercise the full routing loop.
196
+
197
+ ## Source Map
198
+
199
+ - TestAgent: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/qa/testing/test_agent.py
200
+ - QuickTest / TestResult: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/qa/testing/quick_test.py
201
+ - MockToolRegistry: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/qa/testing/mock_tools.py
202
+ - CLI test command: https://github.com/10xGraph/10xgraph-api/blob/main/tenxgraph_api/cli/commands/test.py