Skip to content

Commit 815e862

Browse files
feat: add run.sh to f1_radio_rag (#19)
## Summary Adds CI support to `use-cases/f1_radio_rag` — the first example to go through the new PR test workflow (from PR #16). ## What changed - `run.sh` — runs `ingest` then `ask`, logging one RAG trace to Opik. Lightweight alternative to the full eval suite for CI. - `config.py` — reads `OPIK_EXAMPLES_MODEL` so CI routes to a cheap model (`openai/gpt-4o-mini`). Removes `LLM_READY` guard — missing keys now fail loudly via litellm rather than silently skipping. - `cli.py` — removes the `LLM_READY` dry-run branch in `ask`. ## Tested locally Ran `bash run.sh` with real credentials. Trace appeared in Opik workspace under project `f1-radio-rag`. **Depends on PR #16 merging first** for the PR test workflow to fire. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents fdfd4cb + 2d1530d commit 815e862

7 files changed

Lines changed: 33 additions & 19 deletions

File tree

templates/use-case-template/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Or, with `uv` (recommended — this folder is a `uv` project): `uv sync`.
3333

3434
| Environment variable | Required | Description |
3535
|---|---|---|
36-
| `ANTHROPIC_API_KEY` | for `run`/`eval`/`optimize`/`promote` | Anthropic key; used via litellm for generation, judge metrics, and the optimizer |
36+
| `ANTHROPIC_API_KEY` (or the key for your `OPIK_EXAMPLES_MODEL` provider) | for live `run`/`eval`/`optimize`/`promote` | Model-provider key used via litellm for generation, judge metrics, and the optimizer |
3737
| `OPIK_API_KEY` | for `eval`/`optimize`/`promote` | Your Opik API key. Unset → those commands run in DRY_RUN |
3838
| `OPIK_WORKSPACE` | for `eval`/`optimize`/`promote` | Your Opik workspace name |
3939
| `OPIK_PROJECT_NAME` | No | Opik project for traces/experiments (default `example-use-case`) |

templates/use-case-template/src/example_use_case/cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
@app.command()
1010
def run(input: str, context: list[str] = typer.Option(None, "--context", "-c")) -> None:
11-
"""Run the app on a single input (traced in Opik when an LLM key is set)."""
12-
if not config.LLM_READY:
13-
typer.echo("[DRY RUN] ANTHROPIC_API_KEY not set — would run the app on:")
11+
"""Run the app on a single input (traced in Opik when credentials are set)."""
12+
if config.DRY_RUN:
13+
typer.echo("[DRY RUN] Opik creds not set — would run the app on:")
1414
typer.echo(f" input: {input}")
1515
for line in context or []:
1616
typer.echo(f" context: {line}")

templates/use-case-template/src/example_use_case/config.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
OPIK_API_KEY = os.environ.get("OPIK_API_KEY")
44
OPIK_WORKSPACE = os.environ.get("OPIK_WORKSPACE")
55
OPIK_PROJECT_NAME = os.environ.get("OPIK_PROJECT_NAME", "example-use-case")
6-
ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY")
76

87
# No Opik credentials -> every command still runs and prints locally instead of calling Opik.
98
DRY_RUN = not (OPIK_API_KEY and OPIK_WORKSPACE)
10-
# Generation/judging needs an Anthropic key; without it we describe what would happen.
11-
LLM_READY = bool(ANTHROPIC_API_KEY)
129

1310
# litellm model strings (Anthropic provider). Swap for any litellm-supported model.
1411
# CI sets OPIK_EXAMPLES_MODEL to a cheap model; locally, leave it unset to use the full model.

use-cases/f1_radio_rag/README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ Or, with `uv` (recommended — this folder is a `uv` project): `uv sync`.
3838

3939
| Environment variable | Required | Description |
4040
|---|---|---|
41-
| `ANTHROPIC_API_KEY` | for `ask`/`eval`/`optimize`/`promote` | Anthropic key; used via litellm for generation, the LLM-judge metrics, and the optimizer |
41+
| `ANTHROPIC_API_KEY` (or the key for your `OPIK_EXAMPLES_MODEL` provider) | for live `ask`/`eval`/`optimize`/`promote` | Model-provider key used via litellm for generation, the LLM-judge metrics, and the optimizer |
4242
| `OPIK_API_KEY` | for `eval`/`optimize`/`promote` | Your Opik API key. Unset → those commands run in DRY_RUN |
4343
| `OPIK_WORKSPACE` | for `eval`/`optimize`/`promote` | Your Opik workspace name |
4444
| `OPIK_PROJECT_NAME` | No | Opik project for traces/experiments (default `f1-radio-rag`) |
45+
| `OPIK_EXAMPLES_MODEL` | No | litellm model for generation/judging/optimising. Unset → `anthropic/claude-sonnet-4-6`; CI sets a cheap model (e.g. `openai/gpt-4o-mini`) |
4546
| `OPIK_URL_OVERRIDE` | No | Base URL for self-hosted Opik (default: Opik Cloud) |
4647

4748
## Running it
@@ -66,13 +67,22 @@ uv run f1rag promote # optimised prompt saved to the Prompt Library (version
6667
uv run f1rag run-all # the whole loop in one shot
6768
```
6869

70+
`run.sh` is the entrypoint CI runs: it exports `OPIK_PROJECT_NAME`, then `uv sync` and runs
71+
`ingest` + `ask`. With no credentials it stays in dry-run and exits 0 (the secrets-free CI check);
72+
with credentials set it logs a live trace to Opik.
73+
74+
```bash
75+
bash run.sh
76+
```
77+
6978
## How it works
7079

7180
1. **Ingest** (`rag.py`) — `chromadb.PersistentClient` stores one document per radio message with
7281
session/driver/lap metadata, using ChromaDB's default local embeddings (no embedding-API cost).
7382
2. **Ask** (`rag.py`) — `answer()` retrieves the top-k messages, then calls
74-
`litellm.completion(model="anthropic/claude-sonnet-4-6", ...)` with the summariser prompt from
75-
`prompts.py`. It's decorated with `@opik.track`, so each call appears as a trace in Opik.
83+
`litellm.completion(model=config.GEN_MODEL, ...)` with the summariser prompt from `prompts.py`
84+
(`GEN_MODEL` defaults to `anthropic/claude-sonnet-4-6`, overridable via `OPIK_EXAMPLES_MODEL`).
85+
It's decorated with `@opik.track`, so each call appears as a trace in Opik.
7686
3. **Eval** (`evaluation.py`) — builds an Opik dataset and a test suite, then scores the live RAG
7787
task. The test suite checks plain-English **assertions**; `evaluate` runs the `ContextRecall`
7888
(retrieval quality) and `Hallucination` (faithfulness) metrics. The eval cases live in

use-cases/f1_radio_rag/run.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
export OPIK_PROJECT_NAME="f1-radio-rag"
5+
6+
uv sync
7+
8+
uv run f1rag ingest
9+
uv run f1rag ask "Why did Verstappen pit early?"

use-cases/f1_radio_rag/src/f1_radio_rag/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def ask(query: str, k: int = 5) -> None:
2323
typer.echo("Retrieved messages:")
2424
for message in context:
2525
typer.echo(f" - {message}")
26-
if not config.LLM_READY:
27-
typer.echo("\n[DRY RUN] ANTHROPIC_API_KEY not set — would summarise the above with Claude.")
26+
if config.DRY_RUN:
27+
typer.echo(f"\n[DRY RUN] Opik creds not set — would summarise with {config.GEN_MODEL}.")
2828
return
2929
result = rag.answer(query, k)
3030
typer.echo(f"\nSummary:\n{result['output']}")

use-cases/f1_radio_rag/src/f1_radio_rag/config.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@
33
OPIK_API_KEY = os.environ.get("OPIK_API_KEY")
44
OPIK_WORKSPACE = os.environ.get("OPIK_WORKSPACE")
55
OPIK_PROJECT_NAME = os.environ.get("OPIK_PROJECT_NAME", "f1-radio-rag")
6-
ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY")
76

87
# No Opik credentials -> every command still runs and prints locally instead of calling Opik.
98
DRY_RUN = not (OPIK_API_KEY and OPIK_WORKSPACE)
10-
# Generation/judging needs an Anthropic key; without it we describe what would happen.
11-
LLM_READY = bool(ANTHROPIC_API_KEY)
129

13-
# litellm model strings (Anthropic provider).
14-
GEN_MODEL = "anthropic/claude-sonnet-4-6" # deployed summariser
15-
JUDGE_MODEL = "anthropic/claude-sonnet-4-6" # LLM-as-judge for metrics
16-
OPTIMIZER_MODEL = "anthropic/claude-sonnet-4-6" # meta-model that rewrites the prompt
10+
# litellm model strings. CI sets OPIK_EXAMPLES_MODEL to a cheap model (e.g. openai/gpt-4o-mini);
11+
# locally, leave it unset to use the full model.
12+
GEN_MODEL = os.environ.get("OPIK_EXAMPLES_MODEL", "anthropic/claude-sonnet-4-6") # deployed summariser
13+
JUDGE_MODEL = GEN_MODEL # LLM-as-judge for metrics
14+
OPTIMIZER_MODEL = GEN_MODEL # meta-model that rewrites the prompt
1715

1816
CHROMA_DIR = "chroma_db"
1917
COLLECTION = "f1_radio"

0 commit comments

Comments
 (0)