Framework-agnostic Python agent containerization CLI for Dank Cloud
Project description
dank-py
Framework-agnostic Python CLI for turning existing agent code into Dockerized HTTP microservices with a consistent runtime contract.
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.
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.
In short:
dank-py: local/self-hosted packaging and runtime control.- Dank Cloud: managed microservice deployment and operations, built on top of
dank-py.
Table of Contents
- 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
Why dank-py
- 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.
In 2 Commands
dank-py auto-init --strict
dank-py run
That 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).
Architecture
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)"]
Install
Requirements
- Python
>=3.11(supported host range) - Python
3.12is the target runtime/lock version - Docker CLI + running Docker daemon
Install from PyPI
pip install dank-py
Editable install (local development)
pip install -e /absolute/path/to/dank-py
Both command names are available:
dankdank-py
Quick Start
# 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 run
One command setup:
dank-py auto-init
Strict setup (includes full isolated validation):
dank-py auto-init --strict
Command Reference
Version
Quick version check:
dank-py -v
# or
dank-py --version
# or
dank-py version
What 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. |
dank-py init
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. |
dank-py auto-init
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. |
dank-py deps
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. |
dank-py inspect
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. |
dank-py build
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
dank-py build:prod
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. |
dank-py run
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
dank-py logs
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
dank-py stop
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.
dank-py status
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. |
dank-py clean
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. |
Configuration Reference (dank.config.json)
Top-level fields
| 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
AgentConfig
| 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. |
EntryConfig
| 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. |
IOConfig
| 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
BundleConfig
| 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
Example: Partial bundle + leftover separate agent
{
"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
Runtime API Contract
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}
Bundle prompt routing
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
Trace headers
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 (
Logging and Observability
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>
Environment and Secrets
Runtime env injection (recommended)
dank-py run --env-file .env
# or
dank-py run -e OPENAI_API_KEY=... -e OPENAI_MODEL=gpt-4o-mini
By default, run auto-loads project .env when --env-file is not explicitly provided.
Use --no-auto-env-file to disable that behavior.
Build context safety
Use .dankignore to keep secrets out of image build context.
Recommended:
- keep
.envand.env.*ignored - inject secrets at runtime with
--env-file/-e
Build vs Build:Prod
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).
Agent Examples
See agent-examples/ for framework-diverse templates:
01-multi-agent-mixed-repo02-crewai-kickoff-agent03-pydanticai-structured-agent04-llamaindex-query-agent05-langgraph-websearch-agent
Troubleshooting
Docker not installed/running
docker version
docker info
dank-py run/build includes Docker availability checks and remediation prompts.
Platform mismatch on build:prod
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.
Missing pip-tools
python -m pip install pip-tools
# or
dank-py deps --install-tools
Ambiguous logs target
Use exact container name from dank-py status.
Release
Release and publish workflow:
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dank_py-1.0.0.tar.gz.
File metadata
- Download URL: dank_py-1.0.0.tar.gz
- Upload date:
- Size: 94.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
008d6cae364342b867e38566db3429d8279a0c1dc4d2beb4bce58da9ff4099a6
|
|
| MD5 |
c520b9b8576560ed81ea3800bc8ac5dc
|
|
| BLAKE2b-256 |
44e91f32127173c10fd02ee5f32870c2ff1b123ad1ab141af6dffbd75d50ed4b
|
File details
Details for the file dank_py-1.0.0-py3-none-any.whl.
File metadata
- Download URL: dank_py-1.0.0-py3-none-any.whl
- Upload date:
- Size: 78.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fe701d5cdf8472f2e29bf44efdce6d2cfe430e3677207e6d0679d0b144dee52
|
|
| MD5 |
77345a44975828f2731ea8c37bd1d1b4
|
|
| BLAKE2b-256 |
9a5dbb7edab74fce14ec5f603b01f676903a7d9691ff8e4d0765367214f151d0
|