Observed since 20 Oct, 22:27 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agentino-framework
Lightweight Python agent framework. Config → agents → run.
A lightweight Python agent framework. YAML config → tool-calling loop → output. No graphs, no DSLs, no DAG editors — just functions you decorate and a runtime that knows how to call them.
pip install agentino-framework
The distribution is agentino-framework because the agentino name on PyPI
belongs to an unrelated project. It imports as agentino regardless:
from agentino import Agent, tool
from agentino import Agent, tool
@tool
async def get_weather(city: str) -> str:
"""Look up current weather for a city."""
return f"It's 22°C in {city}."
agent = Agent(instructions="You're a helpful assistant.", tools=[get_weather])
print(await agent.run("What's the weather in Lisbon?"))
# → "It's 22°C in Lisbon. Want a forecast?"
That's the whole API: define tools as plain async functions, hand them to an Agent, call .run(). The framework handles the LLM round-trip, tool dispatch, retries, and the final-text extraction.
src/agentino/
├── core/ Agent, Runner, LLM, Tool, Message, Context, State, Session
├── config/ YAML loaders for agents, pipelines, tools
├── pipeline/ Pipeline, StagedPipeline (multi-stage flows with verdicts)
├── safety/ GateManager, HookManager, security, sanitizers
├── reliability/ resilience (retry/backoff), compaction, error taxonomy
├── extras/ knowledge (TF-IDF + embeddings), memory, audio, skills
├── providers/ Codex, Anthropic — pluggable LLM backends
├── scheduler/ CronScheduler + JobStore protocol (file/sqlite/in-memory)
├── tools/std/ Built-in tools: files, shell, grep, web search and fetch,
│ weather, document generation (pdf/docx/xlsx/pptx/csv),
│ agent memory. `BUILTIN_TOOLS` is the ten a coding-style
│ agent gets by default; the rest are opt-in.
├── transport/ Outbound channel adapters (Telegram, Slack, WhatsApp, WebSocket)
├── workers/ fork_agent, make_spawn_tool — multi-agent spawning
└── cli/ REPL renderer
Top-level from agentino import … exports the curated public API. Deeper paths
like from agentino.safety.gates import GateManager are how internal packages
talk to each other.
# agents.yml
agents:
reviewer:
model: gpt-5.4-codex
instructions_file: prompts/reviewer.md
tools: [read_file, grep, shell] # auto-discovered from tools/
knowledge:
dir: ./knowledge # TF-IDF + dense embeddings
agentino run agents.yml # one-shot REPL
agentino run agents.yml --agent reviewer # specific agent
agentino run agents.yml --serve 8080 # HTTP server
agentino run agents.yml -m "Review PR #42"
agentino run agents.yml -m "Review PR #42" --mode json # machine-readable (one envelope)
agentino run agents.yml -m "Review PR #42" --mode jsonl # streaming events + final envelope
--mode json|jsonl makes agentino run --message … emit a structured contract
on stdout instead of ANSI-prettified markdown — the same shape pi --print,
codex exec --json, and claude -p --output-format stream-json provide. Lets
non-Python harnesses (IDE extensions, polyglot stacks)
shell out to agentino and parse the result programmatically.
$ agentino run agents.yml -m "List open invoices" --mode json
{"type":"final","text":"…","tools_used":["list_invoices"],
"tool_outputs":["…"],"usage":{"prompt_tokens":1200,"completion_tokens":85},
"model":"gpt-5.4-codex","elapsed_ms":2254}
StagedPipeline runs multi-stage flows where each stage produces a verdict
the next stage can read. A benchmark harness can use it for security check → execute →
report; the security stage rejects unsafe inputs before the execute stage
ever runs.
from agentino import StagedPipeline, StageDef
pipeline = StagedPipeline(stages=[
StageDef(name="security", agent=security_agent, verdict_required=True),
StageDef(name="execute", agent=worker_agent, on_reject="report_threat"),
])
GateManager rejects tool calls whose preconditions haven't been met.
Useful when you want guarantees beyond the LLM following its instructions.
from agentino.safety.gates import GateRule, GateManager
rules = [GateRule(
gate="invoice_listed",
tools=["set_invoice_status"],
message="Run list_invoices first so you've actually seen the IDs.",
)]
When the agent loop encounters set_invoice_status and invoice_listed
isn't marked, the tool returns the rejection message instead of running.
Two flavours of HookManager: Python callbacks
(in-process, fast — for audit logs, history mirroring, metric emission)
and shell commands (subprocess — for ops integrations, external validators).
from agentino.safety.hooks import HookManager
hooks = HookManager()
hooks.register("PostToolUse", matcher={"tool_name": "chat"},
callback=lambda ctx: audit_db.insert(ctx))
from agentino.scheduler import CronScheduler, FileJobStore
scheduler = CronScheduler(store=FileJobStore("data/jobs.json"))
await scheduler.start()
JobStore is a protocol — ship InMemoryJobStore, SqliteJobStore,
FileJobStore, or write your own (e.g. file-as-truth tenant routines, see
runspace's tenant routine store).
Hybrid TF-IDF + dense-embedding retrieval with one tool: search_knowledge.
Drop markdown files in a directory, point an agent at it, the LLM gets a
search tool and reaches into the corpus when it needs to.
agentino run agents.yml --gateway
Maps Slack, Telegram, WhatsApp, and WebSocket transports onto the same agent config. One agent serves users from any channel without changing its code.
export AGENTINO_BASE_URL=https://api.openai.com/v1 # or vLLM, Ollama, OpenRouter…
export AGENTINO_API_KEY=sk-…
The wire protocol is inferred from the URL: Anthropic for an Anthropic
endpoint, Codex for chatgpt.com/backend-api or a /codex path, and plain
OpenAI-compatible /chat/completions for everything else — which is what vLLM,
Ollama, LM Studio, OpenRouter and api.openai.com all speak.
Set AGENTINO_PROVIDER to openai, openai-codex or anthropic to override
the guess. A sk-ant- key implies Anthropic, and a ChatGPT subscription token
implies Codex, whatever the URL says.
Agentino was built around a few opinions other frameworks make hard:
@tool-decorated async def. No
BaseTool.execute() ceremony.Pipeline / StagedPipeline
composed in code. If you can read a function call, you can read your flow.If you've fought a framework's abstractions to get a simple agent working, agentino is the one with the smallest surface that still grows with you.
Concrete recipes for the patterns above:
docs/cookbook/hooks.md — Python callbacks +
shell hooks; PostToolUse audit trails; PreToolUse blockersdocs/architecture/ — design rationale for
the agent loop, tool chain, async-first core, JSONL sessionsdocs/integration-guide.md — wiring agentino
into an existing FastAPI appAgentino powers:
from agentino import … is stable as of v1.0agentino.context moved to agentino.core.contextasyncio.run(agent.run(...)) from sync codeDownload counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
croniterEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | croniter>=2.0 | runtime |
httpxEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | httpx>=0.27 | runtime |
pyyamlEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pyyaml>=6.0 | runtime |
aiogramEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiogram>=3.10; extra == "all" | runtime |
aiohttpEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiohttp>=3.9; extra == "all" | runtime |
beautifulsoup4Environment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | beautifulsoup4>=4.12; extra == "all" | runtime |
fastapiEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | fastapi>=0.109; extra == "all" | runtime |
feedparserEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | feedparser>=6.0; extra == "all" | runtime |
markdownEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | markdown>=3.5; extra == "all" | runtime |
numpyEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.26; extra == "all" | runtime |
openpyxlEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | openpyxl>=3.1; extra == "all" | runtime |
python-docxEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-docx>=1.0; extra == "all" | runtime |
python-pptxEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-pptx>=0.6; extra == "all" | runtime |
reportlabEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | reportlab>=4.0; extra == "all" | runtime |
slack-boltEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | slack-bolt>=1.18; extra == "all" | runtime |
starletteEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | starlette>=0.38; extra == "all" | runtime |
uvicornEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | uvicorn>=0.30; extra == "all" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio>=0.23; extra == "dev" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=8.0; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | ruff>=0.5; extra == "dev" | runtime |
tomliEnvironment{
"extras": "",
"marker": "python_version < \"3.11\" and extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | tomli>=2.0; python_version < "3.11" and extra == "dev" | runtime |
markdownEnvironment{
"extras": "",
"marker": "extra == \"docgen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | markdown>=3.5; extra == "docgen" | runtime |
openpyxlEnvironment{
"extras": "",
"marker": "extra == \"docgen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | openpyxl>=3.1; extra == "docgen" | runtime |
python-docxEnvironment{
"extras": "",
"marker": "extra == \"docgen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-docx>=1.0; extra == "docgen" | runtime |
python-pptxEnvironment{
"extras": "",
"marker": "extra == \"docgen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-pptx>=0.6; extra == "docgen" | runtime |
reportlabEnvironment{
"extras": "",
"marker": "extra == \"docgen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | reportlab>=4.0; extra == "docgen" | runtime |
numpyEnvironment{
"extras": "",
"marker": "extra == \"memory\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.26; extra == "memory" | runtime |
fastapiEnvironment{
"extras": "",
"marker": "extra == \"serve\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | fastapi>=0.109; extra == "serve" | runtime |
starletteEnvironment{
"extras": "",
"marker": "extra == \"serve\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | starlette>=0.38; extra == "serve" | runtime |
uvicornEnvironment{
"extras": "",
"marker": "extra == \"serve\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | uvicorn>=0.30; extra == "serve" | runtime |
aiohttpEnvironment{
"extras": "",
"marker": "extra == \"slack\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiohttp>=3.9; extra == "slack" | runtime |
slack-boltEnvironment{
"extras": "",
"marker": "extra == \"slack\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | slack-bolt>=1.18; extra == "slack" | runtime |
beautifulsoup4Environment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | beautifulsoup4>=4.12; extra == "stdtools" | runtime |
feedparserEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | feedparser>=6.0; extra == "stdtools" | runtime |
markdownEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | markdown>=3.5; extra == "stdtools" | runtime |
numpyEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.26; extra == "stdtools" | runtime |
openpyxlEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | openpyxl>=3.1; extra == "stdtools" | runtime |
python-docxEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-docx>=1.0; extra == "stdtools" | runtime |
python-pptxEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | python-pptx>=0.6; extra == "stdtools" | runtime |
reportlabEnvironment{
"extras": "",
"marker": "extra == \"stdtools\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | reportlab>=4.0; extra == "stdtools" | runtime |
aiogramEnvironment{
"extras": "",
"marker": "extra == \"telegram\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiogram>=3.10; extra == "telegram" | runtime |
aiogramEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiogram>=3.10; extra == "transports" | runtime |
aiohttpEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiohttp>=3.9; extra == "transports" | runtime |
fastapiEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | fastapi>=0.109; extra == "transports" | runtime |
slack-boltEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | slack-bolt>=1.18; extra == "transports" | runtime |
starletteEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | starlette>=0.38; extra == "transports" | runtime |
uvicornEnvironment{
"extras": "",
"marker": "extra == \"transports\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | uvicorn>=0.30; extra == "transports" | runtime |
beautifulsoup4Environment{
"extras": "",
"marker": "extra == \"web\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | beautifulsoup4>=4.12; extra == "web" | runtime |
feedparserEnvironment{
"extras": "",
"marker": "extra == \"web\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | feedparser>=6.0; extra == "web" | runtime |
aiohttpEnvironment{
"extras": "",
"marker": "extra == \"websocket\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | aiohttp>=3.9; extra == "websocket" | runtime |
bdist_wheel · 216.19 KB · Python py3
sdist · 262.25 KB · Python source
Observed since 20 Oct, 22:27 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:7a9e2db4d360662702011c1c8c679bbc2cb7e49addb7f895968d5568b1c386dd