Runaway agents
create_react_agent covers the demo. The moment a model wants one tool call too many, you're debugging a GraphRecursionError in front of a user instead of getting one clean final answer.
Open source · MIT licensed · two independent packages
Calling Gemini, Claude, or GPT is one function call. An agent that calls tools, remembers a conversation, retrieves the right tool out of hundreds, knows what it cost, and never loops forever — that's a week of plumbing every team rebuilds from scratch. VisvoAI is that plumbing: two small, composable Python packages instead of a framework you configure.
pip install visvoai-core "visvoai-ai[gemini]"Same model, same question — the left side is what you write against a provider's SDK directly; the right is the same call throughvisvoai-ai. Nothing hidden: pricing tracked for you instead of hardcoded and going stale.
from google import genai
client = genai.Client(api_key="...")
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Explain attention in one sentence.",
)
# pricing isn't in the SDK -- hardcode it,
# and keep it updated by hand
IN_COST = 0.30 # $ / million input tokens
OUT_COST = 2.50 # $ / million output tokens
u = response.usage_metadata
cost = (
u.prompt_token_count / 1e6 * IN_COST
+ u.candidates_token_count / 1e6 * OUT_COST
)from visvoai.ai import build_chat_model
from visvoai.ai import cost_of, usage_from
model = build_chat_model("gemini:gemini-2.5-flash")
response = model.invoke(
"Explain attention in one sentence."
)
u = usage_from(response)
cost = cost_of(
"gemini:gemini-2.5-flash", u["input"], u["output"]
)Switch to Claude or GPT and the left side is a different SDK, a different response shape, a different pricing lookup, entirely. The right side is the same three lines with a different deployment id — see every supported provider & model.
LangChain and LangGraph give you the graph and the message primitives. They don't give you an opinion on the three things that actually break in production:
create_react_agent covers the demo. The moment a model wants one tool call too many, you're debugging a GraphRecursionError in front of a user instead of getting one clean final answer.
Connect a handful of MCP servers and you're holding hundreds of tools. Binding all of them wrecks tool-choice accuracy and bloats every request — you need retrieval, not a bigger prompt.
Gemini calls it thinking_budget, Claude calls it extended thinking, OpenAI calls it reasoning_effort. Three vocabularies for the same idea, multiplied by every model you support.
VisvoAI files down exactly those edges — a soft step cap, semantic tool retrieval, one normalized thinking-level scale — as two small, MIT-licensed packages you can read end to end, not a platform you configure. Use either alone; visvoai-core takes any LangChain BaseChatModel, it doesn't require visvoai-ai.
Start wherever your problem is — each stands alone.
Gemini, Claude, GPT, and every OpenAI-compatible endpoint (Together AI, Groq, OpenRouter, vLLM, your own gateway) behind one build_chat_model() call — plus a live model registry so your app can choose and meter models, not just call them.
Providers, registry, cost & thinking levels ->visvoai-coreA soft step cap instead of GraphRecursionError, semantic tool retrieval for hundreds of MCP tools, a tool lifecycle with pluggable persistence, and seven extension seams proven by two shipping products.
The loop, tool shapes, seams, subagents ->Nine concepts, each with runnable code from the real examples/ directory in both packages — not truncated snippets.
build_chat_model, deployment identity (provider:model@effort), and how the baked + live models.dev catalog stay current.
cost_of / usage_from, and one normalized ThinkingLevel scale across thinking_budget, extended thinking, and reasoning_effort.
run_search / fetch_url using native provider-side grounding — no second scraping stack to maintain.
AgentRuntime, build_graph, and the soft step cap that forces one clean final answer instead of a recursion crash.
Plain functions, Args:-documented functions, async functions, and the BaseAgentTool lifecycle class — mixed freely.
Seven AgentRuntime hooks for approval gates, custom routing, checkpointers, and state — subclass, never fork.
ToolPersistence / LLMPersistence — every tool call and LLM call lands in your datastore with zero call-site wrapping.
A BM25/hybrid ToolCatalog that binds 8 relevant tools out of 300, re-chosen every agent round.
An agent calling another agent is a ~25-line helper — no special API, no recursion trust exercise.
Every seam documented here is exercised by two real consumers: a full terminal coding agent and a hosted multi-tenant platform, both subclassing the same AgentRuntime — no forks, no private branches of this code.
Tools are plain Python functions — type hints become the schema, the docstring becomes the description the model routes on. No framework imports required to write one.
from visvoai.ai import build_chat_model
from visvoai.core.runtime import AgentRuntime
from visvoai.core import ask
def read_file(path: str) -> str:
"""Read a file and return its contents."""
return open(path).read()
graph = AgentRuntime().build_graph(
model=build_chat_model("gemini:gemini-2.5-flash"),
core_tools=[read_file],
system_prompt="You are a code assistant.",
)
answer = await ask(graph, "What's in pyproject.toml?")pip install visvoai-core "visvoai-ai[gemini]"