llm-testrunner-components 1.2.2 → 1.2.4

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 (98) hide show
  1. package/dist/cjs/{app-chips_4.cjs.entry.js → app-chips_5.cjs.entry.js} +41 -6
  2. package/dist/cjs/app-chips_5.cjs.entry.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +235 -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/collection-manifest.json +1 -0
  8. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js +22 -12
  9. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js.map +1 -1
  10. package/dist/collection/components/llm-test-runner/llm-test-runner.js +91 -30
  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 +101 -0
  13. package/dist/collection/components/llm-test-runner/test-cases/chat-history.js +105 -0
  14. package/dist/collection/components/llm-test-runner/test-cases/chat-history.js.map +1 -0
  15. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js +37 -4
  16. package/dist/collection/components/llm-test-runner/test-cases/expected-outcome-renderer.js.map +1 -1
  17. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +12 -2
  18. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -1
  19. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +2 -2
  20. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -1
  21. package/dist/collection/index.js.map +1 -1
  22. package/dist/collection/lib/evaluation/evaluation-service.js +14 -7
  23. package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -1
  24. package/dist/collection/lib/form/components/app-chips.js +1 -1
  25. package/dist/collection/lib/form/components/app-select.js +1 -1
  26. package/dist/collection/lib/form/components/app-textarea.css +17 -0
  27. package/dist/collection/lib/form/components/app-textarea.js +4 -1
  28. package/dist/collection/lib/form/components/app-textarea.js.map +1 -1
  29. package/dist/collection/lib/import-export/test-suite-exporter.js +4 -0
  30. package/dist/collection/lib/import-export/test-suite-exporter.js.map +1 -1
  31. package/dist/collection/lib/test-cases/dynamic-expected-outcome-resolver.js +44 -0
  32. package/dist/collection/lib/test-cases/dynamic-expected-outcome-resolver.js.map +1 -0
  33. package/dist/collection/lib/test-cases/test-case-factory.js +2 -0
  34. package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -1
  35. package/dist/collection/lib/test-cases/test-case-mutations.js +35 -0
  36. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -1
  37. package/dist/collection/schemas/expected-outcome.js +15 -1
  38. package/dist/collection/schemas/expected-outcome.js.map +1 -1
  39. package/dist/collection/schemas/test-case.js +6 -0
  40. package/dist/collection/schemas/test-case.js.map +1 -1
  41. package/dist/collection/types/expected-outcome.js.map +1 -1
  42. package/dist/collection/types/llm-test-runner.js.map +1 -1
  43. package/dist/collection/types/test-case.js.map +1 -1
  44. package/dist/components/app-chips.js +1 -1
  45. package/dist/components/app-select.js +1 -1
  46. package/dist/components/app-textarea.js +1 -1
  47. package/dist/components/chat-history.d.ts +11 -0
  48. package/dist/components/chat-history.js +2 -0
  49. package/dist/components/chat-history.js.map +1 -0
  50. package/dist/components/index.js +1 -1
  51. package/dist/components/llm-test-runner.js +1 -1
  52. package/dist/components/{p-CJBscebi.js → p-B87Lt3z4.js} +3 -3
  53. package/dist/components/p-B87Lt3z4.js.map +1 -0
  54. package/dist/components/p-Bx2jqguC.js +2 -0
  55. package/dist/components/p-Bx2jqguC.js.map +1 -0
  56. package/dist/components/p-D2qDAxFN.js +2 -0
  57. package/dist/components/p-D2qDAxFN.js.map +1 -0
  58. package/dist/components/{p-Dv7cB5FU.js → p-D4dHUFN9.js} +2 -2
  59. package/dist/components/{p-CE5-1jfZ.js → p-eN2dLrsr.js} +2 -2
  60. package/dist/esm/{app-chips_4.entry.js → app-chips_5.entry.js} +41 -7
  61. package/dist/esm/app-chips_5.entry.js.map +1 -0
  62. package/dist/esm/index.js +235 -44
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/llm-testrunner.js +1 -1
  65. package/dist/esm/loader.js +1 -1
  66. package/dist/llm-testrunner/index.esm.js +2 -2
  67. package/dist/llm-testrunner/index.esm.js.map +1 -1
  68. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  69. package/dist/llm-testrunner/p-21202f12.entry.js +2 -0
  70. package/dist/llm-testrunner/p-21202f12.entry.js.map +1 -0
  71. package/dist/react/components.d.ts +6 -1
  72. package/dist/react/components.d.ts.map +1 -1
  73. package/dist/react/components.js +9 -0
  74. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +6 -0
  75. package/dist/types/components/llm-test-runner/test-cases/chat-history.d.ts +14 -0
  76. package/dist/types/components/llm-test-runner/test-cases/expected-outcome-renderer.d.ts +1 -0
  77. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +6 -0
  78. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +3 -0
  79. package/dist/types/components.d.ts +55 -0
  80. package/dist/types/index.d.ts +1 -1
  81. package/dist/types/lib/import-export/test-suite-exporter.d.ts +4 -0
  82. package/dist/types/lib/test-cases/dynamic-expected-outcome-resolver.d.ts +7 -0
  83. package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -1
  84. package/dist/types/schemas/expected-outcome.d.ts +16 -1
  85. package/dist/types/schemas/test-case.d.ts +34 -2
  86. package/dist/types/types/expected-outcome.d.ts +1 -1
  87. package/dist/types/types/llm-test-runner.d.ts +3 -2
  88. package/dist/types/types/test-case.d.ts +1 -1
  89. package/package.json +1 -1
  90. package/dist/cjs/app-chips_4.cjs.entry.js.map +0 -1
  91. package/dist/components/p-BZrzx5jG.js +0 -2
  92. package/dist/components/p-BZrzx5jG.js.map +0 -1
  93. package/dist/components/p-CJBscebi.js.map +0 -1
  94. package/dist/esm/app-chips_4.entry.js.map +0 -1
  95. package/dist/llm-testrunner/p-2cc09217.entry.js +0 -2
  96. package/dist/llm-testrunner/p-2cc09217.entry.js.map +0 -1
  97. /package/dist/components/{p-Dv7cB5FU.js.map → p-D4dHUFN9.js.map} +0 -0
  98. /package/dist/components/{p-CE5-1jfZ.js.map → p-eN2dLrsr.js.map} +0 -0
@@ -40,7 +40,7 @@ const AppChips = class {
40
40
  name: c.name,
41
41
  autocomplete: c.autocomplete,
42
42
  };
43
- return (index$1.h("div", { key: '4f081007c0b11fb20bd74644eb063a6dd5a45d98', class: "app-chips" }, c.label && (index$1.h("label", { key: '3934ec12898c947b163a15494330a9326fe3d6f1', class: "app-chips__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: 'cba194df0033a5951f568875e4686a76c9c28438', class: "app-chips__container" }, this.value.map((chip) => (index$1.h("span", { class: "app-chips__chip", key: chip }, c.type === 'url' ? (index$1.h("a", { href: chip, target: "_blank", rel: "noopener noreferrer", class: "app-chips__link" }, chip)) : (chip), index$1.h("button", { class: "app-chips__remove", type: "button", onClick: () => this.emitRemoveChip(chip) }, "\u00D7")))), index$1.h("input", { key: '11c9e7f326c37cb3a413e9124926ca88eefb1911', class: "app-chips__input", type: c.type || 'text', ...allowedAttrs, onKeyDown: (e) => {
43
+ return (index$1.h("div", { key: 'fb7d4d5444e9c9ac33c56aec88e3e10ed103c8be', class: "app-chips" }, c.label && (index$1.h("label", { key: '2d0041b3a137fecddef2273eac3792b5e8de27ab', class: "app-chips__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: 'f73b1105e567b233626073e05b9da712689e7b12', class: "app-chips__container" }, this.value.map((chip) => (index$1.h("span", { class: "app-chips__chip", key: chip }, c.type === 'url' ? (index$1.h("a", { href: chip, target: "_blank", rel: "noopener noreferrer", class: "app-chips__link" }, chip)) : (chip), index$1.h("button", { class: "app-chips__remove", type: "button", onClick: () => this.emitRemoveChip(chip) }, "\u00D7")))), index$1.h("input", { key: '7676ff95531b34d10cbf9402e72a723e7e123e0e', class: "app-chips__input", type: c.type || 'text', ...allowedAttrs, onKeyDown: (e) => {
44
44
  if (e.key === 'Enter') {
45
45
  const input = e.target;
46
46
  const val = input.value.trim();
@@ -79,7 +79,7 @@ const AppSelect = class {
79
79
  placeholder: c.placeholder,
80
80
  autocomplete: c.autocomplete,
81
81
  };
82
- return (index$1.h("div", { key: '4d9d250026b51799b073ae828c7d15563ec27c22', class: "app-select" }, c.label && (index$1.h("label", { key: '53e101427e78063bd6ed671a31919bbe373c5563', class: "app-select__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: '28b18245c261a3766948ed7d151c356b8faeb4cf' }, index$1.h("select", { key: 'eed9bcb9fc3e00ae62c681a37e967815d350d5fb', ...allowedAttrs, class: "app-select__select", onInput: (e) => {
82
+ return (index$1.h("div", { key: '968f0fffe1eff976ac7e00f02db6fb84aa529de4', class: "app-select" }, c.label && (index$1.h("label", { key: 'dac02d2335754ff5d6ce6ba1df5777f8b019cfae', class: "app-select__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: 'c92698199479bbdde1cfd559d69fc97e54d2862a' }, index$1.h("select", { key: '6e9b27b034d057921f52fbd12653c5ef1b6af1bc', ...allowedAttrs, class: "app-select__select", onInput: (e) => {
83
83
  const raw = e.target.value;
84
84
  const matched = c.optionList.find(opt => String(opt) === raw);
85
85
  this.valueChange.emit({
@@ -90,7 +90,7 @@ const AppSelect = class {
90
90
  };
91
91
  AppSelect.style = appSelectCss();
92
92
 
93
- const appTextareaCss = () => `.textarea-wrapper{margin-bottom:var(--spacing-4)}.textarea-label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.textarea-element{width:95%;box-sizing:border-box;padding:var(--spacing-3);border:2px solid var(--input);border-radius:var(--radius);font-size:var(--font-size-sm);resize:vertical;outline:none;transition:border-color 0.2s ease;font-family:inherit;background:var(--background);color:var(--foreground)}.textarea-element:focus{border-color:var(--ring);box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.help-text{margin-top:var(--spacing-1);font-size:var(--font-size-xs);color:var(--muted-foreground, #6b7280);line-height:1.4}`;
93
+ const appTextareaCss = () => `.textarea-wrapper{margin-bottom:var(--spacing-4)}.textarea-label{display:block;margin-bottom:var(--spacing-2);font-weight:var(--font-weight-medium);color:var(--foreground);font-size:var(--font-size-sm)}.textarea-element{width:95%;box-sizing:border-box;padding:var(--spacing-3);border:2px solid var(--input);border-radius:var(--radius);font-size:var(--font-size-sm);resize:vertical;outline:none;transition:border-color 0.2s ease;font-family:inherit;background:var(--background);color:var(--foreground)}.textarea-element:focus{border-color:var(--ring);box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.textarea-wrapper--read-only .textarea-label{color:var(--muted-foreground)}.textarea-element:read-only{background:var(--muted);color:var(--muted-foreground);border-color:var(--border);cursor:not-allowed;resize:none}.textarea-element:read-only:focus{border-color:var(--border);box-shadow:none}.help-text{margin-top:var(--spacing-1);font-size:var(--font-size-xs);color:var(--muted-foreground, #6b7280);line-height:1.4}`;
94
94
 
95
95
  const AppTextarea = class {
96
96
  constructor(hostRef) {
@@ -118,15 +118,50 @@ const AppTextarea = class {
118
118
  name: c.name,
119
119
  autocomplete: c.autocomplete,
120
120
  };
121
- return (index$1.h("div", { key: 'cc0073d70c9de40a8a12a1cabc3c1c06ac8c1863', class: "textarea-wrapper" }, c.label && (index$1.h("label", { key: 'a886cf95f25afee2e09ebcbc4dc62d2c9bca5ee0', class: "textarea-label", htmlFor: c.name }, c.label)), index$1.h("textarea", { key: 'c16b072269c450f3e09764b23cdfdaf966975977', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && index$1.h("p", { key: '9df941aa068b937d90f9a5f2a841a8fe426b3371', class: "help-text" }, c.helpText)));
121
+ return (index$1.h("div", { key: 'f0749b1f2badf8434272da9fb37b354b42ba988b', class: {
122
+ 'textarea-wrapper': true,
123
+ 'textarea-wrapper--read-only': !!c.readOnly,
124
+ } }, c.label && (index$1.h("label", { key: '3448c838bcf9e962df005eae8fc313d216497c35', class: "textarea-label", htmlFor: c.name }, c.label)), index$1.h("textarea", { key: 'b4ee67a24fa71b0fa042625f943b0e226a6d14b7', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && index$1.h("p", { key: 'fb6263c32e6cc5e36dbc77344c31487d63d51a1c', class: "help-text" }, c.helpText)));
122
125
  }
123
126
  };
124
127
  AppTextarea.style = appTextareaCss();
125
128
 
129
+ const chatHistoryCss = () => `.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;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{position:absolute;top:2px;left:2px;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) - 4px))}.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}`;
130
+
131
+ const CHAT_HISTORY_PLACEHOLDER = `[
132
+ {"role": "user", "content": "How do I import a saved suite?"},
133
+ {"role": "model", "content": "Use Import and pick the JSON from Export suite."}
134
+ ]`;
135
+ const ChatHistory = class {
136
+ constructor(hostRef) {
137
+ index$1.registerInstance(this, hostRef);
138
+ this.chatHistoryChange = index$1.createEvent(this, "chatHistoryChange");
139
+ }
140
+ chatHistoryEnabled = false;
141
+ chatHistoryValue = '';
142
+ chatHistoryChange;
143
+ emit(detail) {
144
+ this.chatHistoryChange.emit(detail);
145
+ }
146
+ onToggle = (e) => {
147
+ const checked = e.target.checked;
148
+ this.emit({ enabled: checked, value: this.chatHistoryValue });
149
+ };
150
+ onTextInput = (e) => {
151
+ const value = e.target.value;
152
+ this.emit({ enabled: this.chatHistoryEnabled, value });
153
+ };
154
+ render() {
155
+ return (index$1.h("div", { key: 'f444a4b5bd9b48df151cad67b54bef54116d11b3', class: "chat-history" }, index$1.h("div", { key: '7216d764fb905ac5e8e33209d9586525e59fa218', class: "chat-history__toggle-row" }, index$1.h("label", { key: 'd5d092c323b48543c96525d449bcb03dcaf5113b', class: "chat-history__switch" }, index$1.h("input", { key: '5104ce460686434156454fc1dfd3685ba0e0968c', type: "checkbox", class: "chat-history__switch-input", checked: this.chatHistoryEnabled, onInput: this.onToggle }), index$1.h("span", { key: 'fa2e51bce0d6ff2d3bf133383e1ce940521097b0', class: "chat-history__switch-ui", "aria-hidden": "true" }, index$1.h("span", { key: 'e0d897ca166623ece09834b44b5baa12605cb9f3', class: "chat-history__switch-thumb" })), index$1.h("span", { key: 'e769678fde5d670e634d7a8e905ef09ce936f440', class: "chat-history__switch-text" }, "Chat history"))), this.chatHistoryEnabled ? (index$1.h("textarea", { class: "chat-history__textarea", value: this.chatHistoryValue, rows: 8, placeholder: CHAT_HISTORY_PLACEHOLDER, "aria-label": "Chat history", onInput: this.onTextInput })) : null));
156
+ }
157
+ };
158
+ ChatHistory.style = chatHistoryCss();
159
+
126
160
  exports.llm_test_runner = index.LLMTestRunner;
127
161
  exports.app_chips = AppChips;
128
162
  exports.app_select = AppSelect;
129
163
  exports.app_textarea = AppTextarea;
130
- //# sourceMappingURL=app-chips.app-select.app-textarea.llm-test-runner.entry.cjs.js.map
164
+ exports.chat_history = ChatHistory;
165
+ //# sourceMappingURL=app-chips.app-select.app-textarea.chat-history.llm-test-runner.entry.cjs.js.map
131
166
 
132
- //# sourceMappingURL=app-chips_4.cjs.entry.js.map
167
+ //# sourceMappingURL=app-chips_5.cjs.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"app-chips_5.cjs.entry.js","mappings":"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","names":["h"],"sources":["src/lib/form/components/app-chips.css?tag=app-chips&encapsulation=shadow","src/lib/form/components/app-chips.tsx","src/lib/form/components/app-select.css?tag=app-select&encapsulation=shadow","src/lib/form/components/app-select.tsx","src/lib/form/components/app-textarea.css?tag=app-textarea&encapsulation=shadow","src/lib/form/components/app-textarea.tsx","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"],"version":3}
@@ -106,6 +106,10 @@ function formatTestSuiteAsJson(testCases) {
106
106
  id: testCase.id,
107
107
  question: testCase.question,
108
108
  expectedOutcome: testCase.expectedOutcome,
109
+ chatHistory: {
110
+ enabled: testCase.chatHistory.enabled,
111
+ value: testCase.chatHistory.value,
112
+ },
109
113
  }));
110
114
  return JSON.stringify(exportData, null, 2);
111
115
  }
@@ -303,6 +307,7 @@ function createTestCase(expectedOutcomeSchema = DEFAULT_EXPECTED_OUTCOME_SCHEMA)
303
307
  id: v4(),
304
308
  question: '',
305
309
  expectedOutcome: createExpectedOutcomeFromSchema(expectedOutcomeSchema),
310
+ chatHistory: { enabled: false, value: '' },
306
311
  isRunning: false,
307
312
  };
308
313
  }
@@ -362,6 +367,7 @@ function createExpectedOutcomeFromSchema(expectedOutcomeSchema) {
362
367
  function createTestCaseFromInput(data) {
363
368
  return {
364
369
  ...data,
370
+ chatHistory: data.chatHistory ?? { enabled: false, value: '' },
365
371
  expectedOutcome: data.expectedOutcome.map(normalizeExpectedOutcomeField),
366
372
  };
367
373
  }
@@ -4955,6 +4961,7 @@ const optionalPositiveInt = number().int().positive().optional();
4955
4961
  const optionalString = string().optional();
4956
4962
  const selectOptionsSchema = array(nonEmptyString).min(1);
4957
4963
  const optionalNumber = number().optional();
4964
+ const expectedOutcomeModeSchema = _enum(['static', 'dynamic']);
4958
4965
  const evaluationParametersSchema = object({
4959
4966
  approach: _enum(EvaluationApproach),
4960
4967
  threshold: optionalNumber,
@@ -5016,8 +5023,21 @@ const expectedOutcomeFieldSchema = discriminatedUnion('type', [
5016
5023
  defaultFieldDefinitions.text.extend({
5017
5024
  value: string(),
5018
5025
  }),
5019
- defaultFieldDefinitions.textarea.extend({
5026
+ defaultFieldDefinitions.textarea
5027
+ .extend({
5020
5028
  value: string(),
5029
+ outcomeMode: expectedOutcomeModeSchema.default('static'),
5030
+ resolutionQuery: string().optional(),
5031
+ })
5032
+ .superRefine((field, ctx) => {
5033
+ if (field.outcomeMode === 'dynamic' &&
5034
+ (!field.resolutionQuery || field.resolutionQuery.trim().length === 0)) {
5035
+ ctx.addIssue({
5036
+ code: 'custom',
5037
+ path: ['resolutionQuery'],
5038
+ message: 'resolutionQuery is required when outcomeMode is dynamic.',
5039
+ });
5040
+ }
5021
5041
  }),
5022
5042
  defaultFieldDefinitions.chipsInput.extend({
5023
5043
  value: array(string()).superRefine((values, ctx) => {
@@ -5051,16 +5071,22 @@ function validateExpectedOutcomeSchema(schema) {
5051
5071
  }
5052
5072
  }
5053
5073
 
5074
+ const testCaseChatHistorySchema = object({
5075
+ enabled: boolean(),
5076
+ value: string(),
5077
+ });
5054
5078
  const testCaseInputSchema = object({
5055
5079
  id: string(),
5056
5080
  question: string(),
5057
5081
  expectedOutcome: expectedOutcomeArraySchema,
5082
+ chatHistory: testCaseChatHistorySchema.optional(),
5058
5083
  });
5059
5084
  const testCaseInputArraySchema = array(testCaseInputSchema);
5060
5085
  object({
5061
5086
  id: string(),
5062
5087
  question: string(),
5063
5088
  expectedOutcome: expectedOutcomeArraySchema,
5089
+ chatHistory: testCaseChatHistorySchema,
5064
5090
  output: string().optional(),
5065
5091
  isRunning: boolean().optional(),
5066
5092
  error: string().optional(),
@@ -5111,6 +5137,50 @@ function importTestSuite(jsonContent) {
5111
5137
  }
5112
5138
  }
5113
5139
 
5140
+ const MISSING_RESOLVER_MESSAGE = 'resolveExpectedOutcome is required when a test case has dynamic expected outcomes.';
5141
+ function isDynamicTextareaField(field) {
5142
+ return field.type === 'textarea' && field.outcomeMode === 'dynamic';
5143
+ }
5144
+ function applyResolvedDynamicValues(testCase, resolvedValues) {
5145
+ if (resolvedValues.length === 0) {
5146
+ return testCase;
5147
+ }
5148
+ const expectedOutcome = [...(testCase.expectedOutcome || [])];
5149
+ for (const resolved of resolvedValues) {
5150
+ const field = expectedOutcome[resolved.index];
5151
+ if (!field || !isDynamicTextareaField(field)) {
5152
+ continue;
5153
+ }
5154
+ expectedOutcome[resolved.index] = {
5155
+ ...field,
5156
+ value: resolved.value,
5157
+ };
5158
+ }
5159
+ return {
5160
+ ...testCase,
5161
+ expectedOutcome,
5162
+ };
5163
+ }
5164
+ async function resolveDynamicExpectedOutcomes(testCase, resolver) {
5165
+ const dynamicFields = (testCase.expectedOutcome || []).flatMap((field, index) => {
5166
+ if (!isDynamicTextareaField(field)) {
5167
+ return [];
5168
+ }
5169
+ return [{ field, index }];
5170
+ });
5171
+ if (dynamicFields.length === 0) {
5172
+ return testCase;
5173
+ }
5174
+ if (!resolver) {
5175
+ throw new Error(MISSING_RESOLVER_MESSAGE);
5176
+ }
5177
+ const resolvedValues = await Promise.all(dynamicFields.map(async ({ field, index }) => ({
5178
+ index,
5179
+ value: await resolver(field.resolutionQuery || '', { testCase, fieldIndex: index }),
5180
+ })));
5181
+ return applyResolvedDynamicValues(testCase, resolvedValues);
5182
+ }
5183
+
5114
5184
  function applyExpectedOutcomeChange(testCase, change) {
5115
5185
  const { index } = change;
5116
5186
  const expectedOutcome = [...(testCase.expectedOutcome || [])];
@@ -5123,6 +5193,9 @@ function applyExpectedOutcomeChange(testCase, change) {
5123
5193
  if (target.type === 'chips-input') {
5124
5194
  return testCase;
5125
5195
  }
5196
+ if (target.type === 'textarea' && target.outcomeMode === 'dynamic') {
5197
+ return testCase;
5198
+ }
5126
5199
  expectedOutcome[index] = {
5127
5200
  ...target,
5128
5201
  value: change.value,
@@ -5151,6 +5224,38 @@ function applyExpectedOutcomeChange(testCase, change) {
5151
5224
  }
5152
5225
  case 'set-evaluation-approach':
5153
5226
  return updateExpectedOutcomeFieldApproach(testCase, index, change.value);
5227
+ case 'set-outcome-mode': {
5228
+ if (target.type !== 'textarea') {
5229
+ return testCase;
5230
+ }
5231
+ const mode = change.value;
5232
+ if (mode === 'static') {
5233
+ const { resolutionQuery: _, ...rest } = target;
5234
+ expectedOutcome[index] = {
5235
+ ...rest,
5236
+ outcomeMode: 'static',
5237
+ value: '',
5238
+ };
5239
+ }
5240
+ else {
5241
+ expectedOutcome[index] = {
5242
+ ...target,
5243
+ outcomeMode: 'dynamic',
5244
+ value: '',
5245
+ };
5246
+ }
5247
+ return { ...testCase, expectedOutcome };
5248
+ }
5249
+ case 'set-resolution-query': {
5250
+ if (target.type !== 'textarea' || target.outcomeMode !== 'dynamic') {
5251
+ return testCase;
5252
+ }
5253
+ expectedOutcome[index] = {
5254
+ ...target,
5255
+ resolutionQuery: change.value,
5256
+ };
5257
+ return { ...testCase, expectedOutcome };
5258
+ }
5154
5259
  }
5155
5260
  }
5156
5261
  /**
@@ -30018,13 +30123,20 @@ class EvaluationService {
30018
30123
  console.warn('⚠️ No output to evaluate for test case:', testCase.id);
30019
30124
  return;
30020
30125
  }
30021
- const fields = (testCase.expectedOutcome || []).map((field, index) => ({
30022
- index,
30023
- label: field.label,
30024
- type: field.type,
30025
- expectedValue: getFieldExpectedValue(field),
30026
- evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30027
- }));
30126
+ const fields = (testCase.expectedOutcome || []).flatMap((field, index) => {
30127
+ if (field.type === 'textarea' && field.outcomeMode === 'dynamic') {
30128
+ return [];
30129
+ }
30130
+ return [
30131
+ {
30132
+ index,
30133
+ label: field.label,
30134
+ type: field.type,
30135
+ expectedValue: getFieldExpectedValue(field),
30136
+ evaluationParameters: normalizeEvaluationParametersForField(field.type, field.evaluationParameters),
30137
+ },
30138
+ ];
30139
+ });
30028
30140
  const evaluationRequest = {
30029
30141
  testCaseId: testCase.id,
30030
30142
  question: testCase.question,
@@ -30119,7 +30231,7 @@ var FormFieldType;
30119
30231
  FormFieldType["SELECT"] = "select";
30120
30232
  })(FormFieldType || (FormFieldType = {}));
30121
30233
 
30122
- const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange, }) => {
30234
+ const ExpectedOutcomeRenderer = ({ testCaseId, fields, dynamicResolutionSupported = false, onExpectedOutcomeChange, }) => {
30123
30235
  const emit = (detail) => onExpectedOutcomeChange({
30124
30236
  detail,
30125
30237
  });
@@ -30132,6 +30244,23 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30132
30244
  optionList,
30133
30245
  defaultValue: EvaluationApproach.EXACT,
30134
30246
  });
30247
+ const buildOutcomeModeConfig = (index) => ({
30248
+ name: `expectedOutcomeMode-${index}`,
30249
+ fieldType: FormFieldType.SELECT,
30250
+ label: 'Outcome Mode',
30251
+ placeholder: 'Select outcome mode',
30252
+ required: true,
30253
+ optionList: ['static', 'dynamic'],
30254
+ defaultValue: 'static',
30255
+ });
30256
+ const buildResolutionQueryConfig = (index) => ({
30257
+ name: `expectedOutcomeResolutionQuery-${index}`,
30258
+ fieldType: FormFieldType.TEXT_AREA,
30259
+ label: 'Resolution Query',
30260
+ placeholder: 'Query used to resolve expected value',
30261
+ required: false,
30262
+ rows: 2,
30263
+ });
30135
30264
  const renderEvaluationSelector = (field, index$1) => {
30136
30265
  const optionList = getAllowedApproachesForFieldType(field.type);
30137
30266
  return (index.h("app-select", { config: buildEvaluationConfig(index$1, optionList), value: field.evaluationParameters?.approach, onValueChange: (e) => emit({
@@ -30143,12 +30272,17 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30143
30272
  };
30144
30273
  return (index.h("div", { class: "expected-outcome-renderer" }, (fields || []).map((field, index$1) => {
30145
30274
  if (field.type === 'textarea') {
30275
+ const isDynamic = dynamicResolutionSupported && field.outcomeMode === 'dynamic';
30146
30276
  const config = {
30147
30277
  name: `expectedOutcome-${index$1}`,
30148
30278
  fieldType: FormFieldType.TEXT_AREA,
30149
30279
  label: field.label,
30150
- placeholder: field.placeholder,
30151
- required: true,
30280
+ placeholder: isDynamic ? 'Resolved on run' : field.placeholder,
30281
+ required: !isDynamic,
30282
+ readOnly: isDynamic,
30283
+ helpText: isDynamic
30284
+ ? 'Filled automatically when the test is run'
30285
+ : undefined,
30152
30286
  rows: field.rows || 2,
30153
30287
  };
30154
30288
  return (index.h("div", { class: "expected-outcome-renderer__group" }, index.h("app-textarea", { config: config, value: field.value, onValueChange: (e) => emit({
@@ -30156,7 +30290,18 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30156
30290
  index: index$1,
30157
30291
  operation: 'set-value',
30158
30292
  value: e.detail.value,
30159
- }) }), renderEvaluationSelector(field, index$1)));
30293
+ }) }), dynamicResolutionSupported && (index.h("app-select", { config: buildOutcomeModeConfig(index$1), value: field.outcomeMode || 'static', onValueChange: (e) => emit({
30294
+ testCaseId,
30295
+ index: index$1,
30296
+ operation: 'set-outcome-mode',
30297
+ value: e.detail.value,
30298
+ }) })), dynamicResolutionSupported &&
30299
+ field.outcomeMode === 'dynamic' && (index.h("app-textarea", { config: buildResolutionQueryConfig(index$1), value: field.resolutionQuery || '', onValueChange: (e) => emit({
30300
+ testCaseId,
30301
+ index: index$1,
30302
+ operation: 'set-resolution-query',
30303
+ value: e.detail.value,
30304
+ }) })), !isDynamic && renderEvaluationSelector(field, index$1)));
30160
30305
  }
30161
30306
  if (field.type === 'chips-input') {
30162
30307
  const config = {
@@ -30203,7 +30348,7 @@ const ExpectedOutcomeRenderer = ({ testCaseId, fields, onExpectedOutcomeChange,
30203
30348
  })));
30204
30349
  };
30205
30350
 
30206
- const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30351
+ const LLMTestCaseRow = ({ testCase, dynamicResolutionSupported = false, onRun, onDelete, handleTestCaseChange, onExpectedOutcomeChange, onChatHistoryChange, }) => {
30207
30352
  const questionConfig = {
30208
30353
  name: 'question',
30209
30354
  fieldType: FormFieldType.TEXT_AREA,
@@ -30219,11 +30364,21 @@ const LLMTestCaseRow = ({ testCase, onRun, onDelete, handleTestCaseChange, onExp
30219
30364
  key: 'question',
30220
30365
  value: e.detail.value,
30221
30366
  },
30222
- }) }), index.h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], onExpectedOutcomeChange: onExpectedOutcomeChange })), index.h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), index.h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), index.h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30367
+ }) }), index.h("chat-history", { chatHistoryEnabled: testCase.chatHistory?.enabled ?? false, chatHistoryValue: testCase.chatHistory?.value ?? '', onChatHistoryChange: (e) => {
30368
+ const { enabled, value } = e
30369
+ .detail;
30370
+ onChatHistoryChange({
30371
+ detail: {
30372
+ testCaseId: testCase.id,
30373
+ enabled,
30374
+ value,
30375
+ },
30376
+ });
30377
+ } }), index.h(ExpectedOutcomeRenderer, { testCaseId: testCase.id, fields: testCase.expectedOutcome || [], dynamicResolutionSupported: dynamicResolutionSupported, onExpectedOutcomeChange: onExpectedOutcomeChange })), index.h(ResponseOutput, { output: testCase.output, isRunning: testCase.isRunning }), index.h(EvaluationSummary, { result: testCase.evaluationResult, isRunning: testCase.isRunning }), index.h(RowActions, { isRunning: testCase.isRunning, canRun: !!testCase.question.trim(), onRun: () => onRun(testCase), onDelete: () => onDelete(testCase.id) })));
30223
30378
  };
30224
30379
 
30225
- const LLMTestCases = ({ testCases, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, }) => {
30226
- return (index.h("div", { class: "test-cases" }, index.h("div", { class: "test-cases__column-headers" }, index.h("div", { class: "test-cases__column-header" }, "Input"), index.h("div", { class: "test-cases__column-header" }, "Output"), index.h("div", { class: "test-cases__column-header" }, "Evaluation"), index.h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (index.h(LLMTestCaseRow, { testCase: testCase, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange }))), index.h("div", { class: "test-cases__add-section" }, index.h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30380
+ const LLMTestCases = ({ testCases, dynamicResolutionSupported = false, onRun, onDelete, onAddTestCase, handleTestCaseChange, onExpectedOutcomeChange, onChatHistoryChange, }) => {
30381
+ return (index.h("div", { class: "test-cases" }, index.h("div", { class: "test-cases__column-headers" }, index.h("div", { class: "test-cases__column-header" }, "Input"), index.h("div", { class: "test-cases__column-header" }, "Output"), index.h("div", { class: "test-cases__column-header" }, "Evaluation"), index.h("div", { class: "test-cases__column-header" }, "Actions")), testCases.map(testCase => (index.h(LLMTestCaseRow, { testCase: testCase, dynamicResolutionSupported: dynamicResolutionSupported, onRun: onRun, onDelete: onDelete, handleTestCaseChange: handleTestCaseChange, onExpectedOutcomeChange: onExpectedOutcomeChange, onChatHistoryChange: onChatHistoryChange }))), index.h("div", { class: "test-cases__add-section" }, index.h(Button, { variant: "outline", size: "md", onClick: onAddTestCase }, "+ Add Question"))));
30227
30382
  };
30228
30383
 
30229
30384
  const tokensCss = () => `:host{--spacing:0.25rem;--spacing-1:calc(var(--spacing) * 1);--spacing-2:calc(var(--spacing) * 2);--spacing-3:calc(var(--spacing) * 3);--spacing-4:calc(var(--spacing) * 4);--spacing-5:calc(var(--spacing) * 5);--spacing-6:calc(var(--spacing) * 6);--spacing-8:calc(var(--spacing) * 8);--spacing-10:calc(var(--spacing) * 10);--spacing-12:calc(var(--spacing) * 12);--spacing-16:calc(var(--spacing) * 16);--spacing-20:calc(var(--spacing) * 20);--spacing-24:calc(var(--spacing) * 24);--radius-none:0;--radius-sm:0.125rem;--radius-md:0.375rem;--radius-lg:0.5rem;--radius-xl:0.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--radius-full:9999px;--radius:var(--radius-lg);--font-size-xs:0.75rem;--font-size-sm:0.875rem;--font-size-base:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--font-size-2xl:1.5rem;--font-size-3xl:1.875rem;--font-size-4xl:2.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-none:1;--line-height-tight:1.25;--line-height-snug:1.375;--line-height-normal:1.5;--line-height-relaxed:1.625;--line-height-loose:2;--letter-spacing-tight:-0.025em;--letter-spacing-normal:0;--letter-spacing-wide:0.05em;--shadow-sm:0 1px 2px 0 rgba(0, 0, 0, 0.05);--shadow-md:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);--shadow-lg:0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);--shadow-xl:0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);--shadow-2xl:0 25px 50px -12px rgba(0, 0, 0, 0.25);--border-width:1px;--z-base:0;--z-dropdown:1000;--z-sticky:1100;--z-modal:1200;--z-popover:1300;--z-tooltip:1400;--opacity-disabled:0.5;--opacity-hover:0.8;--opacity-muted:0.6;--max-w-sm:24rem;--max-w-md:28rem;--max-w-lg:32rem;--max-w-xl:42rem;--max-w-2xl:48rem;--max-w-full:100%;--breakpoint-sm:640px;--breakpoint-md:768px;--breakpoint-lg:1024px;--breakpoint-xl:1280px;--breakpoint-2xl:1536px;--background:#ffffff;--foreground:#0a0a0a;--card:#ffffff;--card-foreground:#0a0a0a;--popover:#ffffff;--popover-foreground:#0a0a0a;--primary:#0a0a0a;--primary-foreground:#fafafa;--secondary:#f4f4f5;--secondary-foreground:#0a0a0a;--muted:#f4f4f5;--muted-foreground:#71717a;--accent:#f4f4f5;--accent-foreground:#0a0a0a;--destructive:#ef4444;--destructive-foreground:#fafafa;--border:#e4e4e7;--input:#e4e4e7;--ring:#3b82f6;--success:#10b981;--success-foreground:#fafafa;--warning:#f59e0b;--warning-foreground:#fafafa;--info:#3b82f6;--info-foreground:#fafafa}:host([data-theme='dark']){--background:#0a0a0a;--foreground:#fafafa;--card:#171717;--card-foreground:#fafafa;--popover:#171717;--popover-foreground:#fafafa;--primary:#fafafa;--primary-foreground:#0a0a0a;--secondary:#27272a;--secondary-foreground:#fafafa;--muted:#27272a;--muted-foreground:#a1a1aa;--accent:#27272a;--accent-foreground:#fafafa;--destructive:#dc2626;--destructive-foreground:#fafafa;--border:#27272a;--input:#27272a;--ring:#3b82f6;--success:#059669;--success-foreground:#fafafa;--warning:#d97706;--warning-foreground:#fafafa;--info:#2563eb;--info-foreground:#fafafa}`;
@@ -30259,6 +30414,7 @@ const LLMTestRunner = class {
30259
30414
  delayMs = 500;
30260
30415
  useSave = false;
30261
30416
  usePromptEditor = false;
30417
+ resolveExpectedOutcome;
30262
30418
  initialTestCases;
30263
30419
  defaultExpectedOutcomeSchema;
30264
30420
  testCases = [
@@ -30272,6 +30428,7 @@ const LLMTestRunner = class {
30272
30428
  value: '',
30273
30429
  },
30274
30430
  ],
30431
+ chatHistory: { enabled: false, value: '' },
30275
30432
  isRunning: false,
30276
30433
  },
30277
30434
  ];
@@ -30329,6 +30486,12 @@ const LLMTestRunner = class {
30329
30486
  const { testCaseId, key, value } = event.detail;
30330
30487
  this.testCases = this.testCases.map(tc => tc.id === testCaseId ? { ...tc, [key]: value } : tc);
30331
30488
  };
30489
+ handleChatHistoryChange = (event) => {
30490
+ const { testCaseId, enabled, value } = event.detail;
30491
+ this.updateTestCase(testCaseId, {
30492
+ chatHistory: { enabled, value },
30493
+ });
30494
+ };
30332
30495
  addNewTestCase() {
30333
30496
  try {
30334
30497
  const schema = this.getResolvedExpectedOutcomeSchema();
@@ -30345,38 +30508,66 @@ const LLMTestRunner = class {
30345
30508
  updateTestCase(id, updates) {
30346
30509
  this.testCases = this.testCases.map(tc => tc.id === id ? { ...tc, ...updates } : tc);
30347
30510
  }
30511
+ requestLlmText(testCase) {
30512
+ return new Promise((resolve, reject) => {
30513
+ const payload = {
30514
+ prompt: testCase.question,
30515
+ resolve,
30516
+ reject,
30517
+ };
30518
+ if (testCase.chatHistory?.enabled) {
30519
+ payload.chatHistory = testCase.chatHistory.value;
30520
+ }
30521
+ this.llmRequest.emit(payload);
30522
+ });
30523
+ }
30524
+ throwError(reason) {
30525
+ throw reason instanceof Error ? reason : new Error(String(reason));
30526
+ }
30527
+ addErrorMessage(reason, fallback) {
30528
+ return reason instanceof Error ? reason.message : fallback;
30529
+ }
30348
30530
  async runSingleTest(testCase) {
30349
30531
  const startTime = Date.now();
30350
30532
  this.updateTestCase(testCase.id, { isRunning: true });
30351
- return new Promise((resolve, reject) => {
30352
- this.llmRequest.emit({
30353
- prompt: testCase.question,
30354
- resolve: async (aiResponse) => {
30355
- const endTime = Date.now();
30356
- const responseTime = endTime - startTime;
30357
- this.updateTestCase(testCase.id, {
30358
- isRunning: false,
30359
- output: aiResponse,
30360
- error: null,
30361
- responseTime: responseTime,
30362
- });
30363
- await this.evaluateResponse({
30364
- ...testCase,
30365
- output: aiResponse,
30366
- responseTime: responseTime,
30367
- });
30368
- resolve();
30369
- },
30370
- reject: (error) => {
30371
- this.updateTestCase(testCase.id, {
30372
- isRunning: false,
30373
- output: null,
30374
- error: error instanceof Error ? error.message : 'Unknown error',
30375
- });
30376
- reject(error);
30377
- },
30533
+ const [llmSettled, resolutionSettled] = await Promise.allSettled([
30534
+ this.requestLlmText(testCase),
30535
+ resolveDynamicExpectedOutcomes(testCase, this.resolveExpectedOutcome),
30536
+ ]);
30537
+ const responseTime = Date.now() - startTime;
30538
+ if (llmSettled.status === 'rejected') {
30539
+ this.updateTestCase(testCase.id, {
30540
+ isRunning: false,
30541
+ output: null,
30542
+ error: this.addErrorMessage(llmSettled.reason, 'Unknown error'),
30543
+ responseTime,
30378
30544
  });
30545
+ this.throwError(llmSettled.reason);
30546
+ }
30547
+ const aiResponse = llmSettled.value;
30548
+ if (resolutionSettled.status === 'rejected') {
30549
+ this.updateTestCase(testCase.id, {
30550
+ isRunning: false,
30551
+ output: aiResponse,
30552
+ error: this.addErrorMessage(resolutionSettled.reason, 'Failed to resolve dynamic expected outcome.'),
30553
+ responseTime,
30554
+ });
30555
+ this.throwError(resolutionSettled.reason);
30556
+ }
30557
+ const resolvedTestCase = resolutionSettled.value;
30558
+ const forEvaluationTestCase = {
30559
+ ...resolvedTestCase,
30560
+ output: aiResponse,
30561
+ responseTime,
30562
+ };
30563
+ this.updateTestCase(testCase.id, {
30564
+ isRunning: false,
30565
+ output: aiResponse,
30566
+ error: null,
30567
+ responseTime,
30568
+ expectedOutcome: forEvaluationTestCase.expectedOutcome,
30379
30569
  });
30570
+ await this.evaluateResponse(forEvaluationTestCase);
30380
30571
  }
30381
30572
  deleteTestCase(id) {
30382
30573
  this.testCases = this.testCases.filter(tc => tc.id !== id);
@@ -30487,7 +30678,7 @@ const LLMTestRunner = class {
30487
30678
  }
30488
30679
  }
30489
30680
  render() {
30490
- return (index.h("div", { key: '29cf8a93402ebad6f6df43e147fa10406577c9aa', class: "test-runner-container" }, index.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() }), index.h(ErrorMessage, { key: 'ec68912728b06fc4a76c330fb1b7d5acde92c3d1', message: this.error, onClear: () => (this.error = '') }), index.h("div", { key: 'ce308dd4bd5437c94ae6e3e8a28970b799865281', class: "test-runner-container__content" }, index.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 }))));
30681
+ return (index.h("div", { key: 'cc808096f929b2e1c570c53144aab195d177c187', class: "test-runner-container" }, index.h(LLMTestRunnerHeader, { key: 'b91cf3df7df0e95bfd4908a2f91c7310b5b7a09a', isExportingTestSuite: this.isExportingTestSuite, isExportingTestResults: this.isExportingTestResults, isRunningAll: this.isRunningAll, useSave: this.useSave, isSaving: this.isSaving, usePromptEditor: this.usePromptEditor, onImport: file => this.handleImport(file), onExportSuite: () => this.handleExportTestSuite(), onExportResults: () => this.handleExportTestResults(), onRunAll: () => this.runAllTests(), onSave: () => this.handleSave() }), index.h(ErrorMessage, { key: 'c7991497173fa9843e7aa42f5283d0897ddff2e2', message: this.error, onClear: () => (this.error = '') }), index.h("div", { key: '2b57132564442b8047d8672c6adcba62cdc9ae87', class: "test-runner-container__content" }, index.h(LLMTestCases, { key: '146e9d8c76a34980a2a274dd856887c22e1ed0e9', 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, onChatHistoryChange: this.handleChatHistoryChange }))));
30491
30682
  }
30492
30683
  };
30493
30684
  LLMTestRunner.style = tokensCss() + (llmTestRunnerCss() + (llmTestRunnerHeaderCss() + (llmTestCasesCss() + (llmTestCaseRowCss() + (rowActionsCss() + (evaluationSummaryCss() + (responseOutputCss() + (errorMessageCss() + (buttonCss() + iconButtonCss())))))))));