llm-testrunner-components 1.2.2 → 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 (61) 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 +202 -44
  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 +80 -30
  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-mutations.js +35 -0
  23. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -1
  24. package/dist/collection/schemas/expected-outcome.js +15 -1
  25. package/dist/collection/schemas/expected-outcome.js.map +1 -1
  26. package/dist/collection/schemas/test-case.js.map +1 -1
  27. package/dist/collection/types/expected-outcome.js.map +1 -1
  28. package/dist/collection/types/llm-test-runner.js.map +1 -1
  29. package/dist/components/app-textarea.js +1 -1
  30. package/dist/components/index.js +1 -1
  31. package/dist/components/llm-test-runner.js +1 -1
  32. package/dist/components/{p-CJBscebi.js → p-BcygfrMf.js} +3 -3
  33. package/dist/components/p-BcygfrMf.js.map +1 -0
  34. package/dist/components/p-CVtKFBJl.js +2 -0
  35. package/dist/components/p-CVtKFBJl.js.map +1 -0
  36. package/dist/esm/app-chips_4.entry.js +5 -2
  37. package/dist/esm/app-chips_4.entry.js.map +1 -1
  38. package/dist/esm/index.js +202 -44
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/llm-testrunner.js +1 -1
  41. package/dist/esm/loader.js +1 -1
  42. package/dist/llm-testrunner/index.esm.js +2 -2
  43. package/dist/llm-testrunner/index.esm.js.map +1 -1
  44. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  45. package/dist/llm-testrunner/{p-2cc09217.entry.js → p-5df053b4.entry.js} +2 -2
  46. package/dist/llm-testrunner/{p-2cc09217.entry.js.map → p-5df053b4.entry.js.map} +1 -1
  47. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +5 -0
  48. package/dist/types/components/llm-test-runner/test-cases/expected-outcome-renderer.d.ts +1 -0
  49. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +1 -0
  50. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +1 -0
  51. package/dist/types/components.d.ts +4 -0
  52. package/dist/types/lib/test-cases/dynamic-expected-outcome-resolver.d.ts +7 -0
  53. package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -1
  54. package/dist/types/schemas/expected-outcome.d.ts +16 -1
  55. package/dist/types/schemas/test-case.d.ts +17 -2
  56. package/dist/types/types/expected-outcome.d.ts +1 -1
  57. package/dist/types/types/llm-test-runner.d.ts +1 -1
  58. package/package.json +1 -1
  59. package/dist/components/p-BZrzx5jG.js +0 -2
  60. package/dist/components/p-BZrzx5jG.js.map +0 -1
  61. package/dist/components/p-CJBscebi.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ import{p as e,H as a,e as r,h as t,t as o}from"./p-D9BrlHdP.js";const n=()=>`.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}`;const s=e(class e extends a{constructor(e){super();if(e!==false){this.__registerHost()}this.__attachShadow();this.valueChange=r(this,"valueChange")}value;config;valueChange;handleChange=e=>{const a=e.target;this.valueChange.emit({value:a.value})};render(){const e=this.config;const a={placeholder:e.placeholder,required:e.required,disabled:e.disabled,readOnly:e.readOnly,rows:e.rows,id:e.name,name:e.name,autocomplete:e.autocomplete};return t("div",{key:"29bbcd954df6d18e968f6b2670224fda6f03a9f2",class:{"textarea-wrapper":true,"textarea-wrapper--read-only":!!e.readOnly}},e.label&&t("label",{key:"d95d3cbf01ce66c5bd074ede2eb1f87a0c98f3e4",class:"textarea-label",htmlFor:e.name},e.label),t("textarea",{key:"faeb28c35a543213d9c5847e93779f957e367ff0",...a,class:"textarea-element",value:this.value,onInput:this.handleChange}),e.helpText&&t("p",{key:"558abfe03f7107352a6d40aea78a35b304bc11a6",class:"help-text"},e.helpText))}static get style(){return n()}},[513,"app-textarea",{value:[1],config:[16]}]);function l(){if(typeof customElements==="undefined"){return}const e=["app-textarea"];e.forEach((e=>{switch(e){case"app-textarea":if(!customElements.get(o(e))){customElements.define(o(e),s)}break}}))}export{s as A,l as d};
2
+ //# sourceMappingURL=p-CVtKFBJl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["appTextareaCss","AppTextarea","__stencil_proxyCustomElement","HTMLElement","value","config","valueChange","handleChange","e","target","this","emit","render","c","allowedAttrs","placeholder","required","disabled","readOnly","rows","id","name","autocomplete","h","key","class","label","htmlFor","onInput","helpText"],"sources":["src/lib/form/components/app-textarea.css?tag=app-textarea&encapsulation=shadow","src/lib/form/components/app-textarea.tsx"],"sourcesContent":[".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"],"mappings":"gEAAA,MAAMA,EAAiB,IAAM,s/B,MCQhBC,EAAWC,EAAA,MAAAD,UAAAE,E,wHACdC,MACAC,OAECC,YAEDC,aAAgBC,IACtB,MAAMC,EAASD,EAAEC,OAEjBC,KAAKJ,YAAYK,KAAK,CACpBP,MAAOK,EAAOL,OACd,EAGJ,MAAAQ,GACE,MAAMC,EAAIH,KAAKL,OAEf,MAAMS,EAAe,CACnBC,YAAaF,EAAEE,YACfC,SAAUH,EAAEG,SACZC,SAAUJ,EAAEI,SACZC,SAAUL,EAAEK,SACZC,KAAMN,EAAEM,KACRC,GAAIP,EAAEQ,KACNA,KAAMR,EAAEQ,KACRC,aAAcT,EAAES,cAGlB,OACEC,EAAA,OAAAC,IAAA,2CACEC,MAAO,CACL,mBAAoB,KACpB,gCAAiCZ,EAAEK,WAGpCL,EAAEa,OACDH,EAAA,SAAAC,IAAA,2CAAOC,MAAM,iBAAiBE,QAASd,EAAEQ,MACtCR,EAAEa,OAIPH,EAAA,YAAAC,IAAA,8CACMV,EACJW,MAAM,mBACNrB,MAAOM,KAAKN,MACZwB,QAASlB,KAAKH,eAGfM,EAAEgB,UAAYN,EAAA,KAAAC,IAAA,2CAAGC,MAAM,aAAaZ,EAAEgB,U","ignoreList":[]}
@@ -88,7 +88,7 @@ const AppSelect = class {
88
88
  };
89
89
  AppSelect.style = appSelectCss();
90
90
 
91
- 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}`;
91
+ 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}`;
92
92
 
93
93
  const AppTextarea = class {
94
94
  constructor(hostRef) {
@@ -116,7 +116,10 @@ const AppTextarea = class {
116
116
  name: c.name,
117
117
  autocomplete: c.autocomplete,
118
118
  };
119
- return (h("div", { key: 'cc0073d70c9de40a8a12a1cabc3c1c06ac8c1863', class: "textarea-wrapper" }, c.label && (h("label", { key: 'a886cf95f25afee2e09ebcbc4dc62d2c9bca5ee0', class: "textarea-label", htmlFor: c.name }, c.label)), h("textarea", { key: 'c16b072269c450f3e09764b23cdfdaf966975977', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && h("p", { key: '9df941aa068b937d90f9a5f2a841a8fe426b3371', class: "help-text" }, c.helpText)));
119
+ return (h("div", { key: '29bbcd954df6d18e968f6b2670224fda6f03a9f2', class: {
120
+ 'textarea-wrapper': true,
121
+ 'textarea-wrapper--read-only': !!c.readOnly,
122
+ } }, c.label && (h("label", { key: 'd95d3cbf01ce66c5bd074ede2eb1f87a0c98f3e4', class: "textarea-label", htmlFor: c.name }, c.label)), h("textarea", { key: 'faeb28c35a543213d9c5847e93779f957e367ff0', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && h("p", { key: '558abfe03f7107352a6d40aea78a35b304bc11a6', class: "help-text" }, c.helpText)));
120
123
  }
121
124
  };
122
125
  AppTextarea.style = appTextareaCss();
@@ -1 +1 @@
1
- {"file":"app-chips_4.entry.js","mappings":"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","names":[],"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.entry.js","mappings":"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","names":[],"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}
package/dist/esm/index.js CHANGED
@@ -4952,6 +4952,7 @@ const optionalPositiveInt = number().int().positive().optional();
4952
4952
  const optionalString = string().optional();
4953
4953
  const selectOptionsSchema = array(nonEmptyString).min(1);
4954
4954
  const optionalNumber = number().optional();
4955
+ const expectedOutcomeModeSchema = _enum(['static', 'dynamic']);
4955
4956
  const evaluationParametersSchema = object({
4956
4957
  approach: _enum(EvaluationApproach),
4957
4958
  threshold: optionalNumber,
@@ -5013,8 +5014,21 @@ const expectedOutcomeFieldSchema = discriminatedUnion('type', [
5013
5014
  defaultFieldDefinitions.text.extend({
5014
5015
  value: string(),
5015
5016
  }),
5016
- defaultFieldDefinitions.textarea.extend({
5017
+ defaultFieldDefinitions.textarea
5018
+ .extend({
5017
5019
  value: string(),
5020
+ outcomeMode: expectedOutcomeModeSchema.default('static'),
5021
+ resolutionQuery: string().optional(),
5022
+ })
5023
+ .superRefine((field, ctx) => {
5024
+ if (field.outcomeMode === 'dynamic' &&
5025
+ (!field.resolutionQuery || field.resolutionQuery.trim().length === 0)) {
5026
+ ctx.addIssue({
5027
+ code: 'custom',
5028
+ path: ['resolutionQuery'],
5029
+ message: 'resolutionQuery is required when outcomeMode is dynamic.',
5030
+ });
5031
+ }
5018
5032
  }),
5019
5033
  defaultFieldDefinitions.chipsInput.extend({
5020
5034
  value: array(string()).superRefine((values, ctx) => {
@@ -5108,6 +5122,50 @@ function importTestSuite(jsonContent) {
5108
5122
  }
5109
5123
  }
5110
5124
 
5125
+ const MISSING_RESOLVER_MESSAGE = 'resolveExpectedOutcome is required when a test case has dynamic expected outcomes.';
5126
+ function isDynamicTextareaField(field) {
5127
+ return field.type === 'textarea' && field.outcomeMode === 'dynamic';
5128
+ }
5129
+ function applyResolvedDynamicValues(testCase, resolvedValues) {
5130
+ if (resolvedValues.length === 0) {
5131
+ return testCase;
5132
+ }
5133
+ const expectedOutcome = [...(testCase.expectedOutcome || [])];
5134
+ for (const resolved of resolvedValues) {
5135
+ const field = expectedOutcome[resolved.index];
5136
+ if (!field || !isDynamicTextareaField(field)) {
5137
+ continue;
5138
+ }
5139
+ expectedOutcome[resolved.index] = {
5140
+ ...field,
5141
+ value: resolved.value,
5142
+ };
5143
+ }
5144
+ return {
5145
+ ...testCase,
5146
+ expectedOutcome,
5147
+ };
5148
+ }
5149
+ async function resolveDynamicExpectedOutcomes(testCase, resolver) {
5150
+ const dynamicFields = (testCase.expectedOutcome || []).flatMap((field, index) => {
5151
+ if (!isDynamicTextareaField(field)) {
5152
+ return [];
5153
+ }
5154
+ return [{ field, index }];
5155
+ });
5156
+ if (dynamicFields.length === 0) {
5157
+ return testCase;
5158
+ }
5159
+ if (!resolver) {
5160
+ throw new Error(MISSING_RESOLVER_MESSAGE);
5161
+ }
5162
+ const resolvedValues = await Promise.all(dynamicFields.map(async ({ field, index }) => ({
5163
+ index,
5164
+ value: await resolver(field.resolutionQuery || '', { testCase, fieldIndex: index }),
5165
+ })));
5166
+ return applyResolvedDynamicValues(testCase, resolvedValues);
5167
+ }
5168
+
5111
5169
  function applyExpectedOutcomeChange(testCase, change) {
5112
5170
  const { index } = change;
5113
5171
  const expectedOutcome = [...(testCase.expectedOutcome || [])];
@@ -5120,6 +5178,9 @@ function applyExpectedOutcomeChange(testCase, change) {
5120
5178
  if (target.type === 'chips-input') {
5121
5179
  return testCase;
5122
5180
  }
5181
+ if (target.type === 'textarea' && target.outcomeMode === 'dynamic') {
5182
+ return testCase;
5183
+ }
5123
5184
  expectedOutcome[index] = {
5124
5185
  ...target,
5125
5186
  value: change.value,
@@ -5148,6 +5209,38 @@ function applyExpectedOutcomeChange(testCase, change) {
5148
5209
  }
5149
5210
  case 'set-evaluation-approach':
5150
5211
  return updateExpectedOutcomeFieldApproach(testCase, index, change.value);
5212
+ case 'set-outcome-mode': {
5213
+ if (target.type !== 'textarea') {
5214
+ return testCase;
5215
+ }
5216
+ const mode = change.value;
5217
+ if (mode === 'static') {
5218
+ const { resolutionQuery: _, ...rest } = target;
5219
+ expectedOutcome[index] = {
5220
+ ...rest,
5221
+ outcomeMode: 'static',
5222
+ value: '',
5223
+ };
5224
+ }
5225
+ else {
5226
+ expectedOutcome[index] = {
5227
+ ...target,
5228
+ outcomeMode: 'dynamic',
5229
+ value: '',
5230
+ };
5231
+ }
5232
+ return { ...testCase, expectedOutcome };
5233
+ }
5234
+ case 'set-resolution-query': {
5235
+ if (target.type !== 'textarea' || target.outcomeMode !== 'dynamic') {
5236
+ return testCase;
5237
+ }
5238
+ expectedOutcome[index] = {
5239
+ ...target,
5240
+ resolutionQuery: change.value,
5241
+ };
5242
+ return { ...testCase, expectedOutcome };
5243
+ }
5151
5244
  }
5152
5245
  }
5153
5246
  /**
@@ -30015,13 +30108,20 @@ class EvaluationService {
30015
30108
  console.warn('⚠️ No output to evaluate for test case:', testCase.id);
30016
30109
  return;
30017
30110
  }
30018
- const fields = (testCase.expectedOutcome || []).map((field, index) => ({
30019
- index,
30020
- label: field.label,
30021
- type: field.type,
30022
- expectedValue: getFieldExpectedValue(field),
30023
- evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30024
- }));
30111
+ const fields = (testCase.expectedOutcome || []).flatMap((field, index) => {
30112
+ if (field.type === 'textarea' && field.outcomeMode === 'dynamic') {
30113
+ return [];
30114
+ }
30115
+ return [
30116
+ {
30117
+ index,
30118
+ label: field.label,
30119
+ type: field.type,
30120
+ expectedValue: getFieldExpectedValue(field),
30121
+ evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30122
+ },
30123
+ ];
30124
+ });
30025
30125
  const evaluationRequest = {
30026
30126
  testCaseId: testCase.id,
30027
30127
  question: testCase.question,
@@ -30116,7 +30216,7 @@ var FormFieldType;
30116
30216
  FormFieldType["SELECT"] = "select";
30117
30217
  })(FormFieldType || (FormFieldType = {}));
30118
30218
 
30119
- const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange, }) => {
30219
+ const ExpectedOutcomeRenderer = ({ testCaseId, fields, dynamicResolutionSupported = false, onExpectedOutcomeChange, }) => {
30120
30220
  const emit = (detail) => onExpectedOutcomeChange({
30121
30221
  detail,
30122
30222
  });
@@ -30129,6 +30229,23 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30129
30229
  optionList,
30130
30230
  defaultValue: EvaluationApproach.EXACT,
30131
30231
  });
30232
+ const buildOutcomeModeConfig = (index) => ({
30233
+ name: `expectedOutcomeMode-${index}`,
30234
+ fieldType: FormFieldType.SELECT,
30235
+ label: 'Outcome Mode',
30236
+ placeholder: 'Select outcome mode',
30237
+ required: true,
30238
+ optionList: ['static', 'dynamic'],
30239
+ defaultValue: 'static',
30240
+ });
30241
+ const buildResolutionQueryConfig = (index) => ({
30242
+ name: `expectedOutcomeResolutionQuery-${index}`,
30243
+ fieldType: FormFieldType.TEXT_AREA,
30244
+ label: 'Resolution Query',
30245
+ placeholder: 'Query used to resolve expected value',
30246
+ required: false,
30247
+ rows: 2,
30248
+ });
30132
30249
  const renderEvaluationSelector = (field, index) => {
30133
30250
  const optionList = getAllowedApproachesForFieldType(field.type);
30134
30251
  return (h("app-select", { config: buildEvaluationConfig(index, optionList), value: field.evaluationParameters?.approach, onValueChange: (e) => emit({
@@ -30140,12 +30257,17 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30140
30257
  };
30141
30258
  return (h("div", { class: "expected-outcome-renderer" }, (fields || []).map((field, index) => {
30142
30259
  if (field.type === 'textarea') {
30260
+ const isDynamic = dynamicResolutionSupported && field.outcomeMode === 'dynamic';
30143
30261
  const config = {
30144
30262
  name: `expectedOutcome-${index}`,
30145
30263
  fieldType: FormFieldType.TEXT_AREA,
30146
30264
  label: field.label,
30147
- placeholder: field.placeholder,
30148
- required: true,
30265
+ placeholder: isDynamic ? 'Resolved on run' : field.placeholder,
30266
+ required: !isDynamic,
30267
+ readOnly: isDynamic,
30268
+ helpText: isDynamic
30269
+ ? 'Filled automatically when the test is run'
30270
+ : undefined,
30149
30271
  rows: field.rows || 2,
30150
30272
  };
30151
30273
  return (h("div", { class: "expected-outcome-renderer__group" }, h("app-textarea", { config: config, value: field.value, onValueChange: (e) => emit({
@@ -30153,7 +30275,18 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30153
30275
  index,
30154
30276
  operation: 'set-value',
30155
30277
  value: e.detail.value,
30156
- }) }), renderEvaluationSelector(field, index)));
30278
+ }) }), dynamicResolutionSupported && (h("app-select", { config: buildOutcomeModeConfig(index), value: field.outcomeMode || 'static', onValueChange: (e) => emit({
30279
+ testCaseId,
30280
+ index,
30281
+ operation: 'set-outcome-mode',
30282
+ value: e.detail.value,
30283
+ }) })), dynamicResolutionSupported &&
30284
+ field.outcomeMode === 'dynamic' && (h("app-textarea", { config: buildResolutionQueryConfig(index), value: field.resolutionQuery || '', onValueChange: (e) => emit({
30285
+ testCaseId,
30286
+ index,
30287
+ operation: 'set-resolution-query',
30288
+ value: e.detail.value,
30289
+ }) })), !isDynamic && renderEvaluationSelector(field, index)));
30157
30290
  }
30158
30291
  if (field.type === 'chips-input') {
30159
30292
  const config = {
@@ -30200,7 +30333,7 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30200
30333
  })));
30201
30334
  };
30202
30335
 
30203
- const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30336
+ const LLMTestCaseRow = ({ testCase, dynamicResolutionSupported = false, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30204
30337
  const questionConfig = {
30205
30338
  name: 'question',
30206
30339
  fieldType: FormFieldType.TEXT_AREA,
@@ -30216,11 +30349,11 @@ const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExp
30216
30349
  key: 'question',
30217
30350
  value: e.detail.value,
30218
30351
  },
30219
- }) }), h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], onExpectedOutcomeChange: onExpectedOutcomeChange })), h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30352
+ }) }), h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], dynamicResolutionSupported: dynamicResolutionSupported, onExpectedOutcomeChange: onExpectedOutcomeChange })), h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30220
30353
  };
30221
30354
 
30222
- const LLMTestCases = ({ testCases, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30223
- return (h("div", { class: "test-cases" }, h("div", { class: "test-cases__column-headers" }, h("div", { class: "test-cases__column-header" }, "Input"), h("div", { class: "test-cases__column-header" }, "Output"), h("div", { class: "test-cases__column-header" }, "Evaluation"), h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (h(LLMTestCaseRow, { testCase: testCase, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange }))), h("div", { class: "test-cases__add-section" }, h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30355
+ const LLMTestCases = ({ testCases, dynamicResolutionSupported = false, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30356
+ return (h("div", { class: "test-cases" }, h("div", { class: "test-cases__column-headers" }, h("div", { class: "test-cases__column-header" }, "Input"), h("div", { class: "test-cases__column-header" }, "Output"), h("div", { class: "test-cases__column-header" }, "Evaluation"), h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (h(LLMTestCaseRow, { testCase: testCase, dynamicResolutionSupported: dynamicResolutionSupported, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange }))), h("div", { class: "test-cases__add-section" }, h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30224
30357
  };
30225
30358
 
30226
30359
  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}`;
@@ -30256,6 +30389,7 @@ const LLMTestRunner = class {
30256
30389
  delayMs = 500;
30257
30390
  useSave = false;
30258
30391
  usePromptEditor = false;
30392
+ resolveExpectedOutcome;
30259
30393
  initialTestCases;
30260
30394
  defaultExpectedOutcomeSchema;
30261
30395
  testCases = [
@@ -30342,38 +30476,62 @@ const LLMTestRunner = class {
30342
30476
  updateTestCase(id, updates) {
30343
30477
  this.testCases = this.testCases.map(tc => tc.id === id ? { ...tc, ...updates } : tc);
30344
30478
  }
30345
- async runSingleTest(testCase) {
30346
- const startTime = Date.now();
30347
- this.updateTestCase(testCase.id, { isRunning: true });
30479
+ requestLlmText(testCase) {
30348
30480
  return new Promise((resolve, reject) => {
30349
30481
  this.llmRequest.emit({
30350
30482
  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
- },
30483
+ resolve,
30484
+ reject,
30485
+ });
30486
+ });
30487
+ }
30488
+ throwError(reason) {
30489
+ throw reason instanceof Error ? reason : new Error(String(reason));
30490
+ }
30491
+ addErrorMessage(reason, fallback) {
30492
+ return reason instanceof Error ? reason.message : fallback;
30493
+ }
30494
+ async runSingleTest(testCase) {
30495
+ const startTime = Date.now();
30496
+ this.updateTestCase(testCase.id, { isRunning: true });
30497
+ const [llmSettled, resolutionSettled] = await Promise.allSettled([
30498
+ this.requestLlmText(testCase),
30499
+ resolveDynamicExpectedOutcomes(testCase, this.resolveExpectedOutcome),
30500
+ ]);
30501
+ const responseTime = Date.now() - startTime;
30502
+ if (llmSettled.status === 'rejected') {
30503
+ this.updateTestCase(testCase.id, {
30504
+ isRunning: false,
30505
+ output: null,
30506
+ error: this.addErrorMessage(llmSettled.reason, 'Unknown error'),
30507
+ responseTime,
30375
30508
  });
30509
+ this.throwError(llmSettled.reason);
30510
+ }
30511
+ const aiResponse = llmSettled.value;
30512
+ if (resolutionSettled.status === 'rejected') {
30513
+ this.updateTestCase(testCase.id, {
30514
+ isRunning: false,
30515
+ output: aiResponse,
30516
+ error: this.addErrorMessage(resolutionSettled.reason, 'Failed to resolve dynamic expected outcome.'),
30517
+ responseTime,
30518
+ });
30519
+ this.throwError(resolutionSettled.reason);
30520
+ }
30521
+ const resolvedTestCase = resolutionSettled.value;
30522
+ const forEvaluationTestCase = {
30523
+ ...resolvedTestCase,
30524
+ output: aiResponse,
30525
+ responseTime,
30526
+ };
30527
+ this.updateTestCase(testCase.id, {
30528
+ isRunning: false,
30529
+ output: aiResponse,
30530
+ error: null,
30531
+ responseTime,
30532
+ expectedOutcome: forEvaluationTestCase.expectedOutcome,
30376
30533
  });
30534
+ await this.evaluateResponse(forEvaluationTestCase);
30377
30535
  }
30378
30536
  deleteTestCase(id) {
30379
30537
  this.testCases = this.testCases.filter(tc => tc.id !== id);
@@ -30484,7 +30642,7 @@ const LLMTestRunner = class {
30484
30642
  }
30485
30643
  }
30486
30644
  render() {
30487
- return (h("div", { key: '29cf8a93402ebad6f6df43e147fa10406577c9aa', class: "test-runner-container" }, h(LLMTestRunnerHeader, { key: 'a07d3d1d823f8d473808752932cd1b2ab72d9e08', 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() }), h(ErrorMessage, { key: 'ec68912728b06fc4a76c330fb1b7d5acde92c3d1', message: this.error, onClear: () => (this.error = '') }), h("div", { key: 'ce308dd4bd5437c94ae6e3e8a28970b799865281', class: "test-runner-container__content" }, h(LLMTestCases, { key: '3368df0bb7de4d099da1fad400f59dfc9a2cfb62', testCases: this.testCases, onRun: testCase => this.runSingleTest(testCase).catch(() => { }), onDelete: id => this.deleteTestCase(id), onAddTestCase: () => this.addNewTestCase(), handleTestCaseChange: this.handleTestCaseChange, onExpectedOutcomeChange: this.handleExpectedOutcomeChange }))));
30645
+ return (h("div", { key: '5536c02dcbf03e1d21de2df307f5255a17c000c7', class: "test-runner-container" }, 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() }), h(ErrorMessage, { key: '48407658a40dd79864ddf24426df743df9206b30', message: this.error, onClear: () => (this.error = '') }), h("div", { key: '2b1db34dbb4defc98c255ca7bcb9318ca1c52cba', class: "test-runner-container__content" }, 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
30646
  }
30489
30647
  };
30490
30648
  LLMTestRunner.style = tokensCss() + (llmTestRunnerCss() + (llmTestRunnerHeaderCss() + (llmTestCasesCss() + (llmTestCaseRowCss() + (rowActionsCss() + (evaluationSummaryCss() + (responseOutputCss() + (errorMessageCss() + (buttonCss() + iconButtonCss())))))))));