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.
Files changed (61) hide show
  1. riffdb_mlflow-0.2.0/.github/riffdb-revision +1 -0
  2. riffdb_mlflow-0.2.0/.github/workflows/release.yml +175 -0
  3. riffdb_mlflow-0.2.0/.github/workflows/riffdb-compatibility.yml +69 -0
  4. riffdb_mlflow-0.2.0/.gitignore +9 -0
  5. riffdb_mlflow-0.2.0/AGENTS.md +63 -0
  6. riffdb_mlflow-0.2.0/DESIGN.md +88 -0
  7. riffdb_mlflow-0.2.0/Makefile +27 -0
  8. riffdb_mlflow-0.2.0/PKG-INFO +78 -0
  9. riffdb_mlflow-0.2.0/PLAN.md +75 -0
  10. riffdb_mlflow-0.2.0/README.md +67 -0
  11. riffdb_mlflow-0.2.0/benchmarks/bench_out_of_order_spans.py +359 -0
  12. riffdb_mlflow-0.2.0/generated/riffdb.application.exact.json +1 -0
  13. riffdb_mlflow-0.2.0/generated/riffdb.contract.bundle +0 -0
  14. riffdb_mlflow-0.2.0/pyproject.toml +47 -0
  15. riffdb_mlflow-0.2.0/riffdb/contract.riff +1176 -0
  16. riffdb_mlflow-0.2.0/riffdb/queries/aggregate_span_analytics_by_model.riffq +48 -0
  17. riffdb_mlflow-0.2.0/riffdb/queries/aggregate_span_analytics_by_name_status.riffq +46 -0
  18. riffdb_mlflow-0.2.0/riffdb/queries/aggregate_trace_analytics_by_name.riffq +45 -0
  19. riffdb_mlflow-0.2.0/riffdb/queries/aggregate_trace_analytics_by_status.riffq +45 -0
  20. riffdb_mlflow-0.2.0/riffdb/queries/batch_get_trace_assessments.riffq +22 -0
  21. riffdb_mlflow-0.2.0/riffdb/queries/batch_get_trace_spans.riffq +20 -0
  22. riffdb_mlflow-0.2.0/riffdb/queries/batch_get_traces.riffq +40 -0
  23. riffdb_mlflow-0.2.0/riffdb/queries/get_experiment.riffq +24 -0
  24. riffdb_mlflow-0.2.0/riffdb/queries/get_experiment_by_name.riffq +24 -0
  25. riffdb_mlflow-0.2.0/riffdb/queries/get_logged_model.riffq +19 -0
  26. riffdb_mlflow-0.2.0/riffdb/queries/get_metric_history.riffq +24 -0
  27. riffdb_mlflow-0.2.0/riffdb/queries/get_run.riffq +18 -0
  28. riffdb_mlflow-0.2.0/riffdb/queries/get_run_lineage.riffq +16 -0
  29. riffdb_mlflow-0.2.0/riffdb/queries/get_trace.riffq +49 -0
  30. riffdb_mlflow-0.2.0/riffdb/queries/query_assessment_analytics.riffq +21 -0
  31. riffdb_mlflow-0.2.0/riffdb/queries/query_span_analytics.riffq +23 -0
  32. riffdb_mlflow-0.2.0/riffdb/queries/query_span_model_rollup.riffq +30 -0
  33. riffdb_mlflow-0.2.0/riffdb/queries/query_trace_analytics.riffq +24 -0
  34. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments.riffq +50 -0
  35. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name.riffq +45 -0
  36. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name_ilike.riffq +45 -0
  37. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_name_like.riffq +45 -0
  38. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag.riffq +48 -0
  39. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag_ilike.riffq +48 -0
  40. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_tag_like.riffq +48 -0
  41. riffdb_mlflow-0.2.0/riffdb/queries/search_experiments_by_two_tags.riffq +56 -0
  42. riffdb_mlflow-0.2.0/riffdb/queries/search_logged_models.riffq +21 -0
  43. riffdb_mlflow-0.2.0/riffdb/queries/search_runs.riffq +21 -0
  44. riffdb_mlflow-0.2.0/riffdb/queries/search_runs_by_lifecycle.riffq +23 -0
  45. riffdb_mlflow-0.2.0/riffdb/queries/search_traces.riffq +26 -0
  46. riffdb_mlflow-0.2.0/riffdb.application.json +121 -0
  47. riffdb_mlflow-0.2.0/riffdb.application.lock.json +1 -0
  48. riffdb_mlflow-0.2.0/riffdb.toml +8 -0
  49. riffdb_mlflow-0.2.0/src/riffdb_mlflow/__init__.py +8 -0
  50. riffdb_mlflow-0.2.0/src/riffdb_mlflow/__main__.py +1133 -0
  51. riffdb_mlflow-0.2.0/src/riffdb_mlflow/_extended_store.py +3081 -0
  52. riffdb_mlflow-0.2.0/src/riffdb_mlflow/config.py +38 -0
  53. riffdb_mlflow-0.2.0/src/riffdb_mlflow/generated/client.py +6277 -0
  54. riffdb_mlflow-0.2.0/src/riffdb_mlflow/py.typed +1 -0
  55. riffdb_mlflow-0.2.0/src/riffdb_mlflow/store.py +1632 -0
  56. riffdb_mlflow-0.2.0/tests/test_config.py +29 -0
  57. riffdb_mlflow-0.2.0/tests/test_experiments.py +290 -0
  58. riffdb_mlflow-0.2.0/tests/test_runs.py +474 -0
  59. riffdb_mlflow-0.2.0/tests/test_surface.py +87 -0
  60. riffdb_mlflow-0.2.0/tests/test_traces_and_analytics.py +1112 -0
  61. 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,9 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .mypy_cache/
4
+ .ruff_cache/
5
+ __pycache__/
6
+ *.py[cod]
7
+ build/
8
+ dist/
9
+ *.egg-info/
@@ -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.