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.
- 10xgraph_api-0.7.0.dist-info/METADATA +618 -0
- 10xgraph_api-0.7.0.dist-info/RECORD +203 -0
- 10xgraph_api-0.7.0.dist-info/WHEEL +5 -0
- 10xgraph_api-0.7.0.dist-info/entry_points.txt +3 -0
- 10xgraph_api-0.7.0.dist-info/licenses/LICENSE +21 -0
- 10xgraph_api-0.7.0.dist-info/top_level.txt +2 -0
- agentflow_cli/__init__.py +69 -0
- tenxgraph_api/__init__.py +23 -0
- tenxgraph_api/cli/__init__.py +6 -0
- tenxgraph_api/cli/capabilities.py +127 -0
- tenxgraph_api/cli/commands/__init__.py +49 -0
- tenxgraph_api/cli/commands/api.py +262 -0
- tenxgraph_api/cli/commands/audit.py +213 -0
- tenxgraph_api/cli/commands/build.py +305 -0
- tenxgraph_api/cli/commands/config.py +71 -0
- tenxgraph_api/cli/commands/demo.py +93 -0
- tenxgraph_api/cli/commands/eval.py +909 -0
- tenxgraph_api/cli/commands/init.py +531 -0
- tenxgraph_api/cli/commands/skills.py +532 -0
- tenxgraph_api/cli/commands/test.py +113 -0
- tenxgraph_api/cli/commands/version.py +47 -0
- tenxgraph_api/cli/config_editor/__init__.py +15 -0
- tenxgraph_api/cli/config_editor/schema.py +566 -0
- tenxgraph_api/cli/config_editor/server.py +240 -0
- tenxgraph_api/cli/config_editor/static/app.css +2 -0
- tenxgraph_api/cli/config_editor/static/app.js +3 -0
- tenxgraph_api/cli/config_editor/static/favicon.svg +11 -0
- tenxgraph_api/cli/config_editor/static/index.html +20 -0
- tenxgraph_api/cli/config_editor/store.py +120 -0
- tenxgraph_api/cli/config_editor/validation.py +377 -0
- tenxgraph_api/cli/constants.py +112 -0
- tenxgraph_api/cli/context.py +46 -0
- tenxgraph_api/cli/core/__init__.py +0 -0
- tenxgraph_api/cli/core/animations.py +550 -0
- tenxgraph_api/cli/core/config.py +279 -0
- tenxgraph_api/cli/core/output.py +625 -0
- tenxgraph_api/cli/core/prompts.py +220 -0
- tenxgraph_api/cli/core/screen.py +287 -0
- tenxgraph_api/cli/core/steps.py +621 -0
- tenxgraph_api/cli/core/theme.py +242 -0
- tenxgraph_api/cli/core/validation.py +251 -0
- tenxgraph_api/cli/exceptions.py +129 -0
- tenxgraph_api/cli/logger.py +84 -0
- tenxgraph_api/cli/main.py +936 -0
- tenxgraph_api/cli/templates/__init__.py +0 -0
- tenxgraph_api/cli/templates/defaults.py +844 -0
- tenxgraph_api/cli/templates/dev/.env.example +1 -0
- tenxgraph_api/cli/templates/dev/10xgraph.json +16 -0
- tenxgraph_api/cli/templates/dev/graph/__init__.py +0 -0
- tenxgraph_api/cli/templates/dev/graph/agent.py +63 -0
- tenxgraph_api/cli/templates/prod/.env.example +75 -0
- tenxgraph_api/cli/templates/prod/.pre-commit-config.yaml +50 -0
- tenxgraph_api/cli/templates/prod/.python-version +1 -0
- tenxgraph_api/cli/templates/prod/10xgraph.json +42 -0
- tenxgraph_api/cli/templates/prod/auth/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/auth/agent_auth.py +46 -0
- tenxgraph_api/cli/templates/prod/evals/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/evals/user_simulator_eval.py +76 -0
- tenxgraph_api/cli/templates/prod/evals/weather_agents_eval.py +53 -0
- tenxgraph_api/cli/templates/prod/graph/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/graph/agent.py +50 -0
- tenxgraph_api/cli/templates/prod/graph/state.py +5 -0
- tenxgraph_api/cli/templates/prod/graph/thread_name_generator.py +7 -0
- tenxgraph_api/cli/templates/prod/graph/tools/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/graph/tools/weather_tool.py +43 -0
- tenxgraph_api/cli/templates/prod/graph/validators/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/graph/validators/lifecyle.py +107 -0
- tenxgraph_api/cli/templates/prod/graph/validators/manager.py +9 -0
- tenxgraph_api/cli/templates/prod/graph/validators/validators.py +21 -0
- tenxgraph_api/cli/templates/prod/pyproject.toml +79 -0
- tenxgraph_api/cli/templates/prod/tests/__init__.py +0 -0
- tenxgraph_api/cli/templates/prod/tests/conftest.py +6 -0
- tenxgraph_api/cli/templates/prod/tests/test_agent_eval.py +78 -0
- tenxgraph_api/cli/templates/prod/tests/test_catalog_tools.py +47 -0
- tenxgraph_api/cli/templates/prod/tests/test_graph_nodes.py +57 -0
- tenxgraph_api/cli/templates/skills/10xgraph/SKILL.md +92 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/agents-and-tools.md +389 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/api-client.md +61 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/api-configuration.md +75 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/api-settings-and-middleware.md +89 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/architecture.md +91 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/auth-and-authorization.md +149 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/callbacks-and-command.md +283 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/checkpointing-and-threads.md +63 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/cli-commands.md +73 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/client-auth-and-errors.md +94 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/client-messages-invoke-stream.md +115 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/client-threads-memory-files.md +80 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/context-id-background.md +80 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/dependency-injection.md +58 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/evaluation.md +500 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/id-and-thread-name-generators.md +69 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/media-and-files.md +66 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/memory-and-store.md +70 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/prebuilt-agents-and-tools.md +394 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/production-runtime.md +56 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/providers-and-adapters.md +89 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/publishers-and-runtime-protocols.md +84 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/rate-limiting.md +194 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/realtime.md +172 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/remote-tools.md +55 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/rest-api-and-errors.md +97 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/security-and-validators.md +86 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/state-and-messages.md +68 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/state-graph.md +232 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/stream-emitter.md +144 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/streaming.md +53 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/testing-and-evaluation.md +64 -0
- tenxgraph_api/cli/templates/skills/10xgraph/references/unit-testing.md +202 -0
- tenxgraph_api/cli/templates/skills/copilot/10xgraph.instructions.md +108 -0
- tenxgraph_api/py.typed +0 -0
- tenxgraph_api/src/__init__.py +0 -0
- tenxgraph_api/src/app/__init__.py +13 -0
- tenxgraph_api/src/app/core/__init__.py +15 -0
- tenxgraph_api/src/app/core/auth/__init__.py +0 -0
- tenxgraph_api/src/app/core/auth/auth_backend.py +38 -0
- tenxgraph_api/src/app/core/auth/authorization.py +379 -0
- tenxgraph_api/src/app/core/auth/base_auth.py +27 -0
- tenxgraph_api/src/app/core/auth/jwt_auth.py +122 -0
- tenxgraph_api/src/app/core/auth/ownership_resolver.py +170 -0
- tenxgraph_api/src/app/core/auth/permissions.py +354 -0
- tenxgraph_api/src/app/core/auth/request_config.py +130 -0
- tenxgraph_api/src/app/core/auth/route_guard.py +105 -0
- tenxgraph_api/src/app/core/config/__init__.py +0 -0
- tenxgraph_api/src/app/core/config/graph_config.py +532 -0
- tenxgraph_api/src/app/core/config/media_settings.py +72 -0
- tenxgraph_api/src/app/core/config/sentry_config.py +60 -0
- tenxgraph_api/src/app/core/config/settings.py +202 -0
- tenxgraph_api/src/app/core/config/setup_logs.py +65 -0
- tenxgraph_api/src/app/core/config/setup_middleware.py +483 -0
- tenxgraph_api/src/app/core/config/worker_middleware.py +99 -0
- tenxgraph_api/src/app/core/exceptions/__init__.py +17 -0
- tenxgraph_api/src/app/core/exceptions/general_exception.py +58 -0
- tenxgraph_api/src/app/core/exceptions/handle_errors.py +363 -0
- tenxgraph_api/src/app/core/exceptions/resources_exceptions.py +72 -0
- tenxgraph_api/src/app/core/exceptions/user_exception.py +58 -0
- tenxgraph_api/src/app/core/middleware/__init__.py +25 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/__init__.py +15 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/base.py +27 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/factory.py +98 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/keying.py +133 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/memory.py +98 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/middleware.py +90 -0
- tenxgraph_api/src/app/core/middleware/rate_limit/redis.py +115 -0
- tenxgraph_api/src/app/core/middleware/request_limits.py +154 -0
- tenxgraph_api/src/app/core/middleware/security_headers.py +239 -0
- tenxgraph_api/src/app/core/utils/__init__.py +14 -0
- tenxgraph_api/src/app/core/utils/log_sanitizer.py +216 -0
- tenxgraph_api/src/app/loader.py +516 -0
- tenxgraph_api/src/app/main.py +180 -0
- tenxgraph_api/src/app/routers/__init__.py +4 -0
- tenxgraph_api/src/app/routers/ag_ui/__init__.py +10 -0
- tenxgraph_api/src/app/routers/ag_ui/converter.py +195 -0
- tenxgraph_api/src/app/routers/ag_ui/event_mapper.py +353 -0
- tenxgraph_api/src/app/routers/ag_ui/router.py +90 -0
- tenxgraph_api/src/app/routers/ag_ui/service.py +193 -0
- tenxgraph_api/src/app/routers/checkpointer/__init__.py +1 -0
- tenxgraph_api/src/app/routers/checkpointer/router.py +482 -0
- tenxgraph_api/src/app/routers/checkpointer/schemas/__init__.py +1 -0
- tenxgraph_api/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +121 -0
- tenxgraph_api/src/app/routers/checkpointer/services/__init__.py +1 -0
- tenxgraph_api/src/app/routers/checkpointer/services/checkpointer_service.py +300 -0
- tenxgraph_api/src/app/routers/evals/__init__.py +6 -0
- tenxgraph_api/src/app/routers/evals/router.py +38 -0
- tenxgraph_api/src/app/routers/evals/services/__init__.py +1 -0
- tenxgraph_api/src/app/routers/evals/services/eval_report_service.py +354 -0
- tenxgraph_api/src/app/routers/graph/__init__.py +6 -0
- tenxgraph_api/src/app/routers/graph/realtime_guard.py +161 -0
- tenxgraph_api/src/app/routers/graph/router.py +821 -0
- tenxgraph_api/src/app/routers/graph/schemas/__init__.py +1 -0
- tenxgraph_api/src/app/routers/graph/schemas/graph_schemas.py +386 -0
- tenxgraph_api/src/app/routers/graph/services/__init__.py +1 -0
- tenxgraph_api/src/app/routers/graph/services/graph_service.py +1219 -0
- tenxgraph_api/src/app/routers/graph/services/multimodal_preprocessor.py +164 -0
- tenxgraph_api/src/app/routers/media/__init__.py +506 -0
- tenxgraph_api/src/app/routers/media/router.py +242 -0
- tenxgraph_api/src/app/routers/media/schemas.py +39 -0
- tenxgraph_api/src/app/routers/ping/__init__.py +4 -0
- tenxgraph_api/src/app/routers/ping/router.py +28 -0
- tenxgraph_api/src/app/routers/setup_router.py +60 -0
- tenxgraph_api/src/app/routers/store/__init__.py +4 -0
- tenxgraph_api/src/app/routers/store/router.py +200 -0
- tenxgraph_api/src/app/routers/store/schemas/__init__.py +28 -0
- tenxgraph_api/src/app/routers/store/schemas/store_schemas.py +200 -0
- tenxgraph_api/src/app/routers/store/services/__init__.py +4 -0
- tenxgraph_api/src/app/routers/store/services/store_service.py +203 -0
- tenxgraph_api/src/app/tasks/__init__.py +0 -0
- tenxgraph_api/src/app/tasks/user_tasks.py +38 -0
- tenxgraph_api/src/app/utils/__init__.py +12 -0
- tenxgraph_api/src/app/utils/callable_helper.py +26 -0
- tenxgraph_api/src/app/utils/media/__init__.py +15 -0
- tenxgraph_api/src/app/utils/media/extractor.py +172 -0
- tenxgraph_api/src/app/utils/media/pipeline.py +83 -0
- tenxgraph_api/src/app/utils/parse_output.py +17 -0
- tenxgraph_api/src/app/utils/response_helper.py +129 -0
- tenxgraph_api/src/app/utils/schemas/__init__.py +11 -0
- tenxgraph_api/src/app/utils/schemas/output_schemas.py +28 -0
- tenxgraph_api/src/app/utils/schemas/user_schemas.py +13 -0
- tenxgraph_api/src/app/utils/snowflake_id_generator.py +90 -0
- tenxgraph_api/src/app/utils/swagger_helper.py +160 -0
- tenxgraph_api/src/app/utils/telemetry_store.py +123 -0
- tenxgraph_api/src/app/utils/thread_name_generator.py +304 -0
- tenxgraph_api/src/app/worker.py +91 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Use this when deciding where a change belongs or explaining how 10xGraph packages interact. Check https://10xgraph.com/ first for public package names.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Published packages
|
|
8
|
+
|
|
9
|
+
| Public package | Registry | Install | Source |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| `10xgraph` | PyPI | `pip install 10xgraph` | `tenxgraph/` in 10xGraph/10xGraph |
|
|
12
|
+
| `10xgraph-api` | PyPI | `pip install 10xgraph-api` | `10xgraph-api/tenxgraph_api` |
|
|
13
|
+
| `@10xscale/agentflow-client` | npm | `npm install @10xscale/agentflow-client` | `agentflow-client/src` |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Layer responsibilities
|
|
18
|
+
|
|
19
|
+
### `10xgraph` — core Python library
|
|
20
|
+
|
|
21
|
+
| Sub-package | Key exports |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `tenxgraph.core` | `StateGraph`, `Agent`, `ToolNode`, `AgentState`, `Message`, `StreamChunk` |
|
|
24
|
+
| `tenxgraph.prebuilt.agent` | `ReactAgent`, `PlanActReflectAgent`, `StructuredOutputAgent`, `SupervisorTeamAgent`, `SwarmAgent`, `RAGAgent`, `AudioAgent` (realtime voice) |
|
|
25
|
+
| `tenxgraph.core.realtime` | `LiveInputQueue`, `RealtimeConfig`, `RealtimeEvent` (Gemini Live audio-to-audio) |
|
|
26
|
+
| `tenxgraph.prebuilt.tools` | `fetch_url`, `safe_calculator`, `file_read`, `file_write`, `file_search`, `google_web_search`, `vertex_ai_search`, `memory_tool`, `create_handoff_tool` |
|
|
27
|
+
| `tenxgraph.storage.checkpointer` | `InMemoryCheckpointer`, `PgCheckpointer` |
|
|
28
|
+
| `tenxgraph.storage.store` | `QdrantStore`, `Mem0Store` |
|
|
29
|
+
| `tenxgraph.storage.media` | `InMemoryMediaStore`, `LocalFileMediaStore`, `CloudMediaStore` |
|
|
30
|
+
| `tenxgraph.runtime` | Publisher adapters (SSE, A2A, Kafka, RabbitMQ, Redis Pub/Sub) |
|
|
31
|
+
| `tenxgraph.utils` | `ResponseGranularity`, `CallbackManager`, `tool` decorator |
|
|
32
|
+
| `tenxgraph.qa` | Testing helpers and evaluation tools |
|
|
33
|
+
|
|
34
|
+
### `10xgraph-api` — API and CLI
|
|
35
|
+
|
|
36
|
+
Owns `10xgraph api`, `10xgraph play`, `10xgraph init`, `10xgraph build`, REST routers, auth/middleware, config loading, and graph service execution.
|
|
37
|
+
|
|
38
|
+
### `@10xscale/agentflow-client` — TypeScript HTTP client
|
|
39
|
+
|
|
40
|
+
Typed methods for invoke, stream, threads, memory store, file uploads, graph metadata, remote tools, and auth helpers. Calls a running 10xGraph API server — does not run Python graphs.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Request flow: invoke
|
|
45
|
+
|
|
46
|
+
1. `@10xscale/agentflow-client` or another HTTP caller sends messages plus `config.thread_id`.
|
|
47
|
+
2. FastAPI receives the request through auth and routers.
|
|
48
|
+
3. `GraphService` invokes against the compiled graph loaded at startup.
|
|
49
|
+
4. The compiled graph loads state from the checkpointer when `thread_id` exists.
|
|
50
|
+
5. Graph nodes run and update `AgentState`.
|
|
51
|
+
6. Checkpointer saves state, messages, and thread metadata.
|
|
52
|
+
7. API returns JSON to the caller.
|
|
53
|
+
|
|
54
|
+
## Request flow: stream
|
|
55
|
+
|
|
56
|
+
Identical through authentication and state loading. The difference: the graph sends `StreamChunk` events incrementally via SSE. Each chunk carries `event` (`"message"`, `"state"`, `"error"`, or `"updates"`), and the response is a `StreamingResponse`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Key design decisions
|
|
61
|
+
|
|
62
|
+
| Decision | Rationale |
|
|
63
|
+
|---|---|
|
|
64
|
+
| Graph compiled once at startup | Avoids repeated module loading per request |
|
|
65
|
+
| `thread_id` in every request | Allows stateless servers to restore conversation history |
|
|
66
|
+
| Checkpointer is injected, not hardcoded | Graph code does not depend on the storage backend |
|
|
67
|
+
| Auth is middleware, not in the graph | Business logic stays separate from access control |
|
|
68
|
+
| `injectq` for service wiring | Nodes and tools declare dependencies declaratively; the runtime resolves them |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Design rules
|
|
73
|
+
|
|
74
|
+
- Compile graphs once at startup for API serving.
|
|
75
|
+
- Keep graph code storage-agnostic; wire checkpointer/store/media/dependencies through compile arguments, `InjectQ`, or `10xgraph.json`.
|
|
76
|
+
- Treat `thread_id` as the continuity key for conversation state.
|
|
77
|
+
- Treat long-term memory store records as cross-thread knowledge, not thread history.
|
|
78
|
+
- Keep auth and request permissions in API middleware/routers, not inside graph nodes.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Source map
|
|
83
|
+
|
|
84
|
+
- Core graph: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/core/graph
|
|
85
|
+
- State/message models: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/core/state
|
|
86
|
+
- Checkpointers: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/storage/checkpointer
|
|
87
|
+
- Memory stores: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/storage/store
|
|
88
|
+
- Media stores: https://github.com/10xGraph/10xGraph/tree/main/tenxgraph/storage/media
|
|
89
|
+
- API routers: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/src/app/routers
|
|
90
|
+
- TS client: https://github.com/10xHub/agentflow-client/blob/main/src/client.ts
|
|
91
|
+
- Concepts: https://10xgraph.com/concepts/architecture
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Auth and Authorization
|
|
2
|
+
|
|
3
|
+
Use this when changing HTTP authentication, authorization, permission checks, scopes, storage
|
|
4
|
+
isolation, or client auth examples.
|
|
5
|
+
|
|
6
|
+
Two independent layers:
|
|
7
|
+
|
|
8
|
+
- Authentication (`BaseAuth`): who is the caller? Produces a `user` dict.
|
|
9
|
+
- Authorization (`AuthorizationBackend`): what may they do, and whose data can they touch?
|
|
10
|
+
|
|
11
|
+
## Authentication Modes
|
|
12
|
+
|
|
13
|
+
Default:
|
|
14
|
+
|
|
15
|
+
- `auth: null`
|
|
16
|
+
- No authentication. Local development / trusted gateways only.
|
|
17
|
+
|
|
18
|
+
JWT:
|
|
19
|
+
|
|
20
|
+
- `auth: "jwt"`
|
|
21
|
+
- Requires `JWT_SECRET_KEY` and `JWT_ALGORITHM`. Token must carry `user_id` and `exp`.
|
|
22
|
+
- Requests use `Authorization: Bearer <token>`. Decoded payload becomes the `user` context.
|
|
23
|
+
|
|
24
|
+
Custom:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{ "auth": { "method": "custom", "path": "graph.auth:MyAuthBackend" } }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Custom backends subclass `BaseAuth`. `authenticate` is **synchronous** (called without `await`)
|
|
31
|
+
and takes three args:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
def authenticate(self, request, response, credential) -> dict | None: ...
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- `request` may be a `Request` or a `WebSocket` (both are `HTTPConnection`) — read
|
|
38
|
+
`request.headers`, do not assume Request-only APIs.
|
|
39
|
+
- `credential` is the parsed bearer token (`HTTPAuthorizationCredentials | None`). For
|
|
40
|
+
API-key/cookie schemes, ignore it and read headers directly.
|
|
41
|
+
- Return a dict with at least `user_id` (keys flow into `config["user"]`); `{}`/`None` = anonymous;
|
|
42
|
+
raise `UserAccountError`/`HTTPException` to reject (401 HTTP, 1008 close on WebSocket).
|
|
43
|
+
- Do NOT declare it `async def` — an un-awaited coroutine breaks auth silently.
|
|
44
|
+
- Include `roles` / `scopes` in the returned dict to feed authorization.
|
|
45
|
+
|
|
46
|
+
## Authorization
|
|
47
|
+
|
|
48
|
+
Configured via the `authorization` key; separate from `auth`. Values:
|
|
49
|
+
|
|
50
|
+
- `null` (unset): mode-based default — `"ownership"` in production, `"allow_all"` in development.
|
|
51
|
+
- `"ownership"`: owner-only. A thread is readable/runnable/deletable only by its creator; a
|
|
52
|
+
foreign `invoke`/`stream` is rejected up front (403). Enforced on every thread-touching step.
|
|
53
|
+
- `"allow_all"` (aliases `"default"`, `"none"`): any authenticated user may do anything.
|
|
54
|
+
- `"module:attr"`: custom `AuthorizationBackend`.
|
|
55
|
+
- `{ "backend": "rbac", ... }`: role-based access control (below).
|
|
56
|
+
|
|
57
|
+
Ownership is scalable: ownership is immutable, so it is cached (in-process LRU + optional shared
|
|
58
|
+
Redis when `redis`/`REDIS_URL` is set) via `ThreadOwnershipResolver` over
|
|
59
|
+
`BaseCheckpointer.aget_thread_owner`. After the first lookup a check is an in-memory hit, not a
|
|
60
|
+
per-request DB call; the cache is evicted on thread deletion. Ownership needs a persistent
|
|
61
|
+
checkpointer (pg/sqlite/in-memory); with none, requests pass through.
|
|
62
|
+
|
|
63
|
+
### Custom backend interface
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
class MyAuthz(AuthorizationBackend):
|
|
67
|
+
async def authorize(
|
|
68
|
+
self, user: dict, resource: str, action: str,
|
|
69
|
+
resource_id: str | None = None, **context,
|
|
70
|
+
) -> bool: ... # required; False -> 403
|
|
71
|
+
|
|
72
|
+
def isolation_scope(self) -> str: # optional; "owner" | "none" (default "none")
|
|
73
|
+
return "owner"
|
|
74
|
+
|
|
75
|
+
def scopes_for(self, user: dict) -> list[str] | None: # optional; None = permissive
|
|
76
|
+
return user.get("scopes")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Subclass `OwnershipAuthorizationBackend` to inherit cached owner-only checks and override only
|
|
80
|
+
what you need.
|
|
81
|
+
|
|
82
|
+
### Scopes
|
|
83
|
+
|
|
84
|
+
Each endpoint requires the scope `"<resource>:<action>"`. `RequirePermission` checks it against
|
|
85
|
+
`authz.scopes_for(user)`; `None` = permissive (default, nothing breaks until scopes are issued).
|
|
86
|
+
|
|
87
|
+
- `graph`: `invoke`, `stream`, `stop`, `fix`, `setup`, `read`
|
|
88
|
+
- `checkpointer`: `read`, `write`, `delete`
|
|
89
|
+
- `store`: `read`, `write`, `delete`
|
|
90
|
+
- `files`: `upload`, `read`
|
|
91
|
+
- `config`: `read`
|
|
92
|
+
|
|
93
|
+
### RBAC config (no code)
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"authorization": {
|
|
98
|
+
"backend": "rbac",
|
|
99
|
+
"roles": { "admin": ["*"], "member": ["graph:invoke", "checkpointer:read"] },
|
|
100
|
+
"default_scopes": ["graph:read"],
|
|
101
|
+
"isolation": "owner"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Maps `user["roles"]` to scopes on top of owner isolation. `"*"` -> all scopes. `default_scopes`
|
|
107
|
+
granted to everyone. `isolation`: `"owner"` | `"none"`.
|
|
108
|
+
|
|
109
|
+
### Data isolation (config["authz"])
|
|
110
|
+
|
|
111
|
+
API checks decide access; the storage layer (checkpointer, store) enforces isolation from a
|
|
112
|
+
trusted policy. After a successful check, `RequirePermission` stamps
|
|
113
|
+
`user["authz"] = {user_id, scope, scopes}` where `scope` = backend `isolation_scope()`. Services
|
|
114
|
+
copy the trusted `user` into `config["user"]`, so the core library reads `config["user"]["authz"]`
|
|
115
|
+
and scopes rows to the caller (`owner`) or not (`none`). Set server-side only — not client-forgeable.
|
|
116
|
+
|
|
117
|
+
## Guarantees
|
|
118
|
+
|
|
119
|
+
- Routes are guarded by construction: `assert_all_routes_protected` refuses to boot if any
|
|
120
|
+
non-public route lacks `RequirePermission`. Public: `/ping` and dev-only eval endpoints.
|
|
121
|
+
- Return 401 for auth failure, 403 for authorization/scope failure.
|
|
122
|
+
|
|
123
|
+
## TypeScript Client
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const client = new AgentFlowClient({
|
|
127
|
+
baseUrl: "http://127.0.0.1:8000",
|
|
128
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Rules
|
|
133
|
+
|
|
134
|
+
- No unauthenticated API mode in production unless a trusted gateway handles auth.
|
|
135
|
+
- Keep `authenticate` synchronous; keep authorization out of graph business logic.
|
|
136
|
+
- Sanitize logging for tokens and user payloads.
|
|
137
|
+
- Update the scope catalog and permission tables when adding routes.
|
|
138
|
+
|
|
139
|
+
## Source Map
|
|
140
|
+
|
|
141
|
+
- Base auth: tenxgraph_api/src/app/core/auth/base_auth.py
|
|
142
|
+
- JWT auth: tenxgraph_api/src/app/core/auth/jwt_auth.py
|
|
143
|
+
- Auth loader/dependency: tenxgraph_api/src/app/core/auth/auth_backend.py
|
|
144
|
+
- Authorization backends (ownership, RBAC): tenxgraph_api/src/app/core/auth/authorization.py
|
|
145
|
+
- Permission dependency + scope check + authz stamp: tenxgraph_api/src/app/core/auth/permissions.py
|
|
146
|
+
- Ownership cache: tenxgraph_api/src/app/core/auth/ownership_resolver.py
|
|
147
|
+
- Boot-time route guard: tenxgraph_api/src/app/core/auth/route_guard.py
|
|
148
|
+
- Scope catalog + authz contract: tenxgraph/core/authz.py
|
|
149
|
+
- Docs: https://10xgraph.com/
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Callbacks and Command
|
|
2
|
+
|
|
3
|
+
Use this when adding validation, tracing, invocation hooks, error recovery, or runtime graph jumps.
|
|
4
|
+
|
|
5
|
+
## CallbackManager
|
|
6
|
+
|
|
7
|
+
`CallbackManager` registers hooks around AI, tool, MCP, input validation, and skill invocations.
|
|
8
|
+
|
|
9
|
+
Invocation types:
|
|
10
|
+
|
|
11
|
+
- `InvocationType.AI`
|
|
12
|
+
- `InvocationType.TOOL`
|
|
13
|
+
- `InvocationType.MCP`
|
|
14
|
+
- `InvocationType.INPUT_VALIDATION`
|
|
15
|
+
- `InvocationType.SKILL`
|
|
16
|
+
|
|
17
|
+
Hook families:
|
|
18
|
+
|
|
19
|
+
- `register_before_invoke(invocation_type, callback)`: validate or transform input before invocation.
|
|
20
|
+
- `register_after_invoke(invocation_type, callback)`: inspect or transform output after invocation.
|
|
21
|
+
- `register_on_error(invocation_type, callback)`: log, recover, or return a recovery `Message`.
|
|
22
|
+
- `register_input_validator(validator)`: add a `BaseValidator` that validates incoming messages.
|
|
23
|
+
|
|
24
|
+
Callbacks can be callable objects or functions and may be sync or async. Error callbacks should return a `Message` recovery value or `None`.
|
|
25
|
+
|
|
26
|
+
## Validators
|
|
27
|
+
|
|
28
|
+
Use `BaseValidator` for input validation. Implement:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
async def validate(self, messages: list[Message]) -> bool:
|
|
32
|
+
...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Register validators on a `CallbackManager`, then pass it to graph compilation:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
callback_manager = CallbackManager()
|
|
39
|
+
callback_manager.register_input_validator(MyValidator())
|
|
40
|
+
|
|
41
|
+
app = graph.compile(callback_manager=callback_manager)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use validators for safety checks, business rules, input policy, and prompt-injection detection.
|
|
45
|
+
|
|
46
|
+
## Graph Lifecycle Hooks
|
|
47
|
+
|
|
48
|
+
Graph lifecycle hooks fire at **graph orchestration level** — before/after the entire graph run, on checkpoints, on interrupts, on resume, and after each node transition. They complement the invocation-level hooks above (`before_invoke` / `after_invoke` / `on_error`), which fire inside a single node's AI/Tool/MCP call.
|
|
49
|
+
|
|
50
|
+
### GraphLifecycleContext
|
|
51
|
+
|
|
52
|
+
All 7 hooks receive this as their first argument. It provides run-identifying metadata.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
@dataclass
|
|
56
|
+
class GraphLifecycleContext:
|
|
57
|
+
config: dict[str, Any] # full config dict passed to invoke/stream
|
|
58
|
+
timestamp: str # ISO8601 start time from config["timestamp"]
|
|
59
|
+
metadata: dict[str, Any] | None = None # open-ended extra context
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def thread_id(self) -> str | None: ...
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def run_id(self) -> str | None: ...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### GraphLifecycleHook
|
|
69
|
+
|
|
70
|
+
Subclass `GraphLifecycleHook` and override only the methods you need. All methods are async and default to no-ops.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from tenxgraph.utils.callbacks import GraphLifecycleHook, GraphLifecycleContext, CallbackManager
|
|
74
|
+
from tenxgraph.core.state import AgentState
|
|
75
|
+
from tenxgraph.core.state.message import Message
|
|
76
|
+
|
|
77
|
+
class GraphLifecycleHook(ABC):
|
|
78
|
+
async def on_graph_start(
|
|
79
|
+
self, context: GraphLifecycleContext, state: AgentState
|
|
80
|
+
) -> AgentState | None: ...
|
|
81
|
+
# Fires: after state is loaded, before the first node executes.
|
|
82
|
+
# Return modified AgentState to replace the initial state, or None.
|
|
83
|
+
|
|
84
|
+
async def on_graph_end(
|
|
85
|
+
self, context: GraphLifecycleContext, final_state: AgentState,
|
|
86
|
+
messages: list[Message], total_steps: int
|
|
87
|
+
) -> AgentState | None: ...
|
|
88
|
+
# Fires: after execution loop completes, before final persistence.
|
|
89
|
+
# Return modified AgentState or None.
|
|
90
|
+
|
|
91
|
+
async def on_graph_error(
|
|
92
|
+
self, context: GraphLifecycleContext, error: Exception,
|
|
93
|
+
partial_state: AgentState, messages: list[Message], step: int, node_name: str
|
|
94
|
+
) -> tuple[AgentState, str] | None: ...
|
|
95
|
+
# Fires: when an unhandled exception escapes the graph loop.
|
|
96
|
+
# Return (AgentState, error_message) to change persisted error snapshot, or None.
|
|
97
|
+
# Cannot suppress the error — always re-raised after this hook.
|
|
98
|
+
|
|
99
|
+
async def on_interrupt(
|
|
100
|
+
self, context: GraphLifecycleContext, interrupted_node: str,
|
|
101
|
+
interrupt_type: str, # "before" | "after" | "stop" | "remote_tool"
|
|
102
|
+
state: AgentState
|
|
103
|
+
) -> AgentState | None: ...
|
|
104
|
+
# Fires: before interrupt state is persisted (covers before/after/stop/remote_tool types).
|
|
105
|
+
# Return modified AgentState or None.
|
|
106
|
+
|
|
107
|
+
async def on_resume(
|
|
108
|
+
self, context: GraphLifecycleContext, resumed_node: str,
|
|
109
|
+
state: AgentState, resume_data: dict[str, Any]
|
|
110
|
+
) -> AgentState | None: ...
|
|
111
|
+
# Fires: when a paused graph is resumed, before clear_interrupt().
|
|
112
|
+
# resume_data is mutable in-place. Return modified AgentState or None.
|
|
113
|
+
|
|
114
|
+
async def on_checkpoint(
|
|
115
|
+
self, context: GraphLifecycleContext, state: AgentState,
|
|
116
|
+
messages: list[Message], is_context_trimmed: bool
|
|
117
|
+
) -> tuple[AgentState, list[Message]] | AgentState | None: ...
|
|
118
|
+
# Fires: immediately before state/messages are persisted (every checkpoint, not just final).
|
|
119
|
+
# Return (AgentState, messages), AgentState, or None.
|
|
120
|
+
|
|
121
|
+
async def on_state_update(
|
|
122
|
+
self, context: GraphLifecycleContext, node_name: str,
|
|
123
|
+
old_state: AgentState, new_state: AgentState, step: int
|
|
124
|
+
) -> AgentState | None: ...
|
|
125
|
+
# Fires: after each node produces a result and state is merged.
|
|
126
|
+
# Return modified AgentState or None.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Registration
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
callback_mgr = CallbackManager()
|
|
133
|
+
callback_mgr.register_lifecycle_hook(MyHook())
|
|
134
|
+
|
|
135
|
+
# Combine with existing invocation-level hooks on the same manager:
|
|
136
|
+
callback_mgr.register_after_invoke(InvocationType.AI, my_ai_callback)
|
|
137
|
+
|
|
138
|
+
app = graph.compile(callback_manager=callback_mgr)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Hook Summary
|
|
142
|
+
|
|
143
|
+
| Hook | Returns | Fires N times | Fire location |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| `on_graph_start` | `AgentState \| None` | 1 per run | After state load, before loop |
|
|
146
|
+
| `on_graph_end` | `AgentState \| None` | 1 per successful run | After `state.complete()`, before final `sync_data()` |
|
|
147
|
+
| `on_graph_error` | `tuple[AgentState, str] \| None` | 1 per failed run | In except block, before error `sync_data()` |
|
|
148
|
+
| `on_interrupt` | `AgentState \| None` | 0–N per run | Before interrupt checkpoint persistence |
|
|
149
|
+
| `on_resume` | `AgentState \| None` | 0–1 per call | Before `clear_interrupt()` |
|
|
150
|
+
| `on_checkpoint` | `(AgentState, list[Message]) \| AgentState \| None` | 1–N per run | Before every durable checkpoint write |
|
|
151
|
+
| `on_state_update` | `AgentState \| None` | N per run (once per node) | After each node result is merged |
|
|
152
|
+
| `on_turn_start` | `AgentState \| None` | N (realtime only) | Start of each model turn |
|
|
153
|
+
| `on_turn_end` | `AgentState \| None` | N (realtime only) | End of each model turn |
|
|
154
|
+
|
|
155
|
+
### Realtime hooks
|
|
156
|
+
|
|
157
|
+
The same `GraphLifecycleHook` fires for realtime (audio) graphs, with two methods that fire **only**
|
|
158
|
+
in realtime (no-ops for `invoke` / `stream`):
|
|
159
|
+
|
|
160
|
+
- `on_graph_start(ctx, state)` / `on_graph_end(ctx, final_state, messages, total_steps)` — once per
|
|
161
|
+
session (the `LIVE` node *is* the graph); `total_steps` = number of turns.
|
|
162
|
+
- `on_turn_start(ctx, state, turn_index)` / `on_turn_end(ctx, state, turn_index)` — per model turn
|
|
163
|
+
(1-based; a turn spans one model generation, bounded by `turn_complete` or a barge-in). A turn cut
|
|
164
|
+
off by session end still gets a balanced `on_turn_end`.
|
|
165
|
+
|
|
166
|
+
Realtime has no `AI`-invocation callback or input-validator pass (no discrete LLM call); the per-turn
|
|
167
|
+
hooks are the observability stand-in. Tool/MCP `before/after/error` callbacks fire as usual. See
|
|
168
|
+
`realtime.md`.
|
|
169
|
+
|
|
170
|
+
### Example
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
class ObservabilityHook(GraphLifecycleHook):
|
|
174
|
+
async def on_graph_start(self, ctx, state):
|
|
175
|
+
self._span = tracer.start_span(f"graph.run.{ctx.thread_id}")
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
async def on_graph_end(self, ctx, final_state, messages, total_steps):
|
|
179
|
+
self._span.set_attribute("steps", total_steps)
|
|
180
|
+
self._span.end()
|
|
181
|
+
|
|
182
|
+
async def on_graph_error(self, ctx, error, partial_state, messages, step, node_name):
|
|
183
|
+
self._span.record_exception(error)
|
|
184
|
+
self._span.end()
|
|
185
|
+
alert_oncall(f"Graph failed at node {node_name}: {error}")
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
async def on_interrupt(self, ctx, interrupted_node, interrupt_type, state):
|
|
189
|
+
notify_frontend(ctx.thread_id, status="waiting_for_input", node=interrupted_node)
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
async def on_resume(self, ctx, resumed_node, state, resume_data):
|
|
193
|
+
notify_frontend(ctx.thread_id, status="resuming", node=resumed_node)
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
async def on_checkpoint(self, ctx, state, messages, is_context_trimmed):
|
|
197
|
+
metrics.increment("tenxgraph.checkpoints", tags={"thread": ctx.thread_id})
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
async def on_state_update(self, ctx, node_name, old_state, new_state, step):
|
|
201
|
+
diff = compute_diff(old_state, new_state)
|
|
202
|
+
stream_diff_to_frontend(ctx.thread_id, diff)
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
callback_mgr = CallbackManager()
|
|
207
|
+
callback_mgr.register_lifecycle_hook(ObservabilityHook())
|
|
208
|
+
app = graph.compile(callback_manager=callback_mgr)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Common Use Cases by Hook
|
|
212
|
+
|
|
213
|
+
- **`on_graph_start`**: inject trace IDs, pre-populate state from external DB, set rate-limit budgets, initialize OpenTelemetry spans.
|
|
214
|
+
- **`on_graph_end`**: send completion notifications (Slack/email), record step/message count metrics, archive transcripts, trigger downstream webhooks.
|
|
215
|
+
- **`on_graph_error`**: alert PagerDuty/Sentry, log structured failure diagnostics, close OTel spans with error status.
|
|
216
|
+
- **`on_interrupt`**: push "waiting for approval" notifications to frontend/mobile, start timeout timers, update task queue status.
|
|
217
|
+
- **`on_resume`**: cancel timeout timers, validate resume payload, record interrupt→resume cycle for audit trail.
|
|
218
|
+
- **`on_checkpoint`**: redact sensitive data before persistence, replicate to secondary store, invalidate caches, compliance audit logging (SOC2/HIPAA).
|
|
219
|
+
- **`on_state_update`**: real-time state diffing for frontend streaming, per-node invariant assertions, security scanning of state content.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Command
|
|
224
|
+
|
|
225
|
+
`Command` lets a node combine state/message updates with control flow. Use it when the next node depends on runtime logic inside the node and is awkward to express as a static conditional edge.
|
|
226
|
+
|
|
227
|
+
Fields:
|
|
228
|
+
|
|
229
|
+
- `update`: state update, `Message`, string, converter, or `None`.
|
|
230
|
+
- `goto`: next node name or `END`.
|
|
231
|
+
- `graph`: optional graph target; `Command.PARENT` is reserved for parent graph navigation patterns.
|
|
232
|
+
- `state`: optional attached state.
|
|
233
|
+
|
|
234
|
+
Prefer conditional edges for normal routing because they are easier to visualize and test. Use `Command(goto=...)` for dynamic jumps, recovery branches, handoffs, or side-effect-dependent routing.
|
|
235
|
+
|
|
236
|
+
## Patterns
|
|
237
|
+
|
|
238
|
+
Callback for tracing:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from tenxgraph.utils.callbacks import CallbackContext, CallbackManager, InvocationType
|
|
242
|
+
|
|
243
|
+
async def trace_after(context: CallbackContext, input_data, output_data):
|
|
244
|
+
print(context.invocation_type, context.node_name)
|
|
245
|
+
return output_data
|
|
246
|
+
|
|
247
|
+
callback_manager = CallbackManager()
|
|
248
|
+
callback_manager.register_after_invoke(InvocationType.TOOL, trace_after)
|
|
249
|
+
app = graph.compile(callback_manager=callback_manager)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Command for dynamic routing:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from tenxgraph.utils import Command, END
|
|
256
|
+
|
|
257
|
+
def router_node(state, config):
|
|
258
|
+
if state.context[-1].text() == "stop":
|
|
259
|
+
return Command(goto=END)
|
|
260
|
+
return Command(update={"route": "repair"}, goto="REPAIR")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Rules
|
|
264
|
+
|
|
265
|
+
- Keep callback side effects bounded; they run inside graph execution paths.
|
|
266
|
+
- Avoid storing per-request mutable state globally inside callbacks.
|
|
267
|
+
- Use `CallbackContext` metadata to distinguish node/function/invocation details.
|
|
268
|
+
- Return transformed data from callbacks only when the downstream invocation expects that shape.
|
|
269
|
+
- Use `Command` sparingly; static and conditional edges remain the default graph structure.
|
|
270
|
+
- Always test command routes for recursion limits and missing destination nodes.
|
|
271
|
+
|
|
272
|
+
## Source Map
|
|
273
|
+
|
|
274
|
+
- Callback system (invocation hooks + graph lifecycle hooks): https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/utils/callbacks.py
|
|
275
|
+
- Default validators: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/utils/validators.py
|
|
276
|
+
- Graph compile callback argument: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/state_graph.py
|
|
277
|
+
- Command API: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/utils/command.py
|
|
278
|
+
- Command execution paths: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/compiled_graph.py
|
|
279
|
+
- Lifecycle hook fire points — invoke path: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/utils/invoke_handler.py
|
|
280
|
+
- Lifecycle hook fire points — stream path: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/utils/stream_handler.py
|
|
281
|
+
- Lifecycle hook fire points — interrupt/resume: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/utils/heandler_utils.py
|
|
282
|
+
- Lifecycle hook fire points — checkpoint: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/core/graph/utils/utils.py
|
|
283
|
+
- Legacy docs: https://github.com/10xHub/Agentflow/blob/main/agentflow-docs/docs-mkdocs-legacy/reference/library/Command.md
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Checkpointing and Threads
|
|
2
|
+
|
|
3
|
+
Use this when adding persistence, thread APIs, message history, interrupts, or multi-worker behavior.
|
|
4
|
+
|
|
5
|
+
## Concept
|
|
6
|
+
|
|
7
|
+
A checkpointer persists per-thread `AgentState`, messages, and thread metadata. Without a checkpointer, each `invoke` starts from a fresh state. With a checkpointer, calls with the same `config.thread_id` resume the saved state.
|
|
8
|
+
|
|
9
|
+
## Config
|
|
10
|
+
|
|
11
|
+
At minimum, checkpointed calls need:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
config = {"thread_id": "conv-1"}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use `user_id` for multi-tenant scoping where the backend supports it.
|
|
18
|
+
|
|
19
|
+
## Backends
|
|
20
|
+
|
|
21
|
+
- `InMemoryCheckpointer`: development/tests/single-process only. State is lost on restart and not shared across workers.
|
|
22
|
+
- `PgCheckpointer`: production backend using PostgreSQL for durable data and Redis for fast cache. Requires setup before use.
|
|
23
|
+
|
|
24
|
+
Use production storage for load-balanced API deployments.
|
|
25
|
+
|
|
26
|
+
## API Surface
|
|
27
|
+
|
|
28
|
+
Checkpointer methods exist in async and sync forms:
|
|
29
|
+
|
|
30
|
+
- State: put/get/clear state and state cache.
|
|
31
|
+
- Messages: append/list/get/delete thread messages.
|
|
32
|
+
- Threads: create/get/list/clean thread metadata and content.
|
|
33
|
+
- Generic cache: namespaced key-value cache with optional TTL.
|
|
34
|
+
|
|
35
|
+
## REST and Client
|
|
36
|
+
|
|
37
|
+
The API exposes thread state/message/thread operations under `/v1/threads...` routes in the checkpointer router. The TypeScript client wraps these with methods such as:
|
|
38
|
+
|
|
39
|
+
- `threadState`
|
|
40
|
+
- `updateThreadState`
|
|
41
|
+
- `clearThreadState`
|
|
42
|
+
- `threadDetails`
|
|
43
|
+
- `threads`
|
|
44
|
+
- `threadMessages`
|
|
45
|
+
- `addThreadMessages`
|
|
46
|
+
- `singleMessage`
|
|
47
|
+
- `deleteMessage`
|
|
48
|
+
- `deleteThread`
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
- Always preserve `thread_id` across a conversation.
|
|
53
|
+
- Use `PgCheckpointer` or equivalent durable storage for multi-worker production.
|
|
54
|
+
- Store conversation continuity in the checkpointer, not the memory store.
|
|
55
|
+
- Use `aclean_thread`/delete APIs when removing a thread so state, messages, and metadata stay consistent.
|
|
56
|
+
|
|
57
|
+
## Source Map
|
|
58
|
+
|
|
59
|
+
- Base API: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/storage/checkpointer/base_checkpointer.py
|
|
60
|
+
- In-memory backend: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/storage/checkpointer/in_memory_checkpointer.py
|
|
61
|
+
- Postgres backend: https://github.com/10xGraph/10xGraph/blob/main/tenxgraph/storage/checkpointer/pg_checkpointer.py
|
|
62
|
+
- API router: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/src/app/routers/checkpointer
|
|
63
|
+
- TS endpoints: https://github.com/10xHub/agentflow-client/tree/main/src/endpoints
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CLI Commands
|
|
2
|
+
|
|
3
|
+
Use this when changing `10xgraph-api` command behavior, generated files, or deployment scaffolding.
|
|
4
|
+
|
|
5
|
+
## Package
|
|
6
|
+
|
|
7
|
+
Install:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install 10xgraph-api
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Entry point:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
10xgraph = tenxgraph_api.cli.main:main
|
|
17
|
+
agentflow = tenxgraph_api.cli.main:legacy_main # deprecated alias, removed in 2.0
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
`10xgraph init`
|
|
23
|
+
|
|
24
|
+
- Scaffolds `10xgraph.json`, `graph/__init__.py`, and `graph/react.py`, and suggests `10xgraph skills` as a next step.
|
|
25
|
+
- Options include `--path/-p`, `--force/-f`, `--prod`, `--verbose/-v`, and `--quiet/-q`.
|
|
26
|
+
- `--prod` also adds production project files such as `pyproject.toml` and `.pre-commit-config.yaml`.
|
|
27
|
+
|
|
28
|
+
`10xgraph api`
|
|
29
|
+
|
|
30
|
+
- Starts the FastAPI server for a compiled graph.
|
|
31
|
+
- Options include `--config/-c`, `--host/-H`, `--port/-p`, `--reload/--no-reload`, `--verbose/-v`, and `--quiet/-q`.
|
|
32
|
+
- Defaults are config `10xgraph.json`, host `127.0.0.1`, port `8000`, reload enabled.
|
|
33
|
+
|
|
34
|
+
`10xgraph play`
|
|
35
|
+
|
|
36
|
+
- Starts the API server and opens/prints the hosted playground URL.
|
|
37
|
+
- Accepts the same server options as `api`.
|
|
38
|
+
- Uses host/port to build the playground backend URL.
|
|
39
|
+
|
|
40
|
+
`10xgraph build`
|
|
41
|
+
|
|
42
|
+
- Generates Docker deployment files.
|
|
43
|
+
- Options include `--output/-o`, `--force/-f`, `--python-version`, `--port/-p`, `--docker-compose/--no-docker-compose`, `--service-name`, `--verbose/-v`, and `--quiet/-q`.
|
|
44
|
+
|
|
45
|
+
`10xgraph skills`
|
|
46
|
+
|
|
47
|
+
- Installs the bundled 10xGraph skill (an Agent Skills spec folder: `SKILL.md` + `references/`) into an agent-specific project directory. Every agent gets the same folder; paths inside it are relative to the skill directory.
|
|
48
|
+
- Prompts for the target agent when `--agent` is omitted:
|
|
49
|
+
- `1` / `codex`: `.agents/skills/10xgraph`
|
|
50
|
+
- `2` / `claude`: `.claude/skills/10xgraph`
|
|
51
|
+
- `3` / `github`: `.github/instructions/10xgraph.instructions.md` and `.github/skills/10xgraph`
|
|
52
|
+
- Options include `--agent/-a`, `--path/-p`, `--force/-f`, `--all`, `--list/-l`, `--verbose/-v`, and `--quiet/-q`.
|
|
53
|
+
- `--validate PATH` (repeatable) checks a skill directory, or a folder of skill directories, against the Agent Skills specification (https://agentskills.io/specification) and exits non-zero on errors. Nothing is installed.
|
|
54
|
+
- Source templates: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/cli/templates/skills
|
|
55
|
+
|
|
56
|
+
`10xgraph version`
|
|
57
|
+
|
|
58
|
+
- Prints CLI and library version information.
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- Use docs package names in user-facing text.
|
|
63
|
+
- Keep command options aligned with https://10xgraph.com/
|
|
64
|
+
- If command defaults change, update docs, templates, and tests together.
|
|
65
|
+
- Generated skill templates live under https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/cli/templates/skills
|
|
66
|
+
|
|
67
|
+
## Source Map
|
|
68
|
+
|
|
69
|
+
- CLI main: https://github.com/10xGraph/10xgraph-api/blob/main/tenxgraph_api/cli/main.py
|
|
70
|
+
- Commands: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/cli/commands
|
|
71
|
+
- CLI config/output/validation: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/cli/core
|
|
72
|
+
- Templates: https://github.com/10xGraph/10xgraph-api/tree/main/tenxgraph_api/cli/templates
|
|
73
|
+
- Docs: https://10xgraph.com/
|