@@ -9,7 +9,7 @@ experiment. The newest and most actively developed package in the repo.
99
1010## Owns
1111
12- - The ` gd-eval ` CLI (` gd-eval run` , ` gd-eval models` )
12+ - The ` gd-eval ` CLI (` generate ` , ` run ` , ` report ` , ` models ` )
1313- Dataset loading and the evaluation run loop
1414- Per-capability evaluators and their scoring
1515- Result reporting, and pushing experiments, scores and trace links to Langfuse
@@ -27,7 +27,7 @@ experiment. The newest and most actively developed package in the repo.
2727| ` core/agentic/ ` | multi-turn agentic evaluation per capability, ** plus** all Langfuse trace polling and linking (` _langfuse.py ` , ` _trace_linker.py ` ) |
2828| ` core/chat/ ` | SSE client for the agent's streaming chat endpoint |
2929| ` core/summary/ ` | HTTP client for the dedicated dashboard-summary endpoint — a single-shot chat backend, not reporting |
30- | ` core/dataset/ ` | dataset format and loading |
30+ | ` core/dataset/ ` | dataset format, loading, and ` from_insights.py ` — dataset generation from a workspace's real insights |
3131| ` core/evaluators/ ` | single-shot evaluators and their registry |
3232| ` 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 |
@@ -61,6 +61,106 @@ its own shape. `test_kind` on the item is what labels the result, not the evalua
6161which is why ` knowledge_question ` can reuse ` GeneralQuestionEvaluator ` verbatim.
6262` dashboard_summary ` items additionally need ` summary_input ` .
6363
64+ ## Running the pipeline
65+
66+ Four subcommands, in the order you use them. Everything runs through ` uv ` ; never a bare
67+ ` python ` . There is no build step -- ` uv run ` syncs the environment from ` uv.lock ` on first
68+ use, so a fresh clone needs nothing but:
69+
70+ ``` bash
71+ uv run --package gooddata-eval gd-eval < subcommand> --help
72+ ```
73+
74+ ` openai ` is an optional extra (` llm-judge ` ) so the published package stays installable
75+ without it, but the ` dev ` dependency group pulls it in, which is why a plain ` uv run ` here
76+ has the phrasing step and the LLM judge. Installing ` gooddata-eval ` from PyPI does not --
77+ there the extra is explicit, and every ` openai ` import site is guarded or deferred.
78+
79+ Connection is the same for every subcommand that talks to the platform: ` --host ` +
80+ ` --token ` , or ` GOODDATA_TOKEN ` in the environment, or ` --profile <name> ` reading
81+ ` ~/.gooddata/profiles.yaml ` . Precedence is flags > env > profile.
82+
83+ ### 1. ` generate ` — build a dataset from a workspace
84+
85+ Reverse-engineers ` visualization ` items out of the charts a workspace already has, so the
86+ expected output is copied from a live object rather than invented. Needs ` OPENAI_API_KEY `
87+ for the phrasing step, or ` --no-phrase ` to emit mechanical ` Show <title> ` questions.
88+
89+ ``` bash
90+ uv run --package gooddata-eval gd-eval generate \
91+ --host " $GOODDATA_HOST " --workspace " $WORKSPACE_ID " \
92+ --dataset-name ecommerce --out ./datasets/ecommerce \
93+ --snapshot-out /tmp/ws.json \
94+ --phrase-model gpt-4o --skip-ambiguous
95+ ```
96+
97+ Iterate offline instead of re-fetching: ` --snapshot-out ` writes everything the generator
98+ read as one JSON file, and ` --snapshot-in ` replays it with no host, token or network. Add
99+ ` --dry-run ` to print the shape counts and each spec's brief without writing anything —
100+ the fastest way to see what a workspace yields.
101+
102+ Quality gates fail the command (exit 1) below ` --min-questions ` (15) or ` --min-filtered ` (1). Lower them for a smoke test; do not lower them to ship a dataset.
103+ ` --langfuse-out ` additionally writes a Langfuse-importable file, and ` --id-prefix ` rewrites
104+ ids on that export only, because Langfuse item ids are unique per project.
105+
106+ Insights the AAC spec cannot express without guessing are skipped with a printed reason
107+ (` SKIP <id>: derived measure (previousPeriodMeasure) ` ). Read those — they are the
108+ generator telling you what it refused to invent, not noise.
109+
110+ ### 2. ` run ` — evaluate
111+
112+ ``` bash
113+ uv run --package gooddata-eval gd-eval run \
114+ --host " $GOODDATA_HOST " --workspace " $WORKSPACE_ID " \
115+ --dataset ./datasets/ecommerce --kind visualization \
116+ --model gpt-5.2 --model ProviderName/gpt-4o \
117+ --runs 3 --gate power --concurrency 4 \
118+ --json ./results/run.json --html ./results/run.html
119+ ```
120+
121+ ` --dataset ` reads a local folder; ` --langfuse-dataset ` pulls one by name instead. ` --kind `
122+ only supplies a default for items that do not carry their own ` test_kind ` . Repeat
123+ ` --model ` to compare models in one run. ` --runs ` with ` --gate power ` measures stability
124+ (every run must pass) rather than pass@K. ` --langfuse ` pushes the run as a scored
125+ experiment, needing ` LANGFUSE_HOST ` , ` LANGFUSE_PUBLIC_KEY ` and ` LANGFUSE_SECRET_KEY ` .
126+
127+ ` --concurrency ` is capped for you where it matters: kinds that create workspace objects
128+ run one at a time regardless, see the parallel-safety gotcha below.
129+
130+ ### 3. ` report ` — compare runs
131+
132+ ``` bash
133+ uv run --package gooddata-eval gd-eval report \
134+ ./results/* .json -o ./results/comparison.html --title " luna vs 4o" --redact
135+ ```
136+
137+ Several JSON reports become side-by-side columns keyed by file name. ` --redact ` is the
138+ customer-safe form: conversation ids, response ids and raw reasoning dropped, model names
139+ replaced with "Model A", "Model B".
140+
141+ ### 4. ` models ` — what the org has configured
142+
143+ ``` bash
144+ uv run --package gooddata-eval gd-eval models --host " $GOODDATA_HOST "
145+ ```
146+
147+ Run this before guessing a ` --model ` string.
148+
149+ ### Environment
150+
151+ | Variable | Used by |
152+ | ---| ---|
153+ | ` GOODDATA_TOKEN ` | every platform-facing subcommand |
154+ | ` OPENAI_API_KEY ` | ` generate ` phrasing, and the LLM-as-judge evaluators |
155+ | ` GD_EVAL_JUDGE_MODEL ` | judge model, same as ` --judge-model ` |
156+ | ` GD_EVAL_AGENT_ID ` | which agent to drive, same as ` --agent-id ` |
157+ | ` LANGFUSE_HOST ` , ` LANGFUSE_PUBLIC_KEY ` , ` LANGFUSE_SECRET_KEY ` | ` --langfuse ` , ` --langfuse-dataset ` |
158+ | ` GOODDATA_EVAL_CHAT_* ` | SSE retry, backoff and timeout knobs |
159+ | ` GD_EVAL_TIMERS ` | same as ` --timers ` |
160+
161+ A gitignored ` .env ` at the repo root is the normal place for these; load it with
162+ ` set -a && . ./.env && set +a ` before the command.
163+
64164## Gotchas
65165
66166** Adding an evaluator is a registry change, not a naming convention.** Single-shot kinds go
@@ -89,6 +189,23 @@ ingestion has no pass/fail signal and inflates or misattributes per-item latency
89189(` run_trace_link_inline ` is the synchronous alternative). Do not "fix" a slow item by
90190making trace scoring synchronous again.
91191
192+ ** A generated item's ` expected_output ` is copied, never invented — keep it that way.**
193+ ` core/dataset/from_insights.py ` converts each insight with the platform's own
194+ ` declarative_visualization_to_aac() ` (from ` gooddata-code-convertors ` , via ` gooddata-sdk ` ),
195+ so the mapping is not ours to get wrong. What is ours is deciding what the evaluator cannot
196+ yet score — derived measures and measure-level filters convert fine and then compare wrong,
197+ so they are skipped with a printed reason — and stripping the no-op filters AD saves for an
198+ "All" selection, which would otherwise let a question claim a filter its chart lacks. Teach
199+ the comparator about a construct and the matching skip can go; do not make one convert by
200+ hand. Chart type names are the convertor's, which are also the agent's — do not rename them. One
201+ granularity is patched in ` CONVERTOR_GRANULARITY_FIXES ` : ` week_us ` → ` WEEK_US ` is a convertor
202+ bug, the platform enum is ` WEEK ` .
203+
204+ ** The snapshot is a plain-JSON contract.** ` --snapshot-in ` /` --snapshot-out ` is what makes
205+ the generator testable offline and iterable without re-fetching, and it is why the
206+ generator reads the declarative analytics model rather than ` sdk.visualizations ` . Anything
207+ that changes the fetch shape invalidates every saved snapshot.
208+
92209** Scoring weights do not sum to 1.** ` quality_score ` is the fraction of boolean-valued keys
93210in ` best_detail ` that are true, falling back to ` pass_at_k ` when there are none (text
94211evaluators). ` value_score ` is ` 0.6 * quality + 0.2 * speed ` — the 0.8 total is what the
0 commit comments