Skip to content

Commit 5cb5204

Browse files
committed
Merge remote-tracking branch 'origin/master' into fix/adhoc-viz-fallback-missing-id
2 parents d7e9a7d + 72858ca commit 5cb5204

35 files changed

Lines changed: 3628 additions & 578 deletions

‎packages/gooddata-eval/AGENTS.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
`gdc-nas`) through a dataset of natural-language questions and scores what comes back,
55
including side-by-side comparison across models. Each dataset item is a JSON envelope
66
loaded from a local folder or pulled from a Langfuse dataset. Results are aggregated into
7-
pass@K / pass^K reports and optionally pushed to Langfuse as scored traces tied to a
8-
dataset run. The newest and most actively developed package in the repo.
7+
pass@K / pass^K reports and optionally pushed to Langfuse as scored traces tied to an
8+
experiment. The newest and most actively developed package in the repo.
99

1010
## Owns
1111

1212
- The `gd-eval` CLI (`gd-eval run`, `gd-eval models`)
1313
- Dataset loading and the evaluation run loop
1414
- Per-capability evaluators and their scoring
15-
- Result reporting, and pushing runs, scores and trace links to Langfuse
15+
- Result reporting, and pushing experiments, scores and trace links to Langfuse
1616

1717
## Does NOT Own
1818

@@ -29,7 +29,7 @@ dataset run. The newest and most actively developed package in the repo.
2929
| `core/summary/` | HTTP client for the dedicated dashboard-summary endpoint — a single-shot chat backend, not reporting |
3030
| `core/dataset/` | dataset format and loading |
3131
| `core/evaluators/` | single-shot evaluators and their registry |
32-
| `core/langfuse/` | `sink.py` only — pushes single-turn scores and dataset-run items |
32+
| `core/langfuse/` | the whole Langfuse v4 client: `_env` (base URL + credentials), `otlp` (OTLP/JSON encoding), `experiment` (root-span construction, score targets), `observations` (trace reads), `client` (httpx calls), `sink` (single-shot results as experiments) |
3333
| `core/reporting/` | console and JSON output rendering |
3434
| `core/scoring.py`, `core/runner.py` | scoring and orchestration |
3535
| `core/models.py` | `DatasetItem`, `ChatResult`, `ItemReport` and friends |

‎packages/gooddata-eval/README.md‎

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ gd-eval run \
113113
| Flag | Description |
114114
|---|---|
115115
| `--dataset PATH` | Flat folder of JSON files — one question per file. |
116-
| `--langfuse-dataset NAME` | Pull items by name from a Langfuse dataset. Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`. |
116+
| `--langfuse-dataset NAME` | Pull items by name from a Langfuse dataset. Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY` and `LANGFUSE_BASE_URL` (or the legacy `LANGFUSE_HOST`). |
117117
| `--kind TEST_KIND` | Fallback `test_kind` for dataset items that do not embed one. Defaults to `visualization`; use e.g. `agentic_metric_skill` for multi-turn agentic evaluation. Items that declare their own `test_kind` ignore this. |
118118

119119
#### Model selection
@@ -152,15 +152,39 @@ interleaves when K > 1, and per-item latencies rise, so they stop being clean si
152152

153153
| Flag | Description |
154154
|---|---|
155-
| `--langfuse` | Log scores and traces to Langfuse after each item. Requires `--langfuse-dataset`. Names each experiment run `{dataset_name}_{timestamp}_{model}`, suffixed `_effort-{level}` when `--reasoning-effort` is set (so runs differing only by effort stay separate) and `_run{N}` per run when `--runs` > 1 — e.g. `general_question_2026-09-02-11-13_gpt-5.2_run0`. Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`. |
155+
| `--langfuse` | Log scores and traces to Langfuse after each item. Requires `--langfuse-dataset`. Names each experiment run `{dataset_name}_{timestamp}_{model}`, suffixed `_effort-{level}` when `--reasoning-effort` is set (so runs differing only by effort stay separate) and `_run{N}` per run when `--runs` > 1 — e.g. `general_question_2026-09-02-11-13_gpt-5.2_run0`. Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY` and `LANGFUSE_BASE_URL` (or the legacy `LANGFUSE_HOST`). |
156156

157-
Set `TAVERN_E2E_SKIP_TRACE_LINK=1` to skip trace lookup entirely (scores are then orphaned; the run says so).
157+
##### Langfuse v4
158158

159-
**A local `--dataset` cannot be attached to a Langfuse run.** `--langfuse` is refused alongside `--dataset`
160-
because a local folder's item ids are not Langfuse dataset item ids. But trace linking does not depend on that
161-
flag — each `evaluate_agentic_*` builds its own client whenever `LANGFUSE_PUBLIC_KEY`/`LANGFUSE_SECRET_KEY` are
162-
exported — so a local run still finds its traces and writes its scores onto them, and only the per-run grouping
163-
fails, with one `404 from dataset-run-items` reported per run. The run warns about this before it starts. Use
159+
One run is one Langfuse **experiment**. Each evaluated item becomes its own trace whose root span carries the
160+
experiment and dataset-item attributes (`langfuse.experiment.name`, `langfuse.experiment.dataset.id`,
161+
`langfuse.experiment.item.id`), and the four scores attach to that root observation. gd-eval speaks to Langfuse
162+
over four REST endpoints and uses no Langfuse SDK, so it runs on every Python version the package supports:
163+
164+
| Endpoint | Used for |
165+
|---|---|
166+
| `POST /api/public/otel/v1/traces` | exporting the experiment root span as OTLP/HTTP JSON |
167+
| `POST /api/public/scores` | one score per write, on a trace or on a single observation inside it |
168+
| `GET /api/public/v2/observations` | finding the agent's gen-ai trace for a conversation |
169+
| `GET /api/public/dataset-items` | loading `--langfuse-dataset` items, and resolving an item's dataset id |
170+
171+
Two consequences of v4's immutable observations. gd-eval sets `version` only on its own experiment span, never on
172+
the agent's gen-ai trace — filter on the gd-eval experiment's `langfuse.version` to compare models. And on the
173+
agentic kinds the latency in `value_score` is the gen-ai trace's root generation latency, read from the
174+
observations endpoint; the single-shot `--langfuse` sink keeps using the item's own measured average latency.
175+
176+
Langfuse Cloud drops v3 on **2026-11-16**; a self-hosted Langfuse must be on v4 for any of this to work.
177+
178+
Set `TAVERN_E2E_SKIP_TRACE_LINK=1` to turn the whole **agentic** Langfuse write path off — no trace lookup, no
179+
span export and no scores for `agentic_*` items. The run says so once. It does not reach the `--langfuse` sink,
180+
which still writes a span and four scores for every single-shot item; drop `--langfuse` to silence that too.
181+
182+
**A local `--dataset` cannot be attached to a Langfuse experiment.** `--langfuse` is refused alongside
183+
`--dataset` because a local folder's item ids are not Langfuse dataset item ids. But trace linking does not
184+
depend on that flag — each `evaluate_agentic_*` builds its own client whenever
185+
`LANGFUSE_PUBLIC_KEY`/`LANGFUSE_SECRET_KEY` are exported — so a local run still finds its traces and writes its
186+
scores onto them, and only the per-run grouping fails: the dataset-item lookup 404s and the run reports the item
187+
as one that does not exist in Langfuse, once. The run also warns about this before it starts. Use
164188
`--langfuse-dataset` when you want runs that are comparable across models, or `TAVERN_E2E_SKIP_TRACE_LINK=1` to
165189
skip linking altogether.
166190

@@ -380,6 +404,10 @@ Without `[llm-judge]`, those items are **skipped**.
380404

381405
## Scores (in JSON report and Langfuse)
382406

407+
In Langfuse every score is written to the experiment run's root observation — `traceId` plus `observationId` of
408+
the item's own root span. On the agentic path each score is mirrored onto the agent's gen-ai trace as well
409+
(`traceId` only), so a score survives even when one of the two traces is missing.
410+
383411
| Score | Description |
384412
|---|---|
385413
| `pass_at_k` | 1 if any of the K runs passed strict checks, else 0. |

‎packages/gooddata-eval/src/gooddata_eval/cli/main.py‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,14 @@ def _apply_timer_flag(enabled: bool) -> None:
181181

182182

183183
def _warn_if_local_dataset_cannot_link(config: RunConfig, agentic_items: list) -> None:
184-
"""Say up front that dataset-run assembly will fail, rather than after the run.
184+
"""Say up front that experiment assembly will fail, rather than after the run.
185185
186186
--langfuse is refused outright with a local dataset because local item ids cannot be
187187
linked. But every evaluate_agentic_* falls back to try_make_langfuse_client() when the
188-
caller passes none, so with LANGFUSE_* exported the linking runs anyway and each
189-
conversation earns a 404 from dataset-run-items -- arriving in a block at the very end
190-
of the run, long after the flag that would have prevented it could be changed. The
191-
fallback is deliberate (direct library and tavern callers rely on it), so this warns
192-
instead of disabling it.
188+
caller passes none, so with LANGFUSE_* exported the linking runs anyway and every
189+
dataset-item lookup 404s -- arriving in a block at the very end of the run, long after
190+
the flag that would have prevented it could be changed. The fallback is deliberate
191+
(direct library and tavern callers rely on it), so this warns instead of disabling it.
193192
"""
194193
from gooddata_eval.core.agentic._langfuse import SKIP_ENV_VAR, langfuse_credentials_present # noqa: PLC0415
195194
from gooddata_eval.core.config import env_flag # noqa: PLC0415
@@ -201,7 +200,7 @@ def _warn_if_local_dataset_cannot_link(config: RunConfig, agentic_items: list) -
201200
print(
202201
f"warning: --dataset is a local folder, so its item ids are not Langfuse dataset item ids. "
203202
f"Traces will be found and scored, but the per-run grouping that makes models comparable "
204-
f"cannot be created and each conversation will report a 404 from dataset-run-items. "
203+
f"cannot be created and each conversation will report that its item does not exist in Langfuse. "
205204
f"Use --langfuse-dataset for comparable runs, or set {SKIP_ENV_VAR}=1 to skip trace linking.",
206205
file=sys.stderr,
207206
)

0 commit comments

Comments
 (0)