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.
Files changed (153) hide show
  1. pleiad-0.1.0/LICENSE +21 -0
  2. pleiad-0.1.0/PKG-INFO +317 -0
  3. pleiad-0.1.0/README.md +268 -0
  4. pleiad-0.1.0/pleiad.egg-info/PKG-INFO +317 -0
  5. pleiad-0.1.0/pleiad.egg-info/SOURCES.txt +151 -0
  6. pleiad-0.1.0/pleiad.egg-info/dependency_links.txt +1 -0
  7. pleiad-0.1.0/pleiad.egg-info/requires.txt +47 -0
  8. pleiad-0.1.0/pleiad.egg-info/top_level.txt +1 -0
  9. pleiad-0.1.0/pyproject.toml +149 -0
  10. pleiad-0.1.0/setup.cfg +4 -0
  11. pleiad-0.1.0/src/__init__.py +0 -0
  12. pleiad-0.1.0/src/agent/__init__.py +0 -0
  13. pleiad-0.1.0/src/agent/agent.py +62 -0
  14. pleiad-0.1.0/src/agent/errors.py +21 -0
  15. pleiad-0.1.0/src/agent/policy.py +18 -0
  16. pleiad-0.1.0/src/agent/prompt.py +37 -0
  17. pleiad-0.1.0/src/capabilities/__init__.py +0 -0
  18. pleiad-0.1.0/src/capabilities/errors.py +51 -0
  19. pleiad-0.1.0/src/capabilities/search.py +193 -0
  20. pleiad-0.1.0/src/context/__init__.py +0 -0
  21. pleiad-0.1.0/src/context/budget.py +121 -0
  22. pleiad-0.1.0/src/context/compaction/__init__.py +0 -0
  23. pleiad-0.1.0/src/context/compaction/compactor.py +194 -0
  24. pleiad-0.1.0/src/context/compaction/models.py +26 -0
  25. pleiad-0.1.0/src/context/errors.py +21 -0
  26. pleiad-0.1.0/src/context/rendering.py +36 -0
  27. pleiad-0.1.0/src/context/selection/__init__.py +0 -0
  28. pleiad-0.1.0/src/context/selection/builder.py +43 -0
  29. pleiad-0.1.0/src/context/selection/models.py +24 -0
  30. pleiad-0.1.0/src/contracts.py +153 -0
  31. pleiad-0.1.0/src/errors/__init__.py +0 -0
  32. pleiad-0.1.0/src/errors/base.py +59 -0
  33. pleiad-0.1.0/src/events/__init__.py +0 -0
  34. pleiad-0.1.0/src/events/contracts.py +21 -0
  35. pleiad-0.1.0/src/events/graph.py +46 -0
  36. pleiad-0.1.0/src/events/message.py +62 -0
  37. pleiad-0.1.0/src/events/runtime.py +166 -0
  38. pleiad-0.1.0/src/events/supervisor.py +63 -0
  39. pleiad-0.1.0/src/logging/__init__.py +0 -0
  40. pleiad-0.1.0/src/logging/context.py +88 -0
  41. pleiad-0.1.0/src/logging/filters.py +23 -0
  42. pleiad-0.1.0/src/logging/rotation.py +44 -0
  43. pleiad-0.1.0/src/mcp/__init__.py +0 -0
  44. pleiad-0.1.0/src/mcp/contracts.py +33 -0
  45. pleiad-0.1.0/src/mcp/discovery.py +91 -0
  46. pleiad-0.1.0/src/mcp/errors.py +60 -0
  47. pleiad-0.1.0/src/mcp/tool.py +125 -0
  48. pleiad-0.1.0/src/memory/__init__.py +0 -0
  49. pleiad-0.1.0/src/memory/backends/__init__.py +0 -0
  50. pleiad-0.1.0/src/memory/backends/in_memory.py +139 -0
  51. pleiad-0.1.0/src/memory/backends/sqlite.py +296 -0
  52. pleiad-0.1.0/src/memory/errors.py +60 -0
  53. pleiad-0.1.0/src/memory/short_memory.py +195 -0
  54. pleiad-0.1.0/src/memory/turn.py +135 -0
  55. pleiad-0.1.0/src/messages/__init__.py +0 -0
  56. pleiad-0.1.0/src/messages/message.py +109 -0
  57. pleiad-0.1.0/src/models/__init__.py +0 -0
  58. pleiad-0.1.0/src/models/anthropic/__init__.py +0 -0
  59. pleiad-0.1.0/src/models/anthropic/client.py +145 -0
  60. pleiad-0.1.0/src/models/anthropic/request.py +84 -0
  61. pleiad-0.1.0/src/models/anthropic/response.py +335 -0
  62. pleiad-0.1.0/src/models/contracts.py +12 -0
  63. pleiad-0.1.0/src/models/errors.py +152 -0
  64. pleiad-0.1.0/src/models/openai/__init__.py +0 -0
  65. pleiad-0.1.0/src/models/openai/client.py +145 -0
  66. pleiad-0.1.0/src/models/openai/request.py +114 -0
  67. pleiad-0.1.0/src/models/openai/response.py +431 -0
  68. pleiad-0.1.0/src/models/providers/__init__.py +0 -0
  69. pleiad-0.1.0/src/models/providers/anthropic.py +129 -0
  70. pleiad-0.1.0/src/models/providers/deepseek.py +235 -0
  71. pleiad-0.1.0/src/models/providers/glm.py +263 -0
  72. pleiad-0.1.0/src/models/providers/kimi.py +253 -0
  73. pleiad-0.1.0/src/models/providers/minimax.py +235 -0
  74. pleiad-0.1.0/src/models/providers/openai.py +120 -0
  75. pleiad-0.1.0/src/models/providers/qwen.py +358 -0
  76. pleiad-0.1.0/src/models/result.py +135 -0
  77. pleiad-0.1.0/src/observability/__init__.py +0 -0
  78. pleiad-0.1.0/src/observability/attributes.py +66 -0
  79. pleiad-0.1.0/src/observability/config.py +31 -0
  80. pleiad-0.1.0/src/observability/errors.py +17 -0
  81. pleiad-0.1.0/src/observability/integrations/__init__.py +0 -0
  82. pleiad-0.1.0/src/observability/integrations/langfuse.py +163 -0
  83. pleiad-0.1.0/src/observability/metrics.py +68 -0
  84. pleiad-0.1.0/src/observability/privacy.py +191 -0
  85. pleiad-0.1.0/src/observability/tracing.py +168 -0
  86. pleiad-0.1.0/src/orchestration/__init__.py +0 -0
  87. pleiad-0.1.0/src/orchestration/errors.py +49 -0
  88. pleiad-0.1.0/src/orchestration/graph/__init__.py +0 -0
  89. pleiad-0.1.0/src/orchestration/graph/edge.py +59 -0
  90. pleiad-0.1.0/src/orchestration/graph/errors.py +41 -0
  91. pleiad-0.1.0/src/orchestration/graph/executor.py +496 -0
  92. pleiad-0.1.0/src/orchestration/graph/graph.py +82 -0
  93. pleiad-0.1.0/src/orchestration/graph/mapping.py +55 -0
  94. pleiad-0.1.0/src/orchestration/graph/node.py +86 -0
  95. pleiad-0.1.0/src/orchestration/graph/ports.py +12 -0
  96. pleiad-0.1.0/src/orchestration/graph/scheduler.py +20 -0
  97. pleiad-0.1.0/src/orchestration/graph/state.py +95 -0
  98. pleiad-0.1.0/src/orchestration/graph/validation.py +137 -0
  99. pleiad-0.1.0/src/orchestration/handoff/__init__.py +0 -0
  100. pleiad-0.1.0/src/orchestration/handoff/handoff.py +34 -0
  101. pleiad-0.1.0/src/orchestration/handoff/state.py +30 -0
  102. pleiad-0.1.0/src/orchestration/handoff/tools.py +11 -0
  103. pleiad-0.1.0/src/orchestration/supervisor/__init__.py +0 -0
  104. pleiad-0.1.0/src/orchestration/supervisor/state.py +31 -0
  105. pleiad-0.1.0/src/orchestration/supervisor/supervisor.py +480 -0
  106. pleiad-0.1.0/src/orchestration/supervisor/tools.py +214 -0
  107. pleiad-0.1.0/src/orchestration/supervisor/worker.py +69 -0
  108. pleiad-0.1.0/src/py.typed +2 -0
  109. pleiad-0.1.0/src/runtime/__init__.py +0 -0
  110. pleiad-0.1.0/src/runtime/config.py +23 -0
  111. pleiad-0.1.0/src/runtime/errors.py +25 -0
  112. pleiad-0.1.0/src/runtime/execution/__init__.py +0 -0
  113. pleiad-0.1.0/src/runtime/execution/control.py +37 -0
  114. pleiad-0.1.0/src/runtime/execution/loop.py +1075 -0
  115. pleiad-0.1.0/src/runtime/execution/model_input.py +495 -0
  116. pleiad-0.1.0/src/runtime/execution/runner.py +847 -0
  117. pleiad-0.1.0/src/runtime/execution/stream.py +68 -0
  118. pleiad-0.1.0/src/runtime/lifecycle/__init__.py +0 -0
  119. pleiad-0.1.0/src/runtime/lifecycle/result.py +38 -0
  120. pleiad-0.1.0/src/runtime/lifecycle/state.py +96 -0
  121. pleiad-0.1.0/src/runtime/policy.py +84 -0
  122. pleiad-0.1.0/src/sandbox/__init__.py +0 -0
  123. pleiad-0.1.0/src/sandbox/backends/__init__.py +0 -0
  124. pleiad-0.1.0/src/sandbox/backends/oci.py +485 -0
  125. pleiad-0.1.0/src/sandbox/backends/process.py +136 -0
  126. pleiad-0.1.0/src/sandbox/errors.py +44 -0
  127. pleiad-0.1.0/src/sandbox/models.py +120 -0
  128. pleiad-0.1.0/src/sandbox/policy.py +52 -0
  129. pleiad-0.1.0/src/sandbox/sandbox.py +18 -0
  130. pleiad-0.1.0/src/schemas.py +38 -0
  131. pleiad-0.1.0/src/skills/__init__.py +0 -0
  132. pleiad-0.1.0/src/skills/contracts.py +37 -0
  133. pleiad-0.1.0/src/skills/errors.py +25 -0
  134. pleiad-0.1.0/src/skills/loader.py +88 -0
  135. pleiad-0.1.0/src/skills/manifest.py +129 -0
  136. pleiad-0.1.0/src/skills/package.py +113 -0
  137. pleiad-0.1.0/src/tools/__init__.py +0 -0
  138. pleiad-0.1.0/src/tools/base.py +113 -0
  139. pleiad-0.1.0/src/tools/builtins/__init__.py +0 -0
  140. pleiad-0.1.0/src/tools/builtins/command.py +132 -0
  141. pleiad-0.1.0/src/tools/builtins/defaults.py +60 -0
  142. pleiad-0.1.0/src/tools/builtins/file_support.py +188 -0
  143. pleiad-0.1.0/src/tools/builtins/read_file.py +113 -0
  144. pleiad-0.1.0/src/tools/builtins/write_file.py +120 -0
  145. pleiad-0.1.0/src/tools/contracts.py +94 -0
  146. pleiad-0.1.0/src/tools/errors.py +211 -0
  147. pleiad-0.1.0/src/tools/interruption.py +37 -0
  148. pleiad-0.1.0/src/tools/manifest.py +70 -0
  149. pleiad-0.1.0/src/tools/registry.py +76 -0
  150. pleiad-0.1.0/src/tools/runtime.py +191 -0
  151. pleiad-0.1.0/src/version.py +3 -0
  152. pleiad-0.1.0/tests/test_errors.py +66 -0
  153. 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.