Repository navigation
fix: make the online eval run safe for production - #25
Conversation
vojtechj-apify
left a comment
There was a problem hiding this comment.
All three fixes are right, and the checkpoint test that switches the default store between runs is a good reproduction of the bug.
- The guard defaults to the project name. Has it been run with the real "Apify AI Agent" keys yet?
- "Prettier passes" holds only for the new files. A few files inherited from the base commit are flagged. That goes away with the rebase onto main.
- Merge order with #15: both change how the Langfuse keys are handled, and they conflict. Which one goes first?
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
|
Addressed every open review thread on #1 to #5 and #25 here (commits 3982874..3354391), each thread has a reply with its commit. Verified end to end: unit tests (195), typecheck, and two cloud runs of build 0.1.2 of |
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
3354391 to
7c5e318
Compare
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
7c5e318 to
e75e949
Compare
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
e75e949 to
177bebc
Compare
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
177bebc to
c523147
Compare
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
c523147 to
53098a9
Compare
…team#267) actorCheckpointStore() used Actor.getValue/setValue, which act on the run's default key-value store. Every platform run gets a new default store, so every scheduled run read null and fell back to now-24h. The 33 min between the window end and the next 06:00 run were never judged, and a failed run lost its window although finishOnlineRun() held the checkpoint back. The checkpoint now lives in the named store apify-ai-online-state (CHECKPOINT_STORE_NAME), under ONLINE_CHECKPOINT-<environment>, so a staging run never reads or moves the prod checkpoint. The CheckpointStore seam and the override rule are unchanged. New test: writes through actorCheckpointStore() on SDK local storage, then switches ACTOR_DEFAULT_KEY_VALUE_STORE_ID as a new run does and reads back. It failed with null before this change. JUDGE_IMPL_VERSION is not bumped: it is part of every online score id, and this change does not change how a trace is scored.
…ction project (ai-team#271) The Actor env keys point at the dataset-run project (MCP Agent Evals), because the workflow runner starts the judge without keys. An online run that fell back to them selected from and wrote scores into that project without an error. Online mode now resolves the keys' project with GET /api/public/projects before any other Langfuse call and fails unless it matches the new langfuseProject input (name or id, default Apify AI Agent). Dataset-run mode is unchanged.
…Langfuse keys (ai-team#271) The schedule ran the Actor with no keys, and the README said to put them into the Actor env vars. Those must stay on the dataset-run project, so the daily run read and scored the wrong project. The schedule now runs a Task (RUN_ACTOR_TASK) whose input holds the online settings and the Apify AI Agent keys as secret input. online-task.json is the Task body with placeholders only; the schedule README covers the Task, the keys and the creation steps, and the Actor README describes online keys.
…and drop the unused checkpoint write (ai-team#267 review) A root span that starts in the last AGENT_REQUEST_TIMEOUT_MS of the window may still be running at the window end, so one quiet turn straddling the end (1 in, 0 completed) no longer fails the run or a fixed-bound backfill; isCompletionGateBroken() requires a root started before window.end - 30 min. requestedWindow() returns the raw bounds so the empty-window log does not recompute them. selectTraces() only returns the checkpoint record now: the writeCheckpoint option and Selection.checkpointWritten had no caller left.
…69 review) Deduplicating messages on role plus text dropped a real repeat such as user: "yes" ... user: "yes", pointed the prompt at the first one and lost everything between the two. collectItems() now skips, per generation, the longest prefix of its input that matches message for message what earlier generations already contributed, and never dedups by text. Also drops the stale module-comment reference.
…arn once per batch on a schema mismatch (ai-team#269 review) Nothing escaped the fence literals, so a tool result or user message containing <<<APIFY_AI_TURN_DATA_END>>> closed the fence. The delimiters now live in online-render.ts (re-exported from online-judge.ts) and renderTurnForJudge() replaces every occurrence inside the turn with a visible marker, looping until none is left so a nested one cannot reassemble. The toolSchemaHash mismatch is expected on every trace until the agent fix, so judgeOnlineTrace() logs it at debug and main.ts warns once per batch.
… window above 50% judge failures (ai-team#270 review) - Each trace's scores are written right after it is judged (main.ts), so a timeout at trace 95 of 100 leaves 94 in Langfuse; finishOnlineRun() now takes what was written and does only the rollup and the checkpoint. - Both score copies set environment and carry it in metadata; the run id and the rollup item id are suffixed with the environment for every environment but prod, so a staging backfill never joins prod's run or rollup (also closes the #25 thread on finishing the per-environment design). - TooManyJudgeFailuresError replaces AllJudgementsFailedError: the window is held when more than MAX_JUDGE_FAILURE_RATIO (0.5) of the traces given to the judge failed, not only when all did. - Rollup comments say what is true after a retry (scores are the source of truth; n and passes count what each run wrote); rollupItemId() is used wherever the id is built; main.ts uses DEFAULT_ONLINE_JUDGE_MODEL, DEFAULT_JUDGE_MODEL and HOLISTIC_SCORE_NAME instead of literals.
…e Task not the Actor (ai-team#271 review) llm.ts makes three attempts of 120 s with 2 s sleeps, 364 s worst case per trace (the doc comment said two attempts); 25 waves at concurrency 4 is 152 min, so timeoutSecs is 10800. The run-status alert goes on the apify-ai-online-evals Task, because the workflow runner calls the same Actor in datasetRun mode and an Actor-level alert would report offline failures too. The pass-rate alert filter can add environment = prod now that scores carry it.
…d document the changed online behaviour (ai-team#268 review) Score configs cannot be deleted, so the script now resolves the keys' project first and stops unless it is Apify AI Agent (LANGFUSE_PROJECT overrides). The Actor README follows the review changes: settled completion gate, per-trace writes, environment on scores and ids, judge failure ratio, fence-marker stripping, rollup semantics after a retry.
…existed (ai-team#270) The first cloud run after the environment change replaced the day's existing rollup because isRollup() demanded the new field; an older item is still a rollup and merges as the fresh (prod) environment.
53098a9 to
010fb79
Compare
Part of apify/ai-team#249, stacked on the #271 PR. Fixes three blockers that a production-readiness review of the online evals stack found. The unit tests did not catch any of them.
1. The stack did not merge into
main(fixed by the rebase, not in this PR)The stack was based on
poc/workflow-runner, butmaingot the PoC as squash PRs #7 to #13. A test merge conflicted in 21 files. PRs #1 to #5 are now rebased ontomain. Two old merge commits had quietly adapted the stack tomain; the rebase redoes them as commits:fix: read the online judge reply from judgeLlmCall's result(in the #269 PR). Onmain,judgeLlmCallreturns{json, usage, ...}. Without this, every online judgement fails withInvalidJudgeReplyError. The build passed; onlytypecheckcaught it.test: pin the online score id to JUDGE_IMPL_VERSION 0.4.0(in the #270 PR).2. The checkpoint did not carry over between runs
actorCheckpointStore()usedActor.getValue/Actor.setValue, so the run's default key-value store. Every platform run gets a new default store, so every scheduled run readnulland fell back tonow - 24h. Results: the traffic from 05:27 to 06:00 UTC was never judged, and a failed or timed-out run lost its window (the documented retry had no effect).apify-ai-online-state, one record per environment (ONLINE_CHECKPOINT-<environment>), so a staging run never moves the prod checkpoint.test/checkpoint-store.test.tsruns against the SDK's real local storage and changes the default store id between "runs", like the platform. It fails on the old code (expected null to deeply equal {...}) and passes now.JUDGE_IMPL_VERSIONis not bumped: it is part of every score id, and scoring did not change.3. The scheduled run would use the wrong Langfuse project
Online mode must read "Apify AI Agent". The Actor env keys belong to "MCP Agent Evals" and must stay there, because the workflow runner starts the judge without keys. The schedule sent no keys and its README said to put them in the Actor env, which breaks one mode or the other.
src/online-project.ts): before any other Langfuse call, online mode resolves the keys' project withGET /api/public/projectsand fails the run unless its name or id equals the newlangfuseProjectinput (defaultApify AI Agent). Dataset-run mode does not change. 8 tests.RUN_ACTOR_TASK).schedule/online-task.jsonis thePOST /v2/actor-tasksbody with placeholders; the keys go into the Task's secret inputs, typed in the Console..gitignoreblocks filled-in*.local.jsoncopies.Verification
On the top of the stack with this PR:
npm ci, build,npm testfor contract (26), judge (179), workflow-runner (42) and tools (8),scenarios:check, judgetypecheck, and Prettier all pass. ESLint is clean on the changed files; the remaining errors are older lines frommain.Not verified yet
apify-aitraces since apify-ai-agent v0.3.4 are not checked yet: that needs the "Apify AI Agent" keys.POST /v2/actor-tasksencryptsisSecretinput. The README avoids the question: type the keys in the Console.