Skip to content

Commit 8ea2406

Browse files
committed
misc rename continuations
Signed-off-by: shanaiabuggy <59746633+shanaiabuggy@users.noreply.github.com>
1 parent 42707b8 commit 8ea2406

16 files changed

Lines changed: 62 additions & 70 deletions

File tree

packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-experiments-upload/SKILL.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,6 @@ If `run_count` is 0 after ingesting, the traces didn't associate — almost alwa
194194
## Gotchas
195195

196196
- **Create before you log.** The Evaluation entity must exist before any ingest referencing it — otherwise `400`.
197-
- **Evaluation and Experiment references use different fields.** `evaluation_name` associates telemetry with an Evaluation, while `experiment_ids` assigns that Evaluation to its parent Experiments.
198-
- **OTLP evaluation context:** set `nemo.evaluation.name` and, when applicable, `nemo.test_case.name` as root-span attributes. These correspond to `evaluation_name` and `test_case_name` in the JSON `evaluation_context` used by other ingest endpoints.
199197
- **The parent lives at `/experiments`; `/experiment-groups` is a deprecated hidden alias.** Prefer `/experiments`. Evaluations are created and logged under `/evaluations`.
200198
- **`metadata` is `dict[str, str]`** — stringify non-string values or you'll get a `422`.
201199
- **ATIF and chat-completions are `extra="forbid"`** (unknown keys → 422); `evaluation_context` itself is lenient (`extra="ignore"`).

packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-intake/references/ingest-formats.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ Full request schemas for the three intake ingest endpoints. All are under
2121
- `test_case_name` is optional and identifies which task or test case the run covers.
2222
- The referenced Evaluation **must already exist** (create it first) or the request is rejected with
2323
`400 "…must be created before it can be logged."`
24-
- The model is lenient (`extra="ignore"`): retired keys (`evaluation_sha`, `evaluation_run_id`,
25-
`metadata`) are accepted but dropped.
26-
- A deprecated `experiment_context` `{experiment_id, test_case_id}` shape is still accepted;
27-
`evaluation_context` wins if both are present. Use `evaluation_context`.
2824

2925
---
3026

plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def trial_to_atif_ingest(
8484
trial: AgentEvalTrial,
8585
*,
8686
run_id: str,
87-
experiment_id: str,
87+
evaluation_name: str,
8888
agent_name: str,
8989
started_at: datetime,
9090
agent_version: str = DEFAULT_AGENT_VERSION,
@@ -132,7 +132,7 @@ def trial_to_atif_ingest(
132132
"session_id": session_id_for(run_id, trial.id),
133133
"agent": agent,
134134
"steps": [step],
135-
"evaluation_context": run_task_to_evaluation_context(trial, evaluation_name=experiment_id),
135+
"evaluation_context": run_task_to_evaluation_context(trial, evaluation_name=evaluation_name),
136136
}
137137
if final_metrics is not None:
138138
body["final_metrics"] = final_metrics

plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ async def _publish_trial(trial: AgentEvalTrial) -> PublishedTrial:
186186
body = mapping.trial_to_atif_ingest(
187187
trial,
188188
run_id=result.run_id,
189-
experiment_id=experiment_id,
189+
evaluation_name=experiment_id,
190190
agent_name=agent_name,
191191
started_at=started_at,
192192
agent_version=agent_version,

plugins/nemo-evaluator/tests/intake/test_mapping.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_trial_to_atif_ingest_shape() -> None:
9090
body = trial_to_atif_ingest(
9191
_trial(trial_id="t-1", task_id="task-1", output_text="final answer"),
9292
run_id="run-1",
93-
experiment_id="exp-1",
93+
evaluation_name="exp-1",
9494
agent_name="my-agent",
9595
started_at=STARTED_AT,
9696
model_name="gpt-4o",
@@ -104,14 +104,16 @@ def test_trial_to_atif_ingest_shape() -> None:
104104

105105

106106
def test_trial_to_atif_ingest_defaults_version_and_omits_model_name() -> None:
107-
body = trial_to_atif_ingest(_trial(), run_id="run-1", experiment_id="exp-1", agent_name="a", started_at=STARTED_AT)
107+
body = trial_to_atif_ingest(
108+
_trial(), run_id="run-1", evaluation_name="exp-1", agent_name="a", started_at=STARTED_AT
109+
)
108110
assert body["agent"] == {"name": "a", "version": "unknown"}
109111
assert "model_name" not in body["agent"]
110112

111113

112114
def test_trial_to_atif_ingest_handles_missing_output() -> None:
113115
body = trial_to_atif_ingest(
114-
_trial(output_text=None), run_id="run-1", experiment_id="exp-1", agent_name="a", started_at=STARTED_AT
116+
_trial(output_text=None), run_id="run-1", evaluation_name="exp-1", agent_name="a", started_at=STARTED_AT
115117
)
116118
assert body["steps"] == [{"source": "agent", "step_id": 1, "message": "", "timestamp": STARTED_AT}]
117119

@@ -120,7 +122,7 @@ def test_trial_to_atif_ingest_includes_final_metrics_when_given() -> None:
120122
body = trial_to_atif_ingest(
121123
_trial(),
122124
run_id="run-1",
123-
experiment_id="exp-1",
125+
evaluation_name="exp-1",
124126
agent_name="a",
125127
started_at=STARTED_AT,
126128
final_metrics={"total_prompt_tokens": 10},
@@ -133,7 +135,7 @@ def test_trial_to_atif_ingest_adds_invocation_window_when_ended_at_given() -> No
133135
# root-span latency is the trial's runtime instead of 0.
134136
ended = STARTED_AT + timedelta(seconds=12.5)
135137
body = trial_to_atif_ingest(
136-
_trial(), run_id="run-1", experiment_id="exp-1", agent_name="a", started_at=STARTED_AT, ended_at=ended
138+
_trial(), run_id="run-1", evaluation_name="exp-1", agent_name="a", started_at=STARTED_AT, ended_at=ended
137139
)
138140
(step,) = body["steps"]
139141
assert step["extra"] == {
@@ -142,7 +144,9 @@ def test_trial_to_atif_ingest_adds_invocation_window_when_ended_at_given() -> No
142144

143145

144146
def test_trial_to_atif_ingest_omits_invocation_window_without_ended_at() -> None:
145-
body = trial_to_atif_ingest(_trial(), run_id="run-1", experiment_id="exp-1", agent_name="a", started_at=STARTED_AT)
147+
body = trial_to_atif_ingest(
148+
_trial(), run_id="run-1", evaluation_name="exp-1", agent_name="a", started_at=STARTED_AT
149+
)
146150
assert "extra" not in body["steps"][0]
147151

148152

sdk/python/nemo-platform/src/nemo_platform/skills/nemo-experiments-upload/SKILL.md

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/python/nemo-platform/src/nemo_platform/skills/nemo-intake/references/ingest-formats.md

Lines changed: 0 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/intake/scripts/spans/seed_experiments_demo.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ def _seed_sessions(
516516
prompt_tokens = max(10, int(rng.gauss(spec.prompt_tokens_mean, spec.prompt_tokens_mean * 0.25)))
517517
completion_tokens = max(5, int(rng.gauss(spec.completion_tokens_mean, spec.completion_tokens_mean * 0.3)))
518518

519-
test_case_id = f"case-{i:04d}"
519+
test_case_name = f"case-{i:04d}"
520520
run_id = f"run-{i // 25:02d}"
521521
# Spread sessions across the ~5.5h prior to "now" so the Studio timeline looks varied.
522522
offset_seconds = (i / max(1, spec.n_sessions)) * 5.5 * 3600
@@ -531,9 +531,9 @@ def _seed_sessions(
531531
)
532532
atif_body = _demo_atif_body(
533533
base_started_at=base_started_at,
534-
evaluation_id=spec.name,
534+
evaluation_name=spec.name,
535535
run_id=run_id,
536-
test_case_id=test_case_id,
536+
test_case_name=test_case_name,
537537
cost_usd=cost_usd,
538538
latency_ms=latency_ms,
539539
offset_seconds=offset_seconds,
@@ -571,9 +571,9 @@ def _seed_sessions(
571571
def _demo_atif_body(
572572
*,
573573
base_started_at: datetime,
574-
evaluation_id: str,
574+
evaluation_name: str,
575575
run_id: str,
576-
test_case_id: str,
576+
test_case_name: str,
577577
cost_usd: float,
578578
latency_ms: int,
579579
offset_seconds: float,
@@ -585,20 +585,20 @@ def _demo_atif_body(
585585
) -> dict[str, Any]:
586586
session_started_at = base_started_at + timedelta(seconds=offset_seconds)
587587
finished_at = session_started_at + timedelta(milliseconds=latency_ms)
588-
session_id = f"{evaluation_id}-{run_id}-{test_case_id}"
588+
session_id = f"{evaluation_name}-{run_id}-{test_case_name}"
589589
# `extra.verifier` carries the timing block (used by the rollup for session latency).
590590
# We omit `extra.verifier_result` so ATIF ingest doesn't auto-create a `harbor.verifier`
591591
# evaluator alongside our cleanly-named ones from POST /evaluator-results.
592592
return {
593593
"schema_version": "ATIF-v1.7",
594594
"session_id": session_id,
595595
"evaluation_context": {
596-
"evaluation_name": evaluation_id,
597-
"test_case_name": test_case_id,
596+
"evaluation_name": evaluation_name,
597+
"test_case_name": test_case_name,
598598
},
599599
"extra": {
600-
"task_id": test_case_id,
601-
"task_name": test_case_id,
600+
"task_id": test_case_name,
601+
"task_name": test_case_name,
602602
"verifier": {
603603
"started_at": _iso(session_started_at),
604604
"finished_at": _iso(finished_at),
@@ -614,14 +614,14 @@ def _demo_atif_body(
614614
"step_id": 1,
615615
"timestamp": _iso(session_started_at),
616616
"source": "user",
617-
"message": f"test case: {test_case_id}",
617+
"message": f"test case: {test_case_name}",
618618
},
619619
{
620620
"step_id": 2,
621621
"timestamp": _iso(finished_at),
622622
"source": "agent",
623623
"model_name": model_name,
624-
"message": f"solved {test_case_id}",
624+
"message": f"solved {test_case_name}",
625625
"metrics": {
626626
"prompt_tokens": prompt_tokens,
627627
"completion_tokens": completion_tokens,

services/intake/src/nmp/intake/api/v2/experiments/endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ def _enqueue_stale_denormalization(
13851385
or entity.agent_versions != rollup.agent_versions
13861386
or entity.model_names != rollup.model_names
13871387
):
1388-
denormalizer.mark_dirty(workspace=workspace, evaluation_id=entity.name)
1388+
denormalizer.mark_dirty(workspace=workspace, evaluation_name=entity.name)
13891389

13901390

13911391
async def _evaluation_response_with_rollup(

services/intake/src/nmp/intake/experiments/denormalizer.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
"""Denormalizes agent/model name fields from ClickHouse onto Evaluation entities.
55
6-
Ingest marks ``(workspace, evaluation_id)`` dirty; a background loop recomputes each touched
6+
Ingest marks ``(workspace, evaluation_name)`` dirty; a background loop recomputes each touched
77
evaluation's rollup and writes the distinct ``agent_names``/``agent_versions``/``model_names`` sets onto
88
the (system-managed) fields of its Evaluation entity. That lets the Evaluations list filter by
99
agent/model name against the entity store (``$contains``) instead of scanning the ClickHouse session
@@ -35,7 +35,7 @@
3535

3636

3737
class EvaluationDenormalizer(BackgroundWorker):
38-
"""Coalesces dirty evaluation ids and refreshes their denormalized name fields on a fixed cadence.
38+
"""Coalesces dirty evaluation names and refreshes their denormalized name fields on a fixed cadence.
3939
4040
A burst of :meth:`mark_dirty` calls for one evaluation within an interval collapses to a single
4141
refresh, and marking is a plain, non-blocking set add — so the ingest and read hot paths are never
@@ -55,12 +55,12 @@ def __init__(
5555
self._interval_seconds = interval_seconds
5656
self._dirty: set[tuple[str, str]] = set()
5757

58-
def mark_dirty(self, *, workspace: str, evaluation_id: str) -> None:
58+
def mark_dirty(self, *, workspace: str, evaluation_name: str) -> None:
5959
"""Queue an evaluation for refresh. Cheap and non-blocking; safe to call from the ingest path."""
60-
self._dirty.add((workspace, evaluation_id))
60+
self._dirty.add((workspace, evaluation_name))
6161

6262
def pending(self) -> set[tuple[str, str]]:
63-
"""Return a copy of the currently-queued ``(workspace, evaluation_id)`` pairs (observability/tests)."""
63+
"""Return a copy of the currently-queued ``(workspace, evaluation_name)`` pairs (observability/tests)."""
6464
return set(self._dirty)
6565

6666
async def _run(self) -> None:
@@ -99,28 +99,28 @@ async def flush(self) -> None:
9999
batch = self._dirty
100100
self._dirty = set()
101101
by_workspace: dict[str, list[str]] = {}
102-
for workspace, evaluation_id in batch:
103-
by_workspace.setdefault(workspace, []).append(evaluation_id)
104-
for workspace, evaluation_ids in by_workspace.items():
102+
for workspace, evaluation_name in batch:
103+
by_workspace.setdefault(workspace, []).append(evaluation_name)
104+
for workspace, evaluation_names in by_workspace.items():
105105
try:
106-
await self._refresh_workspace(workspace, evaluation_ids)
106+
await self._refresh_workspace(workspace, evaluation_names)
107107
except Exception:
108108
# Re-queue the whole workspace batch for the next cycle (e.g. ClickHouse unavailable).
109109
logger.exception("Failed to refresh evaluation names for workspace %s; re-queuing", workspace)
110-
for evaluation_id in evaluation_ids:
111-
self.mark_dirty(workspace=workspace, evaluation_id=evaluation_id)
110+
for evaluation_name in evaluation_names:
111+
self.mark_dirty(workspace=workspace, evaluation_name=evaluation_name)
112112

113-
async def _refresh_workspace(self, workspace: str, evaluation_ids: list[str]) -> None:
114-
rollups = await self._rollup_repository.get_rollups(workspace=workspace, evaluation_ids=evaluation_ids)
115-
for evaluation_id in evaluation_ids:
116-
rollup = rollups.get(evaluation_id)
113+
async def _refresh_workspace(self, workspace: str, evaluation_names: list[str]) -> None:
114+
rollups = await self._rollup_repository.get_rollups(workspace=workspace, evaluation_ids=evaluation_names)
115+
for evaluation_name in evaluation_names:
116+
rollup = rollups.get(evaluation_name)
117117
if rollup is None:
118118
continue
119-
await self._write_names(workspace, evaluation_id, rollup)
119+
await self._write_names(workspace, evaluation_name, rollup)
120120

121-
async def _write_names(self, workspace: str, evaluation_id: str, rollup: EvaluationRollup) -> None:
121+
async def _write_names(self, workspace: str, evaluation_name: str, rollup: EvaluationRollup) -> None:
122122
try:
123-
evaluation = await self._entity_client.get(Experiment, name=evaluation_id, workspace=workspace)
123+
evaluation = await self._entity_client.get(Experiment, name=evaluation_name, workspace=workspace)
124124
except EntityNotFoundError:
125125
# Deleted between ingest and refresh; nothing to update.
126126
return
@@ -139,4 +139,4 @@ async def _write_names(self, workspace: str, evaluation_id: str, rollup: Evaluat
139139
await self._entity_client.update(evaluation)
140140
except EntityConflictError:
141141
# A concurrent user edit won the optimistic lock; re-queue for the next cycle.
142-
self.mark_dirty(workspace=workspace, evaluation_id=evaluation_id)
142+
self.mark_dirty(workspace=workspace, evaluation_name=evaluation_name)

0 commit comments

Comments
 (0)