Repository navigation
feat(agent-testing): add a Template case type and let turns attach files - #1473
Merged
Merged
Conversation
- A Template case calls one of the entry agent's templates directly, the way code such as a photo check calls it outside any conversation, so a template can be tested without routing, function calls or dialog history - Turns can now attach files, which both Template and Agent cases pass on to the model, so vision templates can be tested with real photos - Saving a Template case requires entryAgentId and templateName, checks that the agent has that template, and rejects history
- The queue built AgentTestRunExecutor without ILlmProviderService, so SnapshotPricing always returned empty and the run page showed unit cost as unknown even with model pricing configured. Case cost was unaffected: TokenStatistics resolves the service on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
- Cancel now sets CancelRequested with a single-field update on Pending or Running runs instead of replacing the run document, which could revert a finished run to Running or lose case counts. - UpdateRunAsync only replaces while the stored CancelRequested matches, so the executor's whole-document writes never undo a cancel; the executor also honours a cancel sent while the run was Pending. - A case PUT without "enabled" keeps the current value instead of enabling an unreviewed draft. - The startup sweep also fails Pending runs created before the process started; they were lost with the old in-process queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… drafts - Mocks and tool assertions naming a function the entry agent lacks are kept with a warning: mocks match for every agent in the conversation, so a routed-to agent's function is valid and was being deleted. - When the model rewrites turns without their files, the files are restored by position if the turn count is unchanged, otherwise a warning reports the loss. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test - The judge resolves its completion provider from the case's scope, so its tokens and cost landed in the case's TotalTokens/Cost and skewed model comparisons. The runner now measures each judge call and takes it back out. - Judge spend is recorded on its own as JudgeTokens/JudgeCost on each case result and summed per model in the performance summary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n startup - The startup sweep failed every Running run, including runs another host was still executing: dev runs several pods, and a local host can share the same database. Those runs finished but kept the "host restarted" error text. - Runs now carry UpdateDate, set on every write. Listing, reading or deleting a run fails it when it is Running with no write for twice the suite's CaseTimeoutSeconds; a live executor writes after every case, and each case is cut off at that timeout. - Pending runs lost with a host are no longer swept; they stay Pending until triggered again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The run report showed judge spend next to the tested model's unit cost, with no way to tell which model the judge ran on - Taken from the suite the run executed with, since the suite's judge setting can change afterwards Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Abandonment checks can overwrite newer run progress or misclassify still-executing cases as failed, potentially allowing live runs to be deleted.
7 open issues
Persist the run's effective case timeout · New Prevent abandonment while case stages are still executing · New Prevent stale run snapshots from overwriting newer progress · New Match template names using the runtime's case-sensitive comparison · New Include templateName in case authoring and diff handling · New Propagate cancellation after each case update · New Support URL-only attachments in Anthropic vision cases · New
What changed in this PR
Extends the agent-testing plugin with direct Template cases and file attachments, alongside improvements to run management, cost reporting, and AI-assisted case authoring.
Changes:
- Adds Template case validation and execution, with attached files supported in both Template and Agent turns.
- Revises cancellation and abandoned-run handling, preserves omitted case-enabled values, and separates judge usage from model-under-test costs.
- Preserves routed-agent mocks and assertions with warnings, restores omitted turn files, and adds regression tests.
| File | Description |
|---|---|
| CaseAuthoringTests.cs | Tests warning-based retention and turn-file restoration. |
| AgentTestRunExecutorTests.cs | Tests pending cancellation and separate judge totals. |
| AgentTestJudgeTests.cs | Updates the driver stub for files and Template calls. |
| AgentTestControllerTests.cs | Tests abandoned-run detection and deletion. |
| AgentTestCaseRunnerTests.cs | Tests Template execution, attachments, and judge accounting. |
| LlmCaseAuthor.cs | Restores files and retains unfamiliar tool names with warnings. |
| IAgentConversationDriver.cs | Adds file parameters and direct Template execution. |
| CaseValidation.cs | Validates required Template fields and rejects history. |
| BotSharpAgentConversationDriver.cs | Forwards attachments and invokes templates through InstructService. |
| AgentTestRunQueue.cs | Removes startup reconciliation and supplies pricing services. |
| AgentTestRunExecutor.cs | Honors pending cancellation and summarizes judge pricing and spend. |
| AgentTestCaseRunner.cs | Dispatches Template calls and separates judge usage. |
| AgentTestRepository.cs | Adds targeted cancellation writes and progress timestamps. |
| AgentTestRun.cs | Adds judge pricing, judge totals, and update timestamps. |
| AgentTestDtos.cs | Adds template names and nullable enabled updates. |
| AgentTestCaseResult.cs | Records judge tokens and cost separately. |
| AgentTestCase.cs | Defines Template cases and turn attachments. |
| AgentTestController.cs | Validates templates and updates cancellation and abandonment handling. |
Review effort: Balanced
- TotalCount only counts finished executions, so a run never said how many it set out to do. PlannedCount (cases x models) is written with the Running status. - The latency, token and cost summary was computed only at the end, so the run page had no cost table mid-run and a run whose host died had none at all. It is now recomputed after every case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Liveness came from case completions, but PrepareAsync and the judge's provider call are not cut off by the case timeout, and the suite's timeout can change mid-run, so a live run could go quiet for longer than the threshold, be failed and then deleted. The executor now stamps UpdateDate every 30s while cases run; a Running run is failed after 3 minutes without one. - Failing an abandoned run was a whole-document replace of the copy just read, which could overwrite newer progress or a finished status. It is now an atomic update that applies only while the run is still Running with the UpdateDate that was read, and sets only Status, Error and CompletedAt; otherwise the current run is reloaded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…image URLs - Template names are checked with an ordinal match on save, as AgentService.RenderTemplate looks them up with ==; a name matching only ignoring case saved fine and then rendered an empty instruction. - The case author can declare templateName, so it can draft and repair a Template case. - A cancel carried over by the per-case write now stops the next case; it was checked before the write and missed. - The Anthropic provider sends URL-only images as URL image sources instead of dropping them, so vision cases with file URLs reach Claude. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Agent-testing plugin: a new Template case type with turn files, plus a round of fixes to run state, cost reporting and AI case authoring.
Template cases and turn files
entryAgentIdandtemplateName, checks that the agent has that template (exact, case-sensitive match, as the runtime looks it up), and rejects history.Run state
CancelRequestedwith a single-field update on Pending or Running runs instead of replacing the run document, which could revert a finished run to Running or lose case counts. It returns 409 when the run already finished.UpdateRunAsynconly replaces while the storedCancelRequestedmatches, so the executor's whole-document writes never undo a cancel; the executor also honours a cancel sent while the run was Pending.enabledkeeps the current value instead of enabling an unreviewed draft.UpdateDate, set on every write and by a 30s executor heartbeat while cases run; listing, reading or deleting a run fails it when it is Running with no heartbeat for 3 minutes. The heartbeat is independent of case completions, sincePrepareAsyncand the judge's provider call are not cut off by the case timeout. Failing is an atomic update conditioned on the run still being Running with theUpdateDatethat was read, and sets onlyStatus,ErrorandCompletedAt, so a stale read never overwrites newer progress or a finished status. Pending runs lost with a host stay Pending until triggered again.PlannedCount(cases x models) when they start Running, so progress reads as finished / planned.Cost reporting
ILlmProviderService, so the pricing snapshot is filled and the run page no longer shows unit cost as unknown when model pricing is configured.TotalTokens/Cost. It is now recorded asJudgeTokens/JudgeCoston each case result and summed per model in the performance summary.JudgePricing, taken from the suite it executed with, since the suite's judge setting can change afterwards.AI case authoring
templateName, so it can draft and repair a Template case.UI
Matching UI changes: SciSharp/BotSharp-UI#494.
Test plan
BotSharp.Core.UnitTestsAgentTesting (369 passed)🤖 Generated with Claude Code