Repository navigation
Releases: epam/ai-dial-admin-evaluation-framework-backend
Release list
0.5.0
Features
- Add an authenticated inbound MCP endpoint — stateless HTTP exposes
list_deploymentsandget_deploymenttools #209 (#208) - Add optional DIAL Application Route execution — per-request keys keep long evaluations authenticated and auto-share EF files (#236)
- Query test-suite runs with deployment context, run counts, metric names, overall scores, and optional total costs through the structured Query DSL #197 (#201, #206, #221, #222)
- Fetch total costs for up to 1,000 test-suite runs in one request — preserves request order and returns
nullwhen usage is absent (#205) - List suite metric definitions with declaration and schema-version details — clients no longer join them locally #224 (#226)
Fixes
- Calculate average run costs per test case instead of per usage request — corrects multi-request, multi-turn, and multi-metric suites (#207)
- Calculate test-case scores across all turns, requests, and reruns — skipped metrics are excluded and test cases remain equally weighted (#231)
- Restore application and model target listings after DIAL Core changed model pricing — accepts both scalar and object
prompt/completionvalues #243 (#244, #247) - Resolve API-key roles against issuer-specific claim paths, including array-wrapped issuer claims — multi-provider authentication now maps authorities correctly (#241, #248)
- Preserve multi-turn data, schemas, file refs, and content types in ZIP round trips — imports are transactional and archive parsing is bounded #225 (#227, #228)
- Identify the failed test case and broken file reference when ZIP export cannot download an attachment #225 (#232)
- Return template variables per request for multi-request suites — extraction and try-out now use request-indexed maps #217 (#235)
- Upgrade Jackson to
2.22.3/3.2.3across the service, CLI, runner, and build tooling — addresses CVE-2026-19032, CVE-2026-68497, CVE-2026-83557, CVE-2026-89407, CVE-2026-89425, CVE-2026-91776, and CVE-2026-91777 #242 (#233, #234, #245) - Preserve test-case values when renaming dataset schema fields — stable IDs move stored data, while bound public datasets reject destructive updates and imports #246 (#249, #254)
Other
- Move run metric snapshots to metadata storage — run deletion now cascades, existing rows are copied, and the old analytics route remains a deprecated alias #197 (#199)
- Rename the API-key role-mapping variables to
API_KEY_KEY_ROLES_MAPPING/API_KEY_JWT_ROLES_MAPPING— names now distinguish project-key from JWT-rooted per-request-key mappings; old names are no longer read (#256)
Deployment Changes
Removed environment variables
Caution
Renamed without fallback; affects every deployment with API_KEY_ENABLED=true since 0.3.0. Keeping both old names fails startup; renaming only one leaves the other mapping empty, so callers on that path get no authorities.
| Variable | Reason |
|---|---|
API_KEY_ROLES_MAPPING |
Renamed to API_KEY_KEY_ROLES_MAPPING — project-key role mapping (#256) |
API_KEY_DEFAULT_ROLES_MAPPING |
Renamed to API_KEY_JWT_ROLES_MAPPING — JWT-rooted per-request-key (userClaims) role mapping (#256) |
New environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_ENABLED |
No | false |
Adds total_cost to row-mode test_suite_runs query results. |
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_TIMEOUT_SEC |
Conditional | 2 |
End-to-end cost lookup deadline; minimum 1. |
CSV_IMPORT_ZIP_MAX_ENTRIES |
No | 1000 |
Maximum ZIP entries accepted during test-case import. |
CSV_IMPORT_ZIP_MAX_TOTAL_UNCOMPRESSED_SIZE |
No | 1GB |
Maximum total uncompressed ZIP size. |
CSV_IMPORT_ZIP_MAX_MANIFEST_SIZE |
No | 1MB |
Maximum manifest.json size. |
SPRING_AI_MCP_SERVER_ENABLED |
No | false |
Enables the inbound MCP server; /mcp returns 404 when disabled. |
SPRING_AI_MCP_SERVER_STDIO |
No | false |
Enables local stdio transport; HTTP is the deployment default. |
SPRING_AI_MCP_SERVER_NAME |
No | ai-dial-admin-evaluation-framework-backend |
Server name returned by MCP initialization. |
SPRING_AI_MCP_SERVER_VERSION |
No | Project version | Server version returned by MCP initialization. |
SPRING_AI_MCP_SERVER_TYPE |
No | SYNC |
MCP implementation type; SYNC preserves request-thread caller identity. |
SPRING_AI_MCP_SERVER_PROTOCOL |
No | STATELESS |
HTTP protocol; STREAMABLE requires one replica or sticky sessions. |
SPRING_AI_MCP_SERVER_CAPABILITIES_TOOL |
No | true |
Advertises MCP tools. |
SPRING_AI_MCP_SERVER_CAPABILITIES_RESOURCE |
No | false |
Advertises MCP resources; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_PROMPT |
No | false |
Advertises MCP prompts; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_COMPLETION |
No | false |
Advertises MCP completions; none ship in this release. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_MCP_ENDPOINT |
No | /mcp |
Sets the authenticated MCP HTTP path. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_DISALLOW_DELETE |
No | false |
Disables session termination by DELETE in STREAMABLE mode. |
SPRING_AI_MCP_SERVER_ANNOTATION_SCANNER_ENABLED |
No | true |
Discovers @McpTool methods. |
SPRING_AI_MCP_SERVER_INSTRUCTIONS |
No | Built-in workflow guidance | Instructions returned by MCP initialization. |
Enabling Application mode for test suite runs
Application mode (see DIAL Configuration changes below) routes long-running evals through a DIAL
Application Route instead of the user's JWT, so a run no longer fails when the JWT expires mid-run.
It is off by default. To turn it on:
1. Enable DIAL API-Key authentication first — the internal run-execution endpoint is authenticated
via the Api-Key header, not the user's JWT, so Application mode cannot start without it:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
API_KEY_ENABLED |
false |
Yes | Enables DIAL API-Key authentication via the Api-Key request header. Must be true before DIAL_APP_PROXY_ENABLED can be set — EF fails fast at startup otherwise. |
API_KEY_CORE_URL |
- | Yes | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. |
API_KEY_KEY_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core project-key role names to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. At least one of this or API_KEY_JWT_ROLES_MAPPING must be non-empty. |
API_KEY_JWT_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core roles from the JWT-rooted userClaims response shape to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
No | Claim name read from the introspection response's userClaims object to obtain the caller's raw roles. Set to dial_roles to match this claim naming in Core. |
2. Turn on the Application Route flag:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
DIAL_APP_PROXY_ENABLED |
false |
No | Enables DIAL Application Routes mode for run execution — eval runs are triggered via DIAL Core's Application Route using a per-request key instead of the user's JWT. Requires API_KEY_ENABLED=true. |
DIAL_APP_PROXY_DEPLOYMENT_NAME |
EF |
No | DIAL Core application deployment name. Must match the applications.<name> key used in the DIAL Core config snippet in DIAL Configuration changes below. |
DIAL_APP_PROXY_HEARTBEAT_INTERVAL_MS |
30000 |
No | Heartbeat interval for the internal run SSE connection. |
DIAL_APP_PROXY_TRIGGER_READ_TIMEOUT_MS |
43200000 |
No | DIAL Application Route trigger timeout in milliseconds. |
3. Recommended tuning (not required to enable the mode, but needed for long evals):
| Environment Variable | Default | Required | Description |
|---|---|---|---|
TEST_SUITE_RUN_SSE_TIMEOUT_MINUTES |
30 |
No | SSE client connection timeout, in minutes. Raise this under Application mode (e.g. 720 for up to 12 hours) so the status stream doesn't time out before a long-running eval completes. |
Behavioral changes
Note
These take effect automatically on upgrade unless noted otherwise.
- API-key role mapping — with OIDC providers configured, introspection matches
userClaims.issand unions the provider'sroleClaims;API_KEY_USER_CLAIMS_ROLE_CLAIMis now the fallback (#241, #248) - dial-adas compatibility — run-cost averages now call
POST /v1/queries/execute-sql; verify the deployed dial-adas version exposes this endpoint (#207)
DIAL Configuration changes
Important
DIAL Core registration is required only when DIAL_APP_PROXY_ENABLED=true; direct JWT execution remains the default.
Required setup (#236):
- Add the
applications.EFblock to DIAL Core configuration, or use the application name set byDIAL_APP_PROXY_DEPLOYMENT_NAME, and grant it access to the EF file bucket. Apply the snippet withhttp://eval-framework-urlreplacement:
{
"applications": {
"EF...0.5.0-rc.2
Features
- Add an authenticated inbound MCP endpoint — stateless HTTP exposes
list_deploymentsandget_deploymenttools #209 (#208) - Add optional DIAL Application Route execution — per-request keys keep long evaluations authenticated and auto-share EF files (#236)
- Query test-suite runs with deployment context, run counts, metric names, overall scores, and optional total costs through the structured Query DSL #197 (#201, #206, #221, #222)
- Fetch total costs for up to 1,000 test-suite runs in one request — preserves request order and returns
nullwhen usage is absent (#205) - List suite metric definitions with declaration and schema-version details — clients no longer join them locally #224 (#226)
Fixes
- Calculate average run costs per test case instead of per usage request — corrects multi-request, multi-turn, and multi-metric suites (#207)
- Calculate test-case scores across all turns, requests, and reruns — skipped metrics are excluded and test cases remain equally weighted (#231)
- Restore application and model target listings after DIAL Core changed model pricing — accepts both scalar and object
prompt/completionvalues #243 (#244, #247) - Resolve API-key roles against issuer-specific claim paths, including array-wrapped issuer claims — multi-provider authentication now maps authorities correctly (#241, #248)
- Preserve multi-turn data, schemas, file refs, and content types in ZIP round trips — imports are transactional and archive parsing is bounded #225 (#227, #228)
- Identify the failed test case and broken file reference when ZIP export cannot download an attachment #225 (#232)
- Return template variables per request for multi-request suites — extraction and try-out now use request-indexed maps #217 (#235)
- Upgrade Jackson to
2.22.3/3.2.3across the service, CLI, runner, and build tooling — addresses CVE-2026-19032, CVE-2026-68497, CVE-2026-83557, CVE-2026-89407, CVE-2026-89425, CVE-2026-91776, and CVE-2026-91777 #242 (#233, #234, #245) - Preserve test-case values when renaming dataset schema fields — stable IDs move stored data, while bound public datasets reject destructive updates and imports #246 (#249, #254)
Other
- Move run metric snapshots to metadata storage — run deletion now cascades, existing rows are copied, and the old analytics route remains a deprecated alias #197 (#199)
- Rename the API-key role-mapping variables to
API_KEY_KEY_ROLES_MAPPING/API_KEY_JWT_ROLES_MAPPING— names now distinguish project-key from JWT-rooted per-request-key mappings; old names are no longer read (#256)
Deployment Changes
Removed environment variables
Caution
Renamed without fallback; affects every deployment with API_KEY_ENABLED=true since 0.3.0. Keeping both old names fails startup; renaming only one leaves the other mapping empty, so callers on that path get no authorities.
| Variable | Reason |
|---|---|
API_KEY_ROLES_MAPPING |
Renamed to API_KEY_KEY_ROLES_MAPPING — project-key role mapping (#256) |
API_KEY_DEFAULT_ROLES_MAPPING |
Renamed to API_KEY_JWT_ROLES_MAPPING — JWT-rooted per-request-key (userClaims) role mapping (#256) |
New environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_ENABLED |
No | false |
Adds total_cost to row-mode test_suite_runs query results. |
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_TIMEOUT_SEC |
Conditional | 2 |
End-to-end cost lookup deadline; minimum 1. |
CSV_IMPORT_ZIP_MAX_ENTRIES |
No | 1000 |
Maximum ZIP entries accepted during test-case import. |
CSV_IMPORT_ZIP_MAX_TOTAL_UNCOMPRESSED_SIZE |
No | 1GB |
Maximum total uncompressed ZIP size. |
CSV_IMPORT_ZIP_MAX_MANIFEST_SIZE |
No | 1MB |
Maximum manifest.json size. |
SPRING_AI_MCP_SERVER_ENABLED |
No | false |
Enables the inbound MCP server; /mcp returns 404 when disabled. |
SPRING_AI_MCP_SERVER_STDIO |
No | false |
Enables local stdio transport; HTTP is the deployment default. |
SPRING_AI_MCP_SERVER_NAME |
No | ai-dial-admin-evaluation-framework-backend |
Server name returned by MCP initialization. |
SPRING_AI_MCP_SERVER_VERSION |
No | Project version | Server version returned by MCP initialization. |
SPRING_AI_MCP_SERVER_TYPE |
No | SYNC |
MCP implementation type; SYNC preserves request-thread caller identity. |
SPRING_AI_MCP_SERVER_PROTOCOL |
No | STATELESS |
HTTP protocol; STREAMABLE requires one replica or sticky sessions. |
SPRING_AI_MCP_SERVER_CAPABILITIES_TOOL |
No | true |
Advertises MCP tools. |
SPRING_AI_MCP_SERVER_CAPABILITIES_RESOURCE |
No | false |
Advertises MCP resources; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_PROMPT |
No | false |
Advertises MCP prompts; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_COMPLETION |
No | false |
Advertises MCP completions; none ship in this release. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_MCP_ENDPOINT |
No | /mcp |
Sets the authenticated MCP HTTP path. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_DISALLOW_DELETE |
No | false |
Disables session termination by DELETE in STREAMABLE mode. |
SPRING_AI_MCP_SERVER_ANNOTATION_SCANNER_ENABLED |
No | true |
Discovers @McpTool methods. |
SPRING_AI_MCP_SERVER_INSTRUCTIONS |
No | Built-in workflow guidance | Instructions returned by MCP initialization. |
Enabling Application mode for test suite runs
Application mode (see DIAL Configuration changes below) routes long-running evals through a DIAL
Application Route instead of the user's JWT, so a run no longer fails when the JWT expires mid-run.
It is off by default. To turn it on:
1. Enable DIAL API-Key authentication first — the internal run-execution endpoint is authenticated
via the Api-Key header, not the user's JWT, so Application mode cannot start without it:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
API_KEY_ENABLED |
false |
Yes | Enables DIAL API-Key authentication via the Api-Key request header. Must be true before DIAL_APP_PROXY_ENABLED can be set — EF fails fast at startup otherwise. |
API_KEY_CORE_URL |
- | Yes | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. |
API_KEY_KEY_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core project-key role names to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. At least one of this or API_KEY_JWT_ROLES_MAPPING must be non-empty. |
API_KEY_JWT_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core roles from the JWT-rooted userClaims response shape to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
No | Claim name read from the introspection response's userClaims object to obtain the caller's raw roles. Set to dial_roles to match this claim naming in Core. |
2. Turn on the Application Route flag:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
DIAL_APP_PROXY_ENABLED |
false |
No | Enables DIAL Application Routes mode for run execution — eval runs are triggered via DIAL Core's Application Route using a per-request key instead of the user's JWT. Requires API_KEY_ENABLED=true. |
DIAL_APP_PROXY_DEPLOYMENT_NAME |
EF |
No | DIAL Core application deployment name. Must match the applications.<name> key used in the DIAL Core config snippet in DIAL Configuration changes below. |
DIAL_APP_PROXY_HEARTBEAT_INTERVAL_MS |
30000 |
No | Heartbeat interval for the internal run SSE connection. |
DIAL_APP_PROXY_TRIGGER_READ_TIMEOUT_MS |
43200000 |
No | DIAL Application Route trigger timeout in milliseconds. |
3. Recommended tuning (not required to enable the mode, but needed for long evals):
| Environment Variable | Default | Required | Description |
|---|---|---|---|
TEST_SUITE_RUN_SSE_TIMEOUT_MINUTES |
30 |
No | SSE client connection timeout, in minutes. Raise this under Application mode (e.g. 720 for up to 12 hours) so the status stream doesn't time out before a long-running eval completes. |
Behavioral changes
Note
These take effect automatically on upgrade unless noted otherwise.
- API-key role mapping — with OIDC providers configured, introspection matches
userClaims.issand unions the provider'sroleClaims;API_KEY_USER_CLAIMS_ROLE_CLAIMis now the fallback (#241, #248) - dial-adas compatibility — run-cost averages now call
POST /v1/queries/execute-sql; verify the deployed dial-adas version exposes this endpoint (#207)
DIAL Configuration changes
Important
DIAL Core registration is required only when DIAL_APP_PROXY_ENABLED=true; direct JWT execution remains the default.
Required setup (#236):
- Add the
applications.EFblock to DIAL Core configuration, or use the application name set byDIAL_APP_PROXY_DEPLOYMENT_NAME, and grant it access to the EF file bucket. Apply the snippet withhttp://eval-framework-urlreplacement:
{
"applications": {
"EF...0.5.0-rc.1
Features
- Add an authenticated inbound MCP endpoint — stateless HTTP exposes
list_deploymentsandget_deploymenttools #209 (#208) - Add optional DIAL Application Route execution — per-request keys keep long evaluations authenticated and auto-share EF files (#236)
- Query test-suite runs with deployment context, run counts, metric names, overall scores, and optional total costs through the structured Query DSL #197 (#201, #206, #221, #222)
- Fetch total costs for up to 1,000 test-suite runs in one request — preserves request order and returns
nullwhen usage is absent (#205) - List suite metric definitions with declaration and schema-version details — clients no longer join them locally #224 (#226)
Fixes
- Calculate average run costs per test case instead of per usage request — corrects multi-request, multi-turn, and multi-metric suites (#207)
- Calculate test-case scores across all turns, requests, and reruns — skipped metrics are excluded and test cases remain equally weighted (#231)
- Restore application and model target listings after DIAL Core changed model pricing — accepts both scalar and object
prompt/completionvalues #243 (#244, #247) - Resolve API-key roles against issuer-specific claim paths, including array-wrapped issuer claims — multi-provider authentication now maps authorities correctly (#241, #248)
- Preserve multi-turn data, schemas, file refs, and content types in ZIP round trips — imports are transactional and archive parsing is bounded #225 (#227, #228)
- Identify the failed test case and broken file reference when ZIP export cannot download an attachment #225 (#232)
- Return template variables per request for multi-request suites — extraction and try-out now use request-indexed maps #217 (#235)
- Upgrade Jackson to
2.22.3/3.2.3across the service, CLI, runner, and build tooling — addresses CVE-2026-19032, CVE-2026-68497, CVE-2026-83557, CVE-2026-89407, CVE-2026-89425, CVE-2026-91776, and CVE-2026-91777 #242 (#233, #234, #245)
Other
- Move run metric snapshots to metadata storage — run deletion now cascades, existing rows are copied, and the old analytics route remains a deprecated alias #197 (#199)
- Rename the API-key role-mapping variables to
API_KEY_KEY_ROLES_MAPPING/API_KEY_JWT_ROLES_MAPPING— names now distinguish project-key from JWT-rooted per-request-key mappings; old names are no longer read (#256)
Deployment Changes
Removed environment variables
Caution
Renamed without fallback; affects every deployment with API_KEY_ENABLED=true since 0.3.0. Keeping both old names fails startup; renaming only one leaves the other mapping empty, so callers on that path get no authorities.
| Variable | Reason |
|---|---|
API_KEY_ROLES_MAPPING |
Renamed to API_KEY_KEY_ROLES_MAPPING — project-key role mapping (#256) |
API_KEY_DEFAULT_ROLES_MAPPING |
Renamed to API_KEY_JWT_ROLES_MAPPING — JWT-rooted per-request-key (userClaims) role mapping (#256) |
New environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_ENABLED |
No | false |
Adds total_cost to row-mode test_suite_runs query results. |
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_TIMEOUT_SEC |
Conditional | 2 |
End-to-end cost lookup deadline; minimum 1. |
CSV_IMPORT_ZIP_MAX_ENTRIES |
No | 1000 |
Maximum ZIP entries accepted during test-case import. |
CSV_IMPORT_ZIP_MAX_TOTAL_UNCOMPRESSED_SIZE |
No | 1GB |
Maximum total uncompressed ZIP size. |
CSV_IMPORT_ZIP_MAX_MANIFEST_SIZE |
No | 1MB |
Maximum manifest.json size. |
SPRING_AI_MCP_SERVER_ENABLED |
No | false |
Enables the inbound MCP server; /mcp returns 404 when disabled. |
SPRING_AI_MCP_SERVER_STDIO |
No | false |
Enables local stdio transport; HTTP is the deployment default. |
SPRING_AI_MCP_SERVER_NAME |
No | ai-dial-admin-evaluation-framework-backend |
Server name returned by MCP initialization. |
SPRING_AI_MCP_SERVER_VERSION |
No | Project version | Server version returned by MCP initialization. |
SPRING_AI_MCP_SERVER_TYPE |
No | SYNC |
MCP implementation type; SYNC preserves request-thread caller identity. |
SPRING_AI_MCP_SERVER_PROTOCOL |
No | STATELESS |
HTTP protocol; STREAMABLE requires one replica or sticky sessions. |
SPRING_AI_MCP_SERVER_CAPABILITIES_TOOL |
No | true |
Advertises MCP tools. |
SPRING_AI_MCP_SERVER_CAPABILITIES_RESOURCE |
No | false |
Advertises MCP resources; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_PROMPT |
No | false |
Advertises MCP prompts; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_COMPLETION |
No | false |
Advertises MCP completions; none ship in this release. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_MCP_ENDPOINT |
No | /mcp |
Sets the authenticated MCP HTTP path. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_DISALLOW_DELETE |
No | false |
Disables session termination by DELETE in STREAMABLE mode. |
SPRING_AI_MCP_SERVER_ANNOTATION_SCANNER_ENABLED |
No | true |
Discovers @McpTool methods. |
SPRING_AI_MCP_SERVER_INSTRUCTIONS |
No | Built-in workflow guidance | Instructions returned by MCP initialization. |
Enabling Application mode for test suite runs
Application mode (see DIAL Configuration changes below) routes long-running evals through a DIAL
Application Route instead of the user's JWT, so a run no longer fails when the JWT expires mid-run.
It is off by default. To turn it on:
1. Enable DIAL API-Key authentication first — the internal run-execution endpoint is authenticated
via the Api-Key header, not the user's JWT, so Application mode cannot start without it:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
API_KEY_ENABLED |
false |
Yes | Enables DIAL API-Key authentication via the Api-Key request header. Must be true before DIAL_APP_PROXY_ENABLED can be set — EF fails fast at startup otherwise. |
API_KEY_CORE_URL |
- | Yes | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. |
API_KEY_KEY_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core project-key role names to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. At least one of this or API_KEY_JWT_ROLES_MAPPING must be non-empty. |
API_KEY_JWT_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core roles from the JWT-rooted userClaims response shape to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
No | Claim name read from the introspection response's userClaims object to obtain the caller's raw roles. Set to dial_roles to match this claim naming in Core. |
2. Turn on the Application Route flag:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
DIAL_APP_PROXY_ENABLED |
false |
No | Enables DIAL Application Routes mode for run execution — eval runs are triggered via DIAL Core's Application Route using a per-request key instead of the user's JWT. Requires API_KEY_ENABLED=true. |
DIAL_APP_PROXY_DEPLOYMENT_NAME |
EF |
No | DIAL Core application deployment name. Must match the applications.<name> key used in the DIAL Core config snippet in DIAL Configuration changes below. |
DIAL_APP_PROXY_HEARTBEAT_INTERVAL_MS |
30000 |
No | Heartbeat interval for the internal run SSE connection. |
DIAL_APP_PROXY_TRIGGER_READ_TIMEOUT_MS |
43200000 |
No | DIAL Application Route trigger timeout in milliseconds. |
3. Recommended tuning (not required to enable the mode, but needed for long evals):
| Environment Variable | Default | Required | Description |
|---|---|---|---|
TEST_SUITE_RUN_SSE_TIMEOUT_MINUTES |
30 |
No | SSE client connection timeout, in minutes. Raise this under Application mode (e.g. 720 for up to 12 hours) so the status stream doesn't time out before a long-running eval completes. |
Behavioral changes
Note
These take effect automatically on upgrade unless noted otherwise.
- API-key role mapping — with OIDC providers configured, introspection matches
userClaims.issand unions the provider'sroleClaims;API_KEY_USER_CLAIMS_ROLE_CLAIMis now the fallback (#241, #248) - dial-adas compatibility — run-cost averages now call
POST /v1/queries/execute-sql; verify the deployed dial-adas version exposes this endpoint (#207)
DIAL Configuration changes
Important
DIAL Core registration is required only when DIAL_APP_PROXY_ENABLED=true; direct JWT execution remains the default.
Required setup (#236):
- Add the
applications.EFblock to DIAL Core configuration, or use the application name set byDIAL_APP_PROXY_DEPLOYMENT_NAME, and grant it access to the EF file bucket. Apply the snippet withhttp://eval-framework-urlreplacement:
{
"applications": {
"EF": { ...0.5.0-rc.0
Features
- Add an authenticated inbound MCP endpoint — stateless HTTP exposes
list_deploymentsandget_deploymenttools #209 (#208) - Add optional DIAL Application Route execution — per-request keys keep long evaluations authenticated and auto-share EF files (#236)
- Query test-suite runs with deployment context, run counts, metric names, overall scores, and optional total costs through the structured Query DSL #197 (#201, #206, #221, #222)
- Fetch total costs for up to 1,000 test-suite runs in one request — preserves request order and returns
nullwhen usage is absent (#205) - List suite metric definitions with declaration and schema-version details — clients no longer join them locally #224 (#226)
Fixes
- Calculate average run costs per test case instead of per usage request — corrects multi-request, multi-turn, and multi-metric suites (#207)
- Calculate test-case scores across all turns, requests, and reruns — skipped metrics are excluded and test cases remain equally weighted (#231)
- Restore application and model target listings after DIAL Core changed model pricing — accepts both scalar and object
prompt/completionvalues #243 (#244, #247) - Resolve API-key roles against issuer-specific claim paths, including array-wrapped issuer claims — multi-provider authentication now maps authorities correctly (#241, #248)
- Preserve multi-turn data, schemas, file refs, and content types in ZIP round trips — imports are transactional and archive parsing is bounded #225 (#227, #228)
- Identify the failed test case and broken file reference when ZIP export cannot download an attachment #225 (#232)
- Return template variables per request for multi-request suites — extraction and try-out now use request-indexed maps #217 (#235)
- Upgrade Jackson to
2.22.3/3.2.3across the service, CLI, runner, and build tooling — addresses CVE-2026-19032, CVE-2026-68497, CVE-2026-83557, CVE-2026-89407, CVE-2026-89425, CVE-2026-91776, and CVE-2026-91777 #242 (#233, #234, #245)
Other
- Move run metric snapshots to metadata storage — run deletion now cascades, existing rows are copied, and the old analytics route remains a deprecated alias #197 (#199)
Deployment Changes
New environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_ENABLED |
No | false |
Adds total_cost to row-mode test_suite_runs query results. |
QUERY_DSL_EXTENSION_TEST_SUITE_RUN_COST_TIMEOUT_SEC |
Conditional | 2 |
End-to-end cost lookup deadline; minimum 1. |
CSV_IMPORT_ZIP_MAX_ENTRIES |
No | 1000 |
Maximum ZIP entries accepted during test-case import. |
CSV_IMPORT_ZIP_MAX_TOTAL_UNCOMPRESSED_SIZE |
No | 1GB |
Maximum total uncompressed ZIP size. |
CSV_IMPORT_ZIP_MAX_MANIFEST_SIZE |
No | 1MB |
Maximum manifest.json size. |
SPRING_AI_MCP_SERVER_ENABLED |
No | false |
Enables the inbound MCP server; /mcp returns 404 when disabled. |
SPRING_AI_MCP_SERVER_STDIO |
No | false |
Enables local stdio transport; HTTP is the deployment default. |
SPRING_AI_MCP_SERVER_NAME |
No | ai-dial-admin-evaluation-framework-backend |
Server name returned by MCP initialization. |
SPRING_AI_MCP_SERVER_VERSION |
No | Project version | Server version returned by MCP initialization. |
SPRING_AI_MCP_SERVER_TYPE |
No | SYNC |
MCP implementation type; SYNC preserves request-thread caller identity. |
SPRING_AI_MCP_SERVER_PROTOCOL |
No | STATELESS |
HTTP protocol; STREAMABLE requires one replica or sticky sessions. |
SPRING_AI_MCP_SERVER_CAPABILITIES_TOOL |
No | true |
Advertises MCP tools. |
SPRING_AI_MCP_SERVER_CAPABILITIES_RESOURCE |
No | false |
Advertises MCP resources; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_PROMPT |
No | false |
Advertises MCP prompts; none ship in this release. |
SPRING_AI_MCP_SERVER_CAPABILITIES_COMPLETION |
No | false |
Advertises MCP completions; none ship in this release. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_MCP_ENDPOINT |
No | /mcp |
Sets the authenticated MCP HTTP path. |
SPRING_AI_MCP_SERVER_STREAMABLE_HTTP_DISALLOW_DELETE |
No | false |
Disables session termination by DELETE in STREAMABLE mode. |
SPRING_AI_MCP_SERVER_ANNOTATION_SCANNER_ENABLED |
No | true |
Discovers @McpTool methods. |
SPRING_AI_MCP_SERVER_INSTRUCTIONS |
No | Built-in workflow guidance | Instructions returned by MCP initialization. |
Enabling Application mode for test suite runs
Application mode (see DIAL Configuration changes below) routes long-running evals through a DIAL
Application Route instead of the user's JWT, so a run no longer fails when the JWT expires mid-run.
It is off by default. To turn it on:
1. Enable DIAL API-Key authentication first — the internal run-execution endpoint is authenticated
via the Api-Key header, not the user's JWT, so Application mode cannot start without it:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
API_KEY_ENABLED |
false |
Yes | Enables DIAL API-Key authentication via the Api-Key request header. Must be true before DIAL_APP_PROXY_ENABLED can be set — EF fails fast at startup otherwise. |
API_KEY_CORE_URL |
- | Yes | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. |
API_KEY_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core project-key role names to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. At least one of this or API_KEY_DEFAULT_ROLES_MAPPING must be non-empty. |
API_KEY_DEFAULT_ROLES_MAPPING |
- (empty) | Conditional | JSON object mapping DIAL Core roles from the JWT-rooted userClaims response shape to EF authority strings, e.g. {"admin":["FULL_ADMIN"],"default":["READ_ONLY_ADMIN"]}. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
No | Claim name read from the introspection response's userClaims object to obtain the caller's raw roles. Set to dial_roles to match this claim naming in Core. |
2. Turn on the Application Route flag:
| Environment Variable | Default | Required | Description |
|---|---|---|---|
DIAL_APP_PROXY_ENABLED |
false |
No | Enables DIAL Application Routes mode for run execution — eval runs are triggered via DIAL Core's Application Route using a per-request key instead of the user's JWT. Requires API_KEY_ENABLED=true. |
DIAL_APP_PROXY_DEPLOYMENT_NAME |
EF |
No | DIAL Core application deployment name. Must match the applications.<name> key used in the DIAL Core config snippet in DIAL Configuration changes below. |
DIAL_APP_PROXY_HEARTBEAT_INTERVAL_MS |
30000 |
No | Heartbeat interval for the internal run SSE connection. |
DIAL_APP_PROXY_TRIGGER_READ_TIMEOUT_MS |
43200000 |
No | DIAL Application Route trigger timeout in milliseconds. |
3. Recommended tuning (not required to enable the mode, but needed for long evals):
| Environment Variable | Default | Required | Description |
|---|---|---|---|
TEST_SUITE_RUN_SSE_TIMEOUT_MINUTES |
30 |
No | SSE client connection timeout, in minutes. Raise this under Application mode (e.g. 720 for up to 12 hours) so the status stream doesn't time out before a long-running eval completes. |
Behavioral changes
Note
These take effect automatically on upgrade unless noted otherwise.
- API-key role mapping — with OIDC providers configured, introspection matches
userClaims.issand unions the provider'sroleClaims;API_KEY_USER_CLAIMS_ROLE_CLAIMis now the fallback (#241, #248) - dial-adas compatibility — run-cost averages now call
POST /v1/queries/execute-sql; verify the deployed dial-adas version exposes this endpoint (#207)
DIAL Configuration changes
Important
DIAL Core registration is required only when DIAL_APP_PROXY_ENABLED=true; direct JWT execution remains the default.
Required setup (#236):
- Add the
applications.EFblock to DIAL Core configuration, or use the application name set byDIAL_APP_PROXY_DEPLOYMENT_NAME, and grant it access to the EF file bucket. Apply the snippet withhttp://eval-framework-urlreplacement:
{
"applications": {
"EF": {
"endpoint": "http://xxx",
"display_name": "EF",
"routes": {
"runs": { ...0.4.0
Features
- add endpoint to retrieve run costs (#160)
- add overall score computation per test case (#178)
- cancellation status update #187 #191 (#190)
- deployment costs endpoint (#202)
- deployment id validation #188 (#195)
- extend metrics api #182 (#183)
- extra metric provider env-driven config #167 (#166)
- migrate deployment-by-id lookup to unified /v1/deployments/{id} endpoint (#194)
- multi request test suites #98 (#114)
- openai Responses API support #20 (#186)
- query deployment without type (#180)
- support Anthropic Messages API (#193)
- support try-out for multi-request test suites #162 (#163)
- username resolution for test suite's created_by #47 (#184)
Fixes
CI
- (deps) bump epam/ai-dial-ci from 4.9.0 to 4.11.0 (#189)
- (deps) bump step-security/harden-runner from 2.20.1 to 2.21.0 in the github-actions group across 1 directory (#156)
- (deps) bump the ai-dial-ci group with 4 updates (#165)
Other
0.4.0-rc.0
Features
- Add per-test-case overall score and pass/fail — each eval-summary row gains nullable
score/passed, computed by graftingid IN (…)+GROUP BY idonto the existing Phase-3 scoring query so all three definition types (mean,weighted_mean,custom_function) work uniformly; the new optional suite fieldtestCaseOverallScorelets per-row scoring use a different definition than the run-leveloverall, and both fields are filterable/sortable through the Query DSL #85 (#178) - Cancellation is now interrupt-driven and observable —
POST /cancelsynchronously writes the newCANCELLINGstatus (RUNNING → CANCELLING → CANCELLED, idempotent, SSE-emitted, reconciled on restart), each run owns an executor shut down withshutdownNow()so in-flight calls abort immediately instead of draining a grace period, and an analytics write failure now correctly ends the runFAILEDrather thanCANCELLED#187 #191 (#190) - Add run cost analytics —
GET /api/v1/costs/test-suite-run/{id}returnsavgTestCaseCostandavgMetricEvalCost, the average per-call price of the execution and metric-evaluation phases read live from dial-adas usage logs and correlated through theeval.run.id/eval.phaseOTel baggage the service already emits #124 (#160) - Add deployment cost analytics —
GET /api/v1/costs/deployment/{id}?from&toreturnstotalTestCaseCostandtotalMetricEvalCostover an inclusive epoch-millisecond range, answering "what has this deployment cost this month" across all runs; all cost logic moves into a dedicatedCostServiceandGET /api/v1/test-suite-runs/{id}/costsstays as a backward-compatible alias #65 (#202) - Support the Anthropic Messages API — a suite targets a Claude-style deployment by setting
endpointRef.relativeUrlPattern = "/anthropic/v1/messages", which passes through to DIAL Core's deployment-less route (the body'smodelfield does the routing), andStreamingResponseAccumulatorgains a third mode that reassemblesmessage_start/content_block_delta/message_deltaSSE events into a proper non-streaming response instead of a raw{"events":[…]}envelope #168 (#193) - Support the OpenAI Responses API —
/openai/v1/responsespaths now pass throughDialCoreUrlBuilderverbatim, and a newResponsesApiAccumulatoruses the terminalresponse.completed/incomplete/failedevent'sresponseobject verbatim, so response columns such asoutput[0].content[0].textresolve identically whether the deployment streamed or not #20 (#186) - Validate that a request body's
modelmatches the suite's deployment — the fixed-path Responses and Messages APIs route onmodel, not the URL, so a mismatch silently executed against a different deployment; a singleRequestModelValidatornow enforces it statically at suite save (soft warning, marks the suite invalid) and again at the final pre-invocation boundary for runs, CLI--deployment-idoverrides and try-it-out, per request in a chain and per turn #188 (#195) - Look up a deployment without knowing its type —
GET /api/v1/deployments/all/{id}resolves any deployment by ID alone, backed by DIAL Core's unifiedGET /v1/deployments/{id}in a single call instead of three concurrent type probes, and a missing deployment now returns a real404instead of a generic502 UPSTREAM_NOT_FOUND#179 #192 (#180, #194) - Add
GET /api/v1/metric-declarations/versions/latest— returns every metric declaration joined with its latest schema version in one call, so clients no longer fetch versions per declaration; served by a new unique(metric_declaration_id, schema_version DESC)index #182 (#183) - Resolve
createdByto a display name — withSECURITY_JWT_RESOLVE_USER_NAME=truethe raw JWT claim (an opaque id in most IdPs) is replaced at creation time byuserDisplayNamefrom DIAL Core'sGET /v1/user/info, falling back to the claim on a missing name or any Core error so the request never fails #47 (#184)
Fixes
- Warn when a required MCP tool argument is left empty —
MCP_TOOLsuite validation never consultedtoolRef.inputSchema, so a blank constant, a missing key or a placeholder bound to a blank constant persisted as valid and failed only at run time inside the tool; a schema-driven coverage check now emits aREQUIREDwarning at$.argumentTemplate.argumentsand marks the suite invalid #69 (#174) - Stop leaking raw SQL in the private-dataset binding error — the PL/pgSQL guard's
P0001error had no Spring translation, so jOOQ's exception fell through to the generic 500 handler and echoed the failing statement to the client; binding a second suite to aPRIVATEdataset is now an app-level409 PRIVATE_DATASET_ALREADY_BOUNDwith a fixed message, the DB message logged and never returned #22 (#172)
Other
- Promote the Unified Query DSL out of
experimental—com.epam.aidial.evaluation.experimental.query.*becomescom.epam.aidial.evaluation.query.*anddocs/experimental/becomesdocs/query-dsl/, reflecting that the DSL is load-bearing production functionality; no REST API, DTO, database, configuration or runtime behavior change #115 (#161) - Upgrade Spring Boot to
4.1.1and drop the build-time dependency pins it now subsumes — the explicit CVE-driven version constraints added in0.3.0are supplied by the Boot BOM #171 (#173)
Deployment Changes
New environment variables
Required: ✅ = must be supplied, the service will not start without it. ❌ = ships with a working default. Nothing in this release is required in a default deployment;
⚠️ in a description marks a variable that becomes mandatory once you switch on the feature it belongs to.
| Variable | Required | Default | Description |
|---|---|---|---|
DIAL_ADAS_URL |
❌ | http://localhost:8087 |
GET /api/v1/costs/** returns anything useful. |
DIAL_ADAS_CONNECT_TIMEOUT_MS |
❌ | 5000 |
Connection timeout in milliseconds for the dial-adas client. |
DIAL_ADAS_READ_TIMEOUT_MS |
❌ | 30000 |
Read timeout in milliseconds for the dial-adas client. |
SECURITY_JWT_RESOLVE_USER_NAME |
✅ | true |
When true, AuthorResolver calls DIAL Core GET /v1/user/info with the caller's bearer token and stores userDisplayName in createdBy instead of the raw claim value. Applies only when config.rest.security.mode=oidc. The stored value is a snapshot taken at creation time. |
Removed environment variables
| Variable | Reason |
|---|---|
TEST_SUITE_RUN_EXECUTOR_CORE_POOL_SIZE |
The fixed run-execution thread pool is replaced by an unbounded thread-per-run SimpleAsyncTaskExecutor; concurrency is now governed solely by the run limits (#190) |
TEST_SUITE_RUN_EXECUTOR_MAX_POOL_SIZE |
Same as above (#190) |
TEST_SUITE_RUN_EXECUTOR_QUEUE_CAPACITY |
Same as above (#190) |
TEST_SUITE_RUN_EXECUTION_CANCELLATION_GRACE_PERIOD_MS |
Cancellation is interrupt-driven — there is no drain phase left to bound (#190) |
TEST_SUITE_RUN_CANCELLATION_GRACE_PERIOD_MS / CLI_RUN_CANCELLATION_GRACE_PERIOD_MS |
The eval-cli equivalents of the above (#190) |
Note
All five are ignored if still set — a stale value will not fail startup.
Behavioral changes
Note
These take effect automatically on upgrade — no operator action required, but run concurrency and cancellation timing differ from 0.3.0.
- Run concurrency — the fixed pool (core
5/ max10/ queue50) that capped real concurrency at 5 despitemax-concurrent-runs-global = 20is gone; a run now gets its own thread and the configured run limits are the only ceiling. Expect more runs genuinely executing in parallel, and size the database connection pools and deployment quotas accordingly (#190) - Cancellation timing and outcome — cancelling no longer waits out a grace period; the run's executor is interrupted immediately and the metric-provider client moves to
JdkClientHttpRequestFactoryso a call blocked on the 150 s read timeout aborts at once. A run whose analytics batch write fails now endsFAILED/ANALYTICS_WRITE_FAILEDinstead of being mislabelledCANCELLED, and a late cancel can never endCOMPLETED#187 #191 (#190) - Deployment by-id lookup —
GET /api/v1/deployments/all/{id}issues one request to DIAL Core's unified endpoint instead of three concurrent probes (3x less upstream traffic), and an unknown deployment returns404where it previously returned502 UPSTREAM_NOT_FOUND#192 (#194) - Suite validity on model-selecting endpoints — a suite whose literal request body sets
modelto something other than itsdeploymentRef.idis now persisted as invalid with aREQUEST_BODY_VALIDATION_ERRORwarning, so it is excluded from runnable selection. Suites saved as valid before this rule existed are caught at run time instead and produce an ERROR row #188 (#193, #195) - Private-dataset double binding — returns
409 PRIVATE_DATASET_ALREADY_BOUNDfrom the service layer instead of a500carrying the driver message; the PL/pgSQL trigger is demoted to a race backstop #22 (#172)
Database migrations
Note
Flyway applies these automatically on startup; the service account needs DDL privileges on both schemas.
- analytics —
V1.19__CreateTestCaseEvalScoresTable.sql— createstest_case_eval_scores(eval_summary_idPK,score,passed,computed_at_ms) for per-row scoring; forward-only, historical rows read back asnull(#178) - meta —
V1.30__AddUniqueIndexToMetricDeclarationVersions.sql— drops the non-uniqueidx_metric_declaration_versions_declaration_versionfromV1.9and replaces it with a uniqueuq_metric_declaration_versions_declaration_versionon(metric_declaration_id, schema_version DESC)(#183) - meta — `V1.31__AddTestCaseOverallSco...
0.3.0
Features
- Introduce multi-turn conversation test cases — a multi-turn case is a single test case carrying an ordered
multiTurnDataarray (mutually exclusive withdata), executed as a sequential fail-fast turn loop that writes one result row and one eval summary per turn; try-it-out previews the whole conversation and a suitetestCaseFiltermatches only when every turn satisfies it #17 (#100, #132, #135, #140, #147, #133) - Introduce multi-request test suites — the suite's own request becomes request #0 of an ordered chain and the new
additionalRequestsarray (max 10, each with its ownname,endpointRef,requestTemplate,responseColumnsandinputBindings) supplies requests 1..N, executed sequentially against the same deployment so a setup call's output feeds the call under test; response columns form one flat namespace capped at 50 suite-wide, the JSONata frame accumulates monotonically across requests, any subset of the chain may be multi-turn, and a row's identity becomes the pair(request_index, turn_index)#98 (#114, #164) - Try-out is chain-aware — both
try-it-outendpoints now run the wholeadditionalRequestschain, threading one accumulated frame across requests and turns exactly as the run engine does, each history entry carrying its ownrequestIndex/turnIndexstamps plus itsextractedColumnsandextractionWarnings;GET …/resolved-request?requestIndex=Npreviews any position in the chain #162 (#163, #164) - Request-template JSON bodies accept a JSONata expression via the new
jsonataContentfield — conversation history becomes a user-authored expression over the$_request/$_responseframe bindings instead of hardcodedmessagesreassembly, so multi-turn works against non-chat-completions APIs and streaming works on every turn #17 (#113, #121) - Add conditional metric execution — an optional per-metric-definition
condition(JSONata, max 2000 chars) decides per result row whether that metric runs, evaluated over{data, response, turn:{index,total,last}, request:{index,total,last,name}}#17 (#100, #114, #164) - Import externally evaluated test-case results —
POST /api/v1/test-suites/{id}/runs/importaccepts a results CSV and runs metric evaluation and score computation over it, so a deployment evaluated elsewhere can still be scored by the framework #93 (#99, #122) - Add
eval-cli, a standalone DB-free CLI —clone,fetch,run,importand the chainingevaluatecommand reuse existing suites and the shared Phase 1 engine to evaluate a deployment with no Evaluation Framework instance alongside it;fetchalso pulls the bound dataset's test-case schema and the results CSV carriesrequestIndex/totalRequests/turnIndex/totalTurns, so multi-request and multi-turn suites import with correct row identity; ships with aDockerfilerecipe for CI runners #92 (#112, #117, #128, #154, #164) - Add
Api-Keyheader authentication as an alternative to OIDC/JWT — keys are introspected against DIAL Core'sGET /v1/user/info, cached, and mapped to application authorities; off by default and active only inoidcsecurity mode (#134) - Add a run comparison endpoint —
GET /api/v1/analytics/metric-scores/comparison?runIds=<a>,<b>recomputes per-metric statistics,overalland average execution duration over only the eval-summary rows the two runs have in common, and returns the ids of the rows that did not match #103 (#106) - Measure metric evaluation time — each eval-summary row gains
metricEvalDurationMs, the summed latency of that row's provider/evaluatecalls, filterable and aggregatable through the Query DSL #123 (#150) - Expose deployment-reference fields on the
test_suitesQuery DSL entity —deployment_ref::id|name|versionandmcp_deployment_ref::id|name|typeas flatSTRINGfields for filter, select, sort and group_by #97 (#101) - Metric provider entries gain a per-entry
enabledflag and a stock second slot —METRIC_PROVIDERS_EXTRA_*configures a ready-to-useextraprovider (disabled by default), and disabling an entry skips it in the sync job while its already-synced declarations stay in the catalog and remain evaluable during runs #167 (#166, #164) - Make test-suite-run request timeouts configurable —
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT/TEST_SUITE_RUN_EXEC_MAX_TIMEOUTreplace the hardcoded values, with generous defaults so slow streaming responses no longer trip a request timeout (#153)
Fixes
- Write eval summaries for runs with no metrics configured — listing, count, aggregate and CSV export of such a run returned an empty page or HTTP 404; "latest computation" now resolves from
test_case_eval_summariesinstead of the legitimately empty metric snapshots (#108) - Carry the full DIAL Core deployment contract through listing — version, owner, timestamps, keywords, attachment types, model capabilities/limits/pricing, application properties and toolset transport were silently dropped, and an unrecognized
objectdiscriminator now degrades to a skippable entry instead of failing the whole fetch #130 (#131) - Restore application and model listing after DIAL Core switched deployment names and descriptions to multilingual objects (#145)
- Allow running a suite whose
testCaseFilteruses an array-field Contains condition —lower/upperover an ARRAY-bound field renderedlower(jsonb)and failed run creation with an unhandled HTTP 500; it now translates to case-insensitive whole-element JSONB containment #142 (#148) - Stop
nc/ne/notfilters from silently dropping rows whose field is null or absent — negated comparisons are now total, so an absent value correctly satisfies "does not contain" #141 (#143) - Ignore the legacy
disabled_test_case_idsexclusion during run selection — suites created before run conditions existed reported "39 of 39 selected" while executing 4 cases, and adding a run condition failedPOST /runswith 409 #151 (#152) - Upgrade Jackson to 2.21.5 / 3.1.5 to address CVE-2026-59889, CVE-2026-54515 and GHSA-mhm7-754m-9p8w, and Netty to 4.2.17.Final; build-time constraints added for CVE-2026-54399, CVE-2026-54428, CVE-2026-64607, CVE-2025-48924 and CVE-2025-67030 (#155)
- Patch OS packages inherited from the container base image at build time — the runtime stage now runs
apk --no-cache upgrade, picking uplibssl3/libcrypto3advisories fixed after the base image was cut (#166, #164) - Return a short projection from
GET /api/v1/deployments— the full-contract listing added in0.3.0-rc.0inflated the payload; entries now carry onlydeploymentId,displayNameanddescription(plustransportfor toolsets), and the full representation is fetched per deployment (regression fix, affects pre-release users of the expanded deployment contract only) (#157)
Deployment Changes
New environment variables
Required: ✅ = must be supplied, the service will not start without it. ❌ = ships with a working default. Nothing in this release is required in a default deployment;
⚠️ in a description marks a variable that becomes mandatory once you switch on the feature it belongs to.
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY_ENABLED |
❌ | false |
Enables DIAL API-Key authentication via the Api-Key request header. Applies only when config.rest.security.mode=oidc. |
API_KEY_CORE_URL |
❌ | - | GET /v1/user/info. Empty by default; startup fails with …requires …core-url to be set when API_KEY_ENABLED=true. |
API_KEY_CACHE_TTL_SECONDS |
❌ | 60 |
Time-to-live, in seconds, for cached successful introspection results. |
API_KEY_CACHE_MAX_SIZE |
❌ | 10000 |
Maximum number of cached introspection results. |
API_KEY_REQUEST_TIMEOUT_MS |
❌ | 3000 |
Connect/read timeout, in milliseconds, for the introspection call to DIAL Core. |
API_KEY_ROLES_MAPPING |
❌ | - (empty) | API_KEY_ENABLED=true, at least one of the two role mappings must be non-empty or startup fails. |
API_KEY_DEFAULT_ROLES_MAPPING |
❌ | - (empty) | userClaims) to lists of this service's authority strings. When API_KEY_ENABLED=true, at least one of the two role mappings must be non-empty or startup fails. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
❌ | roles |
Claim name read out of the introspection response's userClaims object to obtain the caller's raw roles. |
API_KEY_STARTUP_PROBE |
❌ | true |
When true, the service calls DIAL Core's /v1/user/info at startup and fails to start if Core is unreachable or misconfigured. |
ANALYTICS_COMPARISON_MAX_UNMATCHED_ROWS |
❌ | 5000 |
Maximum number of non-matching eval-summary rows a single run comparison may report per run; exceeding it fails the request with HTTP 409. Minimum 1. |
JSONATA_EVALUATION_TIMEOUT_MS |
❌ | 10000 |
Maximum wall-clock time in milliseconds a single JSONata expression evaluation may run before it is aborted. Minimum 1. |
JSONATA_MAX_RECURSION_DEPTH |
❌ | 1000 |
Maximum call-stack recursion depth a single JSONata expression evaluation may reach before it is aborted. Minimum 1. |
ANALYTICS_RESULTS_CSV_IMPORT_MAX_FILE_SIZE |
❌ | 10MB |
Maximum CSV file size for the eval-results import endpoint. Larger requests are rejected with HTTP 400 before parsing begins. |
TEST_CASE_MULTI_TURN_MAX_TURNS |
❌ | 10 |
Maximum number of turns a multi-turn test case may carry. An over-cap case is persisted but marked invalid with a warning, so it is excluded from runnable selection. |
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT |
❌ | 600000 |
Default per-request timeout, in milliseconds, for deployment requests issued during a test ... |
0.3.0-rc.2
Features
- Introduce multi-turn conversation test cases — a multi-turn case is a single test case carrying an ordered
multiTurnDataarray (mutually exclusive withdata), executed as a sequential fail-fast turn loop that writes one result row and one eval summary per turn; try-it-out previews the whole conversation and a suitetestCaseFiltermatches only when every turn satisfies it #17 (#100, #132, #135, #140, #147, #133) - Introduce multi-request test suites — the suite's own request becomes request #0 of an ordered chain and the new
additionalRequestsarray (max 10, each with its ownname,endpointRef,requestTemplate,responseColumnsandinputBindings) supplies requests 1..N, executed sequentially against the same deployment so a setup call's output feeds the call under test; response columns form one flat namespace capped at 50 suite-wide, the JSONata frame accumulates monotonically across requests, any subset of the chain may be multi-turn, and a row's identity becomes the pair(request_index, turn_index)#98 (#114, #164) - Try-out is chain-aware — both
try-it-outendpoints now run the wholeadditionalRequestschain, threading one accumulated frame across requests and turns exactly as the run engine does, each history entry carrying its ownrequestIndex/turnIndexstamps plus itsextractedColumnsandextractionWarnings;GET …/resolved-request?requestIndex=Npreviews any position in the chain #162 (#163, #164) - Request-template JSON bodies accept a JSONata expression via the new
jsonataContentfield — conversation history becomes a user-authored expression over the$_request/$_responseframe bindings instead of hardcodedmessagesreassembly, so multi-turn works against non-chat-completions APIs and streaming works on every turn #17 (#113, #121) - Add conditional metric execution — an optional per-metric-definition
condition(JSONata, max 2000 chars) decides per result row whether that metric runs, evaluated over{data, response, turn:{index,total,last}, request:{index,total,last,name}}#17 (#100, #114, #164) - Import externally evaluated test-case results —
POST /api/v1/test-suites/{id}/runs/importaccepts a results CSV and runs metric evaluation and score computation over it, so a deployment evaluated elsewhere can still be scored by the framework #93 (#99, #122) - Add
eval-cli, a standalone DB-free CLI —clone,fetch,run,importand the chainingevaluatecommand reuse existing suites and the shared Phase 1 engine to evaluate a deployment with no Evaluation Framework instance alongside it;fetchalso pulls the bound dataset's test-case schema and the results CSV carriesrequestIndex/totalRequests/turnIndex/totalTurns, so multi-request and multi-turn suites import with correct row identity; ships with aDockerfilerecipe for CI runners #92 (#112, #117, #128, #154, #164) - Add
Api-Keyheader authentication as an alternative to OIDC/JWT — keys are introspected against DIAL Core'sGET /v1/user/info, cached, and mapped to application authorities; off by default and active only inoidcsecurity mode (#134) - Add a run comparison endpoint —
GET /api/v1/analytics/metric-scores/comparison?runIds=<a>,<b>recomputes per-metric statistics,overalland average execution duration over only the eval-summary rows the two runs have in common, and returns the ids of the rows that did not match #103 (#106) - Measure metric evaluation time — each eval-summary row gains
metricEvalDurationMs, the summed latency of that row's provider/evaluatecalls, filterable and aggregatable through the Query DSL #123 (#150) - Expose deployment-reference fields on the
test_suitesQuery DSL entity —deployment_ref::id|name|versionandmcp_deployment_ref::id|name|typeas flatSTRINGfields for filter, select, sort and group_by #97 (#101) - Metric provider entries gain a per-entry
enabledflag and a stock second slot —METRIC_PROVIDERS_EXTRA_*configures a ready-to-useextraprovider (disabled by default), and disabling an entry skips it in the sync job while its already-synced declarations stay in the catalog and remain evaluable during runs #167 (#166, #164) - Make test-suite-run request timeouts configurable —
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT/TEST_SUITE_RUN_EXEC_MAX_TIMEOUTreplace the hardcoded values, with generous defaults so slow streaming responses no longer trip a request timeout (#153)
Fixes
- Write eval summaries for runs with no metrics configured — listing, count, aggregate and CSV export of such a run returned an empty page or HTTP 404; "latest computation" now resolves from
test_case_eval_summariesinstead of the legitimately empty metric snapshots (#108) - Carry the full DIAL Core deployment contract through listing — version, owner, timestamps, keywords, attachment types, model capabilities/limits/pricing, application properties and toolset transport were silently dropped, and an unrecognized
objectdiscriminator now degrades to a skippable entry instead of failing the whole fetch #130 (#131) - Restore application and model listing after DIAL Core switched deployment names and descriptions to multilingual objects (#145)
- Allow running a suite whose
testCaseFilteruses an array-field Contains condition —lower/upperover an ARRAY-bound field renderedlower(jsonb)and failed run creation with an unhandled HTTP 500; it now translates to case-insensitive whole-element JSONB containment #142 (#148) - Stop
nc/ne/notfilters from silently dropping rows whose field is null or absent — negated comparisons are now total, so an absent value correctly satisfies "does not contain" #141 (#143) - Ignore the legacy
disabled_test_case_idsexclusion during run selection — suites created before run conditions existed reported "39 of 39 selected" while executing 4 cases, and adding a run condition failedPOST /runswith 409 #151 (#152) - Upgrade Jackson to 2.21.5 / 3.1.5 to address CVE-2026-59889, CVE-2026-54515 and GHSA-mhm7-754m-9p8w, and Netty to 4.2.17.Final; build-time constraints added for CVE-2026-54399, CVE-2026-54428, CVE-2026-64607, CVE-2025-48924 and CVE-2025-67030 (#155)
- Patch OS packages inherited from the container base image at build time — the runtime stage now runs
apk --no-cache upgrade, picking uplibssl3/libcrypto3advisories fixed after the base image was cut (#166, #164) - Return a short projection from
GET /api/v1/deployments— the full-contract listing added in0.3.0-rc.0inflated the payload; entries now carry onlydeploymentId,displayNameanddescription(plustransportfor toolsets), and the full representation is fetched per deployment (regression fix, affects pre-release users of the expanded deployment contract only) (#157)
Deployment Changes
New environment variables
Required: ✅ = must be supplied, the service will not start without it. ❌ = ships with a working default. Nothing in this release is required in a default deployment;
⚠️ in a description marks a variable that becomes mandatory once you switch on the feature it belongs to.
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY_ENABLED |
❌ | false |
Enables DIAL API-Key authentication via the Api-Key request header. Applies only when config.rest.security.mode=oidc. |
API_KEY_CORE_URL |
❌ | - | GET /v1/user/info. Empty by default; startup fails with …requires …core-url to be set when API_KEY_ENABLED=true. |
API_KEY_CACHE_TTL_SECONDS |
❌ | 60 |
Time-to-live, in seconds, for cached successful introspection results. |
API_KEY_CACHE_MAX_SIZE |
❌ | 10000 |
Maximum number of cached introspection results. |
API_KEY_REQUEST_TIMEOUT_MS |
❌ | 3000 |
Connect/read timeout, in milliseconds, for the introspection call to DIAL Core. |
API_KEY_ROLES_MAPPING |
❌ | - (empty) | API_KEY_ENABLED=true, at least one of the two role mappings must be non-empty or startup fails. |
API_KEY_DEFAULT_ROLES_MAPPING |
❌ | - (empty) | userClaims) to lists of this service's authority strings. When API_KEY_ENABLED=true, at least one of the two role mappings must be non-empty or startup fails. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
❌ | roles |
Claim name read out of the introspection response's userClaims object to obtain the caller's raw roles. |
API_KEY_STARTUP_PROBE |
❌ | true |
When true, the service calls DIAL Core's /v1/user/info at startup and fails to start if Core is unreachable or misconfigured. |
ANALYTICS_COMPARISON_MAX_UNMATCHED_ROWS |
❌ | 5000 |
Maximum number of non-matching eval-summary rows a single run comparison may report per run; exceeding it fails the request with HTTP 409. Minimum 1. |
JSONATA_EVALUATION_TIMEOUT_MS |
❌ | 10000 |
Maximum wall-clock time in milliseconds a single JSONata expression evaluation may run before it is aborted. Minimum 1. |
JSONATA_MAX_RECURSION_DEPTH |
❌ | 1000 |
Maximum call-stack recursion depth a single JSONata expression evaluation may reach before it is aborted. Minimum 1. |
ANALYTICS_RESULTS_CSV_IMPORT_MAX_FILE_SIZE |
❌ | 10MB |
Maximum CSV file size for the eval-results import endpoint. Larger requests are rejected with HTTP 400 before parsing begins. |
TEST_CASE_MULTI_TURN_MAX_TURNS |
❌ | 10 |
Maximum number of turns a multi-turn test case may carry. An over-cap case is persisted but marked invalid with a warning, so it is excluded from runnable selection. |
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT |
❌ | 600000 |
Default per-request timeout, in milliseconds, for deployment requests issued during a test ... |
0.3.0-rc.1
Features
- Introduce multi-turn conversation test cases — a multi-turn case is a single test case carrying an ordered
multiTurnDataarray (mutually exclusive withdata), executed as a sequential fail-fast turn loop that writes one result row and one eval summary per turn; try-it-out previews the whole conversation and a suitetestCaseFiltermatches only when every turn satisfies it #17 (#100, #132, #135, #140, #147, #133) - Request-template JSON bodies accept a JSONata expression via the new
jsonataContentfield — conversation history becomes a user-authored expression over the$_request/$_responseframe bindings instead of hardcodedmessagesreassembly, so multi-turn works against non-chat-completions APIs and streaming works on every turn #17 (#113, #121) - Add conditional metric execution — an optional per-metric-definition
condition(JSONata, max 2000 chars) decides per result row whether that metric runs, evaluated over{data, response, turn:{index,total,last}}#17 (#100) - Import externally evaluated test-case results —
POST /api/v1/test-suites/{id}/runs/importaccepts a results CSV and runs metric evaluation and score computation over it, so a deployment evaluated elsewhere can still be scored by the framework #93 (#99, #122) - Add
eval-cli, a standalone DB-free CLI —clone,fetch,run,importand the chainingevaluatecommand reuse existing suites and the shared Phase 1 engine to evaluate a deployment with no Evaluation Framework instance alongside it; ships with aDockerfilerecipe for CI runners #92 (#112, #117, #128, #154) - Add
Api-Keyheader authentication as an alternative to OIDC/JWT — keys are introspected against DIAL Core'sGET /v1/user/info, cached, and mapped to application authorities; off by default and active only inoidcsecurity mode (#134) - Add a run comparison endpoint —
GET /api/v1/analytics/metric-scores/comparison?runIds=<a>,<b>recomputes per-metric statistics,overalland average execution duration over only the eval-summary rows the two runs have in common, and returns the ids of the rows that did not match #103 (#106) - Measure metric evaluation time — each eval-summary row gains
metricEvalDurationMs, the summed latency of that row's provider/evaluatecalls, filterable and aggregatable through the Query DSL #123 (#150) - Expose deployment-reference fields on the
test_suitesQuery DSL entity —deployment_ref::id|name|versionandmcp_deployment_ref::id|name|typeas flatSTRINGfields for filter, select, sort and group_by #97 (#101) - Make test-suite-run request timeouts configurable —
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT/TEST_SUITE_RUN_EXEC_MAX_TIMEOUTreplace the hardcoded values, with generous defaults so slow streaming responses no longer trip a request timeout (#153)
Fixes
- Write eval summaries for runs with no metrics configured — listing, count, aggregate and CSV export of such a run returned an empty page or HTTP 404; "latest computation" now resolves from
test_case_eval_summariesinstead of the legitimately empty metric snapshots (#108) - Carry the full DIAL Core deployment contract through listing — version, owner, timestamps, keywords, attachment types, model capabilities/limits/pricing, application properties and toolset transport were silently dropped, and an unrecognized
objectdiscriminator now degrades to a skippable entry instead of failing the whole fetch #130 (#131) - Restore application and model listing after DIAL Core switched deployment names and descriptions to multilingual objects (#145)
- Allow running a suite whose
testCaseFilteruses an array-field Contains condition —lower/upperover an ARRAY-bound field renderedlower(jsonb)and failed run creation with an unhandled HTTP 500; it now translates to case-insensitive whole-element JSONB containment #142 (#148) - Stop
nc/ne/notfilters from silently dropping rows whose field is null or absent — negated comparisons are now total, so an absent value correctly satisfies "does not contain" #141 (#143) - Ignore the legacy
disabled_test_case_idsexclusion during run selection — suites created before run conditions existed reported "39 of 39 selected" while executing 4 cases, and adding a run condition failedPOST /runswith 409 #151 (#152) - Upgrade Jackson to 2.21.5 / 3.1.5 to address CVE-2026-59889, CVE-2026-54515 and GHSA-mhm7-754m-9p8w, and Netty to 4.2.17.Final; build-time constraints added for CVE-2026-54399, CVE-2026-54428, CVE-2026-64607, CVE-2025-48924 and CVE-2025-67030 (#155)
- Return a short projection from
GET /api/v1/deployments— the full-contract listing added in0.3.0-rc.0inflated the payload; entries now carry onlydeploymentId,displayNameanddescription(plustransportfor toolsets), and the full representation is fetched per deployment (regression fix, affects pre-release users of the expanded deployment contract only) (#157)
Deployment Changes
New environment variables
| Variable | Default | Description |
|---|---|---|
API_KEY_ENABLED |
false |
Enables DIAL API-Key authentication via the Api-Key request header. Applies only when config.rest.security.mode=oidc. |
API_KEY_CORE_URL |
- | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. Required when API-Key auth is enabled. |
API_KEY_CACHE_TTL_SECONDS |
60 |
Time-to-live, in seconds, for cached successful introspection results. |
API_KEY_CACHE_MAX_SIZE |
10000 |
Maximum number of cached introspection results. |
API_KEY_REQUEST_TIMEOUT_MS |
3000 |
Connect/read timeout, in milliseconds, for the introspection call to DIAL Core. |
API_KEY_ROLES_MAPPING |
- (empty) | JSON object mapping DIAL Core project-key role names to lists of this service's authority strings. Conditional — at least one of the two role mappings must be non-empty. |
API_KEY_DEFAULT_ROLES_MAPPING |
- (empty) | JSON object mapping DIAL Core roles from the JWT-rooted per-request-key response shape (userClaims) to lists of this service's authority strings. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
Claim name read out of the introspection response's userClaims object to obtain the caller's raw roles. |
API_KEY_STARTUP_PROBE |
true |
When true, the service calls DIAL Core's /v1/user/info at startup and fails to start if Core is unreachable or misconfigured. |
ANALYTICS_COMPARISON_MAX_UNMATCHED_ROWS |
5000 |
Maximum number of non-matching eval-summary rows a single run comparison may report per run; exceeding it fails the request with HTTP 409. Minimum 1. |
JSONATA_EVALUATION_TIMEOUT_MS |
10000 |
Maximum wall-clock time in milliseconds a single JSONata expression evaluation may run before it is aborted. Minimum 1. |
JSONATA_MAX_RECURSION_DEPTH |
1000 |
Maximum call-stack recursion depth a single JSONata expression evaluation may reach before it is aborted. Minimum 1. |
ANALYTICS_RESULTS_CSV_IMPORT_MAX_FILE_SIZE |
10MB |
Maximum CSV file size for the eval-results import endpoint. Larger requests are rejected with HTTP 400 before parsing begins. |
TEST_CASE_MULTI_TURN_MAX_TURNS |
10 |
Maximum number of turns a multi-turn test case may carry. An over-cap case is persisted but marked invalid with a warning, so it is excluded from runnable selection. |
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT |
600000 |
Default per-request timeout, in milliseconds, for deployment requests issued during a test suite run. |
TEST_SUITE_RUN_EXEC_MAX_TIMEOUT |
1200000 |
Upper bound, in milliseconds, a run may request for its per-request timeout. |
VIRTUAL_THREADS_ENABLED |
true |
Toggles Spring's virtual-thread executor; set to false to aid troubleshooting. |
Behavioral changes
Note
These take effect automatically on upgrade — no operator action required, but existing suites may select or time out differently than they did on 0.2.0.
- Test suite run request timeouts — the default per-request timeout rises from
30sto600sand the maximum from600sto1200s, so a slow streaming deployment no longer trips a request timeout (#153) - Query DSL null polarity —
nc,neandnotare now total over nulls: a row whose field is null or absent satisfies a negated predicate instead of being dropped. SuitetestCaseFilters using these operators will select more cases than before; the previous behavior is expressible asand(ne(field, null), nc(field, "x"))#141 (#143) - Run selection — the only rule is now
is_valid = TRUE AND testCaseFilter. Suites carrying staletest_suites.disabled_test_case_idsvalues run every valid, filter-matching case; the column is retained but no longer read or written, with the drop deferred to a follow-up #151 (#152) - Suite snapshots on imported runs — the snapshot phase now runs for every run, so an imported run scores against frozen suite config instead of resynthesizing from the live suite (#122)
Database migrations
Note
Flyway applies these automatically on startup; the service account needs DDL privileges on both schemas. All column adds are backward compatible — nullable JSONB, or NOT NULL DEFAULT backfilled in a metadata-only statement with no table rewrite.
- meta —
V1.26__AddConditionToTestSuiteMetricDefinitions.sql— adds the nullablecondition VARCHAR(2000)column totest_suite_metric_definitions(#100) - meta —
V1.27__AddMultiTurnDataToTestCases.sql— adds the nullablemulti_turn_dataJSONB column plus a mutual-exclusivity CHECK totest_cases(#100) - meta —
V1.28__AddMultiTurnDataToTestCaseRunInputs.sql— adds the nullablemulti_turn_dataJSONB column totest_case_run_inputsfor the suite-run snapshot (#100) - analytics —
V1.13__AddTurnColumnsToTestCaseRunResults.sql— addsturn_index/total_turnstotest_case_run_resultsand extends `...
0.3.0-rc.0
Features
- Introduce multi-turn conversation test cases — a multi-turn case is a single test case carrying an ordered
multiTurnDataarray (mutually exclusive withdata), executed as a sequential fail-fast turn loop that writes one result row and one eval summary per turn; try-it-out previews the whole conversation and a suitetestCaseFiltermatches only when every turn satisfies it #17 (#100, #132, #135, #140, #147, #133) - Request-template JSON bodies accept a JSONata expression via the new
jsonataContentfield — conversation history becomes a user-authored expression over the$_request/$_responseframe bindings instead of hardcodedmessagesreassembly, so multi-turn works against non-chat-completions APIs and streaming works on every turn #17 (#113, #121) - Add conditional metric execution — an optional per-metric-definition
condition(JSONata, max 2000 chars) decides per result row whether that metric runs, evaluated over{data, response, turn:{index,total,last}}#17 (#100) - Import externally evaluated test-case results —
POST /api/v1/test-suites/{id}/runs/importaccepts a results CSV and runs metric evaluation and score computation over it, so a deployment evaluated elsewhere can still be scored by the framework #93 (#99, #122) - Add
eval-cli, a standalone DB-free CLI —clone,fetch,run,importand the chainingevaluatecommand reuse existing suites and the shared Phase 1 engine to evaluate a deployment with no Evaluation Framework instance alongside it; ships with aDockerfilerecipe for CI runners #92 (#112, #117, #128, #154) - Add
Api-Keyheader authentication as an alternative to OIDC/JWT — keys are introspected against DIAL Core'sGET /v1/user/info, cached, and mapped to application authorities; off by default and active only inoidcsecurity mode (#134) - Add a run comparison endpoint —
GET /api/v1/analytics/metric-scores/comparison?runIds=<a>,<b>recomputes per-metric statistics,overalland average execution duration over only the eval-summary rows the two runs have in common, and returns the ids of the rows that did not match #103 (#106) - Measure metric evaluation time — each eval-summary row gains
metricEvalDurationMs, the summed latency of that row's provider/evaluatecalls, filterable and aggregatable through the Query DSL #123 (#150) - Expose deployment-reference fields on the
test_suitesQuery DSL entity —deployment_ref::id|name|versionandmcp_deployment_ref::id|name|typeas flatSTRINGfields for filter, select, sort and group_by #97 (#101) - Make test-suite-run request timeouts configurable —
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT/TEST_SUITE_RUN_EXEC_MAX_TIMEOUTreplace the hardcoded values, with generous defaults so slow streaming responses no longer trip a request timeout (#153)
Fixes
- Write eval summaries for runs with no metrics configured — listing, count, aggregate and CSV export of such a run returned an empty page or HTTP 404; "latest computation" now resolves from
test_case_eval_summariesinstead of the legitimately empty metric snapshots (#108) - Carry the full DIAL Core deployment contract through listing — version, owner, timestamps, keywords, attachment types, model capabilities/limits/pricing, application properties and toolset transport were silently dropped, and an unrecognized
objectdiscriminator now degrades to a skippable entry instead of failing the whole fetch #130 (#131) - Restore application and model listing after DIAL Core switched deployment names and descriptions to multilingual objects (#145)
- Allow running a suite whose
testCaseFilteruses an array-field Contains condition —lower/upperover an ARRAY-bound field renderedlower(jsonb)and failed run creation with an unhandled HTTP 500; it now translates to case-insensitive whole-element JSONB containment #142 (#148) - Stop
nc/ne/notfilters from silently dropping rows whose field is null or absent — negated comparisons are now total, so an absent value correctly satisfies "does not contain" #141 (#143) - Ignore the legacy
disabled_test_case_idsexclusion during run selection — suites created before run conditions existed reported "39 of 39 selected" while executing 4 cases, and adding a run condition failedPOST /runswith 409 #151 (#152) - Upgrade Jackson to 2.21.5 / 3.1.5 to address CVE-2026-59889, CVE-2026-54515 and GHSA-mhm7-754m-9p8w, and Netty to 4.2.17.Final; build-time constraints added for CVE-2026-54399, CVE-2026-54428, CVE-2026-64607, CVE-2025-48924 and CVE-2025-67030 (#155)
Deployment Changes
New environment variables
| Variable | Default | Description |
|---|---|---|
API_KEY_ENABLED |
false |
Enables DIAL API-Key authentication via the Api-Key request header. Applies only when config.rest.security.mode=oidc. |
API_KEY_CORE_URL |
- | Base URL of the DIAL Core instance used to introspect API keys via GET /v1/user/info. Required when API-Key auth is enabled. |
API_KEY_CACHE_TTL_SECONDS |
60 |
Time-to-live, in seconds, for cached successful introspection results. |
API_KEY_CACHE_MAX_SIZE |
10000 |
Maximum number of cached introspection results. |
API_KEY_REQUEST_TIMEOUT_MS |
3000 |
Connect/read timeout, in milliseconds, for the introspection call to DIAL Core. |
API_KEY_ROLES_MAPPING |
- (empty) | JSON object mapping DIAL Core project-key role names to lists of this service's authority strings. Conditional — at least one of the two role mappings must be non-empty. |
API_KEY_DEFAULT_ROLES_MAPPING |
- (empty) | JSON object mapping DIAL Core roles from the JWT-rooted per-request-key response shape (userClaims) to lists of this service's authority strings. |
API_KEY_USER_CLAIMS_ROLE_CLAIM |
roles |
Claim name read out of the introspection response's userClaims object to obtain the caller's raw roles. |
API_KEY_STARTUP_PROBE |
true |
When true, the service calls DIAL Core's /v1/user/info at startup and fails to start if Core is unreachable or misconfigured. |
ANALYTICS_COMPARISON_MAX_UNMATCHED_ROWS |
5000 |
Maximum number of non-matching eval-summary rows a single run comparison may report per run; exceeding it fails the request with HTTP 409. Minimum 1. |
JSONATA_EVALUATION_TIMEOUT_MS |
10000 |
Maximum wall-clock time in milliseconds a single JSONata expression evaluation may run before it is aborted. Minimum 1. |
JSONATA_MAX_RECURSION_DEPTH |
1000 |
Maximum call-stack recursion depth a single JSONata expression evaluation may reach before it is aborted. Minimum 1. |
ANALYTICS_RESULTS_CSV_IMPORT_MAX_FILE_SIZE |
10MB |
Maximum CSV file size for the eval-results import endpoint. Larger requests are rejected with HTTP 400 before parsing begins. |
TEST_CASE_MULTI_TURN_MAX_TURNS |
10 |
Maximum number of turns a multi-turn test case may carry. An over-cap case is persisted but marked invalid with a warning, so it is excluded from runnable selection. |
TEST_SUITE_RUN_EXEC_DEFAULT_TIMEOUT |
600000 |
Default per-request timeout, in milliseconds, for deployment requests issued during a test suite run. |
TEST_SUITE_RUN_EXEC_MAX_TIMEOUT |
1200000 |
Upper bound, in milliseconds, a run may request for its per-request timeout. |
VIRTUAL_THREADS_ENABLED |
true |
Toggles Spring's virtual-thread executor; set to false to aid troubleshooting. |
Behavioral changes
Note
These take effect automatically on upgrade — no operator action required, but existing suites may select or time out differently than they did on 0.2.0.
- Test suite run request timeouts — the default per-request timeout rises from
30sto600sand the maximum from600sto1200s, so a slow streaming deployment no longer trips a request timeout (#153) - Query DSL null polarity —
nc,neandnotare now total over nulls: a row whose field is null or absent satisfies a negated predicate instead of being dropped. SuitetestCaseFilters using these operators will select more cases than before; the previous behavior is expressible asand(ne(field, null), nc(field, "x"))#141 (#143) - Run selection — the only rule is now
is_valid = TRUE AND testCaseFilter. Suites carrying staletest_suites.disabled_test_case_idsvalues run every valid, filter-matching case; the column is retained but no longer read or written, with the drop deferred to a follow-up #151 (#152) - Suite snapshots on imported runs — the snapshot phase now runs for every run, so an imported run scores against frozen suite config instead of resynthesizing from the live suite (#122)
Database migrations
Note
Flyway applies these automatically on startup; the service account needs DDL privileges on both schemas. All column adds are backward compatible — nullable JSONB, or NOT NULL DEFAULT backfilled in a metadata-only statement with no table rewrite.
- meta —
V1.26__AddConditionToTestSuiteMetricDefinitions.sql— adds the nullablecondition VARCHAR(2000)column totest_suite_metric_definitions(#100) - meta —
V1.27__AddMultiTurnDataToTestCases.sql— adds the nullablemulti_turn_dataJSONB column plus a mutual-exclusivity CHECK totest_cases(#100) - meta —
V1.28__AddMultiTurnDataToTestCaseRunInputs.sql— adds the nullablemulti_turn_dataJSONB column totest_case_run_inputsfor the suite-run snapshot (#100) - analytics —
V1.13__AddTurnColumnsToTestCaseRunResults.sql— addsturn_index/total_turnstotest_case_run_resultsand extendsuq_results_run_case_indexwithturn_index(#100) - analytics —
V1.14__AddTurnColumnsToEvalSummaries.sql— addsturn_index/total_turnstotest_case_eval_summariesand extendsuq_eval_summaries_natural_keywithturn_index(#100) - analytics —
V1.15__AddEvalSummariesRunComputedAtIndex.sql— adds a covering `(test_suite_run_id, computed_at_ms DESC, comput...