Metadata-Version: 2.4
Name: xml-pipeline
Version: 0.3.1
Summary: Schema-driven XML message bus for multi-agent systems
Author: xml-pipeline contributors
License: MIT
Project-URL: Homepage, https://xml-pipeline.org
Project-URL: Documentation, https://xml-pipeline.org/docs
Project-URL: Repository, https://git.xml-pipeline.org/dullfig/xml-pipeline
Project-URL: Issues, https://git.xml-pipeline.org/dullfig/xml-pipeline/issues
Keywords: xml,multi-agent,message-bus,llm,pipeline,async
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Framework :: AsyncIO
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: lxml>=4.9
Requires-Dist: aiostream>=0.5
Requires-Dist: pyyaml>=6.0
Requires-Dist: pyhumps>=3.0
Requires-Dist: cryptography>=41.0
Requires-Dist: httpx>=0.27
Requires-Dist: termcolor>=2.0
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.39; extra == "anthropic"
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == "openai"
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == "redis"
Provides-Extra: search
Requires-Dist: duckduckgo-search>=6.0; extra == "search"
Provides-Extra: console
Requires-Dist: prompt_toolkit>=3.0; extra == "console"
Provides-Extra: llm
Requires-Dist: xml-pipeline[anthropic,openai]; extra == "llm"
Provides-Extra: tools
Requires-Dist: xml-pipeline[redis,search]; extra == "tools"
Provides-Extra: all
Requires-Dist: xml-pipeline[console,llm,tools]; extra == "all"
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Requires-Dist: pytest-cov>=4.0; extra == "test"
Provides-Extra: dev
Requires-Dist: xml-pipeline[all,test]; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Dynamic: license-file

# 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.*
