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 @@
|
|
|
1
|
+
# Graph schemas
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
4
|
+
from tenxgraph.core.state import Message
|
|
5
|
+
from tenxgraph.utils import ResponseGranularity
|
|
6
|
+
|
|
7
|
+
from tenxgraph_api.src.app.core.auth.request_config import client_tool_call_error
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GraphInputSchema(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Schema for graph input including messages and configuration.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
messages: list[Message] = Field(
|
|
16
|
+
default_factory=list,
|
|
17
|
+
description=(
|
|
18
|
+
"List of messages to process through the graph. Required unless `resume` is set."
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
resume: Any = Field(
|
|
22
|
+
default=None,
|
|
23
|
+
description=(
|
|
24
|
+
"Answer for a thread paused by interrupt(); the paused node runs again and "
|
|
25
|
+
"interrupt() returns this value. Send it, possibly as null, only to resume."
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
initial_state: dict[str, Any] | None = Field(
|
|
29
|
+
default=None,
|
|
30
|
+
description="Initial state for the graph execution",
|
|
31
|
+
)
|
|
32
|
+
config: dict[str, Any] | None = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
description="Optional configuration for graph execution",
|
|
35
|
+
)
|
|
36
|
+
recursion_limit: int = Field(
|
|
37
|
+
default=25,
|
|
38
|
+
ge=1,
|
|
39
|
+
le=100,
|
|
40
|
+
description="Maximum recursion limit for graph execution",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@field_validator("messages")
|
|
44
|
+
@classmethod
|
|
45
|
+
def messages_must_not_carry_tool_calls(cls, v: list[Message]) -> list[Message]:
|
|
46
|
+
if error := client_tool_call_error(v):
|
|
47
|
+
raise ValueError(error)
|
|
48
|
+
return v
|
|
49
|
+
|
|
50
|
+
@model_validator(mode="after")
|
|
51
|
+
def messages_or_resume(self) -> "GraphInputSchema":
|
|
52
|
+
if not self.messages and not self.is_resume:
|
|
53
|
+
raise ValueError("messages must contain at least one message")
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_resume(self) -> bool:
|
|
58
|
+
"""Whether the request resumes an interrupt() (``resume`` was sent, even as null)."""
|
|
59
|
+
return "resume" in self.model_fields_set
|
|
60
|
+
|
|
61
|
+
response_granularity: ResponseGranularity = Field(
|
|
62
|
+
default=ResponseGranularity.LOW,
|
|
63
|
+
description="Granularity of the response (full, partial, low)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class GraphInvokeOutputSchema(BaseModel):
|
|
68
|
+
"""
|
|
69
|
+
Schema for graph invoke output.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
messages: list[Message] = Field(
|
|
73
|
+
...,
|
|
74
|
+
description="Final processed messages from the graph",
|
|
75
|
+
)
|
|
76
|
+
state: dict[str, Any] | None = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
description="State information from the graph execution",
|
|
79
|
+
)
|
|
80
|
+
context: list[Message] | None = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="Context information from the graph execution",
|
|
83
|
+
)
|
|
84
|
+
summary: str | None = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
description="Summary information from the graph execution",
|
|
87
|
+
)
|
|
88
|
+
meta: dict[str, Any] | None = Field(
|
|
89
|
+
default=None,
|
|
90
|
+
description="Meta information from the graph execution",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# class GraphStreamChunkSchema(BaseModel):
|
|
95
|
+
# """
|
|
96
|
+
# Schema for individual stream chunks from graph execution.
|
|
97
|
+
# """
|
|
98
|
+
|
|
99
|
+
# data: dict[str, Any] = Field(..., description="Chunk data")
|
|
100
|
+
# metadata: dict[str, Any] | None = Field(default=None, description="Chunk metadata")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class NodeSchema(BaseModel):
|
|
104
|
+
"""Schema for individual graph nodes."""
|
|
105
|
+
|
|
106
|
+
id: str = Field(..., description="Unique identifier for the node")
|
|
107
|
+
name: str = Field(..., description="Name of the node")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class EdgeSchema(BaseModel):
|
|
111
|
+
"""Schema for individual graph edges."""
|
|
112
|
+
|
|
113
|
+
id: str = Field(..., description="Unique identifier for the edge")
|
|
114
|
+
source: str = Field(..., description="Source node identifier")
|
|
115
|
+
target: str = Field(..., description="Target node identifier")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class GraphInfoSchema(BaseModel):
|
|
119
|
+
"""Schema for graph metadata and configuration."""
|
|
120
|
+
|
|
121
|
+
node_count: int = Field(..., description="Number of nodes in the graph")
|
|
122
|
+
edge_count: int = Field(..., description="Number of edges in the graph")
|
|
123
|
+
is_realtime: bool = Field(
|
|
124
|
+
False,
|
|
125
|
+
description="Whether the graph is a realtime/live agent (driven over /v1/graph/live)",
|
|
126
|
+
)
|
|
127
|
+
checkpointer: bool = Field(..., description="Whether checkpointer is enabled")
|
|
128
|
+
checkpointer_type: str | None = Field(None, description="Type of checkpointer if enabled")
|
|
129
|
+
publisher: bool = Field(..., description="Whether publisher is enabled")
|
|
130
|
+
store: bool = Field(..., description="Whether store is enabled")
|
|
131
|
+
interrupt_before: list[str] | None = Field(None, description="Nodes to interrupt before")
|
|
132
|
+
interrupt_after: list[str] | None = Field(None, description="Nodes to interrupt after")
|
|
133
|
+
context_type: str | None = Field(None, description="Type of context for the graph")
|
|
134
|
+
id_generator: str | None = Field(None, description="ID generator type for the graph")
|
|
135
|
+
id_type: str | None = Field(None, description="ID type for the graph")
|
|
136
|
+
state_type: str | None = Field(None, description="State type for the graph")
|
|
137
|
+
state_fields: list[str] | None = Field(None, description="State fields for the graph")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class GraphSchema(BaseModel):
|
|
141
|
+
"""Schema for the complete graph structure."""
|
|
142
|
+
|
|
143
|
+
info: GraphInfoSchema = Field(..., description="Graph metadata and configuration")
|
|
144
|
+
nodes: list[NodeSchema] = Field(..., description="List of nodes in the graph")
|
|
145
|
+
edges: list[EdgeSchema] = Field(..., description="List of edges in the graph")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class ToolSchema(BaseModel):
|
|
149
|
+
"""Schema for a single tool exposed by a tool node."""
|
|
150
|
+
|
|
151
|
+
name: str = Field(..., description="Name of the tool")
|
|
152
|
+
description: str = Field("", description="Human-readable description of the tool")
|
|
153
|
+
source: Literal["local", "mcp", "remote"] = Field(
|
|
154
|
+
...,
|
|
155
|
+
description=(
|
|
156
|
+
"Where the tool is defined: 'local' (a Python function on the node), "
|
|
157
|
+
"'mcp' (provided by a connected MCP server), or 'remote' "
|
|
158
|
+
"(client-side tool declared in 10xgraph.json)."
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
parameters: dict[str, Any] = Field(
|
|
162
|
+
default_factory=dict,
|
|
163
|
+
description="JSON Schema of the tool's parameters (OpenAI function-calling shape)",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ToolNodeSchema(BaseModel):
|
|
168
|
+
"""Schema for a tool node and the tools it exposes."""
|
|
169
|
+
|
|
170
|
+
node_name: str = Field(..., description="Name of the tool node in the graph")
|
|
171
|
+
tool_count: int = Field(..., description="Number of tools exposed by this node")
|
|
172
|
+
tools: list[ToolSchema] = Field(default_factory=list, description="Tools exposed by this node")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class GraphToolsSchema(BaseModel):
|
|
176
|
+
"""Schema for all tool nodes and their tools across the graph."""
|
|
177
|
+
|
|
178
|
+
node_count: int = Field(..., description="Number of tool nodes in the graph")
|
|
179
|
+
tool_count: int = Field(..., description="Total number of tools across all tool nodes")
|
|
180
|
+
nodes: list[ToolNodeSchema] = Field(
|
|
181
|
+
default_factory=list, description="Tool nodes and their tools"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class ObsSpanSchema(BaseModel):
|
|
186
|
+
"""A reconstructed span in the run trace."""
|
|
187
|
+
|
|
188
|
+
id: str = Field(..., description="Span id (stable within the run)")
|
|
189
|
+
name: str = Field(..., description="Display name, e.g. 'node: agent' or 'tool: get_weather'")
|
|
190
|
+
kind: Literal["root", "node", "llm", "tool"] = Field(..., description="Span kind")
|
|
191
|
+
parent: str | None = Field(None, description="Parent span id")
|
|
192
|
+
start_ms: float = Field(..., description="Start offset from run start, in ms")
|
|
193
|
+
duration_ms: float = Field(..., description="Duration in ms")
|
|
194
|
+
model: str | None = Field(None, description="LLM model (llm spans)")
|
|
195
|
+
input_tokens: int | None = Field(None, description="Prompt tokens (llm spans)")
|
|
196
|
+
output_tokens: int | None = Field(None, description="Completion tokens (llm spans)")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ObsEventSchema(BaseModel):
|
|
200
|
+
"""A reconstructed event in the run trace."""
|
|
201
|
+
|
|
202
|
+
id: str = Field(..., description="Event id")
|
|
203
|
+
type: str = Field(..., description="Event type: message | updates | state | error | result")
|
|
204
|
+
node: str = Field("", description="Node the event is attributed to")
|
|
205
|
+
offset_ms: float = Field(..., description="Offset from run start, in ms")
|
|
206
|
+
summary: str = Field("", description="Human-readable one-line summary")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ObsTokenUsageSchema(BaseModel):
|
|
210
|
+
"""Aggregated token usage for the run."""
|
|
211
|
+
|
|
212
|
+
prompt_tokens: int = 0
|
|
213
|
+
completion_tokens: int = 0
|
|
214
|
+
reasoning_tokens: int = 0
|
|
215
|
+
total_tokens: int = 0
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class ObsRunSchema(BaseModel):
|
|
219
|
+
"""A single reconstructed run trace."""
|
|
220
|
+
|
|
221
|
+
run_id: str
|
|
222
|
+
thread_id: str
|
|
223
|
+
status: str = Field(..., description="running | done | error | stopped")
|
|
224
|
+
started_at: float | None = None
|
|
225
|
+
finished_at: float | None = None
|
|
226
|
+
duration_ms: float = 0.0
|
|
227
|
+
spans: list[ObsSpanSchema] = Field(default_factory=list)
|
|
228
|
+
events: list[ObsEventSchema] = Field(default_factory=list)
|
|
229
|
+
usage: ObsTokenUsageSchema = Field(default_factory=ObsTokenUsageSchema)
|
|
230
|
+
llm_calls: int = 0
|
|
231
|
+
tool_calls: int = 0
|
|
232
|
+
iterations: int = 0
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class ObservabilitySchema(BaseModel):
|
|
236
|
+
"""Observability payload for a thread: available runs + the selected run."""
|
|
237
|
+
|
|
238
|
+
thread_id: str
|
|
239
|
+
run_count: int = 0
|
|
240
|
+
run_ids: list[str] = Field(default_factory=list)
|
|
241
|
+
run: ObsRunSchema | None = Field(None, description="The requested/latest run, if any")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class GraphStopSchema(BaseModel):
|
|
245
|
+
"""Schema for stopping graph execution."""
|
|
246
|
+
|
|
247
|
+
thread_id: str = Field(..., min_length=1, description="Thread ID to stop execution for")
|
|
248
|
+
|
|
249
|
+
@field_validator("thread_id")
|
|
250
|
+
@classmethod
|
|
251
|
+
def thread_id_not_blank(cls, v: str) -> str:
|
|
252
|
+
if not v.strip():
|
|
253
|
+
raise ValueError("thread_id cannot be empty or whitespace")
|
|
254
|
+
return v.strip()
|
|
255
|
+
|
|
256
|
+
config: dict[str, Any] | None = Field(
|
|
257
|
+
default=None, description="Optional configuration for the stop operation"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class FixGraphRequestSchema(BaseModel):
|
|
262
|
+
"""Schema for fixing graph state by removing messages with empty tool call content."""
|
|
263
|
+
|
|
264
|
+
thread_id: str = Field(..., min_length=1, description="Thread ID to fix the graph state for")
|
|
265
|
+
|
|
266
|
+
@field_validator("thread_id")
|
|
267
|
+
@classmethod
|
|
268
|
+
def thread_id_not_blank(cls, v: str) -> str:
|
|
269
|
+
if not v.strip():
|
|
270
|
+
raise ValueError("thread_id cannot be empty or whitespace")
|
|
271
|
+
return v.strip()
|
|
272
|
+
|
|
273
|
+
config: dict[str, Any] | None = Field(
|
|
274
|
+
default=None, description="Optional configuration for the fix operation"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class FixGraphResponseSchema(BaseModel):
|
|
279
|
+
"""Schema for the fix graph operation response."""
|
|
280
|
+
|
|
281
|
+
success: bool = Field(..., description="Whether the fix operation was successful")
|
|
282
|
+
message: str = Field(..., description="Status message from the fix operation")
|
|
283
|
+
removed_count: int = Field(
|
|
284
|
+
default=0, description="Number of messages with empty tool calls that were removed"
|
|
285
|
+
)
|
|
286
|
+
state: dict[str, Any] | None = Field(
|
|
287
|
+
default=None, description="Updated state after fixing the graph"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class WsGraphInputSchema(BaseModel):
|
|
292
|
+
"""
|
|
293
|
+
WebSocket graph input schema.
|
|
294
|
+
|
|
295
|
+
Extends ``GraphInputSchema`` with an ``invoke_type`` discriminator so the
|
|
296
|
+
server can log the intent and validate fields appropriately without
|
|
297
|
+
inspecting the stream.
|
|
298
|
+
|
|
299
|
+
``invoke_type="fresh"``
|
|
300
|
+
Start a new run. ``messages`` must contain at least one user message.
|
|
301
|
+
``config.thread_id`` is optional — omitting it starts a new thread.
|
|
302
|
+
|
|
303
|
+
``invoke_type="resume"``
|
|
304
|
+
Continue after a remote tool call. ``tool_result`` must contain the
|
|
305
|
+
tool-result messages. ``config.thread_id`` is required so the server
|
|
306
|
+
can resume the correct checkpointed thread. ``messages`` is unused.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
invoke_type: Literal["fresh", "resume"] = Field(
|
|
310
|
+
default="fresh",
|
|
311
|
+
description=(
|
|
312
|
+
"Run type: 'fresh' to start a new run; 'resume' to continue after a remote tool call."
|
|
313
|
+
),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# ── Fresh fields ─────────────────────────────────────────────────────
|
|
317
|
+
messages: list[Message] = Field(
|
|
318
|
+
default_factory=list,
|
|
319
|
+
description="User messages — required and non-empty for 'fresh' runs.",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# ── Resume fields ─────────────────────────────────────────────────────
|
|
323
|
+
tool_result: list[Message] | None = Field(
|
|
324
|
+
default=None,
|
|
325
|
+
description=(
|
|
326
|
+
"Tool-result messages to inject into the graph state for 'resume' runs. "
|
|
327
|
+
"The server passes these to stream_graph as the input messages so the "
|
|
328
|
+
"graph checkpointer picks them up and the graph continues from where it "
|
|
329
|
+
"left off. Ignored for 'fresh' runs."
|
|
330
|
+
),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# ── Shared fields (mirror GraphInputSchema) ───────────────────────────
|
|
334
|
+
initial_state: dict[str, Any] | None = Field(
|
|
335
|
+
default=None,
|
|
336
|
+
description="Initial state for the graph execution.",
|
|
337
|
+
)
|
|
338
|
+
config: dict[str, Any] | None = Field(
|
|
339
|
+
default=None,
|
|
340
|
+
description="Optional configuration. Must include 'thread_id' for resume runs.",
|
|
341
|
+
)
|
|
342
|
+
recursion_limit: int = Field(
|
|
343
|
+
default=25,
|
|
344
|
+
ge=1,
|
|
345
|
+
le=100,
|
|
346
|
+
description="Maximum recursion limit for graph execution.",
|
|
347
|
+
)
|
|
348
|
+
response_granularity: ResponseGranularity = Field(
|
|
349
|
+
default=ResponseGranularity.LOW,
|
|
350
|
+
description="Granularity of the response (full, partial, low).",
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
@model_validator(mode="after")
|
|
354
|
+
def validate_by_invoke_type(self) -> "WsGraphInputSchema":
|
|
355
|
+
if self.invoke_type == "fresh":
|
|
356
|
+
if not self.messages:
|
|
357
|
+
raise ValueError("messages must not be empty for invoke_type='fresh'")
|
|
358
|
+
elif self.invoke_type == "resume":
|
|
359
|
+
if not self.tool_result:
|
|
360
|
+
raise ValueError("tool_result must not be empty for invoke_type='resume'")
|
|
361
|
+
if not (self.config or {}).get("thread_id"):
|
|
362
|
+
raise ValueError("config.thread_id is required for invoke_type='resume'")
|
|
363
|
+
# Resume sends tool *results*; neither run type may carry a tool call.
|
|
364
|
+
for messages in (self.messages, self.tool_result):
|
|
365
|
+
if error := client_tool_call_error(messages):
|
|
366
|
+
raise ValueError(error)
|
|
367
|
+
return self
|
|
368
|
+
|
|
369
|
+
def to_graph_input(self) -> "GraphInputSchema":
|
|
370
|
+
"""
|
|
371
|
+
Convert to a ``GraphInputSchema`` for ``GraphService.stream_graph``.
|
|
372
|
+
|
|
373
|
+
For *fresh* runs the user's ``messages`` are passed directly.
|
|
374
|
+
For *resume* runs the ``tool_result`` messages are used — the graph
|
|
375
|
+
loads the saved checkpoint (identified by ``config.thread_id``) and
|
|
376
|
+
the tool-result messages are appended to the state, which the graph
|
|
377
|
+
then processes to continue execution.
|
|
378
|
+
"""
|
|
379
|
+
msgs = self.messages if self.invoke_type == "fresh" else (self.tool_result or [])
|
|
380
|
+
return GraphInputSchema(
|
|
381
|
+
messages=msgs,
|
|
382
|
+
initial_state=self.initial_state,
|
|
383
|
+
config=self.config,
|
|
384
|
+
recursion_limit=self.recursion_limit,
|
|
385
|
+
response_granularity=self.response_granularity,
|
|
386
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Graph services
|