Skip to content

Commit 79a3454

Browse files
committed
refactor: remove use_metadata_tags config option from MLflow provider
The use_metadata_tags configuration option has been removed to simplify the MLflow provider API and ensure optimal performance. Metadata tags are now always used for efficient prompt filtering and discovery. Changes: - Remove use_metadata_tags field from MLflowPromptsConfig - Update PromptIDMapper to always generate metadata tags - Remove conditional logic in list_prompts() - always uses tag filtering - Update unit and integration tests - Update documentation to remove references to use_metadata_tags - Add GitHub Actions workflow for MLflow integration tests Signed-off-by: William Caban <[email protected]>
1 parent 8f150ec commit 79a3454

File tree

8 files changed

+133
-53
lines changed

8 files changed

+133
-53
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: MLflow Prompts Integration Tests
2+
3+
run-name: Run the integration test suite with MLflow Prompt Registry provider
4+
5+
on:
6+
push:
7+
branches:
8+
- main
9+
- 'release-[0-9]+.[0-9]+.x'
10+
pull_request:
11+
branches:
12+
- main
13+
- 'release-[0-9]+.[0-9]+.x'
14+
paths:
15+
- 'src/llama_stack/providers/remote/prompts/mlflow/**'
16+
- 'tests/integration/providers/remote/prompts/mlflow/**'
17+
- 'tests/unit/providers/remote/prompts/mlflow/**'
18+
- 'uv.lock'
19+
- 'pyproject.toml'
20+
- 'requirements.txt'
21+
- '.github/workflows/integration-mlflow-tests.yml' # This workflow
22+
schedule:
23+
- cron: '0 0 * * *' # Daily at 12 AM UTC
24+
25+
concurrency:
26+
group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}
27+
cancel-in-progress: true
28+
29+
jobs:
30+
test-mlflow:
31+
runs-on: ubuntu-latest
32+
strategy:
33+
matrix:
34+
python-version: ${{ github.event.schedule == '0 0 * * *' && fromJSON('["3.12", "3.13"]') || fromJSON('["3.12"]') }}
35+
fail-fast: false
36+
37+
steps:
38+
- name: Checkout repository
39+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
40+
41+
- name: Install dependencies
42+
uses: ./.github/actions/setup-runner
43+
with:
44+
python-version: ${{ matrix.python-version }}
45+
46+
- name: Setup MLflow Server
47+
run: |
48+
docker run --rm -d --pull always \
49+
--name mlflow \
50+
-p 5555:5555 \
51+
ghcr.io/mlflow/mlflow:latest \
52+
mlflow server \
53+
--host 0.0.0.0 \
54+
--port 5555 \
55+
--backend-store-uri sqlite:///mlflow.db \
56+
--default-artifact-root ./mlruns
57+
58+
- name: Wait for MLflow to be ready
59+
run: |
60+
echo "Waiting for MLflow to be ready..."
61+
for i in {1..60}; do
62+
if curl -s http://localhost:5555/health | grep -q '"status": "OK"'; then
63+
echo "MLflow is ready!"
64+
exit 0
65+
fi
66+
echo "Not ready yet... ($i/60)"
67+
sleep 2
68+
done
69+
echo "MLflow failed to start"
70+
docker logs mlflow
71+
exit 1
72+
73+
- name: Verify MLflow API
74+
run: |
75+
echo "Testing MLflow API..."
76+
curl -X GET http://localhost:5555/api/2.0/mlflow/experiments/list
77+
echo ""
78+
echo "MLflow API is responding!"
79+
80+
- name: Build Llama Stack
81+
run: |
82+
uv run --no-sync llama stack list-deps ci-tests | xargs -L1 uv pip install
83+
84+
- name: Install MLflow Python client
85+
run: |
86+
uv pip install 'mlflow>=3.4.0'
87+
88+
- name: Check Storage and Memory Available Before Tests
89+
if: ${{ always() }}
90+
run: |
91+
free -h
92+
df -h
93+
94+
- name: Run MLflow Integration Tests
95+
env:
96+
MLFLOW_TRACKING_URI: http://localhost:5555
97+
run: |
98+
uv run --no-sync \
99+
pytest -sv \
100+
tests/integration/providers/remote/prompts/mlflow/
101+
102+
- name: Check Storage and Memory Available After Tests
103+
if: ${{ always() }}
104+
run: |
105+
free -h
106+
df -h
107+
108+
- name: Write MLflow logs to file
109+
if: ${{ always() }}
110+
run: |
111+
docker logs mlflow > mlflow.log 2>&1 || true
112+
113+
- name: Upload all logs to artifacts
114+
if: ${{ always() }}
115+
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
116+
with:
117+
name: mlflow-logs-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.python-version }}
118+
path: |
119+
*.log
120+
retention-days: 1
121+
122+
- name: Stop MLflow container
123+
if: ${{ always() }}
124+
run: |
125+
docker stop mlflow || true

docs/docs/providers/prompts/remote_mlflow.mdx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ prompts:
155155
config:
156156
mlflow_tracking_uri: https://mlflow.example.com
157157
experiment_name: team-prompts
158-
use_metadata_tags: true
159158
timeout_seconds: 45
160159
```
161160
@@ -732,7 +731,6 @@ See [MLflow's documentation](https://mlflow.org/docs/latest/prompts.html) for mo
732731
| `mlflow_registry_uri` | `str \| None` | No | None | MLflow model registry URI (defaults to tracking URI if not set) |
733732
| `experiment_name` | `str` | No | llama-stack-prompts | MLflow experiment name for storing prompts |
734733
| `auth_credential` | `SecretStr \| None` | No | None | MLflow API token for authentication. Can be overridden via provider data header. |
735-
| `use_metadata_tags` | `bool` | No | True | Whether to store Llama Stack metadata as MLflow tags |
736734
| `timeout_seconds` | `int` | No | 30 | Timeout for MLflow API calls (1-300 seconds) |
737735

738736
## Sample Configuration
@@ -741,7 +739,6 @@ See [MLflow's documentation](https://mlflow.org/docs/latest/prompts.html) for mo
741739
```yaml
742740
mlflow_tracking_uri: http://localhost:5555
743741
experiment_name: llama-stack-prompts
744-
use_metadata_tags: true
745742
timeout_seconds: 30
746743
```
747744

@@ -750,6 +747,5 @@ timeout_seconds: 30
750747
mlflow_tracking_uri: ${env.MLFLOW_TRACKING_URI:=http://localhost:5000}
751748
experiment_name: llama-stack-prompts
752749
auth_credential: ${env.MLFLOW_TRACKING_TOKEN:=}
753-
use_metadata_tags: true
754750
timeout_seconds: 30
755751
```

src/llama_stack/providers/remote/prompts/mlflow/config.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ class MLflowPromptsConfig(BaseModel):
4545
mlflow_registry_uri: MLflow registry URI (optional, defaults to tracking_uri)
4646
experiment_name: MLflow experiment name for prompt storage
4747
auth_credential: MLflow API token for authentication (optional, can be overridden by provider data)
48-
use_metadata_tags: Store Llama Stack metadata in MLflow tags (default: True)
4948
timeout_seconds: Timeout for MLflow API calls in seconds (default: 30)
5049
"""
5150

@@ -65,10 +64,6 @@ class MLflowPromptsConfig(BaseModel):
6564
default=None,
6665
description="MLflow API token for authentication. Can be overridden via provider data header.",
6766
)
68-
use_metadata_tags: bool = Field(
69-
default=True,
70-
description="Store Llama Stack metadata (prompt_id, variables) in MLflow tags",
71-
)
7267
timeout_seconds: int = Field(
7368
default=30,
7469
ge=1,

src/llama_stack/providers/remote/prompts/mlflow/mapping.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ class PromptIDMapper:
3131
# Prefix for MLflow prompt names managed by Llama Stack
3232
MLFLOW_NAME_PREFIX = "llama_prompt_"
3333

34-
def __init__(self, use_metadata: bool = True):
34+
def __init__(self):
3535
"""Initialize ID mapper.
3636
37-
Args:
38-
use_metadata: Store Llama Stack ID in MLflow tags for discoverability
37+
Llama Stack metadata is always stored in MLflow tags for efficient
38+
filtering and prompt discovery.
3939
"""
40-
self.use_metadata = use_metadata
40+
pass
4141

4242
def to_mlflow_name(self, prompt_id: str) -> str:
4343
"""Convert Llama Stack prompt_id to MLflow prompt name.
@@ -119,9 +119,6 @@ def get_metadata_tags(self, prompt_id: str, variables: list[str] | None = None)
119119
>>> tags
120120
{"llama_stack_id": "pmpt_abc123...", "llama_stack_managed": "true", "variables": "var1,var2"}
121121
"""
122-
if not self.use_metadata:
123-
return {}
124-
125122
tags = {
126123
"llama_stack_id": prompt_id,
127124
"llama_stack_managed": "true",

src/llama_stack/providers/remote/prompts/mlflow/mlflow.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self, config: MLflowPromptsConfig):
6161
"""
6262
self.config = config
6363
self.mlflow_client: "MlflowClient | None" = None
64-
self.mapper = PromptIDMapper(use_metadata=config.use_metadata_tags)
64+
self.mapper = PromptIDMapper()
6565
logger.info(
6666
f"MLflowPromptsAdapter initialized: tracking_uri={config.mlflow_tracking_uri}, "
6767
f"experiment={config.experiment_name}"
@@ -416,25 +416,14 @@ async def list_prompts(self) -> ListPromptsResponse:
416416
(those with llama_stack_managed=true tag)
417417
"""
418418
try:
419-
# Search for Llama Stack managed prompts
420-
if self.config.use_metadata_tags:
421-
prompts = mlflow.genai.search_prompts(filter_string="tag.llama_stack_managed='true'")
422-
else:
423-
# If not using metadata, search for prompts with llama_prompt_ prefix
424-
# Note: MLflow search_prompts may not support name prefix filtering
425-
# In this case, we need to list all and filter
426-
prompts = mlflow.genai.search_prompts()
419+
# Search for Llama Stack managed prompts using metadata tags
420+
prompts = mlflow.genai.search_prompts(filter_string="tag.llama_stack_managed='true'")
427421
except Exception as e:
428422
logger.error(f"Failed to search prompts in MLflow: {e}")
429423
return ListPromptsResponse(data=[])
430424

431425
llama_prompts = []
432426
for mlflow_prompt in prompts:
433-
# Filter by name prefix if not using metadata
434-
if not self.config.use_metadata_tags:
435-
if not mlflow_prompt.name.startswith(self.mapper.MLFLOW_NAME_PREFIX):
436-
continue
437-
438427
try:
439428
# Convert MLflow name to Llama Stack ID
440429
prompt_id = self.mapper.to_llama_id(mlflow_prompt.name)

tests/integration/providers/remote/prompts/mlflow/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ async def mlflow_config(mlflow_tracking_uri, mlflow_server_available):
6666
return MLflowPromptsConfig(
6767
mlflow_tracking_uri=mlflow_tracking_uri,
6868
experiment_name="test-llama-stack-prompts",
69-
use_metadata_tags=True,
7069
timeout_seconds=30,
7170
)
7271

tests/unit/providers/remote/prompts/mlflow/test_config.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def test_default_config(self):
2525
assert config.mlflow_tracking_uri == "http://localhost:5000"
2626
assert config.mlflow_registry_uri is None
2727
assert config.experiment_name == "llama-stack-prompts"
28-
assert config.use_metadata_tags is True
2928
assert config.timeout_seconds == 30
3029

3130
def test_custom_config(self):
@@ -34,14 +33,12 @@ def test_custom_config(self):
3433
mlflow_tracking_uri="http://mlflow.example.com:8080",
3534
mlflow_registry_uri="http://registry.example.com:8080",
3635
experiment_name="my-prompts",
37-
use_metadata_tags=False,
3836
timeout_seconds=60,
3937
)
4038

4139
assert config.mlflow_tracking_uri == "http://mlflow.example.com:8080"
4240
assert config.mlflow_registry_uri == "http://registry.example.com:8080"
4341
assert config.experiment_name == "my-prompts"
44-
assert config.use_metadata_tags is False
4542
assert config.timeout_seconds == 60
4643

4744
def test_databricks_uri(self):
@@ -113,14 +110,6 @@ def test_registry_uri_defaults_to_none(self):
113110
config = MLflowPromptsConfig()
114111
assert config.mlflow_registry_uri is None
115112

116-
def test_use_metadata_tags_boolean(self):
117-
"""Test use_metadata_tags accepts boolean values."""
118-
config_true = MLflowPromptsConfig(use_metadata_tags=True)
119-
assert config_true.use_metadata_tags is True
120-
121-
config_false = MLflowPromptsConfig(use_metadata_tags=False)
122-
assert config_false.use_metadata_tags is False
123-
124113

125114
class TestMLflowProviderDataValidator:
126115
"""Tests for MLflowProviderDataValidator."""

tests/unit/providers/remote/prompts/mlflow/test_mapping.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class TestPromptIDMapper:
1717
@pytest.fixture
1818
def mapper(self):
1919
"""Create ID mapper instance."""
20-
return PromptIDMapper(use_metadata=True)
20+
return PromptIDMapper()
2121

2222
def test_to_mlflow_name_valid_id(self, mapper):
2323
"""Test converting valid prompt_id to MLflow name."""
@@ -148,16 +148,6 @@ def test_get_metadata_tags_empty_variables(self, mapper):
148148
assert tags["llama_stack_managed"] == "true"
149149
assert "variables" not in tags
150150

151-
def test_get_metadata_tags_disabled(self):
152-
"""Test metadata tags returns empty dict when disabled."""
153-
mapper = PromptIDMapper(use_metadata=False)
154-
prompt_id = "pmpt_" + "f" * 48
155-
variables = ["var1"]
156-
157-
tags = mapper.get_metadata_tags(prompt_id, variables)
158-
159-
assert tags == {}
160-
161151
def test_extract_variables_from_tags(self, mapper):
162152
"""Test extracting variables from tags."""
163153
tags = {"variables": "var1,var2,var3"}

0 commit comments

Comments
 (0)