muninn-py 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.
- muninn_py-0.1.0/.claude/commands/whats-next.md +45 -0
- muninn_py-0.1.0/.claude/hooks/whats-next.sh +69 -0
- muninn_py-0.1.0/.claude/settings.json +17 -0
- muninn_py-0.1.0/.github/dependabot.yml +40 -0
- muninn_py-0.1.0/.github/workflows/ci.yml +96 -0
- muninn_py-0.1.0/.github/workflows/docs.yml +39 -0
- muninn_py-0.1.0/.github/workflows/integration.yml +173 -0
- muninn_py-0.1.0/.github/workflows/release.yml +131 -0
- muninn_py-0.1.0/.gitignore +64 -0
- muninn_py-0.1.0/CHANGELOG.md +49 -0
- muninn_py-0.1.0/CLAUDE.md +67 -0
- muninn_py-0.1.0/CODE_OF_CONDUCT.md +28 -0
- muninn_py-0.1.0/CONTRIBUTING.md +108 -0
- muninn_py-0.1.0/LICENSE +202 -0
- muninn_py-0.1.0/PKG-INFO +344 -0
- muninn_py-0.1.0/README.md +289 -0
- muninn_py-0.1.0/SECURITY.md +42 -0
- muninn_py-0.1.0/docs/RELEASING.md +76 -0
- muninn_py-0.1.0/docs/ROADMAP.md +130 -0
- muninn_py-0.1.0/docs/api/async_client.md +5 -0
- muninn_py-0.1.0/docs/api/client.md +5 -0
- muninn_py-0.1.0/docs/api/exceptions.md +13 -0
- muninn_py-0.1.0/docs/api/models.md +11 -0
- muninn_py-0.1.0/docs/api/notebook.md +19 -0
- muninn_py-0.1.0/docs/api/streaming.md +10 -0
- muninn_py-0.1.0/docs/changelog.md +5 -0
- muninn_py-0.1.0/docs/getting-started.md +136 -0
- muninn_py-0.1.0/docs/index.md +40 -0
- muninn_py-0.1.0/docs/notebooks.md +71 -0
- muninn_py-0.1.0/mkdocs.yml +63 -0
- muninn_py-0.1.0/notebooks/_build_drift_notebook.py +363 -0
- muninn_py-0.1.0/notebooks/alpha_backtest_demo.ipynb +246 -0
- muninn_py-0.1.0/notebooks/feature_drift_monitoring.ipynb +356 -0
- muninn_py-0.1.0/pyproject.toml +133 -0
- muninn_py-0.1.0/requirements-docs.txt +3 -0
- muninn_py-0.1.0/scripts/smoke.sh +186 -0
- muninn_py-0.1.0/setup.cfg +4 -0
- muninn_py-0.1.0/src/muninn/__init__.py +63 -0
- muninn_py-0.1.0/src/muninn/_cache.py +115 -0
- muninn_py-0.1.0/src/muninn/_retry.py +180 -0
- muninn_py-0.1.0/src/muninn/_transport.py +192 -0
- muninn_py-0.1.0/src/muninn/_version.py +7 -0
- muninn_py-0.1.0/src/muninn/async_client.py +329 -0
- muninn_py-0.1.0/src/muninn/cli.py +264 -0
- muninn_py-0.1.0/src/muninn/client.py +439 -0
- muninn_py-0.1.0/src/muninn/dashboard/__init__.py +60 -0
- muninn_py-0.1.0/src/muninn/dashboard/app.py +411 -0
- muninn_py-0.1.0/src/muninn/exceptions.py +53 -0
- muninn_py-0.1.0/src/muninn/models.py +116 -0
- muninn_py-0.1.0/src/muninn/notebook.py +219 -0
- muninn_py-0.1.0/src/muninn/pandas_client.py +184 -0
- muninn_py-0.1.0/src/muninn/py.typed +0 -0
- muninn_py-0.1.0/src/muninn/streaming.py +215 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/PKG-INFO +344 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/SOURCES.txt +73 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/dependency_links.txt +1 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/entry_points.txt +2 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/requires.txt +30 -0
- muninn_py-0.1.0/src/muninn_py.egg-info/top_level.txt +1 -0
- muninn_py-0.1.0/tests/__init__.py +0 -0
- muninn_py-0.1.0/tests/bench_client.py +138 -0
- muninn_py-0.1.0/tests/conftest.py +13 -0
- muninn_py-0.1.0/tests/test_async_client.py +370 -0
- muninn_py-0.1.0/tests/test_cache.py +218 -0
- muninn_py-0.1.0/tests/test_cli.py +247 -0
- muninn_py-0.1.0/tests/test_client.py +346 -0
- muninn_py-0.1.0/tests/test_get_panel.py +163 -0
- muninn_py-0.1.0/tests/test_integration.py +721 -0
- muninn_py-0.1.0/tests/test_models.py +153 -0
- muninn_py-0.1.0/tests/test_notebook.py +164 -0
- muninn_py-0.1.0/tests/test_openapi_contract.py +214 -0
- muninn_py-0.1.0/tests/test_pandas_client.py +147 -0
- muninn_py-0.1.0/tests/test_retry.py +229 -0
- muninn_py-0.1.0/tests/test_streaming.py +126 -0
- muninn_py-0.1.0/tests/testdata/muninn_api_docs_v1.json +221 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Audit the current project state and surface what's next, what's missing, and what's at risk.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Audit this project's current state and tell me three things, in this order:
|
|
6
|
+
|
|
7
|
+
1. **What's next.** The next 3 actionable items, ranked by leverage. For each, name the file or component it touches and the single thing that would close it.
|
|
8
|
+
2. **What's missing.** Anything tracked as deferred, partial, scaffolded, or TODO that should be revisited. Cross-reference these against the ROADMAP and any `🟡` markers.
|
|
9
|
+
3. **What's at risk.** Items likely to silently rot — stale docs that contradict the code, untested code paths, ADRs the codebase has drifted from, dependencies overdue for a bump, exit criteria not yet verified by a test.
|
|
10
|
+
|
|
11
|
+
## How to gather the picture
|
|
12
|
+
|
|
13
|
+
Read, in order:
|
|
14
|
+
|
|
15
|
+
- `docs/steering/ROADMAP.md` if present, otherwise `docs/ROADMAP.md`.
|
|
16
|
+
- The most recent `## [Unreleased]` block of `CHANGELOG.md`.
|
|
17
|
+
- The last 5 commits — `git log --oneline -5`.
|
|
18
|
+
- Anything in `TaskList` that's still `pending` or `in_progress`.
|
|
19
|
+
- `git status --short` for uncommitted work.
|
|
20
|
+
|
|
21
|
+
If this is a multi-repo session (e.g., both the server `muninn` and the SDK `muninn-py` are checked out), audit whichever working tree the current shell is in. Note the other one only if its state is directly load-bearing for an item.
|
|
22
|
+
|
|
23
|
+
## How to report
|
|
24
|
+
|
|
25
|
+
Keep the whole reply under ~250 words. Format:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
## What's next
|
|
29
|
+
1. <action> — <file or component> — <closing condition>
|
|
30
|
+
2. ...
|
|
31
|
+
3. ...
|
|
32
|
+
|
|
33
|
+
## What's missing
|
|
34
|
+
- <item> — <why it matters / what would close it>
|
|
35
|
+
- ...
|
|
36
|
+
|
|
37
|
+
## What's at risk
|
|
38
|
+
- <item> — <how it might rot>
|
|
39
|
+
- ...
|
|
40
|
+
|
|
41
|
+
## Recommended next move
|
|
42
|
+
<one sentence — usually pointing at the top "What's next" item>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Don't dispatch agents. Don't run the test suite. Don't write or modify files. This is an audit, not work. If you can't tell the state of something from the docs + git log alone, say so explicitly.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop hook — emits a one-line "next up" nudge after every Claude response.
|
|
3
|
+
#
|
|
4
|
+
# Reads ROADMAP.md (project conventions: docs/steering/ROADMAP.md or docs/ROADMAP.md),
|
|
5
|
+
# counts phase markers, and surfaces the next un-done phase. Silent when no roadmap
|
|
6
|
+
# is present so this hook is harmless in other repos that share the same global config.
|
|
7
|
+
#
|
|
8
|
+
# Designed to run in well under a second; no network, no Claude, just shell + grep.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# Resolve repo root from $CLAUDE_PROJECT_DIR (set by Claude Code), or fall back to pwd.
|
|
13
|
+
ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
14
|
+
|
|
15
|
+
# Locate a roadmap file. First match wins.
|
|
16
|
+
ROADMAP=""
|
|
17
|
+
for candidate in \
|
|
18
|
+
"$ROOT/docs/steering/ROADMAP.md" \
|
|
19
|
+
"$ROOT/docs/ROADMAP.md" \
|
|
20
|
+
"$ROOT/ROADMAP.md"
|
|
21
|
+
do
|
|
22
|
+
if [ -f "$candidate" ]; then
|
|
23
|
+
ROADMAP="$candidate"
|
|
24
|
+
break
|
|
25
|
+
fi
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# No roadmap — exit silently so the hook is invisible in unrelated repos.
|
|
29
|
+
[ -z "$ROADMAP" ] && exit 0
|
|
30
|
+
|
|
31
|
+
# Count phases by completion marker.
|
|
32
|
+
# A phase is "done" when its heading carries ✅ (complete), 🟢 (mostly complete),
|
|
33
|
+
# or _(Merged…)_ (rolled into another phase).
|
|
34
|
+
DONE_PATTERN='^## Phase .*(✅|🟢|_\(Merged)'
|
|
35
|
+
DONE=$(grep -cE "$DONE_PATTERN" "$ROADMAP" 2>/dev/null || true)
|
|
36
|
+
TOTAL=$(grep -cE '^## Phase ' "$ROADMAP" 2>/dev/null || true)
|
|
37
|
+
|
|
38
|
+
# Default counts to 0 if grep returned nothing.
|
|
39
|
+
DONE="${DONE:-0}"
|
|
40
|
+
TOTAL="${TOTAL:-0}"
|
|
41
|
+
|
|
42
|
+
# First "## Phase ..." line that isn't marked done. Strip the prefix + trailing
|
|
43
|
+
# section emoji/marker for a compact display.
|
|
44
|
+
NEXT_PHASE=$(
|
|
45
|
+
grep -E '^## Phase ' "$ROADMAP" 2>/dev/null \
|
|
46
|
+
| grep -vE '✅|🟢|_\(Merged' \
|
|
47
|
+
| head -n 1 \
|
|
48
|
+
| sed -E 's/^## Phase //; s/ —.*$//; s/ \(.*$//; s/ 🟡.*$//'
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Open work — anything with a 🟡 (scaffolded / in-progress) marker, useful as a
|
|
52
|
+
# soft heads-up.
|
|
53
|
+
OPEN=$(grep -cE '^- 🟡' "$ROADMAP" 2>/dev/null || true)
|
|
54
|
+
OPEN="${OPEN:-0}"
|
|
55
|
+
|
|
56
|
+
if [ -z "$NEXT_PHASE" ] && [ "$TOTAL" -gt 0 ] && [ "$DONE" -eq "$TOTAL" ]; then
|
|
57
|
+
printf '→ Roadmap fully ✅ (%s/%s). Try /whats-next for follow-ups or post-Phase ideas.\n' \
|
|
58
|
+
"$DONE" "$TOTAL"
|
|
59
|
+
elif [ -n "$NEXT_PHASE" ]; then
|
|
60
|
+
if [ "$OPEN" -gt 0 ]; then
|
|
61
|
+
printf '→ Next: Phase %s · %s/%s done · %s open item(s) tagged 🟡 · /whats-next for detail.\n' \
|
|
62
|
+
"$NEXT_PHASE" "$DONE" "$TOTAL" "$OPEN"
|
|
63
|
+
else
|
|
64
|
+
printf '→ Next: Phase %s · %s/%s done · /whats-next for detail.\n' \
|
|
65
|
+
"$NEXT_PHASE" "$DONE" "$TOTAL"
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
# Dependabot configuration for muninn-py.
|
|
4
|
+
# - Watches pip dependencies declared in pyproject.toml.
|
|
5
|
+
# - Watches the GitHub Actions used by .github/workflows/*.
|
|
6
|
+
# - Weekly cadence; small bumps grouped to reduce noise.
|
|
7
|
+
|
|
8
|
+
updates:
|
|
9
|
+
- package-ecosystem: "pip"
|
|
10
|
+
directory: "/"
|
|
11
|
+
schedule:
|
|
12
|
+
interval: "weekly"
|
|
13
|
+
day: "monday"
|
|
14
|
+
open-pull-requests-limit: 10
|
|
15
|
+
groups:
|
|
16
|
+
runtime:
|
|
17
|
+
applies-to: version-updates
|
|
18
|
+
patterns:
|
|
19
|
+
- "httpx"
|
|
20
|
+
- "polars"
|
|
21
|
+
- "pandas"
|
|
22
|
+
- "pydantic"
|
|
23
|
+
dev:
|
|
24
|
+
applies-to: version-updates
|
|
25
|
+
patterns:
|
|
26
|
+
- "pytest*"
|
|
27
|
+
- "respx"
|
|
28
|
+
- "ruff"
|
|
29
|
+
- "mypy"
|
|
30
|
+
labels:
|
|
31
|
+
- "dependencies"
|
|
32
|
+
|
|
33
|
+
- package-ecosystem: "github-actions"
|
|
34
|
+
directory: "/"
|
|
35
|
+
schedule:
|
|
36
|
+
interval: "weekly"
|
|
37
|
+
day: "monday"
|
|
38
|
+
labels:
|
|
39
|
+
- "dependencies"
|
|
40
|
+
- "github-actions"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
name: test (py${{ matrix.python }})
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 15
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
python: ["3.10", "3.11", "3.12"]
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
|
+
|
|
27
|
+
- name: Set up Python ${{ matrix.python }}
|
|
28
|
+
uses: actions/setup-python@v6
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python }}
|
|
31
|
+
cache: pip
|
|
32
|
+
|
|
33
|
+
- name: Install
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --upgrade pip
|
|
36
|
+
pip install -e ".[dev]"
|
|
37
|
+
|
|
38
|
+
- name: Lint
|
|
39
|
+
run: ruff check .
|
|
40
|
+
|
|
41
|
+
- name: Type-check
|
|
42
|
+
run: mypy src/muninn
|
|
43
|
+
|
|
44
|
+
- name: Tests
|
|
45
|
+
run: pytest -ra
|
|
46
|
+
|
|
47
|
+
benchmark:
|
|
48
|
+
name: benchmarks (py3.12)
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
timeout-minutes: 15
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v6
|
|
54
|
+
|
|
55
|
+
- name: Set up Python 3.12
|
|
56
|
+
uses: actions/setup-python@v6
|
|
57
|
+
with:
|
|
58
|
+
python-version: "3.12"
|
|
59
|
+
cache: pip
|
|
60
|
+
|
|
61
|
+
- name: Install
|
|
62
|
+
run: |
|
|
63
|
+
python -m pip install --upgrade pip
|
|
64
|
+
pip install -e ".[dev]"
|
|
65
|
+
|
|
66
|
+
- name: Run benchmarks
|
|
67
|
+
# --benchmark-only: run only benchmark tests (skip regular tests)
|
|
68
|
+
# --benchmark-json: save results for artifact upload
|
|
69
|
+
# When .benchmarks/baseline.json exists (committed), --benchmark-compare
|
|
70
|
+
# enforces the 25% regression gate. Until a baseline is committed, this
|
|
71
|
+
# step runs in observation mode and saves the results as an artifact.
|
|
72
|
+
run: |
|
|
73
|
+
if [ -f .benchmarks/baseline.json ]; then
|
|
74
|
+
pytest tests/bench_client.py \
|
|
75
|
+
--benchmark-only \
|
|
76
|
+
--benchmark-compare=.benchmarks/baseline.json \
|
|
77
|
+
--benchmark-fail-max-delta-mean=0.25 \
|
|
78
|
+
--benchmark-json=benchmark-results.json \
|
|
79
|
+
-v
|
|
80
|
+
else
|
|
81
|
+
pytest tests/bench_client.py \
|
|
82
|
+
--benchmark-only \
|
|
83
|
+
--benchmark-json=benchmark-results.json \
|
|
84
|
+
-v
|
|
85
|
+
echo "No baseline found. To enable the regression gate, run:"
|
|
86
|
+
echo " pytest tests/bench_client.py --benchmark-save=baseline --benchmark-only"
|
|
87
|
+
echo "then commit .benchmarks/baseline.json"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
- name: Upload benchmark results
|
|
91
|
+
uses: actions/upload-artifact@v7
|
|
92
|
+
if: always()
|
|
93
|
+
with:
|
|
94
|
+
name: benchmark-results-py3.12
|
|
95
|
+
path: benchmark-results.json
|
|
96
|
+
retention-days: 30
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "requirements-docs.txt"
|
|
10
|
+
- "src/muninn/**"
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 10
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v6
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- uses: actions/setup-python@v6
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
cache: pip
|
|
30
|
+
cache-dependency-path: requirements-docs.txt
|
|
31
|
+
|
|
32
|
+
- name: Install docs dependencies
|
|
33
|
+
run: pip install -r requirements-docs.txt
|
|
34
|
+
|
|
35
|
+
- name: Install muninn-py (for mkdocstrings introspection)
|
|
36
|
+
run: pip install -e .
|
|
37
|
+
|
|
38
|
+
- name: Deploy to GitHub Pages
|
|
39
|
+
run: mkdocs gh-deploy --force
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
name: Integration
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: integration-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
integration-tests:
|
|
16
|
+
name: integration tests (py3.12)
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 20
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
|
|
23
|
+
- name: Check out Muninn server
|
|
24
|
+
uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
repository: lgreene03/muninn
|
|
27
|
+
path: muninn
|
|
28
|
+
|
|
29
|
+
- name: Set up Python 3.12
|
|
30
|
+
uses: actions/setup-python@v6
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
cache: pip
|
|
34
|
+
|
|
35
|
+
- name: Install SDK with test extras
|
|
36
|
+
run: |
|
|
37
|
+
python -m pip install --upgrade pip
|
|
38
|
+
pip install -e ".[dev]"
|
|
39
|
+
|
|
40
|
+
- name: Run integration tests
|
|
41
|
+
run: pytest -m integration -ra -v
|
|
42
|
+
env:
|
|
43
|
+
MUNINN_SERVER_DIR: ${{ github.workspace }}/muninn
|
|
44
|
+
|
|
45
|
+
notebook-execution:
|
|
46
|
+
name: notebook smoke test (py3.12)
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
timeout-minutes: 25
|
|
49
|
+
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v6
|
|
52
|
+
|
|
53
|
+
- name: Check out Muninn server
|
|
54
|
+
uses: actions/checkout@v6
|
|
55
|
+
with:
|
|
56
|
+
repository: lgreene03/muninn
|
|
57
|
+
path: muninn-server
|
|
58
|
+
|
|
59
|
+
- name: Set up Python 3.12
|
|
60
|
+
uses: actions/setup-python@v6
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.12"
|
|
63
|
+
cache: pip
|
|
64
|
+
|
|
65
|
+
- name: Install SDK with notebook extras
|
|
66
|
+
run: |
|
|
67
|
+
python -m pip install --upgrade pip
|
|
68
|
+
pip install -e ".[notebooks,dev]"
|
|
69
|
+
pip install nbconvert ipykernel
|
|
70
|
+
|
|
71
|
+
- name: Register Jupyter kernel
|
|
72
|
+
run: python -m ipykernel install --user --name muninn-py
|
|
73
|
+
|
|
74
|
+
- name: Build Muninn server image
|
|
75
|
+
working-directory: muninn-server
|
|
76
|
+
run: docker build -t muninn:ci .
|
|
77
|
+
|
|
78
|
+
- name: Start infrastructure
|
|
79
|
+
working-directory: muninn-server
|
|
80
|
+
run: docker compose up -d
|
|
81
|
+
|
|
82
|
+
- name: Wait for infrastructure health
|
|
83
|
+
working-directory: muninn-server
|
|
84
|
+
run: |
|
|
85
|
+
echo "Waiting for PostgreSQL..."
|
|
86
|
+
until docker compose exec -T postgres pg_isready -U muninn; do sleep 2; done
|
|
87
|
+
echo "Waiting for Redpanda..."
|
|
88
|
+
until docker compose exec -T redpanda rpk cluster health; do sleep 2; done
|
|
89
|
+
echo "Waiting for MinIO..."
|
|
90
|
+
until docker compose exec -T minio mc ready local; do sleep 2; done
|
|
91
|
+
|
|
92
|
+
- name: Start Muninn server
|
|
93
|
+
working-directory: muninn-server
|
|
94
|
+
run: |
|
|
95
|
+
docker run -d --name muninn-server \
|
|
96
|
+
--network "$(docker compose ps -q postgres | xargs docker inspect -f '{{range .NetworkSettings.Networks}}{{.NetworkID}}{{end}}' | head -1)" \
|
|
97
|
+
-e SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/muninn \
|
|
98
|
+
-e SPRING_DATASOURCE_USERNAME=muninn \
|
|
99
|
+
-e SPRING_DATASOURCE_PASSWORD=muninn \
|
|
100
|
+
-e SPRING_KAFKA_BOOTSTRAP_SERVERS=redpanda:9092 \
|
|
101
|
+
-e MUNINN_STORAGE_S3_ENDPOINT=http://minio:9000 \
|
|
102
|
+
-e MUNINN_STORAGE_S3_ACCESS_KEY=minioadmin \
|
|
103
|
+
-e MUNINN_STORAGE_S3_SECRET_KEY=minioadmin \
|
|
104
|
+
-e MUNINN_INGESTION_BINANCE_ENABLED=false \
|
|
105
|
+
-p 8080:8080 \
|
|
106
|
+
muninn:ci
|
|
107
|
+
|
|
108
|
+
- name: Wait for Muninn API
|
|
109
|
+
run: |
|
|
110
|
+
echo "Waiting for Muninn API..."
|
|
111
|
+
for i in $(seq 1 60); do
|
|
112
|
+
if curl -sf http://localhost:8080/actuator/health > /dev/null 2>&1; then
|
|
113
|
+
echo "Muninn API is up"
|
|
114
|
+
break
|
|
115
|
+
fi
|
|
116
|
+
if [ $i -eq 60 ]; then
|
|
117
|
+
echo "Muninn API failed to start"
|
|
118
|
+
docker logs muninn-server
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
sleep 3
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
- name: Seed synthetic data via smoke script
|
|
125
|
+
working-directory: muninn-server
|
|
126
|
+
run: |
|
|
127
|
+
if [ -f scripts/smoke.sh ]; then
|
|
128
|
+
bash scripts/smoke.sh || echo "Smoke script exited with $? (non-fatal for notebook test)"
|
|
129
|
+
else
|
|
130
|
+
echo "No smoke script found — notebooks may run with empty data"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
- name: Execute alpha backtest notebook
|
|
134
|
+
run: |
|
|
135
|
+
jupyter nbconvert \
|
|
136
|
+
--to notebook \
|
|
137
|
+
--execute \
|
|
138
|
+
--ExecutePreprocessor.timeout=120 \
|
|
139
|
+
--ExecutePreprocessor.kernel_name=muninn-py \
|
|
140
|
+
--output-dir=/tmp/notebook-output \
|
|
141
|
+
notebooks/alpha_backtest_demo.ipynb || {
|
|
142
|
+
echo "::warning::alpha_backtest_demo.ipynb failed — server may lack sufficient data"
|
|
143
|
+
exit 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
- name: Execute feature drift monitoring notebook
|
|
147
|
+
run: |
|
|
148
|
+
jupyter nbconvert \
|
|
149
|
+
--to notebook \
|
|
150
|
+
--execute \
|
|
151
|
+
--ExecutePreprocessor.timeout=120 \
|
|
152
|
+
--ExecutePreprocessor.kernel_name=muninn-py \
|
|
153
|
+
--output-dir=/tmp/notebook-output \
|
|
154
|
+
notebooks/feature_drift_monitoring.ipynb || {
|
|
155
|
+
echo "::warning::feature_drift_monitoring.ipynb failed — server may lack sufficient data"
|
|
156
|
+
exit 0
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
- name: Upload executed notebooks
|
|
160
|
+
uses: actions/upload-artifact@v7
|
|
161
|
+
if: always()
|
|
162
|
+
with:
|
|
163
|
+
name: executed-notebooks
|
|
164
|
+
path: /tmp/notebook-output/
|
|
165
|
+
retention-days: 14
|
|
166
|
+
|
|
167
|
+
- name: Cleanup
|
|
168
|
+
if: always()
|
|
169
|
+
working-directory: muninn-server
|
|
170
|
+
run: |
|
|
171
|
+
docker stop muninn-server || true
|
|
172
|
+
docker rm muninn-server || true
|
|
173
|
+
docker compose down -v || true
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Tagged release flow:
|
|
4
|
+
# 1. Bump the version in pyproject.toml + src/muninn/_version.py + CHANGELOG.
|
|
5
|
+
# 2. Commit, tag (vX.Y.Z), and push the tag.
|
|
6
|
+
# 3. This workflow builds the sdist + wheel and publishes to PyPI via
|
|
7
|
+
# Trusted Publishing (OIDC) — no API tokens stored as secrets.
|
|
8
|
+
#
|
|
9
|
+
# One-time setup before the first publish:
|
|
10
|
+
# - Reserve the name `muninn-py` on PyPI (https://pypi.org/manage/account/publishing/).
|
|
11
|
+
# - Add a "Trusted publisher" pointing at this workflow:
|
|
12
|
+
# PyPI Project Name : muninn-py
|
|
13
|
+
# Owner : lgreene03
|
|
14
|
+
# Repository name : muninn-py
|
|
15
|
+
# Workflow name : release.yml
|
|
16
|
+
# Environment name : pypi
|
|
17
|
+
# - The pre-release run against TestPyPI uses the `testpypi` environment;
|
|
18
|
+
# register the equivalent trusted publisher at https://test.pypi.org.
|
|
19
|
+
#
|
|
20
|
+
# Manual dispatch is supported for republishing the latest build to TestPyPI
|
|
21
|
+
# without cutting a new tag.
|
|
22
|
+
|
|
23
|
+
on:
|
|
24
|
+
push:
|
|
25
|
+
tags:
|
|
26
|
+
- "v*.*.*"
|
|
27
|
+
workflow_dispatch:
|
|
28
|
+
inputs:
|
|
29
|
+
target:
|
|
30
|
+
description: "Where to upload"
|
|
31
|
+
required: true
|
|
32
|
+
default: "testpypi"
|
|
33
|
+
type: choice
|
|
34
|
+
options:
|
|
35
|
+
- testpypi
|
|
36
|
+
- pypi
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
concurrency:
|
|
42
|
+
group: release-${{ github.ref }}
|
|
43
|
+
cancel-in-progress: false
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
build:
|
|
47
|
+
name: build distributions
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 10
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v6
|
|
52
|
+
|
|
53
|
+
- name: Set up Python
|
|
54
|
+
uses: actions/setup-python@v6
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
|
|
58
|
+
- name: Install build tooling
|
|
59
|
+
run: |
|
|
60
|
+
python -m pip install --upgrade pip
|
|
61
|
+
pip install build twine
|
|
62
|
+
|
|
63
|
+
- name: Build sdist + wheel
|
|
64
|
+
run: python -m build
|
|
65
|
+
|
|
66
|
+
- name: Check artifact metadata
|
|
67
|
+
run: twine check dist/*
|
|
68
|
+
|
|
69
|
+
- name: Verify version matches tag (tag-triggered runs only)
|
|
70
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
71
|
+
run: |
|
|
72
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
73
|
+
pkg_version=$(python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
74
|
+
if [ "$tag_version" != "$pkg_version" ]; then
|
|
75
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match pyproject.toml version $pkg_version"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
echo "Tag $GITHUB_REF_NAME matches pyproject.toml version $pkg_version"
|
|
79
|
+
|
|
80
|
+
- name: Upload built artifacts
|
|
81
|
+
uses: actions/upload-artifact@v7
|
|
82
|
+
with:
|
|
83
|
+
name: dist
|
|
84
|
+
path: dist/
|
|
85
|
+
retention-days: 14
|
|
86
|
+
|
|
87
|
+
publish-testpypi:
|
|
88
|
+
name: publish to TestPyPI
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
needs: build
|
|
91
|
+
if: |
|
|
92
|
+
github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
93
|
+
environment:
|
|
94
|
+
name: testpypi
|
|
95
|
+
url: https://test.pypi.org/project/muninn-py/
|
|
96
|
+
permissions:
|
|
97
|
+
id-token: write
|
|
98
|
+
steps:
|
|
99
|
+
- name: Download distributions
|
|
100
|
+
uses: actions/download-artifact@v8
|
|
101
|
+
with:
|
|
102
|
+
name: dist
|
|
103
|
+
path: dist/
|
|
104
|
+
|
|
105
|
+
- name: Publish to TestPyPI
|
|
106
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
107
|
+
with:
|
|
108
|
+
repository-url: https://test.pypi.org/legacy/
|
|
109
|
+
skip-existing: true
|
|
110
|
+
|
|
111
|
+
publish-pypi:
|
|
112
|
+
name: publish to PyPI
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
needs: build
|
|
115
|
+
if: |
|
|
116
|
+
startsWith(github.ref, 'refs/tags/v')
|
|
117
|
+
|| (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
118
|
+
environment:
|
|
119
|
+
name: pypi
|
|
120
|
+
url: https://pypi.org/project/muninn-py/
|
|
121
|
+
permissions:
|
|
122
|
+
id-token: write
|
|
123
|
+
steps:
|
|
124
|
+
- name: Download distributions
|
|
125
|
+
uses: actions/download-artifact@v8
|
|
126
|
+
with:
|
|
127
|
+
name: dist
|
|
128
|
+
path: dist/
|
|
129
|
+
|
|
130
|
+
- name: Publish to PyPI
|
|
131
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
*.egg
|
|
21
|
+
MANIFEST
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
env/
|
|
27
|
+
ENV/
|
|
28
|
+
|
|
29
|
+
# Test / type / lint
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
.coverage
|
|
34
|
+
.coverage.*
|
|
35
|
+
htmlcov/
|
|
36
|
+
.tox/
|
|
37
|
+
.nox/
|
|
38
|
+
|
|
39
|
+
# Jupyter
|
|
40
|
+
.ipynb_checkpoints/
|
|
41
|
+
*.ipynb-checkpoint
|
|
42
|
+
|
|
43
|
+
# IDE
|
|
44
|
+
.idea/
|
|
45
|
+
.vscode/
|
|
46
|
+
*.swp
|
|
47
|
+
*.swo
|
|
48
|
+
|
|
49
|
+
# OS
|
|
50
|
+
.DS_Store
|
|
51
|
+
Thumbs.db
|
|
52
|
+
|
|
53
|
+
# MkDocs
|
|
54
|
+
site/
|
|
55
|
+
|
|
56
|
+
# Benchmarks
|
|
57
|
+
.benchmarks/
|
|
58
|
+
|
|
59
|
+
# Local data
|
|
60
|
+
data/
|
|
61
|
+
*.parquet
|
|
62
|
+
*.feather
|
|
63
|
+
.env
|
|
64
|
+
.env.local
|