riffdb-mlflow 0.2.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.
- riffdb_mlflow-0.2.0/.github/riffdb-revision +1 -0
- riffdb_mlflow-0.2.0/.github/workflows/release.yml +175 -0
- riffdb_mlflow-0.2.0/.github/workflows/riffdb-compatibility.yml +69 -0
- riffdb_mlflow-0.2.0/.gitignore +9 -0
- riffdb_mlflow-0.2.0/AGENTS.md +63 -0
- riffdb_mlflow-0.2.0/DESIGN.md +88 -0
- riffdb_mlflow-0.2.0/Makefile +27 -0
- riffdb_mlflow-0.2.0/PKG-INFO +78 -0
- riffdb_mlflow-0.2.0/PLAN.md +75 -0
- riffdb_mlflow-0.2.0/README.md +67 -0
- riffdb_mlflow-0.2.0/benchmarks/bench_out_of_order_spans.py +359 -0
- riffdb_mlflow-0.2.0/generated/riffdb.application.exact.json +1 -0
- riffdb_mlflow-0.2.0/generated/riffdb.contract.bundle +0 -0
- riffdb_mlflow-0.2.0/pyproject.toml +47 -0
- riffdb_mlflow-0.2.0/riffdb/contract.riff +1176 -0
- riffdb_mlflow-0.2.0/riffdb/queries/aggregate_span_analytics_by_model.riffq +48 -0
- riffdb_mlflow-0.2.0/riffdb/queries/aggregate_span_analytics_by_name_status.riffq +46 -0
- riffdb_mlflow-0.2.0/riffdb/queries/aggregate_trace_analytics_by_name.riffq +45 -0
- riffdb_mlflow-0.2.0/riffdb/queries/aggregate_trace_analytics_by_status.riffq +45 -0
- riffdb_mlflow-0.2.0/riffdb/queries/batch_get_trace_assessments.riffq +22 -0
- riffdb_mlflow-0.2.0/riffdb/queries/batch_get_trace_spans.riffq +20 -0
- riffdb_mlflow-0.2.0/riffdb/queries/batch_get_traces.riffq +40 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_experiment.riffq +24 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_experiment_by_name.riffq +24 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_logged_model.riffq +19 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_metric_history.riffq +24 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_run.riffq +18 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_run_lineage.riffq +16 -0
- riffdb_mlflow-0.2.0/riffdb/queries/get_trace.riffq +49 -0
- riffdb_mlflow-0.2.0/riffdb/queries/query_assessment_analytics.riffq +21 -0
- riffdb_mlflow-0.2.0/riffdb/queries/query_span_analytics.riffq +23 -0
- riffdb_mlflow-0.2.0/riffdb/queries/query_span_model_rollup.riffq +30 -0
- riffdb_mlflow-0.2.0/riffdb/queries/query_trace_analytics.riffq +24 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments.riffq +50 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name.riffq +45 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name_ilike.riffq +45 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name_like.riffq +45 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag.riffq +48 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag_ilike.riffq +48 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag_like.riffq +48 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_two_tags.riffq +56 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_logged_models.riffq +21 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_runs.riffq +21 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_runs_by_lifecycle.riffq +23 -0
- riffdb_mlflow-0.2.0/riffdb/queries/search_traces.riffq +26 -0
- riffdb_mlflow-0.2.0/riffdb.application.json +121 -0
- riffdb_mlflow-0.2.0/riffdb.application.lock.json +1 -0
- riffdb_mlflow-0.2.0/riffdb.toml +8 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/__init__.py +8 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/__main__.py +1133 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/_extended_store.py +3081 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/config.py +38 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/generated/client.py +6277 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/py.typed +1 -0
- riffdb_mlflow-0.2.0/src/riffdb_mlflow/store.py +1632 -0
- riffdb_mlflow-0.2.0/tests/test_config.py +29 -0
- riffdb_mlflow-0.2.0/tests/test_experiments.py +290 -0
- riffdb_mlflow-0.2.0/tests/test_runs.py +474 -0
- riffdb_mlflow-0.2.0/tests/test_surface.py +87 -0
- riffdb_mlflow-0.2.0/tests/test_traces_and_analytics.py +1112 -0
- riffdb_mlflow-0.2.0/uv.lock +2438 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fc72da6e3d2af6ab29be72712139e9d16bcaf233
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
release_tag:
|
|
10
|
+
description: 'Release tag to verify (for example v0.2.0)'
|
|
11
|
+
required: false
|
|
12
|
+
default: 'v0.2.0'
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: release-${{ github.workflow }}-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
verify-and-build:
|
|
23
|
+
name: Verify adapter and build Python distributions
|
|
24
|
+
runs-on: ubuntu-24.04
|
|
25
|
+
timeout-minutes: 20
|
|
26
|
+
outputs:
|
|
27
|
+
release_tag: ${{ steps.meta.outputs.release_tag }}
|
|
28
|
+
version: ${{ steps.meta.outputs.version }}
|
|
29
|
+
revision: ${{ steps.meta.outputs.revision }}
|
|
30
|
+
steps:
|
|
31
|
+
- name: Check out adapter
|
|
32
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- name: Install Python
|
|
37
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
38
|
+
with:
|
|
39
|
+
python-version: '3.13'
|
|
40
|
+
- name: Install uv
|
|
41
|
+
run: python3 -m pip install --quiet uv
|
|
42
|
+
- name: Resolve release metadata and download RiffDB release bundle
|
|
43
|
+
id: meta
|
|
44
|
+
run: |
|
|
45
|
+
if [[ "${{ github.event_name }}" == "push" ]]; then
|
|
46
|
+
release_tag="${GITHUB_REF_NAME}"
|
|
47
|
+
else
|
|
48
|
+
release_tag="${{ inputs.release_tag || 'v0.2.0' }}"
|
|
49
|
+
fi
|
|
50
|
+
[[ "$release_tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]
|
|
51
|
+
version="${BASH_REMATCH[1]}"
|
|
52
|
+
python3 - "$version" <<'PY'
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
import sys
|
|
55
|
+
import tomllib
|
|
56
|
+
expected = sys.argv[1]
|
|
57
|
+
pyproject = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
|
|
58
|
+
assert pyproject["project"]["version"] == expected, pyproject["project"]["version"]
|
|
59
|
+
init_text = Path("src/riffdb_mlflow/__init__.py").read_text(encoding="utf-8")
|
|
60
|
+
assert f'__version__ = "{expected}"' in init_text, init_text
|
|
61
|
+
PY
|
|
62
|
+
riffdb_rev="$(cat .github/riffdb-revision)"
|
|
63
|
+
[[ "$riffdb_rev" =~ ^[0-9a-f]{40}$ ]]
|
|
64
|
+
|
|
65
|
+
bundle="riffdb-${version}-x86_64-unknown-linux-gnu.tar.gz"
|
|
66
|
+
base_url="https://github.com/riffdb/riffdb/releases/download/${release_tag}"
|
|
67
|
+
mkdir -p "$RUNNER_TEMP/riffdb-release"
|
|
68
|
+
curl -fsSL "$base_url/release-provenance-v1.json" -o "$RUNNER_TEMP/riffdb-release/release-provenance-v1.json"
|
|
69
|
+
curl -fsSL "$base_url/$bundle" -o "$RUNNER_TEMP/riffdb-release/$bundle"
|
|
70
|
+
expected_sha="$(jq -r '.bundle_archive.sha256' "$RUNNER_TEMP/riffdb-release/release-provenance-v1.json")"
|
|
71
|
+
echo "${expected_sha} $RUNNER_TEMP/riffdb-release/$bundle" | sha256sum -c -
|
|
72
|
+
tar -xzf "$RUNNER_TEMP/riffdb-release/$bundle" -C "$RUNNER_TEMP/riffdb-release"
|
|
73
|
+
bundle_dir="$RUNNER_TEMP/riffdb-release/riffdb-${version}-x86_64-unknown-linux-gnu"
|
|
74
|
+
[[ "$(tr -d '\n' < "$bundle_dir/REVISION")" == "$riffdb_rev" ]]
|
|
75
|
+
"$bundle_dir/bin/riffdb" application check
|
|
76
|
+
|
|
77
|
+
printf 'release_tag=%s\n' "$release_tag" >>"$GITHUB_OUTPUT"
|
|
78
|
+
printf 'version=%s\n' "$version" >>"$GITHUB_OUTPUT"
|
|
79
|
+
printf 'revision=%s\n' "$riffdb_rev" >>"$GITHUB_OUTPUT"
|
|
80
|
+
- name: Run adapter checks and build wheel and sdist
|
|
81
|
+
run: |
|
|
82
|
+
uv sync --locked
|
|
83
|
+
make check
|
|
84
|
+
mkdir -p "$RUNNER_TEMP/release-out"
|
|
85
|
+
uv build --out-dir "$RUNNER_TEMP/release-out"
|
|
86
|
+
(
|
|
87
|
+
cd "$RUNNER_TEMP/release-out"
|
|
88
|
+
sha256sum "riffdb_mlflow-${{ steps.meta.outputs.version }}"* >SHA256SUMS
|
|
89
|
+
)
|
|
90
|
+
- name: Upload verified release artifacts
|
|
91
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
92
|
+
with:
|
|
93
|
+
name: riffdb-mlflow-release
|
|
94
|
+
path: ${{ runner.temp }}/release-out/*
|
|
95
|
+
if-no-files-found: error
|
|
96
|
+
|
|
97
|
+
publish-release:
|
|
98
|
+
name: Publish GitHub Release and PyPI package
|
|
99
|
+
needs: [verify-and-build]
|
|
100
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
101
|
+
runs-on: ubuntu-24.04
|
|
102
|
+
permissions:
|
|
103
|
+
contents: write
|
|
104
|
+
id-token: write
|
|
105
|
+
attestations: write
|
|
106
|
+
steps:
|
|
107
|
+
- name: Check out adapter
|
|
108
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
109
|
+
- name: Download verified release artifacts
|
|
110
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
111
|
+
with:
|
|
112
|
+
name: riffdb-mlflow-release
|
|
113
|
+
path: release-out
|
|
114
|
+
- name: Attest build provenance
|
|
115
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
116
|
+
with:
|
|
117
|
+
subject-path: |
|
|
118
|
+
release-out/riffdb_mlflow-${{ needs.verify-and-build.outputs.version }}-py3-none-any.whl
|
|
119
|
+
release-out/riffdb_mlflow-${{ needs.verify-and-build.outputs.version }}.tar.gz
|
|
120
|
+
release-out/SHA256SUMS
|
|
121
|
+
- name: Publish GitHub Release
|
|
122
|
+
env:
|
|
123
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
124
|
+
run: |
|
|
125
|
+
tag="${{ needs.verify-and-build.outputs.release_tag }}"
|
|
126
|
+
version="${{ needs.verify-and-build.outputs.version }}"
|
|
127
|
+
notes="Compiled RiffDB tracking-store adapter for MLflow v${version} (pinned to RiffDB \`${{ needs.verify-and-build.outputs.revision }}\` and \`riffdb==${version}\`)."
|
|
128
|
+
if gh release view "$tag" >/dev/null 2>&1; then
|
|
129
|
+
gh release upload "$tag" release-out/* --clobber
|
|
130
|
+
else
|
|
131
|
+
gh release create "$tag" release-out/* \
|
|
132
|
+
--title "riffdb-mlflow ${tag}" \
|
|
133
|
+
--notes "$notes" \
|
|
134
|
+
--verify-tag
|
|
135
|
+
fi
|
|
136
|
+
- name: Publish to PyPI (when token or Trusted Publishing is configured)
|
|
137
|
+
env:
|
|
138
|
+
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
139
|
+
run: |
|
|
140
|
+
python3 -m pip install --quiet twine
|
|
141
|
+
if [[ -n "${PYPI_TOKEN:-}" ]]; then
|
|
142
|
+
python3 -m twine upload --non-interactive --skip-existing -u __token__ -p "$PYPI_TOKEN" release-out/*.whl release-out/*.tar.gz
|
|
143
|
+
else
|
|
144
|
+
python3 - <<'PY' || echo "::warning::PyPI Trusted Publishing or PYPI_TOKEN is not yet configured for riffdb-mlflow; GitHub Release wheel and sdist are published."
|
|
145
|
+
import json
|
|
146
|
+
import os
|
|
147
|
+
import subprocess
|
|
148
|
+
import sys
|
|
149
|
+
import urllib.request
|
|
150
|
+
|
|
151
|
+
req_url = os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"]
|
|
152
|
+
req_tok = os.environ["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
|
|
153
|
+
sep = "&" if "?" in req_url else "?"
|
|
154
|
+
req = urllib.request.Request(
|
|
155
|
+
f"{req_url}{sep}audience=pypi",
|
|
156
|
+
headers={"Authorization": f"Bearer {req_tok}", "Accept": "application/json; api-version=2.0"},
|
|
157
|
+
)
|
|
158
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
159
|
+
jwt = json.loads(resp.read().decode("utf-8"))["value"]
|
|
160
|
+
mint = urllib.request.Request(
|
|
161
|
+
"https://upload.pypi.org/_/oidc/mint-token",
|
|
162
|
+
data=json.dumps({"token": jwt}).encode("utf-8"),
|
|
163
|
+
headers={"Content-Type": "application/json", "Accept": "application/json"},
|
|
164
|
+
method="POST",
|
|
165
|
+
)
|
|
166
|
+
with urllib.request.urlopen(mint, timeout=30) as resp:
|
|
167
|
+
pypi_tok = json.loads(resp.read().decode("utf-8"))["token"]
|
|
168
|
+
import glob
|
|
169
|
+
pkgs = sorted(glob.glob("release-out/*.whl") + glob.glob("release-out/*.tar.gz"))
|
|
170
|
+
subprocess.run(
|
|
171
|
+
[sys.executable, "-m", "twine", "upload", "--non-interactive", "--skip-existing", "-u", "__token__", "-p", pypi_tok, *pkgs],
|
|
172
|
+
check=True,
|
|
173
|
+
)
|
|
174
|
+
PY
|
|
175
|
+
fi
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: RiffDB compatibility
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '23 4 * * *'
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
riffdb_ref:
|
|
11
|
+
description: 'Full RiffDB commit or main; empty uses the adapter pin'
|
|
12
|
+
required: false
|
|
13
|
+
default: ''
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: riffdb-compatibility-${{ github.ref }}-${{ github.event_name }}
|
|
20
|
+
cancel-in-progress: true
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
application-compatibility:
|
|
24
|
+
name: Application compatibility
|
|
25
|
+
runs-on: ubuntu-24.04
|
|
26
|
+
timeout-minutes: 30
|
|
27
|
+
env:
|
|
28
|
+
CARGO_INCREMENTAL: '0'
|
|
29
|
+
CARGO_TARGET_DIR: ${{ github.workspace }}/riffdb/target
|
|
30
|
+
steps:
|
|
31
|
+
- name: Check out adapter
|
|
32
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
33
|
+
with:
|
|
34
|
+
path: adapter
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- name: Select RiffDB revision
|
|
37
|
+
id: riffdb
|
|
38
|
+
env:
|
|
39
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
40
|
+
REQUESTED_REF: ${{ inputs.riffdb_ref }}
|
|
41
|
+
run: |
|
|
42
|
+
revision="$(cat adapter/.github/riffdb-revision)"
|
|
43
|
+
[[ "$revision" =~ ^[0-9a-f]{40}$ ]]
|
|
44
|
+
if [[ "$EVENT_NAME" == schedule ]]; then revision=main; fi
|
|
45
|
+
if [[ -n "$REQUESTED_REF" ]]; then revision="$REQUESTED_REF"; fi
|
|
46
|
+
[[ "$revision" =~ ^([0-9a-f]{40}|main)$ ]]
|
|
47
|
+
printf 'revision=%s\n' "$revision" >>"$GITHUB_OUTPUT"
|
|
48
|
+
- name: Check out RiffDB
|
|
49
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
50
|
+
with:
|
|
51
|
+
repository: riffdb/riffdb
|
|
52
|
+
ref: ${{ steps.riffdb.outputs.revision }}
|
|
53
|
+
path: riffdb
|
|
54
|
+
persist-credentials: false
|
|
55
|
+
- name: Install Rust toolchain
|
|
56
|
+
run: rustup toolchain install 1.98.1 --profile minimal
|
|
57
|
+
- name: Cache Cargo sources
|
|
58
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
59
|
+
with:
|
|
60
|
+
path: |
|
|
61
|
+
~/.cargo/registry
|
|
62
|
+
~/.cargo/git
|
|
63
|
+
key: cargo-compatibility-${{ runner.os }}-${{ hashFiles('riffdb/Cargo.lock') }}
|
|
64
|
+
- name: Build selected RiffDB CLI
|
|
65
|
+
working-directory: riffdb
|
|
66
|
+
run: cargo build --locked --release -p riffdb-cli --bin riffdb
|
|
67
|
+
- name: Check adapter application
|
|
68
|
+
working-directory: adapter
|
|
69
|
+
run: ../riffdb/target/release/riffdb application check
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# AGENTS.md — RiffDB MLflow adapter
|
|
2
|
+
|
|
3
|
+
## Mission
|
|
4
|
+
|
|
5
|
+
Implement a standalone Python tracking-store plugin for pinned MLflow releases
|
|
6
|
+
using a compiled RiffDB application. All MLflow-specific behavior belongs in
|
|
7
|
+
this repository, never in RiffDB.
|
|
8
|
+
|
|
9
|
+
## Authoritative inputs
|
|
10
|
+
|
|
11
|
+
- MLflow `v3.15.1`, especially `mlflow.store.tracking.AbstractStore` and the
|
|
12
|
+
upstream SQL tracking-store tests.
|
|
13
|
+
- This repository's `DESIGN.md` and `PLAN.md`.
|
|
14
|
+
- The accepted RiffDB specification and ADRs at the revision recorded in the
|
|
15
|
+
generated application lock and dependency receipt.
|
|
16
|
+
|
|
17
|
+
If these inputs conflict, stop and request maintainer review. Do not weaken an
|
|
18
|
+
MLflow semantic or a RiffDB safety guarantee to make a test pass.
|
|
19
|
+
|
|
20
|
+
## Architecture boundaries
|
|
21
|
+
|
|
22
|
+
1. MLflow mutations use generated, compiled RiffDB commands only.
|
|
23
|
+
2. Reads use generated named RiffQL queries only. Do not add generic CRUD,
|
|
24
|
+
dynamically assembled RiffQL, client-side filtering, or full-store scans.
|
|
25
|
+
3. One MLflow API mutation that is atomic in the SQL store remains one atomic
|
|
26
|
+
compiled command, including bounded metric/parameter/tag batches.
|
|
27
|
+
4. Search syntax is parsed and normalized according to MLflow. Every accepted
|
|
28
|
+
shape dispatches to a finite named-query family; unsupported shapes fail
|
|
29
|
+
before contacting RiffDB.
|
|
30
|
+
5. Pagination is bounded and continuation tokens are opaque, filter-bound, and
|
|
31
|
+
rejected on cross-query reuse.
|
|
32
|
+
6. MLflow metric values preserve their exact IEEE-754 payload and MLflow's
|
|
33
|
+
latest-metric ordering semantics, including NaN and infinities.
|
|
34
|
+
7. Artifact bytes remain in an MLflow artifact repository. This adapter stores
|
|
35
|
+
tracking metadata and never turns RiffDB into an artifact blob store.
|
|
36
|
+
8. Public exceptions map deliberately to MLflow error codes and never expose
|
|
37
|
+
RiffDB internals, credentials, protected values, or symbolic plan details.
|
|
38
|
+
|
|
39
|
+
## Stop conditions
|
|
40
|
+
|
|
41
|
+
Pause for a generic RiffDB capability or DX review if implementation would
|
|
42
|
+
require:
|
|
43
|
+
|
|
44
|
+
- splitting an atomic MLflow batch or lifecycle transition;
|
|
45
|
+
- an unbounded query, client-side filtering, sorting, or cursor walking used to
|
|
46
|
+
repair semantics;
|
|
47
|
+
- process-local time, IDs, ordering, locks, or leases for durable correctness;
|
|
48
|
+
- arbitrary transaction callbacks, raw query text, or direct storage access;
|
|
49
|
+
- target-language reimplementation of RiffDB transport guarantees; or
|
|
50
|
+
- MLflow-specific behavior, vocabulary, routes, or schemas in RiffDB.
|
|
51
|
+
|
|
52
|
+
## Completion checks
|
|
53
|
+
|
|
54
|
+
Before calling the adapter usable:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
make check
|
|
58
|
+
make conformance
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Unit fakes may supplement upstream tests but never replace real RiffDB
|
|
62
|
+
loopback coverage.
|
|
63
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
## Compatibility target
|
|
4
|
+
|
|
5
|
+
The first release pins MLflow `3.15.1` and registers the `riffdb` URI scheme in
|
|
6
|
+
the official `mlflow.tracking_store` entry-point group. The package implements
|
|
7
|
+
the tracking backend used for experiments, runs, metrics, parameters, tags,
|
|
8
|
+
inputs, and search. Artifact payloads remain in the artifact repository named
|
|
9
|
+
by MLflow's `artifact_uri` argument.
|
|
10
|
+
|
|
11
|
+
MLflow 3.15.1's tracking `AbstractStore` has sixteen abstract methods. The
|
|
12
|
+
compatibility gate records those methods and fails when a pinned upstream
|
|
13
|
+
upgrade changes the abstract surface. Concrete optional domains added by
|
|
14
|
+
MLflow—traces, assessments, evaluation datasets, scorers, review queues, and
|
|
15
|
+
similar GenAI metadata—remain explicitly unsupported until a profile and its
|
|
16
|
+
upstream tests are added. They must never silently fall back to another store.
|
|
17
|
+
|
|
18
|
+
The Python floor is 3.13, matching the published RiffDB Python driver rather
|
|
19
|
+
than MLflow's broader interpreter matrix.
|
|
20
|
+
|
|
21
|
+
## Core behavior
|
|
22
|
+
|
|
23
|
+
| Domain | Required behavior |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Experiments | Unique names, create/get/search, rename, soft delete/restore, tags, stable pages |
|
|
26
|
+
| Runs | Create/get/search, lifecycle updates, soft delete/restore, exact latest data |
|
|
27
|
+
| Metrics | Atomic bounded batches, full paged history, exact IEEE-754 values, MLflow latest-value tie-breaking |
|
|
28
|
+
| Parameters | Immutable per-run key/value binding and duplicate-conflict behavior |
|
|
29
|
+
| Tags | Last-write-wins set and exact deletion semantics |
|
|
30
|
+
| Inputs | Atomic dataset/model input associations with deterministic bounded encoding |
|
|
31
|
+
| Authorization | Per-experiment and per-run row policy through the compiled profile, never Python filtering |
|
|
32
|
+
|
|
33
|
+
The SQL store is the behavioral reference. RiffDB is not treated as SQL and
|
|
34
|
+
the adapter does not emulate SQL transactions; it exposes equivalent MLflow
|
|
35
|
+
semantics through compiled commands and named queries.
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
MLflow constructs the plugin with `(store_uri, artifact_uri)`. The canonical
|
|
40
|
+
tracking URI is `riffdb://<database>`. It contains no credential or TLS
|
|
41
|
+
material. Connection authority comes from protected process configuration:
|
|
42
|
+
|
|
43
|
+
- `RIFFDB_MLFLOW_ENDPOINT`
|
|
44
|
+
- `RIFFDB_MLFLOW_TRUST_ROOT`
|
|
45
|
+
- `RIFFDB_MLFLOW_SERVER_NAME`
|
|
46
|
+
- `RIFFDB_MLFLOW_CREDENTIAL_FILE`
|
|
47
|
+
|
|
48
|
+
The credential file is consumed through RiffDB's nonextractable protected-file
|
|
49
|
+
API. The database in the URI becomes `CallMetadata`'s database alias. Unknown
|
|
50
|
+
URI components, user information, ports, paths, query parameters, and fragments
|
|
51
|
+
fail before any transport work.
|
|
52
|
+
|
|
53
|
+
## IDs and time
|
|
54
|
+
|
|
55
|
+
MLflow-facing experiment and run IDs remain strings with the behavior required
|
|
56
|
+
by upstream tests. MLflow's FileStore establishes that experiment IDs may be
|
|
57
|
+
random fixed-width integers rather than SQL sequences, so the adapter generates
|
|
58
|
+
an 18-digit numeric ID and submits it to one atomic create command. Run IDs use
|
|
59
|
+
MLflow's UUID-shaped convention. Millisecond values supplied by MLflow remain
|
|
60
|
+
exact signed integers. Adapter wall-clock time never establishes durable
|
|
61
|
+
ordering, state transitions, or lease validity.
|
|
62
|
+
|
|
63
|
+
## Metric representation
|
|
64
|
+
|
|
65
|
+
An MLflow metric value is an IEEE-754 binary64, not a business decimal. The
|
|
66
|
+
adapter preserves its canonical 64-bit payload, including signed zero, NaN, and
|
|
67
|
+
infinities. It also derives a bytewise sortable key whose ordering is proven
|
|
68
|
+
against MLflow's numeric comparison behavior. The contract stores both values;
|
|
69
|
+
neither is rounded through decimal or JSON. Latest metrics are selected by
|
|
70
|
+
MLflow's `(step, timestamp, value)` precedence in a named query.
|
|
71
|
+
|
|
72
|
+
## Query compilation boundary
|
|
73
|
+
|
|
74
|
+
MLflow accepts a textual search grammar. Python parses that grammar using
|
|
75
|
+
MLflow's own parser and normalizes it into a closed internal shape. The shape
|
|
76
|
+
selects a generated named query; it never supplies field, operator, index,
|
|
77
|
+
order, or raw predicate text to RiffDB. Unsupported conjunctions, fields, or
|
|
78
|
+
orders fail with MLflow's invalid-parameter error.
|
|
79
|
+
|
|
80
|
+
Search result hydration must be bounded in the query or command result. N+1
|
|
81
|
+
reads, fetch-and-filter, adapter sorting, and offset simulation are prohibited.
|
|
82
|
+
|
|
83
|
+
## Error mapping
|
|
84
|
+
|
|
85
|
+
RiffDB application outcomes map to MLflow's documented error codes for missing
|
|
86
|
+
resources, duplicates, invalid states, invalid parameters, concurrency
|
|
87
|
+
conflicts, cancellation, deadline, and temporary unavailability. Unknown
|
|
88
|
+
transport or validation failures become a stable redacted internal error.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
UV ?= uv
|
|
2
|
+
PYTHON ?= $(UV) run python
|
|
3
|
+
RIFFDB ?= riffdb
|
|
4
|
+
|
|
5
|
+
.PHONY: bootstrap format lint typecheck test check conformance
|
|
6
|
+
|
|
7
|
+
bootstrap:
|
|
8
|
+
$(UV) sync
|
|
9
|
+
|
|
10
|
+
format:
|
|
11
|
+
$(UV) run ruff format --check .
|
|
12
|
+
|
|
13
|
+
lint:
|
|
14
|
+
$(UV) run ruff check .
|
|
15
|
+
|
|
16
|
+
typecheck:
|
|
17
|
+
$(UV) run mypy
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
$(UV) run pytest
|
|
21
|
+
|
|
22
|
+
check: format lint typecheck test
|
|
23
|
+
git diff --exit-code
|
|
24
|
+
|
|
25
|
+
conformance:
|
|
26
|
+
$(RIFFDB) dev --reset --exec '$(PYTHON) -m riffdb_mlflow "$$RIFFDB_ENDPOINT" "$$RIFFDB_CREDENTIAL_FILE" "$$RIFFDB_DATABASE"'
|
|
27
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: riffdb-mlflow
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Compiled RiffDB tracking-store adapter for MLflow
|
|
5
|
+
Author: RiffDB contributors
|
|
6
|
+
License: MIT OR Apache-2.0
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: mlflow==3.15.1
|
|
9
|
+
Requires-Dist: riffdb==0.2.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# riffdb-mlflow
|
|
13
|
+
|
|
14
|
+
`riffdb-mlflow` is a compiled RiffDB tracking-store plugin for MLflow. It is an
|
|
15
|
+
out-of-tree integration: no MLflow-specific code or vocabulary belongs in the
|
|
16
|
+
RiffDB repository.
|
|
17
|
+
|
|
18
|
+
The project targets **MLflow 3.15.1** and implements the complete `AbstractStore`
|
|
19
|
+
tracking, tracing, evaluation assessment, trace analytics, logged-model, and
|
|
20
|
+
run-lineage surface:
|
|
21
|
+
|
|
22
|
+
- **Experiments & Runs**: atomic experiment creation with tags, lifecycle and
|
|
23
|
+
tag updates, `exact`/`LIKE`/`ILIKE` experiment search, atomic run creation
|
|
24
|
+
with tags, run get/update/delete/restore, bounded atomic `log_batch`
|
|
25
|
+
(metrics, parameters, tags), `get_metric_history`, `log_inputs`, `log_outputs`,
|
|
26
|
+
and `record_logged_model`.
|
|
27
|
+
- **Traces & Out-of-Order Spans (`TraceInfo`, `Span`)**: `start_trace`,
|
|
28
|
+
`get_trace_info`, `batch_get_traces`, `log_spans`, `log_spans_async`,
|
|
29
|
+
`ingest_spans_out_of_order`, `search_traces`, `delete_traces`,
|
|
30
|
+
`set_trace_tag`, `delete_trace_tag`, `link_traces_to_run`,
|
|
31
|
+
`unlink_traces_from_run`, and `link_prompts_to_trace`. `IngestTraceSpans`
|
|
32
|
+
uses `init_or_mutate` across `Trace` and `TraceSpan` in one partition commit,
|
|
33
|
+
allowing child spans to arrive before parent/root spans and even before
|
|
34
|
+
`start_trace()` without foreign-key failures or read-time `FINAL` merges.
|
|
35
|
+
- **Assessments (`Feedback` & `Expectation`)**: `create_assessment`,
|
|
36
|
+
`get_assessment`, `update_assessment`, and `delete_assessment`.
|
|
37
|
+
- **Trace Metrics & Analytical Rollups (`query_trace_metrics`, `ADR-0315`)**:
|
|
38
|
+
backed by contract-declared scalar columnar projections (`TraceAnalytics`,
|
|
39
|
+
`SpanAnalytics`, `AssessmentAnalytics`) and event-driven rollups
|
|
40
|
+
(`SpanModelRollup` with `min`/`max`/`mean`/`sum`/`count` and `hour(...)`
|
|
41
|
+
temporal bucket keys) queried via `source projected` in `.riffq`.
|
|
42
|
+
- **Logged Models (`LoggedModel`)**: `create_logged_model`, `get_logged_model`,
|
|
43
|
+
`delete_logged_model`, `finalize_logged_model`, `set_logged_model_tags`,
|
|
44
|
+
`delete_logged_model_tag`, `log_logged_model_params`, and
|
|
45
|
+
`search_logged_models`.
|
|
46
|
+
|
|
47
|
+
The intended server configuration is:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
mlflow server \
|
|
51
|
+
--backend-store-uri riffdb://mlflow-development \
|
|
52
|
+
--default-artifact-root s3://example/mlflow-artifacts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
RiffDB TLS and credential material is supplied through protected environment
|
|
56
|
+
configuration described in [DESIGN.md](DESIGN.md); it is never embedded in the
|
|
57
|
+
tracking URI.
|
|
58
|
+
|
|
59
|
+
Development and verification:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv sync
|
|
63
|
+
make check
|
|
64
|
+
make conformance
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## RiffDB compatibility CI
|
|
69
|
+
|
|
70
|
+
This adapter owns its RiffDB compatibility checks. Pull requests compile the
|
|
71
|
+
application sources and verify existing locks and generated artifacts against
|
|
72
|
+
the full commit in `.github/riffdb-revision`. Scheduled checks use RiffDB
|
|
73
|
+
`main`; manual dispatch accepts a full commit or `main`. Failures are reported
|
|
74
|
+
here and do not block RiffDB itself. Framework conformance remains separate.
|
|
75
|
+
|
|
76
|
+
To upgrade, update the pin, migrate sources as needed, review the result of
|
|
77
|
+
`riffdb application lock --write`, and verify with `riffdb application check`.
|
|
78
|
+
Commit the pin and migration together in this repository.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Implementation plan
|
|
2
|
+
|
|
3
|
+
## WP-001 — Repository and upstream surface
|
|
4
|
+
|
|
5
|
+
Status: in progress.
|
|
6
|
+
|
|
7
|
+
- Pin MLflow `3.15.1` and record its exact source revision.
|
|
8
|
+
- Register the `riffdb` tracking-store URI scheme.
|
|
9
|
+
- Lock the sixteen-method abstract surface and inventory the SQL-store tests.
|
|
10
|
+
- Establish package, configuration, redaction, and stop boundaries.
|
|
11
|
+
|
|
12
|
+
Exit: a clean wheel discovers the plugin and upstream surface drift fails CI.
|
|
13
|
+
|
|
14
|
+
## WP-002 — Core contract and generated Python boundary
|
|
15
|
+
|
|
16
|
+
Status: pending.
|
|
17
|
+
|
|
18
|
+
- Model experiments, runs, metrics, latest metrics, parameters, tags, and
|
|
19
|
+
inputs with explicit bounds and indexes.
|
|
20
|
+
- Compile every mutation as a named command and every read as a named query.
|
|
21
|
+
- Declare application source V7 with only the Python generated surface.
|
|
22
|
+
- Generate and check the exact manifest, V8 lock, and Python client.
|
|
23
|
+
|
|
24
|
+
Exit: source/lock checks are exact and all core data paths are generated.
|
|
25
|
+
|
|
26
|
+
## WP-003 — Experiments and runs
|
|
27
|
+
|
|
28
|
+
Status: in progress. The base experiment and run lifecycle slice passes focused
|
|
29
|
+
unit tests and disposable real-RiffDB loopback; upstream SQL-store parity and
|
|
30
|
+
the remaining run-data operations are not yet complete.
|
|
31
|
+
|
|
32
|
+
- Implement create/get/rename/delete/restore for experiments.
|
|
33
|
+
- Implement create/get/update/delete/restore for runs.
|
|
34
|
+
- Preserve unique-name, lifecycle, random numeric ID, timestamp, and
|
|
35
|
+
public-error semantics.
|
|
36
|
+
- Add focused concurrency and retry tests over real loopback.
|
|
37
|
+
|
|
38
|
+
## WP-004 — Batches and run data
|
|
39
|
+
|
|
40
|
+
Status: pending.
|
|
41
|
+
|
|
42
|
+
- Implement atomic `log_batch` for metrics, parameters, and tags.
|
|
43
|
+
- Implement latest metric selection and paged metric history.
|
|
44
|
+
- Implement experiment/run tags and dataset/model inputs.
|
|
45
|
+
- Prove maximum legal batches, duplicate parameters, IEEE-754 edge cases, and
|
|
46
|
+
idempotent uncertainty recovery.
|
|
47
|
+
|
|
48
|
+
## WP-005 — Search and pagination
|
|
49
|
+
|
|
50
|
+
Status: in progress. Experiment search and lifecycle-aware run search use
|
|
51
|
+
compiled queries and bounded cursor coalescing. Remaining run predicates and
|
|
52
|
+
order families are still pending.
|
|
53
|
+
|
|
54
|
+
- Inventory the MLflow experiment/run search grammar and order matrix.
|
|
55
|
+
- Map accepted normalized shapes to finite bounded named queries.
|
|
56
|
+
- Bind continuation tokens to operation, normalized filter, order, authority,
|
|
57
|
+
and underlying RiffDB cursor.
|
|
58
|
+
- Prove pages without client filtering, sorting, or offset repair.
|
|
59
|
+
|
|
60
|
+
## WP-006 — Upstream and real-driver conformance
|
|
61
|
+
|
|
62
|
+
Status: pending.
|
|
63
|
+
|
|
64
|
+
- Run the applicable upstream SQL tracking-store semantics against RiffDB.
|
|
65
|
+
- Run an MLflow tracking server and public `MlflowClient` loopback suite.
|
|
66
|
+
- Compare correctness and cold/warm timing with PostgreSQL.
|
|
67
|
+
- Prove clean installation from an immutable local Python publication.
|
|
68
|
+
|
|
69
|
+
## WP-007 — Optional MLflow domains and release readiness
|
|
70
|
+
|
|
71
|
+
Status: deferred until the core tracking backend is green.
|
|
72
|
+
|
|
73
|
+
- Materialize separate reviewed profiles for optional newer MLflow domains.
|
|
74
|
+
- Add compatibility policy, CI, package provenance, and release metadata.
|
|
75
|
+
- Do not publish publicly until explicitly authorized.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# riffdb-mlflow
|
|
2
|
+
|
|
3
|
+
`riffdb-mlflow` is a compiled RiffDB tracking-store plugin for MLflow. It is an
|
|
4
|
+
out-of-tree integration: no MLflow-specific code or vocabulary belongs in the
|
|
5
|
+
RiffDB repository.
|
|
6
|
+
|
|
7
|
+
The project targets **MLflow 3.15.1** and implements the complete `AbstractStore`
|
|
8
|
+
tracking, tracing, evaluation assessment, trace analytics, logged-model, and
|
|
9
|
+
run-lineage surface:
|
|
10
|
+
|
|
11
|
+
- **Experiments & Runs**: atomic experiment creation with tags, lifecycle and
|
|
12
|
+
tag updates, `exact`/`LIKE`/`ILIKE` experiment search, atomic run creation
|
|
13
|
+
with tags, run get/update/delete/restore, bounded atomic `log_batch`
|
|
14
|
+
(metrics, parameters, tags), `get_metric_history`, `log_inputs`, `log_outputs`,
|
|
15
|
+
and `record_logged_model`.
|
|
16
|
+
- **Traces & Out-of-Order Spans (`TraceInfo`, `Span`)**: `start_trace`,
|
|
17
|
+
`get_trace_info`, `batch_get_traces`, `log_spans`, `log_spans_async`,
|
|
18
|
+
`ingest_spans_out_of_order`, `search_traces`, `delete_traces`,
|
|
19
|
+
`set_trace_tag`, `delete_trace_tag`, `link_traces_to_run`,
|
|
20
|
+
`unlink_traces_from_run`, and `link_prompts_to_trace`. `IngestTraceSpans`
|
|
21
|
+
uses `init_or_mutate` across `Trace` and `TraceSpan` in one partition commit,
|
|
22
|
+
allowing child spans to arrive before parent/root spans and even before
|
|
23
|
+
`start_trace()` without foreign-key failures or read-time `FINAL` merges.
|
|
24
|
+
- **Assessments (`Feedback` & `Expectation`)**: `create_assessment`,
|
|
25
|
+
`get_assessment`, `update_assessment`, and `delete_assessment`.
|
|
26
|
+
- **Trace Metrics & Analytical Rollups (`query_trace_metrics`, `ADR-0315`)**:
|
|
27
|
+
backed by contract-declared scalar columnar projections (`TraceAnalytics`,
|
|
28
|
+
`SpanAnalytics`, `AssessmentAnalytics`) and event-driven rollups
|
|
29
|
+
(`SpanModelRollup` with `min`/`max`/`mean`/`sum`/`count` and `hour(...)`
|
|
30
|
+
temporal bucket keys) queried via `source projected` in `.riffq`.
|
|
31
|
+
- **Logged Models (`LoggedModel`)**: `create_logged_model`, `get_logged_model`,
|
|
32
|
+
`delete_logged_model`, `finalize_logged_model`, `set_logged_model_tags`,
|
|
33
|
+
`delete_logged_model_tag`, `log_logged_model_params`, and
|
|
34
|
+
`search_logged_models`.
|
|
35
|
+
|
|
36
|
+
The intended server configuration is:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
mlflow server \
|
|
40
|
+
--backend-store-uri riffdb://mlflow-development \
|
|
41
|
+
--default-artifact-root s3://example/mlflow-artifacts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
RiffDB TLS and credential material is supplied through protected environment
|
|
45
|
+
configuration described in [DESIGN.md](DESIGN.md); it is never embedded in the
|
|
46
|
+
tracking URI.
|
|
47
|
+
|
|
48
|
+
Development and verification:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv sync
|
|
52
|
+
make check
|
|
53
|
+
make conformance
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## RiffDB compatibility CI
|
|
58
|
+
|
|
59
|
+
This adapter owns its RiffDB compatibility checks. Pull requests compile the
|
|
60
|
+
application sources and verify existing locks and generated artifacts against
|
|
61
|
+
the full commit in `.github/riffdb-revision`. Scheduled checks use RiffDB
|
|
62
|
+
`main`; manual dispatch accepts a full commit or `main`. Failures are reported
|
|
63
|
+
here and do not block RiffDB itself. Framework conformance remains separate.
|
|
64
|
+
|
|
65
|
+
To upgrade, update the pin, migrate sources as needed, review the result of
|
|
66
|
+
`riffdb application lock --write`, and verify with `riffdb application check`.
|
|
67
|
+
Commit the pin and migration together in this repository.
|