lughus
Micro-framework for building A2A agents with LiteLLM. Register tools, run an agentic loop, get a result. No graphs, no runners, no magic.
Install
pip install lughus # Core (litellm, python-dotenv, jsonschema)
pip install lughus[server] # + FastAPI, uvicorn, a2a-sdk
pip install lughus[all] # Everything
Quick Start
A complete agent in one script. Register a tool, call agent_loop, get the LLM's response:
import asyncio
import json
from lughus import ToolRegistry, agent_loop
from lughus.testing import MockLLM
# 1. Create a tool registry and register a tool
registry = ToolRegistry()
@registry.tool(
"greet",
"Greet a user by name.",
{
"type": "object",
"properties": {
"name": {"type": "string", "description": "Name to greet"},
},
"required": ["name"],
"additionalProperties": False,
},
)
def greet(*, name: str, state) -> str:
return json.dumps({"greeting": f"Hello, {name}!"})
# 2. Create an LLM (MockLLM for offline testing, LLM for production)
llm = MockLLM(
[
# Turn 1: LLM calls the greet tool
[{"name": "greet", "arguments": {"name": "World"}, "id": "call_1"}],
# Turn 2: LLM produces a text response (ends the loop)
"Hello, World!",
]
)
# 3. Run the agent 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"],
state=None,
)
print(result) # "Hello, World!"
print(f"{result.iterations} iterations, {result.total_tokens} tokens")
asyncio.run(main())
For production, swap MockLLM for a real LLM:
from lughus import LLM
llm = LLM(model="openai/gpt-4o", max_output_tokens=16384)
Features
agent_loop()-- iterates LLM + tools until a text response, with parallel tool executionagent_loop_stream()-- same, but yields text chunks as the LLM generates themToolRegistry--@registry.tool()decorator for sync and async Python functionsBaseGateway-- A2AAgentExecutor(message extraction, artifact handling)LLM-- thin wrapper aroundlitellm.acompletion(), supports 100+ providersbuild_app()/serve()-- A2A ASGI app + uvicorn in one call- Governance -- deterministic tool policies, scoped permissions, human-in-the-loop approvals
- Observability -- native OpenTelemetry traces and metrics on every request
Configuration
All configuration is via environment variables. Key settings:
| Variable | Default | Description |
|---|---|---|
AGENT_MODEL |
(required) | LiteLLM model string (e.g. openai/gpt-4o) |
MAX_OUTPUT_TOKENS |
16384 |
Max output tokens per LLM call |
HOST / PORT |
0.0.0.0 / 8080 |
Server listen address |
LUGHUS_ENV |
development |
Set production for strict startup validation |
API_BEARER_TOKEN |
(not set) | Bearer token for non-health routes |
Provider routing is automatic via LiteLLM:
export AGENT_MODEL="openai/gpt-4o" && export OPENAI_API_KEY="sk-..."
export AGENT_MODEL="anthropic/claude-sonnet-4-20250514" && export ANTHROPIC_API_KEY="sk-ant-..."
export AGENT_MODEL="gemini/gemini-2.5-flash" && export GEMINI_API_KEY="..."
Scaffold a New Agent
lughus new my_agent
cd my_agent && pip install -e ".[dev]" && pytest -q
python -m my_agent # starts A2A server on :8080
Governance
Tools can declare risk levels, required scopes, and approval workflows. The policy engine evaluates actions deterministically before execution -- prompt instructions are never used as access controls. See docs/guides/agentic-design.md for agentic design rules.
from lughus import ToolRegistry, ToolRisk, ToolEffect
registry = ToolRegistry()
@registry.tool(
"deploy",
"Deploy to production.",
{"type": "object", "properties": {"service": {"type": "string"}}, "required": ["service"]},
risk=ToolRisk.CRITICAL,
effects=frozenset([ToolEffect.WRITE, ToolEffect.IRREVERSIBLE]),
requires_approval=True,
)
def deploy(*, service: str, state) -> str:
return json.dumps({"status": "deployed"})
Links
License
MIT -- see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lughus-0.15.0.tar.gz.
File metadata
- Download URL: lughus-0.15.0.tar.gz
- Upload date:
- Size: 182.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b79ea65b859f54dcd767e9a9a51f9d8b86d259e373ba8ac2f9bccc6bfb09d90b
|
|
| MD5 |
0d38af3f7c8b7fc2cde00a8e1680d721
|
|
| BLAKE2b-256 |
e7c6fa882b153c3343d93fcbb6716c08e24243932c01debb795c4f00135fa3c5
|
Provenance
The following attestation bundles were made for lughus-0.15.0.tar.gz:
Publisher:
publish.yml on hdg-zero/lughus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lughus-0.15.0.tar.gz -
Subject digest:
b79ea65b859f54dcd767e9a9a51f9d8b86d259e373ba8ac2f9bccc6bfb09d90b - Sigstore transparency entry: 2609279871
- Sigstore integration time:
-
Permalink:
hdg-zero/lughus@70c9db445fd1b4a3a1383b4546b4d5434df2c491 -
Branch / Tag:
refs/tags/v0.15.0 - Owner: https://github.com/hdg-zero
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70c9db445fd1b4a3a1383b4546b4d5434df2c491 -
Trigger Event:
release
-
Statement type:
File details
Details for the file lughus-0.15.0-py3-none-any.whl.
File metadata
- Download URL: lughus-0.15.0-py3-none-any.whl
- Upload date:
- Size: 123.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bcb5ca87150c1e24db7fffb4e793402a7045242bca154da611f992eaac3ec7b2
|
|
| MD5 |
60690d398433f505ef87025d16348dcc
|
|
| BLAKE2b-256 |
3a801042dd8f11fc8f62bb2329ff637ef2842d903b1d01fa693f9ccd5fed77ab
|
Provenance
The following attestation bundles were made for lughus-0.15.0-py3-none-any.whl:
Publisher:
publish.yml on hdg-zero/lughus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lughus-0.15.0-py3-none-any.whl -
Subject digest:
bcb5ca87150c1e24db7fffb4e793402a7045242bca154da611f992eaac3ec7b2 - Sigstore transparency entry: 2609280066
- Sigstore integration time:
-
Permalink:
hdg-zero/lughus@70c9db445fd1b4a3a1383b4546b4d5434df2c491 -
Branch / Tag:
refs/tags/v0.15.0 - Owner: https://github.com/hdg-zero
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@70c9db445fd1b4a3a1383b4546b4d5434df2c491 -
Trigger Event:
release
-
Statement type: