llm-testrunner-components 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cjs/app-chips_4.cjs.entry.js +5 -2
  2. package/dist/cjs/app-chips_4.cjs.entry.js.map +1 -1
  3. package/dist/cjs/index.cjs.js +206 -45
  4. package/dist/cjs/index.cjs.js.map +1 -1
  5. package/dist/cjs/llm-testrunner.cjs.js +1 -1
  6. package/dist/cjs/loader.cjs.js +1 -1
  7. package/dist/collection/components/llm-test-runner/llm-test-runner.js +111 -29
  8. package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
  9. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js +37 -4
  10. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js.map +1 -1
  11. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +2 -2
  12. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -1
  13. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +2 -2
  14. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -1
  15. package/dist/collection/lib/evaluation/evaluation-service.js +14 -7
  16. package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -1
  17. package/dist/collection/lib/form/components/app-textarea.css +17 -0
  18. package/dist/collection/lib/form/components/app-textarea.js +4 -1
  19. package/dist/collection/lib/form/components/app-textarea.js.map +1 -1
  20. package/dist/collection/lib/test-cases/dynamic-expected-outcome-resolver.js +44 -0
  21. package/dist/collection/lib/test-cases/dynamic-expected-outcome-resolver.js.map +1 -0
  22. package/dist/collection/lib/test-cases/test-case-factory.js +1 -1
  23. package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -1
  24. package/dist/collection/lib/test-cases/test-case-mutations.js +35 -0
  25. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -1
  26. package/dist/collection/schemas/expected-outcome.js +15 -1
  27. package/dist/collection/schemas/expected-outcome.js.map +1 -1
  28. package/dist/collection/schemas/test-case.js.map +1 -1
  29. package/dist/collection/types/expected-outcome.js.map +1 -1
  30. package/dist/collection/types/llm-test-runner.js.map +1 -1
  31. package/dist/components/app-textarea.js +1 -1
  32. package/dist/components/index.js +1 -1
  33. package/dist/components/llm-test-runner.js +1 -1
  34. package/dist/components/{p-JPMPoOC8.js → p-BcygfrMf.js} +3 -3
  35. package/dist/components/p-BcygfrMf.js.map +1 -0
  36. package/dist/components/p-CVtKFBJl.js +2 -0
  37. package/dist/components/p-CVtKFBJl.js.map +1 -0
  38. package/dist/esm/app-chips_4.entry.js +5 -2
  39. package/dist/esm/app-chips_4.entry.js.map +1 -1
  40. package/dist/esm/index.js +206 -45
  41. package/dist/esm/index.js.map +1 -1
  42. package/dist/esm/llm-testrunner.js +1 -1
  43. package/dist/esm/loader.js +1 -1
  44. package/dist/llm-testrunner/index.esm.js +2 -2
  45. package/dist/llm-testrunner/index.esm.js.map +1 -1
  46. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  47. package/dist/llm-testrunner/{p-2cc09217.entry.js → p-5df053b4.entry.js} +2 -2
  48. package/dist/llm-testrunner/{p-2cc09217.entry.js.map → p-5df053b4.entry.js.map} +1 -1
  49. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +6 -0
  50. package/dist/types/components/llm-test-runner/test-cases/expected-outcome-renderer.d.ts +1 -0
  51. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +1 -0
  52. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +1 -0
  53. package/dist/types/components.d.ts +7 -0
  54. package/dist/types/lib/test-cases/dynamic-expected-outcome-resolver.d.ts +7 -0
  55. package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -1
  56. package/dist/types/schemas/expected-outcome.d.ts +16 -1
  57. package/dist/types/schemas/test-case.d.ts +17 -2
  58. package/dist/types/types/expected-outcome.d.ts +1 -1
  59. package/dist/types/types/llm-test-runner.d.ts +1 -1
  60. package/package.json +5 -1
  61. package/dist/components/p-BZrzx5jG.js +0 -2
  62. package/dist/components/p-BZrzx5jG.js.map +0 -1
  63. package/dist/components/p-JPMPoOC8.js.map +0 -1
@@ -90,7 +90,7 @@ const AppSelect = class {
90
90
  };
91
91
  AppSelect.style = appSelectCss();
92
92
 
93
- const appTextareaCss = () => `.textarea-wrapper{margin-bottom:var(--spacing-4)}.textarea-label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.textarea-element{width:95%;box-sizing:border-box;padding:var(--spacing-3);border:2px solid var(--input);border-radius:var(--radius);font-size:var(--font-size-sm);resize:vertical;outline:none;transition:border-color 0.2s ease;font-family:inherit;background:var(--background);color:var(--foreground)}.textarea-element:focus{border-color:var(--ring);box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.help-text{margin-top:var(--spacing-1);font-size:var(--font-size-xs);color:var(--muted-foreground, #6b7280);line-height:1.4}`;
93
+ const appTextareaCss = () => `.textarea-wrapper{margin-bottom:var(--spacing-4)}.textarea-label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.textarea-element{width:95%;box-sizing:border-box;padding:var(--spacing-3);border:2px solid var(--input);border-radius:var(--radius);font-size:var(--font-size-sm);resize:vertical;outline:none;transition:border-color 0.2s ease;font-family:inherit;background:var(--background);color:var(--foreground)}.textarea-element:focus{border-color:var(--ring);box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.textarea-wrapper--read-only .textarea-label{color:var(--muted-foreground)}.textarea-element:read-only{background:var(--muted);color:var(--muted-foreground);border-color:var(--border);cursor:not-allowed;resize:none}.textarea-element:read-only:focus{border-color:var(--border);box-shadow:none}.help-text{margin-top:var(--spacing-1);font-size:var(--font-size-xs);color:var(--muted-foreground, #6b7280);line-height:1.4}`;
94
94
 
95
95
  const AppTextarea = class {
96
96
  constructor(hostRef) {
@@ -118,7 +118,10 @@ const AppTextarea = class {
118
118
  name: c.name,
119
119
  autocomplete: c.autocomplete,
120
120
  };
121
- return (index$1.h("div", { key: 'cc0073d70c9de40a8a12a1cabc3c1c06ac8c1863', class: "textarea-wrapper" }, c.label && (index$1.h("label", { key: 'a886cf95f25afee2e09ebcbc4dc62d2c9bca5ee0', class: "textarea-label", htmlFor: c.name }, c.label)), index$1.h("textarea", { key: 'c16b072269c450f3e09764b23cdfdaf966975977', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && index$1.h("p", { key: '9df941aa068b937d90f9a5f2a841a8fe426b3371', class: "help-text" }, c.helpText)));
121
+ return (index$1.h("div", { key: '29bbcd954df6d18e968f6b2670224fda6f03a9f2', class: {
122
+ 'textarea-wrapper': true,
123
+ 'textarea-wrapper--read-only': !!c.readOnly,
124
+ } }, c.label && (index$1.h("label", { key: 'd95d3cbf01ce66c5bd074ede2eb1f87a0c98f3e4', class: "textarea-label", htmlFor: c.name }, c.label)), index$1.h("textarea", { key: 'faeb28c35a543213d9c5847e93779f957e367ff0', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && index$1.h("p", { key: '558abfe03f7107352a6d40aea78a35b304bc11a6', class: "help-text" }, c.helpText)));
122
125
  }
123
126
  };
124
127
  AppTextarea.style = appTextareaCss();
@@ -1 +1 @@
1
- {"file":"app-chips_4.cjs.entry.js","mappings":"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","names":["h"],"sources":["src/lib/form/components/app-chips.css?tag=app-chips&encapsulation=shadow","src/lib/form/components/app-chips.tsx","src/lib/form/components/app-select.css?tag=app-select&encapsulation=shadow","src/lib/form/components/app-select.tsx","src/lib/form/components/app-textarea.css?tag=app-textarea&encapsulation=shadow","src/lib/form/components/app-textarea.tsx"],"sourcesContent":[".app-chips {\n margin-bottom: var(--spacing-4);\n}\n\n.app-chips__label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.app-chips__container {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-2);\n align-items: center;\n}\n\n.app-chips__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-2);\n padding: var(--spacing-1) var(--spacing-2);\n font-size: var(--font-size-xs);\n font-weight: var(--font-weight-medium);\n border-radius: var(--radius-md);\n background: var(--accent);\n border: var(--border-width) solid var(--border);\n}\n\n/* Keyword-style chip override (non-URL chips) */\n.app-chips__chip:not(:has(a)) {\n background: var(--info);\n color: var(--info-foreground);\n border: none;\n border-radius: var(--radius-2xl);\n}\n\n.app-chips__link {\n color: var(--info);\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.app-chips__link:hover {\n text-decoration: underline;\n}\n\n.app-chips__remove {\n background: none;\n border: none;\n cursor: pointer;\n font-size: var(--font-size-xs);\n padding: 0;\n width: var(--spacing-4);\n height: var(--spacing-4);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-full);\n color: var(--muted-foreground);\n opacity: var(--opacity-muted);\n}\n\n.app-chips__chip:not(:has(a)) .app-chips__remove {\n color: var(--info-foreground);\n opacity: var(--opacity-hover);\n}\n\n.app-chips__remove:hover {\n opacity: 1;\n background: var(--muted);\n}\n\n.app-chips__chip:not(:has(a)) .app-chips__remove:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.app-chips__input {\n border: var(--border-width) solid var(--input);\n border-radius: var(--radius-md);\n padding: var(--spacing-2);\n font-size: var(--font-size-xs);\n outline: none;\n min-width: 120px;\n background: var(--background);\n color: var(--foreground);\n}\n\n.app-chips__input:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n","import { Component, Prop, h, Event, EventEmitter } from '@stencil/core';\nimport { ChipsConfig } from '../schema';\n\n@Component({\n tag: 'app-chips',\n styleUrl: 'app-chips.css',\n shadow: true,\n})\nexport class AppChips {\n @Prop() value: string[] = [];\n @Prop() config: ChipsConfig;\n\n @Event() addChip: EventEmitter<{ value: string }>;\n\n @Event() removeChip: EventEmitter<{ value: string }>;\n\n private emitAddChip(val: string) {\n this.addChip.emit({\n value: val,\n });\n }\n\n private emitRemoveChip(value: string) {\n this.removeChip.emit({\n value,\n });\n }\n\n private hasDuplicateChip(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n return this.value.some(chip => chip.trim().toLowerCase() === normalized);\n }\n\n render() {\n const c = this.config;\n\n const allowedAttrs = {\n placeholder: c.placeholder,\n required: c.required,\n disabled: c.disabled,\n readOnly: c.readOnly,\n id: c.name,\n name: c.name,\n autocomplete: c.autocomplete,\n };\n\n return (\n <div class=\"app-chips\">\n {c.label && (\n <label class=\"app-chips__label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <div class=\"app-chips__container\">\n {this.value.map((chip) => (\n <span class=\"app-chips__chip\" key={chip}>\n {c.type === 'url' ? (\n <a\n href={chip}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"app-chips__link\"\n >\n {chip}\n </a>\n ) : (\n chip\n )}\n\n <button\n class=\"app-chips__remove\"\n type=\"button\"\n onClick={() => this.emitRemoveChip(chip)}\n >\n ×\n </button>\n </span>\n ))}\n\n <input\n class=\"app-chips__input\"\n type={c.type || 'text'}\n {...allowedAttrs}\n onKeyDown={(e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n const input = e.target as HTMLInputElement;\n const val = input.value.trim();\n if (!val) return;\n if (this.hasDuplicateChip(val)) {\n input.value = '';\n return;\n }\n\n this.emitAddChip(val);\n input.value = '';\n }\n }}\n />\n </div>\n </div>\n );\n }\n}\n",".app-select {\n margin-bottom: var(--spacing-4);\n}\n\n.app-select__label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.app-select__select {\n border: var(--border-width) solid var(--input);\n border-radius: var(--radius-md);\n font-size: var(--font-size-sm);\n font-weight: var(--font-weight-medium);\n padding: var(--spacing-1) var(--spacing-2);\n outline: none;\n background: var(--background);\n width: 145px;\n color: var(--foreground);\n}\n\n.app-select__select:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\nimport { SelectConfig } from '../schema';\n\n@Component({\n tag: 'app-select',\n styleUrl: 'app-select.css',\n shadow: true,\n})\nexport class AppSelect {\n @Prop() value: string;\n @Prop() config: SelectConfig;\n @Event() valueChange: EventEmitter<{ value: string }>;\n\n render() {\n const c = this.config;\n const allowedAttrs = {\n id: c.name,\n name: c.name,\n disabled: c.disabled,\n required: c.required,\n readOnly: c.readOnly,\n placeholder: c.placeholder,\n autocomplete: c.autocomplete,\n };\n return (\n <div class=\"app-select\">\n {c.label && (\n <label class=\"app-select__label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <div>\n <select\n {...allowedAttrs}\n class=\"app-select__select\"\n onInput={(e: Event) => {\n const raw = (e.target as HTMLSelectElement).value;\n const matched = c.optionList.find(opt => String(opt) === raw);\n this.valueChange.emit({\n value: matched !== undefined ? matched : raw,\n });\n }}\n >\n {c.optionList?.map(option => (\n <option\n value={String(option)}\n key={String(option)}\n selected={this.value === option}\n >\n {String(option)}\n </option>\n ))}\n </select>\n </div>\n </div>\n );\n }\n}\n",".textarea-wrapper {\n margin-bottom: var(--spacing-4);\n}\n\n.textarea-label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.textarea-element {\n width: 95%;\n box-sizing: border-box;\n padding: var(--spacing-3);\n border: 2px solid var(--input);\n border-radius: var(--radius);\n font-size: var(--font-size-sm);\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n background: var(--background);\n color: var(--foreground);\n}\n\n.textarea-element:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.help-text {\n margin-top: var(--spacing-1);\n font-size: var(--font-size-xs);\n color: var(--muted-foreground, #6b7280);\n line-height: 1.4;\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\nimport { TextAreaConfig } from '../schema';\n\n@Component({\n tag: 'app-textarea',\n styleUrl: 'app-textarea.css',\n shadow: true,\n})\nexport class AppTextarea {\n @Prop() value: string;\n @Prop() config: TextAreaConfig;\n\n @Event() valueChange: EventEmitter<{ value: string }>;\n\n private handleChange = (e: Event) => {\n const target = e.target as HTMLTextAreaElement;\n\n this.valueChange.emit({\n value: target.value,\n });\n };\n\n render() {\n const c = this.config;\n\n const allowedAttrs = {\n placeholder: c.placeholder,\n required: c.required,\n disabled: c.disabled,\n readOnly: c.readOnly,\n rows: c.rows,\n id: c.name,\n name: c.name,\n autocomplete: c.autocomplete,\n };\n\n return (\n <div class=\"textarea-wrapper\">\n {c.label && (\n <label class=\"textarea-label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <textarea\n {...allowedAttrs}\n class=\"textarea-element\"\n value={this.value}\n onInput={this.handleChange}\n ></textarea>\n\n {c.helpText && <p class=\"help-text\">{c.helpText}</p>}\n </div>\n );\n }\n}\n"],"version":3}
1
+ {"file":"app-chips_4.cjs.entry.js","mappings":"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","names":["h"],"sources":["src/lib/form/components/app-chips.css?tag=app-chips&encapsulation=shadow","src/lib/form/components/app-chips.tsx","src/lib/form/components/app-select.css?tag=app-select&encapsulation=shadow","src/lib/form/components/app-select.tsx","src/lib/form/components/app-textarea.css?tag=app-textarea&encapsulation=shadow","src/lib/form/components/app-textarea.tsx"],"sourcesContent":[".app-chips {\n margin-bottom: var(--spacing-4);\n}\n\n.app-chips__label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.app-chips__container {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-2);\n align-items: center;\n}\n\n.app-chips__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-2);\n padding: var(--spacing-1) var(--spacing-2);\n font-size: var(--font-size-xs);\n font-weight: var(--font-weight-medium);\n border-radius: var(--radius-md);\n background: var(--accent);\n border: var(--border-width) solid var(--border);\n}\n\n/* Keyword-style chip override (non-URL chips) */\n.app-chips__chip:not(:has(a)) {\n background: var(--info);\n color: var(--info-foreground);\n border: none;\n border-radius: var(--radius-2xl);\n}\n\n.app-chips__link {\n color: var(--info);\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.app-chips__link:hover {\n text-decoration: underline;\n}\n\n.app-chips__remove {\n background: none;\n border: none;\n cursor: pointer;\n font-size: var(--font-size-xs);\n padding: 0;\n width: var(--spacing-4);\n height: var(--spacing-4);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-full);\n color: var(--muted-foreground);\n opacity: var(--opacity-muted);\n}\n\n.app-chips__chip:not(:has(a)) .app-chips__remove {\n color: var(--info-foreground);\n opacity: var(--opacity-hover);\n}\n\n.app-chips__remove:hover {\n opacity: 1;\n background: var(--muted);\n}\n\n.app-chips__chip:not(:has(a)) .app-chips__remove:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.app-chips__input {\n border: var(--border-width) solid var(--input);\n border-radius: var(--radius-md);\n padding: var(--spacing-2);\n font-size: var(--font-size-xs);\n outline: none;\n min-width: 120px;\n background: var(--background);\n color: var(--foreground);\n}\n\n.app-chips__input:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n","import { Component, Prop, h, Event, EventEmitter } from '@stencil/core';\nimport { ChipsConfig } from '../schema';\n\n@Component({\n tag: 'app-chips',\n styleUrl: 'app-chips.css',\n shadow: true,\n})\nexport class AppChips {\n @Prop() value: string[] = [];\n @Prop() config: ChipsConfig;\n\n @Event() addChip: EventEmitter<{ value: string }>;\n\n @Event() removeChip: EventEmitter<{ value: string }>;\n\n private emitAddChip(val: string) {\n this.addChip.emit({\n value: val,\n });\n }\n\n private emitRemoveChip(value: string) {\n this.removeChip.emit({\n value,\n });\n }\n\n private hasDuplicateChip(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n return this.value.some(chip => chip.trim().toLowerCase() === normalized);\n }\n\n render() {\n const c = this.config;\n\n const allowedAttrs = {\n placeholder: c.placeholder,\n required: c.required,\n disabled: c.disabled,\n readOnly: c.readOnly,\n id: c.name,\n name: c.name,\n autocomplete: c.autocomplete,\n };\n\n return (\n <div class=\"app-chips\">\n {c.label && (\n <label class=\"app-chips__label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <div class=\"app-chips__container\">\n {this.value.map((chip) => (\n <span class=\"app-chips__chip\" key={chip}>\n {c.type === 'url' ? (\n <a\n href={chip}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"app-chips__link\"\n >\n {chip}\n </a>\n ) : (\n chip\n )}\n\n <button\n class=\"app-chips__remove\"\n type=\"button\"\n onClick={() => this.emitRemoveChip(chip)}\n >\n ×\n </button>\n </span>\n ))}\n\n <input\n class=\"app-chips__input\"\n type={c.type || 'text'}\n {...allowedAttrs}\n onKeyDown={(e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n const input = e.target as HTMLInputElement;\n const val = input.value.trim();\n if (!val) return;\n if (this.hasDuplicateChip(val)) {\n input.value = '';\n return;\n }\n\n this.emitAddChip(val);\n input.value = '';\n }\n }}\n />\n </div>\n </div>\n );\n }\n}\n",".app-select {\n margin-bottom: var(--spacing-4);\n}\n\n.app-select__label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.app-select__select {\n border: var(--border-width) solid var(--input);\n border-radius: var(--radius-md);\n font-size: var(--font-size-sm);\n font-weight: var(--font-weight-medium);\n padding: var(--spacing-1) var(--spacing-2);\n outline: none;\n background: var(--background);\n width: 145px;\n color: var(--foreground);\n}\n\n.app-select__select:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\nimport { SelectConfig } from '../schema';\n\n@Component({\n tag: 'app-select',\n styleUrl: 'app-select.css',\n shadow: true,\n})\nexport class AppSelect {\n @Prop() value: string;\n @Prop() config: SelectConfig;\n @Event() valueChange: EventEmitter<{ value: string }>;\n\n render() {\n const c = this.config;\n const allowedAttrs = {\n id: c.name,\n name: c.name,\n disabled: c.disabled,\n required: c.required,\n readOnly: c.readOnly,\n placeholder: c.placeholder,\n autocomplete: c.autocomplete,\n };\n return (\n <div class=\"app-select\">\n {c.label && (\n <label class=\"app-select__label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <div>\n <select\n {...allowedAttrs}\n class=\"app-select__select\"\n onInput={(e: Event) => {\n const raw = (e.target as HTMLSelectElement).value;\n const matched = c.optionList.find(opt => String(opt) === raw);\n this.valueChange.emit({\n value: matched !== undefined ? matched : raw,\n });\n }}\n >\n {c.optionList?.map(option => (\n <option\n value={String(option)}\n key={String(option)}\n selected={this.value === option}\n >\n {String(option)}\n </option>\n ))}\n </select>\n </div>\n </div>\n );\n }\n}\n",".textarea-wrapper {\n margin-bottom: var(--spacing-4);\n}\n\n.textarea-label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.textarea-element {\n width: 95%;\n box-sizing: border-box;\n padding: var(--spacing-3);\n border: 2px solid var(--input);\n border-radius: var(--radius);\n font-size: var(--font-size-sm);\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n background: var(--background);\n color: var(--foreground);\n}\n\n.textarea-element:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.textarea-wrapper--read-only .textarea-label {\n color: var(--muted-foreground);\n}\n\n.textarea-element:read-only {\n background: var(--muted);\n color: var(--muted-foreground);\n border-color: var(--border);\n cursor: not-allowed;\n resize: none;\n}\n\n.textarea-element:read-only:focus {\n border-color: var(--border);\n box-shadow: none;\n}\n\n.help-text {\n margin-top: var(--spacing-1);\n font-size: var(--font-size-xs);\n color: var(--muted-foreground, #6b7280);\n line-height: 1.4;\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\nimport { TextAreaConfig } from '../schema';\n\n@Component({\n tag: 'app-textarea',\n styleUrl: 'app-textarea.css',\n shadow: true,\n})\nexport class AppTextarea {\n @Prop() value: string;\n @Prop() config: TextAreaConfig;\n\n @Event() valueChange: EventEmitter<{ value: string }>;\n\n private handleChange = (e: Event) => {\n const target = e.target as HTMLTextAreaElement;\n\n this.valueChange.emit({\n value: target.value,\n });\n };\n\n render() {\n const c = this.config;\n\n const allowedAttrs = {\n placeholder: c.placeholder,\n required: c.required,\n disabled: c.disabled,\n readOnly: c.readOnly,\n rows: c.rows,\n id: c.name,\n name: c.name,\n autocomplete: c.autocomplete,\n };\n\n return (\n <div\n class={{\n 'textarea-wrapper': true,\n 'textarea-wrapper--read-only': !!c.readOnly,\n }}\n >\n {c.label && (\n <label class=\"textarea-label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <textarea\n {...allowedAttrs}\n class=\"textarea-element\"\n value={this.value}\n onInput={this.handleChange}\n ></textarea>\n\n {c.helpText && <p class=\"help-text\">{c.helpText}</p>}\n </div>\n );\n }\n}\n"],"version":3}
@@ -338,7 +338,7 @@ function createExpectedOutcomeFieldFromSchema(schemaField) {
338
338
  type: 'select',
339
339
  label: schemaField.label,
340
340
  placeholder: schemaField.placeholder,
341
- value: '',
341
+ value: schemaField.options[0],
342
342
  options: schemaField.options,
343
343
  evaluationParameters: normalizeEvaluationParametersForField(schemaField.type, schemaField.evaluationParameters),
344
344
  };
@@ -4955,6 +4955,7 @@ const optionalPositiveInt = number().int().positive().optional();
4955
4955
  const optionalString = string().optional();
4956
4956
  const selectOptionsSchema = array(nonEmptyString).min(1);
4957
4957
  const optionalNumber = number().optional();
4958
+ const expectedOutcomeModeSchema = _enum(['static', 'dynamic']);
4958
4959
  const evaluationParametersSchema = object({
4959
4960
  approach: _enum(EvaluationApproach),
4960
4961
  threshold: optionalNumber,
@@ -5016,8 +5017,21 @@ const expectedOutcomeFieldSchema = discriminatedUnion('type', [
5016
5017
  defaultFieldDefinitions.text.extend({
5017
5018
  value: string(),
5018
5019
  }),
5019
- defaultFieldDefinitions.textarea.extend({
5020
+ defaultFieldDefinitions.textarea
5021
+ .extend({
5020
5022
  value: string(),
5023
+ outcomeMode: expectedOutcomeModeSchema.default('static'),
5024
+ resolutionQuery: string().optional(),
5025
+ })
5026
+ .superRefine((field, ctx) => {
5027
+ if (field.outcomeMode === 'dynamic' &&
5028
+ (!field.resolutionQuery || field.resolutionQuery.trim().length === 0)) {
5029
+ ctx.addIssue({
5030
+ code: 'custom',
5031
+ path: ['resolutionQuery'],
5032
+ message: 'resolutionQuery is required when outcomeMode is dynamic.',
5033
+ });
5034
+ }
5021
5035
  }),
5022
5036
  defaultFieldDefinitions.chipsInput.extend({
5023
5037
  value: array(string()).superRefine((values, ctx) => {
@@ -5111,6 +5125,50 @@ function importTestSuite(jsonContent) {
5111
5125
  }
5112
5126
  }
5113
5127
 
5128
+ const MISSING_RESOLVER_MESSAGE = 'resolveExpectedOutcome is required when a test case has dynamic expected outcomes.';
5129
+ function isDynamicTextareaField(field) {
5130
+ return field.type === 'textarea' && field.outcomeMode === 'dynamic';
5131
+ }
5132
+ function applyResolvedDynamicValues(testCase, resolvedValues) {
5133
+ if (resolvedValues.length === 0) {
5134
+ return testCase;
5135
+ }
5136
+ const expectedOutcome = [...(testCase.expectedOutcome || [])];
5137
+ for (const resolved of resolvedValues) {
5138
+ const field = expectedOutcome[resolved.index];
5139
+ if (!field || !isDynamicTextareaField(field)) {
5140
+ continue;
5141
+ }
5142
+ expectedOutcome[resolved.index] = {
5143
+ ...field,
5144
+ value: resolved.value,
5145
+ };
5146
+ }
5147
+ return {
5148
+ ...testCase,
5149
+ expectedOutcome,
5150
+ };
5151
+ }
5152
+ async function resolveDynamicExpectedOutcomes(testCase, resolver) {
5153
+ const dynamicFields = (testCase.expectedOutcome || []).flatMap((field, index) => {
5154
+ if (!isDynamicTextareaField(field)) {
5155
+ return [];
5156
+ }
5157
+ return [{ field, index }];
5158
+ });
5159
+ if (dynamicFields.length === 0) {
5160
+ return testCase;
5161
+ }
5162
+ if (!resolver) {
5163
+ throw new Error(MISSING_RESOLVER_MESSAGE);
5164
+ }
5165
+ const resolvedValues = await Promise.all(dynamicFields.map(async ({ field, index }) => ({
5166
+ index,
5167
+ value: await resolver(field.resolutionQuery || '', { testCase, fieldIndex: index }),
5168
+ })));
5169
+ return applyResolvedDynamicValues(testCase, resolvedValues);
5170
+ }
5171
+
5114
5172
  function applyExpectedOutcomeChange(testCase, change) {
5115
5173
  const { index } = change;
5116
5174
  const expectedOutcome = [...(testCase.expectedOutcome || [])];
@@ -5123,6 +5181,9 @@ function applyExpectedOutcomeChange(testCase, change) {
5123
5181
  if (target.type === 'chips-input') {
5124
5182
  return testCase;
5125
5183
  }
5184
+ if (target.type === 'textarea' && target.outcomeMode === 'dynamic') {
5185
+ return testCase;
5186
+ }
5126
5187
  expectedOutcome[index] = {
5127
5188
  ...target,
5128
5189
  value: change.value,
@@ -5151,6 +5212,38 @@ function applyExpectedOutcomeChange(testCase, change) {
5151
5212
  }
5152
5213
  case 'set-evaluation-approach':
5153
5214
  return updateExpectedOutcomeFieldApproach(testCase, index, change.value);
5215
+ case 'set-outcome-mode': {
5216
+ if (target.type !== 'textarea') {
5217
+ return testCase;
5218
+ }
5219
+ const mode = change.value;
5220
+ if (mode === 'static') {
5221
+ const { resolutionQuery: _, ...rest } = target;
5222
+ expectedOutcome[index] = {
5223
+ ...rest,
5224
+ outcomeMode: 'static',
5225
+ value: '',
5226
+ };
5227
+ }
5228
+ else {
5229
+ expectedOutcome[index] = {
5230
+ ...target,
5231
+ outcomeMode: 'dynamic',
5232
+ value: '',
5233
+ };
5234
+ }
5235
+ return { ...testCase, expectedOutcome };
5236
+ }
5237
+ case 'set-resolution-query': {
5238
+ if (target.type !== 'textarea' || target.outcomeMode !== 'dynamic') {
5239
+ return testCase;
5240
+ }
5241
+ expectedOutcome[index] = {
5242
+ ...target,
5243
+ resolutionQuery: change.value,
5244
+ };
5245
+ return { ...testCase, expectedOutcome };
5246
+ }
5154
5247
  }
5155
5248
  }
5156
5249
  /**
@@ -30018,13 +30111,20 @@ class EvaluationService {
30018
30111
  console.warn('⚠️ No output to evaluate for test case:', testCase.id);
30019
30112
  return;
30020
30113
  }
30021
- const fields = (testCase.expectedOutcome || []).map((field, index) => ({
30022
- index,
30023
- label: field.label,
30024
- type: field.type,
30025
- expectedValue: getFieldExpectedValue(field),
30026
- evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30027
- }));
30114
+ const fields = (testCase.expectedOutcome || []).flatMap((field, index) => {
30115
+ if (field.type === 'textarea' && field.outcomeMode === 'dynamic') {
30116
+ return [];
30117
+ }
30118
+ return [
30119
+ {
30120
+ index,
30121
+ label: field.label,
30122
+ type: field.type,
30123
+ expectedValue: getFieldExpectedValue(field),
30124
+ evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30125
+ },
30126
+ ];
30127
+ });
30028
30128
  const evaluationRequest = {
30029
30129
  testCaseId: testCase.id,
30030
30130
  question: testCase.question,
@@ -30119,7 +30219,7 @@ var FormFieldType;
30119
30219
  FormFieldType["SELECT"] = "select";
30120
30220
  })(FormFieldType || (FormFieldType = {}));
30121
30221
 
30122
- const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange, }) => {
30222
+ const ExpectedOutcomeRenderer = ({ testCaseId, fields, dynamicResolutionSupported = false, onExpectedOutcomeChange, }) => {
30123
30223
  const emit = (detail) => onExpectedOutcomeChange({
30124
30224
  detail,
30125
30225
  });
@@ -30132,6 +30232,23 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30132
30232
  optionList,
30133
30233
  defaultValue: EvaluationApproach.EXACT,
30134
30234
  });
30235
+ const buildOutcomeModeConfig = (index) => ({
30236
+ name: `expectedOutcomeMode-${index}`,
30237
+ fieldType: FormFieldType.SELECT,
30238
+ label: 'Outcome Mode',
30239
+ placeholder: 'Select outcome mode',
30240
+ required: true,
30241
+ optionList: ['static', 'dynamic'],
30242
+ defaultValue: 'static',
30243
+ });
30244
+ const buildResolutionQueryConfig = (index) => ({
30245
+ name: `expectedOutcomeResolutionQuery-${index}`,
30246
+ fieldType: FormFieldType.TEXT_AREA,
30247
+ label: 'Resolution Query',
30248
+ placeholder: 'Query used to resolve expected value',
30249
+ required: false,
30250
+ rows: 2,
30251
+ });
30135
30252
  const renderEvaluationSelector = (field, index$1) => {
30136
30253
  const optionList = getAllowedApproachesForFieldType(field.type);
30137
30254
  return (index.h("app-select", { config: buildEvaluationConfig(index$1, optionList), value: field.evaluationParameters?.approach, onValueChange: (e) => emit({
@@ -30143,12 +30260,17 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30143
30260
  };
30144
30261
  return (index.h("div", { class: "expected-outcome-renderer" }, (fields || []).map((field, index$1) => {
30145
30262
  if (field.type === 'textarea') {
30263
+ const isDynamic = dynamicResolutionSupported && field.outcomeMode === 'dynamic';
30146
30264
  const config = {
30147
30265
  name: `expectedOutcome-${index$1}`,
30148
30266
  fieldType: FormFieldType.TEXT_AREA,
30149
30267
  label: field.label,
30150
- placeholder: field.placeholder,
30151
- required: true,
30268
+ placeholder: isDynamic ? 'Resolved on run' : field.placeholder,
30269
+ required: !isDynamic,
30270
+ readOnly: isDynamic,
30271
+ helpText: isDynamic
30272
+ ? 'Filled automatically when the test is run'
30273
+ : undefined,
30152
30274
  rows: field.rows || 2,
30153
30275
  };
30154
30276
  return (index.h("div", { class: "expected-outcome-renderer__group" }, index.h("app-textarea", { config: config, value: field.value, onValueChange: (e) => emit({
@@ -30156,7 +30278,18 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30156
30278
  index: index$1,
30157
30279
  operation: 'set-value',
30158
30280
  value: e.detail.value,
30159
- }) }), renderEvaluationSelector(field, index$1)));
30281
+ }) }), dynamicResolutionSupported && (index.h("app-select", { config: buildOutcomeModeConfig(index$1), value: field.outcomeMode || 'static', onValueChange: (e) => emit({
30282
+ testCaseId,
30283
+ index: index$1,
30284
+ operation: 'set-outcome-mode',
30285
+ value: e.detail.value,
30286
+ }) })), dynamicResolutionSupported &&
30287
+ field.outcomeMode === 'dynamic' && (index.h("app-textarea", { config: buildResolutionQueryConfig(index$1), value: field.resolutionQuery || '', onValueChange: (e) => emit({
30288
+ testCaseId,
30289
+ index: index$1,
30290
+ operation: 'set-resolution-query',
30291
+ value: e.detail.value,
30292
+ }) })), !isDynamic && renderEvaluationSelector(field, index$1)));
30160
30293
  }
30161
30294
  if (field.type === 'chips-input') {
30162
30295
  const config = {
@@ -30203,7 +30336,7 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30203
30336
  })));
30204
30337
  };
30205
30338
 
30206
- const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30339
+ const LLMTestCaseRow = ({ testCase, dynamicResolutionSupported = false, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30207
30340
  const questionConfig = {
30208
30341
  name: 'question',
30209
30342
  fieldType: FormFieldType.TEXT_AREA,
@@ -30219,11 +30352,11 @@ const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExp
30219
30352
  key: 'question',
30220
30353
  value: e.detail.value,
30221
30354
  },
30222
- }) }), index.h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], onExpectedOutcomeChange: onExpectedOutcomeChange })), index.h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), index.h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), index.h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30355
+ }) }), index.h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], dynamicResolutionSupported: dynamicResolutionSupported, onExpectedOutcomeChange: onExpectedOutcomeChange })), index.h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), index.h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), index.h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30223
30356
  };
30224
30357
 
30225
- const LLMTestCases = ({ testCases, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30226
- return (index.h("div", { class: "test-cases" }, index.h("div", { class: "test-cases__column-headers" }, index.h("div", { class: "test-cases__column-header" }, "Input"), index.h("div", { class: "test-cases__column-header" }, "Output"), index.h("div", { class: "test-cases__column-header" }, "Evaluation"), index.h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (index.h(LLMTestCaseRow, { testCase: testCase, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange }))), index.h("div", { class: "test-cases__add-section" }, index.h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30358
+ const LLMTestCases = ({ testCases, dynamicResolutionSupported = false, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30359
+ return (index.h("div", { class: "test-cases" }, index.h("div", { class: "test-cases__column-headers" }, index.h("div", { class: "test-cases__column-header" }, "Input"), index.h("div", { class: "test-cases__column-header" }, "Output"), index.h("div", { class: "test-cases__column-header" }, "Evaluation"), index.h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (index.h(LLMTestCaseRow, { testCase: testCase, dynamicResolutionSupported: dynamicResolutionSupported, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange }))), index.h("div", { class: "test-cases__add-section" }, index.h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30227
30360
  };
30228
30361
 
30229
30362
  const tokensCss = () => `:host{--spacing:0.25rem;--spacing-1:calc(var(--spacing) * 1);--spacing-2:calc(var(--spacing) * 2);--spacing-3:calc(var(--spacing) * 3);--spacing-4:calc(var(--spacing) * 4);--spacing-5:calc(var(--spacing) * 5);--spacing-6:calc(var(--spacing) * 6);--spacing-8:calc(var(--spacing) * 8);--spacing-10:calc(var(--spacing) * 10);--spacing-12:calc(var(--spacing) * 12);--spacing-16:calc(var(--spacing) * 16);--spacing-20:calc(var(--spacing) * 20);--spacing-24:calc(var(--spacing) * 24);--radius-none:0;--radius-sm:0.125rem;--radius-md:0.375rem;--radius-lg:0.5rem;--radius-xl:0.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--radius-full:9999px;--radius:var(--radius-lg);--font-size-xs:0.75rem;--font-size-sm:0.875rem;--font-size-base:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--font-size-2xl:1.5rem;--font-size-3xl:1.875rem;--font-size-4xl:2.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-none:1;--line-height-tight:1.25;--line-height-snug:1.375;--line-height-normal:1.5;--line-height-relaxed:1.625;--line-height-loose:2;--letter-spacing-tight:-0.025em;--letter-spacing-normal:0;--letter-spacing-wide:0.05em;--shadow-sm:0 1px 2px 0 rgba(0, 0, 0, 0.05);--shadow-md:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);--shadow-lg:0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);--shadow-xl:0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);--shadow-2xl:0 25px 50px -12px rgba(0, 0, 0, 0.25);--border-width:1px;--z-base:0;--z-dropdown:1000;--z-sticky:1100;--z-modal:1200;--z-popover:1300;--z-tooltip:1400;--opacity-disabled:0.5;--opacity-hover:0.8;--opacity-muted:0.6;--max-w-sm:24rem;--max-w-md:28rem;--max-w-lg:32rem;--max-w-xl:42rem;--max-w-2xl:48rem;--max-w-full:100%;--breakpoint-sm:640px;--breakpoint-md:768px;--breakpoint-lg:1024px;--breakpoint-xl:1280px;--breakpoint-2xl:1536px;--background:#ffffff;--foreground:#0a0a0a;--card:#ffffff;--card-foreground:#0a0a0a;--popover:#ffffff;--popover-foreground:#0a0a0a;--primary:#0a0a0a;--primary-foreground:#fafafa;--secondary:#f4f4f5;--secondary-foreground:#0a0a0a;--muted:#f4f4f5;--muted-foreground:#71717a;--accent:#f4f4f5;--accent-foreground:#0a0a0a;--destructive:#ef4444;--destructive-foreground:#fafafa;--border:#e4e4e7;--input:#e4e4e7;--ring:#3b82f6;--success:#10b981;--success-foreground:#fafafa;--warning:#f59e0b;--warning-foreground:#fafafa;--info:#3b82f6;--info-foreground:#fafafa}:host([data-theme='dark']){--background:#0a0a0a;--foreground:#fafafa;--card:#171717;--card-foreground:#fafafa;--popover:#171717;--popover-foreground:#fafafa;--primary:#fafafa;--primary-foreground:#0a0a0a;--secondary:#27272a;--secondary-foreground:#fafafa;--muted:#27272a;--muted-foreground:#a1a1aa;--accent:#27272a;--accent-foreground:#fafafa;--destructive:#dc2626;--destructive-foreground:#fafafa;--border:#27272a;--input:#27272a;--ring:#3b82f6;--success:#059669;--success-foreground:#fafafa;--warning:#d97706;--warning-foreground:#fafafa;--info:#2563eb;--info-foreground:#fafafa}`;
@@ -30259,6 +30392,7 @@ const LLMTestRunner = class {
30259
30392
  delayMs = 500;
30260
30393
  useSave = false;
30261
30394
  usePromptEditor = false;
30395
+ resolveExpectedOutcome;
30262
30396
  initialTestCases;
30263
30397
  defaultExpectedOutcomeSchema;
30264
30398
  testCases = [
@@ -30322,6 +30456,9 @@ const LLMTestRunner = class {
30322
30456
  async resetSavingState() {
30323
30457
  this.isSaving = false;
30324
30458
  }
30459
+ async getTestCases() {
30460
+ return this.testCases;
30461
+ }
30325
30462
  handleTestCaseChange = (event) => {
30326
30463
  const { testCaseId, key, value } = event.detail;
30327
30464
  this.testCases = this.testCases.map(tc => tc.id === testCaseId ? { ...tc, [key]: value } : tc);
@@ -30342,38 +30479,62 @@ const LLMTestRunner = class {
30342
30479
  updateTestCase(id, updates) {
30343
30480
  this.testCases = this.testCases.map(tc => tc.id === id ? { ...tc, ...updates } : tc);
30344
30481
  }
30345
- async runSingleTest(testCase) {
30346
- const startTime = Date.now();
30347
- this.updateTestCase(testCase.id, { isRunning: true });
30482
+ requestLlmText(testCase) {
30348
30483
  return new Promise((resolve, reject) => {
30349
30484
  this.llmRequest.emit({
30350
30485
  prompt: testCase.question,
30351
- resolve: async (aiResponse) => {
30352
- const endTime = Date.now();
30353
- const responseTime = endTime - startTime;
30354
- this.updateTestCase(testCase.id, {
30355
- isRunning: false,
30356
- output: aiResponse,
30357
- error: null,
30358
- responseTime: responseTime,
30359
- });
30360
- await this.evaluateResponse({
30361
- ...testCase,
30362
- output: aiResponse,
30363
- responseTime: responseTime,
30364
- });
30365
- resolve();
30366
- },
30367
- reject: (error) => {
30368
- this.updateTestCase(testCase.id, {
30369
- isRunning: false,
30370
- output: null,
30371
- error: error instanceof Error ? error.message : 'Unknown error',
30372
- });
30373
- reject(error);
30374
- },
30486
+ resolve,
30487
+ reject,
30488
+ });
30489
+ });
30490
+ }
30491
+ throwError(reason) {
30492
+ throw reason instanceof Error ? reason : new Error(String(reason));
30493
+ }
30494
+ addErrorMessage(reason, fallback) {
30495
+ return reason instanceof Error ? reason.message : fallback;
30496
+ }
30497
+ async runSingleTest(testCase) {
30498
+ const startTime = Date.now();
30499
+ this.updateTestCase(testCase.id, { isRunning: true });
30500
+ const [llmSettled, resolutionSettled] = await Promise.allSettled([
30501
+ this.requestLlmText(testCase),
30502
+ resolveDynamicExpectedOutcomes(testCase, this.resolveExpectedOutcome),
30503
+ ]);
30504
+ const responseTime = Date.now() - startTime;
30505
+ if (llmSettled.status === 'rejected') {
30506
+ this.updateTestCase(testCase.id, {
30507
+ isRunning: false,
30508
+ output: null,
30509
+ error: this.addErrorMessage(llmSettled.reason, 'Unknown error'),
30510
+ responseTime,
30375
30511
  });
30512
+ this.throwError(llmSettled.reason);
30513
+ }
30514
+ const aiResponse = llmSettled.value;
30515
+ if (resolutionSettled.status === 'rejected') {
30516
+ this.updateTestCase(testCase.id, {
30517
+ isRunning: false,
30518
+ output: aiResponse,
30519
+ error: this.addErrorMessage(resolutionSettled.reason, 'Failed to resolve dynamic expected outcome.'),
30520
+ responseTime,
30521
+ });
30522
+ this.throwError(resolutionSettled.reason);
30523
+ }
30524
+ const resolvedTestCase = resolutionSettled.value;
30525
+ const forEvaluationTestCase = {
30526
+ ...resolvedTestCase,
30527
+ output: aiResponse,
30528
+ responseTime,
30529
+ };
30530
+ this.updateTestCase(testCase.id, {
30531
+ isRunning: false,
30532
+ output: aiResponse,
30533
+ error: null,
30534
+ responseTime,
30535
+ expectedOutcome: forEvaluationTestCase.expectedOutcome,
30376
30536
  });
30537
+ await this.evaluateResponse(forEvaluationTestCase);
30377
30538
  }
30378
30539
  deleteTestCase(id) {
30379
30540
  this.testCases = this.testCases.filter(tc => tc.id !== id);
@@ -30484,7 +30645,7 @@ const LLMTestRunner = class {
30484
30645
  }
30485
30646
  }
30486
30647
  render() {
30487
- return (index.h("div", { key: '323b5e140740bb72d4767c0763c382a6b125caa2', class: "test-runner-container" }, index.h(LLMTestRunnerHeader, { key: 'e1e2efdf6cfe5f406de7e26e745b5775f307d294', isExportingTestSuite: this.isExportingTestSuite, isExportingTestResults: this.isExportingTestResults, isRunningAll: this.isRunningAll, useSave: this.useSave, isSaving: this.isSaving, usePromptEditor: this.usePromptEditor, onImport: file => this.handleImport(file), onExportSuite: () => this.handleExportTestSuite(), onExportResults: () => this.handleExportTestResults(), onRunAll: () => this.runAllTests(), onSave: () => this.handleSave() }), index.h(ErrorMessage, { key: 'c6a34b81f66c6cd835eb8bc253f7a28d68c49874', message: this.error, onClear: () => (this.error = '') }), index.h("div", { key: '674daad8a2754afc8144463e9a173690a3d1d589', class: "test-runner-container__content" }, index.h(LLMTestCases, { key: '96c1aeae37f56378b7a9b5d54be73c5df48ae448', testCases: this.testCases, onRun: testCase => this.runSingleTest(testCase).catch(() => { }), onDelete: id => this.deleteTestCase(id), onAddTestCase: () => this.addNewTestCase(), handleTestCaseChange: this.handleTestCaseChange, onExpectedOutcomeChange: this.handleExpectedOutcomeChange }))));
30648
+ return (index.h("div", { key: '5536c02dcbf03e1d21de2df307f5255a17c000c7', class: "test-runner-container" }, index.h(LLMTestRunnerHeader, { key: 'b6b7db13d7b5576986f4de469c4eeff62b9be873', isExportingTestSuite: this.isExportingTestSuite, isExportingTestResults: this.isExportingTestResults, isRunningAll: this.isRunningAll, useSave: this.useSave, isSaving: this.isSaving, usePromptEditor: this.usePromptEditor, onImport: file => this.handleImport(file), onExportSuite: () => this.handleExportTestSuite(), onExportResults: () => this.handleExportTestResults(), onRunAll: () => this.runAllTests(), onSave: () => this.handleSave() }), index.h(ErrorMessage, { key: '48407658a40dd79864ddf24426df743df9206b30', message: this.error, onClear: () => (this.error = '') }), index.h("div", { key: '2b1db34dbb4defc98c255ca7bcb9318ca1c52cba', class: "test-runner-container__content" }, index.h(LLMTestCases, { key: '90b4d25f51d5de43790cabeb4fa6fc1c90c246cd', testCases: this.testCases, dynamicResolutionSupported: !!this.resolveExpectedOutcome, onRun: testCase => this.runSingleTest(testCase).catch(() => { }), onDelete: id => this.deleteTestCase(id), onAddTestCase: () => this.addNewTestCase(), handleTestCaseChange: this.handleTestCaseChange, onExpectedOutcomeChange: this.handleExpectedOutcomeChange }))));
30488
30649
  }
30489
30650
  };
30490
30651
  LLMTestRunner.style = tokensCss() + (llmTestRunnerCss() + (llmTestRunnerHeaderCss() + (llmTestCasesCss() + (llmTestCaseRowCss() + (rowActionsCss() + (evaluationSummaryCss() + (responseOutputCss() + (errorMessageCss() + (buttonCss() + iconButtonCss())))))))));