operator-architecture 0.2.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.
- operator_architecture-0.2.0/.gitignore +15 -0
- operator_architecture-0.2.0/.python-version +1 -0
- operator_architecture-0.2.0/MEMORY.md +24 -0
- operator_architecture-0.2.0/ORIGINAL_CONCEPT.md +74 -0
- operator_architecture-0.2.0/PKG-INFO +216 -0
- operator_architecture-0.2.0/README.md +207 -0
- operator_architecture-0.2.0/examples/minimal_callable.py +60 -0
- operator_architecture-0.2.0/pyproject.toml +17 -0
- operator_architecture-0.2.0/src/operator_architecture/__init__.py +40 -0
- operator_architecture-0.2.0/src/operator_architecture/agent.py +157 -0
- operator_architecture-0.2.0/src/operator_architecture/coordinator.py +42 -0
- operator_architecture-0.2.0/src/operator_architecture/machine.py +507 -0
- operator_architecture-0.2.0/src/operator_architecture/messages.py +65 -0
- operator_architecture-0.2.0/src/operator_architecture/orchestration.py +88 -0
- operator_architecture-0.2.0/src/operator_architecture/streaming.py +38 -0
- operator_architecture-0.2.0/uv.lock +376 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Operator Architecture memory
|
|
2
|
+
|
|
3
|
+
## What this is
|
|
4
|
+
- **Pure orchestration SDK** (`operator-architecture` → `import operator_architecture`)
|
|
5
|
+
- Owns state, OpenAI-shaped context, sub-agent registry, commission → stage → accept/instruct
|
|
6
|
+
- Does **not** call LLMs, run tool loops, or ship coding tools / TUI / CLI
|
|
7
|
+
|
|
8
|
+
## Public API
|
|
9
|
+
- `StateMachine`, `Coordinator`, `AgentSpec`, `AgentRequest`, `AgentResult`, `AgentRunner`
|
|
10
|
+
- `Messages` (OpenAI chat dicts), `callable_agent`, `orchestration_tools` / `tool_schemas`
|
|
11
|
+
- `streaming_callback` for host observability (`StreamEvent` phases)
|
|
12
|
+
|
|
13
|
+
## Host contract
|
|
14
|
+
- Each `AgentSpec.runner` implements `async run(request, *, streaming_callback=None) -> AgentResult`
|
|
15
|
+
- Adapters (Relay, LangChain, HTTP) live in the host — zero coupling to encode / axe / courier-os
|
|
16
|
+
|
|
17
|
+
## Layout
|
|
18
|
+
- `src/operator_architecture/` — SDK only
|
|
19
|
+
- `examples/minimal_callable.py` — no-LLM smoke example
|
|
20
|
+
- `ORIGINAL_CONCEPT.md` — archived coding-operator vision
|
|
21
|
+
- `README.md` — current SDK docs
|
|
22
|
+
|
|
23
|
+
## Non-goals (here)
|
|
24
|
+
- AXE / agentloop, Courier OS inference, Scout FS tools, SQLite/ACP (future optional modules)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
> **Historical document.** This describes an earlier coding-operator concept (CLI, five pillars, Rust/SQLite SM). It is **not** the Operator Architecture SDK contract. See [README.md](README.md) for the current orchestration SDK.
|
|
2
|
+
|
|
3
|
+
# Operator Architecture Technical Specification
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
The Operator Architecture is designed to solve the problem of "context explosion" in large-scale LLM-driven development. Instead of a single behemoth implementation that loses accuracy as context windows fill, this architecture splits complex tasks into focused, purpose-built **Sub-Agents**. Each sub-agent operates with hyper-efficient context management, receiving only the specific information required to complete its current objective, while a centralized state machine maintains the global truth.
|
|
7
|
+
|
|
8
|
+
## Core Components
|
|
9
|
+
|
|
10
|
+
### 1. The Coordinator
|
|
11
|
+
The primary agent persona for user interaction. The Coordinator acts as the orchestrator and reviewer, translating high-level user intent into actionable objectives and validating the outputs of sub-agents. It is the sole interface for the human user.
|
|
12
|
+
|
|
13
|
+
### 2. The State Machine
|
|
14
|
+
The backbone of the entire system. Implemented in **Rust**, this layer manages orchestration and persistence via **SQLite**.
|
|
15
|
+
- **Session Management**: Tracks active agent sessions.
|
|
16
|
+
- **Plan & State**: Maintains the current plan, step statuses, and objective progress.
|
|
17
|
+
- **Audit Log**: Tracks all file edits (diff logs) for traceability and safety.
|
|
18
|
+
|
|
19
|
+
### 3. Sub-Agents (The 5 Pillars)
|
|
20
|
+
Each sub-agent is specialized in a specific domain to minimize token noise and maximize reasoning capabilities:
|
|
21
|
+
- **Researcher**: A read-only toolkit specialized in codebase navigation, grep, and semantic comprehension.
|
|
22
|
+
- **Implementer**: A read/write toolkit capable of performing complex code modifications and surgical edits.
|
|
23
|
+
- **Bash/Tester**: A terminal-access agent designed to execute commands, run test suites, and verify environment state.
|
|
24
|
+
- **Documenter**: A specialized toolkit for generating, updating, and maintaining Markdown-based documentation.
|
|
25
|
+
- **Planner**: A strategic agent responsible for breaking down high-level objectives into granular, executable implementation steps.
|
|
26
|
+
|
|
27
|
+
## The Workflow
|
|
28
|
+
|
|
29
|
+
### Commissioning
|
|
30
|
+
The Coordinator does not simply "talk" to agents; it commissions them using the State Machine. It passes structured properties to sub-agents:
|
|
31
|
+
- **Objective**: The specific goal of the sub-agent.
|
|
32
|
+
- **Checklist**: A list of requirements to be met.
|
|
33
|
+
- **Agent Props**: Context-specific metadata needed for the task.
|
|
34
|
+
|
|
35
|
+
### Plan Execution
|
|
36
|
+
Plans are executed incrementally:
|
|
37
|
+
1. The **Planner** creates a sequence of steps.
|
|
38
|
+
2. **Implementation Loop**. Each step triggers an implementation agent with the context of the plan overview, all the steps overview, it's specific step implementation plan, and the agent summary from previously completed steps.
|
|
39
|
+
3. **Verification Loop**: Implementation steps can trigger the **Bash/Tester** agent automatically to verify that the code change satisfies the requirement before the step is marked as complete.
|
|
40
|
+
|
|
41
|
+
### Context Efficiency
|
|
42
|
+
To prevent hallucinations and token waste, agents do not receive full conversation histories. They receive:
|
|
43
|
+
- A high-level **Plan Overview**.
|
|
44
|
+
- The current **Step Objective**.
|
|
45
|
+
- **Summaries** of prior successful steps.
|
|
46
|
+
This ensures the LLM's attention is focused entirely on the task at hand.
|
|
47
|
+
|
|
48
|
+
## The CLI & Server Model
|
|
49
|
+
|
|
50
|
+
### Lifecycle Management
|
|
51
|
+
The `operator` CLI manages a persistent background server:
|
|
52
|
+
- `operator server --start`: Starts the orchestration server.
|
|
53
|
+
- `operator server --stop`: Gracefully shuts down the server.
|
|
54
|
+
- `operator server --bind <session>`: Attaches the CLI to an existing session/project.
|
|
55
|
+
- `operator server --drop <session>`: Detaches the live session from the project.
|
|
56
|
+
|
|
57
|
+
### Intelligent Automation
|
|
58
|
+
The CLI is designed for ease of use: if a command is issued while the server is offline, the CLI automatically handles the start/bind sequence.
|
|
59
|
+
|
|
60
|
+
### Extensibility
|
|
61
|
+
The system exposes an **OpenAPI surface on port 9200**, allowing web-based dashboards or third-party tools to orchestrate agents and monitor the State Machine in real-time.
|
|
62
|
+
|
|
63
|
+
This allows developers to integrate into IDEs or create their own visual agent windows that utilize the Operator Architecture.
|
|
64
|
+
|
|
65
|
+
## Technical Stack
|
|
66
|
+
- Python
|
|
67
|
+
- **Persistence**: [SQLite](https://www.sqlite.org/) (Lightweight, file-based, and ACID-compliant for session and diff tracking).
|
|
68
|
+
- **API**: REST/OpenAPI (For seamless integration). Using **FastAPI**
|
|
69
|
+
- **Configuration**: Managed via a `.operator` directory containing the database and system settings.
|
|
70
|
+
|
|
71
|
+
## Future Work
|
|
72
|
+
- **Parallel Step Execution**: Allowing multiple non-dependent sub-agents to work simultaneously.
|
|
73
|
+
- **Courier Cloud**: Integration for remote authentication and automated configuration population.
|
|
74
|
+
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: operator-architecture
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Framework-agnostic multi-agent orchestration SDK (Operator Architecture)
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Provides-Extra: dev
|
|
7
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# Operator Architecture
|
|
11
|
+
|
|
12
|
+
**Framework-agnostic multi-agent orchestration SDK.**
|
|
13
|
+
|
|
14
|
+
Operator Architecture (OA) manages **state**, **context**, **sub-agents**, and **orchestration**. It does **not** call LLMs, run tool loops, or ship coding tools. You plug in any agent runtime — Relay, LangChain, OpenAI Agents, HTTP services, or a plain async function.
|
|
15
|
+
|
|
16
|
+
For the earlier coding-CLI / five-pillars vision, see [ORIGINAL_CONCEPT.md](ORIGINAL_CONCEPT.md).
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -e .
|
|
22
|
+
# or
|
|
23
|
+
uv pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Runtime dependencies: **none** (stdlib only).
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from operator_architecture import (
|
|
30
|
+
StateMachine,
|
|
31
|
+
Coordinator,
|
|
32
|
+
AgentSpec,
|
|
33
|
+
AgentRequest,
|
|
34
|
+
AgentResult,
|
|
35
|
+
callable_agent,
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What OA owns vs what you own
|
|
40
|
+
|
|
41
|
+
| Operator Architecture | Your host |
|
|
42
|
+
|-----------------------|-----------|
|
|
43
|
+
| `StateMachine`, `Coordinator`, `AgentSpec` | Agent implementations (`AgentRunner`) |
|
|
44
|
+
| OpenAI-compatible message threads | Relay / LangChain / custom loops |
|
|
45
|
+
| `commission` → stage → `accept` / `instruct` | Models, API keys, tools, MCP, FS |
|
|
46
|
+
| Optional `streaming_callback` fan-in | Emitting stream events from runners |
|
|
47
|
+
|
|
48
|
+
## Core objects
|
|
49
|
+
|
|
50
|
+
### `AgentSpec` + `AgentRunner`
|
|
51
|
+
|
|
52
|
+
Register any number of sub-agents. Each needs a **runner** OA will call:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
async def research(request: AgentRequest) -> AgentResult:
|
|
56
|
+
# call Relay, LangChain, HTTP, … — OA does not care
|
|
57
|
+
return AgentResult(content=f"Findings for: {request.objective}")
|
|
58
|
+
|
|
59
|
+
researcher = AgentSpec(
|
|
60
|
+
name="researcher",
|
|
61
|
+
description="Read-only exploration",
|
|
62
|
+
skill="You are a careful researcher. Answer with concrete findings.",
|
|
63
|
+
runner=callable_agent(research),
|
|
64
|
+
model="my-model", # metadata only
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Protocol:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
class AgentRunner(Protocol):
|
|
72
|
+
async def run(
|
|
73
|
+
self,
|
|
74
|
+
request: AgentRequest,
|
|
75
|
+
*,
|
|
76
|
+
streaming_callback: StreamingCallback = None,
|
|
77
|
+
) -> AgentResult: ...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`AgentRequest` carries OpenAI-shaped `messages`, `objective`, `skill`, optional `checklist` / `agent_props`.
|
|
81
|
+
`AgentResult.content` is staged as `agent_message`.
|
|
82
|
+
|
|
83
|
+
### `Coordinator`
|
|
84
|
+
|
|
85
|
+
Owns the user-facing skill string and optional runner for `sm.run()`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
coordinator = Coordinator(
|
|
89
|
+
skill="You operate the state machine…", # default skill provided
|
|
90
|
+
runner=my_coordinator_runner, # optional
|
|
91
|
+
model="coord-model",
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `StateMachine`
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
sm = StateMachine(coordinator=coordinator, agents=[researcher])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
No process-global singleton — hold the instance yourself (`sm = StateMachine(...)`).
|
|
102
|
+
|
|
103
|
+
## Lifecycle
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
user → (optional) sm.run / host
|
|
107
|
+
→ commission(agent, objective)
|
|
108
|
+
→ AgentRunner.run(AgentRequest)
|
|
109
|
+
→ stage agent_message (status=staged)
|
|
110
|
+
→ accept_agent_result(agent, index) # compact result for core thread
|
|
111
|
+
or instruct_agent(agent, index, message) # continue junior
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Direct API (always available)
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
staged = await sm.commission("researcher", "Find all uses of vLLM")
|
|
118
|
+
msg = sm.get_agent_message("researcher", 1)
|
|
119
|
+
accepted = sm.accept("researcher", 1) # or accept_agent_result
|
|
120
|
+
# or:
|
|
121
|
+
await sm.instruct("researcher", 1, "Also check Dockerfiles")
|
|
122
|
+
|
|
123
|
+
sm.list_agents()
|
|
124
|
+
sm.list_objectives()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Indexed access: `sm.agent("researcher")[1].agent_message`.
|
|
128
|
+
|
|
129
|
+
### `sm.run` (optional)
|
|
130
|
+
|
|
131
|
+
If `Coordinator.runner` is set, `await sm.run(user_text, streaming_callback=...)` appends the user message and invokes that runner with:
|
|
132
|
+
|
|
133
|
+
- `metadata["tools"]` — orchestration callables
|
|
134
|
+
- `metadata["tool_schemas"]` — OpenAI `tools[]` schemas
|
|
135
|
+
|
|
136
|
+
**OA does not execute a tool loop.** Your runner (Relay, LangChain, …) must invoke those callables when the model requests them.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
tools = sm.orchestration_tools()
|
|
140
|
+
# list_agents, commission, get_agent_message,
|
|
141
|
+
# accept_agent_result, instruct_agent, list_objectives
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## OpenAI-compatible context
|
|
145
|
+
|
|
146
|
+
Coordinator and junior threads are lists of chat.completions-style dicts:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
{"role": "system"|"user"|"assistant"|"tool", "content": "...", ...}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Helpers: `Messages` (`.system()`, `.user()`, `.assistant()`, `.to_list()`).
|
|
153
|
+
|
|
154
|
+
## `streaming_callback`
|
|
155
|
+
|
|
156
|
+
Optional observability hook (UI, logs, websockets). Sync or async:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
async def on_stream(event: dict) -> None:
|
|
160
|
+
print(event["phase"], event.get("detail", "")[:80])
|
|
161
|
+
|
|
162
|
+
await sm.commission("researcher", "…", streaming_callback=on_stream)
|
|
163
|
+
# or
|
|
164
|
+
await sm.run("…", streaming_callback=on_stream)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Phases: `start`, `token`, `tool_start`, `tool_result`, `tool_error`, `commissioned`, `done`, `fail`.
|
|
168
|
+
Runners may emit events; OA forwards them and also emits lifecycle events around commission.
|
|
169
|
+
|
|
170
|
+
## Writing adapters
|
|
171
|
+
|
|
172
|
+
| Adapter idea | Wraps |
|
|
173
|
+
|--------------|--------|
|
|
174
|
+
| `callable_agent(fn)` | Plain async/sync function (shipped) |
|
|
175
|
+
| Relay agent | `encode.relay_async` / `courier_os.relay` inside `run()` |
|
|
176
|
+
| LangChain agent | AgentExecutor / LangGraph; map messages ↔ LC messages |
|
|
177
|
+
| HTTP agent | POST OpenAI-compatible or custom JSON API |
|
|
178
|
+
|
|
179
|
+
OA never imports those libraries. Keep adapters in your host.
|
|
180
|
+
|
|
181
|
+
### Minimal Relay sketch (host code)
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
class RelayRunner:
|
|
185
|
+
def __init__(self, model, api_key, base_url, tools):
|
|
186
|
+
self.model, self.api_key, self.base_url, self.tools = model, api_key, base_url, tools
|
|
187
|
+
|
|
188
|
+
async def run(self, request, *, streaming_callback=None):
|
|
189
|
+
import encode
|
|
190
|
+
messages = encode.Messages()
|
|
191
|
+
for m in request.messages:
|
|
192
|
+
# map dicts into encode.Messages as needed
|
|
193
|
+
...
|
|
194
|
+
out = await encode.relay_async(
|
|
195
|
+
model=self.model,
|
|
196
|
+
api_key=self.api_key,
|
|
197
|
+
base_url=self.base_url,
|
|
198
|
+
messages=messages,
|
|
199
|
+
tools=self.tools or request.metadata.get("tools"),
|
|
200
|
+
)
|
|
201
|
+
return AgentResult(content=out.content or "", raw=out)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Example
|
|
205
|
+
|
|
206
|
+
See [`examples/minimal_callable.py`](examples/minimal_callable.py).
|
|
207
|
+
|
|
208
|
+
## Design boundaries
|
|
209
|
+
|
|
210
|
+
- **Not** a coding CLI or competitor to Cursor/Claude Code
|
|
211
|
+
- **Not** an inference or tool-loop SDK (use Courier OS, encode, agentloop, LangChain, …)
|
|
212
|
+
- **Not** coupled to AXE or Courier OS — hosts may wrap them as runners
|
|
213
|
+
|
|
214
|
+
## License / status
|
|
215
|
+
|
|
216
|
+
Early SDK (`0.2.0`). API may evolve; the orchestration contract (commission / stage / accept) is the stable idea.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Operator Architecture
|
|
2
|
+
|
|
3
|
+
**Framework-agnostic multi-agent orchestration SDK.**
|
|
4
|
+
|
|
5
|
+
Operator Architecture (OA) manages **state**, **context**, **sub-agents**, and **orchestration**. It does **not** call LLMs, run tool loops, or ship coding tools. You plug in any agent runtime — Relay, LangChain, OpenAI Agents, HTTP services, or a plain async function.
|
|
6
|
+
|
|
7
|
+
For the earlier coding-CLI / five-pillars vision, see [ORIGINAL_CONCEPT.md](ORIGINAL_CONCEPT.md).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install -e .
|
|
13
|
+
# or
|
|
14
|
+
uv pip install -e .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Runtime dependencies: **none** (stdlib only).
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from operator_architecture import (
|
|
21
|
+
StateMachine,
|
|
22
|
+
Coordinator,
|
|
23
|
+
AgentSpec,
|
|
24
|
+
AgentRequest,
|
|
25
|
+
AgentResult,
|
|
26
|
+
callable_agent,
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What OA owns vs what you own
|
|
31
|
+
|
|
32
|
+
| Operator Architecture | Your host |
|
|
33
|
+
|-----------------------|-----------|
|
|
34
|
+
| `StateMachine`, `Coordinator`, `AgentSpec` | Agent implementations (`AgentRunner`) |
|
|
35
|
+
| OpenAI-compatible message threads | Relay / LangChain / custom loops |
|
|
36
|
+
| `commission` → stage → `accept` / `instruct` | Models, API keys, tools, MCP, FS |
|
|
37
|
+
| Optional `streaming_callback` fan-in | Emitting stream events from runners |
|
|
38
|
+
|
|
39
|
+
## Core objects
|
|
40
|
+
|
|
41
|
+
### `AgentSpec` + `AgentRunner`
|
|
42
|
+
|
|
43
|
+
Register any number of sub-agents. Each needs a **runner** OA will call:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
async def research(request: AgentRequest) -> AgentResult:
|
|
47
|
+
# call Relay, LangChain, HTTP, … — OA does not care
|
|
48
|
+
return AgentResult(content=f"Findings for: {request.objective}")
|
|
49
|
+
|
|
50
|
+
researcher = AgentSpec(
|
|
51
|
+
name="researcher",
|
|
52
|
+
description="Read-only exploration",
|
|
53
|
+
skill="You are a careful researcher. Answer with concrete findings.",
|
|
54
|
+
runner=callable_agent(research),
|
|
55
|
+
model="my-model", # metadata only
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Protocol:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
class AgentRunner(Protocol):
|
|
63
|
+
async def run(
|
|
64
|
+
self,
|
|
65
|
+
request: AgentRequest,
|
|
66
|
+
*,
|
|
67
|
+
streaming_callback: StreamingCallback = None,
|
|
68
|
+
) -> AgentResult: ...
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`AgentRequest` carries OpenAI-shaped `messages`, `objective`, `skill`, optional `checklist` / `agent_props`.
|
|
72
|
+
`AgentResult.content` is staged as `agent_message`.
|
|
73
|
+
|
|
74
|
+
### `Coordinator`
|
|
75
|
+
|
|
76
|
+
Owns the user-facing skill string and optional runner for `sm.run()`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
coordinator = Coordinator(
|
|
80
|
+
skill="You operate the state machine…", # default skill provided
|
|
81
|
+
runner=my_coordinator_runner, # optional
|
|
82
|
+
model="coord-model",
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `StateMachine`
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
sm = StateMachine(coordinator=coordinator, agents=[researcher])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
No process-global singleton — hold the instance yourself (`sm = StateMachine(...)`).
|
|
93
|
+
|
|
94
|
+
## Lifecycle
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
user → (optional) sm.run / host
|
|
98
|
+
→ commission(agent, objective)
|
|
99
|
+
→ AgentRunner.run(AgentRequest)
|
|
100
|
+
→ stage agent_message (status=staged)
|
|
101
|
+
→ accept_agent_result(agent, index) # compact result for core thread
|
|
102
|
+
or instruct_agent(agent, index, message) # continue junior
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Direct API (always available)
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
staged = await sm.commission("researcher", "Find all uses of vLLM")
|
|
109
|
+
msg = sm.get_agent_message("researcher", 1)
|
|
110
|
+
accepted = sm.accept("researcher", 1) # or accept_agent_result
|
|
111
|
+
# or:
|
|
112
|
+
await sm.instruct("researcher", 1, "Also check Dockerfiles")
|
|
113
|
+
|
|
114
|
+
sm.list_agents()
|
|
115
|
+
sm.list_objectives()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Indexed access: `sm.agent("researcher")[1].agent_message`.
|
|
119
|
+
|
|
120
|
+
### `sm.run` (optional)
|
|
121
|
+
|
|
122
|
+
If `Coordinator.runner` is set, `await sm.run(user_text, streaming_callback=...)` appends the user message and invokes that runner with:
|
|
123
|
+
|
|
124
|
+
- `metadata["tools"]` — orchestration callables
|
|
125
|
+
- `metadata["tool_schemas"]` — OpenAI `tools[]` schemas
|
|
126
|
+
|
|
127
|
+
**OA does not execute a tool loop.** Your runner (Relay, LangChain, …) must invoke those callables when the model requests them.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
tools = sm.orchestration_tools()
|
|
131
|
+
# list_agents, commission, get_agent_message,
|
|
132
|
+
# accept_agent_result, instruct_agent, list_objectives
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## OpenAI-compatible context
|
|
136
|
+
|
|
137
|
+
Coordinator and junior threads are lists of chat.completions-style dicts:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
{"role": "system"|"user"|"assistant"|"tool", "content": "...", ...}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Helpers: `Messages` (`.system()`, `.user()`, `.assistant()`, `.to_list()`).
|
|
144
|
+
|
|
145
|
+
## `streaming_callback`
|
|
146
|
+
|
|
147
|
+
Optional observability hook (UI, logs, websockets). Sync or async:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
async def on_stream(event: dict) -> None:
|
|
151
|
+
print(event["phase"], event.get("detail", "")[:80])
|
|
152
|
+
|
|
153
|
+
await sm.commission("researcher", "…", streaming_callback=on_stream)
|
|
154
|
+
# or
|
|
155
|
+
await sm.run("…", streaming_callback=on_stream)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Phases: `start`, `token`, `tool_start`, `tool_result`, `tool_error`, `commissioned`, `done`, `fail`.
|
|
159
|
+
Runners may emit events; OA forwards them and also emits lifecycle events around commission.
|
|
160
|
+
|
|
161
|
+
## Writing adapters
|
|
162
|
+
|
|
163
|
+
| Adapter idea | Wraps |
|
|
164
|
+
|--------------|--------|
|
|
165
|
+
| `callable_agent(fn)` | Plain async/sync function (shipped) |
|
|
166
|
+
| Relay agent | `encode.relay_async` / `courier_os.relay` inside `run()` |
|
|
167
|
+
| LangChain agent | AgentExecutor / LangGraph; map messages ↔ LC messages |
|
|
168
|
+
| HTTP agent | POST OpenAI-compatible or custom JSON API |
|
|
169
|
+
|
|
170
|
+
OA never imports those libraries. Keep adapters in your host.
|
|
171
|
+
|
|
172
|
+
### Minimal Relay sketch (host code)
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
class RelayRunner:
|
|
176
|
+
def __init__(self, model, api_key, base_url, tools):
|
|
177
|
+
self.model, self.api_key, self.base_url, self.tools = model, api_key, base_url, tools
|
|
178
|
+
|
|
179
|
+
async def run(self, request, *, streaming_callback=None):
|
|
180
|
+
import encode
|
|
181
|
+
messages = encode.Messages()
|
|
182
|
+
for m in request.messages:
|
|
183
|
+
# map dicts into encode.Messages as needed
|
|
184
|
+
...
|
|
185
|
+
out = await encode.relay_async(
|
|
186
|
+
model=self.model,
|
|
187
|
+
api_key=self.api_key,
|
|
188
|
+
base_url=self.base_url,
|
|
189
|
+
messages=messages,
|
|
190
|
+
tools=self.tools or request.metadata.get("tools"),
|
|
191
|
+
)
|
|
192
|
+
return AgentResult(content=out.content or "", raw=out)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Example
|
|
196
|
+
|
|
197
|
+
See [`examples/minimal_callable.py`](examples/minimal_callable.py).
|
|
198
|
+
|
|
199
|
+
## Design boundaries
|
|
200
|
+
|
|
201
|
+
- **Not** a coding CLI or competitor to Cursor/Claude Code
|
|
202
|
+
- **Not** an inference or tool-loop SDK (use Courier OS, encode, agentloop, LangChain, …)
|
|
203
|
+
- **Not** coupled to AXE or Courier OS — hosts may wrap them as runners
|
|
204
|
+
|
|
205
|
+
## License / status
|
|
206
|
+
|
|
207
|
+
Early SDK (`0.2.0`). API may evolve; the orchestration contract (commission / stage / accept) is the stable idea.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Minimal OA example — host-provided callable agents, no LLM libraries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
from operator_architecture import (
|
|
8
|
+
AgentRequest,
|
|
9
|
+
AgentResult,
|
|
10
|
+
AgentSpec,
|
|
11
|
+
Coordinator,
|
|
12
|
+
StateMachine,
|
|
13
|
+
callable_agent,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def research(request: AgentRequest) -> AgentResult:
|
|
18
|
+
lines = [
|
|
19
|
+
f"Skill: {request.skill[:60]}…",
|
|
20
|
+
f"Objective: {request.objective}",
|
|
21
|
+
]
|
|
22
|
+
if request.checklist:
|
|
23
|
+
lines.append("Checklist: " + ", ".join(request.checklist))
|
|
24
|
+
return AgentResult(content="\n".join(lines))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def main() -> None:
|
|
28
|
+
sm = StateMachine(
|
|
29
|
+
coordinator=Coordinator(),
|
|
30
|
+
agents=[
|
|
31
|
+
AgentSpec(
|
|
32
|
+
name="researcher",
|
|
33
|
+
description="Read-only exploration",
|
|
34
|
+
skill="You are a researcher. Be concrete.",
|
|
35
|
+
runner=callable_agent(research),
|
|
36
|
+
),
|
|
37
|
+
],
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
events: list[str] = []
|
|
41
|
+
|
|
42
|
+
async def on_stream(event: dict) -> None:
|
|
43
|
+
events.append(f"{event.get('agent')}:{event.get('phase')}")
|
|
44
|
+
|
|
45
|
+
staged = await sm.commission(
|
|
46
|
+
"researcher",
|
|
47
|
+
"Map where vLLM is configured",
|
|
48
|
+
checklist=["find config", "note versions"],
|
|
49
|
+
streaming_callback=on_stream,
|
|
50
|
+
)
|
|
51
|
+
print("staged:", staged["status"], staged["index"])
|
|
52
|
+
print("message:\n", sm.agent("researcher")[1].agent_message)
|
|
53
|
+
print("accept:", sm.accept("researcher", 1)["status"])
|
|
54
|
+
print("stream phases:", events)
|
|
55
|
+
print("agents:", sm.list_agents())
|
|
56
|
+
print("objectives:", sm.list_objectives())
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "operator-architecture"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Framework-agnostic multi-agent orchestration SDK (Operator Architecture)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.optional-dependencies]
|
|
10
|
+
dev = ["pytest>=8.0"]
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.wheel]
|
|
17
|
+
packages = ["src/operator_architecture"]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Operator Architecture — framework-agnostic multi-agent orchestration SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from operator_architecture.agent import (
|
|
6
|
+
AgentHandle,
|
|
7
|
+
AgentRequest,
|
|
8
|
+
AgentResult,
|
|
9
|
+
AgentRunner,
|
|
10
|
+
AgentSpec,
|
|
11
|
+
ObjectiveSlot,
|
|
12
|
+
callable_agent,
|
|
13
|
+
)
|
|
14
|
+
from operator_architecture.coordinator import DEFAULT_COORDINATOR_SKILL, Coordinator
|
|
15
|
+
from operator_architecture.machine import StateMachine
|
|
16
|
+
from operator_architecture.messages import Message, Messages
|
|
17
|
+
from operator_architecture.orchestration import openai_tool_schema, tool_schemas
|
|
18
|
+
from operator_architecture.streaming import StreamEvent, StreamingCallback, emit_stream
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AgentHandle",
|
|
22
|
+
"AgentRequest",
|
|
23
|
+
"AgentResult",
|
|
24
|
+
"AgentRunner",
|
|
25
|
+
"AgentSpec",
|
|
26
|
+
"Coordinator",
|
|
27
|
+
"DEFAULT_COORDINATOR_SKILL",
|
|
28
|
+
"Message",
|
|
29
|
+
"Messages",
|
|
30
|
+
"ObjectiveSlot",
|
|
31
|
+
"StateMachine",
|
|
32
|
+
"StreamEvent",
|
|
33
|
+
"StreamingCallback",
|
|
34
|
+
"callable_agent",
|
|
35
|
+
"emit_stream",
|
|
36
|
+
"openai_tool_schema",
|
|
37
|
+
"tool_schemas",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
__version__ = "0.2.0"
|