promptdock 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- promptdock-0.1.0/.dockerignore +11 -0
- promptdock-0.1.0/.env.example +1 -0
- promptdock-0.1.0/.gitattributes +1 -0
- promptdock-0.1.0/.github/workflows/ci.yml +56 -0
- promptdock-0.1.0/.github/workflows/release.yml +114 -0
- promptdock-0.1.0/.gitignore +16 -0
- promptdock-0.1.0/.pre-commit-config.yaml +15 -0
- promptdock-0.1.0/CHANGELOG.md +55 -0
- promptdock-0.1.0/CONTRIBUTING.md +34 -0
- promptdock-0.1.0/Dockerfile +34 -0
- promptdock-0.1.0/LICENSE +21 -0
- promptdock-0.1.0/PKG-INFO +226 -0
- promptdock-0.1.0/README.md +179 -0
- promptdock-0.1.0/alembic.ini +6 -0
- promptdock-0.1.0/docker-compose.yml +34 -0
- promptdock-0.1.0/examples/anthropic_app.py +58 -0
- promptdock-0.1.0/examples/app.py +37 -0
- promptdock-0.1.0/examples/core_flow.py +96 -0
- promptdock-0.1.0/examples/langchain_app.py +41 -0
- promptdock-0.1.0/examples/openai_app.py +36 -0
- promptdock-0.1.0/examples/push_evals_llm_judge.py +102 -0
- promptdock-0.1.0/pyproject.toml +84 -0
- promptdock-0.1.0/src/promptdock/__init__.py +29 -0
- promptdock-0.1.0/src/promptdock/actor.py +11 -0
- promptdock-0.1.0/src/promptdock/backends/__init__.py +5 -0
- promptdock-0.1.0/src/promptdock/backends/base.py +75 -0
- promptdock-0.1.0/src/promptdock/backends/db.py +117 -0
- promptdock-0.1.0/src/promptdock/backends/http.py +244 -0
- promptdock-0.1.0/src/promptdock/cache.py +111 -0
- promptdock-0.1.0/src/promptdock/cli.py +633 -0
- promptdock-0.1.0/src/promptdock/client.py +283 -0
- promptdock-0.1.0/src/promptdock/config.py +41 -0
- promptdock-0.1.0/src/promptdock/connect.py +27 -0
- promptdock-0.1.0/src/promptdock/core/__init__.py +1 -0
- promptdock-0.1.0/src/promptdock/core/compare.py +138 -0
- promptdock-0.1.0/src/promptdock/core/gates.py +40 -0
- promptdock-0.1.0/src/promptdock/core/service.py +562 -0
- promptdock-0.1.0/src/promptdock/db/__init__.py +1 -0
- promptdock-0.1.0/src/promptdock/db/engine.py +23 -0
- promptdock-0.1.0/src/promptdock/db/migrate.py +49 -0
- promptdock-0.1.0/src/promptdock/db/migrations/env.py +40 -0
- promptdock-0.1.0/src/promptdock/db/migrations/script.py.mako +25 -0
- promptdock-0.1.0/src/promptdock/db/migrations/versions/0001_initial.py +184 -0
- promptdock-0.1.0/src/promptdock/db/migrations/versions/0002_drop_tag_is_protected.py +27 -0
- promptdock-0.1.0/src/promptdock/db/migrations/versions/0003_readable_views.py +91 -0
- promptdock-0.1.0/src/promptdock/db/migrations/versions/0004_drop_api_keys.py +36 -0
- promptdock-0.1.0/src/promptdock/db/tables.py +150 -0
- promptdock-0.1.0/src/promptdock/errors.py +87 -0
- promptdock-0.1.0/src/promptdock/mcp/__init__.py +1 -0
- promptdock-0.1.0/src/promptdock/mcp/server.py +116 -0
- promptdock-0.1.0/src/promptdock/models.py +234 -0
- promptdock-0.1.0/src/promptdock/prompt.py +74 -0
- promptdock-0.1.0/src/promptdock/py.typed +0 -0
- promptdock-0.1.0/src/promptdock/server/__init__.py +5 -0
- promptdock-0.1.0/src/promptdock/server/access.py +57 -0
- promptdock-0.1.0/src/promptdock/server/app.py +72 -0
- promptdock-0.1.0/src/promptdock/server/errors.py +55 -0
- promptdock-0.1.0/src/promptdock/server/routes.py +225 -0
- promptdock-0.1.0/src/promptdock/sync.py +222 -0
- promptdock-0.1.0/src/promptdock/templates.py +64 -0
- promptdock-0.1.0/src/promptdock/ui/__init__.py +13 -0
- promptdock-0.1.0/src/promptdock/ui/static/assets/index-B7bJCH_W.css +1 -0
- promptdock-0.1.0/src/promptdock/ui/static/assets/index-BbnhHlWV.js +109 -0
- promptdock-0.1.0/src/promptdock/ui/static/index.html +17 -0
- promptdock-0.1.0/tests/conftest.py +97 -0
- promptdock-0.1.0/tests/test_cache.py +94 -0
- promptdock-0.1.0/tests/test_cli.py +260 -0
- promptdock-0.1.0/tests/test_client.py +218 -0
- promptdock-0.1.0/tests/test_compare.py +165 -0
- promptdock-0.1.0/tests/test_config.py +50 -0
- promptdock-0.1.0/tests/test_gates.py +49 -0
- promptdock-0.1.0/tests/test_mcp.py +141 -0
- promptdock-0.1.0/tests/test_migrations.py +157 -0
- promptdock-0.1.0/tests/test_models.py +114 -0
- promptdock-0.1.0/tests/test_repo.py +19 -0
- promptdock-0.1.0/tests/test_server.py +175 -0
- promptdock-0.1.0/tests/test_service.py +407 -0
- promptdock-0.1.0/tests/test_smoke.py +14 -0
- promptdock-0.1.0/tests/test_templates.py +47 -0
- promptdock-0.1.0/tests/test_ui.py +59 -0
- promptdock-0.1.0/tests/test_views.py +79 -0
- promptdock-0.1.0/ui/index.html +16 -0
- promptdock-0.1.0/ui/package-lock.json +2134 -0
- promptdock-0.1.0/ui/package.json +19 -0
- promptdock-0.1.0/ui/src/App.jsx +67 -0
- promptdock-0.1.0/ui/src/api.js +59 -0
- promptdock-0.1.0/ui/src/components/DiffPanel.jsx +96 -0
- promptdock-0.1.0/ui/src/components/HistoryPanel.jsx +61 -0
- promptdock-0.1.0/ui/src/components/LeaderboardPanel.jsx +153 -0
- promptdock-0.1.0/ui/src/components/PromptDetail.jsx +60 -0
- promptdock-0.1.0/ui/src/components/PromptList.jsx +64 -0
- promptdock-0.1.0/ui/src/components/Settings.jsx +58 -0
- promptdock-0.1.0/ui/src/components/TagsPanel.jsx +161 -0
- promptdock-0.1.0/ui/src/components/VersionsPanel.jsx +54 -0
- promptdock-0.1.0/ui/src/components/common.jsx +37 -0
- promptdock-0.1.0/ui/src/main.jsx +10 -0
- promptdock-0.1.0/ui/src/styles.css +138 -0
- promptdock-0.1.0/ui/src/useApi.js +29 -0
- promptdock-0.1.0/ui/vite.config.js +19 -0
- promptdock-0.1.0/uv.lock +3273 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DATABASE_URL=postgresql+psycopg://promptdock:promptdock@localhost:5432/promptdock
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_call: # reused by release.yml
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- run: uv sync
|
|
16
|
+
- run: uv run ruff check .
|
|
17
|
+
- run: uv run ruff format --check .
|
|
18
|
+
- run: uv run mypy
|
|
19
|
+
- run: uv run mypy tests
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
name: test (Python ${{ matrix.python-version }}, Postgres ${{ matrix.postgres }})
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
28
|
+
postgres: ["14", "17"]
|
|
29
|
+
env:
|
|
30
|
+
PROMPTDOCK_TEST_POSTGRES_IMAGE: postgres:${{ matrix.postgres }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: astral-sh/setup-uv@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
- run: uv sync
|
|
37
|
+
- run: uv run pytest -v
|
|
38
|
+
|
|
39
|
+
ui:
|
|
40
|
+
name: dashboard build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: actions/setup-node@v4
|
|
45
|
+
with:
|
|
46
|
+
node-version: "22"
|
|
47
|
+
cache: npm
|
|
48
|
+
cache-dependency-path: ui/package-lock.json
|
|
49
|
+
- run: npm ci
|
|
50
|
+
working-directory: ui
|
|
51
|
+
- run: npm run build
|
|
52
|
+
working-directory: ui
|
|
53
|
+
- uses: astral-sh/setup-uv@v5
|
|
54
|
+
- run: uv sync
|
|
55
|
+
# Serves the real build through `serve --ui` (skipped in the other jobs).
|
|
56
|
+
- run: uv run pytest -v tests/test_ui.py
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Push a tag like v0.1.0 to publish that version everywhere:
|
|
4
|
+
# tests -> build UI -> uv build -> PyPI (Trusted Publishing) -> GHCR image -> GitHub Release
|
|
5
|
+
# No tokens live on anyone's laptop: PyPI trusts this workflow, GHCR uses GITHUB_TOKEN.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
uses: ./.github/workflows/ci.yml
|
|
17
|
+
|
|
18
|
+
build:
|
|
19
|
+
needs: test
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
outputs:
|
|
22
|
+
version: ${{ steps.version.outputs.version }}
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- name: Check the tag matches pyproject.toml
|
|
26
|
+
id: version
|
|
27
|
+
run: |
|
|
28
|
+
version=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
29
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
30
|
+
echo "Tag $GITHUB_REF_NAME does not match pyproject.toml version $version" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
34
|
+
- uses: actions/setup-node@v4
|
|
35
|
+
with:
|
|
36
|
+
node-version: "22"
|
|
37
|
+
cache: npm
|
|
38
|
+
cache-dependency-path: ui/package-lock.json
|
|
39
|
+
- run: npm ci && npm run build
|
|
40
|
+
working-directory: ui
|
|
41
|
+
- uses: astral-sh/setup-uv@v5
|
|
42
|
+
- run: uv build
|
|
43
|
+
- name: Check the dashboard is inside the wheel
|
|
44
|
+
run: unzip -l dist/*.whl | grep -q 'promptdock/ui/static/index.html'
|
|
45
|
+
- uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
|
|
50
|
+
pypi:
|
|
51
|
+
needs: build
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: pypi
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write # Trusted Publishing
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
62
|
+
|
|
63
|
+
docker:
|
|
64
|
+
needs: build
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
permissions:
|
|
67
|
+
contents: read
|
|
68
|
+
packages: write
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
- uses: docker/setup-qemu-action@v3
|
|
72
|
+
- uses: docker/setup-buildx-action@v3
|
|
73
|
+
- uses: docker/login-action@v3
|
|
74
|
+
with:
|
|
75
|
+
registry: ghcr.io
|
|
76
|
+
username: ${{ github.actor }}
|
|
77
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
78
|
+
- id: meta
|
|
79
|
+
uses: docker/metadata-action@v5
|
|
80
|
+
with:
|
|
81
|
+
images: ghcr.io/${{ github.repository_owner }}/promptdock
|
|
82
|
+
tags: |
|
|
83
|
+
type=raw,value=${{ needs.build.outputs.version }}
|
|
84
|
+
type=raw,value=latest
|
|
85
|
+
- uses: docker/build-push-action@v6
|
|
86
|
+
with:
|
|
87
|
+
context: .
|
|
88
|
+
platforms: linux/amd64,linux/arm64
|
|
89
|
+
push: true
|
|
90
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
91
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
92
|
+
|
|
93
|
+
github-release:
|
|
94
|
+
needs: [build, pypi, docker]
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
permissions:
|
|
97
|
+
contents: write
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
- uses: actions/download-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: dist
|
|
103
|
+
path: dist/
|
|
104
|
+
- name: Release notes from CHANGELOG.md
|
|
105
|
+
run: |
|
|
106
|
+
awk -v heading="## [${{ needs.build.outputs.version }}]" '
|
|
107
|
+
index($0, heading) == 1 { found = 1; next }
|
|
108
|
+
found && (index($0, "## [") == 1 || index($0, "[") == 1) { exit }
|
|
109
|
+
found { print }
|
|
110
|
+
' CHANGELOG.md > notes.md
|
|
111
|
+
test -s notes.md || echo "See CHANGELOG.md" > notes.md
|
|
112
|
+
- run: gh release create "$GITHUB_REF_NAME" dist/* --title "PromptDock $GITHUB_REF_NAME" --notes-file notes.md
|
|
113
|
+
env:
|
|
114
|
+
GH_TOKEN: ${{ github.token }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: local
|
|
9
|
+
hooks:
|
|
10
|
+
- id: mypy
|
|
11
|
+
name: mypy
|
|
12
|
+
entry: uv run mypy
|
|
13
|
+
language: system
|
|
14
|
+
types: [python]
|
|
15
|
+
pass_filenames: false
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
|
+
project uses [semantic versioning](https://semver.org/) (staying on 0.x until the API is
|
|
6
|
+
stable).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-11-01
|
|
11
|
+
|
|
12
|
+
First release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Versions and tags.** Immutable, numbered prompt versions (text or chat messages, Jinja2
|
|
17
|
+
variables detected, identical content deduplicated) and tags (`dev`, `preprod`, `prod`
|
|
18
|
+
by default, plus custom ones) that point at them. Promote moves a tag; rollback undoes
|
|
19
|
+
the last promote, again and again like undo. Every move is recorded in an audit log with
|
|
20
|
+
from/to versions, a self-reported actor and an optional reason.
|
|
21
|
+
- **Eval results and gates.** Submit results in a fixed schema (`prompt`, `version`,
|
|
22
|
+
`metric`, `score`, `run_id`, `evaluator`, `metadata`). A gate on a tag requires the latest
|
|
23
|
+
score per metric to meet `min` (higher is better) / `max` (lower is better) rules and,
|
|
24
|
+
optionally, be at least as good as the current version. `force` with a reason bypasses
|
|
25
|
+
a failing gate and is recorded. Rollback never runs the gate.
|
|
26
|
+
- **Leaderboard and diff.** Versions x metrics with the latest score, sortable by any
|
|
27
|
+
column; text diff of two versions plus `model_config` and variable changes. Promote
|
|
28
|
+
warns when a version needs new template variables (`strict` refuses instead).
|
|
29
|
+
- **Client** (`pip install promptdock`). One API in library mode (`Client(db_url=...)`,
|
|
30
|
+
straight to Postgres) and server mode (`Client(base_url=..., token=...)`): `get_prompt`
|
|
31
|
+
returns a `Prompt` with `.render(**values)`, `.version`, `.model_config`. Sandboxed
|
|
32
|
+
rendering with `MissingVariableError`, 60 s cache, last-known-good fallback when the
|
|
33
|
+
database or server is down, optional snapshot file for cold starts.
|
|
34
|
+
- **REST server** (`pip install promptdock[server]`, `promptdock serve`). OpenAPI docs at
|
|
35
|
+
`/docs`, ETag/304 on the prompt fetch. No user accounts: listens on 127.0.0.1 by default,
|
|
36
|
+
optional shared `PROMPTDOCK_TOKEN`, warning when exposed without one; actor via the
|
|
37
|
+
`X-Actor` header.
|
|
38
|
+
- **Dashboard** (`pip install promptdock[ui]`, `promptdock serve --ui`). Prompts, versions,
|
|
39
|
+
tags with promote (gate result shown) and rollback (confirm), leaderboard with a
|
|
40
|
+
score-over-versions chart per metric, diff, audit log.
|
|
41
|
+
- **CLI.** `migrate`, `serve`, `prompts list`, `versions list|show`, `promote`, `rollback`,
|
|
42
|
+
`history`, `evals push`, `export` / `push` (YAML files; never overwrites, never moves
|
|
43
|
+
`prod`), `gate set|show|clear`, `leaderboard`, `diff`, `mcp`.
|
|
44
|
+
- **MCP server** (`pip install promptdock[mcp]`, `promptdock mcp`). Tools for agents:
|
|
45
|
+
`list_prompts`, `get_prompt`, `create_version`, `compare_versions`, `get_leaderboard`,
|
|
46
|
+
and `promote` only with `--allow-promote` (never forced); actor `mcp:<OS username>`.
|
|
47
|
+
- **Docker image** `ghcr.io/shreyamhetre/promptdock` (migrates, then serves API and
|
|
48
|
+
dashboard) and `docker compose --profile server up` for local use.
|
|
49
|
+
- Read-only database views with names instead of ids: `v_prompt_status`, `v_versions`,
|
|
50
|
+
`v_tags`, `v_tag_history`, `v_eval_results`.
|
|
51
|
+
- Examples for OpenAI, Anthropic and LangChain, and an LLM-as-judge eval script.
|
|
52
|
+
- No telemetry or usage tracking.
|
|
53
|
+
|
|
54
|
+
[Unreleased]: https://github.com/shreyamhetre/PromptDock/compare/v0.1.0...HEAD
|
|
55
|
+
[0.1.0]: https://github.com/shreyamhetre/PromptDock/releases/tag/v0.1.0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
1. Install [uv](https://docs.astral.sh/uv/) and Docker.
|
|
6
|
+
2. Install dependencies: `uv sync`
|
|
7
|
+
3. Install git hooks: `uv run pre-commit install`
|
|
8
|
+
4. Start a local Postgres: `docker compose up -d`
|
|
9
|
+
5. Copy `.env.example` to `.env` (or export `DATABASE_URL`).
|
|
10
|
+
|
|
11
|
+
## Checks
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv run ruff check . # lint
|
|
15
|
+
uv run ruff format . # format
|
|
16
|
+
uv run mypy # type check
|
|
17
|
+
uv run pytest # tests (DB tests start Postgres via testcontainers)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Tests use Postgres 16 by default. CI runs Python 3.10–3.13 against Postgres 14 and 17;
|
|
21
|
+
to try another version locally: `PROMPTDOCK_TEST_POSTGRES_IMAGE=postgres:14 uv run pytest`.
|
|
22
|
+
|
|
23
|
+
## Dashboard
|
|
24
|
+
|
|
25
|
+
The UI source lives in `ui/` (React + Vite + Recharts). Build it into the Python package
|
|
26
|
+
with `cd ui && npm ci && npm run build` (output: `src/promptdock/ui/static/`, git-ignored
|
|
27
|
+
but included in the wheel). For live reload, run `promptdock serve` and `npm run dev`
|
|
28
|
+
(it proxies API calls to port 8000).
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
- `main` is protected: open a pull request, get one review, keep CI green.
|
|
33
|
+
- Track work as GitHub Issues with a milestone per phase.
|
|
34
|
+
- Keep business logic in `src/promptdock/core/`; backends and server routes only call into it.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# PromptDock server + dashboard.
|
|
2
|
+
# docker build -t promptdock .
|
|
3
|
+
# docker run -p 8000:8000 -e DATABASE_URL=postgresql://... -e PROMPTDOCK_TOKEN=... promptdock
|
|
4
|
+
# Configure with DATABASE_URL (required) and PROMPTDOCK_TOKEN (optional, recommended: the
|
|
5
|
+
# container listens on 0.0.0.0, so without a token anyone who can reach it has access).
|
|
6
|
+
|
|
7
|
+
# 1. Build the dashboard (Node is only needed here, never at runtime).
|
|
8
|
+
FROM node:22-slim AS ui
|
|
9
|
+
WORKDIR /build/ui
|
|
10
|
+
COPY ui/package.json ui/package-lock.json ./
|
|
11
|
+
RUN npm ci --no-audit --no-fund
|
|
12
|
+
COPY ui/ ./
|
|
13
|
+
RUN npm run build
|
|
14
|
+
|
|
15
|
+
# 2. Build the wheel with the dashboard inside it.
|
|
16
|
+
FROM python:3.12-slim AS wheel
|
|
17
|
+
COPY --from=ghcr.io/astral-sh/uv:0.13 /uv /bin/uv
|
|
18
|
+
WORKDIR /build
|
|
19
|
+
COPY pyproject.toml README.md LICENSE ./
|
|
20
|
+
COPY src ./src
|
|
21
|
+
COPY --from=ui /build/src/promptdock/ui/static ./src/promptdock/ui/static
|
|
22
|
+
RUN uv build --wheel --out-dir /dist
|
|
23
|
+
|
|
24
|
+
# 3. Runtime: just the installed package.
|
|
25
|
+
FROM python:3.12-slim
|
|
26
|
+
RUN useradd --create-home --uid 10001 promptdock
|
|
27
|
+
COPY --from=wheel /dist /tmp/dist
|
|
28
|
+
RUN pip install --no-cache-dir --timeout 120 --retries 5 "$(ls /tmp/dist/*.whl)[server,ui]" \
|
|
29
|
+
&& rm -rf /tmp/dist
|
|
30
|
+
USER promptdock
|
|
31
|
+
EXPOSE 8000
|
|
32
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s \
|
|
33
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
|
|
34
|
+
CMD ["sh", "-c", "promptdock migrate && exec promptdock serve --ui --host 0.0.0.0 --port 8000"]
|
promptdock-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shreya Mhetre and Parth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: promptdock
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Focused prompt versioning: immutable versions, movable tags, eval-gated promotion.
|
|
5
|
+
Project-URL: Homepage, https://github.com/shreyamhetre/PromptDock
|
|
6
|
+
Project-URL: Repository, https://github.com/shreyamhetre/PromptDock
|
|
7
|
+
Project-URL: Issues, https://github.com/shreyamhetre/PromptDock/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/shreyamhetre/PromptDock/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Shreya Mhetre, Parth
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: alembic>=1.13
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: jinja2>=3.1
|
|
19
|
+
Requires-Dist: psycopg[binary]>=3.1
|
|
20
|
+
Requires-Dist: pydantic>=2.6
|
|
21
|
+
Requires-Dist: pyyaml>=6
|
|
22
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
23
|
+
Requires-Dist: typer>=0.12
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
26
|
+
Requires-Dist: mcp<3,>=2.3; extra == 'all'
|
|
27
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
30
|
+
Requires-Dist: mcp<3,>=2.3; extra == 'dev'
|
|
31
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
32
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
35
|
+
Requires-Dist: testcontainers[postgres]>=4; extra == 'dev'
|
|
36
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
37
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'dev'
|
|
38
|
+
Provides-Extra: mcp
|
|
39
|
+
Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
|
|
40
|
+
Provides-Extra: server
|
|
41
|
+
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
42
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'server'
|
|
43
|
+
Provides-Extra: ui
|
|
44
|
+
Requires-Dist: fastapi>=0.110; extra == 'ui'
|
|
45
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'ui'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# PromptDock
|
|
49
|
+
|
|
50
|
+
Focused prompt versioning. PromptDock stores **immutable prompt versions**, points **tags**
|
|
51
|
+
(`dev`, `preprod`, `prod`, or your own) at them, and attaches **eval results** so a tag can only
|
|
52
|
+
move to a version that passes its gate. Promote and rollback move a pointer, never your code.
|
|
53
|
+
|
|
54
|
+
> Status: 0.1 — early. The API may still change before 1.0.
|
|
55
|
+
|
|
56
|
+
PromptDock collects **no telemetry or usage data**. It only talks to your own Postgres and,
|
|
57
|
+
if you run one, your own PromptDock server.
|
|
58
|
+
|
|
59
|
+
## Ways to use it
|
|
60
|
+
|
|
61
|
+
| Mode | Install | Run |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| Library | `pip install promptdock` | import and use |
|
|
64
|
+
| Server | `pip install promptdock[server]` | `promptdock serve` |
|
|
65
|
+
| Server + UI | `pip install promptdock[ui]` | `promptdock serve --ui` |
|
|
66
|
+
| Docker | `docker run ghcr.io/shreyamhetre/promptdock` | — |
|
|
67
|
+
|
|
68
|
+
All modes use Postgres (14 or newer), configured with `DATABASE_URL`. Python 3.10+.
|
|
69
|
+
|
|
70
|
+
The idea in one table — apps always ask for a **tag**; you move tags, never app code:
|
|
71
|
+
|
|
72
|
+
| Step | What you do | Versions | dev | preprod | prod |
|
|
73
|
+
| --- | --- | --- | --- | --- | --- |
|
|
74
|
+
| 1 | save the prompt | v1 | – | – | – |
|
|
75
|
+
| 2 | promote v1 to dev, then preprod, then prod | v1 | v1 | v1 | v1 |
|
|
76
|
+
| 3 | edit and save | v1, v2 | v1 | v1 | v1 |
|
|
77
|
+
| 4 | promote v2 to dev, preprod, prod (evals pass the gate) | v1, v2 | v2 | v2 | v2 |
|
|
78
|
+
| 5 | v2 misbehaves: roll back prod | v1, v2 | v2 | v2 | **v1** |
|
|
79
|
+
|
|
80
|
+
## Quickstart (library mode)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
export DATABASE_URL=postgresql://user:password@localhost:5432/promptdock
|
|
84
|
+
promptdock migrate
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from promptdock import Client
|
|
89
|
+
|
|
90
|
+
client = Client() # reads DATABASE_URL; or Client("postgresql://...", actor="shreya")
|
|
91
|
+
|
|
92
|
+
client.create_prompt("support-bot")
|
|
93
|
+
client.create_version(
|
|
94
|
+
"support-bot",
|
|
95
|
+
[
|
|
96
|
+
{"role": "system", "content": "You support {{ product }}."},
|
|
97
|
+
{"role": "user", "content": "{{ question }}"},
|
|
98
|
+
],
|
|
99
|
+
model_config={"model": "gpt-4o", "temperature": 0.2},
|
|
100
|
+
commit_message="first version",
|
|
101
|
+
)
|
|
102
|
+
client.promote("support-bot", "prod", 1) # recorded with actor = your OS username
|
|
103
|
+
|
|
104
|
+
# In your app: always ask for a tag, never a hard-coded version.
|
|
105
|
+
prompt = client.get_prompt("support-bot", tag="prod")
|
|
106
|
+
messages = prompt.render(product="PromptDock", question="How do I roll back?")
|
|
107
|
+
# call your LLM with messages and prompt.model_config
|
|
108
|
+
|
|
109
|
+
client.rollback("support-bot", "prod") # apps pick this up within the cache TTL (60 s)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Reads are cached for `cache_ttl` seconds (default 60). If the database is unreachable,
|
|
113
|
+
`get_prompt` returns the last version it fetched and logs a warning; pass
|
|
114
|
+
`snapshot_path="prompts.json"` to keep those copies on disk for restarts.
|
|
115
|
+
|
|
116
|
+
## Quickstart (server mode)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install "promptdock[server]"
|
|
120
|
+
promptdock migrate
|
|
121
|
+
promptdock serve # http://127.0.0.1:8000, API docs at /docs
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from promptdock import Client
|
|
126
|
+
|
|
127
|
+
client = Client(base_url="http://127.0.0.1:8000") # same methods as library mode
|
|
128
|
+
prompt = client.get_prompt("support-bot", tag="prod")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Any language can call the REST API, e.g.
|
|
132
|
+
`curl "http://127.0.0.1:8000/prompts/support-bot?tag=prod"`.
|
|
133
|
+
|
|
134
|
+
**Who did it:** send `X-Actor: <name>` on changes (the Python client sends its `actor`,
|
|
135
|
+
which defaults to your OS username). It is recorded in the history, not verified.
|
|
136
|
+
|
|
137
|
+
### Securing your server
|
|
138
|
+
|
|
139
|
+
There are no user accounts. `promptdock serve` only listens on
|
|
140
|
+
this machine by default. To share it, either run it on a private network or set a shared
|
|
141
|
+
token: `PROMPTDOCK_TOKEN=... promptdock serve --host 0.0.0.0`. Every request except
|
|
142
|
+
`/health` must then send `Authorization: Bearer <token>`; clients pass `token=...` (or set
|
|
143
|
+
`PROMPTDOCK_TOKEN`). Listening on `0.0.0.0` without a token prints a warning.
|
|
144
|
+
|
|
145
|
+
## Quickstart (Docker)
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
docker run -p 8000:8000 -e DATABASE_URL=postgresql://user:password@db-host:5432/promptdock -e PROMPTDOCK_TOKEN=change-me ghcr.io/shreyamhetre/promptdock
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The container runs `promptdock migrate`, then serves the API and dashboard on port 8000
|
|
152
|
+
(`/docs`, `/ui/`). It listens on `0.0.0.0`, so set `PROMPTDOCK_TOKEN` unless it runs on a
|
|
153
|
+
private network. To try it locally with a bundled Postgres:
|
|
154
|
+
`docker compose --profile server up -d`.
|
|
155
|
+
|
|
156
|
+
## Dashboard
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pip install "promptdock[ui]"
|
|
160
|
+
promptdock serve --ui # http://127.0.0.1:8000/ui/
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Prompts list, versions and tags, a sortable leaderboard with a score-over-versions chart
|
|
164
|
+
per metric, diffs between any two versions, and the audit log. You can promote (the gate
|
|
165
|
+
result is shown) and roll back (with a confirm step). It asks once for your name (sent as
|
|
166
|
+
`X-Actor`) and, if the server has one, the token; both stay in your browser. No Node is
|
|
167
|
+
needed to run it.
|
|
168
|
+
|
|
169
|
+
## Command line
|
|
170
|
+
|
|
171
|
+
The CLI connects like the client: `PROMPTDOCK_URL` (+ `PROMPTDOCK_TOKEN`) for a server,
|
|
172
|
+
otherwise `DATABASE_URL`. Changes are recorded with `--actor` (default: your OS username).
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
promptdock export ./prompts # one YAML file per prompt (latest version)
|
|
176
|
+
# ...edit ./prompts/support-bot.yaml...
|
|
177
|
+
promptdock push ./prompts --tag dev -m "friendlier tone" # new versions only for changed files
|
|
178
|
+
promptdock evals push results.jsonl # eval results, one JSON object per line
|
|
179
|
+
promptdock promote support-bot prod 3 --reason "evals passed"
|
|
180
|
+
promptdock rollback support-bot prod --reason "complaints"
|
|
181
|
+
promptdock versions list support-bot
|
|
182
|
+
promptdock history support-bot prod
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`push` never overwrites anything and never moves `prod`; promote to `prod` explicitly.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
promptdock gate set support-bot prod --min accuracy=0.85 --max latency_ms=1200
|
|
189
|
+
promptdock leaderboard support-bot --sort accuracy
|
|
190
|
+
promptdock diff support-bot 3 7
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
A gate uses the latest result per metric; a missing metric fails. `--force --reason ...`
|
|
194
|
+
bypasses a failing gate and is recorded. Rollback never runs the gate.
|
|
195
|
+
|
|
196
|
+
## AI agents (MCP)
|
|
197
|
+
|
|
198
|
+
`pip install "promptdock[mcp]"`, then add PromptDock to your agent's MCP config. For
|
|
199
|
+
Claude Desktop (`claude_desktop_config.json`) or Cursor (`.cursor/mcp.json`):
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"mcpServers": {
|
|
204
|
+
"promptdock": {
|
|
205
|
+
"command": "promptdock",
|
|
206
|
+
"args": ["mcp"],
|
|
207
|
+
"env": { "DATABASE_URL": "postgresql://user:password@localhost:5432/promptdock" }
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Use `PROMPTDOCK_URL` (+ `PROMPTDOCK_TOKEN`) instead of `DATABASE_URL` to go through a
|
|
214
|
+
server. Tools: `list_prompts`, `get_prompt`, `create_version`, `compare_versions`,
|
|
215
|
+
`get_leaderboard`. `promote` is only offered with `"args": ["mcp", "--allow-promote"]`,
|
|
216
|
+
gates still apply, and agents can never force. Agent changes are recorded with the actor
|
|
217
|
+
`mcp:<your OS username>`.
|
|
218
|
+
|
|
219
|
+
## Development
|
|
220
|
+
|
|
221
|
+
See [CONTRIBUTING.md](https://github.com/shreyamhetre/PromptDock/blob/main/CONTRIBUTING.md). The dashboard source is in `ui/` (React + Vite + Recharts):
|
|
222
|
+
`cd ui && npm ci && npm run build` writes the built files into `src/promptdock/ui/static/`.
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|