A production-grade, type-safe Python Agent framework
Project description
Nonoka
A production-grade, type-safe Python agent framework with deterministic orchestration, conversational execution, and first-class MCP integration.
Features
- Type-safe core — Pydantic-validated schemas throughout; agents, tools, and plans are all strongly typed
- Deterministic orchestration —
Plan+Step+ref()for explicit control flow, not just prompt-and-pray - Conversational execution —
ReActAgent,ReflectiveAgent, andPlanExecutorparadigms out of the box - First-class tools —
@tooldecorator with automatic Pydantic schema generation - Prompt engineering —
@promptdecorator andPromptTemplatefor composable, type-safe prompt construction - MCP ready — built-in MCP (Model Context Protocol) support via
mcp - Resilient execution — structured error taxonomy (
TransientError,LogicError,SafetyError, etc.) with configurableRetryPolicy - Observable hooks —
Hookssystem for tracing, logging, and custom middleware - Multi-backend LLM — powered by
litellm, supporting OpenAI, Anthropic, DeepSeek, and 100+ providers
Installation
pip install nonoka
Or with uv:
uv add nonoka
Quick Start
import asyncio
import nonoka
@nonoka.tool
async def get_weather(city: str) -> str:
"""Get the weather for a city."""
return f"Sunny in {city}!"
# Sync functions are also supported
@nonoka.tool
def get_time() -> str:
"""Get the current time."""
return "It's noon."
async def main():
agent = nonoka.Agent(
name="weather-bot",
tools=[get_weather],
)
result = await agent.run("What's the weather in Tokyo?")
print(result.output)
asyncio.run(main())
Plans & Orchestration
Explicit multi-step workflows with type-safe references:
from nonoka import PlanBuilder, ref
plan = (
PlanBuilder(objective="Research workflow")
.step("research", search_tool, query="Latest AI breakthroughs")
.step("summarize", summarize_tool, content=ref("research"))
.build()
)
executor = nonoka.PlanExecutor(plan=plan)
result = await executor.run("Latest AI breakthroughs")
Prompt Templates
Composable, type-safe prompts:
from nonoka import prompt, PromptTemplate
@prompt
def translate(text: str, target: str = "Chinese") -> str:
"""Translate the following text to {target}:
{text}
"""
# Or programmatically with Jinja2 syntax
tpl = PromptTemplate("Summarize this in {{style}}:\n{{content}}")
output = tpl.render(style="bullet points", content=long_text)
ReAct Agent
agent = nonoka.ReActAgent(tools=[search, calculator])
result = await agent.run("What is 42 * the current temperature in Paris?")
Configuration
Nonoka supports three ways to configure agents: declarative files (YAML/JSON/TOML), fluent builders, and direct code.
Declarative Config (YAML)
Write a nonoka.yaml and load it:
# nonoka.yaml
agents:
weather_assistant:
model: gpt-4o
system_prompt: "You are a weather assistant."
max_turns: 10
tools:
- import: my_tools.weather:get_weather
code_assistant:
model: deepseek-chat
system_prompt: "You are a coding assistant."
runner:
checkpoint: memory
memory: in_memory
defaults:
model: deepseek-chat
max_turns: 10
from nonoka import Config
config = Config.load("nonoka.yaml") # or Config.auto_find()
agent = config.agents["weather_assistant"].build()
runner = config.runner.build()
Single-agent shorthand (no agents: dict needed):
agent:
model: gpt-4o
system_prompt: "You are helpful."
agent = config.agent.build()
Environment Variables in Config
Use ${VAR} or ${VAR:-default} in YAML values:
agent:
model: ${NONOKA_MODEL:-gpt-4o}
system_prompt: ${NONOKA_PROMPT}
Fluent Builder API
from nonoka import AgentBuilder, tool
@tool
async def get_weather(city: str) -> str:
return f"Sunny in {city}!"
agent = (
AgentBuilder()
.model("gpt-4o")
.system_prompt("You are a weather assistant.")
.tool(get_weather)
.tool_by_import("my_tools.search:search_city")
.max_turns(20)
.retry(max_retries=5, backoff=1.5)
.metadata(category="weather")
.tag("production")
.build()
)
From Dict / YAML / JSON
from nonoka import Agent
# From dict
agent = Agent.from_dict({
"model": "gpt-4o",
"tools": ["my_tools:get_weather"],
})
# From file
agent = Agent.from_yaml("agent.yaml")
agent = Agent.from_json("agent.json")
Environment-driven Settings
Nonoka also integrates with pydantic-settings for framework-level config:
from nonoka.core.config import settings
print(settings.default_model) # from NONOKA_DEFAULT_MODEL env var
print(settings.openai_api_key) # from NONOKA_OPENAI_API_KEY env var
Requirements
- Python >= 3.10
License
MIT
Project details
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 nonoka-1.1.2.tar.gz.
File metadata
- Download URL: nonoka-1.1.2.tar.gz
- Upload date:
- Size: 79.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b7bfa0e0a09f90a3929674625eda14a26effe3d9d02e9907f237eb021d095ae
|
|
| MD5 |
ab6acc2ccb09d5fd03e40e11dc3058e4
|
|
| BLAKE2b-256 |
86c6bb008a5bacf32b91d4eb3b5977989c452a3aaeaf7cbb2fffbb98aa8eb8a0
|
File details
Details for the file nonoka-1.1.2-py3-none-any.whl.
File metadata
- Download URL: nonoka-1.1.2-py3-none-any.whl
- Upload date:
- Size: 68.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff6f82518cc49acaad730f06c7232ba0101af3f8a7d6a984160e3e4f577d28c5
|
|
| MD5 |
bcbce26fdac00a0ee06f141a5028f7c8
|
|
| BLAKE2b-256 |
c36bd5a9f32d73a232cf3f6ff87bdeafe5f120c23648abf29a29fefd19137ffd
|