Quickstart: Python
The same pack as the TypeScript quickstart, in Python: two tools, an agent that calls them, a guardrail and a flow.
Before you start, set up what the Install page describes:
Docker, Python 3.11 and uv, and access to the runtime image. No Node: the CLI
comes from PyPI. Sign in at access.kindgi.com
with GitHub for the runtime image's pull credentials, then log in once with
kindgi auth registry.
1. Create the pack
Section titled “1. Create the pack”uvx --from "kindgi-cli>=0.1,<0.2" kindgi init my-pack --template=pythoncd my-packuv sync # a .venv with the kindgi package and the kindgi CLIuv run pytest # the template's tests: the tools and the check, called directlyuvx --from "kindgi-cli>=0.1,<0.2" kindgiruns the newest 0.1 release of the Kindgi CLI from PyPI, the line these docs describe, without installing it (Check this machine says how). The range is optional:uvx --from kindgi-cli kindgi init my-pack --template=pythonruns the newest release, which may be on a later line than these docs.init my-packmakes the pack in a new folder,my-pack.--template=pythonmakes it a Python pack, with examples.
init prints what it made. uv sync installs the pack's dependencies, and
the template's tests pass:
…✓ Python pack scaffolded at …/my-pack/…Installed 26 packages in 206ms…============================== 3 passed in 0.82s ===============================The pack's config is the [tool.kindgi] table of its pyproject.toml; its
tools, guardrails, agents and flows live in four folders:
my-pack/├── pyproject.toml # [tool.kindgi]: the pack's id, version and folders├── tools/echo.py, tools/greet.py # @tool├── guardrails/response_not_empty.py # @guardrail├── agents/echo_agent.py # Agent(...)├── flows/echo_flow.py # Flow(...)├── tests/test_tools.py├── .claude/skills/ # skills for your coding agent└── .agents/skills/ # the same, where Codex and Gemini CLI look2. Run it
Section titled “2. Run it”uv run kindgi devkindgi dev starts the Kindgi runtime in Docker, indexes the pack with the
pack's own Python (.venv/bin/python), runs its tools and checks in a pack
service, and reloads on every save. It writes the API's URL and a token to
.kindgirc.json, so the next commands find the runtime by themselves.
Leave it running.
The first address its banner prints is the console's, where you can see each run: what it was asked, the steps it took and what it answered:
Console http://127.0.0.1:4000/console/ (open in your browser) Sign in: "Sign in as seeded user" on the sign-in page (the dev token, below)To open the console, run uv run kindgi console, or go to the banner's
Console address. Its port is 4000, or the next free one. On the sign-in
page, click Sign in as seeded user.
3. Run the agent and the flow
Section titled “3. Run the agent and the flow”In a second terminal, in my-pack:
uv run kindgi runs start --agent=my-pack.echo-agent --input='{"userMessage":"Ada"}'uv run kindgi runs start --flow=my-pack.echo-flow --input='{"message":"Ada"}'--agent=my-pack.echo-agentand--flow=my-pack.echo-flowname what to run, by its id.--inputis the run's input, as JSON. An agent takes the person's message asuserMessage.
"status": "completed",…⚠ dev-echo answered, and it isn't a real model: it only repeats what it's given. …… "echo": "Ada",Without a model, the agent's answer comes from dev-echo, a stand-in. It
calls the agent's first tool with {"message": <your userMessage>}, and
replies with what the tool returned, after a first line that says it isn't a
real model. The run carries the fallback-provider and
dev-echo-not-a-model warnings. The flow runs the echo tool on its input and returns what the
tool returned. Either way, your Python tool ran: the runtime called it over
HTTP in the pack service.
4. Look at the code
Section titled “4. Look at the code”A tool is a typed Python function. Pydantic models are its input and output, checked on every call:
from datetime import UTC, datetime
from pydantic import BaseModel, Field
from kindgi import tool
class EchoInput(BaseModel): message: str = Field(min_length=1, max_length=500)
class Echo(BaseModel): echo: str echoed_at: str = Field(alias="echoedAt") character_count: int = Field(alias="characterCount", ge=0)
@tool(id="my-pack.echo")def echo(input: EchoInput) -> Echo: """Echoes the caller-provided message with a UTC timestamp and character count.""" return Echo( echo=input.message, echoedAt=datetime.now(UTC).isoformat(), characterCount=len(input.message), )An agent is data, and it refers to the tools themselves, not to strings:
from kindgi import Agent
from ..guardrails.response_not_empty import response_not_emptyfrom ..tools.echo import echofrom ..tools.greet import greet
echo_agent = Agent( id="my-pack.echo-agent", version="0.1.0", name="Echo Agent", description="Uses the pack's echo and greet tools; the response-not-empty guardrail guards the output.", instructions=( "For each user message: if the user sends a name, greet them with the greet tool. " "Otherwise echo their message with the echo tool. Quote the tool result verbatim." ), capabilities=[{"needs": [{"feature": "tool-use"}]}], tools=[echo, greet], guardrails=[response_not_empty], conversation_policy={"historyLimit": 10}, budget={"maxSteps": 4, "maxCostUsd": 0.1, "maxWallMs": 60_000},)uv run python -m kindgi.pack index --pack-dir . prints what Kindgi sees in
the pack. --pack-dir . is the pack's folder, here the current one. A file
with an error is reported with its path, and the rest of the pack
keeps serving.
5. Connect a real model
Section titled “5. Connect a real model”Store your AI model provider's API key as a secret, then register the provider:
uv run kindgi secrets set <KEY_NAME> --env=local --scope=tenantuv run kindgi providers register --preset=<preset><KEY_NAME> and <preset> depend on the provider:
| Provider | <KEY_NAME> |
<preset> |
|---|---|---|
| Anthropic | ANTHROPIC_API_KEY |
anthropic |
| OpenAI | OPENAI_API_KEY |
openai |
| Gemini (a Google AI Studio key) | GEMINI_API_KEY |
gemini-api |
| Groq | GROQ_API_KEY |
groq |
| OpenRouter (a hosted gateway) | OPENROUTER_API_KEY |
openrouter |
secrets set <KEY_NAME>asks for the key without showing it, and writes it to Kindgi's own.kindgi/secrets.env.--env=localis the environment the secret belongs to. Underkindgi dev,localis the pack's env files, that one among them.--scope=tenantis where the secret lives: the tenant, rather than one org (org:<orgId>) or project (project:<projectId>).--preset=<preset>registers the provider's models from Kindgi's built-in preset, which reads the<KEY_NAME>secret.kindgi providers presetslists every preset.
With Anthropic's key, for example, the two commands print:
… Set ANTHROPIC_API_KEY at tenant in local.…✓ Registered anthropic: claude-opus-5-5, claude-sonnet-5-5 (default), claude-haiku-5-5, claude-haiku-4-5 — key ANTHROPIC_API_KEY (env local)The provider takes over from dev-echo at the next turn. Gemini on Vertex AI has a
preset too; any OpenAI-compatible endpoint registers from a short spec file.
The registration is in this project's dev database. To have kindgi dev
register the model on every boot, in each worktree and after --reset,
declare it in pyproject.toml: Declare them in your pack's config.
- Add Kindgi to an existing Python app: your app's own modules as tools.
- Build a support desk in Python.
- Guides: one task at a time.
- Concepts: packs, runs and the journal, security.
- The Python SDK reference.
- Set up your coding agent: it already has Kindgi's skills.