pleiad 0.1.0__tar.gz
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.
- pleiad-0.1.0/LICENSE +21 -0
- pleiad-0.1.0/PKG-INFO +317 -0
- pleiad-0.1.0/README.md +268 -0
- pleiad-0.1.0/pleiad.egg-info/PKG-INFO +317 -0
- pleiad-0.1.0/pleiad.egg-info/SOURCES.txt +151 -0
- pleiad-0.1.0/pleiad.egg-info/dependency_links.txt +1 -0
- pleiad-0.1.0/pleiad.egg-info/requires.txt +47 -0
- pleiad-0.1.0/pleiad.egg-info/top_level.txt +1 -0
- pleiad-0.1.0/pyproject.toml +149 -0
- pleiad-0.1.0/setup.cfg +4 -0
- pleiad-0.1.0/src/__init__.py +0 -0
- pleiad-0.1.0/src/agent/__init__.py +0 -0
- pleiad-0.1.0/src/agent/agent.py +62 -0
- pleiad-0.1.0/src/agent/errors.py +21 -0
- pleiad-0.1.0/src/agent/policy.py +18 -0
- pleiad-0.1.0/src/agent/prompt.py +37 -0
- pleiad-0.1.0/src/capabilities/__init__.py +0 -0
- pleiad-0.1.0/src/capabilities/errors.py +51 -0
- pleiad-0.1.0/src/capabilities/search.py +193 -0
- pleiad-0.1.0/src/context/__init__.py +0 -0
- pleiad-0.1.0/src/context/budget.py +121 -0
- pleiad-0.1.0/src/context/compaction/__init__.py +0 -0
- pleiad-0.1.0/src/context/compaction/compactor.py +194 -0
- pleiad-0.1.0/src/context/compaction/models.py +26 -0
- pleiad-0.1.0/src/context/errors.py +21 -0
- pleiad-0.1.0/src/context/rendering.py +36 -0
- pleiad-0.1.0/src/context/selection/__init__.py +0 -0
- pleiad-0.1.0/src/context/selection/builder.py +43 -0
- pleiad-0.1.0/src/context/selection/models.py +24 -0
- pleiad-0.1.0/src/contracts.py +153 -0
- pleiad-0.1.0/src/errors/__init__.py +0 -0
- pleiad-0.1.0/src/errors/base.py +59 -0
- pleiad-0.1.0/src/events/__init__.py +0 -0
- pleiad-0.1.0/src/events/contracts.py +21 -0
- pleiad-0.1.0/src/events/graph.py +46 -0
- pleiad-0.1.0/src/events/message.py +62 -0
- pleiad-0.1.0/src/events/runtime.py +166 -0
- pleiad-0.1.0/src/events/supervisor.py +63 -0
- pleiad-0.1.0/src/logging/__init__.py +0 -0
- pleiad-0.1.0/src/logging/context.py +88 -0
- pleiad-0.1.0/src/logging/filters.py +23 -0
- pleiad-0.1.0/src/logging/rotation.py +44 -0
- pleiad-0.1.0/src/mcp/__init__.py +0 -0
- pleiad-0.1.0/src/mcp/contracts.py +33 -0
- pleiad-0.1.0/src/mcp/discovery.py +91 -0
- pleiad-0.1.0/src/mcp/errors.py +60 -0
- pleiad-0.1.0/src/mcp/tool.py +125 -0
- pleiad-0.1.0/src/memory/__init__.py +0 -0
- pleiad-0.1.0/src/memory/backends/__init__.py +0 -0
- pleiad-0.1.0/src/memory/backends/in_memory.py +139 -0
- pleiad-0.1.0/src/memory/backends/sqlite.py +296 -0
- pleiad-0.1.0/src/memory/errors.py +60 -0
- pleiad-0.1.0/src/memory/short_memory.py +195 -0
- pleiad-0.1.0/src/memory/turn.py +135 -0
- pleiad-0.1.0/src/messages/__init__.py +0 -0
- pleiad-0.1.0/src/messages/message.py +109 -0
- pleiad-0.1.0/src/models/__init__.py +0 -0
- pleiad-0.1.0/src/models/anthropic/__init__.py +0 -0
- pleiad-0.1.0/src/models/anthropic/client.py +145 -0
- pleiad-0.1.0/src/models/anthropic/request.py +84 -0
- pleiad-0.1.0/src/models/anthropic/response.py +335 -0
- pleiad-0.1.0/src/models/contracts.py +12 -0
- pleiad-0.1.0/src/models/errors.py +152 -0
- pleiad-0.1.0/src/models/openai/__init__.py +0 -0
- pleiad-0.1.0/src/models/openai/client.py +145 -0
- pleiad-0.1.0/src/models/openai/request.py +114 -0
- pleiad-0.1.0/src/models/openai/response.py +431 -0
- pleiad-0.1.0/src/models/providers/__init__.py +0 -0
- pleiad-0.1.0/src/models/providers/anthropic.py +129 -0
- pleiad-0.1.0/src/models/providers/deepseek.py +235 -0
- pleiad-0.1.0/src/models/providers/glm.py +263 -0
- pleiad-0.1.0/src/models/providers/kimi.py +253 -0
- pleiad-0.1.0/src/models/providers/minimax.py +235 -0
- pleiad-0.1.0/src/models/providers/openai.py +120 -0
- pleiad-0.1.0/src/models/providers/qwen.py +358 -0
- pleiad-0.1.0/src/models/result.py +135 -0
- pleiad-0.1.0/src/observability/__init__.py +0 -0
- pleiad-0.1.0/src/observability/attributes.py +66 -0
- pleiad-0.1.0/src/observability/config.py +31 -0
- pleiad-0.1.0/src/observability/errors.py +17 -0
- pleiad-0.1.0/src/observability/integrations/__init__.py +0 -0
- pleiad-0.1.0/src/observability/integrations/langfuse.py +163 -0
- pleiad-0.1.0/src/observability/metrics.py +68 -0
- pleiad-0.1.0/src/observability/privacy.py +191 -0
- pleiad-0.1.0/src/observability/tracing.py +168 -0
- pleiad-0.1.0/src/orchestration/__init__.py +0 -0
- pleiad-0.1.0/src/orchestration/errors.py +49 -0
- pleiad-0.1.0/src/orchestration/graph/__init__.py +0 -0
- pleiad-0.1.0/src/orchestration/graph/edge.py +59 -0
- pleiad-0.1.0/src/orchestration/graph/errors.py +41 -0
- pleiad-0.1.0/src/orchestration/graph/executor.py +496 -0
- pleiad-0.1.0/src/orchestration/graph/graph.py +82 -0
- pleiad-0.1.0/src/orchestration/graph/mapping.py +55 -0
- pleiad-0.1.0/src/orchestration/graph/node.py +86 -0
- pleiad-0.1.0/src/orchestration/graph/ports.py +12 -0
- pleiad-0.1.0/src/orchestration/graph/scheduler.py +20 -0
- pleiad-0.1.0/src/orchestration/graph/state.py +95 -0
- pleiad-0.1.0/src/orchestration/graph/validation.py +137 -0
- pleiad-0.1.0/src/orchestration/handoff/__init__.py +0 -0
- pleiad-0.1.0/src/orchestration/handoff/handoff.py +34 -0
- pleiad-0.1.0/src/orchestration/handoff/state.py +30 -0
- pleiad-0.1.0/src/orchestration/handoff/tools.py +11 -0
- pleiad-0.1.0/src/orchestration/supervisor/__init__.py +0 -0
- pleiad-0.1.0/src/orchestration/supervisor/state.py +31 -0
- pleiad-0.1.0/src/orchestration/supervisor/supervisor.py +480 -0
- pleiad-0.1.0/src/orchestration/supervisor/tools.py +214 -0
- pleiad-0.1.0/src/orchestration/supervisor/worker.py +69 -0
- pleiad-0.1.0/src/py.typed +2 -0
- pleiad-0.1.0/src/runtime/__init__.py +0 -0
- pleiad-0.1.0/src/runtime/config.py +23 -0
- pleiad-0.1.0/src/runtime/errors.py +25 -0
- pleiad-0.1.0/src/runtime/execution/__init__.py +0 -0
- pleiad-0.1.0/src/runtime/execution/control.py +37 -0
- pleiad-0.1.0/src/runtime/execution/loop.py +1075 -0
- pleiad-0.1.0/src/runtime/execution/model_input.py +495 -0
- pleiad-0.1.0/src/runtime/execution/runner.py +847 -0
- pleiad-0.1.0/src/runtime/execution/stream.py +68 -0
- pleiad-0.1.0/src/runtime/lifecycle/__init__.py +0 -0
- pleiad-0.1.0/src/runtime/lifecycle/result.py +38 -0
- pleiad-0.1.0/src/runtime/lifecycle/state.py +96 -0
- pleiad-0.1.0/src/runtime/policy.py +84 -0
- pleiad-0.1.0/src/sandbox/__init__.py +0 -0
- pleiad-0.1.0/src/sandbox/backends/__init__.py +0 -0
- pleiad-0.1.0/src/sandbox/backends/oci.py +485 -0
- pleiad-0.1.0/src/sandbox/backends/process.py +136 -0
- pleiad-0.1.0/src/sandbox/errors.py +44 -0
- pleiad-0.1.0/src/sandbox/models.py +120 -0
- pleiad-0.1.0/src/sandbox/policy.py +52 -0
- pleiad-0.1.0/src/sandbox/sandbox.py +18 -0
- pleiad-0.1.0/src/schemas.py +38 -0
- pleiad-0.1.0/src/skills/__init__.py +0 -0
- pleiad-0.1.0/src/skills/contracts.py +37 -0
- pleiad-0.1.0/src/skills/errors.py +25 -0
- pleiad-0.1.0/src/skills/loader.py +88 -0
- pleiad-0.1.0/src/skills/manifest.py +129 -0
- pleiad-0.1.0/src/skills/package.py +113 -0
- pleiad-0.1.0/src/tools/__init__.py +0 -0
- pleiad-0.1.0/src/tools/base.py +113 -0
- pleiad-0.1.0/src/tools/builtins/__init__.py +0 -0
- pleiad-0.1.0/src/tools/builtins/command.py +132 -0
- pleiad-0.1.0/src/tools/builtins/defaults.py +60 -0
- pleiad-0.1.0/src/tools/builtins/file_support.py +188 -0
- pleiad-0.1.0/src/tools/builtins/read_file.py +113 -0
- pleiad-0.1.0/src/tools/builtins/write_file.py +120 -0
- pleiad-0.1.0/src/tools/contracts.py +94 -0
- pleiad-0.1.0/src/tools/errors.py +211 -0
- pleiad-0.1.0/src/tools/interruption.py +37 -0
- pleiad-0.1.0/src/tools/manifest.py +70 -0
- pleiad-0.1.0/src/tools/registry.py +76 -0
- pleiad-0.1.0/src/tools/runtime.py +191 -0
- pleiad-0.1.0/src/version.py +3 -0
- pleiad-0.1.0/tests/test_errors.py +66 -0
- pleiad-0.1.0/tests/test_logging.py +220 -0
pleiad-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pleiad contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pleiad-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pleiad
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A provider-neutral runtime for building and executing Agents.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Source, https://github.com/RyzeAngler/Pleiad
|
|
7
|
+
Project-URL: Issues, https://github.com/RyzeAngler/Pleiad/issues
|
|
8
|
+
Requires-Python: <3.13,>=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: jsonschema<5.0.0,>=4.25.0
|
|
12
|
+
Requires-Dist: opentelemetry-api<2.0.0,>=1.44.0
|
|
13
|
+
Requires-Dist: pydantic<3.0.0,>=2.12.0
|
|
14
|
+
Requires-Dist: pyyaml<7.0.0,>=6.0.3
|
|
15
|
+
Provides-Extra: openai
|
|
16
|
+
Requires-Dist: httpx[socks]<1.0.0,>=0.28.1; extra == "openai"
|
|
17
|
+
Requires-Dist: openai<3.0.0,>=2.0.0; extra == "openai"
|
|
18
|
+
Provides-Extra: anthropic
|
|
19
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "anthropic"
|
|
20
|
+
Provides-Extra: glm
|
|
21
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "glm"
|
|
22
|
+
Requires-Dist: zai-sdk<0.3.0,>=0.2.3; extra == "glm"
|
|
23
|
+
Provides-Extra: qwen
|
|
24
|
+
Requires-Dist: aiohttp<4.0.0,>=3.9.0; extra == "qwen"
|
|
25
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "qwen"
|
|
26
|
+
Requires-Dist: dashscope<2.0.0,>=1.27.4; extra == "qwen"
|
|
27
|
+
Provides-Extra: kimi
|
|
28
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "kimi"
|
|
29
|
+
Requires-Dist: openai<3.0.0,>=2.0.0; extra == "kimi"
|
|
30
|
+
Provides-Extra: deepseek
|
|
31
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "deepseek"
|
|
32
|
+
Requires-Dist: openai<3.0.0,>=2.0.0; extra == "deepseek"
|
|
33
|
+
Provides-Extra: minimax
|
|
34
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "minimax"
|
|
35
|
+
Requires-Dist: openai<3.0.0,>=2.0.0; extra == "minimax"
|
|
36
|
+
Provides-Extra: models
|
|
37
|
+
Requires-Dist: aiohttp<4.0.0,>=3.9.0; extra == "models"
|
|
38
|
+
Requires-Dist: anthropic<1.0.0,>=0.67.0; extra == "models"
|
|
39
|
+
Requires-Dist: dashscope<2.0.0,>=1.27.4; extra == "models"
|
|
40
|
+
Requires-Dist: httpx[socks]<1.0.0,>=0.28.1; extra == "models"
|
|
41
|
+
Requires-Dist: openai<3.0.0,>=2.0.0; extra == "models"
|
|
42
|
+
Requires-Dist: zai-sdk<0.3.0,>=0.2.3; extra == "models"
|
|
43
|
+
Provides-Extra: mcp
|
|
44
|
+
Requires-Dist: fastmcp<5.0.0,>=4.0.4; extra == "mcp"
|
|
45
|
+
Provides-Extra: observability
|
|
46
|
+
Requires-Dist: langfuse<5.0.0,>=4.15.4; extra == "observability"
|
|
47
|
+
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.44.0; extra == "observability"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# Pleiad
|
|
51
|
+
|
|
52
|
+
Pleiad is the provider-neutral Agent runtime and development framework. The
|
|
53
|
+
official interactive application, Pleiad Weave, is maintained separately.
|
|
54
|
+
|
|
55
|
+
The source package lives directly in `src/`. Setuptools maps that directory to
|
|
56
|
+
the public `pleiad` import namespace; there is no nested `src/pleiad/` directory.
|
|
57
|
+
|
|
58
|
+
The package intentionally contains no TUI, HTTP application, CLI, or product
|
|
59
|
+
configuration. Install optional model extras only when the host application
|
|
60
|
+
needs the bundled provider adapters.
|
|
61
|
+
|
|
62
|
+
Source code and C4 architecture documentation are maintained in the
|
|
63
|
+
[project repository](https://github.com/RyzeAngler/Pleiad).
|
|
64
|
+
|
|
65
|
+
## Model clients
|
|
66
|
+
|
|
67
|
+
The public model objects select the request protocol by their class:
|
|
68
|
+
`OpenAIModelClient` accepts an OpenAI Chat Completions body, and
|
|
69
|
+
`AnthropicModelClient` accepts an Anthropic Messages body. The `provider`
|
|
70
|
+
argument selects the vendor SDK binding that sends the request and converts
|
|
71
|
+
the vendor response back to that protocol. Provider-specific request options
|
|
72
|
+
belong in `provider_options`; callers do not pass a separate protocol value or
|
|
73
|
+
a shared model client.
|
|
74
|
+
|
|
75
|
+
Install `pleiad[models]` for all bundled provider adapters. The protocol object
|
|
76
|
+
determines the public request and response shape; the provider module owns the
|
|
77
|
+
vendor SDK call and the conversion at that boundary:
|
|
78
|
+
|
|
79
|
+
| provider | `OpenAIModelClient` | `AnthropicModelClient` |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `openai` | OpenAI SDK | — |
|
|
82
|
+
| `anthropic` | — | Anthropic SDK |
|
|
83
|
+
| `deepseek` | OpenAI SDK | Anthropic SDK |
|
|
84
|
+
| `glm` | Zhipu `zai-sdk` | Anthropic SDK |
|
|
85
|
+
| `qwen` | DashScope SDK | Anthropic SDK |
|
|
86
|
+
| `kimi` | OpenAI SDK | Anthropic SDK |
|
|
87
|
+
| `minimax` | OpenAI SDK | Anthropic SDK |
|
|
88
|
+
|
|
89
|
+
For example, this remains an OpenAI request even though DeepSeek is the
|
|
90
|
+
provider:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from pleiad.models.openai.client import OpenAIModelClient
|
|
94
|
+
|
|
95
|
+
client = OpenAIModelClient(
|
|
96
|
+
provider="deepseek",
|
|
97
|
+
model="deepseek-chat",
|
|
98
|
+
api_key="...",
|
|
99
|
+
)
|
|
100
|
+
response = await client.complete(
|
|
101
|
+
{"messages": [{"role": "user", "content": "hello"}]},
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Use `AnthropicModelClient` with the same `provider` value when the caller's
|
|
106
|
+
body is an Anthropic Messages request. There is no public `protocol` argument
|
|
107
|
+
and no public common model client.
|
|
108
|
+
|
|
109
|
+
## Agent declarations
|
|
110
|
+
|
|
111
|
+
`Agent` is a frozen declaration of a model, prompt, capabilities, selected Skills,
|
|
112
|
+
output schema, and policy hints. Import it from `pleiad.agent.agent`.
|
|
113
|
+
|
|
114
|
+
Every Agent receives the default Tool references `read_file`, `write_file`, and
|
|
115
|
+
`command`. Explicit Tool references are appended after those defaults and duplicate
|
|
116
|
+
codes are rejected. The host must bind every declared Tool before a Run starts.
|
|
117
|
+
|
|
118
|
+
Selected Skills contribute catalog metadata to the initial prompt. Their instruction
|
|
119
|
+
files are not loaded into that prompt; the host must bind each selected Skill to the
|
|
120
|
+
SDK `read_file` Tool so the model can request its `SKILL.md` when needed.
|
|
121
|
+
|
|
122
|
+
## Context and Runtime
|
|
123
|
+
|
|
124
|
+
`ContextBuilder` selects request-local `ContextBlock` references and
|
|
125
|
+
`ContextRenderer` renders them beside the current User Message. The original
|
|
126
|
+
message and dynamic references remain separate from `ShortMemory`; context
|
|
127
|
+
budgets and compaction are evaluated for each model request at complete Turn
|
|
128
|
+
boundaries.
|
|
129
|
+
|
|
130
|
+
`Runner` owns the ShortMemory-backed Model → Tool → Model loop. Bind concrete
|
|
131
|
+
model clients on the `Agent`, then supply a `ShortMemory`, `RunConfig`, and the
|
|
132
|
+
host's `ToolRegistry`, `SkillLoader`, and `ToolRuntime`:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from pleiad.agent.agent import Agent
|
|
136
|
+
from pleiad.memory.short_memory import ShortMemory
|
|
137
|
+
from pleiad.runtime.config import RunConfig
|
|
138
|
+
from pleiad.runtime.execution.runner import Runner
|
|
139
|
+
|
|
140
|
+
result = await runner.run(
|
|
141
|
+
Agent(name="assistant", model=client, prompt="Answer the user."),
|
|
142
|
+
"hello",
|
|
143
|
+
memory=ShortMemory.in_memory("conversation-1"),
|
|
144
|
+
config=RunConfig(),
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
An interrupted run returns a `RunState` checkpoint in `RunResult`; resume it
|
|
149
|
+
with `Runner.resume` and explicit interruption resolutions. `RunStream` uses
|
|
150
|
+
the same execution path for ordered lifecycle events and early-close
|
|
151
|
+
cancellation. SDK notifications inherit the public `Event` envelope from
|
|
152
|
+
`pleiad.events.contracts`; product applications can define typed Event
|
|
153
|
+
subclasses without registering them with Pleiad. Stream buffering is bounded
|
|
154
|
+
and applies backpressure, while terminal events remain associated with their
|
|
155
|
+
`run_id`.
|
|
156
|
+
|
|
157
|
+
Messages are concrete role-specific contracts imported from
|
|
158
|
+
`pleiad.messages.message`: `SystemMessage`, `UserMessage`, `AssistantMessage`,
|
|
159
|
+
and `ToolMessage`. An Assistant owns its model-declared `tool_calls`; the
|
|
160
|
+
corresponding execution output is a later Tool message. Completed message
|
|
161
|
+
snapshots and live text or Tool-argument deltas are published through the
|
|
162
|
+
typed events in `pleiad.events.message`.
|
|
163
|
+
|
|
164
|
+
Use the stream as an async context manager when the consumer may stop before
|
|
165
|
+
the Run completes. Leaving the scope calls `aclose()`, cancels the owned Run,
|
|
166
|
+
and waits for its cleanup. A bare `break` from `events()` is not the explicit
|
|
167
|
+
resource boundary:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from pleiad.events.message import MessageTextDeltaEvent
|
|
171
|
+
|
|
172
|
+
async with runner.run_streamed(
|
|
173
|
+
agent,
|
|
174
|
+
"hello",
|
|
175
|
+
memory=memory,
|
|
176
|
+
config=config,
|
|
177
|
+
) as stream:
|
|
178
|
+
async for event in stream.events():
|
|
179
|
+
if isinstance(event, MessageTextDeltaEvent):
|
|
180
|
+
render_delta(event.message_id, event.delta)
|
|
181
|
+
result = await stream.result()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
An optional host `event_sink` is awaited and isolated as best-effort
|
|
185
|
+
notification. The RunStream delivery path remains reliable and propagates
|
|
186
|
+
execution failures through `result()`; neither path is a persistence or
|
|
187
|
+
exactly-once transaction interface.
|
|
188
|
+
|
|
189
|
+
## Observability
|
|
190
|
+
|
|
191
|
+
Pleiad emits provider-neutral OpenTelemetry spans and metrics from the actual
|
|
192
|
+
Runtime, Context, Memory, Model, Tool, MCP, Sandbox, and Graph boundaries. The
|
|
193
|
+
base package uses standard no-op providers unless the host explicitly supplies
|
|
194
|
+
providers; a process-global OpenTelemetry provider does not implicitly enable
|
|
195
|
+
Pleiad telemetry. Importing Pleiad does not read credentials, start exporters,
|
|
196
|
+
or make network connections. Content attributes such as prompts, completions, Tool
|
|
197
|
+
arguments, command text, and process output are denied by default.
|
|
198
|
+
|
|
199
|
+
Langfuse is an explicit, host-owned optional integration. Install the extra,
|
|
200
|
+
create it once during application startup, pass its `observability` object to
|
|
201
|
+
each `Runner`, and shut it down from the same owner:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from pydantic import SecretStr
|
|
205
|
+
|
|
206
|
+
from pleiad.observability.config import LangfuseConfig
|
|
207
|
+
from pleiad.observability.integrations.langfuse import create_langfuse_observability
|
|
208
|
+
|
|
209
|
+
langfuse = create_langfuse_observability(
|
|
210
|
+
LangfuseConfig(
|
|
211
|
+
public_key=SecretStr("pk-lf-..."),
|
|
212
|
+
secret_key=SecretStr("sk-lf-..."),
|
|
213
|
+
base_url="https://cloud.langfuse.com",
|
|
214
|
+
environment="production",
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
runner = Runner(..., observability=langfuse.observability)
|
|
218
|
+
try:
|
|
219
|
+
result = await runner.run(...)
|
|
220
|
+
finally:
|
|
221
|
+
await langfuse.shutdown()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The integration owns its private OpenTelemetry provider and Langfuse client;
|
|
225
|
+
Run completion never flushes or closes shared resources. Export and shutdown
|
|
226
|
+
waits are bounded and their failures do not change Run results. Use an
|
|
227
|
+
explicit host Provider with `Observability(tracer_provider=..., meter_provider=...)`
|
|
228
|
+
when the host owns the exporter lifecycle. If bounded shutdown waiting expires,
|
|
229
|
+
the integration retains ownership of the single background shutdown operation;
|
|
230
|
+
a later call waits for that same operation instead of starting another one.
|
|
231
|
+
|
|
232
|
+
## Orchestration
|
|
233
|
+
|
|
234
|
+
Orchestration is an SDK-only layer built on the Runtime. Import concrete
|
|
235
|
+
objects from `pleiad.orchestration.supervisor`, `pleiad.orchestration.handoff`,
|
|
236
|
+
and `pleiad.orchestration.graph`; it does not create a product task or
|
|
237
|
+
execution record. `as_tool` is a module function for synchronous Worker
|
|
238
|
+
delegation, `submit_worker` and `wait_workers` are explicit asynchronous
|
|
239
|
+
controls inside an opened `Supervisor` scope. Concurrent Workers reserve from
|
|
240
|
+
one parent budget ledger and return confirmed unused budget at completion.
|
|
241
|
+
Worker grants are the intersection of parent, Worker, host, and Agent limits.
|
|
242
|
+
Handoff switches the active Agent inside the same Run and
|
|
243
|
+
ShortMemory. Graph uses `Graph`, `Node`, and `Edge`; Agent nodes call the
|
|
244
|
+
existing Runner while normal async functions run directly.
|
|
245
|
+
|
|
246
|
+
## Data model boundaries
|
|
247
|
+
|
|
248
|
+
Pleiad uses Pydantic models for untrusted data, caller-authored configuration,
|
|
249
|
+
persistence records, and public serialization contracts. Configuration models
|
|
250
|
+
use strict validation and field constraints so one invalid input reports its
|
|
251
|
+
exact field without handwritten multi-field checks.
|
|
252
|
+
|
|
253
|
+
Frozen, slotted dataclasses remain the default for already-resolved internal
|
|
254
|
+
values, runtime work sets, live dependency bundles, and behavior-bearing values
|
|
255
|
+
that are not parsing or serialization boundaries. A public Python class is not
|
|
256
|
+
automatically a Pydantic model: the deciding factor is whether it accepts raw
|
|
257
|
+
data or owns a serialization contract.
|
|
258
|
+
|
|
259
|
+
## Sandbox
|
|
260
|
+
|
|
261
|
+
`Sandbox` executes one resolved request through the host-selected `OciSandbox`
|
|
262
|
+
backend. The policy requires a sha256 image digest, a non-root numeric UID:GID,
|
|
263
|
+
finite execution and cleanup budgets, resource limits, and an explicit
|
|
264
|
+
environment allowlist. Requests provide either `argv` or an explicitly allowed
|
|
265
|
+
`shell_command`; the model-visible `command` Tool does not choose images,
|
|
266
|
+
mounts, credentials, or OCI flags.
|
|
267
|
+
|
|
268
|
+
Each request creates one uniquely named, SDK-owned Docker or Podman container,
|
|
269
|
+
attaches to it, inspects its real exit status, and removes it by the confirmed
|
|
270
|
+
container ID. The fixed baseline is network-disabled, read-only rootfs,
|
|
271
|
+
capability-drop-all, no-new-privileges, non-root, no published ports, and no
|
|
272
|
+
host-process fallback. Output is collected per stream with a bounded retained
|
|
273
|
+
prefix while excess bytes are drained. Cancellation propagates after cleanup;
|
|
274
|
+
timeout returns bounded facts only after container removal.
|
|
275
|
+
|
|
276
|
+
`read_file` and `write_file` remain host-side scoped file Tools. `command`
|
|
277
|
+
mounts only the host-provided workspace, selected Skill roots, and scratch root
|
|
278
|
+
at their fixed virtual paths. Skill scripts use the container's own interpreter
|
|
279
|
+
and preinstalled dependencies; the SDK does not install packages or inherit
|
|
280
|
+
host credentials inside the container.
|
|
281
|
+
|
|
282
|
+
## Graph validation and recovery
|
|
283
|
+
|
|
284
|
+
Graph and Node schemas reject external references before execution. JSON Pointer
|
|
285
|
+
array indices use RFC 6901 syntax: `0` or an ASCII decimal integer without a
|
|
286
|
+
leading zero; signed indices are rejected. Branch activation follows the
|
|
287
|
+
validated Node state, including output-schema failures.
|
|
288
|
+
|
|
289
|
+
A recovery fingerprint includes graph input/output schemas, concurrency and
|
|
290
|
+
timeout settings, and node side-effect declarations. Change a graph definition
|
|
291
|
+
by creating a new revision instead of restoring state against a changed contract.
|
|
292
|
+
Executable handler code and external model behavior are host bindings; the
|
|
293
|
+
host must keep them stable when restoring the same graph revision.
|
|
294
|
+
|
|
295
|
+
## Reported usage and output budget
|
|
296
|
+
|
|
297
|
+
`ModelUsage` counts are optional: `None` means the provider did not report a
|
|
298
|
+
count, and `0` is a real reported zero. OpenAI streams request
|
|
299
|
+
`stream_options={"include_usage": true}`. Partial cumulative usage updates
|
|
300
|
+
preserve known fields; totals are derived only when both components are known.
|
|
301
|
+
Anthropic message-start output usage does not establish the final output count.
|
|
302
|
+
Missing counts are omitted from token metrics and span attributes;
|
|
303
|
+
`model.completed` events serialize them as null.
|
|
304
|
+
|
|
305
|
+
`RunUsage` propagates unknown measured totals instead of replacing them with
|
|
306
|
+
zero. `budget_output_tokens` is a separate cumulative budget charge, not a
|
|
307
|
+
measurement. A call with reported output usage charges that count; if output
|
|
308
|
+
usage is absent it charges the requested output limit conservatively. Each
|
|
309
|
+
subsequent request uses only the remaining `RunConfig.max_output_tokens`
|
|
310
|
+
allowance. Exhausted allowance prevents a further model call and returns
|
|
311
|
+
`LIMIT_REACHED`; a final answer already received can still succeed.
|
|
312
|
+
|
|
313
|
+
Worker budget settlement refunds only the allowance left after the child's
|
|
314
|
+
budget charges. Tool completion and checkpoint resume preserve those charges.
|
|
315
|
+
Run checkpoints now use version 2; version 1 checkpoints are rejected because
|
|
316
|
+
they do not establish this accounting contract. No old-state migration or
|
|
317
|
+
compatibility alias is supplied.
|
pleiad-0.1.0/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Pleiad
|
|
2
|
+
|
|
3
|
+
Pleiad is the provider-neutral Agent runtime and development framework. The
|
|
4
|
+
official interactive application, Pleiad Weave, is maintained separately.
|
|
5
|
+
|
|
6
|
+
The source package lives directly in `src/`. Setuptools maps that directory to
|
|
7
|
+
the public `pleiad` import namespace; there is no nested `src/pleiad/` directory.
|
|
8
|
+
|
|
9
|
+
The package intentionally contains no TUI, HTTP application, CLI, or product
|
|
10
|
+
configuration. Install optional model extras only when the host application
|
|
11
|
+
needs the bundled provider adapters.
|
|
12
|
+
|
|
13
|
+
Source code and C4 architecture documentation are maintained in the
|
|
14
|
+
[project repository](https://github.com/RyzeAngler/Pleiad).
|
|
15
|
+
|
|
16
|
+
## Model clients
|
|
17
|
+
|
|
18
|
+
The public model objects select the request protocol by their class:
|
|
19
|
+
`OpenAIModelClient` accepts an OpenAI Chat Completions body, and
|
|
20
|
+
`AnthropicModelClient` accepts an Anthropic Messages body. The `provider`
|
|
21
|
+
argument selects the vendor SDK binding that sends the request and converts
|
|
22
|
+
the vendor response back to that protocol. Provider-specific request options
|
|
23
|
+
belong in `provider_options`; callers do not pass a separate protocol value or
|
|
24
|
+
a shared model client.
|
|
25
|
+
|
|
26
|
+
Install `pleiad[models]` for all bundled provider adapters. The protocol object
|
|
27
|
+
determines the public request and response shape; the provider module owns the
|
|
28
|
+
vendor SDK call and the conversion at that boundary:
|
|
29
|
+
|
|
30
|
+
| provider | `OpenAIModelClient` | `AnthropicModelClient` |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `openai` | OpenAI SDK | — |
|
|
33
|
+
| `anthropic` | — | Anthropic SDK |
|
|
34
|
+
| `deepseek` | OpenAI SDK | Anthropic SDK |
|
|
35
|
+
| `glm` | Zhipu `zai-sdk` | Anthropic SDK |
|
|
36
|
+
| `qwen` | DashScope SDK | Anthropic SDK |
|
|
37
|
+
| `kimi` | OpenAI SDK | Anthropic SDK |
|
|
38
|
+
| `minimax` | OpenAI SDK | Anthropic SDK |
|
|
39
|
+
|
|
40
|
+
For example, this remains an OpenAI request even though DeepSeek is the
|
|
41
|
+
provider:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from pleiad.models.openai.client import OpenAIModelClient
|
|
45
|
+
|
|
46
|
+
client = OpenAIModelClient(
|
|
47
|
+
provider="deepseek",
|
|
48
|
+
model="deepseek-chat",
|
|
49
|
+
api_key="...",
|
|
50
|
+
)
|
|
51
|
+
response = await client.complete(
|
|
52
|
+
{"messages": [{"role": "user", "content": "hello"}]},
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use `AnthropicModelClient` with the same `provider` value when the caller's
|
|
57
|
+
body is an Anthropic Messages request. There is no public `protocol` argument
|
|
58
|
+
and no public common model client.
|
|
59
|
+
|
|
60
|
+
## Agent declarations
|
|
61
|
+
|
|
62
|
+
`Agent` is a frozen declaration of a model, prompt, capabilities, selected Skills,
|
|
63
|
+
output schema, and policy hints. Import it from `pleiad.agent.agent`.
|
|
64
|
+
|
|
65
|
+
Every Agent receives the default Tool references `read_file`, `write_file`, and
|
|
66
|
+
`command`. Explicit Tool references are appended after those defaults and duplicate
|
|
67
|
+
codes are rejected. The host must bind every declared Tool before a Run starts.
|
|
68
|
+
|
|
69
|
+
Selected Skills contribute catalog metadata to the initial prompt. Their instruction
|
|
70
|
+
files are not loaded into that prompt; the host must bind each selected Skill to the
|
|
71
|
+
SDK `read_file` Tool so the model can request its `SKILL.md` when needed.
|
|
72
|
+
|
|
73
|
+
## Context and Runtime
|
|
74
|
+
|
|
75
|
+
`ContextBuilder` selects request-local `ContextBlock` references and
|
|
76
|
+
`ContextRenderer` renders them beside the current User Message. The original
|
|
77
|
+
message and dynamic references remain separate from `ShortMemory`; context
|
|
78
|
+
budgets and compaction are evaluated for each model request at complete Turn
|
|
79
|
+
boundaries.
|
|
80
|
+
|
|
81
|
+
`Runner` owns the ShortMemory-backed Model → Tool → Model loop. Bind concrete
|
|
82
|
+
model clients on the `Agent`, then supply a `ShortMemory`, `RunConfig`, and the
|
|
83
|
+
host's `ToolRegistry`, `SkillLoader`, and `ToolRuntime`:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from pleiad.agent.agent import Agent
|
|
87
|
+
from pleiad.memory.short_memory import ShortMemory
|
|
88
|
+
from pleiad.runtime.config import RunConfig
|
|
89
|
+
from pleiad.runtime.execution.runner import Runner
|
|
90
|
+
|
|
91
|
+
result = await runner.run(
|
|
92
|
+
Agent(name="assistant", model=client, prompt="Answer the user."),
|
|
93
|
+
"hello",
|
|
94
|
+
memory=ShortMemory.in_memory("conversation-1"),
|
|
95
|
+
config=RunConfig(),
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
An interrupted run returns a `RunState` checkpoint in `RunResult`; resume it
|
|
100
|
+
with `Runner.resume` and explicit interruption resolutions. `RunStream` uses
|
|
101
|
+
the same execution path for ordered lifecycle events and early-close
|
|
102
|
+
cancellation. SDK notifications inherit the public `Event` envelope from
|
|
103
|
+
`pleiad.events.contracts`; product applications can define typed Event
|
|
104
|
+
subclasses without registering them with Pleiad. Stream buffering is bounded
|
|
105
|
+
and applies backpressure, while terminal events remain associated with their
|
|
106
|
+
`run_id`.
|
|
107
|
+
|
|
108
|
+
Messages are concrete role-specific contracts imported from
|
|
109
|
+
`pleiad.messages.message`: `SystemMessage`, `UserMessage`, `AssistantMessage`,
|
|
110
|
+
and `ToolMessage`. An Assistant owns its model-declared `tool_calls`; the
|
|
111
|
+
corresponding execution output is a later Tool message. Completed message
|
|
112
|
+
snapshots and live text or Tool-argument deltas are published through the
|
|
113
|
+
typed events in `pleiad.events.message`.
|
|
114
|
+
|
|
115
|
+
Use the stream as an async context manager when the consumer may stop before
|
|
116
|
+
the Run completes. Leaving the scope calls `aclose()`, cancels the owned Run,
|
|
117
|
+
and waits for its cleanup. A bare `break` from `events()` is not the explicit
|
|
118
|
+
resource boundary:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from pleiad.events.message import MessageTextDeltaEvent
|
|
122
|
+
|
|
123
|
+
async with runner.run_streamed(
|
|
124
|
+
agent,
|
|
125
|
+
"hello",
|
|
126
|
+
memory=memory,
|
|
127
|
+
config=config,
|
|
128
|
+
) as stream:
|
|
129
|
+
async for event in stream.events():
|
|
130
|
+
if isinstance(event, MessageTextDeltaEvent):
|
|
131
|
+
render_delta(event.message_id, event.delta)
|
|
132
|
+
result = await stream.result()
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
An optional host `event_sink` is awaited and isolated as best-effort
|
|
136
|
+
notification. The RunStream delivery path remains reliable and propagates
|
|
137
|
+
execution failures through `result()`; neither path is a persistence or
|
|
138
|
+
exactly-once transaction interface.
|
|
139
|
+
|
|
140
|
+
## Observability
|
|
141
|
+
|
|
142
|
+
Pleiad emits provider-neutral OpenTelemetry spans and metrics from the actual
|
|
143
|
+
Runtime, Context, Memory, Model, Tool, MCP, Sandbox, and Graph boundaries. The
|
|
144
|
+
base package uses standard no-op providers unless the host explicitly supplies
|
|
145
|
+
providers; a process-global OpenTelemetry provider does not implicitly enable
|
|
146
|
+
Pleiad telemetry. Importing Pleiad does not read credentials, start exporters,
|
|
147
|
+
or make network connections. Content attributes such as prompts, completions, Tool
|
|
148
|
+
arguments, command text, and process output are denied by default.
|
|
149
|
+
|
|
150
|
+
Langfuse is an explicit, host-owned optional integration. Install the extra,
|
|
151
|
+
create it once during application startup, pass its `observability` object to
|
|
152
|
+
each `Runner`, and shut it down from the same owner:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from pydantic import SecretStr
|
|
156
|
+
|
|
157
|
+
from pleiad.observability.config import LangfuseConfig
|
|
158
|
+
from pleiad.observability.integrations.langfuse import create_langfuse_observability
|
|
159
|
+
|
|
160
|
+
langfuse = create_langfuse_observability(
|
|
161
|
+
LangfuseConfig(
|
|
162
|
+
public_key=SecretStr("pk-lf-..."),
|
|
163
|
+
secret_key=SecretStr("sk-lf-..."),
|
|
164
|
+
base_url="https://cloud.langfuse.com",
|
|
165
|
+
environment="production",
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
runner = Runner(..., observability=langfuse.observability)
|
|
169
|
+
try:
|
|
170
|
+
result = await runner.run(...)
|
|
171
|
+
finally:
|
|
172
|
+
await langfuse.shutdown()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The integration owns its private OpenTelemetry provider and Langfuse client;
|
|
176
|
+
Run completion never flushes or closes shared resources. Export and shutdown
|
|
177
|
+
waits are bounded and their failures do not change Run results. Use an
|
|
178
|
+
explicit host Provider with `Observability(tracer_provider=..., meter_provider=...)`
|
|
179
|
+
when the host owns the exporter lifecycle. If bounded shutdown waiting expires,
|
|
180
|
+
the integration retains ownership of the single background shutdown operation;
|
|
181
|
+
a later call waits for that same operation instead of starting another one.
|
|
182
|
+
|
|
183
|
+
## Orchestration
|
|
184
|
+
|
|
185
|
+
Orchestration is an SDK-only layer built on the Runtime. Import concrete
|
|
186
|
+
objects from `pleiad.orchestration.supervisor`, `pleiad.orchestration.handoff`,
|
|
187
|
+
and `pleiad.orchestration.graph`; it does not create a product task or
|
|
188
|
+
execution record. `as_tool` is a module function for synchronous Worker
|
|
189
|
+
delegation, `submit_worker` and `wait_workers` are explicit asynchronous
|
|
190
|
+
controls inside an opened `Supervisor` scope. Concurrent Workers reserve from
|
|
191
|
+
one parent budget ledger and return confirmed unused budget at completion.
|
|
192
|
+
Worker grants are the intersection of parent, Worker, host, and Agent limits.
|
|
193
|
+
Handoff switches the active Agent inside the same Run and
|
|
194
|
+
ShortMemory. Graph uses `Graph`, `Node`, and `Edge`; Agent nodes call the
|
|
195
|
+
existing Runner while normal async functions run directly.
|
|
196
|
+
|
|
197
|
+
## Data model boundaries
|
|
198
|
+
|
|
199
|
+
Pleiad uses Pydantic models for untrusted data, caller-authored configuration,
|
|
200
|
+
persistence records, and public serialization contracts. Configuration models
|
|
201
|
+
use strict validation and field constraints so one invalid input reports its
|
|
202
|
+
exact field without handwritten multi-field checks.
|
|
203
|
+
|
|
204
|
+
Frozen, slotted dataclasses remain the default for already-resolved internal
|
|
205
|
+
values, runtime work sets, live dependency bundles, and behavior-bearing values
|
|
206
|
+
that are not parsing or serialization boundaries. A public Python class is not
|
|
207
|
+
automatically a Pydantic model: the deciding factor is whether it accepts raw
|
|
208
|
+
data or owns a serialization contract.
|
|
209
|
+
|
|
210
|
+
## Sandbox
|
|
211
|
+
|
|
212
|
+
`Sandbox` executes one resolved request through the host-selected `OciSandbox`
|
|
213
|
+
backend. The policy requires a sha256 image digest, a non-root numeric UID:GID,
|
|
214
|
+
finite execution and cleanup budgets, resource limits, and an explicit
|
|
215
|
+
environment allowlist. Requests provide either `argv` or an explicitly allowed
|
|
216
|
+
`shell_command`; the model-visible `command` Tool does not choose images,
|
|
217
|
+
mounts, credentials, or OCI flags.
|
|
218
|
+
|
|
219
|
+
Each request creates one uniquely named, SDK-owned Docker or Podman container,
|
|
220
|
+
attaches to it, inspects its real exit status, and removes it by the confirmed
|
|
221
|
+
container ID. The fixed baseline is network-disabled, read-only rootfs,
|
|
222
|
+
capability-drop-all, no-new-privileges, non-root, no published ports, and no
|
|
223
|
+
host-process fallback. Output is collected per stream with a bounded retained
|
|
224
|
+
prefix while excess bytes are drained. Cancellation propagates after cleanup;
|
|
225
|
+
timeout returns bounded facts only after container removal.
|
|
226
|
+
|
|
227
|
+
`read_file` and `write_file` remain host-side scoped file Tools. `command`
|
|
228
|
+
mounts only the host-provided workspace, selected Skill roots, and scratch root
|
|
229
|
+
at their fixed virtual paths. Skill scripts use the container's own interpreter
|
|
230
|
+
and preinstalled dependencies; the SDK does not install packages or inherit
|
|
231
|
+
host credentials inside the container.
|
|
232
|
+
|
|
233
|
+
## Graph validation and recovery
|
|
234
|
+
|
|
235
|
+
Graph and Node schemas reject external references before execution. JSON Pointer
|
|
236
|
+
array indices use RFC 6901 syntax: `0` or an ASCII decimal integer without a
|
|
237
|
+
leading zero; signed indices are rejected. Branch activation follows the
|
|
238
|
+
validated Node state, including output-schema failures.
|
|
239
|
+
|
|
240
|
+
A recovery fingerprint includes graph input/output schemas, concurrency and
|
|
241
|
+
timeout settings, and node side-effect declarations. Change a graph definition
|
|
242
|
+
by creating a new revision instead of restoring state against a changed contract.
|
|
243
|
+
Executable handler code and external model behavior are host bindings; the
|
|
244
|
+
host must keep them stable when restoring the same graph revision.
|
|
245
|
+
|
|
246
|
+
## Reported usage and output budget
|
|
247
|
+
|
|
248
|
+
`ModelUsage` counts are optional: `None` means the provider did not report a
|
|
249
|
+
count, and `0` is a real reported zero. OpenAI streams request
|
|
250
|
+
`stream_options={"include_usage": true}`. Partial cumulative usage updates
|
|
251
|
+
preserve known fields; totals are derived only when both components are known.
|
|
252
|
+
Anthropic message-start output usage does not establish the final output count.
|
|
253
|
+
Missing counts are omitted from token metrics and span attributes;
|
|
254
|
+
`model.completed` events serialize them as null.
|
|
255
|
+
|
|
256
|
+
`RunUsage` propagates unknown measured totals instead of replacing them with
|
|
257
|
+
zero. `budget_output_tokens` is a separate cumulative budget charge, not a
|
|
258
|
+
measurement. A call with reported output usage charges that count; if output
|
|
259
|
+
usage is absent it charges the requested output limit conservatively. Each
|
|
260
|
+
subsequent request uses only the remaining `RunConfig.max_output_tokens`
|
|
261
|
+
allowance. Exhausted allowance prevents a further model call and returns
|
|
262
|
+
`LIMIT_REACHED`; a final answer already received can still succeed.
|
|
263
|
+
|
|
264
|
+
Worker budget settlement refunds only the allowance left after the child's
|
|
265
|
+
budget charges. Tool completion and checkpoint resume preserve those charges.
|
|
266
|
+
Run checkpoints now use version 2; version 1 checkpoints are rejected because
|
|
267
|
+
they do not establish this accounting contract. No old-state migration or
|
|
268
|
+
compatibility alias is supplied.
|