sAI

sAI SDK · version 0.2.0 · macOS

sAI's agents.
Inside your app.

Bring your own model. Get a whole team of coding agents — with tools, approvals and a sandbox — in a few lines of Python.

Quick start

The SDK comes with sAI — install and activate it as usual, then:

from sai_sdk import Agent, LLM

agent = Agent(LLM.openai("gpt-4o-mini", api_key="sk-..."),
              folder="~/my-project", allow_edits=True)

for event in agent.run("fix the failing test"):
    print(event)          # every step as it happens; the last one is the Result

result = agent.ask("what does utils.py do?")   # or just the final answer
print(result.answer)

Your model

Use any model with an OpenAI-compatible API. Agents need tool calling to read files, edit and run commands; without it they can still answer questions.

LLM("gpt-4o-mini", api_key=...)                       # OpenAI
LLM("deepseek-chat", base_url="https://api.deepseek.com/v1", api_key=...)
LLM.openrouter("qwen/qwen3-coder", api_key=...)
LLM.groq("openai/gpt-oss-120b", api_key=...)          # free tier
LLM.gemini("gemini-3.5-flash", api_key=...)           # free tier
LLM.ollama("qwen2.5-coder:14b")                       # on your computer, no key
LLM.custom(my_function)                               # anything else

Agent

OptionWhat it does
llmYour model (required).
folderThe project folder. Agents can't reach outside it.
toolsYour own tools — see Your tools.
approveYour function that allows or refuses each file edit and risky command.
allow_editsWithout approve: apply edits automatically. Risky commands are refused.
agentAlways use one agent: Coder, Debugger, Researcher, Reviewer, Writer, Planner, Architect, DevOps.
max_stepsTool steps per request (default 12).
sandboxRun agent commands in the macOS sandbox (default on).

run(prompt) streams events · ask(prompt) returns the Result · reset() starts a new conversation (otherwise it continues) · stop() stops the current run.

Events

EventMeans
NoticeWhich agent took the request.
TextReply text as it's being written.
ToolCallAn agent is using a tool, with its arguments.
ToolResultWhat the tool returned, and whether it worked.
AgentMessageA complete message; final marks the answer.
ResultAlways last: answer, agent, stop_reason, declined, models, tokens, error.

Model and tool failures never crash your program — they arrive as a Result with stop_reason="error".

Approvals

def approve(request):
    print(request.kind, request.description)   # "edit", "command", ...
    print(request.diff)                        # for edits: exactly what changes
    return ask_my_user()                       # True to allow

agent = Agent(llm, folder="~/my-project", approve=approve)

A refused action isn't retried or worked around — the agent explains what it wanted to do, and result.declined lists it.

Your tools

from sai_sdk import tool

@tool
def ticket(ticket_id: str) -> str:
    "Look up a bug ticket by its id."       # what the model sees
    return my_tracker.get(ticket_id)

agent = Agent(llm, folder="~/my-project", tools=[ticket])
agent.ask("Fix the bug described in ticket BUG-142.")

Typed parameters become the tool's arguments; those without defaults are required. Return text. If your function raises, the agent is told and can react.

Good to know