Skip to content

Commit 1675a10

Browse files
feat: add USE_MODEL_REGISTRY env var and run.sh for CI
Introduces an explicit USE_MODEL_REGISTRY flag to decouple model source from Opik tracing. When unset (default), distilgpt2 loads from HuggingFace so CI can run without CometML registry credentials while still sending real traces to Opik. Both the script and notebook are updated to match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8f91cb3 commit 1675a10

3 files changed

Lines changed: 61 additions & 73 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
export OPIK_PROJECT_NAME="tracing-finetuned-models"
5+
6+
# Set USE_MODEL_REGISTRY=true and export COMET_API_KEY + COMET_WORKSPACE to
7+
# download the fine-tuned model from the CometML registry instead of HuggingFace.
8+
# export USE_MODEL_REGISTRY=true
9+
10+
uv sync
11+
12+
# Runs inference tracing only. train_and_register.py requires GPU and is excluded from CI.
13+
uv run python use_registered_model.py

guides/tracing_finetuned_models/tracing_finetuned_models.ipynb

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@
1919
"```\n",
2020
"Section 1 Section 2\n",
2121
"train_and_register fetch_and_trace\n",
22-
" \u2502 \u2502\n",
23-
" \u2502 fine-tunes distilgpt2 \u2502 fetches registered model\n",
24-
" \u2502 logs checkpoints \u2502 runs Opik-traced inference\n",
25-
" \u25bc \u25bc\n",
26-
" CometML Model Registry \u2500\u2500\u2500\u2500\u2500\u2500\u25ba Opik trace\n",
22+
" \n",
23+
" fine-tunes distilgpt2 fetches registered model\n",
24+
" logs checkpoints runs Opik-traced inference\n",
25+
" \n",
26+
" CometML Model Registry ──────► Opik trace\n",
2727
" sft-distilgpt2 v1.0.0 metadata.model_registry_url\n",
2828
"```\n",
2929
"\n",
3030
"**Already have a registered model?** Skip straight to [Section 2](#section-2).\n",
3131
"\n",
3232
"---\n",
3333
"\n",
34-
"**Recommended runtime:** GPU (T4 or better). In Colab: *Runtime \u2192 Change runtime type \u2192 T4 GPU*."
34+
"**Recommended runtime:** GPU (T4 or better). In Colab: *Runtime Change runtime type T4 GPU*."
3535
],
3636
"outputs": [],
3737
"execution_count": null
@@ -61,7 +61,7 @@
6161
"id": "cell-restart-note",
6262
"metadata": {},
6363
"source": [
64-
"> **Note:** If you just installed the packages for the first time, restart the Colab runtime now (*Runtime \u2192 Restart session*) then run all cells again from the top."
64+
"> **Note:** If you just installed the packages for the first time, restart the Colab runtime now (*Runtime Restart session*) then run all cells again from the top."
6565
],
6666
"outputs": [],
6767
"execution_count": null
@@ -72,7 +72,7 @@
7272
"id": "cell-credentials",
7373
"metadata": {},
7474
"outputs": [],
75-
"source": "import os\nimport getpass\n\n# CometML and Opik share the same workspace and API key on the Comet platform.\n# Locally: export COMET_API_KEY and COMET_WORKSPACE before running.\nAPI_KEY = os.environ.get('COMET_API_KEY', '') or getpass.getpass('Enter your Comet API key: ')\nWORKSPACE = os.environ.get('COMET_WORKSPACE', '') or getpass.getpass('Enter your Comet workspace: ')\n\n# Propagate to both SDKs\nos.environ['COMET_API_KEY'] = API_KEY\nos.environ['COMET_WORKSPACE'] = WORKSPACE\nos.environ['OPIK_API_KEY'] = API_KEY\nos.environ['OPIK_WORKSPACE'] = WORKSPACE\n\nCOMET_API_KEY = API_KEY\nCOMET_WORKSPACE = WORKSPACE\nOPIK_API_KEY = API_KEY\nOPIK_WORKSPACE = WORKSPACE\n\n# Update these to match your use case.\nREGISTRY_NAME = 'sft-distilgpt2'\nMODEL_VERSION = '1.0.0'\nOPIK_PROJECT = 'tracing-finetuned-models'\n\nprint(f'Workspace : {WORKSPACE or \"(not set)\"}')"
75+
"source": "import os\nimport getpass\n\n# CometML and Opik share the same workspace and API key on the Comet platform.\n# Locally: export COMET_API_KEY and COMET_WORKSPACE before running.\nAPI_KEY = os.environ.get('COMET_API_KEY', '') or getpass.getpass('Enter your Comet API key: ')\nWORKSPACE = os.environ.get('COMET_WORKSPACE', '') or getpass.getpass('Enter your Comet workspace: ')\n\n# Propagate to both SDKs\nos.environ['COMET_API_KEY'] = API_KEY\nos.environ['COMET_WORKSPACE'] = WORKSPACE\nos.environ['OPIK_API_KEY'] = API_KEY\nos.environ['OPIK_WORKSPACE'] = WORKSPACE\n\nCOMET_API_KEY = API_KEY\nCOMET_WORKSPACE = WORKSPACE\nOPIK_API_KEY = API_KEY\nOPIK_WORKSPACE = WORKSPACE\n\n# Set to True to download the fine-tuned model from the CometML registry.\n# False (default) loads distilgpt2 directly from HuggingFace — Opik tracing still runs.\nUSE_MODEL_REGISTRY = os.environ.get('USE_MODEL_REGISTRY', 'false').lower() in ('1', 'true', 'yes')\n\n# Update these to match your registered model when USE_MODEL_REGISTRY=True.\nREGISTRY_NAME = 'sft-distilgpt2'\nMODEL_VERSION = '1.0.0'\nOPIK_PROJECT = 'tracing-finetuned-models'\n\nprint(f'Workspace : {WORKSPACE or \"(not set)\"}')\nprint(f'USE_MODEL_REGISTRY : {USE_MODEL_REGISTRY}')"
7676
},
7777
{
7878
"cell_type": "markdown",
@@ -110,7 +110,7 @@
110110
"source": [
111111
"# comet_ml MUST be imported first.\n",
112112
"os.environ['COMET_API_KEY'] = COMET_API_KEY or ''\n",
113-
"import comet_ml # noqa: F401 \u2014 triggers auto-integration with Trainer"
113+
"import comet_ml # noqa: F401 triggers auto-integration with Trainer"
114114
]
115115
},
116116
{
@@ -196,7 +196,7 @@
196196
" metadata={'epoch': epoch},\n",
197197
" )\n",
198198
" shutil.rmtree(checkpoint_path)\n",
199-
" print(f' \u2192 Logged checkpoint_epoch_{epoch} to CometML')"
199+
" print(f' Logged checkpoint_epoch_{epoch} to CometML')"
200200
]
201201
},
202202
{
@@ -274,7 +274,7 @@
274274
" print(f' REGISTRY_NAME = \"{REGISTRY_NAME}\"')\n",
275275
" print(f' MODEL_VERSION = \"{MODEL_VERSION}\"')\n",
276276
"else:\n",
277-
" print('No active CometML experiment \u2014 check that COMET_API_KEY is set.')"
277+
" print('No active CometML experiment check that COMET_API_KEY is set.')"
278278
]
279279
},
280280
{
@@ -299,7 +299,7 @@
299299
"id": "cell-download",
300300
"metadata": {},
301301
"outputs": [],
302-
"source": "import comet_ml\n\nMODEL_LOCAL_DIR = './downloaded_model'\n\nif COMET_API_KEY and COMET_WORKSPACE:\n api = comet_ml.API(api_key=COMET_API_KEY)\n print(f'Downloading {REGISTRY_NAME} v{MODEL_VERSION} from CometML Model Registry...')\n registered_model = api.get_model(workspace=COMET_WORKSPACE, model_name=REGISTRY_NAME)\n registered_model.download(version=MODEL_VERSION, output_folder=MODEL_LOCAL_DIR, expand=True)\n model_source = MODEL_LOCAL_DIR\n print(f'Downloaded to {MODEL_LOCAL_DIR}')\nelse:\n print('[No credentials] Loading distilgpt2 from HuggingFace as a stand-in.')\n model_source = 'distilgpt2'"
302+
"source": "import comet_ml\n\nMODEL_LOCAL_DIR = './downloaded_model'\n\nif USE_MODEL_REGISTRY:\n api = comet_ml.API(api_key=COMET_API_KEY)\n print(f'Downloading {REGISTRY_NAME} v{MODEL_VERSION} from CometML Model Registry...')\n registered_model = api.get_model(workspace=COMET_WORKSPACE, model_name=REGISTRY_NAME)\n registered_model.download(version=MODEL_VERSION, output_folder=MODEL_LOCAL_DIR, expand=True)\n model_source = MODEL_LOCAL_DIR\n print(f'Downloaded to {MODEL_LOCAL_DIR}')\nelse:\n print('USE_MODEL_REGISTRY not set — loading distilgpt2 from HuggingFace.')\n print('Set USE_MODEL_REGISTRY=true (+ COMET_API_KEY / COMET_WORKSPACE) to use a registered model.')\n model_source = 'distilgpt2'"
303303
},
304304
{
305305
"cell_type": "code",
@@ -338,32 +338,7 @@
338338
"id": "cell-generate",
339339
"metadata": {},
340340
"outputs": [],
341-
"source": [
342-
"@opik.track(project_name=OPIK_PROJECT)\n",
343-
"def generate(prompt: str, max_new_tokens: int = 100) -> str:\n",
344-
" \"\"\"\n",
345-
" Run inference and attach the registered model version to the Opik trace.\n",
346-
" The model_registry_url field lets you navigate from any trace directly\n",
347-
" to the CometML experiment and checkpoint that produced it.\n",
348-
" \"\"\"\n",
349-
" opik.update_current_trace(\n",
350-
" metadata={\n",
351-
" 'model_registry_name': REGISTRY_NAME,\n",
352-
" 'model_version': MODEL_VERSION,\n",
353-
" 'model_registry_url': _registry_url(),\n",
354-
" }\n",
355-
" )\n",
356-
"\n",
357-
" inputs = inference_tokenizer(prompt, return_tensors='pt')\n",
358-
" with torch.no_grad():\n",
359-
" output_ids = inference_model.generate(\n",
360-
" **inputs,\n",
361-
" max_new_tokens=max_new_tokens,\n",
362-
" pad_token_id=inference_tokenizer.eos_token_id,\n",
363-
" do_sample=False,\n",
364-
" )\n",
365-
" return inference_tokenizer.decode(output_ids[0], skip_special_tokens=True)"
366-
]
341+
"source": "@opik.track(project_name=OPIK_PROJECT)\ndef generate(prompt: str, max_new_tokens: int = 100) -> str:\n \"\"\"Run inference and optionally attach registry metadata to the Opik trace.\"\"\"\n if USE_MODEL_REGISTRY:\n opik.update_current_trace(\n metadata={\n 'model_registry_name': REGISTRY_NAME,\n 'model_version': MODEL_VERSION,\n 'model_registry_url': _registry_url(),\n }\n )\n\n inputs = inference_tokenizer(prompt, return_tensors='pt')\n with torch.no_grad():\n output_ids = inference_model.generate(\n **inputs,\n max_new_tokens=max_new_tokens,\n pad_token_id=inference_tokenizer.eos_token_id,\n do_sample=False,\n )\n return inference_tokenizer.decode(output_ids[0], skip_special_tokens=True)"
367342
},
368343
{
369344
"cell_type": "code",

guides/tracing_finetuned_models/use_registered_model.py

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66
that produced it — so you can always trace a prediction back to the exact
77
checkpoint.
88
9-
Dry-run (no credentials): loads distilgpt2 from HuggingFace directly and
10-
prints traces to the console instead of sending to Opik.
9+
Set USE_MODEL_REGISTRY=true to download from the CometML registry (requires
10+
COMET_API_KEY, COMET_WORKSPACE, and a registered model). Without it the script
11+
loads distilgpt2 directly from HuggingFace. Opik tracing runs in either case
12+
as long as OPIK_API_KEY + OPIK_WORKSPACE are set.
1113
1214
Run:
1315
pip install comet_ml opik transformers torch
14-
export COMET_API_KEY="<your-api-key>"
15-
export COMET_WORKSPACE="<your-workspace>"
16-
export COMET_REGISTRY_NAME="sft-distilgpt2"
17-
export COMET_MODEL_VERSION="1.0.0"
1816
export OPIK_API_KEY="<your-api-key>"
1917
export OPIK_WORKSPACE="<your-workspace>"
18+
# Optional — use a registered model instead of HuggingFace:
19+
# export USE_MODEL_REGISTRY=true
20+
# export COMET_API_KEY="<your-api-key>"
21+
# export COMET_WORKSPACE="<your-workspace>"
22+
# export COMET_REGISTRY_NAME="sft-distilgpt2"
23+
# export COMET_MODEL_VERSION="1.0.0"
2024
python use_registered_model.py
2125
"""
2226

@@ -27,17 +31,18 @@
2731
from transformers import AutoModelForCausalLM, AutoTokenizer
2832

2933
# ── Credentials ───────────────────────────────────────────────────────────────
30-
COMET_API_KEY = os.environ.get("COMET_API_KEY")
31-
COMET_WORKSPACE = os.environ.get("COMET_WORKSPACE")
32-
OPIK_API_KEY = os.environ.get("OPIK_API_KEY")
33-
OPIK_WORKSPACE = os.environ.get("OPIK_WORKSPACE")
34+
OPIK_API_KEY = os.environ.get("OPIK_API_KEY")
35+
OPIK_WORKSPACE = os.environ.get("OPIK_WORKSPACE")
36+
COMET_API_KEY = os.environ.get("COMET_API_KEY")
37+
COMET_WORKSPACE = os.environ.get("COMET_WORKSPACE")
3438

35-
REGISTRY_NAME = os.environ.get("COMET_REGISTRY_NAME", "sft-distilgpt2")
36-
MODEL_VERSION = os.environ.get("COMET_MODEL_VERSION", "1.0.0")
37-
MODEL_LOCAL_DIR = "./downloaded_model"
38-
OPIK_PROJECT = os.environ.get("OPIK_PROJECT_NAME", "tracing-finetuned-models")
39+
REGISTRY_NAME = os.environ.get("COMET_REGISTRY_NAME", "sft-distilgpt2")
40+
MODEL_VERSION = os.environ.get("COMET_MODEL_VERSION", "1.0.0")
41+
MODEL_LOCAL_DIR = "./downloaded_model"
42+
OPIK_PROJECT = os.environ.get("OPIK_PROJECT_NAME", "tracing-finetuned-models")
3943

40-
DRY_RUN = not (COMET_API_KEY and COMET_WORKSPACE and OPIK_API_KEY and OPIK_WORKSPACE)
44+
USE_REGISTRY = os.environ.get("USE_MODEL_REGISTRY", "").lower() in ("1", "true", "yes")
45+
DRY_RUN = not (OPIK_API_KEY and OPIK_WORKSPACE)
4146

4247
# Module-level model handles — populated in main() before any inference call.
4348
tokenizer = None
@@ -73,18 +78,15 @@ def load_model(model_dir: str) -> None:
7378
# ── Step 3: Opik-traced inference ─────────────────────────────────────────────
7479
@opik.track(project_name=OPIK_PROJECT)
7580
def generate(prompt: str, max_new_tokens: int = 100) -> str:
76-
"""
77-
Run inference and attach a link to the registered model version on the trace.
78-
The registry URL in metadata lets you navigate from any Opik trace directly
79-
to the CometML experiment and model version that produced it.
80-
"""
81-
opik.update_current_trace(
82-
metadata={
83-
"model_registry_name": REGISTRY_NAME,
84-
"model_version": MODEL_VERSION,
85-
"model_registry_url": registry_url(),
86-
}
87-
)
81+
"""Run inference; attach registry metadata to the trace when a registry model is loaded."""
82+
if USE_REGISTRY:
83+
opik.update_current_trace(
84+
metadata={
85+
"model_registry_name": REGISTRY_NAME,
86+
"model_version": MODEL_VERSION,
87+
"model_registry_url": registry_url(),
88+
}
89+
)
8890

8991
inputs = tokenizer(prompt, return_tensors="pt")
9092
with torch.no_grad():
@@ -99,12 +101,12 @@ def generate(prompt: str, max_new_tokens: int = 100) -> str:
99101

100102
# ── Main ──────────────────────────────────────────────────────────────────────
101103
def main() -> None:
102-
if DRY_RUN:
103-
print("[DRY RUN] Missing credentials — loading distilgpt2 from HuggingFace directly.")
104-
print("Set COMET_API_KEY, COMET_WORKSPACE, OPIK_API_KEY, OPIK_WORKSPACE to use a registered model.\n")
105-
model_dir = "distilgpt2"
106-
else:
104+
if USE_REGISTRY:
107105
model_dir = download_registered_model()
106+
else:
107+
print("USE_MODEL_REGISTRY not set — loading distilgpt2 from HuggingFace.")
108+
print("Set USE_MODEL_REGISTRY=true (+ COMET_API_KEY / COMET_WORKSPACE) to use a registered model.\n")
109+
model_dir = "distilgpt2"
108110

109111
load_model(model_dir)
110112

@@ -120,12 +122,10 @@ def main() -> None:
120122
print(f"Response: {response}\n")
121123

122124
if DRY_RUN:
123-
print("[DRY RUN] Traces printed locally. Set credentials to send to Opik.")
124-
print(f"Each trace would include: model_registry_url = {registry_url()}")
125+
print("[DRY RUN] OPIK_API_KEY / OPIK_WORKSPACE not set — traces were not sent to Opik.")
125126
else:
126127
opik.flush_tracker()
127128
print(f"Traces sent to Opik project '{OPIK_PROJECT}'.")
128-
print(f"Each trace links to: {registry_url()}")
129129

130130

131131
if __name__ == "__main__":

0 commit comments

Comments
 (0)