fixing docs
This commit is contained in:
parent
86ad3c7eb2
commit
6696c06e4f
2 changed files with 214 additions and 1 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
G# Configuration — organism.yaml (v2.0)
|
G# Configuration — organism.yaml (v2.1)
|
||||||
|
|
||||||
The entire organism is declared in a single YAML file (default: `config/organism.yaml`).
|
The entire organism is declared in a single YAML file (default: `config/organism.yaml`).
|
||||||
Loaded at bootstrap — single source of truth for initial composition.
|
Loaded at bootstrap — single source of truth for initial composition.
|
||||||
|
|
|
||||||
213
docs/listener-class-v2.1.md
Normal file
213
docs/listener-class-v2.1.md
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
**listener-class-v2.1.md**
|
||||||
|
**January 07, 2026**
|
||||||
|
**AgentServer v2.1 — The Listener Class & Registration**
|
||||||
|
|
||||||
|
This is the canonical documentation for defining and registering capabilities in AgentServer v2.1.
|
||||||
|
All other descriptions of listener creation are superseded by this file.
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
A **Listener** declares a bounded, sovereign capability consisting of:
|
||||||
|
- A precise input contract (an `@xmlify` dataclass)
|
||||||
|
- A pure async handler function
|
||||||
|
- A mandatory human-readable description
|
||||||
|
|
||||||
|
From this declaration alone, the organism autonomously generates:
|
||||||
|
- Cached XSD schema
|
||||||
|
- Example payload
|
||||||
|
- Rich tool-prompt fragment
|
||||||
|
- Dedicated preprocessing pipeline
|
||||||
|
- Routing table entry with a fully derived, globally unique root tag
|
||||||
|
|
||||||
|
The developer supplies **only** Python code and a minimal YAML entry. Everything else is drift-proof and attack-resistant.
|
||||||
|
|
||||||
|
### Root Tag Derivation (Locked Rule)
|
||||||
|
The wire-format root element of every payload is constructed automatically as:
|
||||||
|
|
||||||
|
```
|
||||||
|
{lowercase_registered_name}.{lowercase_dataclass_name}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
| Registered name | Dataclass name | → Root tag on wire |
|
||||||
|
|-----------------------|---------------------|------------------------------------------|
|
||||||
|
| calculator.add | AddPayload | calculator.add.addpayload |
|
||||||
|
| calculator.multiply | MultiplyPayload | calculator.multiply.multiplypayload |
|
||||||
|
| researcher | ResearchPayload | researcher.researchpayload |
|
||||||
|
| web_search | SearchPayload | web_search.searchpayload |
|
||||||
|
|
||||||
|
This derivation happens once at registration time.
|
||||||
|
The developer **never** writes, repeats, or overrides the root tag.
|
||||||
|
|
||||||
|
### YAML Entry (organism.yaml)
|
||||||
|
Required fields:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
listeners:
|
||||||
|
- name: calculator.add # Unique across organism, dots allowed for hierarchy
|
||||||
|
payload_class: tools.calculator.AddPayload
|
||||||
|
handler: tools.calculator.add_handler
|
||||||
|
description: "Adds two integers and returns their sum." # Mandatory for prompt generation
|
||||||
|
|
||||||
|
- name: researcher
|
||||||
|
payload_class: agents.researcher.ResearchPayload
|
||||||
|
handler: agents.researcher.research_handler
|
||||||
|
description: "Primary research agent that reasons and coordinates tools."
|
||||||
|
agent: true # Flags LLM agent → unique root enforced, own_name exposed
|
||||||
|
peers: # Allowed targets this agent may address
|
||||||
|
- calculator.add
|
||||||
|
- web_search
|
||||||
|
|
||||||
|
- name: search.google
|
||||||
|
payload_class: gateways.google.SearchPayload
|
||||||
|
handler: gateways.google.search_handler
|
||||||
|
description: "Google search gateway."
|
||||||
|
broadcast: true # Opt-in: permits sharing root tag with other search.* listeners
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional flags:
|
||||||
|
- `agent: true` → designates an LLM-driven listener (enforces unique root tag, exposes `own_name` in metadata)
|
||||||
|
- `peers:` → list of registered names this listener is allowed to call (enforced by pump for agents)
|
||||||
|
- `broadcast: true` → allows multiple listeners to intentionally share the same derived root tag (used for parallel gateways/retrievers)
|
||||||
|
|
||||||
|
### Python Declaration
|
||||||
|
```python
|
||||||
|
from xmlable import xmlify
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from xml_pipeline import Listener, HandlerMetadata
|
||||||
|
|
||||||
|
@xmlify
|
||||||
|
@dataclass
|
||||||
|
class AddPayload:
|
||||||
|
"""Addition request."""
|
||||||
|
a: int = 0 # Field docstrings become parameter descriptions in prompts
|
||||||
|
b: int = 0
|
||||||
|
|
||||||
|
async def add_handler(
|
||||||
|
payload: AddPayload,
|
||||||
|
metadata: HandlerMetadata
|
||||||
|
) -> bytes:
|
||||||
|
result = payload.a + payload.b
|
||||||
|
return f"<result>{result}</result>".encode("utf-8")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handler Signature (Locked)
|
||||||
|
```python
|
||||||
|
async def handler(
|
||||||
|
payload: PayloadDataclass, # Deserialized, XSD-validated instance
|
||||||
|
metadata: HandlerMetadata # Small, trustworthy context
|
||||||
|
) -> bytes:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### HandlerMetadata (frozen, read-only)
|
||||||
|
```python
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class HandlerMetadata:
|
||||||
|
thread_id: str # Opaque UUID matching <thread/> in envelope
|
||||||
|
from_id: str # Registered name of the sender (pump-injected, trustworthy)
|
||||||
|
own_name: str | None = None # Populated ONLY for listeners with agent: true
|
||||||
|
is_self_call: bool = False # Convenience flag: from_id == own_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical uses:
|
||||||
|
- Stateful tools → key persistent data by `thread_id`
|
||||||
|
- Agents → reason about provenance using `from_id`, optionally refer to themselves via `own_name`
|
||||||
|
|
||||||
|
### Handler Return Requirements
|
||||||
|
The handler **must** return a `bytes` object containing one or more payload root elements.
|
||||||
|
|
||||||
|
Returning `None` or a non-`bytes` value is a programming error.
|
||||||
|
|
||||||
|
The message pump protects the organism by injecting a diagnostic payload if invalid bytes are returned:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if response_bytes is None or not isinstance(response_bytes, bytes):
|
||||||
|
response_bytes = b"<huh>Handler failed to return valid bytes — likely missing return statement or wrong type</huh>"
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures:
|
||||||
|
- The thread never hangs due to a forgotten return
|
||||||
|
- The error is immediately visible in logs and thread history
|
||||||
|
- LLM agents can observe and self-correct
|
||||||
|
|
||||||
|
**Correct examples**
|
||||||
|
```python
|
||||||
|
async def good(... ) -> bytes:
|
||||||
|
return b"<result>42</result>"
|
||||||
|
|
||||||
|
async def also_good(... ) -> bytes:
|
||||||
|
# fast synchronous-style computation
|
||||||
|
return b"<empty/>"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dangerous (triggers <huh> injection)**
|
||||||
|
```python
|
||||||
|
async def bad(... ):
|
||||||
|
computation()
|
||||||
|
# forgot return → implicit None
|
||||||
|
```
|
||||||
|
|
||||||
|
Always explicitly return `bytes`.
|
||||||
|
|
||||||
|
### Multi-Payload Emission & Envelope Injection
|
||||||
|
Handlers return **raw XML fragments only**. The pump performs all enveloping:
|
||||||
|
|
||||||
|
1. Wrap response in `<dummy>` (tolerant of dirty output)
|
||||||
|
2. Extract all root elements found inside
|
||||||
|
3. For each extracted payload:
|
||||||
|
- Inherit current `<thread>`
|
||||||
|
- Inject `<from>` = registered name of the executing listener
|
||||||
|
- Build full `<message>` envelope
|
||||||
|
- Re-inject into the pipeline(s) matching the payload’s derived root tag
|
||||||
|
|
||||||
|
Example emission enabling parallel tool calls + self-continuation:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def researcher_step(... ) -> bytes:
|
||||||
|
return b"""
|
||||||
|
<thought>Need weather and a calculation...</thought>
|
||||||
|
<web_search.searchpayload>
|
||||||
|
<query>current weather Los Angeles</query>
|
||||||
|
</web_search.searchpayload>
|
||||||
|
<calculator.add.addpayload>
|
||||||
|
<a>7</a>
|
||||||
|
<b>35</b>
|
||||||
|
</calculator.add.addpayload>
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
The three payloads are automatically enveloped with correct provenance and routed.
|
||||||
|
|
||||||
|
### Registration Lifecycle
|
||||||
|
At startup or privileged OOB hot-reload:
|
||||||
|
|
||||||
|
1. Import `payload_class` and `handler`
|
||||||
|
2. Derive root tag (`registered_name.dataclass_name`)
|
||||||
|
3. Enforce global uniqueness (unless `broadcast: true`)
|
||||||
|
4. Validate mandatory description
|
||||||
|
5. Generate and cache XSD, example, and prompt fragment
|
||||||
|
6. Instantiate dedicated preprocessing pipeline
|
||||||
|
7. Insert into routing table
|
||||||
|
|
||||||
|
Any failure (duplicate root, missing description, import error) → clear error message and abort/reject.
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
- Use hierarchical registered names (`category.sub.action`) for logical grouping and readable wire tags.
|
||||||
|
- Choose clear, specific dataclass names — they become permanent parts of the wire format.
|
||||||
|
- Always write a concise, accurate description — it leads every auto-generated tool prompt.
|
||||||
|
- For agents, declare only the minimal necessary `peers` — keeps prompts bounded and secure.
|
||||||
|
- Pure tools rarely need a `peers` entry.
|
||||||
|
|
||||||
|
### Summary of Key Invariants
|
||||||
|
- Root tag fully derived, never manually specified
|
||||||
|
- Global uniqueness guaranteed by registered-name prefix
|
||||||
|
- Handlers remain pure except for small trustworthy metadata
|
||||||
|
- All envelope construction and provenance injection performed exclusively by the pump
|
||||||
|
- Zero manual XSDs, examples, or prompt fragments required
|
||||||
|
|
||||||
|
This specification is now locked for AgentServer v2.1. All code, examples, and future documentation must align with this file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Ready for the next piece (message pump implementation notes, OOB channel, stateful listener examples, etc.) whenever you are.
|
||||||
Loading…
Reference in a new issue