Metadata-Version: 2.4
Name: lughus
Version: 0.18.0
Summary: Micro-framework for building A2A agents with LiteLLM — agent loop, tool registry, A2A gateway.
Author: hdg-zero
License-Expression: MIT
Project-URL: Homepage, https://github.com/hdg-zero/lughus
Project-URL: Repository, https://github.com/hdg-zero/lughus
Project-URL: Issues, https://github.com/hdg-zero/lughus/issues
Project-URL: Changelog, https://github.com/hdg-zero/lughus/blob/main/CHANGELOG.md
Keywords: agent,a2a,llm,litellm,agentic,tool-calling
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: litellm<2.0,>=1.0
Requires-Dist: pydantic>=2.0
Requires-Dist: httpx>=0.25
Requires-Dist: python-dotenv
Requires-Dist: jsonschema>=4.0
Requires-Dist: opentelemetry-api>=1.20
Provides-Extra: server
Requires-Dist: a2a-sdk<1.0,>=0.3.0; extra == "server"
Requires-Dist: fastapi; extra == "server"
Requires-Dist: uvicorn[standard]; extra == "server"
Requires-Dist: starlette; extra == "server"
Requires-Dist: sse-starlette; extra == "server"
Provides-Extra: otel
Requires-Dist: opentelemetry-sdk>=1.20; extra == "otel"
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20; extra == "otel"
Provides-Extra: all
Requires-Dist: lughus[otel,server]; extra == "all"
Provides-Extra: dev
Requires-Dist: lughus[all]; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-mock>=3.12; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="docs/logo.svg" width="360" alt="lughus logo" />
</p>

<p align="center">
  <a href="https://pypi.org/project/lughus/"><img src="https://img.shields.io/pypi/v/lughus.svg?color=blue" alt="PyPI version" /></a>
  <a href="https://pypi.org/project/lughus/"><img src="https://img.shields.io/pypi/pyversions/lughus.svg" alt="Supported Python versions" /></a>
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
</p>

# lughus

Micro-framework for building [A2A](https://google.github.io/A2A/) agents with [LiteLLM](https://github.com/BerriAI/litellm). Register tools, run an agentic loop, get a result. No graphs, no runners, no magic.

## Installation

```bash
pip install lughus              # Core: agent loop, tool registry, LiteLLM
pip install "lughus[server]"    # + FastAPI, uvicorn, A2A gateway & developer console
pip install "lughus[all]"       # Everything (including OpenTelemetry SDK)
```

---

## 🚀 Two Tracks to Get Started

### Track 1: Micro-Agent in 30 Seconds (Standalone Script)

Run an autonomous agent loop directly in a Python script without starting a server:

```python
import asyncio
import json

from lughus import ToolRegistry, agent_loop
from lughus.testing import MockLLM

# 1. Create a tool registry and register tools (automatic schema inference)
registry = ToolRegistry()


@registry.tool
def greet(name: str) -> str:
    """Greet a user by name.

    Args:
        name: Name to greet
    """
    return json.dumps({"greeting": f"Hello, {name}!"})


# 2. MockLLM for offline tests or swap with LLM for production
llm = MockLLM(
    [
        [{"name": "greet", "arguments": {"name": "World"}, "id": "call_1"}],
        "Hello, World!",
    ]
)


# 3. Execute the loop
async def main():
    result = await agent_loop(
        llm,
        system="You are a greeting assistant. Use the greet tool.",
        context="Say hello to World",
        registry=registry,
        tool_names=["greet"],
    )
    print(result)  # "Hello, World!"
    print(f"{result.iterations} iterations, {result.total_tokens} tokens")


asyncio.run(main())
```

For live execution against 100+ LLM providers, swap `MockLLM` for `LLM`:

```python
from lughus import LLM

llm = LLM(model="openai/gpt-4o", max_output_tokens=16384)
```

---

### Track 2: Production A2A Agent (Server & Developer Console)

When your agent needs network transport, streaming, task status, and an interactive UI:

#### 1. Scaffold an agent project

```bash
lughus new my_agent
cd my_agent && pip install -e ".[dev]"
```

#### 2. Start the A2A server

```bash
export AGENT_MODEL="openai/gpt-4o"
export OPENAI_API_KEY="sk-..."
export ENABLE_CONSOLE="true"

python -m my_agent  # Starts ASGI server on http://localhost:8080
```

#### 3. Explore the Developer Console (`/ui`)

Open `http://localhost:8080/ui` in your browser to access the interactive console:

- **Live Streaming & Timeline**: real-time token stream and step-by-step agent trajectory.
- **Rich GFM Markdown & KaTeX**: rendered tables, GitHub alerts (`[!NOTE]`, `[!WARNING]`), and LaTeX math (`$E=mc^2$`).
- **Interactive Human Approvals**: live amber cards with Approve / Reject actions for sensitive tools.
- **Artifact Downloads**: view and download files generated by the agent.

---

## 🛡️ Governance & Deterministic Policy

Tools declare risk levels, required permission scopes, and approval gates. The policy engine evaluates actions before execution — prompt instructions are never used as access controls:

```python
import json

from lughus import ToolEffect, ToolRegistry, ToolRisk

registry = ToolRegistry()


@registry.tool(
    risk=ToolRisk.CRITICAL,
    effects=frozenset([ToolEffect.WRITE, ToolEffect.IRREVERSIBLE]),
    requires_approval=True,  # Suspends the run until human confirms
)
def deploy(service: str) -> str:
    """Deploy a service to production."""
    return json.dumps({"status": "deployed", "service": service})
```

### Sandboxed Python Code Interpreter

Lughus provides an isolated Python code execution tool with automatic output truncation and timeout handling:

```python
from lughus import ToolRegistry, register_code_interpreter

registry = ToolRegistry()
register_code_interpreter(registry, timeout_s=30.0, requires_approval=True)
```

---

## ⚙️ Configuration

All configuration is managed through environment variables loaded automatically via `.env`:

| Variable | Default | Description |
|---|---|---|
| `AGENT_MODEL` | *(required)* | LiteLLM model string (e.g. `openai/gpt-4o`, `anthropic/claude-3-7-sonnet`) |
| `MAX_OUTPUT_TOKENS` | `16384` | Maximum output tokens per LLM completion call |
| `HOST` / `PORT` | `0.0.0.0` / `8080` | Network binding address for A2A server |
| `LUGHUS_ENV` | `development` | Set to `production` for strict startup configuration validation |
| `ENABLE_CONSOLE` | `false` | Enable the developer console UI at `/ui` (development only) |
| `API_BEARER_TOKEN` | *(not set)* | Shared secret bearer token for non-health endpoints |
| `MAX_CONCURRENT_REQUESTS` | `0` (disabled) | Framework-level backpressure limit on active HTTP requests |

---

## 🏛️ Comparison Matrix

| Feature | Core (`agent_loop`) | A2A Server (`BaseGateway` / `serve`) |
|---|---|---|
| **Execution** | In-process Python async coroutine | HTTP JSON-RPC 2.0 / SSE server |
| **Tool Calling** | Parallel with semaphore bulkhead | Parallel with semaphore bulkhead |
| **Streaming** | `agent_loop_stream` generator | A2A server-sent event updates |
| **Developer UI** | Terminal / Logging | Rich web console at `/ui` |
| **Telemetry** | OpenTelemetry spans & counters | OpenTelemetry spans, counters & metrics |
| **Scaffolding** | Single-script import | CLI scaffold via `lughus new` |

---

## 📚 Documentation & Resources

- [5-Minute Quickstart Guide](docs/quickstart.md)
- [Architecture & ADRs](docs/architecture/)
- [Agentic Design Guidelines](docs/guides/agentic-design.md)
- [Contract Stability Policy](docs/contracts/events.md)
- [Framework Guarantees](docs/guarantees.md)
- [CHANGELOG](CHANGELOG.md)
- [CONTRIBUTING](CONTRIBUTING.md)

## License

MIT — see [LICENSE](LICENSE).
