π Β π¦ Β π Β π‘
Framework-agnostic Python CLI for turning existing agent code into Dockerized HTTP microservices with a consistent runtime contract.
PyPI Β Β β’Β Β Dank Cloud
Turn existing Python agents into production-ready microservices in two commands, with no agent-code rewrites.
- β‘ no code rewrites required
- π standardized HTTP runtime contract
- π§ͺ repeatable dependency locking + validation
- π‘ built-in observability (status, metrics, logs, traces)
- π¦ optional multi-agent bundling for runtime efficiency
Note
βοΈ If you want managed infrastructure instead of self-hosting containers, use Dank Cloud.
Dank Cloud can onboard directly from your GitHub repo and handles packaging, deploys, scaling, auth, logging/tracing & monitoring, and supporting infra (like hosted vector DB + MCP) for you.
- π οΈ
dank-py: local/self-hosted packaging and runtime control. - π Dank Cloud: managed microservice deployment and operations, built on top of
dank-py.
- Why dank-py
- In 2 Commands
- Architecture
- Install
- Quick Start
- Command Reference
- Configuration Reference (
dank.config.json) - Runtime API Contract
- Logging and Observability
- Environment and Secrets
- Build vs Build:Prod
- Agent Examples
- Troubleshooting
- Release
- π§© Works with existing agent code
- Framework-agnostic support across LangChain, LangGraph, CrewAI, PydanticAI, LlamaIndex, or custom OpenAI/SDK implementations.
- Auto-inspects entrypoints and I/O hints to bootstrap
dank.config.json.
- π‘οΈ Production-ready runtime out of the box
- Consistent endpoints (
/health,/prompt,/status,/metrics,/logs,/traces). - Input/output contract validation and strict output enforcement.
- Native async/sync invocation handling for agent callables and methods.
- Request tracing with trace IDs, per-agent log streams, and trace retrieval endpoints.
- Consistent endpoints (
- π§ͺ Reliable build and dependency flow
- Resolver-based lock generation (
requirements.lock.txt). - Isolated validation in temporary virtual environments before build/run.
- Build and production build workflows for local iteration and release pipelines.
- Resolver-based lock generation (
- π¦ Flexible multi-agent deployment
- Run each agent in its own container, or bundle agents into one container with header-based routing.
- Reduce footprint for compatible agents while keeping per-agent observability.
dank-py auto-init --strict
dank-py runThat flow will:
- inspect your project and generate
dank.config.json, - infer and lock dependencies,
- validate agents in an isolated environment,
- build and run containerized agent service(s).
flowchart LR
A["Existing Python Agent Code"] --> B["dank-py auto-init"]
B --> C["dank.config.json + requirements.lock.txt"]
C --> D["dank-py run / build"]
D --> E["Containerized Agent Microservice(s)"]
E --> F["/prompt /health /status /metrics /logs /traces"]
E --> G["Run locally or self-host (Docker/K8s)"]
- Python
>=3.11(supported host range) - Python
3.12is the target runtime/lock version - Docker CLI + running Docker daemon
pip install dank-pypip install -e /absolute/path/to/dank-pyBoth command names are available:
dankdank-py
# 1) Scaffold config and ignore files
dank-py init
# 2) Inspect project and apply candidates
dank-py inspect
# 3) Resolve dependency lock
dank-py deps
# 4) Build and run
dank-py runOne command setup:
dank-py auto-initStrict setup (includes full isolated validation):
dank-py auto-init --strictQuick version check:
dank-py -v
# or
dank-py --version
# or
dank-py versionWhat it shows:
- installed
dank-pyCLI version - configured default base image reference
- Docker availability status
- installed local
dank-py-baseimage tags (if any)
Flags for dank-py version:
| Flag | What it does |
|---|---|
--json |
Outputs version/base-image details as JSON for tooling/scripts. |
Scaffold dank.config.json and .dankignore.
dank-py init [name] [--force]| Arg/Flag | What it does |
|---|---|
name |
Optional target directory. If omitted, uses current directory. |
--force |
Overwrites existing scaffold files instead of leaving them untouched. |
Runs init + inspect apply + deps in one flow.
dank-py auto-init [name] [flags]| Arg/Flag | What it does |
|---|---|
name |
Optional target directory for scaffolding. |
--force |
Overwrites existing scaffold files. |
--validate-dry |
Runs isolated non-live validation after lock creation. |
--validate-full |
Runs isolated live validation (real env vars and non-mock outputs expected). |
--strict |
Alias for --validate-full. |
--fallback-freeze |
Allows lock fallback via pip freeze when resolver lock cannot be produced. |
--no-discover-imports |
Disables import discovery when no dependency metadata exists. |
--install-tools |
Auto-installs missing resolver tooling (pip-tools) in the active environment. |
--no-install-prompt |
Disables interactive prompts for tool installation. |
--lock-python-version <ver> |
Sets lock target Python version (default 3.12). |
--include-lock-comments |
Keeps resolver metadata comments in requirements.lock.txt. |
--no-refresh-lock |
Reuses existing lock file instead of regenerating it. |
Generates or refreshes requirements.lock.txt, optionally validates agents.
dank-py deps [flags]| Flag | What it does |
|---|---|
--project-dir <dir> |
Uses a specific project directory (defaults to current directory). |
-c, --config <path> |
Path to dank.config.json for validation modes. |
--validate-dry |
Runs isolated non-live validation. |
--validate-full |
Runs isolated live validation. |
--fallback-freeze |
Allows pip freeze fallback when resolver lock fails. |
--no-discover-imports |
Disables import-based dependency discovery. |
--install-tools |
Auto-installs pip-tools if missing. |
--no-install-prompt |
Disables prompt asking to install missing tools. |
--lock-python-version <ver> |
Sets lock target Python version (default 3.12). |
--include-lock-comments |
Keeps resolver metadata comments in lock output. |
--no-refresh-lock |
Reuses existing lock instead of regenerating. |
Scans project files for likely agent entrypoints and model/schema hints.
dank-py inspect [flags]| Flag | What it does |
|---|---|
--project-dir <dir> |
Uses a specific project directory. |
--json |
Prints machine-readable inspect output only (no interactive prompts). |
--interactive |
Explicitly runs interactive apply flow (default behavior when not using --json). |
--apply |
Applies selected candidate(s) directly to config. |
--candidate-index <N> |
Chooses a specific 1-based candidate index when applying. |
-c, --config <path> |
Path to target dank.config.json. |
Builds local target image(s).
dank-py build [selector] [flags]Selectors (mutually exclusive):
| Selector | What it does |
|---|---|
--agent <id-or-name> |
Builds one standalone agent target. |
--bundle <bundle-name> |
Builds one configured bundle target. |
--bundle-agents <csv|all> |
Builds one ad-hoc bundle from agent list or all agents. |
Build flags:
| Flag | What it does |
|---|---|
-c, --config <path> |
Path to dank.config.json. |
--bundle-name <name> |
Names an ad-hoc bundle created with --bundle-agents. |
--prompt-routing required|default |
Overrides bundle prompt routing mode. |
--default-agent <id-or-name> |
Sets default routed agent when using prompt-routing=default. |
--tag <tag> |
Sets output image tag (single target). |
--base-image <image> |
Overrides base image reference. |
--pull-base |
Forces pull of base image before target build. |
--no-base-build |
Skips local base-image build fallback if pull fails. |
--force-base |
Forces base image rebuild/pull path. |
--verbose |
Streams raw Docker build output. |
--json |
Returns structured JSON result. |
Default behavior with no selector:
- no bundles configured: builds all agents separately
- bundles configured: builds configured bundles + unbundled agents separately
Buildx-based production build flow.
dank-py build:prod [selector] [flags]Selector flags are the same as build.
Production flags:
| Flag | What it does |
|---|---|
-c, --config <path> |
Path to dank.config.json. |
--bundle-name <name> |
Names ad-hoc bundle from --bundle-agents. |
--prompt-routing required|default |
Bundle prompt routing override. |
--default-agent <id-or-name> |
Default agent for bundle prompt fallback. |
--tag <tag> |
Image tag (default latest). |
--registry <host> |
Registry host for push targets. |
--namespace <prefix> |
Repository namespace/prefix. |
--tag-by-agent |
Uses agent name as tag with shared namespace repo strategy. |
--platform <platform|auto> |
Buildx platform(s); auto chooses based on push/load mode. |
--push / --no-push |
Enables/disables push output explicitly. |
--load / --no-load |
Enables/disables local Docker load output explicitly. |
--no-cache |
Disables build cache. |
--base-image <image> |
Base image override. |
--pull-base |
Pulls base image before production build. |
--force-base |
Forces base image rebuild/pull path. |
--output-metadata <file> |
Writes build metadata JSON file. |
--verbose |
Streams raw buildx output. |
--json |
Returns structured JSON build result. |
Builds (unless --no-build) and runs selected target container(s).
dank-py run [selector] [flags]Selector flags are the same as build.
Run flags:
| Flag | What it does |
|---|---|
-c, --config <path> |
Path to dank.config.json. |
--bundle-name <name> |
Names ad-hoc bundle from --bundle-agents. |
--prompt-routing required|default |
Bundle prompt routing override. |
--default-agent <id-or-name> |
Default bundle target when prompt header is omitted in default mode. |
--tag <tag> |
Uses specific image tag for build/run. |
--base-image <image> |
Base image override. |
--pull-base |
Pulls base image before building. |
--no-build |
Runs existing image without rebuilding. |
-d, --detached |
Starts container(s) in detached mode. |
--foreground |
Forces foreground attach mode. |
--port <host-port> |
Starting host port mapping (auto-increments to avoid collisions). |
--force-base |
Forces local base rebuild path. |
--keep-build-context |
Leaves generated .dank-py build context on disk for debugging. |
--verbose |
Streams raw Docker build logs. |
--quiet |
Reduces runtime request/startup logging. |
--env-file <path> |
Injects env file at runtime (repeatable). |
-e, --env KEY=VALUE|KEY |
Injects explicit env var (repeatable). |
--no-auto-env-file |
Disables automatic loading of project .env. |
--json |
Returns structured run result. |
Run defaults:
- single target: foreground monitor mode
- multi-target: detached mode
Reads logs from container targets and bundled agent runtime streams.
dank-py logs [target] [flags]| Arg/Flag | What it does |
|---|---|
target |
Container name, bundle name, or agent id/name. |
-f, --follow |
Follows logs live (streaming mode). |
-t, --tail <N> |
Number of lines/events to show (default 100). |
--since <timestamp-or-duration> |
Shows logs only since provided time point/window. |
Routing behavior:
- bundled agent target +
--followuses websocket stream - bundled agent target (non-follow) queries runtime logs endpoint
- container target uses Docker logs
Stops running dank-py containers.
dank-py stop [selector] [flags]| Flag | What it does |
|---|---|
-c, --config <path> |
Path to dank.config.json. |
--agent <id-or-name> |
Stops one standalone agent target. |
--bundle <bundle-name> |
Stops one configured/named bundle container. |
--bundle-agents <csv|all> |
Stops one ad-hoc bundle computed from those agents. |
--bundle-name <name> |
Specifies ad-hoc bundle name when used with --bundle-agents. |
--all |
Stops all running dank-py containers. |
--keep |
Stops containers without removing them. |
If no selector is provided, it stops all running dank-py containers.
Displays running/stopped container status and available agent images.
dank-py status [--json]| Flag | What it does |
|---|---|
--json |
Prints status payload as JSON for tooling. |
Removes generated resources.
dank-py clean [flags]| Flag | What it does |
|---|---|
--project-dir <dir> |
Project path used for .dank-py build-context cleanup. |
--all |
Cleans containers, images, and build contexts. |
--containers |
Cleans only dank-py containers. |
--images |
Cleans only dank-py agent images. |
--build-contexts |
Cleans only .dank-py build context directories. |
--include-base |
Also removes local base image when cleaning images. |
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | null |
no | Project label. |
version |
string |
no | Defaults to "1". |
agents |
AgentConfig[] |
yes | Must contain at least one agent. |
bundles |
BundleConfig[] |
no | Defaults to empty list. |
Validation rules:
- unknown top-level fields are rejected
agent.nameandagent.idmust be unique- missing
agent.idis auto-derived from normalizedagent.name
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Human-readable agent name. |
id |
string | null |
no | Stable routing identity; defaults from name. |
entry |
EntryConfig |
yes | Runtime invocation target. |
io |
IOConfig |
no | Input/output contract definitions. |
| Field | Type | Notes |
|---|---|---|
file |
string |
Python file path. |
symbol |
string |
Exported function/class/object. |
method |
string | null |
Method name for object/class entry styles. |
call_type |
auto | callable | method |
Invocation target resolution strategy. |
call_style |
auto | single_arg | kwargs |
Request payload passing strategy. |
| Field | Type | Notes |
|---|---|---|
input.model |
module:Symbol | null |
Optional typed input model reference. |
input.schema |
object | null |
Optional JSON Schema input contract. |
output.model |
module:Symbol | null |
Optional typed output model reference. |
output.schema |
object | null |
Optional JSON Schema output contract. |
strict_output |
boolean |
Defaults to true. |
Runtime error classes:
- input contract failure:
422 - invocation parameter mismatch:
400 - invocation failure:
500 - strict output contract failure:
500
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Unique bundle identifier. |
agents |
string[] |
yes | Included members by id or name. |
prompt_routing |
required | default |
no | Defaults to required. |
default_agent |
string | null |
no | Only valid with prompt_routing: default. |
Rules:
- bundle names must be unique
- all
agents[]references must exist default_agentmust belong to its bundle
{
"name": "multi-project",
"version": "1",
"agents": [
{ "name": "langchain-agent", "id": "langchain-agent", "entry": { "file": "a.py", "symbol": "run" } },
{ "name": "langgraph-agent", "id": "langgraph-agent", "entry": { "file": "b.py", "symbol": "run" } },
{ "name": "custom-agent", "id": "custom-agent", "entry": { "file": "c.py", "symbol": "run" } }
],
"bundles": [
{
"name": "lang-bundle",
"agents": ["langchain-agent", "langgraph-agent"],
"prompt_routing": "default",
"default_agent": "langchain-agent"
}
]
}Default dank-py run behavior for this config:
- one container for
lang-bundle - one separate container for
custom-agent
All runtime containers expose:
| Endpoint | Purpose |
|---|---|
GET /health |
Container health and runtime mode metadata. |
POST /prompt |
Agent invocation endpoint (single or bundled routing). |
GET /status |
Container/runtime status summary. |
GET /status/{agent_id} |
Per-agent status in multi-agent runtime. |
GET /metrics |
Process/system metrics snapshot. |
GET /logs |
Buffered logs query. |
GET /logs/stats |
Log buffer stats. |
WS /logs/stream |
Live log stream. |
Trace endpoints:
GET /traces(default/only agent traces)GET /traces/stats(default/only agent trace stats)GET /traces/{agent_id}GET /traces/stats/{agent_id}GET /trace/{trace_id}
Query filters:
GET /logs:startTime,endTime,minutesAgo,limit,offset,streamGET /traces,GET /traces/{agent_id}:startTime,endTime,minutesAgo,limit,offsetGET /traces/stats,GET /traces/stats/{agent_id}:startTime,endTime,minutesAgoGET /logs/stats,GET /trace/{trace_id}: no query filters
Pagination defaults:
- trace list endpoints default to
limit=100,offset=0 - use
hasMore=truewithoffsetto fetch additional pages
Per-agent live log stream:
WS /logs/stream/{agent_id}
Header: x-dank-agent-id
prompt_routing=required: header is required.prompt_routing=default: header is optional; runtime routes to default agent.
Single-agent containers:
- header optional
- mismatched header returns
400
x-dank-trace-id- Optional request header.
- If omitted, runtime generates one.
- Always returned in
/promptresponse header andmetadata.trace_id. - If provided and already present in in-memory trace/log history, runtime returns
409.
x-dank-include-trace- Optional request header (
true|1|yes|on). - When enabled,
/promptincludes full grouped trace payload inmetadata.trace.
- Optional request header (
Runtime keeps an in-memory log buffer and supports:
- query endpoints (
/logs,/logs/stats) - websocket streaming (
/logs/stream) - grouped trace retrieval (
/traces*) - bounded retention (
DANK_LOG_BUFFER_MAX_SIZE,DANK_LOG_BUFFER_MAX_AGE_MS,DANK_LOG_BUFFER_MAX_BYTES)
/logs/stats includes both:
bufferSize(entry count)bufferBytes(approx current bytes)
CLI support:
dank-py logsdank-py logs --follow <target>
dank-py run --env-file .env
# or
dank-py run -e OPENAI_API_KEY=... -e OPENAI_MODEL=gpt-4o-miniBy default, run auto-loads project .env when --env-file is not explicitly provided.
Use --no-auto-env-file to disable that behavior.
Use .dankignore to keep secrets out of image build context.
Recommended:
- keep
.envand.env.*ignored - inject secrets at runtime with
--env-file/-e
build- local Docker image build
- best for local development and quick iteration
build:prod- buildx workflow for production tagging/loading/pushing
- suited for CI/CD and registry publish workflows
If you only need local execution, use run (or build + run --no-build).
See agent-examples/ for framework-diverse templates:
01-multi-agent-mixed-repo02-crewai-kickoff-agent03-pydanticai-structured-agent04-llamaindex-query-agent05-langgraph-websearch-agent
docker version
docker infodank-py run/build includes Docker availability checks and remediation prompts.
Error like no match for platform in manifest usually means base image architecture mismatch.
Fixes:
- publish multi-arch base image (
linux/amd64,linux/arm64), or - set explicit compatible
--platform.
python -m pip install pip-tools
# or
dank-py deps --install-toolsUse exact container name from dank-py status.
Release and publish workflow:
MIT