llm-testrunner-components 1.2.4 → 1.3.1

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 (93) hide show
  1. package/README.md +9 -5
  2. package/dist/cjs/{app-chips_5.cjs.entry.js → app-chips_4.cjs.entry.js} +20 -22
  3. package/dist/cjs/app-chips_4.cjs.entry.js.map +1 -0
  4. package/dist/cjs/index.cjs.js +464 -66
  5. package/dist/cjs/index.cjs.js.map +1 -1
  6. package/dist/cjs/llm-test-runner.cjs.entry.js +11 -0
  7. package/dist/cjs/llm-test-runner.cjs.entry.js.map +1 -0
  8. package/dist/cjs/llm-testrunner.cjs.js +1 -1
  9. package/dist/cjs/loader.cjs.js +1 -1
  10. package/dist/collection/components/llm-test-runner/llm-test-runner.js +46 -13
  11. package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
  12. package/dist/collection/components/llm-test-runner/test-cases/chat-history.css +5 -5
  13. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js +45 -5
  14. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js.map +1 -1
  15. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.css +21 -0
  16. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +2 -2
  17. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -1
  18. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +2 -2
  19. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -1
  20. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js +1 -1
  21. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js.map +1 -1
  22. package/dist/collection/demo/demo-modes.js +130 -0
  23. package/dist/collection/demo/vanilla-demo.js +56 -0
  24. package/dist/collection/lib/evaluation/actual-value-resolver.js +52 -0
  25. package/dist/collection/lib/evaluation/actual-value-resolver.js.map +1 -0
  26. package/dist/collection/lib/evaluation/evaluation-engine.js +1 -1
  27. package/dist/collection/lib/evaluation/evaluation-engine.js.map +1 -1
  28. package/dist/collection/lib/evaluation/evaluation-service.js +55 -17
  29. package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -1
  30. package/dist/collection/lib/evaluation/types.js.map +1 -1
  31. package/dist/collection/lib/form/components/app-textarea.css +2 -2
  32. package/dist/collection/lib/import-export/test-suite-importer.js +7 -1
  33. package/dist/collection/lib/import-export/test-suite-importer.js.map +1 -1
  34. package/dist/collection/lib/test-cases/test-case-factory.js +5 -0
  35. package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -1
  36. package/dist/collection/lib/test-cases/test-case-mutations.js +58 -23
  37. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -1
  38. package/dist/collection/schemas/expected-outcome.js +39 -0
  39. package/dist/collection/schemas/expected-outcome.js.map +1 -1
  40. package/dist/collection/schemas/model-response.js +7 -0
  41. package/dist/collection/schemas/model-response.js.map +1 -0
  42. package/dist/collection/schemas/test-case.js +2 -1
  43. package/dist/collection/schemas/test-case.js.map +1 -1
  44. package/dist/collection/types/expected-outcome.js.map +1 -1
  45. package/dist/collection/types/llm-test-runner.js.map +1 -1
  46. package/dist/components/app-textarea.js +1 -1
  47. package/dist/components/chat-history.js +1 -1
  48. package/dist/components/index.js +1 -1
  49. package/dist/components/llm-test-runner.js +1 -1
  50. package/dist/components/{p-B87Lt3z4.js → p-D3eincg_.js} +3 -3
  51. package/dist/components/p-D3eincg_.js.map +1 -0
  52. package/dist/components/{p-D2qDAxFN.js → p-D6BL2E3J.js} +2 -2
  53. package/dist/components/{p-D2qDAxFN.js.map → p-D6BL2E3J.js.map} +1 -1
  54. package/dist/components/p-kmtfMXcQ.js +2 -0
  55. package/dist/components/p-kmtfMXcQ.js.map +1 -0
  56. package/dist/esm/{app-chips_5.entry.js → app-chips_4.entry.js} +4 -5
  57. package/dist/esm/app-chips_4.entry.js.map +1 -0
  58. package/dist/esm/index.js +464 -66
  59. package/dist/esm/index.js.map +1 -1
  60. package/dist/esm/llm-test-runner.entry.js +5 -0
  61. package/dist/esm/llm-test-runner.entry.js.map +1 -0
  62. package/dist/esm/llm-testrunner.js +1 -1
  63. package/dist/esm/loader.js +1 -1
  64. package/dist/llm-testrunner/index.esm.js +2 -2
  65. package/dist/llm-testrunner/index.esm.js.map +1 -1
  66. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  67. package/dist/llm-testrunner/p-c3fec0bb.entry.js +2 -0
  68. package/dist/llm-testrunner/{p-21202f12.entry.js.map → p-c3fec0bb.entry.js.map} +1 -1
  69. package/dist/llm-testrunner/p-caccdb4b.entry.js +2 -0
  70. package/dist/llm-testrunner/p-caccdb4b.entry.js.map +1 -0
  71. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +3 -4
  72. package/dist/types/components/llm-test-runner/test-cases/expected-outcome-renderer.d.ts +1 -0
  73. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +1 -0
  74. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +1 -0
  75. package/dist/types/components/llm-test-runner/test-cases/output/response-output.d.ts +2 -1
  76. package/dist/types/components.d.ts +4 -2
  77. package/dist/types/lib/evaluation/actual-value-resolver.d.ts +9 -0
  78. package/dist/types/lib/evaluation/evaluation-service.d.ts +2 -2
  79. package/dist/types/lib/evaluation/types.d.ts +1 -1
  80. package/dist/types/lib/import-export/test-suite-importer.d.ts +1 -1
  81. package/dist/types/lib/test-cases/test-case-mutations.d.ts +10 -1
  82. package/dist/types/schemas/expected-outcome.d.ts +116 -0
  83. package/dist/types/schemas/model-response.d.ts +7 -0
  84. package/dist/types/schemas/test-case.d.ts +76 -1
  85. package/dist/types/types/expected-outcome.d.ts +1 -1
  86. package/dist/types/types/llm-test-runner.d.ts +4 -2
  87. package/package.json +1 -1
  88. package/dist/cjs/app-chips_5.cjs.entry.js.map +0 -1
  89. package/dist/components/p-B87Lt3z4.js.map +0 -1
  90. package/dist/components/p-Bx2jqguC.js +0 -2
  91. package/dist/components/p-Bx2jqguC.js.map +0 -1
  92. package/dist/esm/app-chips_5.entry.js.map +0 -1
  93. package/dist/llm-testrunner/p-21202f12.entry.js +0 -2
@@ -1,2 +1,2 @@
1
- import{p as e,b as t}from"./p-DxzhGhec.js";export{s as setNonce}from"./p-DxzhGhec.js";import{g as a}from"./p-GQwFOmwJ.js";var r=()=>{const t=import.meta.url;const s={};if(t!==""){s.resourcesUrl=new URL(".",t).href}return e(s)};r().then((async e=>{await a();return t([["p-21202f12",[[513,"llm-test-runner",{delayMs:[2,"delay-ms"],useSave:[4,"use-save"],usePromptEditor:[4,"use-prompt-editor"],resolveExpectedOutcome:[16],initialTestCases:[16],defaultExpectedOutcomeSchema:[16],testCases:[32],isRunningAll:[32],error:[32],isExportingTestSuite:[32],isExportingTestResults:[32],isSaving:[32],resetSavingState:[64],getTestCases:[64]}],[513,"app-chips",{value:[16],config:[16]}],[513,"app-select",{value:[1],config:[16]}],[513,"app-textarea",{value:[1],config:[16]}],[513,"chat-history",{chatHistoryEnabled:[4,"chat-history-enabled"],chatHistoryValue:[1,"chat-history-value"]}]]]],e)}));
1
+ import{p as e,b as t}from"./p-DxzhGhec.js";export{s as setNonce}from"./p-DxzhGhec.js";import{g as a}from"./p-GQwFOmwJ.js";var r=()=>{const t=import.meta.url;const s={};if(t!==""){s.resourcesUrl=new URL(".",t).href}return e(s)};r().then((async e=>{await a();return t([["p-c3fec0bb",[[513,"app-chips",{value:[16],config:[16]}],[513,"app-select",{value:[1],config:[16]}],[513,"app-textarea",{value:[1],config:[16]}],[513,"chat-history",{chatHistoryEnabled:[4,"chat-history-enabled"],chatHistoryValue:[1,"chat-history-value"]}]]],["p-caccdb4b",[[513,"llm-test-runner",{delayMs:[2,"delay-ms"],useSave:[4,"use-save"],usePromptEditor:[4,"use-prompt-editor"],resolveExpectedOutcome:[16],evaluationSourceExtractors:[16],initialTestCases:[16],defaultExpectedOutcomeSchema:[16],testCases:[32],isRunningAll:[32],error:[32],isExportingTestSuite:[32],isExportingTestResults:[32],isSaving:[32],resetSavingState:[64],getTestCases:[64]}]]]],e)}));
2
2
  //# sourceMappingURL=llm-testrunner.esm.js.map
@@ -0,0 +1,2 @@
1
+ import{r as e,c as a,h as r}from"./p-DxzhGhec.js";const t=()=>`.app-chips{margin-bottom:var(--spacing-4)}.app-chips__label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.app-chips__container{display:flex;flex-wrap:wrap;gap:var(--spacing-2);align-items:center}.app-chips__chip{display:inline-flex;align-items:center;gap:var(--spacing-2);padding:var(--spacing-1) var(--spacing-2);font-size:var(--font-size-xs);font-weight:var(--font-weight-medium);border-radius:var(--radius-md);background:var(--accent);border:var(--border-width) solid var(--border)}.app-chips__chip:not(:has(a)){background:var(--info);color:var(--info-foreground);border:none;border-radius:var(--radius-2xl)}.app-chips__link{color:var(--info);text-decoration:none;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.app-chips__link:hover{text-decoration:underline}.app-chips__remove{background:none;border:none;cursor:pointer;font-size:var(--font-size-xs);padding:0;width:var(--spacing-4);height:var(--spacing-4);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-full);color:var(--muted-foreground);opacity:var(--opacity-muted)}.app-chips__chip:not(:has(a)) .app-chips__remove{color:var(--info-foreground);opacity:var(--opacity-hover)}.app-chips__remove:hover{opacity:1;background:var(--muted)}.app-chips__chip:not(:has(a)) .app-chips__remove:hover{background:rgba(255, 255, 255, 0.2)}.app-chips__input{border:var(--border-width) solid var(--input);border-radius:var(--radius-md);padding:var(--spacing-2);font-size:var(--font-size-xs);outline:none;min-width:120px;background:var(--background);color:var(--foreground)}.app-chips__input:focus{border-color:var(--ring);box-shadow:0 0 0 2px rgba(59, 130, 246, 0.1)}`;const i=class{constructor(r){e(this,r);this.addChip=a(this,"addChip");this.removeChip=a(this,"removeChip")}value=[];config;addChip;removeChip;emitAddChip(e){this.addChip.emit({value:e})}emitRemoveChip(e){this.removeChip.emit({value:e})}hasDuplicateChip(e){const a=e.trim().toLowerCase();return this.value.some((e=>e.trim().toLowerCase()===a))}render(){const e=this.config;const a={placeholder:e.placeholder,required:e.required,disabled:e.disabled,readOnly:e.readOnly,id:e.name,name:e.name,autocomplete:e.autocomplete};return r("div",{key:"fb7d4d5444e9c9ac33c56aec88e3e10ed103c8be",class:"app-chips"},e.label&&r("label",{key:"2d0041b3a137fecddef2273eac3792b5e8de27ab",class:"app-chips__label",htmlFor:e.name},e.label),r("div",{key:"f73b1105e567b233626073e05b9da712689e7b12",class:"app-chips__container"},this.value.map((a=>r("span",{class:"app-chips__chip",key:a},e.type==="url"?r("a",{href:a,target:"_blank",rel:"noopener noreferrer",class:"app-chips__link"},a):a,r("button",{class:"app-chips__remove",type:"button",onClick:()=>this.emitRemoveChip(a)},"×")))),r("input",{key:"7676ff95531b34d10cbf9402e72a723e7e123e0e",class:"app-chips__input",type:e.type||"text",...a,onKeyDown:e=>{if(e.key==="Enter"){const a=e.target;const r=a.value.trim();if(!r)return;if(this.hasDuplicateChip(r)){a.value="";return}this.emitAddChip(r);a.value=""}}})))}};i.style=t();const o=()=>`.app-select{margin-bottom:var(--spacing-4)}.app-select__label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.app-select__select{border:var(--border-width) solid var(--input);border-radius:var(--radius-md);font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);padding:var(--spacing-1) var(--spacing-2);outline:none;background:var(--background);width:145px;color:var(--foreground)}.app-select__select:focus{border-color:var(--ring);box-shadow:0 0 0 2px rgba(59, 130, 246, 0.1)}`;const s=class{constructor(r){e(this,r);this.valueChange=a(this,"valueChange")}value;config;valueChange;render(){const e=this.config;const a={id:e.name,name:e.name,disabled:e.disabled,required:e.required,readOnly:e.readOnly,placeholder:e.placeholder,autocomplete:e.autocomplete};return r("div",{key:"968f0fffe1eff976ac7e00f02db6fb84aa529de4",class:"app-select"},e.label&&r("label",{key:"dac02d2335754ff5d6ce6ba1df5777f8b019cfae",class:"app-select__label",htmlFor:e.name},e.label),r("div",{key:"c92698199479bbdde1cfd559d69fc97e54d2862a"},r("select",{key:"6e9b27b034d057921f52fbd12653c5ef1b6af1bc",...a,class:"app-select__select",onInput:a=>{const r=a.target.value;const t=e.optionList.find((e=>String(e)===r));this.valueChange.emit({value:t!==undefined?t:r})}},e.optionList?.map((e=>r("option",{value:String(e),key:String(e),selected:this.value===e},String(e)))))))}};s.style=o();const c=()=>`.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:default;resize:vertical}.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 n=class{constructor(r){e(this,r);this.valueChange=a(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 r("div",{key:"f0749b1f2badf8434272da9fb37b354b42ba988b",class:{"textarea-wrapper":true,"textarea-wrapper--read-only":!!e.readOnly}},e.label&&r("label",{key:"3448c838bcf9e962df005eae8fc313d216497c35",class:"textarea-label",htmlFor:e.name},e.label),r("textarea",{key:"b4ee67a24fa71b0fa042625f943b0e226a6d14b7",...a,class:"textarea-element",value:this.value,onInput:this.handleChange}),e.helpText&&r("p",{key:"fb6263c32e6cc5e36dbc77344c31487d63d51a1c",class:"help-text"},e.helpText))}};n.style=c();const d=()=>`.chat-history{display:flex;flex-direction:column;gap:var(--spacing-3);margin-top:var(--spacing-4)}.chat-history__toggle-row{display:flex;align-items:center}.chat-history__switch{position:relative;display:inline-flex;align-items:center;gap:var(--spacing-3);cursor:pointer;user-select:none}.chat-history__switch-input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.chat-history__switch-ui{position:relative;flex-shrink:0;display:flex;align-items:center;padding:0px 4px;width:2.75rem;height:1.5rem;border-radius:var(--radius-full);background:var(--muted);border:var(--border-width) solid var(--border);transition:background 0.15s ease, border-color 0.15s ease}@media (prefers-reduced-motion: reduce){.chat-history__switch-ui,.chat-history__switch-thumb{transition:none}}.chat-history__switch-thumb{display:inline-block;width:calc(1.5rem - 6px);height:calc(1.5rem - 6px);border-radius:var(--radius-full);background:var(--background);box-shadow:var(--shadow-sm);transition:transform 0.15s ease}.chat-history__switch-input:checked+.chat-history__switch-ui{background:var(--primary);border-color:var(--primary)}.chat-history__switch-input:checked+.chat-history__switch-ui .chat-history__switch-thumb{transform:translateX(calc(2.75rem - (1.5rem - 6px)))}.chat-history__switch-input:focus-visible+.chat-history__switch-ui{outline:2px solid var(--ring);outline-offset:2px}.chat-history__switch-text{font-size:var(--font-size-sm, 0.875rem);color:var(--foreground)}.chat-history__textarea{width:100%;box-sizing:border-box;padding:var(--spacing-2) var(--spacing-3);border:var(--border-width) solid var(--border);border-radius:var(--radius-md);font:inherit;resize:vertical;min-height:9rem;background:var(--background)}.chat-history__textarea:focus-visible{outline:2px solid var(--ring);outline-offset:2px}`;const h=`[\n {"role": "user", "content": "How do I import a saved suite?"},\n {"role": "model", "content": "Use Import and pick the JSON from Export suite."}\n]`;const p=class{constructor(r){e(this,r);this.chatHistoryChange=a(this,"chatHistoryChange")}chatHistoryEnabled=false;chatHistoryValue="";chatHistoryChange;emit(e){this.chatHistoryChange.emit(e)}onToggle=e=>{const a=e.target.checked;this.emit({enabled:a,value:this.chatHistoryValue})};onTextInput=e=>{const a=e.target.value;this.emit({enabled:this.chatHistoryEnabled,value:a})};render(){return r("div",{key:"f444a4b5bd9b48df151cad67b54bef54116d11b3",class:"chat-history"},r("div",{key:"7216d764fb905ac5e8e33209d9586525e59fa218",class:"chat-history__toggle-row"},r("label",{key:"d5d092c323b48543c96525d449bcb03dcaf5113b",class:"chat-history__switch"},r("input",{key:"5104ce460686434156454fc1dfd3685ba0e0968c",type:"checkbox",class:"chat-history__switch-input",checked:this.chatHistoryEnabled,onInput:this.onToggle}),r("span",{key:"fa2e51bce0d6ff2d3bf133383e1ce940521097b0",class:"chat-history__switch-ui","aria-hidden":"true"},r("span",{key:"e0d897ca166623ece09834b44b5baa12605cb9f3",class:"chat-history__switch-thumb"})),r("span",{key:"e769678fde5d670e634d7a8e905ef09ce936f440",class:"chat-history__switch-text"},"Chat history"))),this.chatHistoryEnabled?r("textarea",{class:"chat-history__textarea",value:this.chatHistoryValue,rows:8,placeholder:h,"aria-label":"Chat history",onInput:this.onTextInput}):null)}};p.style=d();export{i as app_chips,s as app_select,n as app_textarea,p as chat_history};
2
+ //# sourceMappingURL=p-c3fec0bb.entry.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["appChipsCss","AppChips","value","config","addChip","removeChip","emitAddChip","val","this","emit","emitRemoveChip","hasDuplicateChip","normalized","trim","toLowerCase","some","chip","render","c","allowedAttrs","placeholder","required","disabled","readOnly","id","name","autocomplete","h","key","class","label","htmlFor","map","type","href","target","rel","onClick","onKeyDown","e","input","appSelectCss","AppSelect","valueChange","onInput","raw","matched","optionList","find","opt","String","undefined","option","selected","appTextareaCss","AppTextarea","handleChange","rows","helpText","chatHistoryCss","CHAT_HISTORY_PLACEHOLDER","ChatHistory","chatHistoryEnabled","chatHistoryValue","chatHistoryChange","detail","onToggle","checked","enabled","onTextInput"],"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","src/components/llm-test-runner/test-cases/chat-history.css?tag=chat-history&encapsulation=shadow","src/components/llm-test-runner/test-cases/chat-history.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",".chat-history {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-3);\n margin-top: var(--spacing-4);\n}\n\n.chat-history__toggle-row {\n display: flex;\n align-items: center;\n}\n\n.chat-history__switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-3);\n cursor: pointer;\n user-select: none;\n}\n\n.chat-history__switch-input {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.chat-history__switch-ui {\n position: relative;\n flex-shrink: 0;\n width: 2.75rem;\n height: 1.5rem;\n border-radius: var(--radius-full);\n background: var(--muted);\n border: var(--border-width) solid var(--border);\n transition:\n background 0.15s ease,\n border-color 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chat-history__switch-ui,\n .chat-history__switch-thumb {\n transition: none;\n }\n}\n\n.chat-history__switch-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: calc(1.5rem - 6px);\n height: calc(1.5rem - 6px);\n border-radius: var(--radius-full);\n background: var(--background);\n box-shadow: var(--shadow-sm);\n transition: transform 0.15s ease;\n}\n\n.chat-history__switch-input:checked + .chat-history__switch-ui {\n background: var(--primary);\n border-color: var(--primary);\n}\n\n.chat-history__switch-input:checked + .chat-history__switch-ui .chat-history__switch-thumb {\n /* track width − end padding (2px × 2) − thumb width */\n transform: translateX(calc(2.75rem - (1.5rem - 6px) - 4px));\n}\n\n.chat-history__switch-input:focus-visible + .chat-history__switch-ui {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.chat-history__switch-text {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--foreground);\n}\n\n.chat-history__textarea {\n width: 100%;\n box-sizing: border-box;\n padding: var(--spacing-2) var(--spacing-3);\n border: var(--border-width) solid var(--border);\n border-radius: var(--radius-md);\n font: inherit;\n resize: vertical;\n min-height: 9rem;\n background: var(--background);\n}\n\n.chat-history__textarea:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\n\nconst CHAT_HISTORY_PLACEHOLDER = `[\n {\"role\": \"user\", \"content\": \"How do I import a saved suite?\"},\n {\"role\": \"model\", \"content\": \"Use Import and pick the JSON from Export suite.\"}\n]`;\n\nexport type ChatHistoryChangeDetail = {\n enabled: boolean;\n value: string;\n};\n\n@Component({\n tag: 'chat-history',\n styleUrl: 'chat-history.css',\n shadow: true,\n})\nexport class ChatHistory {\n @Prop() chatHistoryEnabled = false;\n @Prop() chatHistoryValue = '';\n\n @Event({ bubbles: true, composed: true })\n chatHistoryChange: EventEmitter<ChatHistoryChangeDetail>;\n\n private emit(detail: ChatHistoryChangeDetail) {\n this.chatHistoryChange.emit(detail);\n }\n\n private onToggle = (e: Event) => {\n const checked = (e.target as HTMLInputElement).checked;\n this.emit({ enabled: checked, value: this.chatHistoryValue });\n };\n\n private onTextInput = (e: Event) => {\n const value = (e.target as HTMLTextAreaElement).value;\n this.emit({ enabled: this.chatHistoryEnabled, value });\n };\n\n render() {\n return (\n <div class=\"chat-history\">\n <div class=\"chat-history__toggle-row\">\n <label class=\"chat-history__switch\">\n <input\n type=\"checkbox\"\n class=\"chat-history__switch-input\"\n checked={this.chatHistoryEnabled}\n onInput={this.onToggle}\n />\n <span class=\"chat-history__switch-ui\" aria-hidden=\"true\">\n <span class=\"chat-history__switch-thumb\" />\n </span>\n <span class=\"chat-history__switch-text\">Chat history</span>\n </label>\n </div>\n {this.chatHistoryEnabled ? (\n <textarea\n class=\"chat-history__textarea\"\n value={this.chatHistoryValue}\n rows={8}\n placeholder={CHAT_HISTORY_PLACEHOLDER}\n aria-label=\"Chat history\"\n onInput={this.onTextInput}\n />\n ) : null}\n </div>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["appChipsCss","AppChips","value","config","addChip","removeChip","emitAddChip","val","this","emit","emitRemoveChip","hasDuplicateChip","normalized","trim","toLowerCase","some","chip","render","c","allowedAttrs","placeholder","required","disabled","readOnly","id","name","autocomplete","h","key","class","label","htmlFor","map","type","href","target","rel","onClick","onKeyDown","e","input","appSelectCss","AppSelect","valueChange","onInput","raw","matched","optionList","find","opt","String","undefined","option","selected","appTextareaCss","AppTextarea","handleChange","rows","helpText","chatHistoryCss","CHAT_HISTORY_PLACEHOLDER","ChatHistory","chatHistoryEnabled","chatHistoryValue","chatHistoryChange","detail","onToggle","checked","enabled","onTextInput"],"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","src/components/llm-test-runner/test-cases/chat-history.css?tag=chat-history&encapsulation=shadow","src/components/llm-test-runner/test-cases/chat-history.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: default;\n resize: vertical;\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",".chat-history {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-3);\n margin-top: var(--spacing-4);\n}\n\n.chat-history__toggle-row {\n display: flex;\n align-items: center;\n}\n\n.chat-history__switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-3);\n cursor: pointer;\n user-select: none;\n}\n\n.chat-history__switch-input {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.chat-history__switch-ui {\n position: relative;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n padding: 0px 4px;\n width: 2.75rem;\n height: 1.5rem;\n border-radius: var(--radius-full);\n background: var(--muted);\n border: var(--border-width) solid var(--border);\n transition:\n background 0.15s ease,\n border-color 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chat-history__switch-ui,\n .chat-history__switch-thumb {\n transition: none;\n }\n}\n\n.chat-history__switch-thumb {\n display: inline-block;\n width: calc(1.5rem - 6px);\n height: calc(1.5rem - 6px);\n border-radius: var(--radius-full);\n background: var(--background);\n box-shadow: var(--shadow-sm);\n transition: transform 0.15s ease;\n}\n\n.chat-history__switch-input:checked + .chat-history__switch-ui {\n background: var(--primary);\n border-color: var(--primary);\n}\n\n.chat-history__switch-input:checked + .chat-history__switch-ui .chat-history__switch-thumb {\n transform: translateX(calc(2.75rem - (1.5rem - 6px)));\n}\n\n.chat-history__switch-input:focus-visible + .chat-history__switch-ui {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n\n.chat-history__switch-text {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--foreground);\n}\n\n.chat-history__textarea {\n width: 100%;\n box-sizing: border-box;\n padding: var(--spacing-2) var(--spacing-3);\n border: var(--border-width) solid var(--border);\n border-radius: var(--radius-md);\n font: inherit;\n resize: vertical;\n min-height: 9rem;\n background: var(--background);\n}\n\n.chat-history__textarea:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\n\nconst CHAT_HISTORY_PLACEHOLDER = `[\n {\"role\": \"user\", \"content\": \"How do I import a saved suite?\"},\n {\"role\": \"model\", \"content\": \"Use Import and pick the JSON from Export suite.\"}\n]`;\n\nexport type ChatHistoryChangeDetail = {\n enabled: boolean;\n value: string;\n};\n\n@Component({\n tag: 'chat-history',\n styleUrl: 'chat-history.css',\n shadow: true,\n})\nexport class ChatHistory {\n @Prop() chatHistoryEnabled = false;\n @Prop() chatHistoryValue = '';\n\n @Event({ bubbles: true, composed: true })\n chatHistoryChange: EventEmitter<ChatHistoryChangeDetail>;\n\n private emit(detail: ChatHistoryChangeDetail) {\n this.chatHistoryChange.emit(detail);\n }\n\n private onToggle = (e: Event) => {\n const checked = (e.target as HTMLInputElement).checked;\n this.emit({ enabled: checked, value: this.chatHistoryValue });\n };\n\n private onTextInput = (e: Event) => {\n const value = (e.target as HTMLTextAreaElement).value;\n this.emit({ enabled: this.chatHistoryEnabled, value });\n };\n\n render() {\n return (\n <div class=\"chat-history\">\n <div class=\"chat-history__toggle-row\">\n <label class=\"chat-history__switch\">\n <input\n type=\"checkbox\"\n class=\"chat-history__switch-input\"\n checked={this.chatHistoryEnabled}\n onInput={this.onToggle}\n />\n <span class=\"chat-history__switch-ui\" aria-hidden=\"true\">\n <span class=\"chat-history__switch-thumb\" />\n </span>\n <span class=\"chat-history__switch-text\">Chat history</span>\n </label>\n </div>\n {this.chatHistoryEnabled ? (\n <textarea\n class=\"chat-history__textarea\"\n value={this.chatHistoryValue}\n rows={8}\n placeholder={CHAT_HISTORY_PLACEHOLDER}\n aria-label=\"Chat history\"\n onInput={this.onTextInput}\n />\n ) : null}\n </div>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export{LLMTestRunner as llm_test_runner}from"./index.esm.js";import"./p-DxzhGhec.js";
2
+ //# sourceMappingURL=p-caccdb4b.entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":[],"mappings":"","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from '../../stencil-public-runtime';
2
- import { ExpectedOutcomeSchema, TestCase, LLMRequestPayload, SavePayload } from '../../types/llm-test-runner';
2
+ import { ExpectedOutcomeSchema, TestCase, LLMRequestPayload, SavePayload, EvaluationSourceExtractors } from '../../types/llm-test-runner';
3
3
  import { type ExpectedOutcomeResolver } from '../../lib/test-cases/dynamic-expected-outcome-resolver';
4
4
  export declare class LLMTestRunner {
5
5
  llmRequest: EventEmitter<LLMRequestPayload>;
@@ -8,6 +8,7 @@ export declare class LLMTestRunner {
8
8
  useSave?: boolean;
9
9
  usePromptEditor?: boolean;
10
10
  resolveExpectedOutcome?: ExpectedOutcomeResolver;
11
+ evaluationSourceExtractors?: EvaluationSourceExtractors;
11
12
  initialTestCases?: TestCase[];
12
13
  defaultExpectedOutcomeSchema?: ExpectedOutcomeSchema;
13
14
  testCases: TestCase[];
@@ -19,15 +20,13 @@ export declare class LLMTestRunner {
19
20
  private evaluationService;
20
21
  private getResolvedExpectedOutcomeSchema;
21
22
  componentWillLoad(): void;
22
- componentDidLoad(): void;
23
- disconnectedCallback(): void;
24
23
  resetSavingState(): Promise<void>;
25
24
  getTestCases(): Promise<TestCase[]>;
26
25
  private handleTestCaseChange;
27
26
  private handleChatHistoryChange;
28
27
  private addNewTestCase;
29
28
  private updateTestCase;
30
- private requestLlmText;
29
+ private requestLlmResponse;
31
30
  private throwError;
32
31
  private addErrorMessage;
33
32
  private runSingleTest;
@@ -8,6 +8,7 @@ interface ExpectedOutcomeRendererProps {
8
8
  testCaseId: string;
9
9
  fields: ExpectedOutcomeField[];
10
10
  dynamicResolutionSupported?: boolean;
11
+ extractorIds?: string[];
11
12
  onExpectedOutcomeChange: (e: CustomEvent<ExpectedOutcomeChangeDetail>) => void;
12
13
  }
13
14
  export declare const ExpectedOutcomeRenderer: FunctionalComponent<ExpectedOutcomeRendererProps>;
@@ -8,6 +8,7 @@ export type ChatHistoryRowChangeDetail = {
8
8
  export interface LLMTestCaseRowProps {
9
9
  testCase: TestCase;
10
10
  dynamicResolutionSupported?: boolean;
11
+ extractorIds?: string[];
11
12
  onRun: (testCase: TestCase) => void;
12
13
  onDelete: (id: string) => void;
13
14
  handleTestCaseChange: (e: CustomEvent<{
@@ -5,6 +5,7 @@ import { ExpectedOutcomeChangeDetail } from './expected-outcome-renderer';
5
5
  export interface LLMTestCasesProps {
6
6
  testCases: TestCase[];
7
7
  dynamicResolutionSupported?: boolean;
8
+ extractorIds?: string[];
8
9
  onRun: (testCase: TestCase) => void;
9
10
  onDelete: (id: string) => void;
10
11
  onAddTestCase: () => void;
@@ -1,6 +1,7 @@
1
1
  import { FunctionalComponent } from '../../../../stencil-public-runtime';
2
+ import type { ModelResponsePayload } from '../../../../types/llm-test-runner';
2
3
  export interface ResponseOutputProps {
3
- output?: string;
4
+ output?: ModelResponsePayload;
4
5
  isRunning: boolean;
5
6
  }
6
7
  export declare const ResponseOutput: FunctionalComponent<ResponseOutputProps>;
@@ -8,12 +8,12 @@ import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
8
8
  import { ChipsConfig, SelectConfig, TextAreaConfig } from "./lib/form/schema";
9
9
  import { ChatHistoryChangeDetail } from "./components/llm-test-runner/test-cases/chat-history";
10
10
  import { ExpectedOutcomeResolver } from "./lib/test-cases/dynamic-expected-outcome-resolver";
11
- import { ExpectedOutcomeSchema, LLMRequestPayload, SavePayload, TestCase } from "./types/llm-test-runner";
11
+ import { EvaluationSourceExtractors, ExpectedOutcomeSchema, LLMRequestPayload, SavePayload, TestCase } from "./types/llm-test-runner";
12
12
  import { EvaluationResult } from "./lib/evaluation/types";
13
13
  export { ChipsConfig, SelectConfig, TextAreaConfig } from "./lib/form/schema";
14
14
  export { ChatHistoryChangeDetail } from "./components/llm-test-runner/test-cases/chat-history";
15
15
  export { ExpectedOutcomeResolver } from "./lib/test-cases/dynamic-expected-outcome-resolver";
16
- export { ExpectedOutcomeSchema, LLMRequestPayload, SavePayload, TestCase } from "./types/llm-test-runner";
16
+ export { EvaluationSourceExtractors, ExpectedOutcomeSchema, LLMRequestPayload, SavePayload, TestCase } from "./types/llm-test-runner";
17
17
  export { EvaluationResult } from "./lib/evaluation/types";
18
18
  export namespace Components {
19
19
  interface AppChips {
@@ -47,6 +47,7 @@ export namespace Components {
47
47
  * @default 500
48
48
  */
49
49
  "delayMs"?: number;
50
+ "evaluationSourceExtractors"?: EvaluationSourceExtractors;
50
51
  "getTestCases": () => Promise<TestCase[]>;
51
52
  "initialTestCases"?: TestCase[];
52
53
  "resetSavingState": () => Promise<void>;
@@ -214,6 +215,7 @@ declare namespace LocalJSX {
214
215
  * @default 500
215
216
  */
216
217
  "delayMs"?: number;
218
+ "evaluationSourceExtractors"?: EvaluationSourceExtractors;
217
219
  "initialTestCases"?: TestCase[];
218
220
  "onLlmRequest"?: (event: LlmTestRunnerCustomEvent<LLMRequestPayload>) => void;
219
221
  "onSave"?: (event: LlmTestRunnerCustomEvent<SavePayload>) => void;
@@ -0,0 +1,9 @@
1
+ import type { EvaluationSourceExtractors, ExpectedOutcomeField, ModelResponsePayload } from '../../types/llm-test-runner';
2
+ export type ResolvedActualValue = {
3
+ success: true;
4
+ value: string;
5
+ } | {
6
+ success: false;
7
+ error: string;
8
+ };
9
+ export declare function resolveActualValue(field: ExpectedOutcomeField, output?: ModelResponsePayload, extractors?: EvaluationSourceExtractors): Promise<ResolvedActualValue>;
@@ -1,5 +1,5 @@
1
1
  import { EvaluationResult } from './types';
2
- import { TestCase } from '../../types/llm-test-runner';
2
+ import { TestCase, EvaluationSourceExtractors } from '../../types/llm-test-runner';
3
3
  /**
4
4
  * Service for evaluating test case responses
5
5
  */
@@ -11,5 +11,5 @@ export declare class EvaluationService {
11
11
  * @param testCase - The test case to evaluate
12
12
  * @param onResult - Callback to handle the evaluation result
13
13
  */
14
- evaluateTestCase(testCase: TestCase, onResult: (result: EvaluationResult) => void): Promise<void>;
14
+ evaluateTestCase(testCase: TestCase, onResult: (result: EvaluationResult) => void, extractors?: EvaluationSourceExtractors): Promise<void>;
15
15
  }
@@ -12,12 +12,12 @@ export interface FieldEvaluationInput {
12
12
  label: string;
13
13
  type: ExpectedOutcomeFieldType;
14
14
  expectedValue: string;
15
+ actualResponse: string;
15
16
  evaluationParameters: EvaluationParameters;
16
17
  }
17
18
  export interface EvaluationRequestV2 {
18
19
  testCaseId: string;
19
20
  question: string;
20
- actualResponse: string;
21
21
  fields: FieldEvaluationInput[];
22
22
  }
23
23
  export interface EvaluationResult {
@@ -9,4 +9,4 @@ export interface ImportValidationResult {
9
9
  * @param jsonContent - The JSON string to parse and validate
10
10
  * @returns Validation result with test cases or error message
11
11
  */
12
- export declare function importTestSuite(jsonContent: string): ImportValidationResult;
12
+ export declare function importTestSuite(jsonContent: string, allowedExtractorIds?: string[]): ImportValidationResult;
@@ -1,4 +1,4 @@
1
- import { TestCase, type ExpectedOutcomeMode } from '../../types/llm-test-runner';
1
+ import { TestCase, type EvaluationSource, type ExpectedOutcomeMode } from '../../types/llm-test-runner';
2
2
  import { EvaluationApproach } from '../evaluation/constants';
3
3
  export type ExpectedOutcomeChange = {
4
4
  index: number;
@@ -24,6 +24,15 @@ export type ExpectedOutcomeChange = {
24
24
  index: number;
25
25
  operation: 'set-resolution-query';
26
26
  value: string;
27
+ } | {
28
+ index: number;
29
+ operation: 'set-evaluation-source-type';
30
+ value: EvaluationSource['type'];
31
+ fallbackExtractorId?: string;
32
+ } | {
33
+ index: number;
34
+ operation: 'set-evaluation-source-extractor';
35
+ value: string;
27
36
  };
28
37
  export declare function applyExpectedOutcomeChange(testCase: TestCase, change: ExpectedOutcomeChange): TestCase;
29
38
  /**
@@ -1,17 +1,41 @@
1
1
  import { z } from 'zod';
2
2
  import { EvaluationApproach } from '../lib/evaluation/constants';
3
+ import type { ModelResponsePayload } from './model-response';
4
+ export declare const evaluationSourceExtractorSchema: z.ZodCustom<(payload: ModelResponsePayload) => string | Promise<string>, (payload: ModelResponsePayload) => string | Promise<string>>;
5
+ export declare const evaluationSourceExtractorsSchema: z.ZodRecord<z.ZodString, z.ZodCustom<(payload: ModelResponsePayload) => string | Promise<string>, (payload: ModelResponsePayload) => string | Promise<string>>>;
6
+ export declare const evaluationSourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7
+ type: z.ZodLiteral<"text">;
8
+ }, z.core.$strip>, z.ZodObject<{
9
+ type: z.ZodLiteral<"custom">;
10
+ extractorId: z.ZodString;
11
+ }, z.core.$strip>], "type">;
3
12
  export declare const expectedOutcomeModeSchema: z.ZodEnum<{
4
13
  static: "static";
5
14
  dynamic: "dynamic";
6
15
  }>;
7
16
  export type ExpectedOutcomeMode = z.infer<typeof expectedOutcomeModeSchema>;
17
+ export type EvaluationSource = z.infer<typeof evaluationSourceSchema>;
18
+ export type EvaluationSourceExtractor = z.infer<typeof evaluationSourceExtractorSchema>;
19
+ export type EvaluationSourceExtractors = z.infer<typeof evaluationSourceExtractorsSchema>;
8
20
  declare const defaultExpectedOutcomeBaseSchema: z.ZodObject<{
9
21
  label: z.ZodString;
10
22
  placeholder: z.ZodOptional<z.ZodString>;
23
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
24
+ type: z.ZodLiteral<"text">;
25
+ }, z.core.$strip>, z.ZodObject<{
26
+ type: z.ZodLiteral<"custom">;
27
+ extractorId: z.ZodString;
28
+ }, z.core.$strip>], "type">>;
11
29
  }, z.core.$strip>;
12
30
  export declare const expectedOutcomeSchemaFieldSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
13
31
  label: z.ZodString;
14
32
  placeholder: z.ZodOptional<z.ZodString>;
33
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
34
+ type: z.ZodLiteral<"text">;
35
+ }, z.core.$strip>, z.ZodObject<{
36
+ type: z.ZodLiteral<"custom">;
37
+ extractorId: z.ZodString;
38
+ }, z.core.$strip>], "type">>;
15
39
  type: z.ZodLiteral<"text">;
16
40
  evaluationParameters: z.ZodOptional<z.ZodObject<{
17
41
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -20,6 +44,12 @@ export declare const expectedOutcomeSchemaFieldSchema: z.ZodDiscriminatedUnion<[
20
44
  }, z.core.$strip>, z.ZodObject<{
21
45
  label: z.ZodString;
22
46
  placeholder: z.ZodOptional<z.ZodString>;
47
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
48
+ type: z.ZodLiteral<"text">;
49
+ }, z.core.$strip>, z.ZodObject<{
50
+ type: z.ZodLiteral<"custom">;
51
+ extractorId: z.ZodString;
52
+ }, z.core.$strip>], "type">>;
23
53
  type: z.ZodLiteral<"textarea">;
24
54
  rows: z.ZodOptional<z.ZodNumber>;
25
55
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -29,6 +59,12 @@ export declare const expectedOutcomeSchemaFieldSchema: z.ZodDiscriminatedUnion<[
29
59
  }, z.core.$strip>, z.ZodObject<{
30
60
  label: z.ZodString;
31
61
  placeholder: z.ZodOptional<z.ZodString>;
62
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
63
+ type: z.ZodLiteral<"text">;
64
+ }, z.core.$strip>, z.ZodObject<{
65
+ type: z.ZodLiteral<"custom">;
66
+ extractorId: z.ZodString;
67
+ }, z.core.$strip>], "type">>;
32
68
  type: z.ZodLiteral<"chips-input">;
33
69
  evaluationParameters: z.ZodOptional<z.ZodObject<{
34
70
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -37,6 +73,12 @@ export declare const expectedOutcomeSchemaFieldSchema: z.ZodDiscriminatedUnion<[
37
73
  }, z.core.$strip>, z.ZodObject<{
38
74
  label: z.ZodString;
39
75
  placeholder: z.ZodOptional<z.ZodString>;
76
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
77
+ type: z.ZodLiteral<"text">;
78
+ }, z.core.$strip>, z.ZodObject<{
79
+ type: z.ZodLiteral<"custom">;
80
+ extractorId: z.ZodString;
81
+ }, z.core.$strip>], "type">>;
40
82
  type: z.ZodLiteral<"select">;
41
83
  options: z.ZodArray<z.ZodString>;
42
84
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -47,6 +89,12 @@ export declare const expectedOutcomeSchemaFieldSchema: z.ZodDiscriminatedUnion<[
47
89
  export declare const expectedOutcomeSchemaSchema: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
48
90
  label: z.ZodString;
49
91
  placeholder: z.ZodOptional<z.ZodString>;
92
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
93
+ type: z.ZodLiteral<"text">;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ type: z.ZodLiteral<"custom">;
96
+ extractorId: z.ZodString;
97
+ }, z.core.$strip>], "type">>;
50
98
  type: z.ZodLiteral<"text">;
51
99
  evaluationParameters: z.ZodOptional<z.ZodObject<{
52
100
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -55,6 +103,12 @@ export declare const expectedOutcomeSchemaSchema: z.ZodArray<z.ZodDiscriminatedU
55
103
  }, z.core.$strip>, z.ZodObject<{
56
104
  label: z.ZodString;
57
105
  placeholder: z.ZodOptional<z.ZodString>;
106
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
107
+ type: z.ZodLiteral<"text">;
108
+ }, z.core.$strip>, z.ZodObject<{
109
+ type: z.ZodLiteral<"custom">;
110
+ extractorId: z.ZodString;
111
+ }, z.core.$strip>], "type">>;
58
112
  type: z.ZodLiteral<"textarea">;
59
113
  rows: z.ZodOptional<z.ZodNumber>;
60
114
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -64,6 +118,12 @@ export declare const expectedOutcomeSchemaSchema: z.ZodArray<z.ZodDiscriminatedU
64
118
  }, z.core.$strip>, z.ZodObject<{
65
119
  label: z.ZodString;
66
120
  placeholder: z.ZodOptional<z.ZodString>;
121
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
122
+ type: z.ZodLiteral<"text">;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ type: z.ZodLiteral<"custom">;
125
+ extractorId: z.ZodString;
126
+ }, z.core.$strip>], "type">>;
67
127
  type: z.ZodLiteral<"chips-input">;
68
128
  evaluationParameters: z.ZodOptional<z.ZodObject<{
69
129
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -72,6 +132,12 @@ export declare const expectedOutcomeSchemaSchema: z.ZodArray<z.ZodDiscriminatedU
72
132
  }, z.core.$strip>, z.ZodObject<{
73
133
  label: z.ZodString;
74
134
  placeholder: z.ZodOptional<z.ZodString>;
135
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
136
+ type: z.ZodLiteral<"text">;
137
+ }, z.core.$strip>, z.ZodObject<{
138
+ type: z.ZodLiteral<"custom">;
139
+ extractorId: z.ZodString;
140
+ }, z.core.$strip>], "type">>;
75
141
  type: z.ZodLiteral<"select">;
76
142
  options: z.ZodArray<z.ZodString>;
77
143
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -82,6 +148,12 @@ export declare const expectedOutcomeSchemaSchema: z.ZodArray<z.ZodDiscriminatedU
82
148
  export declare const expectedOutcomeFieldSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
83
149
  label: z.ZodString;
84
150
  placeholder: z.ZodOptional<z.ZodString>;
151
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
152
+ type: z.ZodLiteral<"text">;
153
+ }, z.core.$strip>, z.ZodObject<{
154
+ type: z.ZodLiteral<"custom">;
155
+ extractorId: z.ZodString;
156
+ }, z.core.$strip>], "type">>;
85
157
  type: z.ZodLiteral<"text">;
86
158
  evaluationParameters: z.ZodOptional<z.ZodObject<{
87
159
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -91,6 +163,12 @@ export declare const expectedOutcomeFieldSchema: z.ZodDiscriminatedUnion<[z.ZodO
91
163
  }, z.core.$strip>, z.ZodObject<{
92
164
  label: z.ZodString;
93
165
  placeholder: z.ZodOptional<z.ZodString>;
166
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
167
+ type: z.ZodLiteral<"text">;
168
+ }, z.core.$strip>, z.ZodObject<{
169
+ type: z.ZodLiteral<"custom">;
170
+ extractorId: z.ZodString;
171
+ }, z.core.$strip>], "type">>;
94
172
  type: z.ZodLiteral<"textarea">;
95
173
  rows: z.ZodOptional<z.ZodNumber>;
96
174
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -106,6 +184,12 @@ export declare const expectedOutcomeFieldSchema: z.ZodDiscriminatedUnion<[z.ZodO
106
184
  }, z.core.$strip>, z.ZodObject<{
107
185
  label: z.ZodString;
108
186
  placeholder: z.ZodOptional<z.ZodString>;
187
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
188
+ type: z.ZodLiteral<"text">;
189
+ }, z.core.$strip>, z.ZodObject<{
190
+ type: z.ZodLiteral<"custom">;
191
+ extractorId: z.ZodString;
192
+ }, z.core.$strip>], "type">>;
109
193
  type: z.ZodLiteral<"chips-input">;
110
194
  evaluationParameters: z.ZodOptional<z.ZodObject<{
111
195
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -115,6 +199,12 @@ export declare const expectedOutcomeFieldSchema: z.ZodDiscriminatedUnion<[z.ZodO
115
199
  }, z.core.$strip>, z.ZodObject<{
116
200
  label: z.ZodString;
117
201
  placeholder: z.ZodOptional<z.ZodString>;
202
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
203
+ type: z.ZodLiteral<"text">;
204
+ }, z.core.$strip>, z.ZodObject<{
205
+ type: z.ZodLiteral<"custom">;
206
+ extractorId: z.ZodString;
207
+ }, z.core.$strip>], "type">>;
118
208
  type: z.ZodLiteral<"select">;
119
209
  options: z.ZodArray<z.ZodString>;
120
210
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -126,6 +216,12 @@ export declare const expectedOutcomeFieldSchema: z.ZodDiscriminatedUnion<[z.ZodO
126
216
  export declare const expectedOutcomeArraySchema: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
127
217
  label: z.ZodString;
128
218
  placeholder: z.ZodOptional<z.ZodString>;
219
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
220
+ type: z.ZodLiteral<"text">;
221
+ }, z.core.$strip>, z.ZodObject<{
222
+ type: z.ZodLiteral<"custom">;
223
+ extractorId: z.ZodString;
224
+ }, z.core.$strip>], "type">>;
129
225
  type: z.ZodLiteral<"text">;
130
226
  evaluationParameters: z.ZodOptional<z.ZodObject<{
131
227
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -135,6 +231,12 @@ export declare const expectedOutcomeArraySchema: z.ZodArray<z.ZodDiscriminatedUn
135
231
  }, z.core.$strip>, z.ZodObject<{
136
232
  label: z.ZodString;
137
233
  placeholder: z.ZodOptional<z.ZodString>;
234
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
235
+ type: z.ZodLiteral<"text">;
236
+ }, z.core.$strip>, z.ZodObject<{
237
+ type: z.ZodLiteral<"custom">;
238
+ extractorId: z.ZodString;
239
+ }, z.core.$strip>], "type">>;
138
240
  type: z.ZodLiteral<"textarea">;
139
241
  rows: z.ZodOptional<z.ZodNumber>;
140
242
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -150,6 +252,12 @@ export declare const expectedOutcomeArraySchema: z.ZodArray<z.ZodDiscriminatedUn
150
252
  }, z.core.$strip>, z.ZodObject<{
151
253
  label: z.ZodString;
152
254
  placeholder: z.ZodOptional<z.ZodString>;
255
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
256
+ type: z.ZodLiteral<"text">;
257
+ }, z.core.$strip>, z.ZodObject<{
258
+ type: z.ZodLiteral<"custom">;
259
+ extractorId: z.ZodString;
260
+ }, z.core.$strip>], "type">>;
153
261
  type: z.ZodLiteral<"chips-input">;
154
262
  evaluationParameters: z.ZodOptional<z.ZodObject<{
155
263
  approach: z.ZodEnum<typeof EvaluationApproach>;
@@ -159,6 +267,12 @@ export declare const expectedOutcomeArraySchema: z.ZodArray<z.ZodDiscriminatedUn
159
267
  }, z.core.$strip>, z.ZodObject<{
160
268
  label: z.ZodString;
161
269
  placeholder: z.ZodOptional<z.ZodString>;
270
+ evaluationSource: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
271
+ type: z.ZodLiteral<"text">;
272
+ }, z.core.$strip>, z.ZodObject<{
273
+ type: z.ZodLiteral<"custom">;
274
+ extractorId: z.ZodString;
275
+ }, z.core.$strip>], "type">>;
162
276
  type: z.ZodLiteral<"select">;
163
277
  options: z.ZodArray<z.ZodString>;
164
278
  evaluationParameters: z.ZodOptional<z.ZodObject<{
@@ -198,4 +312,6 @@ export type SelectExpectedOutcomeField = Extract<ExpectedOutcomeField, {
198
312
  }>;
199
313
  export declare function validateExpectedOutcomeSchema(schema: unknown): asserts schema is ExpectedOutcomeSchema;
200
314
  export declare function validateExpectedOutcomeArray(expectedOutcome: unknown): asserts expectedOutcome is ExpectedOutcomeField[];
315
+ export declare function validateExpectedOutcomeArrayWithExtractors(expectedOutcome: unknown, allowedExtractorIds: string[]): asserts expectedOutcome is ExpectedOutcomeField[];
316
+ export declare function getExtractorIds(extractors?: EvaluationSourceExtractors): string[];
201
317
  export {};
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ export declare const modelResponseMetadataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
3
+ export declare const modelResponsePayloadSchema: z.ZodObject<{
4
+ text: z.ZodOptional<z.ZodString>;
5
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
6
+ }, z.core.$strip>;
7
+ export type ModelResponsePayload = z.infer<typeof modelResponsePayloadSchema>;