shuvi
A lightweight, extensible async agent loop for building AI companions with Python. shuvi covers tool-using conversational agents, context management, completion verification, callbacks, and multi-agent handoffs.
Features
- Async agent runtime for iterative LLM calls and tool execution.
- Tool calling support through
hydrogen-ai, including text and image results. - Configurable loop controls for max iterations, retries, and completion verification.
- Callback hooks for streaming chunks, assistant messages, tool calls, tool results, errors, and state updates.
Development
Setup
# Clone the repo
git clone https://github.com/waifu-lab/Shuvi
cd Shuvi
# Install all workspace dependencies
uv sync
Example
Basic usage:
import asyncio
import os
from hai.providers.openai import OpenAI
from shuvi.agent import Agent
from shuvi.types import AgentConfig, UserMessage
async def main() -> None:
provider = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
model="gpt-4o-mini",
stream=False,
)
agent = Agent(
AgentConfig(
provider=provider,
system_prompt="You are a helpful AI companion.",
)
)
result = await agent.run(
[
UserMessage(
content=[
{
"type": "text",
"text": "Hello!",
}
]
)
]
)
print(result.messages[-1].content)
asyncio.run(main())
With tools:
import asyncio
import os
from hai.providers.openai import OpenAI
from hai.tool import tool
from shuvi.agent import Agent
from shuvi.types import AgentConfig, LoopConfig, UserMessage
@tool(
description="Add two numbers together",
parameters={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"},
},
"required": ["a", "b"],
},
)
def add(a: float, b: float) -> float:
return a + b
async def main() -> None:
provider = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
model="gpt-4o-mini",
stream=False,
)
agent = Agent(
AgentConfig(
provider=provider,
system_prompt="You are a calculator assistant. Use tools when needed.",
tools=[add],
loop=LoopConfig(max_inner_iterations=10),
)
)
result = await agent.run(
[
UserMessage(
content=[
{
"type": "text",
"text": "Please calculate 123 + 456.",
}
]
)
]
)
print(result.messages[-1].content)
asyncio.run(main())
Streaming mode:
import asyncio
import os
from hai.providers.openai import OpenAI
from shuvi.agent import Agent
from shuvi.types import AgentCallbacks, AgentConfig, UserMessage
def print_chunk(chunk: str) -> None:
print(chunk, end="", flush=True)
async def main() -> None:
provider = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
model="gpt-4o-mini",
stream=True,
)
agent = Agent(
AgentConfig(
provider=provider,
system_prompt="You are a helpful AI companion.",
callbacks=AgentCallbacks(on_chunk=print_chunk),
)
)
await agent.run(
[
UserMessage(
content=[
{
"type": "text",
"text": "Introduce shuvi in three sentences.",
}
]
)
]
)
print()
asyncio.run(main())
Testing
uv run pytest
Metadata
Release files for shuvi 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| shuvi-0.1.3.tar.gz | 9.6 kB | Without provenance | Details for shuvi-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| shuvi-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for shuvi-0.1.3-py3-none-any.whl |
Total release size: 20.1 kB
Release files / shuvi-0.1.3.tar.gz
| Download URL | shuvi-0.1.3.tar.gz |
|---|---|
| Size | 9.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92af2be4d5f658f3606cb6c6883154548426d82d672ffb591d52adfbcedc1ace
|
|
BLAKE2b-256 checksum How to use checksums |
de2db483209640f0545caf226a91832092e8cbca2e179986ff1e944739a525fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.3 CPython/3.13.12 Darwin/25.6.0
|
Release files / shuvi-0.1.3-py3-none-any.whl
| Download URL | shuvi-0.1.3-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1b5993859492e43e2d48c18976e22acacb40363cda3d1f4ddf4a55a5c801356f
|
|
BLAKE2b-256 checksum How to use checksums |
0155424b6c11dd6924952174f0a3dc7cd92a08bf97c6c93932137344dfc530b9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.3 CPython/3.13.12 Darwin/25.6.0
|
Security for shuvi 0.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.