llm-testrunner-components 1.0.6 → 1.0.9

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 (266) hide show
  1. package/README.md +101 -100
  2. package/dist/cjs/app-chips_5.cjs.entry.js +158 -0
  3. package/dist/cjs/app-chips_5.cjs.entry.js.map +1 -0
  4. package/dist/cjs/app-globals-Chb-oJtg.js +34 -0
  5. package/dist/cjs/app-globals-Chb-oJtg.js.map +1 -0
  6. package/dist/cjs/index-By1scwl6.js +25542 -0
  7. package/dist/cjs/index-By1scwl6.js.map +1 -0
  8. package/dist/cjs/index-CgmLNwZO.js +21460 -0
  9. package/dist/cjs/index-CgmLNwZO.js.map +1 -0
  10. package/dist/cjs/index.cjs.js +5 -483
  11. package/dist/cjs/index.cjs.js.map +1 -1
  12. package/dist/cjs/llm-testrunner.cjs.js +6 -4
  13. package/dist/cjs/llm-testrunner.cjs.js.map +1 -1
  14. package/dist/cjs/loader.cjs.js +5 -3
  15. package/dist/collection/collection-manifest.json +8 -3
  16. package/dist/collection/components/error-message/error-message.css +34 -0
  17. package/dist/collection/components/error-message/error-message.js +2 -2
  18. package/dist/collection/components/error-message/error-message.js.map +1 -1
  19. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.css +60 -0
  20. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js +18 -0
  21. package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js.map +1 -0
  22. package/dist/collection/components/llm-test-runner/llm-test-runner.css +17 -657
  23. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js +253 -0
  24. package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js.map +1 -0
  25. package/dist/collection/components/llm-test-runner/llm-test-runner.js +191 -200
  26. package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
  27. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.css +28 -0
  28. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js +6 -0
  29. package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js.map +1 -0
  30. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.css +67 -0
  31. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js +5 -0
  32. package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js.map +1 -0
  33. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.css +42 -0
  34. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +39 -0
  35. package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -0
  36. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.css +39 -0
  37. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +7 -0
  38. package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -0
  39. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.css +51 -0
  40. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js +5 -0
  41. package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js.map +1 -0
  42. package/dist/collection/global/env.js +3 -1
  43. package/dist/collection/global/env.js.map +1 -1
  44. package/dist/collection/index.js.map +1 -1
  45. package/dist/collection/lib/evaluation/constants.js +14 -0
  46. package/dist/collection/lib/evaluation/constants.js.map +1 -0
  47. package/dist/collection/lib/evaluation/evaluation-engine.js +45 -45
  48. package/dist/collection/lib/evaluation/evaluation-engine.js.map +1 -1
  49. package/dist/collection/lib/evaluation/evaluation-service.js +33 -0
  50. package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -0
  51. package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js +116 -0
  52. package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js.map +1 -0
  53. package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js +352 -0
  54. package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js.map +1 -0
  55. package/dist/collection/lib/evaluation/evaluators/exact/exact.js +44 -0
  56. package/dist/collection/lib/evaluation/evaluators/exact/exact.js.map +1 -0
  57. package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js +88 -0
  58. package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js.map +1 -0
  59. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js +82 -0
  60. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js.map +1 -0
  61. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js +326 -0
  62. package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js.map +1 -0
  63. package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js +69 -0
  64. package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js.map +1 -0
  65. package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js +56 -0
  66. package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js.map +1 -0
  67. package/dist/collection/lib/evaluation/evaluators/semantic/index.js +7 -0
  68. package/dist/collection/lib/evaluation/evaluators/semantic/index.js.map +1 -0
  69. package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js +19 -0
  70. package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js.map +1 -0
  71. package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js +16 -0
  72. package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js.map +1 -0
  73. package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js +65 -0
  74. package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js.map +1 -0
  75. package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js +5 -0
  76. package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js.map +1 -0
  77. package/dist/collection/lib/evaluation/index.js.map +1 -1
  78. package/dist/collection/lib/evaluation/rouge1-evaluator.test.js +117 -0
  79. package/dist/collection/lib/evaluation/rouge1-evaluator.test.js.map +1 -0
  80. package/dist/collection/lib/evaluation/types.js.map +1 -1
  81. package/dist/collection/lib/file/file-download.js +18 -0
  82. package/dist/collection/lib/file/file-download.js.map +1 -0
  83. package/dist/collection/lib/file/file-reader.js +14 -0
  84. package/dist/collection/lib/file/file-reader.js.map +1 -0
  85. package/dist/collection/lib/form/components/app-chips.css +97 -0
  86. package/dist/collection/lib/form/components/app-chips.js +155 -0
  87. package/dist/collection/lib/form/components/app-chips.js.map +1 -0
  88. package/dist/collection/lib/form/components/app-select.css +28 -0
  89. package/dist/collection/lib/form/components/app-select.js +101 -0
  90. package/dist/collection/lib/form/components/app-select.js.map +1 -0
  91. package/dist/collection/lib/form/components/app-textarea.css +38 -0
  92. package/dist/collection/lib/form/components/app-textarea.js +126 -0
  93. package/dist/collection/lib/form/components/app-textarea.js.map +1 -0
  94. package/dist/collection/lib/form/form-builder.js +171 -0
  95. package/dist/collection/lib/form/form-builder.js.map +1 -0
  96. package/dist/collection/lib/form/schema/base-input-field-config.js +2 -0
  97. package/dist/collection/lib/form/schema/base-input-field-config.js.map +1 -0
  98. package/dist/collection/lib/form/schema/form-control-config.js +2 -0
  99. package/dist/collection/lib/form/schema/form-control-config.js.map +1 -0
  100. package/dist/collection/lib/form/schema/index.js +8 -0
  101. package/dist/collection/lib/form/schema/index.js.map +1 -0
  102. package/dist/collection/lib/import-export/test-results-csv.js +65 -0
  103. package/dist/collection/lib/import-export/test-results-csv.js.map +1 -0
  104. package/dist/collection/lib/import-export/test-suite-exporter.js +15 -0
  105. package/dist/collection/lib/import-export/test-suite-exporter.js.map +1 -0
  106. package/dist/collection/lib/import-export/test-suite-importer.js +44 -0
  107. package/dist/collection/lib/import-export/test-suite-importer.js.map +1 -0
  108. package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js +6 -6
  109. package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js.map +1 -1
  110. package/dist/collection/lib/test-cases/test-case-factory.js +56 -0
  111. package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -0
  112. package/dist/collection/lib/test-cases/test-case-mutations.js +16 -0
  113. package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -0
  114. package/dist/collection/lib/ui/button/button.css +113 -0
  115. package/dist/collection/lib/ui/button/button.js +21 -0
  116. package/dist/collection/lib/ui/button/button.js.map +1 -0
  117. package/dist/collection/lib/ui/button/index.js +2 -0
  118. package/dist/collection/lib/ui/button/index.js.map +1 -0
  119. package/dist/collection/lib/ui/icon-button/icon-button.css +77 -0
  120. package/dist/collection/lib/ui/icon-button/icon-button.js +19 -0
  121. package/dist/collection/lib/ui/icon-button/icon-button.js.map +1 -0
  122. package/dist/collection/lib/ui/icon-button/index.js +2 -0
  123. package/dist/collection/lib/ui/icon-button/index.js.map +1 -0
  124. package/dist/collection/services/adapters.js +2 -0
  125. package/dist/collection/services/adapters.js.map +1 -0
  126. package/dist/collection/services/models/gemini.js +17 -0
  127. package/dist/collection/services/models/gemini.js.map +1 -0
  128. package/dist/collection/styles/tokens.css +180 -0
  129. package/dist/collection/types/evaluation.js +2 -0
  130. package/dist/collection/types/evaluation.js.map +1 -0
  131. package/dist/collection/types/llm-test-runner.js +2 -0
  132. package/dist/collection/types/llm-test-runner.js.map +1 -0
  133. package/dist/components/app-chips.d.ts +11 -0
  134. package/dist/components/app-chips.js +2 -0
  135. package/dist/components/app-chips.js.map +1 -0
  136. package/dist/components/app-select.d.ts +11 -0
  137. package/dist/components/app-select.js +2 -0
  138. package/dist/components/app-select.js.map +1 -0
  139. package/dist/components/app-textarea.d.ts +11 -0
  140. package/dist/components/app-textarea.js +2 -0
  141. package/dist/components/app-textarea.js.map +1 -0
  142. package/dist/components/form-builder.d.ts +11 -0
  143. package/dist/components/form-builder.js +2 -0
  144. package/dist/components/form-builder.js.map +1 -0
  145. package/dist/components/index.d.ts +2 -0
  146. package/dist/components/index.js +1 -13
  147. package/dist/components/index.js.map +1 -1
  148. package/dist/components/llm-test-runner.js +1 -8
  149. package/dist/components/llm-test-runner.js.map +1 -1
  150. package/dist/components/p--2rdv_J9.js +2 -0
  151. package/dist/components/p--2rdv_J9.js.map +1 -0
  152. package/dist/components/p-B7J48VNq.js +2 -0
  153. package/dist/components/p-B7J48VNq.js.map +1 -0
  154. package/dist/components/p-BCB1rjPS.js +7 -0
  155. package/dist/components/p-BCB1rjPS.js.map +1 -0
  156. package/dist/components/p-BQhb2H_a.js +2 -0
  157. package/dist/components/p-BQhb2H_a.js.map +1 -0
  158. package/dist/components/p-D9BrlHdP.js +297 -0
  159. package/dist/components/p-D9BrlHdP.js.map +1 -0
  160. package/dist/components/p-DtCkZ1g2.js +2 -0
  161. package/dist/components/p-DtCkZ1g2.js.map +1 -0
  162. package/dist/esm/app-chips_5.entry.js +153 -0
  163. package/dist/esm/app-chips_5.entry.js.map +1 -0
  164. package/dist/esm/app-globals-DbR5vV7d.js +32 -0
  165. package/dist/esm/app-globals-DbR5vV7d.js.map +1 -0
  166. package/dist/esm/index-Bvg6mh1M.js +25539 -0
  167. package/dist/esm/index-Bvg6mh1M.js.map +1 -0
  168. package/dist/esm/index-DxzhGhec.js +21450 -0
  169. package/dist/esm/index-DxzhGhec.js.map +1 -0
  170. package/dist/esm/index.js +4 -486
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/llm-testrunner.js +7 -5
  173. package/dist/esm/llm-testrunner.js.map +1 -1
  174. package/dist/esm/loader.js +6 -4
  175. package/dist/llm-testrunner/index.esm.js +1 -1
  176. package/dist/llm-testrunner/index.esm.js.map +1 -1
  177. package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
  178. package/dist/llm-testrunner/llm-testrunner.esm.js.map +1 -1
  179. package/dist/llm-testrunner/p-3f04b0fb.entry.js +2 -0
  180. package/dist/llm-testrunner/p-3f04b0fb.entry.js.map +1 -0
  181. package/dist/llm-testrunner/p-DFds8y01.js +7 -0
  182. package/dist/llm-testrunner/p-DFds8y01.js.map +1 -0
  183. package/dist/llm-testrunner/p-DxzhGhec.js +298 -0
  184. package/dist/llm-testrunner/p-DxzhGhec.js.map +1 -0
  185. package/dist/llm-testrunner/p-GQwFOmwJ.js +2 -0
  186. package/dist/llm-testrunner/p-GQwFOmwJ.js.map +1 -0
  187. package/dist/react/components.d.ts +32 -2
  188. package/dist/react/components.d.ts.map +1 -1
  189. package/dist/react/components.js +44 -2
  190. package/dist/types/components/llm-test-runner/header/llm-test-runner-header.d.ts +14 -0
  191. package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +13 -29
  192. package/dist/types/components/llm-test-runner/llm-test-runner.import-export.test.d.ts +1 -0
  193. package/dist/types/components/llm-test-runner/test-cases/actions/row-actions.d.ts +8 -0
  194. package/dist/types/components/llm-test-runner/test-cases/evaluation/evaluation-summary.d.ts +7 -0
  195. package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +25 -0
  196. package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +26 -0
  197. package/dist/types/components/llm-test-runner/test-cases/output/response-output.d.ts +6 -0
  198. package/dist/types/components.d.ts +199 -4
  199. package/dist/types/global/env.d.ts +2 -0
  200. package/dist/types/index.d.ts +1 -1
  201. package/dist/types/lib/evaluation/constants.d.ts +11 -0
  202. package/dist/types/lib/evaluation/evaluation-engine.d.ts +0 -4
  203. package/dist/types/lib/evaluation/evaluation-service.d.ts +15 -0
  204. package/dist/types/lib/evaluation/evaluators/bleu/bleu-evaluator.d.ts +18 -0
  205. package/dist/types/lib/evaluation/evaluators/bleu/tests/bleu.test.d.ts +1 -0
  206. package/dist/types/lib/evaluation/evaluators/exact/exact.d.ts +2 -0
  207. package/dist/types/lib/evaluation/evaluators/rouge1-evaluator.d.ts +17 -0
  208. package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.d.ts +2 -0
  209. package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.test.d.ts +1 -0
  210. package/dist/types/lib/evaluation/evaluators/semantic/SemanticEvaluator.d.ts +6 -0
  211. package/dist/types/lib/evaluation/evaluators/semantic/evaluate-keywords.d.ts +7 -0
  212. package/dist/types/lib/evaluation/evaluators/semantic/index.d.ts +2 -0
  213. package/dist/types/lib/evaluation/evaluators/semantic/model-loader.d.ts +1 -0
  214. package/dist/types/lib/evaluation/evaluators/semantic/similarity-utils.d.ts +1 -0
  215. package/dist/types/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.d.ts +1 -0
  216. package/dist/types/lib/evaluation/evaluators/semantic/text-utils.d.ts +1 -0
  217. package/dist/types/lib/evaluation/index.d.ts +2 -2
  218. package/dist/types/lib/evaluation/rouge1-evaluator.test.d.ts +1 -0
  219. package/dist/types/lib/evaluation/types.d.ts +19 -7
  220. package/dist/types/lib/file/file-download.d.ts +7 -0
  221. package/dist/types/lib/file/file-reader.d.ts +6 -0
  222. package/dist/types/lib/form/components/app-chips.d.ts +20 -0
  223. package/dist/types/lib/form/components/app-select.d.ts +7 -0
  224. package/dist/types/lib/form/components/app-textarea.d.ts +14 -0
  225. package/dist/types/lib/form/form-builder.d.ts +24 -0
  226. package/dist/types/lib/form/schema/base-input-field-config.d.ts +37 -0
  227. package/dist/types/lib/form/schema/form-control-config.d.ts +13 -0
  228. package/dist/types/lib/form/schema/index.d.ts +9 -0
  229. package/dist/types/lib/import-export/test-results-csv.d.ts +13 -0
  230. package/dist/types/lib/import-export/test-suite-exporter.d.ts +16 -0
  231. package/dist/types/lib/import-export/test-suite-importer.d.ts +12 -0
  232. package/dist/types/lib/rate-limited-fetcher/rate-limited-fetcher.d.ts +1 -1
  233. package/dist/types/lib/test-cases/test-case-factory.d.ts +12 -0
  234. package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -0
  235. package/dist/types/lib/ui/button/button.d.ts +13 -0
  236. package/dist/types/lib/ui/button/index.d.ts +2 -0
  237. package/dist/types/lib/ui/icon-button/icon-button.d.ts +11 -0
  238. package/dist/types/lib/ui/icon-button/index.d.ts +2 -0
  239. package/dist/types/services/adapters.d.ts +3 -0
  240. package/dist/types/services/models/gemini.d.ts +11 -0
  241. package/dist/types/stencil-public-runtime.d.ts +110 -6
  242. package/dist/types/types/evaluation.d.ts +9 -0
  243. package/dist/types/types/llm-test-runner.d.ts +22 -0
  244. package/package.json +30 -6
  245. package/dist/cjs/app-globals-CbbEbofA.js +0 -14
  246. package/dist/cjs/app-globals-CbbEbofA.js.map +0 -1
  247. package/dist/cjs/index-D-FySkoV.js +0 -1470
  248. package/dist/cjs/index-D-FySkoV.js.map +0 -1
  249. package/dist/cjs/llm-test-runner.cjs.entry.js +0 -9
  250. package/dist/cjs/llm-test-runner.entry.cjs.js.map +0 -1
  251. package/dist/components/p-CYUbsbxt.js +0 -1770
  252. package/dist/components/p-CYUbsbxt.js.map +0 -1
  253. package/dist/esm/app-globals-BOQOUavG.js +0 -12
  254. package/dist/esm/app-globals-BOQOUavG.js.map +0 -1
  255. package/dist/esm/index-cncubhtM.js +0 -1463
  256. package/dist/esm/index-cncubhtM.js.map +0 -1
  257. package/dist/esm/llm-test-runner.entry.js +0 -3
  258. package/dist/esm/llm-test-runner.entry.js.map +0 -1
  259. package/dist/llm-testrunner/llm-test-runner.entry.esm.js.map +0 -1
  260. package/dist/llm-testrunner/loader.esm.js.map +0 -1
  261. package/dist/llm-testrunner/p-BOQOUavG.js +0 -2
  262. package/dist/llm-testrunner/p-BOQOUavG.js.map +0 -1
  263. package/dist/llm-testrunner/p-cncubhtM.js +0 -3
  264. package/dist/llm-testrunner/p-cncubhtM.js.map +0 -1
  265. package/dist/llm-testrunner/p-f68fd660.entry.js +0 -2
  266. package/dist/llm-testrunner/p-f68fd660.entry.js.map +0 -1
package/README.md CHANGED
@@ -9,15 +9,22 @@ The LLM TestRunner is a tool for testing Large Language Model (LLM) responses ag
9
9
  - **Question Management**: Add, edit, and organize test questions
10
10
  - **AI Integration**: Can be integrated with any LLM provider
11
11
  - **Automated Evaluation**: Built-in evaluation engine that checks responses against expected keywords and source links
12
- - **Batch Testing**: Run multiple tests sequentially
13
- - **Real-time Results**: Live evaluation results with pass/fail indicators
12
+ - **Batch Testing**: Run multiple tests sequentially
13
+ - **Real-time Results**: Live evaluation results with pass/fail indicators, including details such as:
14
+ - Number of keywords matched.
15
+ - Presence of source links in the response.
16
+
17
+ > **Note:** Source-link checking uses _overlap/partial match_.
18
+ > A full URL match is **not required** — any overlapping portion of the expected link (for example, matching the domain or path segment) in the response counts as present.
14
19
 
15
20
  ## Components
16
21
 
17
22
  ### `<llm-test-runner>`
23
+
18
24
  The main component that provides a complete LLM testing interface.
19
25
 
20
26
  **Features:**
27
+
21
28
  - Question input with expected keywords and source links
22
29
  - Real-time AI response generation any LLM provider
23
30
  - Test case management (add, delete, run individual or all tests)
@@ -26,6 +33,7 @@ The main component that provides a complete LLM testing interface.
26
33
  - Rate limiting for batch operations
27
34
 
28
35
  **Usage:**
36
+
29
37
  ```html
30
38
  <llm-test-runner delay-ms="1000"></llm-test-runner>
31
39
  ```
@@ -33,52 +41,70 @@ The main component that provides a complete LLM testing interface.
33
41
  ## 🎯 Usage Modes
34
42
 
35
43
  ### 1. Direct HTML Usage
44
+
36
45
  Simply include the component in your HTML:
46
+
37
47
  ```html
38
48
  <!DOCTYPE html>
39
49
  <html>
40
- <head>
41
- <script type="module" src="/build/llm-testrunner.esm.js"></script>
42
- <script nomodule src="/build/llm-testrunner.js"></script>
43
- </head>
44
- <body>
45
- <llm-test-runner id="llm-test-runner" delay-ms="1000"></llm-test-runner>
46
- </body>
47
- <script>
50
+ <head>
51
+ <script type="module" src="/build/llm-testrunner.esm.js"></script>
52
+ <script nomodule src="/build/llm-testrunner.js"></script>
53
+ </head>
54
+ <body>
55
+ <llm-test-runner id="llm-test-runner" delay-ms="1000"></llm-test-runner>
56
+ </body>
57
+ <script>
48
58
  const llmTestRunner = document.getElementById('llm-test-runner');
49
59
  // Gemini API
50
60
  async function handlellmRequest(event) {
51
61
  try {
52
62
  const requestBody = {
53
- contents: [{
54
- parts: [{
55
- text: event.detail.prompt
56
- }]
57
- }]
63
+ contents: [
64
+ {
65
+ parts: [
66
+ {
67
+ text: event.detail.prompt,
68
+ },
69
+ ],
70
+ },
71
+ ],
58
72
  };
59
73
 
60
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=your-gemini-api-key-here`, {
61
- method: 'POST',
62
- headers: {
63
- 'Content-Type': 'application/json',
74
+ const response = await fetch(
75
+ `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=your-gemini-api-key-here`,
76
+ {
77
+ method: 'POST',
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ },
81
+ body: JSON.stringify(requestBody),
64
82
  },
65
- body: JSON.stringify(requestBody)
66
- });
83
+ );
67
84
 
68
85
  if (!response.ok) {
69
86
  const errorData = await response.json().catch(() => ({}));
70
- throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);
87
+ throw new Error(
88
+ errorData.error?.message ||
89
+ `HTTP error! status: ${response.status}`,
90
+ );
71
91
  }
72
92
 
73
93
  const data = await response.json();
74
94
 
75
- if (data.candidates && data.candidates[0] && data.candidates[0].content) {
95
+ if (
96
+ data.candidates &&
97
+ data.candidates[0] &&
98
+ data.candidates[0].content
99
+ ) {
76
100
  event.detail.resolve(data.candidates[0].content.parts[0].text);
77
101
  } else {
78
102
  throw new Error('Unexpected response format from Gemini API');
79
103
  }
80
104
  } catch (err) {
81
- event.detail.reject(err);
105
+ event.detail.reject(
106
+ err instanceof Error ? err : new Error(String(err)),
107
+ );
82
108
  }
83
109
  }
84
110
  llmTestRunner.addEventListener('llmRequest', handlellmRequest);
@@ -87,30 +113,34 @@ Simply include the component in your HTML:
87
113
  ```
88
114
 
89
115
  ### 2. Library Integration
116
+
90
117
  Import as a module in your application:
118
+
91
119
  ```javascript
92
120
  import { LLMTestRunner } from 'llm-testrunner-components';
93
121
 
94
122
  // The component is automatically registered and ready to use
95
123
  ```
124
+
96
125
  ## Configuration
97
126
 
98
127
  ### 🧠 delayMs Prop — Controlling API Rate Limiting
128
+
99
129
  The `delayMs` prop allows you to control **how frequently API calls are made** when triggering multiple requests.
100
130
  This helps prevent exceeding **API rate limits** by spacing out requests automatically.
101
131
 
102
132
  ### ⚙️ Description
103
133
 
104
- | Prop Name | Type | Default | Description |
105
- |------------|------|----------|--------------|
134
+ | Prop Name | Type | Default | Description |
135
+ | --------- | -------- | ----------- | -------------------------------------------------------------------------------------------------------------------- |
106
136
  | `delayMs` | `number` | `undefined` | Optional delay (in milliseconds) between consecutive API calls. If not provided, all API calls are made in parallel. |
107
137
 
108
-
109
138
  ```html
110
139
  <llm-test-runner delay-ms="2000"></llm-test-runner>
111
140
  ```
112
141
 
113
142
  ### React/JSX Usage
143
+
114
144
  ```jsx
115
145
  function App() {
116
146
  return (
@@ -131,63 +161,21 @@ The built-in evaluation engine provides:
131
161
  - **Detailed Results**: Shows which keywords and links were found/missing
132
162
 
133
163
  ### Evaluation Criteria
164
+
134
165
  - **Keywords**: Must be present in the AI response (case-insensitive)
135
166
  - **Source Links**: Must be present as exact URL matches
136
167
  - **Pass Condition**: ALL expected keywords AND source links must be found
137
168
 
138
- ## Development
139
-
140
- ### 🛠 Prerequisites
141
-
142
- Before you begin, ensure you have:
143
-
144
- - **Node.js** (>=16.x recommended)
145
- - **npm** (>=7.x) or **yarn**
146
-
147
- ### Building
148
- ```bash
149
- npm run build
150
- ```
151
-
152
- ### Testing
153
- ```bash
154
- npm test
155
- ```
156
-
157
- ### Development Server
158
- ```bash
159
- npm start
160
- ```
161
-
162
- ### Create a new component
163
- ```bash
164
- npm run generate
165
- ```
166
-
167
- ### Project Structure
168
- ```
169
- src/
170
- ├── components/
171
- │ └── llm-test-runner/ # Main component
172
- │ ├── llm-test-runner.tsx # Component logic
173
- │ ├── llm-test-runner.css # Styling
174
- │ └── readme.md # Component documentation
175
- ├── lib/
176
- │ └── evaluation/ # Evaluation engine
177
- │ ├── evaluation-engine.ts # Core evaluation logic
178
- │ ├── types.ts # TypeScript interfaces
179
- │ └── index.ts # Exports
180
- └── index.ts # Main library exports
181
- ```
182
-
183
169
  ## Using in React Applications
184
170
 
185
171
  ### Installation
172
+
186
173
  ```bash
187
174
  npm install llm-testrunner-components
188
175
  ```
189
176
 
190
177
  ### Integration
178
+
191
179
  ```tsx
192
180
  import React, { useEffect } from 'react';
193
181
  import { defineCustomElements } from 'llm-testrunner-components/loader';
@@ -199,38 +187,47 @@ function App() {
199
187
 
200
188
  const handlellmRequest = (event: CustomEvent<LLMRequestPayload>) => {
201
189
  try {
202
- console.log('🚀 callGeminiAPI called with prompt:', event.detail.prompt);
203
- const requestBody = {
204
- contents: [{
205
- parts: [{
206
- text: event.detail.prompt
207
- }]
208
- }]
209
- };
190
+ console.log('🚀 callGeminiAPI called with prompt:', event.detail.prompt);
191
+ const requestBody = {
192
+ contents: [
193
+ {
194
+ parts: [
195
+ {
196
+ text: event.detail.prompt,
197
+ },
198
+ ],
199
+ },
200
+ ],
201
+ };
210
202
 
211
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=your-gemini-api-key-here`, {
203
+ const response = await fetch(
204
+ `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=your-gemini-api-key-here`,
205
+ {
212
206
  method: 'POST',
213
207
  headers: {
214
208
  'Content-Type': 'application/json',
215
209
  },
216
- body: JSON.stringify(requestBody)
217
- });
218
-
219
- if (!response.ok) {
220
- const errorData = await response.json().catch(() => ({}));
221
- throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);
222
- }
210
+ body: JSON.stringify(requestBody),
211
+ },
212
+ );
213
+
214
+ if (!response.ok) {
215
+ const errorData = await response.json().catch(() => ({}));
216
+ throw new Error(
217
+ errorData.error?.message || `HTTP error! status: ${response.status}`,
218
+ );
219
+ }
223
220
 
224
- const data = await response.json();
221
+ const data = await response.json();
225
222
 
226
- if (data.candidates && data.candidates[0] && data.candidates[0].content) {
227
- event.detail.resolve(data.candidates[0].content.parts[0].text);
228
- } else {
229
- throw new Error('Unexpected response format from Gemini API');
230
- }
231
- } catch (err) {
232
- event.detail.reject(err);
223
+ if (data.candidates && data.candidates[0] && data.candidates[0].content) {
224
+ event.detail.resolve(data.candidates[0].content.parts[0].text);
225
+ } else {
226
+ throw new Error('Unexpected response format from Gemini API');
233
227
  }
228
+ } catch (err) {
229
+ event.detail.reject(err instanceof Error ? err : new Error(String(err)));
230
+ }
234
231
  };
235
232
 
236
233
  return (
@@ -243,6 +240,7 @@ function App() {
243
240
  ```
244
241
 
245
242
  ### TypeScript Support
243
+
246
244
  ```tsx
247
245
  declare global {
248
246
  namespace JSX {
@@ -256,6 +254,7 @@ declare global {
256
254
  ## API Reference
257
255
 
258
256
  ### Component Props
257
+
259
258
  ```typescript
260
259
  interface LLMTestRunnerProps {
261
260
  apiKey: string; // Required: Your Gemini API key
@@ -263,12 +262,12 @@ interface LLMTestRunnerProps {
263
262
  ```
264
263
 
265
264
  ### TestCase Interface
265
+
266
266
  ```typescript
267
267
  interface TestCase {
268
268
  id: string;
269
269
  question: string;
270
- expectedKeywords: string[];
271
- expectedSourceLinks: string[];
270
+ expectedOutcome: string;
272
271
  output?: string;
273
272
  isRunning?: boolean;
274
273
  error?: string;
@@ -277,6 +276,7 @@ interface TestCase {
277
276
  ```
278
277
 
279
278
  ### EvaluationResult Interface
279
+
280
280
  ```typescript
281
281
  interface EvaluationResult {
282
282
  testCaseId: string;
@@ -288,10 +288,11 @@ interface EvaluationResult {
288
288
  ```
289
289
 
290
290
  ### LLMRequestPayload Interface
291
+
291
292
  ```typescript
292
293
  interface LLMRequestPayload {
293
294
  prompt: string;
294
295
  resolve: (result: string) => void;
295
- reject: (err: any) => void;
296
+ reject: (err: Error | unknown) => void;
296
297
  }
297
- ```
298
+ ```
@@ -0,0 +1,158 @@
1
+ 'use strict';
2
+
3
+ var index$1 = require('./index-CgmLNwZO.js');
4
+ var index = require('./index-By1scwl6.js');
5
+
6
+ const appChipsCss = () => `.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)}`;
7
+
8
+ const AppChips = class {
9
+ constructor(hostRef) {
10
+ index$1.registerInstance(this, hostRef);
11
+ this.addChip = index$1.createEvent(this, "addChip");
12
+ this.removeChip = index$1.createEvent(this, "removeChip");
13
+ }
14
+ value = [];
15
+ config;
16
+ testCaseId;
17
+ addChip;
18
+ removeChip;
19
+ emitAddChip(val) {
20
+ this.addChip.emit({
21
+ key: this.config.name,
22
+ value: val,
23
+ testCaseId: this.testCaseId,
24
+ });
25
+ }
26
+ emitRemoveChip(index) {
27
+ this.removeChip.emit({
28
+ key: this.config.name,
29
+ index,
30
+ testCaseId: this.testCaseId,
31
+ });
32
+ }
33
+ render() {
34
+ const c = this.config;
35
+ const allowedAttrs = {
36
+ placeholder: c.placeholder,
37
+ required: c.required,
38
+ disabled: c.disabled,
39
+ readOnly: c.readOnly,
40
+ id: c.name,
41
+ name: c.name,
42
+ autocomplete: c.autocomplete,
43
+ };
44
+ return (index$1.h("div", { key: '80363b5a83bffa034454e85213bc554d0685675d', class: "app-chips" }, c.label && (index$1.h("label", { key: '9e6641828170ec9dcb8f2e3cee0e8d04e55c676b', class: "app-chips__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: '4aa19ca62522e7d3c51d37e52162806a62c7ad23', class: "app-chips__container" }, this.value.map((chip, index) => (index$1.h("span", { class: "app-chips__chip", key: index }, 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(index) }, "\u00D7")))), index$1.h("input", { key: '82ec8242da85728f8b3838e58eaf13b583179a26', class: "app-chips__input", type: c.type || 'text', ...allowedAttrs, onKeyDown: (e) => {
45
+ if (e.key === 'Enter') {
46
+ const input = e.target;
47
+ const val = input.value.trim();
48
+ if (!val)
49
+ return;
50
+ this.emitAddChip(val);
51
+ input.value = '';
52
+ }
53
+ } }))));
54
+ }
55
+ };
56
+ AppChips.style = appChipsCss();
57
+
58
+ const appSelectCss = () => `.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)}`;
59
+
60
+ const AppSelect = class {
61
+ constructor(hostRef) {
62
+ index$1.registerInstance(this, hostRef);
63
+ }
64
+ value;
65
+ onChange;
66
+ config;
67
+ render() {
68
+ const c = this.config;
69
+ const allowedAttrs = {
70
+ id: c.name,
71
+ name: c.name,
72
+ disabled: c.disabled,
73
+ required: c.required,
74
+ readOnly: c.readOnly,
75
+ placeholder: c.placeholder,
76
+ autocomplete: c.autocomplete,
77
+ optionList: c.optionList,
78
+ };
79
+ return (index$1.h("div", { key: '9c1db3cb191224e9a947422ee5f435f465cc6dab', class: "app-select" }, c.label && (index$1.h("label", { key: '760325a0a77befdbf6739a527384b396ca77b27e', class: "app-select__label", htmlFor: c.name }, c.label)), index$1.h("div", { key: 'f164d797209ced876a482452aeeeeb2f027dc2da' }, index$1.h("select", { key: '5e82f93ff288e66d3aabd2d4ad4aafd0377cfe35', ...allowedAttrs, class: "app-select__select", onInput: (e) => {
80
+ const raw = e.target.value;
81
+ const matched = c.optionList.find(opt => String(opt) === raw);
82
+ this.onChange?.(matched !== undefined ? matched : raw);
83
+ } }, c.optionList?.map(option => (index$1.h("option", { value: String(option), key: String(option), selected: this.value === option }, String(option))))))));
84
+ }
85
+ };
86
+ AppSelect.style = appSelectCss();
87
+
88
+ 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}`;
89
+
90
+ const AppTextarea = class {
91
+ constructor(hostRef) {
92
+ index$1.registerInstance(this, hostRef);
93
+ this.valueChange = index$1.createEvent(this, "valueChange");
94
+ }
95
+ testCaseId;
96
+ value;
97
+ config;
98
+ valueChange;
99
+ handleChange = (e) => {
100
+ const target = e.target;
101
+ this.valueChange.emit({
102
+ key: this.config.name,
103
+ value: target.value,
104
+ testCaseId: this.testCaseId,
105
+ });
106
+ };
107
+ render() {
108
+ const c = this.config;
109
+ const allowedAttrs = {
110
+ placeholder: c.placeholder,
111
+ required: c.required,
112
+ disabled: c.disabled,
113
+ readOnly: c.readOnly,
114
+ rows: c.rows,
115
+ id: c.name,
116
+ name: c.name,
117
+ autocomplete: c.autocomplete,
118
+ };
119
+ return (index$1.h("div", { key: 'b402c7e109f79c1d331dc1a6c77abaf28ad62ea3', class: "textarea-wrapper" }, c.label && (index$1.h("label", { key: '2e314cd2ad7fc00d5e910422cf073490f72ccf88', class: "textarea-label", htmlFor: c.name }, c.label)), index$1.h("textarea", { key: 'f25add7ec3efa9d58073df5baefd7f71c78ead39', ...allowedAttrs, class: "textarea-element", value: this.value, onInput: this.handleChange }), c.helpText && index$1.h("p", { key: '8773a9bd2d4548e823339f7bcd0275dc58f9c212', class: "help-text" }, c.helpText)));
120
+ }
121
+ };
122
+ AppTextarea.style = appTextareaCss();
123
+
124
+ const FormBuilder = class {
125
+ constructor(hostRef) {
126
+ index$1.registerInstance(this, hostRef);
127
+ }
128
+ fields = [];
129
+ onUpdateApproach;
130
+ testCase;
131
+ handleTestCaseChange;
132
+ addChip;
133
+ removeChip;
134
+ renderField(field) {
135
+ switch (field.fieldType) {
136
+ case index.FormFieldType.TEXT_AREA:
137
+ return (index$1.h("app-textarea", { config: field, value: this.testCase[field.name], testCaseId: this.testCase.id, onValueChange: this.handleTestCaseChange }));
138
+ case index.FormFieldType.CHIPS:
139
+ return (index$1.h("app-chips", { config: field, value: this.testCase[field.name], testCaseId: this.testCase.id, onAddChip: this.addChip, onRemoveChip: this.removeChip }));
140
+ case index.FormFieldType.SELECT:
141
+ return (index$1.h("app-select", { config: field, value: this.testCase.evaluationParameters?.approach, onChange: approach => this.onUpdateApproach(this.testCase, approach) }));
142
+ default:
143
+ return index$1.h("div", null, "Unknown field type");
144
+ }
145
+ }
146
+ render() {
147
+ return (index$1.h("div", { key: '01e89f827a4adfb53e4dd4d5acfc55c323ad3788', class: "form-builder" }, this.fields.map(field => this.renderField(field))));
148
+ }
149
+ };
150
+
151
+ exports.llm_test_runner = index.LLMTestRunner;
152
+ exports.app_chips = AppChips;
153
+ exports.app_select = AppSelect;
154
+ exports.app_textarea = AppTextarea;
155
+ exports.form_builder = FormBuilder;
156
+ //# sourceMappingURL=app-chips.app-select.app-textarea.form-builder.llm-test-runner.entry.cjs.js.map
157
+
158
+ //# 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","FormFieldType"],"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/lib/form/form-builder.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 @Prop() testCaseId: string;\n\n @Event() addChip: EventEmitter<{\n key: string;\n value: string;\n testCaseId: string;\n }>;\n\n @Event() removeChip: EventEmitter<{\n key: string;\n index: number;\n testCaseId: string;\n }>;\n\n private emitAddChip(val: string) {\n this.addChip.emit({\n key: this.config.name,\n value: val,\n testCaseId: this.testCaseId,\n });\n }\n\n private emitRemoveChip(index: number) {\n this.removeChip.emit({\n key: this.config.name,\n index,\n testCaseId: this.testCaseId,\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 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, index) => (\n <span class=\"app-chips__chip\" key={index}>\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(index)}\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\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, 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: any;\n @Prop() onChange: (value: any) => void;\n @Prop() config: SelectConfig;\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 optionList: c.optionList,\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.onChange?.(matched !== undefined ? matched : raw);\n }}\n >\n {c.optionList?.map(option => (\n <option\n value={String(option)}\n key={String(option)}\n selected={this.value === option}\n >\n {String(option)}\n </option>\n ))}\n </select>\n </div>\n </div>\n );\n }\n}\n",".textarea-wrapper {\n margin-bottom: var(--spacing-4);\n}\n\n.textarea-label {\n display: block;\n margin-bottom: var(--spacing-2);\n font-weight: var(--font-weight-medium);\n color: var(--foreground);\n font-size: var(--font-size-sm);\n}\n\n.textarea-element {\n width: 95%;\n box-sizing: border-box;\n padding: var(--spacing-3);\n border: 2px solid var(--input);\n border-radius: var(--radius);\n font-size: var(--font-size-sm);\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n background: var(--background);\n color: var(--foreground);\n}\n\n.textarea-element:focus {\n border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.help-text {\n margin-top: var(--spacing-1);\n font-size: var(--font-size-xs);\n color: var(--muted-foreground, #6b7280);\n line-height: 1.4;\n}\n","import { Component, Event, EventEmitter, Prop, h } from '@stencil/core';\nimport { TextAreaConfig } from '../schema';\n\n@Component({\n tag: 'app-textarea',\n styleUrl: 'app-textarea.css',\n shadow: true,\n})\nexport class AppTextarea {\n @Prop() testCaseId: string;\n @Prop() value: string;\n @Prop() config: TextAreaConfig;\n\n @Event() valueChange: EventEmitter<{\n key: string;\n value: string;\n testCaseId: string;\n }>;\n\n private handleChange = (e: Event) => {\n const target = e.target as HTMLTextAreaElement;\n\n this.valueChange.emit({\n key: this.config.name,\n value: target.value,\n testCaseId: this.testCaseId,\n });\n };\n\n render() {\n const c = this.config;\n\n const allowedAttrs = {\n placeholder: c.placeholder,\n required: c.required,\n disabled: c.disabled,\n readOnly: c.readOnly,\n rows: c.rows,\n id: c.name,\n name: c.name,\n autocomplete: c.autocomplete,\n };\n\n return (\n <div class=\"textarea-wrapper\">\n {c.label && (\n <label class=\"textarea-label\" htmlFor={c.name}>\n {c.label}\n </label>\n )}\n\n <textarea\n {...allowedAttrs}\n class=\"textarea-element\"\n value={this.value}\n onInput={this.handleChange}\n ></textarea>\n\n {c.helpText && <p class=\"help-text\">{c.helpText}</p>}\n </div>\n );\n }\n}\n","import { Component, h, Prop } from '@stencil/core';\n\nimport {\n ChipsConfig,\n FieldConfig,\n FormFieldType,\n SelectConfig,\n TextAreaConfig,\n} from './schema';\nimport { TestCase } from '../../types/llm-test-runner';\n\n@Component({\n tag: 'form-builder',\n shadow: true,\n})\nexport class FormBuilder {\n @Prop() fields: FieldConfig[] = [];\n @Prop() onUpdateApproach: (testCase: TestCase, approach: any) => void;\n @Prop() testCase: TestCase;\n @Prop() handleTestCaseChange: (\n e: CustomEvent<{ testCaseId: string; key: string; value: string }>,\n ) => void;\n @Prop() addChip: (\n e: CustomEvent<{ testCaseId: string; key: string; value: string }>,\n ) => void;\n @Prop() removeChip: (\n e: CustomEvent<{ testCaseId: string; key: string; index: number }>,\n ) => void;\n\n renderField(field: FieldConfig) {\n switch (field.fieldType) {\n case FormFieldType.TEXT_AREA:\n return (\n <app-textarea\n config={field as TextAreaConfig}\n value={this.testCase[field.name]}\n testCaseId={this.testCase.id}\n onValueChange={this.handleTestCaseChange}\n ></app-textarea>\n );\n\n case FormFieldType.CHIPS:\n return (\n <app-chips\n config={field as ChipsConfig}\n value={this.testCase[field.name]}\n testCaseId={this.testCase.id}\n onAddChip={this.addChip}\n onRemoveChip={this.removeChip}\n />\n );\n\n case FormFieldType.SELECT:\n return (\n <app-select\n config={field as SelectConfig}\n value={this.testCase.evaluationParameters?.approach}\n onChange={approach =>\n this.onUpdateApproach(this.testCase, approach)\n }\n />\n );\n\n default:\n return <div>Unknown field type</div>;\n }\n }\n\n render() {\n return (\n <div class=\"form-builder\">\n {this.fields.map(field => this.renderField(field))}\n </div>\n );\n }\n}\n"],"version":3}
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-CgmLNwZO.js');
4
+
5
+ class GeminiAdapter {
6
+ sdk;
7
+ constructor(key) {
8
+ this.sdk = new index.GoogleGenAI({
9
+ apiKey: key,
10
+ });
11
+ }
12
+ async invoke(text) {
13
+ const response = await this.sdk.models.generateContent({
14
+ model: "gemini-3-flash-preview" /* GeminiModels.Gemini3Flash__Preview */,
15
+ contents: text,
16
+ });
17
+ return response.text;
18
+ }
19
+ }
20
+
21
+ function env () {
22
+ window.env = {
23
+ API_KEY: '',
24
+ };
25
+ window.GeminiAdapter = GeminiAdapter;
26
+ }
27
+
28
+ const appGlobalScript = env || (() => {});
29
+ const globalScripts = appGlobalScript;
30
+
31
+ exports.globalScripts = globalScripts;
32
+ //# sourceMappingURL=app-globals-Chb-oJtg.js.map
33
+
34
+ //# sourceMappingURL=app-globals-Chb-oJtg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-globals-Chb-oJtg.js","sources":["src/services/models/gemini.ts","src/global/env.ts","@stencil/core/internal/app-globals"],"sourcesContent":["import { GoogleGenAI } from '@google/genai';\n\nimport type { LlmAdapter } from '../adapters';\n\nexport const enum GeminiModels {\n Gemini3Pro__Preview = 'gemini-3-pro-preview',\n Gemini3ProImage__Preview = 'gemini-3-pro-image-preview',\n Gemini3Flash__Preview = 'gemini-3-flash-preview',\n}\n\nexport class GeminiAdapter implements LlmAdapter {\n private readonly sdk;\n\n constructor(key: string) {\n this.sdk = new GoogleGenAI({\n apiKey: key,\n });\n }\n\n async invoke(text: string) {\n const response = await this.sdk.models.generateContent({\n model: GeminiModels.Gemini3Flash__Preview,\n contents: text,\n });\n\n return response.text;\n }\n}\n","import { GeminiAdapter } from '../services/models/gemini';\n\ndeclare global {\n interface Window {\n env: {\n API_KEY: string;\n };\n GeminiAdapter: typeof GeminiAdapter;\n }\n}\n\nexport default function () {\n window.env = {\n API_KEY: '__GEMINI_API_KEY__',\n };\n\n window.GeminiAdapter = GeminiAdapter;\n}\n","const appGlobalScript = appGlobalScriptNs.default || (() => {});\nimport * as appGlobalScriptNs from '/home/runner/work/llm-testrunner-lib/llm-testrunner-lib/src/global/env.ts';\nexport const globalScripts = appGlobalScript;\nexport const globalStyles = \"\";\n"],"names":["GoogleGenAI","appGlobalScriptNs.default"],"mappings":";;;;MAUa,aAAa,CAAA;AACP,IAAA,GAAG;AAEpB,IAAA,WAAA,CAAY,GAAW,EAAA;AACrB,QAAA,IAAI,CAAC,GAAG,GAAG,IAAIA,iBAAW,CAAC;AACzB,YAAA,MAAM,EAAE,GAAG;AACZ,SAAA,CAAC;;IAGJ,MAAM,MAAM,CAAC,IAAY,EAAA;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC;YACrD,KAAK,EAAA,wBAAA;AACL,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI;;AAEvB;;AChBa,YAAA,IAAA;IACZ,MAAM,CAAC,GAAG,GAAG;AACX,QAAA,OAAO,EAAE,EAAoB;KAC9B;AAED,IAAA,MAAM,CAAC,aAAa,GAAG,aAAa;AACtC;;ACjBA,MAAM,eAAe,GAAGC,GAAyB,KAAK,MAAM,EAAE,CAAC;AAEnD,MAAC,aAAa,GAAG;;;;"}