roboz 0.1.2.dev2__tar.gz → 0.1.2.dev4__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.
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/CHANGELOG.md +28 -1
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/PKG-INFO +12 -9
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/README.md +11 -6
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/pyproject.toml +1 -2
- roboz-0.1.2.dev4/src/roboz/deployment.py +391 -0
- roboz-0.1.2.dev2/src/roboz/deployment.py +0 -151
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/.gitignore +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/LICENSE +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/_naming.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/_execution_context.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/_identifiers.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/_notifications.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/_prompts.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/_tool_observer.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/background_agent.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/core.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/prompt_agent_tool.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/agent/subagent.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/dependencies.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/exceptions.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/_diagnostics.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/_retry.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/_truncation.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/binding.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/calls.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/completion.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/endpoints.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/openrouter.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/llm/prompts.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/_schema.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/_serialization.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/_telemetry.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/core.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/models/truncation.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/py.typed +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/_environment.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/_external.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/_logging.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/_paths.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/events.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/io.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/observability.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/persistence/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/persistence/activity.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/persistence/schema.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/pipe.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/runtime/sinks.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/skill/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/skill/_prompts.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/skill/core.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/_prompts.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/_protocols.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/_typing.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/context.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/core.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tooling/decorators.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tools/__init__.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tools/_identifiers.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tools/control.py +0 -0
- {roboz-0.1.2.dev2 → roboz-0.1.2.dev4}/src/roboz/tools/interaction.py +0 -0
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.2.dev4 - 2026-09-12
|
|
6
|
+
|
|
7
|
+
- Breaking: make `DeployableAgent` an explicitly configured class. Constructor
|
|
8
|
+
capabilities are protected defaults; append capabilities and child agents with
|
|
9
|
+
the add methods, configure endpoints and runtime values separately, and unpack
|
|
10
|
+
`(agent, background_agents)` from `build()`. `build_graph()` is removed.
|
|
11
|
+
- Breaking: capability builders now declare concrete `required_attributes` and
|
|
12
|
+
receive `build(agent, pipe)`. Builds validate the complete graph before
|
|
13
|
+
constructing sinks, pipes, or tools and aggregate missing, `None`, and
|
|
14
|
+
incorrectly typed owner attributes.
|
|
15
|
+
|
|
16
|
+
## 0.1.2.dev3 - 2026-09-11
|
|
17
|
+
|
|
18
|
+
- Breaking: remove the redundant `roboz[shed]` installation extra. Install
|
|
19
|
+
`roboshed` directly; it installs its compatible core Roboz dependency.
|
|
20
|
+
|
|
21
|
+
- Add `DeployableAgent.build_graph()` to return the root agent and all background
|
|
22
|
+
handles for host lifecycle control. `build()` still returns the root alone.
|
|
23
|
+
|
|
24
|
+
- Breaking: replace `AgentDefinition` and `SubAgentSpec` with recursive
|
|
25
|
+
`DeployableAgent` definitions. Put child definitions directly in `subagents`
|
|
26
|
+
or `background_agents`; delegation tools use the child's name and description,
|
|
27
|
+
and background agents start through default tools. Construct generic
|
|
28
|
+
definitions directly with `DeployableAgent(...)`.
|
|
29
|
+
Sandbox-aware `Deployment` lives in `roboshed.deployments`, not core.
|
|
30
|
+
See `docs/agent-factories.md` for migration.
|
|
31
|
+
|
|
5
32
|
## 0.1.2.dev2 - 2026-09-09
|
|
6
33
|
|
|
7
34
|
- Breaking: remove the `roboz[openai]` extra. Install
|
|
@@ -20,7 +47,7 @@
|
|
|
20
47
|
references retain identity and deferred resolution. See
|
|
21
48
|
`docs/agent-factories.md` for builder migration.
|
|
22
49
|
|
|
23
|
-
- Breaking: move the Librarian and its memory/summarization tools out of core into `roboshed.agents` and `roboshed.tools`. Use `librarian(
|
|
50
|
+
- Breaking: move the Librarian and its memory/summarization tools out of core into `roboshed.agents` and `roboshed.tools`. Use `librarian(sandbox, agent_names, agent_endpoint=...)` instead of `LibrarianConstructor` and its path record; see `docs/agent-factories.md`. Core retains control, interaction, and generic construction primitives.
|
|
24
51
|
|
|
25
52
|
- Add generic `AgentDefinition`, `AgentCapability`, `Capability`, and `SubAgentSpec` in `roboz.deployment`. Configure all tools and skills through capabilities, with fresh agent pipes and explicit sink configuration; definitions select no project, memory, or persistence conventions. See `docs/agent-factories.md`.
|
|
26
53
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: roboz
|
|
3
|
-
Version: 0.1.2.
|
|
3
|
+
Version: 0.1.2.dev4
|
|
4
4
|
Import-Name: roboz
|
|
5
5
|
Summary: Typed primitives for composable agentic workflows
|
|
6
6
|
Project-URL: Homepage, https://github.com/Tachion-Oy/roboz
|
|
@@ -25,8 +25,6 @@ Requires-Dist: python-dotenv>=1.2.1
|
|
|
25
25
|
Requires-Dist: rich>=14.2.0
|
|
26
26
|
Provides-Extra: proton-bridge-beta
|
|
27
27
|
Requires-Dist: roboz-proton-bridge<0.2.0,>=0.1.0b1; extra == 'proton-bridge-beta'
|
|
28
|
-
Provides-Extra: shed
|
|
29
|
-
Requires-Dist: roboshed<0.2.0,>=0.1.0a2; extra == 'shed'
|
|
30
28
|
Description-Content-Type: text/markdown
|
|
31
29
|
|
|
32
30
|
# Roboz
|
|
@@ -170,12 +168,17 @@ Install `roboz-endpoints[openai]` for the initial OpenRouter, Cerebras, and Groq
|
|
|
170
168
|
catalogues; it installs core automatically. See the
|
|
171
169
|
[endpoint guide](packages/endpoints/README.md) for model selection and SDK adapters.
|
|
172
170
|
|
|
173
|
-
`roboz.deployment` supplies
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
`roboz.deployment` supplies recursive `DeployableAgent` definitions and
|
|
172
|
+
capability contracts. Put definitions directly in `subagents` or
|
|
173
|
+
`background_agents`, append extensions with the explicit add methods, and unpack
|
|
174
|
+
`agent, background_agents = definition.build()`. Capabilities declare required
|
|
175
|
+
owner attributes and bind with `build(agent, pipe)`. `roboshed` supplies reusable
|
|
176
|
+
orchestrator and Librarian constructors, concrete capabilities, memory tools,
|
|
177
|
+
and sandbox structure. Import the constructors from `roboshed.agents`; the fixed
|
|
178
|
+
lazy RoboSprawl recipe is `roboshed.deployments.robosprawl.RoboSprawl`. Construct
|
|
179
|
+
it without inputs, configure it through setters, then call argument-free
|
|
180
|
+
`build()` with all required inputs supplied. The recipe owns project context
|
|
181
|
+
and persistence; the caller owns invocation.
|
|
179
182
|
|
|
180
183
|
## Documentation
|
|
181
184
|
|
|
@@ -139,12 +139,17 @@ Install `roboz-endpoints[openai]` for the initial OpenRouter, Cerebras, and Groq
|
|
|
139
139
|
catalogues; it installs core automatically. See the
|
|
140
140
|
[endpoint guide](packages/endpoints/README.md) for model selection and SDK adapters.
|
|
141
141
|
|
|
142
|
-
`roboz.deployment` supplies
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
`roboz.deployment` supplies recursive `DeployableAgent` definitions and
|
|
143
|
+
capability contracts. Put definitions directly in `subagents` or
|
|
144
|
+
`background_agents`, append extensions with the explicit add methods, and unpack
|
|
145
|
+
`agent, background_agents = definition.build()`. Capabilities declare required
|
|
146
|
+
owner attributes and bind with `build(agent, pipe)`. `roboshed` supplies reusable
|
|
147
|
+
orchestrator and Librarian constructors, concrete capabilities, memory tools,
|
|
148
|
+
and sandbox structure. Import the constructors from `roboshed.agents`; the fixed
|
|
149
|
+
lazy RoboSprawl recipe is `roboshed.deployments.robosprawl.RoboSprawl`. Construct
|
|
150
|
+
it without inputs, configure it through setters, then call argument-free
|
|
151
|
+
`build()` with all required inputs supplied. The recipe owns project context
|
|
152
|
+
and persistence; the caller owns invocation.
|
|
148
153
|
|
|
149
154
|
## Documentation
|
|
150
155
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "roboz"
|
|
3
|
-
version = "0.1.2.
|
|
3
|
+
version = "0.1.2.dev4"
|
|
4
4
|
description = "Typed primitives for composable agentic workflows"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Tachion Oy" }]
|
|
@@ -30,7 +30,6 @@ requires = ["hatchling>=1.32"]
|
|
|
30
30
|
build-backend = "hatchling.build"
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
|
-
shed = ["roboshed>=0.1.0a2,<0.2.0"]
|
|
34
33
|
proton-bridge-beta = ["roboz-proton-bridge>=0.1.0b1,<0.2.0"]
|
|
35
34
|
|
|
36
35
|
[tool.uv.workspace]
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
"""Configurable agent definitions and runtime-bound capabilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Protocol
|
|
9
|
+
|
|
10
|
+
from roboz.agent import Agent, run_background_agent, run_subagent
|
|
11
|
+
from roboz.llm import EndpointLike
|
|
12
|
+
from roboz.runtime import EventPipe, EventSink, Output
|
|
13
|
+
from roboz.skill import Skill
|
|
14
|
+
from roboz.tooling import Tool
|
|
15
|
+
from roboz.tooling.context import Ctx
|
|
16
|
+
|
|
17
|
+
type RequiredAttributeType = type[object] | tuple[type[object], ...]
|
|
18
|
+
type RequiredAttributes = Mapping[str, RequiredAttributeType]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentCapability(Protocol):
|
|
22
|
+
"""A configured feature that binds its tools to one agent runtime."""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def required_attributes(self) -> RequiredAttributes:
|
|
26
|
+
"""Declare configuration values required from the owning agent."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def build(self, agent: "DeployableAgent", pipe: EventPipe) -> "Capability":
|
|
30
|
+
"""Bind configured tools using the owning agent and its fresh pipe."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class Capability(AgentCapability):
|
|
36
|
+
"""Already-bound tools and skills that provide an agent feature.
|
|
37
|
+
|
|
38
|
+
Tools shared between a chain and default execution retain their identity.
|
|
39
|
+
Supplied objects remain caller-owned and require no owner configuration.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
tools: tuple[Tool | Sequence[Tool], ...] = ()
|
|
43
|
+
default_tools: tuple[Tool, ...] = ()
|
|
44
|
+
skills: tuple[Skill, ...] = ()
|
|
45
|
+
auto_loaded_skills: tuple[Skill, ...] = ()
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def required_attributes(self) -> RequiredAttributes:
|
|
49
|
+
"""Declare that already-bound objects need no owner configuration."""
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
def build(self, agent: "DeployableAgent", pipe: EventPipe) -> "Capability":
|
|
53
|
+
"""Return these already-bound inputs without copying or starting work."""
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DeployableAgent:
|
|
58
|
+
"""Mutable configuration for one agent and its attached child graph.
|
|
59
|
+
|
|
60
|
+
Constructor capabilities are fixed defaults. Later capabilities and child
|
|
61
|
+
definitions can only be appended. Runtime-specific values can be supplied
|
|
62
|
+
after declaration with set_attributes(); they belong only to this node.
|
|
63
|
+
Each build validates the complete graph and creates fresh runtime agents,
|
|
64
|
+
pipes, and capability bindings without retaining execution state here.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
*,
|
|
70
|
+
name: str,
|
|
71
|
+
description: str = "",
|
|
72
|
+
system_prompt: str = "",
|
|
73
|
+
is_agentic: bool = True,
|
|
74
|
+
automatic_tool_prompt: bool = True,
|
|
75
|
+
default_capabilities: Sequence[AgentCapability] = (),
|
|
76
|
+
subagents: Sequence["DeployableAgent"] = (),
|
|
77
|
+
background_agents: Sequence["DeployableAgent"] = (),
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Configure identity, behavior, fixed capabilities, and initial children."""
|
|
80
|
+
self._name = name
|
|
81
|
+
self._description = description
|
|
82
|
+
self._system_prompt = system_prompt
|
|
83
|
+
self._is_agentic = is_agentic
|
|
84
|
+
self._automatic_tool_prompt = automatic_tool_prompt
|
|
85
|
+
self._default_capabilities = tuple(default_capabilities)
|
|
86
|
+
self._additional_capabilities: list[AgentCapability] = []
|
|
87
|
+
self._subagents: list[DeployableAgent] = []
|
|
88
|
+
self._background_agents: list[DeployableAgent] = []
|
|
89
|
+
self._agent_endpoint: EndpointLike | None = None
|
|
90
|
+
self._interaction_mode: Output | None = Output.CLI
|
|
91
|
+
self._initial_messages: tuple[Path | str, ...] = ()
|
|
92
|
+
self._attributes: dict[str, object] = {}
|
|
93
|
+
self.add_subagents(*subagents)
|
|
94
|
+
self.add_background_agents(*background_agents)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def name(self) -> str:
|
|
98
|
+
"""Return this agent's runtime name."""
|
|
99
|
+
return self._name
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def description(self) -> str:
|
|
103
|
+
"""Return this agent's delegation description."""
|
|
104
|
+
return self._description
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def system_prompt(self) -> str:
|
|
108
|
+
"""Return this agent's system prompt."""
|
|
109
|
+
return self._system_prompt
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def is_agentic(self) -> bool:
|
|
113
|
+
"""Return whether the runtime agent performs model-driven turns."""
|
|
114
|
+
return self._is_agentic
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def automatic_tool_prompt(self) -> bool:
|
|
118
|
+
"""Return whether tool instructions are added automatically."""
|
|
119
|
+
return self._automatic_tool_prompt
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def default_capabilities(self) -> tuple[AgentCapability, ...]:
|
|
123
|
+
"""Return the fixed capabilities supplied at construction."""
|
|
124
|
+
return self._default_capabilities
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def additional_capabilities(self) -> tuple[AgentCapability, ...]:
|
|
128
|
+
"""Return capabilities appended after the fixed defaults."""
|
|
129
|
+
return tuple(self._additional_capabilities)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def capabilities(self) -> tuple[AgentCapability, ...]:
|
|
133
|
+
"""Return fixed and additional capabilities in build order."""
|
|
134
|
+
return (*self._default_capabilities, *self._additional_capabilities)
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def subagents(self) -> tuple["DeployableAgent", ...]:
|
|
138
|
+
"""Return attached synchronous child definitions."""
|
|
139
|
+
return tuple(self._subagents)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def background_agents(self) -> tuple["DeployableAgent", ...]:
|
|
143
|
+
"""Return attached background child definitions."""
|
|
144
|
+
return tuple(self._background_agents)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def agent_endpoint(self) -> EndpointLike | None:
|
|
148
|
+
"""Return the endpoint configured for this node."""
|
|
149
|
+
return self._agent_endpoint
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def interaction_mode(self) -> Output | None:
|
|
153
|
+
"""Return this node's selected interaction mode."""
|
|
154
|
+
return self._interaction_mode
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def initial_messages(self) -> tuple[Path | str, ...]:
|
|
158
|
+
"""Return configured initial message sources."""
|
|
159
|
+
return self._initial_messages
|
|
160
|
+
|
|
161
|
+
def __getattr__(self, name: str) -> object:
|
|
162
|
+
"""Expose explicitly supplied capability attributes for owner reads."""
|
|
163
|
+
try:
|
|
164
|
+
attributes: dict[str, object] = object.__getattribute__(self, "_attributes")
|
|
165
|
+
return attributes[name]
|
|
166
|
+
except (AttributeError, KeyError):
|
|
167
|
+
raise AttributeError(name) from None
|
|
168
|
+
|
|
169
|
+
def set_agent_endpoint(self, endpoint: EndpointLike | None) -> None:
|
|
170
|
+
"""Set or defer this node's model endpoint."""
|
|
171
|
+
self._agent_endpoint = endpoint
|
|
172
|
+
|
|
173
|
+
def set_interaction_mode(self, interaction_mode: Output | None) -> None:
|
|
174
|
+
"""Select this node's runtime interaction mode."""
|
|
175
|
+
self._interaction_mode = interaction_mode
|
|
176
|
+
|
|
177
|
+
def set_initial_messages(self, messages: Sequence[Path | str]) -> None:
|
|
178
|
+
"""Replace this node's initial message sources before a build."""
|
|
179
|
+
self._initial_messages = tuple(messages)
|
|
180
|
+
|
|
181
|
+
def set_attributes(self, **values: object) -> None:
|
|
182
|
+
"""Set capability-specific values without altering agent structure.
|
|
183
|
+
|
|
184
|
+
Existing capability attributes may be updated for a later build. Public
|
|
185
|
+
structural attributes, methods, and all private names are protected.
|
|
186
|
+
"""
|
|
187
|
+
for name in values:
|
|
188
|
+
if name.startswith("_") or any(
|
|
189
|
+
name in cls.__dict__ for cls in type(self).__mro__
|
|
190
|
+
):
|
|
191
|
+
raise ValueError(
|
|
192
|
+
f"capability attribute cannot overwrite agent structure: {name!r}"
|
|
193
|
+
)
|
|
194
|
+
self._attributes.update(values)
|
|
195
|
+
|
|
196
|
+
def add_capabilities(self, *capabilities: AgentCapability) -> None:
|
|
197
|
+
"""Append capabilities after this node's fixed defaults."""
|
|
198
|
+
self._additional_capabilities.extend(capabilities)
|
|
199
|
+
|
|
200
|
+
def add_subagents(self, *subagents: "DeployableAgent") -> None:
|
|
201
|
+
"""Append synchronous child definitions."""
|
|
202
|
+
self._subagents.extend(self._checked_agents(subagents))
|
|
203
|
+
|
|
204
|
+
def add_background_agents(self, *agents: "DeployableAgent") -> None:
|
|
205
|
+
"""Append background child definitions."""
|
|
206
|
+
self._background_agents.extend(self._checked_agents(agents))
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _checked_agents(
|
|
210
|
+
agents: Sequence["DeployableAgent"],
|
|
211
|
+
) -> tuple["DeployableAgent", ...]:
|
|
212
|
+
checked = tuple(agents)
|
|
213
|
+
if any(not isinstance(agent, DeployableAgent) for agent in checked):
|
|
214
|
+
raise TypeError("child definitions must be DeployableAgent instances")
|
|
215
|
+
return checked
|
|
216
|
+
|
|
217
|
+
def agent_names(self, *, include_background: bool = True) -> frozenset[str]:
|
|
218
|
+
"""Validate all names and return identities in the selected branches.
|
|
219
|
+
|
|
220
|
+
Excluding background agents also excludes their descendants, for
|
|
221
|
+
foreground-only consumers such as conversation maintenance.
|
|
222
|
+
"""
|
|
223
|
+
if not include_background:
|
|
224
|
+
self.agent_names()
|
|
225
|
+
return frozenset(self._collect_names(include_background, frozenset()))
|
|
226
|
+
|
|
227
|
+
def _collect_names(
|
|
228
|
+
self, include_background: bool, ancestors: frozenset[int]
|
|
229
|
+
) -> set[str]:
|
|
230
|
+
identity = id(self)
|
|
231
|
+
if identity in ancestors:
|
|
232
|
+
raise ValueError("agent graph must not contain cycles")
|
|
233
|
+
ancestors = ancestors | {identity}
|
|
234
|
+
names = {self.name}
|
|
235
|
+
definitions = self.subagents
|
|
236
|
+
if include_background:
|
|
237
|
+
definitions += self.background_agents
|
|
238
|
+
for definition in definitions:
|
|
239
|
+
children = definition._collect_names(include_background, ancestors)
|
|
240
|
+
if names & children:
|
|
241
|
+
raise ValueError("agent names must be unique")
|
|
242
|
+
names.update(children)
|
|
243
|
+
return names
|
|
244
|
+
|
|
245
|
+
def validate(self) -> None:
|
|
246
|
+
"""Validate names and every capability requirement in the full graph."""
|
|
247
|
+
self.agent_names()
|
|
248
|
+
errors = list(self._configuration_errors())
|
|
249
|
+
if not errors:
|
|
250
|
+
return
|
|
251
|
+
details = "\n".join(f"- {error}" for error in errors)
|
|
252
|
+
raise ValueError(f"invalid agent configuration:\n{details}")
|
|
253
|
+
|
|
254
|
+
def _configuration_errors(self) -> Iterator[str]:
|
|
255
|
+
"""Yield capability requirement failures in stable graph order."""
|
|
256
|
+
for agent in self._walk():
|
|
257
|
+
yield from _capability_errors(agent)
|
|
258
|
+
|
|
259
|
+
def _walk(self) -> tuple["DeployableAgent", ...]:
|
|
260
|
+
definitions: list[DeployableAgent] = [self]
|
|
261
|
+
walked: list[DeployableAgent] = []
|
|
262
|
+
while definitions:
|
|
263
|
+
agent = definitions.pop(0)
|
|
264
|
+
walked.append(agent)
|
|
265
|
+
definitions.extend((*agent.subagents, *agent.background_agents))
|
|
266
|
+
return tuple(walked)
|
|
267
|
+
|
|
268
|
+
def build(
|
|
269
|
+
self,
|
|
270
|
+
*,
|
|
271
|
+
event_sinks: Sequence[EventSink] = (),
|
|
272
|
+
event_sink_factory: Callable[[str], Sequence[EventSink]] | None = None,
|
|
273
|
+
) -> tuple[Agent, tuple[Agent, ...]]:
|
|
274
|
+
"""Validate and build a fresh root with every background handle.
|
|
275
|
+
|
|
276
|
+
Caller sinks reach foreground branches only. The optional factory gives
|
|
277
|
+
each named agent its own fresh sinks. Construction starts no agents.
|
|
278
|
+
"""
|
|
279
|
+
self.validate()
|
|
280
|
+
return self._build(
|
|
281
|
+
event_sinks=event_sinks, event_sink_factory=event_sink_factory
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def _build(
|
|
285
|
+
self,
|
|
286
|
+
*,
|
|
287
|
+
event_sinks: Sequence[EventSink] = (),
|
|
288
|
+
event_sink_factory: Callable[[str], Sequence[EventSink]] | None = None,
|
|
289
|
+
) -> tuple[Agent, tuple[Agent, ...]]:
|
|
290
|
+
pipe = EventPipe(
|
|
291
|
+
event_sinks=(
|
|
292
|
+
*(event_sink_factory(self.name) if event_sink_factory else ()),
|
|
293
|
+
*event_sinks,
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
contributions = [
|
|
297
|
+
capability.build(self, pipe) for capability in self.capabilities
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
tools = [tool for contribution in contributions for tool in contribution.tools]
|
|
301
|
+
default_tools = [tool for c in contributions for tool in c.default_tools]
|
|
302
|
+
background_agents: list[Agent] = []
|
|
303
|
+
for definition in self.subagents:
|
|
304
|
+
child, descendants = definition._build(
|
|
305
|
+
event_sinks=event_sinks, event_sink_factory=event_sink_factory
|
|
306
|
+
)
|
|
307
|
+
tools.append(
|
|
308
|
+
run_subagent(Ctx(agent=child)).copy(
|
|
309
|
+
name=child.name,
|
|
310
|
+
description=child.description,
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
background_agents.extend(descendants)
|
|
314
|
+
for definition in self.background_agents:
|
|
315
|
+
child, descendants = definition._build(
|
|
316
|
+
event_sink_factory=event_sink_factory
|
|
317
|
+
)
|
|
318
|
+
default_tools.append(
|
|
319
|
+
run_background_agent(Ctx(agent=child)).copy(
|
|
320
|
+
name=f"start_background_agent_{child.name}",
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
background_agents.append(child)
|
|
324
|
+
background_agents.extend(descendants)
|
|
325
|
+
|
|
326
|
+
agent = Agent(
|
|
327
|
+
name=self.name,
|
|
328
|
+
description=self.description,
|
|
329
|
+
agent_endpoint=self.agent_endpoint,
|
|
330
|
+
event_pipe=pipe,
|
|
331
|
+
interaction_mode=self.interaction_mode,
|
|
332
|
+
is_agentic=self.is_agentic,
|
|
333
|
+
automatic_tool_prompt=self.automatic_tool_prompt,
|
|
334
|
+
system_prompt=self.system_prompt,
|
|
335
|
+
tools=tools,
|
|
336
|
+
default_tools=default_tools,
|
|
337
|
+
skills=[skill for c in contributions for skill in c.skills],
|
|
338
|
+
auto_loaded_skills=[
|
|
339
|
+
skill for c in contributions for skill in c.auto_loaded_skills
|
|
340
|
+
],
|
|
341
|
+
initial_messages=self.initial_messages,
|
|
342
|
+
)
|
|
343
|
+
return agent, tuple(background_agents)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
_MISSING = object()
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _type_name(expected: RequiredAttributeType) -> str:
|
|
350
|
+
types = expected if isinstance(expected, tuple) else (expected,)
|
|
351
|
+
return " or ".join(item.__name__ for item in types)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _capability_errors(agent: DeployableAgent) -> Iterator[str]:
|
|
355
|
+
"""Yield unmet attribute requirements for one configuration node."""
|
|
356
|
+
for capability in agent.capabilities:
|
|
357
|
+
for name, expected in capability.required_attributes.items():
|
|
358
|
+
error = _required_attribute_error(agent, capability, name, expected)
|
|
359
|
+
if error is not None:
|
|
360
|
+
yield error
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _required_attribute_error(
|
|
364
|
+
agent: DeployableAgent,
|
|
365
|
+
capability: AgentCapability,
|
|
366
|
+
name: str,
|
|
367
|
+
expected: RequiredAttributeType,
|
|
368
|
+
) -> str | None:
|
|
369
|
+
"""Describe one unmet requirement, or return None when it is satisfied."""
|
|
370
|
+
value = getattr(agent, name, _MISSING)
|
|
371
|
+
if value is not _MISSING and value is not None and isinstance(value, expected):
|
|
372
|
+
return None
|
|
373
|
+
|
|
374
|
+
owner = f"agent {agent.name!r}, capability {type(capability).__name__}"
|
|
375
|
+
requirement = f"attribute {name!r} must be {_type_name(expected)}"
|
|
376
|
+
if value is _MISSING:
|
|
377
|
+
actual = "it is missing"
|
|
378
|
+
elif value is None:
|
|
379
|
+
actual = "it is None"
|
|
380
|
+
else:
|
|
381
|
+
actual = f"got {type(value).__name__}"
|
|
382
|
+
return f"{owner}: {requirement}; {actual}"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
__all__ = [
|
|
386
|
+
"AgentCapability",
|
|
387
|
+
"Capability",
|
|
388
|
+
"DeployableAgent",
|
|
389
|
+
"RequiredAttributeType",
|
|
390
|
+
"RequiredAttributes",
|
|
391
|
+
]
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
"""Data-driven agent definitions and runtime-bound capabilities."""
|
|
2
|
-
|
|
3
|
-
from collections.abc import Callable, Sequence
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Protocol
|
|
7
|
-
|
|
8
|
-
from roboz.agent import Agent, run_subagent
|
|
9
|
-
from roboz.llm import EndpointLike
|
|
10
|
-
from roboz.runtime import EventPipe, EventSink, Output
|
|
11
|
-
from roboz.skill import Skill
|
|
12
|
-
from roboz.tooling import Tool
|
|
13
|
-
from roboz.tooling.context import Ctx
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class AgentCapability(Protocol):
|
|
17
|
-
"""A configured feature that binds its own tools to an agent runtime.
|
|
18
|
-
|
|
19
|
-
Store tool-specific endpoints on the capability. The build input supplies
|
|
20
|
-
the owning agent's endpoint as a default, independently of its event pipe.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def build(
|
|
24
|
-
self, pipe: EventPipe, *, default_endpoint: EndpointLike | None
|
|
25
|
-
) -> "Capability":
|
|
26
|
-
"""Bind configured tools, using the supplied default for unset endpoints."""
|
|
27
|
-
...
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass(frozen=True)
|
|
31
|
-
class Capability(AgentCapability):
|
|
32
|
-
"""Configured tools and skills that provide an agent feature.
|
|
33
|
-
|
|
34
|
-
Tools shared between a chain and default execution retain their identity.
|
|
35
|
-
Use this directly for already-bound inputs, or return one from a capability
|
|
36
|
-
builder for runtime-bound inputs. Supplied objects remain caller-owned.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
tools: tuple[Tool | Sequence[Tool], ...] = ()
|
|
40
|
-
default_tools: tuple[Tool, ...] = ()
|
|
41
|
-
skills: tuple[Skill, ...] = ()
|
|
42
|
-
auto_loaded_skills: tuple[Skill, ...] = ()
|
|
43
|
-
|
|
44
|
-
def build(
|
|
45
|
-
self, pipe: EventPipe, *, default_endpoint: EndpointLike | None
|
|
46
|
-
) -> "Capability":
|
|
47
|
-
"""Return these already-bound inputs without copying or starting work."""
|
|
48
|
-
return self
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@dataclass(frozen=True)
|
|
52
|
-
class SubAgentSpec:
|
|
53
|
-
"""A specialist definition and its parent-facing tool identity."""
|
|
54
|
-
|
|
55
|
-
definition: "AgentDefinition"
|
|
56
|
-
tool_name: str
|
|
57
|
-
tool_description: str
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@dataclass(frozen=True, kw_only=True)
|
|
61
|
-
class AgentDefinition:
|
|
62
|
-
"""Configured inputs for independently constructing an agent runtime.
|
|
63
|
-
|
|
64
|
-
All tools and skills enter through capabilities. Already-bound inputs and
|
|
65
|
-
endpoint dependencies are supplied objects; callers own their reuse. Scripted
|
|
66
|
-
mock endpoints that consume responses must be recreated for each run.
|
|
67
|
-
Each capability selects its tool endpoints, using the agent endpoint as
|
|
68
|
-
the default. Endpoint objects remain independent of runtime controls.
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
name: str
|
|
72
|
-
agent_endpoint: EndpointLike | None
|
|
73
|
-
description: str = ""
|
|
74
|
-
system_prompt: str = ""
|
|
75
|
-
interaction_mode: Output | None = Output.CLI
|
|
76
|
-
is_agentic: bool = True
|
|
77
|
-
automatic_tool_prompt: bool = True
|
|
78
|
-
capabilities: tuple[AgentCapability, ...] = ()
|
|
79
|
-
subagents: tuple[SubAgentSpec, ...] = ()
|
|
80
|
-
initial_messages: tuple[Path | str, ...] = ()
|
|
81
|
-
|
|
82
|
-
def agent_names(self) -> frozenset[str]:
|
|
83
|
-
"""Return recursive agent identities, rejecting ambiguous names."""
|
|
84
|
-
names = {self.name}
|
|
85
|
-
for spec in self.subagents:
|
|
86
|
-
children = spec.definition.agent_names()
|
|
87
|
-
if names & children:
|
|
88
|
-
raise ValueError("agent names must be unique")
|
|
89
|
-
names.update(children)
|
|
90
|
-
return frozenset(names)
|
|
91
|
-
|
|
92
|
-
def build(
|
|
93
|
-
self,
|
|
94
|
-
*,
|
|
95
|
-
event_sinks: Sequence[EventSink] = (),
|
|
96
|
-
event_sink_factory: Callable[[str], Sequence[EventSink]] | None = None,
|
|
97
|
-
) -> Agent:
|
|
98
|
-
"""Create fresh pipes, resolve capabilities, and construct the agent once.
|
|
99
|
-
|
|
100
|
-
Caller sinks follow specialists. The optional factory supplies fresh
|
|
101
|
-
agent-specific sinks by name; these are never inherited by children.
|
|
102
|
-
Without supplied sinks, construction selects no output or persistence.
|
|
103
|
-
"""
|
|
104
|
-
self.agent_names()
|
|
105
|
-
pipe = EventPipe(
|
|
106
|
-
event_sinks=(
|
|
107
|
-
*(event_sink_factory(self.name) if event_sink_factory else ()),
|
|
108
|
-
*event_sinks,
|
|
109
|
-
)
|
|
110
|
-
)
|
|
111
|
-
contributions = []
|
|
112
|
-
for capability in self.capabilities:
|
|
113
|
-
contributions.append(
|
|
114
|
-
capability.build(pipe, default_endpoint=self.agent_endpoint)
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
tools = [tool for contribution in contributions for tool in contribution.tools]
|
|
118
|
-
for spec in self.subagents:
|
|
119
|
-
child = spec.definition.build(
|
|
120
|
-
event_sinks=event_sinks, event_sink_factory=event_sink_factory
|
|
121
|
-
)
|
|
122
|
-
delegation = run_subagent(Ctx(agent=child)).copy(
|
|
123
|
-
name=spec.tool_name, description=spec.tool_description
|
|
124
|
-
)
|
|
125
|
-
tools.append(delegation)
|
|
126
|
-
|
|
127
|
-
return Agent(
|
|
128
|
-
name=self.name,
|
|
129
|
-
description=self.description,
|
|
130
|
-
agent_endpoint=self.agent_endpoint,
|
|
131
|
-
event_pipe=pipe,
|
|
132
|
-
interaction_mode=self.interaction_mode,
|
|
133
|
-
is_agentic=self.is_agentic,
|
|
134
|
-
automatic_tool_prompt=self.automatic_tool_prompt,
|
|
135
|
-
system_prompt=self.system_prompt,
|
|
136
|
-
tools=tools,
|
|
137
|
-
default_tools=[tool for c in contributions for tool in c.default_tools],
|
|
138
|
-
skills=[skill for c in contributions for skill in c.skills],
|
|
139
|
-
auto_loaded_skills=[
|
|
140
|
-
skill for c in contributions for skill in c.auto_loaded_skills
|
|
141
|
-
],
|
|
142
|
-
initial_messages=self.initial_messages,
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
__all__ = [
|
|
147
|
-
"AgentCapability",
|
|
148
|
-
"AgentDefinition",
|
|
149
|
-
"Capability",
|
|
150
|
-
"SubAgentSpec",
|
|
151
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|