A Python LLM Agent framework powered by litellm. Supports 100+ LLM providers through a unified interface with streaming, tool calling, and event-driven architecture.
Python port of pi-mono's @mariozechner/pi-agent-core, @mariozechner/pi-ai, and @mariozechner/coding-agent packages.
pip install seekingagentOr with uv:
uv add seekingagentAfter installation, the seekingagent command is available globally:
# Interactive mode (REPL)
seekingagent
# Start with a prompt
seekingagent "explain main.py"
# Specify model and working directory
seekingagent -m openai/gpt-4o -C /path/to/project
# Embed file contents with @file syntax
seekingagent "review this" @src/main.py @tests/test_main.py
# Non-interactive (pipe-friendly) mode
seekingagent -p "explain main.py"
echo "fix the bug in utils.py" | seekingagent
# JSON output for scripting
seekingagent -p --output-format json "list all files"| Flag | Description |
|---|---|
-m, --model |
LiteLLM model (default: $SEEKINGAGENT_MODEL or openai/deepseek-v3.2) |
-C, --cwd |
Working directory (default: current dir) |
-p, --print |
Non-interactive mode: run once and exit |
--output-format |
Output format: text or json (print mode) |
--system-prompt |
Custom system prompt (replaces default) |
--append-system-prompt |
Text appended to system prompt |
--api-key |
Explicit API key for LLM calls |
--tools |
Comma-separated tool list |
--no-tools |
Disable all tools |
-c, --continue |
Resume last session |
--no-session |
Disable session persistence |
-v, --verbose |
Verbose output (show thinking, tool details) |
-V, --version |
Show version |
| Command | Description |
|---|---|
/compact |
Compress conversation context |
/model [name] |
Show or switch model |
/new |
Start a new conversation |
/stats |
Show token usage statistics |
/help |
Show available commands |
/quit |
Exit the CLI |
Prefix with ! to run shell commands (e.g., !ls -la). Use @file to embed file contents in your prompt.
Set your API key and base URL before use:
export OPENAI_API_KEY="your-api-key"
export OPENAI_API_BASE="https://your-api-base-url"import asyncio
from seekingagent import (
complete, stream_completion,
Context, UserMessage,
)
async def main():
# Non-streaming
msg = await complete(
"openai/deepseek-v3.2",
Context(messages=[UserMessage(content="Hello!")]),
)
print(msg.content[0].text)
# Streaming
stream = stream_completion(
"openai/deepseek-v3.2",
Context(
messages=[UserMessage(content="Hello!")],
system_prompt="You are helpful.",
),
)
async for event in stream:
if event.type == "text_delta":
print(event.delta, end="", flush=True)
print()
asyncio.run(main())The CodingAgent is a batteries-included coding assistant with 7 built-in tools, session persistence, and context compaction:
import asyncio
from seekingagent import create_coding_agent
async def main():
agent = create_coding_agent(
cwd="/path/to/project",
model="openai/deepseek-v3.2",
)
# Subscribe to streaming events
agent.subscribe(
lambda e: print(e.delta, end="", flush=True)
if e.type == "text_delta" else None
)
# Send prompts - tools are used automatically
await agent.prompt("Read main.py and explain it")
await agent.prompt("Add error handling to the parse function")
# Compress context when it gets too long
result = await agent.compact()
print(f"Compacted {result.tokens_before} tokens")
asyncio.run(main())Built-in tools: read, write, edit, bash, grep, find, ls
Tool sets:
create_coding_tools(cwd)→[read, bash, edit, write](default)create_read_only_tools(cwd)→[read, grep, find, ls]create_all_tools(cwd)→ all 7 tools as a dict
import asyncio
from seekingagent import (
Agent, AgentOptions, AgentTool, AgentToolResult,
TextContent, ThinkingLevel,
)
async def run_calculator(
tool_call_id, args, cancel_event, on_update,
):
result = eval(args["expression"])
return AgentToolResult(
content=[TextContent(text=str(result))],
)
calc_tool = AgentTool(
name="calculate",
description="Evaluate a math expression",
parameters={
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Math expression",
},
},
"required": ["expression"],
},
label="Calculator",
execute=run_calculator,
)
async def main():
agent = Agent(AgentOptions(
initial_state={
"system_prompt": "You are a helpful assistant.",
"model": "openai/deepseek-v3.2",
"tools": [calc_tool],
},
))
events = []
agent.subscribe(lambda e: events.append(e))
await agent.prompt("What is 42 * 17?")
for msg in agent.state.messages:
if msg.role == "assistant":
for block in msg.content:
if block.type == "text":
print(block.text)
asyncio.run(main())Top level: CodingAgent class - Ready-to-use coding assistant with:
- 7 built-in tools (read/write/edit/bash/grep/find/ls)
- JSONL session persistence with branching
- LLM-powered context compaction
- Dynamic system prompt builder
prompt()/continue_()/compact()/reset()
Mid level: Agent class - Stateful, event-driven wrapper with:
prompt()/continue_()for conversationsubscribe()for event callbacksabort()/wait_for_idle()for control flowsteer()/follow_up()for message queuing- State mutators:
set_model(),set_tools(), etc.
Low level: agent_loop - Stateless async loop with:
agent_loop()/agent_loop_continue()returningEventStreamrun_agent_loop()/run_agent_loop_continue()with emit callback
10 agent events covering the full lifecycle:
| Event | When |
|---|---|
agent_start / agent_end |
Agent processing begins/ends |
turn_start / turn_end |
Each LLM call + tool execution cycle |
message_start / message_update / message_end |
Message lifecycle |
tool_execution_start / tool_execution_update / tool_execution_end |
Tool execution lifecycle |
12 streaming events for LLM responses:
start, text_start/delta/end, thinking_start/delta/end, toolcall_start/delta/end, done, error
Tools use JSON Schema for parameter validation:
tool = AgentTool(
name="search",
description="Search the web",
parameters={
"type": "object",
"properties": {
"query": {"type": "string"},
},
"required": ["query"],
},
label="Web Search",
execute=my_search_function,
)Supports sequential and parallel execution modes, plus before_tool_call / after_tool_call lifecycle hooks.
Models use litellm's provider/model-id format:
"openai/deepseek-v3.2"
"openai/gpt-4o"
"anthropic/claude-3-5-sonnet-20241022"
"google/gemini-2.0-flash"
"groq/llama-3.1-70b-versatile"API keys are read from environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) or passed explicitly via StreamOptions.api_key.
| TypeScript (pi-mono) | Python (seekingagent) |
|---|---|
AgentSession class |
CodingAgent class |
SessionManager class |
SessionManager class |
buildSystemPrompt() |
build_system_prompt() |
compact() |
compact() |
Agent class |
Agent class |
agentLoop() |
agent_loop() |
streamSimple() |
stream_completion() |
completeSimple() |
complete() |
AssistantMessageEventStream |
AssistantMessageEventStream |
AgentTool<TSchema> |
AgentTool (JSON Schema dict) |
ThinkingLevel |
ThinkingLevel (StrEnum) |
AbortSignal |
asyncio.Event |
Promise<T> |
Awaitable[T] |
TypeBox TSchema |
dict (JSON Schema) |
uv venv .venv --python=3.11
source .venv/bin/activate
uv sync --dev
# Run tests
uv run pytest
# Lint & format
uv run ruff check src/ tests/
uv run ruff format src/ tests/MIT