xml-pipeline 0.4.0
Schema-driven XML message bus for multi-agent systems
License: MIT
Requires Python: >=3.11
Keywords: xml, multi-agent, message-bus, llm, pipeline, async

# xml-pipeline

**Schema-driven XML message bus for multi-agent systems.**

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

xml-pipeline is a Python library for building multi-agent systems with validated XML message passing. Agents communicate through typed payloads, validated against auto-generated XSD schemas, with built-in LLM routing and conversation memory.

## Why XML?

JSON was a quick hack that became the default for AI tool calling, where its brittleness causes endless prompt surgery and validation headaches. xml-pipeline chooses XML deliberately:

- **Exact contracts** — XSD validation catches malformed messages before they cause problems
- **Tolerant parsing** — Repair mode recovers from LLM output quirks
- **Self-describing** — Namespaces prevent collision, schemas are discoverable
- **No escaping hell** — Mixed content, nested structures, all handled cleanly

Read the [full rationale](docs/why-not-json.md).

## Installation

```bash
pip install xml-pipeline

# With LLM provider support
pip install xml-pipeline[anthropic]    # Anthropic Claude
pip install xml-pipeline[openai]       # OpenAI GPT

# With all features
pip install xml-pipeline[all]
```

## Quick Start

### 1. Define a payload

```python
from dataclasses import dataclass
from third_party.xmlable import xmlify

@xmlify
@dataclass
class Greeting:
    name: str
```

### 2. Write a handler

```python
from xml_pipeline.message_bus.message_state import HandlerMetadata, HandlerResponse

@xmlify
@dataclass
class GreetingReply:
    message: str

async def handle_greeting(payload: Greeting, metadata: HandlerMetadata) -> HandlerResponse:
    return HandlerResponse(
        payload=GreetingReply(message=f"Hello, {payload.name}!"),
        to="output",
    )
```

### 3. Configure the organism

```yaml
# organism.yaml
organism:
  name: hello-world

listeners:
  - name: greeter
    payload_class: myapp.Greeting
    handler: myapp.handle_greeting
    description: Greets users by name

  - name: output
    payload_class: myapp.GreetingReply
    handler: myapp.print_output
    description: Prints output
```

### 4. Run it

```python
import asyncio
from xml_pipeline.message_bus import bootstrap

async def main():
    pump = await bootstrap("organism.yaml")
    await pump.run()

asyncio.run(main())
```

## Console Example

Try the interactive console example:

```bash
pip install xml-pipeline[console]
python -m examples.console
```

```
> @greeter Alice
[greeter] Hello, Alice! Welcome to xml-pipeline.

> @echo Hello world
[echo] Hello world

> /quit
```

See [examples/console/](examples/console/) for the full source.

## Key Features

### Typed Message Passing

Payloads are Python dataclasses with automatic XSD generation:

```python
@xmlify
@dataclass
class Calculate:
    expression: str
    precision: int = 2
```

The library auto-generates:
- XSD schema for validation
- Example XML for documentation
- Usage instructions for LLM prompts

### LLM Router

Multi-backend LLM support with failover:

```yaml
llm:
  strategy: failover
  backends:
    - provider: anthropic
      api_key_env: ANTHROPIC_API_KEY
    - provider: openai
      api_key_env: OPENAI_API_KEY
```

```python
from xml_pipeline.llm import complete

response = await complete(
    model="claude-sonnet-4",
    messages=[{"role": "user", "content": "Hello!"}],
)
```

### Handler Security

Handlers are sandboxed. They cannot:
- Forge sender identity (injected by pump)
- Escape thread context (managed by registry)
- Route to undeclared peers (validated against config)
- Access other threads (opaque UUIDs)

### Conversation Memory

Thread-scoped context buffer tracks message history:

```python
from xml_pipeline.memory import get_context_buffer

buffer = get_context_buffer()
history = buffer.get_thread(metadata.thread_id)
```

## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                         StreamPump                               │
│  • Parallel pipelines per listener                               │
│  • Repair → C14N → Validate → Deserialize → Route → Dispatch    │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                          Handlers                                │
│  • Receive typed payload + metadata                              │
│  • Return HandlerResponse or None                                │
│  • Cannot forge identity or escape thread                        │
└─────────────────────────────────────────────────────────────────┘
```

See [docs/core-principles-v2.1.md](docs/core-principles-v2.1.md) for the full architecture.

## Documentation

| Document | Description |
|----------|-------------|
| [Core Principles](docs/core-principles-v2.1.md) | Architecture overview |
| [Handler Contract](docs/handler-contract-v2.1.md) | How to write handlers |
| [Message Pump](docs/message-pump-v2.1.md) | Pipeline processing |
| [LLM Router](docs/llm-router-v2.1.md) | Multi-backend LLM support |
| [Configuration](docs/configuration.md) | organism.yaml reference |
| [Why Not JSON?](docs/why-not-json.md) | Design rationale |

## Requirements

- Python 3.11+
- Dependencies: lxml, aiostream, pyyaml, httpx, cryptography

## License

MIT License. See [LICENSE](LICENSE).

---

*XML wins. Safely. Permanently.*