llm-testrunner-components 1.0.4 → 1.0.6
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.
- package/README.md +113 -10
- package/dist/cjs/app-globals-CbbEbofA.js +14 -0
- package/dist/cjs/app-globals-CbbEbofA.js.map +1 -0
- package/dist/cjs/{index-CY2lQip_.js → index-D-FySkoV.js} +25 -5
- package/dist/cjs/index-D-FySkoV.js.map +1 -0
- package/dist/cjs/index.cjs.js +159 -66
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/llm-test-runner.cjs.entry.js +1 -1
- package/dist/cjs/llm-testrunner.cjs.js +4 -4
- package/dist/cjs/llm-testrunner.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +3 -3
- package/dist/collection/collection-manifest.json +3 -2
- package/dist/collection/components/llm-test-runner/llm-test-runner.js +152 -72
- package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
- package/dist/collection/global/env.js +6 -0
- package/dist/collection/global/env.js.map +1 -0
- package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js +39 -0
- package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js.map +1 -0
- package/dist/components/index.js +6 -1650
- package/dist/components/index.js.map +1 -1
- package/dist/components/llm-test-runner.js +1 -1
- package/dist/components/p-CYUbsbxt.js +1770 -0
- package/dist/components/p-CYUbsbxt.js.map +1 -0
- package/dist/esm/app-globals-BOQOUavG.js +12 -0
- package/dist/esm/app-globals-BOQOUavG.js.map +1 -0
- package/dist/esm/{index-DBp-rMPb.js → index-cncubhtM.js} +25 -6
- package/dist/esm/index-cncubhtM.js.map +1 -0
- package/dist/esm/index.js +159 -66
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/llm-test-runner.entry.js +1 -1
- package/dist/esm/llm-testrunner.js +5 -5
- package/dist/esm/llm-testrunner.js.map +1 -1
- package/dist/esm/loader.js +4 -4
- package/dist/llm-testrunner/index.esm.js +1 -1
- package/dist/llm-testrunner/index.esm.js.map +1 -1
- package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
- package/dist/llm-testrunner/llm-testrunner.esm.js.map +1 -1
- package/dist/llm-testrunner/p-BOQOUavG.js +2 -0
- package/dist/llm-testrunner/p-BOQOUavG.js.map +1 -0
- package/dist/llm-testrunner/p-cncubhtM.js +3 -0
- package/dist/llm-testrunner/p-cncubhtM.js.map +1 -0
- package/dist/llm-testrunner/p-f68fd660.entry.js +2 -0
- package/dist/react/components.d.ts +6 -3
- package/dist/react/components.d.ts.map +1 -1
- package/dist/react/components.js +2 -2
- package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +13 -4
- package/dist/types/components.d.ts +26 -2
- package/dist/types/global/env.d.ts +8 -0
- package/dist/types/lib/rate-limited-fetcher/rate-limited-fetcher.d.ts +10 -0
- package/dist/types/stencil-public-runtime.d.ts +19 -10
- package/package.json +6 -4
- package/dist/cjs/app-globals-V2Kpy_OQ.js +0 -8
- package/dist/cjs/app-globals-V2Kpy_OQ.js.map +0 -1
- package/dist/cjs/index-CY2lQip_.js.map +0 -1
- package/dist/esm/app-globals-DQuL1Twl.js +0 -6
- package/dist/esm/app-globals-DQuL1Twl.js.map +0 -1
- package/dist/esm/index-DBp-rMPb.js.map +0 -1
- package/dist/llm-testrunner/p-DBp-rMPb.js +0 -3
- package/dist/llm-testrunner/p-DBp-rMPb.js.map +0 -1
- package/dist/llm-testrunner/p-DQuL1Twl.js +0 -2
- package/dist/llm-testrunner/p-DQuL1Twl.js.map +0 -1
- package/dist/llm-testrunner/p-ed2ea423.entry.js +0 -2
- /package/dist/llm-testrunner/{p-ed2ea423.entry.js.map → p-f68fd660.entry.js.map} +0 -0
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["src/lib/evaluation/evaluation-engine.ts","../../node_modules/uuid/dist/esm-browser/stringify.js","../../node_modules/uuid/dist/esm-browser/rng.js","../../node_modules/uuid/dist/esm-browser/native.js","../../node_modules/uuid/dist/esm-browser/v4.js","src/components/error-message/error-message.tsx","src/components/llm-test-runner/llm-test-runner.css?tag=llm-test-runner&encapsulation=shadow","src/components/llm-test-runner/llm-test-runner.tsx"],"sourcesContent":["import {\n EvaluationRequest,\n EvaluationResult,\n KeywordMatch,\n SourceLinkMatch,\n EvaluationCallback\n} from './types';\n\nexport class LLMEvaluationEngine {\n constructor() { }\n\n async evaluateResponse(\n request: EvaluationRequest,\n callback: EvaluationCallback\n ): Promise<void> {\n try {\n console.log('🔍 Starting evaluation for test case:', request.testCaseId);\n\n const result = await this.performEvaluation(request);\n\n console.log('Evaluation completed for test case:', request.testCaseId);\n console.log('Result:', result);\n\n callback(result);\n\n } catch (error) {\n console.error('Evaluation failed:', error);\n\n const errorResult: EvaluationResult = {\n testCaseId: request.testCaseId,\n passed: false,\n keywordMatches: [],\n sourceLinkMatches: [],\n timestamp: new Date().toISOString()\n };\n\n callback(errorResult);\n }\n }\n\n private async performEvaluation(request: EvaluationRequest): Promise<EvaluationResult> {\n const { testCaseId, expectedKeywords, expectedSourceLinks, actualResponse } = request;\n\n const keywordMatches = this.evaluateKeywords(expectedKeywords, actualResponse);\n const sourceLinkMatches = this.evaluateSourceLinks(expectedSourceLinks, actualResponse);\n\n // Test passes only if ALL expected keywords and source links are found\n const totalItems = keywordMatches.length + sourceLinkMatches.length;\n const foundItems = keywordMatches.filter(m => m.found).length + sourceLinkMatches.filter(m => m.found).length;\n const passed = foundItems === totalItems;\n\n return {\n testCaseId,\n passed,\n keywordMatches,\n sourceLinkMatches,\n timestamp: new Date().toISOString()\n };\n }\n\n private evaluateKeywords(expectedKeywords: string[], actualResponse: string): KeywordMatch[] {\n // Case-insensitive keyword matching\n const response = actualResponse.toLowerCase();\n\n return expectedKeywords.map(keyword => {\n const keywordToMatch = keyword.toLowerCase();\n const found = response.includes(keywordToMatch);\n\n return {\n keyword,\n found\n };\n });\n }\n\n private evaluateSourceLinks(expectedSourceLinks: string[], actualResponse: string): SourceLinkMatch[] {\n return expectedSourceLinks.map(link => {\n const found = actualResponse.includes(link);\n\n return {\n link,\n found\n };\n });\n }\n\n\n\n}\n\n","import validate from './validate.js';\n\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nvar byteToHex = [];\nfor (var i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n //\n // Note to future-self: No, you can't remove the `toLowerCase()` call.\n // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351\n return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n var uuid = unsafeStringify(arr, offset);\n // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;","// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\n\nvar getRandomValues;\nvar rnds8 = new Uint8Array(16);\nexport default function rng() {\n // lazy load so that environments that need to polyfill have a chance to do so\n if (!getRandomValues) {\n // getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation.\n getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n }\n return getRandomValues(rnds8);\n}","var randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);\nexport default {\n randomUUID\n};","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n options = options || {};\n var rnds = options.random || (options.rng || rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n offset = offset || 0;\n for (var i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nexport default v4;","import { FunctionalComponent, h } from '@stencil/core';\n\ninterface ErrorMessageProps {\n message: string;\n onClear?: () => void;\n}\n\nexport const ErrorMessage: FunctionalComponent<ErrorMessageProps> = ({ message, onClear }) => {\n if (!message) {\n return null;\n }\n\n return (\n <div class=\"error-message\">\n <span>{message}</span>\n {onClear && (\n <button\n class=\"error-close\"\n title=\"Close\"\n onClick={onClear}\n >\n ×\n </button>\n )}\n </div>\n );\n};\n",":host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n background-color: #f8fafc;\n min-height: 100vh;\n}\n\n.test-runner-container {\n padding: 20px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n margin: 20px 0;\n}\n\n.simple-test {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.test-cases {\n margin: 20px 0;\n}\n\n.test-case {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #eee;\n border-radius: 4px;\n background: #f9f9f9;\n}\n\n.test-case h3 {\n margin-top: 0;\n color: #333;\n}\n\n.test-case textarea {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: inherit;\n margin: 10px 0;\n}\n\n.test-case button {\n background: #007bff;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 4px;\n cursor: pointer;\n margin: 10px 5px 10px 0;\n}\n\n.test-case button:disabled {\n background: #ccc;\n cursor: not-allowed;\n}\n\n.output, .error {\n margin: 10px 0;\n padding: 10px;\n border-radius: 4px;\n}\n\n.output {\n background: #d4edda;\n border: 1px solid #c3e6cb;\n color: #155724;\n}\n\n.error {\n background: #f8d7da;\n border: 1px solid #f5c6cb;\n color: #721c24;\n}\n\n.test-runner-container {\n max-width: 1400px;\n margin: 0 auto;\n background: white;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);\n}\n\n/* Header Styles */\n.test-runner-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px 24px;\n background: /*linear-gradient(135deg, #667eea 0%, #764ba2 100%);*/ white;\n color: white;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.header-left, .header-right {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.header-center {\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.api-status {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.api-status-text {\n color: #28a745;\n font-weight: 500;\n font-size: 0.9rem;\n}\n\n/* Button Styles */\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n text-decoration: none;\n position: relative;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none !important;\n}\n\n.btn-primary {\n color: black;\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);\n}\n\n.btn-primary:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);\n}\n\n.btn-secondary {\n background: rgba(255, 255, 255, 0.2);\n color: blue;\n border: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.btn-outline {\n background: transparent;\n color: #6b7280;\n border: 2px solid #e5e7eb;\n}\n\n.btn-outline:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n transform: translateY(-1px);\n}\n\n.btn-icon {\n padding: 8px;\n min-width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n}\n\n.btn-run {\n color: white;\n}\n\n.btn-run:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.btn-delete {\n color: white;\n}\n\n.btn-delete:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.icon {\n font-size: 16px;\n}\n\n/* Main Content */\n.test-runner-content {\n padding: 0;\n}\n\n/* Column Headers */\n.column-headers {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n background: #e5e7eb;\n border-bottom: 2px solid #d1d5db;\n}\n\n.column-header {\n background: #f8fafc;\n padding: 16px 20px;\n font-weight: 600;\n color: #374151;\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Test Cases */\n.test-cases {\n background: white;\n}\n\n.test-case-row {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n border-bottom: 1px solid #e5e7eb;\n min-height: 200px;\n}\n\n.test-case-row:hover {\n background: #f9fafb;\n}\n\n/* Input Column */\n.input-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n}\n\n.input-group {\n margin-bottom: 16px;\n}\n\n.input-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.input-group textarea {\n width: 95%;\n padding: 12px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n font-size: 14px;\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n}\n\n.input-group textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n/* Keywords and Links */\n.keywords-group, .links-group {\n margin-bottom: 16px;\n}\n\n.keywords-group label, .links-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.tags-container, .links-container {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.tag {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: #dbeafe;\n color: #1e40af;\n padding: 4px 8px;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.tag-remove {\n background: none;\n border: none;\n color: #1e40af;\n cursor: pointer;\n font-size: 14px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.tag-remove:hover {\n background: rgba(30, 64, 175, 0.1);\n}\n\n.link-item {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #f0f9ff;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n}\n\n.link-item a {\n color: #0369a1;\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link-item a:hover {\n text-decoration: underline;\n}\n\n.link-remove {\n background: none;\n border: none;\n color: #0369a1;\n cursor: pointer;\n font-size: 12px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.link-remove:hover {\n background: rgba(3, 105, 161, 0.1);\n}\n\n.tags-container input, .links-container input {\n border: 1px solid #d1d5db;\n border-radius: 6px;\n padding: 6px 8px;\n font-size: 12px;\n outline: none;\n min-width: 120px;\n}\n\n.tags-container input:focus, .links-container input:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n\n/* Output Column */\n.output-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.output-content {\n background: #f8fafc;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 16px;\n font-size: 14px;\n line-height: 1.6;\n color: #374151;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex: 1;\n overflow-y: auto;\n max-height: 250px;\n overflow-x: scroll;\n}\n\n.output-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* Evaluation Column */\n.evaluation-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.evaluation-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n flex: 1;\n}\n\n.score-display {\n text-align: center;\n}\n\n.score-number {\n font-size: 24px;\n font-weight: 700;\n color: #111827;\n display: block;\n margin-bottom: 8px;\n}\n\n.score-bar {\n width: 100%;\n height: 8px;\n background: #e5e7eb;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.score-fill {\n height: 100%;\n background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);\n transition: width 0.3s ease;\n}\n\n.evaluation-details {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.detail-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.status {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n}\n\n.status.pass {\n background: #dcfce7;\n color: #166534;\n}\n\n.status.fail {\n background: #fef2f2;\n color: #dc2626;\n}\n\n.evaluation-text {\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n background: #f9fafb;\n padding: 8px;\n border-radius: 6px;\n border: 1px solid #e5e7eb;\n}\n\n.evaluation-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* New evaluation result styles */\n.evaluation-result {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.evaluation-status {\n font-weight: 600;\n font-size: 14px;\n padding: 8px 12px;\n border-radius: 4px;\n text-align: center;\n}\n\n.evaluation-status.passed {\n background: #d4edda;\n color: #155724;\n border: 1px solid #c3e6cb;\n}\n\n.evaluation-status.failed {\n background: #f8d7da;\n color: #721c24;\n border: 1px solid #f5c6cb;\n}\n\n.evaluation-score {\n font-size: 12px;\n color: #495057;\n text-align: center;\n font-weight: 500;\n}\n\n.evaluation-feedback {\n font-size: 12px;\n color: #6c757d;\n background: #f8f9fa;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #dee2e6;\n}\n\n/* Actions Column */\n.actions-column {\n padding: 20px;\n background: white;\n display: flex;\n flex-direction: column;\n gap: 12px;\n align-items: center;\n justify-content: flex-start;\n align-self: flex-start;\n}\n\n/* Add Test Case */\n.add-test-case {\n padding: 24px;\n text-align: center;\n background: #f9fafb;\n border-top: 1px solid #e5e7eb;\n}\n\n.hidden {\n display: none;\n}\n\n.error-message {\n background: #ffeaea;\n color: #b71c1c;\n border: 1px solid #f44336;\n padding: 0.75em 2.5em 0.75em 1em;\n border-radius: 4px;\n margin: 1em 0;\n position: relative;\n font-size: 1em;\n display: flex;\n align-items: center;\n gap: 1em;\n}\n\n.error-close {\n background: none;\n border: none;\n color: #b71c1c;\n font-size: 1.25em;\n font-weight: bold;\n cursor: pointer;\n position: absolute;\n right: 0.75em;\n top: 50%;\n transform: translateY(-50%);\n line-height: 1;\n padding: 0;\n}\n\n/* Responsive Design */\n@media (max-width: 1200px) {\n .test-case-row {\n grid-template-columns: 1fr;\n gap: 0;\n }\n \n .column-headers {\n display: none;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n border-right: none;\n border-bottom: 1px solid #e5e7eb;\n }\n \n .actions-column {\n flex-direction: row;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .test-runner-header {\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n }\n \n .header-left, .header-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n \n .btn {\n font-size: 12px;\n padding: 8px 12px;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n padding: 16px;\n }\n \n .test-case-row {\n min-height: auto;\n }\n}","import { Component, State, Prop, h } from '@stencil/core';\nimport { LLMEvaluationEngine } from '../../lib/evaluation/evaluation-engine';\nimport { EvaluationRequest, EvaluationResult } from '../../lib/evaluation/types';\nimport { v4 as uuidv4 } from 'uuid';\nimport { ErrorMessage } from '../error-message/error-message';\n\nexport interface TestCase {\n id: string;\n question: string;\n expectedKeywords: string[];\n expectedSourceLinks: string[];\n output?: string;\n isRunning?: boolean;\n error?: string;\n evaluationResult?: EvaluationResult;\n}\n\n@Component({\n tag: 'llm-test-runner',\n styleUrl: 'llm-test-runner.css',\n shadow: true,\n})\nexport class LLMTestRunner {\n @Prop() apiKey: string;\n \n @State() testCases: TestCase[] = [\n {\n id: '1',\n question: '',\n expectedKeywords: [],\n expectedSourceLinks: [],\n isRunning: false\n }\n ];\n @State() isRunningAll: boolean = false;\n @State() error: string = '';\n\n private fileInput!: HTMLInputElement;\n @State() isExportingTestSuite: boolean = false;\n\n private evaluationEngine: LLMEvaluationEngine;\n private apiUrl: string = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';\n\n async componentWillLoad() {\n this.evaluationEngine = new LLMEvaluationEngine();\n \n console.log('🔍 componentWillLoad - apiKey:', this.apiKey ? 'SET' : 'NOT SET');\n console.log('🔍 componentWillLoad - apiKey value:', this.apiKey);\n \n if (!this.apiKey) {\n throw new Error('API key is required. Please provide the apiKey prop: <llm-test-runner apiKey=\"your-api-key\" />');\n }\n }\n\n\n componentDidLoad() {\n }\n\n disconnectedCallback() {\n }\n\n private addNewTestCase() {\n const newTestCase: TestCase = {\n id: this.generateId(),\n question: '',\n expectedKeywords: [],\n expectedSourceLinks: [],\n isRunning: false\n };\n this.testCases = [...this.testCases, newTestCase];\n }\n\n private updateTestCase(id: string, updates: Partial<TestCase>) {\n this.testCases = this.testCases.map(tc => \n tc.id === id ? { ...tc, ...updates } : tc\n );\n }\n\n private async runSingleTest(testCase: TestCase) {\n console.log('🚀 Starting test for question:', testCase.question);\n \n this.updateTestCase(testCase.id, { isRunning: true });\n \n try {\n const aiResponse = await this.callGeminiAPI(testCase.question);\n console.log('✅ AI call completed for test case:', testCase.id);\n \n this.updateTestCase(testCase.id, {\n isRunning: false,\n output: aiResponse,\n error: null\n });\n \n await this.evaluateResponse({\n ...testCase,\n output: aiResponse\n });\n \n } catch (error) {\n console.error('❌ Error in runSingleTest:', error);\n this.updateTestCase(testCase.id, {\n isRunning: false,\n output: null,\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n }\n }\n\n private deleteTestCase(id: string) {\n this.testCases = this.testCases.filter(tc => tc.id !== id);\n }\n\n private addKeyword(testCaseId: string, keyword: string) {\n if (keyword.trim()) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n this.updateTestCase(testCaseId, {\n expectedKeywords: [...testCase.expectedKeywords, keyword.trim()]\n });\n }\n }\n }\n\n private removeKeyword(testCaseId: string, index: number) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n const newKeywords = testCase.expectedKeywords.filter((_, i) => i !== index);\n this.updateTestCase(testCaseId, { expectedKeywords: newKeywords });\n }\n }\n\n private addSourceLink(testCaseId: string, link: string) {\n if (link.trim()) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n this.updateTestCase(testCaseId, {\n expectedSourceLinks: [...testCase.expectedSourceLinks, link.trim()]\n });\n }\n }\n }\n\n private removeSourceLink(testCaseId: string, index: number) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n const newLinks = testCase.expectedSourceLinks.filter((_, i) => i !== index);\n this.updateTestCase(testCaseId, { expectedSourceLinks: newLinks });\n }\n }\n\n private async callGeminiAPI(prompt: string): Promise<string> {\n console.log('🔍 callGeminiAPI - apiKey:', this.apiKey ? 'SET' : 'NOT SET');\n console.log('🔍 callGeminiAPI - apiKey value:', this.apiKey ? `${this.apiKey.substring(0, 10)}...` : 'undefined');\n \n if (!this.apiKey) {\n throw new Error('API key is required. Please provide the apiKey prop.');\n }\n\n const requestBody = {\n contents: [{\n parts: [{\n text: prompt\n }]\n }]\n };\n\n const response = await fetch(`${this.apiUrl}?key=${this.apiKey}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.candidates && data.candidates[0] && data.candidates[0].content) {\n return data.candidates[0].content.parts[0].text;\n } else {\n throw new Error('Unexpected response format from Gemini API');\n }\n }\n\n private async evaluateResponse(testCase: TestCase): Promise<void> {\n if (!testCase.output) {\n console.warn('⚠️ No output to evaluate for test case:', testCase.id);\n return;\n }\n\n const evaluationRequest: EvaluationRequest = {\n testCaseId: testCase.id,\n question: testCase.question,\n expectedKeywords: testCase.expectedKeywords,\n expectedSourceLinks: testCase.expectedSourceLinks,\n actualResponse: testCase.output\n };\n\n await this.evaluationEngine.evaluateResponse(evaluationRequest, (result: EvaluationResult) => {\n console.log('📊 Evaluation result received:', result);\n \n this.updateTestCase(testCase.id, {\n evaluationResult: result\n });\n });\n }\n\n private async runAllTests() {\n this.isRunningAll = true;\n \n for (const testCase of this.testCases) {\n if (!testCase.isRunning && testCase.question.trim()) {\n await this.runSingleTest(testCase);\n // Delay between tests to avoid rate limiting\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n }\n this.isRunningAll = false;\n }\n\n private generateId(): string {\n return uuidv4();\n }\n\n private handleFileSelect(): void {\n this.fileInput.click();\n }\n\n private async handleFileChange(event: Event): Promise<void> {\n const target = event.target as HTMLInputElement;\n const file = target.files?.[0];\n\n // Immediately clear the input value to allow for a new upload.\n target.value = '';\n\n if (!file) {\n this.error = 'No file selected.';\n return;\n }\n\n const isJsonType = file.type === 'application/json';\n const isJsonExtension = file.name.toLowerCase().endsWith('.json');\n if (!isJsonType && !isJsonExtension) {\n this.error = 'Invalid file type. Please select a JSON file.';\n return;\n }\n\n this.error = '';\n\n try {\n const content = await this.readFileAsync(file);\n const fileContent = JSON.parse(content);\n\n if (!Array.isArray(fileContent)) {\n throw new Error(\"Invalid JSON structure. Expected a JSON array.\");\n } \n const importedTestCases: TestCase[] = fileContent.map((item) => ({\n id: this.generateId(),\n question: item.question || '',\n expectedKeywords: Array.isArray(item.expectedKeywords) ? item.expectedKeywords : [],\n expectedSourceLinks: Array.isArray(item.expectedSourceLinks) ? item.expectedSourceLinks : [],\n isRunning: false\n }));\n this.testCases = importedTestCases; \n } catch (err) {\n this.error = err?.message || 'Error processing file. Please ensure it is a valid JSON array.';\n console.error('File Processing Error:', err);\n }\n }\n\n private readFileAsync(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(reader.error);\n reader.readAsText(file);\n });\n }\n\n private downloadFile(content: string, filename: string, mimeType: string) {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n private async handleExportTestSuite() {\n this.isExportingTestSuite = true;\n try {\n // Exporting only input data (question, expected keywords, expected source links)\n const exportData = this.testCases.map(testCase => ({\n id: testCase.id,\n question: testCase.question,\n expectedKeywords: testCase.expectedKeywords,\n expectedSourceLinks: testCase.expectedSourceLinks\n }));\n\n const jsonContent = JSON.stringify(exportData, null, 2);\n\n // Added a small delay to show the loading state\n await new Promise(resolve => setTimeout(resolve, 500));\n\n this.downloadFile(jsonContent, 'test-suite.json', 'application/json');\n } finally {\n this.isExportingTestSuite = false;\n }\n }\n\n render() {\n return (\n <div class=\"test-runner-container\">\n <header class=\"test-runner-header\">\n <div class=\"header-left\">\n <input\n class=\"hidden\"\n type=\"file\"\n ref={(el) => (this.fileInput = el as HTMLInputElement)}\n onChange={(e) => this.handleFileChange(e)}\n accept=\".json,application/json\"\n />\n <button class=\"btn btn-secondary\" onClick={() => this.handleFileSelect()}>\n <span class=\"icon\">↑</span>\n Import Test Suite\n </button>\n <button\n class=\"btn btn-secondary\"\n onClick={() => this.handleExportTestSuite()}\n disabled={this.isExportingTestSuite}\n >\n <span class=\"icon\">\n {this.isExportingTestSuite ? '⏳' : '↓'}\n </span>\n {this.isExportingTestSuite ? 'Exporting...' : 'Export Test Suite'}\n </button>\n </div>\n \n <div class=\"header-right\">\n <button class=\"btn btn-secondary\">\n <span class=\"icon\">⚙️</span>\n Prompt Editor\n </button>\n <button class=\"btn btn-secondary\">\n <span class=\"icon\">↓</span>\n Export Test Results\n </button>\n <button \n class=\"btn btn-primary\" \n onClick={() => this.runAllTests()}\n disabled={this.isRunningAll}\n >\n {this.isRunningAll ? 'Running...' : 'Run All'}\n </button>\n </div>\n </header>\n <ErrorMessage\n message={this.error}\n onClear={() => (this.error = '')}\n />\n <div class=\"test-runner-content\">\n <div class=\"column-headers\">\n <div class=\"column-header\">Input</div>\n <div class=\"column-header\">Output</div>\n <div class=\"column-header\">Evaluation</div>\n <div class=\"column-header\">Actions</div>\n </div>\n\n <div class=\"test-cases\">\n {this.testCases.map((testCase) => (\n <div class=\"test-case-row\" key={testCase.id}>\n <div class=\"input-column\">\n <div class=\"input-group\">\n <label>Question</label>\n <textarea\n value={testCase.question}\n onInput={(e) => this.updateTestCase(testCase.id, { \n question: (e.target as HTMLTextAreaElement).value \n })}\n placeholder=\"Enter your question here...\"\n rows={3}\n ></textarea>\n </div>\n \n <div class=\"keywords-group\">\n <label>Expected keywords</label>\n <div class=\"tags-container\">\n {testCase.expectedKeywords.map((keyword, index) => (\n <span class=\"tag\" key={index}>\n {keyword}\n <button \n class=\"tag-remove\" \n onClick={() => this.removeKeyword(testCase.id, index)}\n >×</button>\n </span>\n ))}\n <input\n type=\"text\"\n placeholder=\"New item...\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n this.addKeyword(testCase.id, (e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n </div>\n \n <div class=\"links-group\">\n <label>Expected source links</label>\n <div class=\"links-container\">\n {testCase.expectedSourceLinks.map((link, index) => (\n <div class=\"link-item\" key={index}>\n <a href={link} target=\"_blank\" rel=\"noopener noreferrer\">{link}</a>\n <button \n class=\"link-remove\" \n onClick={() => this.removeSourceLink(testCase.id, index)}\n >×</button>\n </div>\n ))}\n <input\n type=\"url\"\n placeholder=\"New item...\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n this.addSourceLink(testCase.id, (e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n </div>\n </div>\n\n <div class=\"output-column\">\n {testCase.output ? (\n <div class=\"output-content\">\n {testCase.output}\n </div>\n ) : (\n <div class=\"output-placeholder\">\n {testCase.isRunning ? 'Running...' : ''}\n </div>\n )}\n </div>\n\n <div class=\"evaluation-column\">\n {testCase.evaluationResult ? (\n <div class=\"evaluation-result\">\n <div class={`evaluation-status ${testCase.evaluationResult.passed ? 'passed' : 'failed'}`}>\n {testCase.evaluationResult.passed ? '✅ PASSED' : '❌ FAILED'}\n </div>\n <div class=\"evaluation-details\">\n Keywords: {testCase.evaluationResult.keywordMatches.filter(m => m.found).length}/{testCase.evaluationResult.keywordMatches.length} found\n </div>\n </div>\n ) : (\n <div class=\"evaluation-placeholder\">\n {testCase.isRunning ? 'Evaluating...' : ''}\n </div>\n )}\n </div>\n\n <div class=\"actions-column\">\n <button \n class=\"btn btn-icon btn-run\"\n onClick={() => this.runSingleTest(testCase)}\n disabled={testCase.isRunning || !testCase.question.trim()}\n title={!testCase.question.trim() ? \"Enter a question first\" : \"Run this test\"}\n >\n {testCase.isRunning ? '⏳' : '▶️'}\n </button>\n <button \n class=\"btn btn-icon btn-delete\"\n onClick={() => this.deleteTestCase(testCase.id)}\n title=\"Delete this test\"\n >\n 🗑️\n </button>\n </div>\n </div>\n ))}\n </div>\n\n <div class=\"add-test-case\">\n <button class=\"btn btn-outline\" onClick={() => this.addNewTestCase()}>\n + Add Question\n </button>\n </div>\n </div>\n </div>\n );\n }\n}"],"names":["uuidv4"],"mappings":"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","x_google_ignoreList":[1,2,3,4]}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["src/lib/evaluation/evaluation-engine.ts","../../node_modules/uuid/dist/esm-browser/stringify.js","../../node_modules/uuid/dist/esm-browser/rng.js","../../node_modules/uuid/dist/esm-browser/native.js","../../node_modules/uuid/dist/esm-browser/v4.js","src/components/error-message/error-message.tsx","src/lib/rate-limited-fetcher/rate-limited-fetcher.ts","src/components/llm-test-runner/llm-test-runner.css?tag=llm-test-runner&encapsulation=shadow","src/components/llm-test-runner/llm-test-runner.tsx"],"sourcesContent":["import {\n EvaluationRequest,\n EvaluationResult,\n KeywordMatch,\n SourceLinkMatch,\n EvaluationCallback\n} from './types';\n\nexport class LLMEvaluationEngine {\n constructor() { }\n\n async evaluateResponse(\n request: EvaluationRequest,\n callback: EvaluationCallback\n ): Promise<void> {\n try {\n console.log('🔍 Starting evaluation for test case:', request.testCaseId);\n\n const result = await this.performEvaluation(request);\n\n console.log('Evaluation completed for test case:', request.testCaseId);\n console.log('Result:', result);\n\n callback(result);\n\n } catch (error) {\n console.error('Evaluation failed:', error);\n\n const errorResult: EvaluationResult = {\n testCaseId: request.testCaseId,\n passed: false,\n keywordMatches: [],\n sourceLinkMatches: [],\n timestamp: new Date().toISOString()\n };\n\n callback(errorResult);\n }\n }\n\n private async performEvaluation(request: EvaluationRequest): Promise<EvaluationResult> {\n const { testCaseId, expectedKeywords, expectedSourceLinks, actualResponse } = request;\n\n const keywordMatches = this.evaluateKeywords(expectedKeywords, actualResponse);\n const sourceLinkMatches = this.evaluateSourceLinks(expectedSourceLinks, actualResponse);\n\n // Test passes only if ALL expected keywords and source links are found\n const totalItems = keywordMatches.length + sourceLinkMatches.length;\n const foundItems = keywordMatches.filter(m => m.found).length + sourceLinkMatches.filter(m => m.found).length;\n const passed = foundItems === totalItems;\n\n return {\n testCaseId,\n passed,\n keywordMatches,\n sourceLinkMatches,\n timestamp: new Date().toISOString()\n };\n }\n\n private evaluateKeywords(expectedKeywords: string[], actualResponse: string): KeywordMatch[] {\n // Case-insensitive keyword matching\n const response = actualResponse.toLowerCase();\n\n return expectedKeywords.map(keyword => {\n const keywordToMatch = keyword.toLowerCase();\n const found = response.includes(keywordToMatch);\n\n return {\n keyword,\n found\n };\n });\n }\n\n private evaluateSourceLinks(expectedSourceLinks: string[], actualResponse: string): SourceLinkMatch[] {\n return expectedSourceLinks.map(link => {\n const found = actualResponse.includes(link);\n\n return {\n link,\n found\n };\n });\n }\n\n\n\n}\n\n","import validate from './validate.js';\n\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nvar byteToHex = [];\nfor (var i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n //\n // Note to future-self: No, you can't remove the `toLowerCase()` call.\n // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351\n return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n var uuid = unsafeStringify(arr, offset);\n // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;","// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\n\nvar getRandomValues;\nvar rnds8 = new Uint8Array(16);\nexport default function rng() {\n // lazy load so that environments that need to polyfill have a chance to do so\n if (!getRandomValues) {\n // getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation.\n getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n }\n return getRandomValues(rnds8);\n}","var randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);\nexport default {\n randomUUID\n};","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n options = options || {};\n var rnds = options.random || (options.rng || rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n offset = offset || 0;\n for (var i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nexport default v4;","import { FunctionalComponent, h } from '@stencil/core';\n\ninterface ErrorMessageProps {\n message: string;\n onClear?: () => void;\n}\n\nexport const ErrorMessage: FunctionalComponent<ErrorMessageProps> = ({ message, onClear }) => {\n if (!message) {\n return null;\n }\n\n return (\n <div class=\"error-message\">\n <span>{message}</span>\n {onClear && (\n <button\n class=\"error-close\"\n title=\"Close\"\n onClick={onClear}\n >\n ×\n </button>\n )}\n </div>\n );\n};\n","export class RateLimitedFetcher {\n private queue: (() => void)[] = [];\n private delay: number; // delay in milliseconds\n private intervalId?: any;\n\n constructor(delayMs: number) {\n this.delay = delayMs;\n }\n\n private startQueue() {\n if (this.intervalId) return;\n this.intervalId = setInterval(() => {\n const task = this.queue.shift();\n if (task) task();\n if (this.queue.length === 0) {\n this.stop();\n }\n }, this.delay);\n }\n\n public schedule<T>(task: () => Promise<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n this.queue.push(() => {\n task().then(resolve).catch(reject);\n });\n this.startQueue(); \n });\n }\n\n public stop() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n }\n\n public async runAll<T>(tasks: Array<() => Promise<T>>): Promise<T[]> {\n const promises = tasks.map(task => this.schedule(task));\n return Promise.all(promises);\n }\n}\n",":host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n background-color: #f8fafc;\n min-height: 100vh;\n}\n\n.test-runner-container {\n padding: 20px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n margin: 20px 0;\n}\n\n.simple-test {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.test-cases {\n margin: 20px 0;\n}\n\n.test-case {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #eee;\n border-radius: 4px;\n background: #f9f9f9;\n}\n\n.test-case h3 {\n margin-top: 0;\n color: #333;\n}\n\n.test-case textarea {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: inherit;\n margin: 10px 0;\n}\n\n.test-case button {\n background: #007bff;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 4px;\n cursor: pointer;\n margin: 10px 5px 10px 0;\n}\n\n.test-case button:disabled {\n background: #ccc;\n cursor: not-allowed;\n}\n\n.output, .error {\n margin: 10px 0;\n padding: 10px;\n border-radius: 4px;\n}\n\n.output {\n background: #d4edda;\n border: 1px solid #c3e6cb;\n color: #155724;\n}\n\n.error {\n background: #f8d7da;\n border: 1px solid #f5c6cb;\n color: #721c24;\n}\n\n.test-runner-container {\n max-width: 1400px;\n margin: 0 auto;\n background: white;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);\n}\n\n/* Header Styles */\n.test-runner-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px 24px;\n background: /*linear-gradient(135deg, #667eea 0%, #764ba2 100%);*/ white;\n color: white;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.header-left, .header-right {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.header-center {\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.api-status {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.api-status-text {\n color: #28a745;\n font-weight: 500;\n font-size: 0.9rem;\n}\n\n/* Button Styles */\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n text-decoration: none;\n position: relative;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none !important;\n}\n\n.btn-primary {\n color: black;\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);\n}\n\n.btn-primary:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);\n}\n\n.btn-secondary {\n background: rgba(255, 255, 255, 0.2);\n color: blue;\n border: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.btn-outline {\n background: transparent;\n color: #6b7280;\n border: 2px solid #e5e7eb;\n}\n\n.btn-outline:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n transform: translateY(-1px);\n}\n\n.btn-icon {\n padding: 8px;\n min-width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n}\n\n.btn-run {\n color: white;\n}\n\n.btn-run:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.btn-delete {\n color: white;\n}\n\n.btn-delete:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.icon {\n font-size: 16px;\n}\n\n/* Main Content */\n.test-runner-content {\n padding: 0;\n}\n\n/* Column Headers */\n.column-headers {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n background: #e5e7eb;\n border-bottom: 2px solid #d1d5db;\n}\n\n.column-header {\n background: #f8fafc;\n padding: 16px 20px;\n font-weight: 600;\n color: #374151;\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Test Cases */\n.test-cases {\n background: white;\n}\n\n.test-case-row {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n border-bottom: 1px solid #e5e7eb;\n min-height: 200px;\n}\n\n.test-case-row:hover {\n background: #f9fafb;\n}\n\n/* Input Column */\n.input-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n}\n\n.input-group {\n margin-bottom: 16px;\n}\n\n.input-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.input-group textarea {\n width: 95%;\n padding: 12px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n font-size: 14px;\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n}\n\n.input-group textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n/* Keywords and Links */\n.keywords-group, .links-group {\n margin-bottom: 16px;\n}\n\n.keywords-group label, .links-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.tags-container, .links-container {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.tag {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: #dbeafe;\n color: #1e40af;\n padding: 4px 8px;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.tag-remove {\n background: none;\n border: none;\n color: #1e40af;\n cursor: pointer;\n font-size: 14px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.tag-remove:hover {\n background: rgba(30, 64, 175, 0.1);\n}\n\n.link-item {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #f0f9ff;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n}\n\n.link-item a {\n color: #0369a1;\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link-item a:hover {\n text-decoration: underline;\n}\n\n.link-remove {\n background: none;\n border: none;\n color: #0369a1;\n cursor: pointer;\n font-size: 12px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.link-remove:hover {\n background: rgba(3, 105, 161, 0.1);\n}\n\n.tags-container input, .links-container input {\n border: 1px solid #d1d5db;\n border-radius: 6px;\n padding: 6px 8px;\n font-size: 12px;\n outline: none;\n min-width: 120px;\n}\n\n.tags-container input:focus, .links-container input:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n\n/* Output Column */\n.output-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.output-content {\n background: #f8fafc;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 16px;\n font-size: 14px;\n line-height: 1.6;\n color: #374151;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex: 1;\n overflow-y: auto;\n max-height: 250px;\n overflow-x: scroll;\n}\n\n.output-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* Evaluation Column */\n.evaluation-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.evaluation-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n flex: 1;\n}\n\n.score-display {\n text-align: center;\n}\n\n.score-number {\n font-size: 24px;\n font-weight: 700;\n color: #111827;\n display: block;\n margin-bottom: 8px;\n}\n\n.score-bar {\n width: 100%;\n height: 8px;\n background: #e5e7eb;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.score-fill {\n height: 100%;\n background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);\n transition: width 0.3s ease;\n}\n\n.evaluation-details {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.detail-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.status {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n}\n\n.status.pass {\n background: #dcfce7;\n color: #166534;\n}\n\n.status.fail {\n background: #fef2f2;\n color: #dc2626;\n}\n\n.evaluation-text {\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n background: #f9fafb;\n padding: 8px;\n border-radius: 6px;\n border: 1px solid #e5e7eb;\n}\n\n.evaluation-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* New evaluation result styles */\n.evaluation-result {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.evaluation-status {\n font-weight: 600;\n font-size: 14px;\n padding: 8px 12px;\n border-radius: 4px;\n text-align: center;\n}\n\n.evaluation-status.passed {\n background: #d4edda;\n color: #155724;\n border: 1px solid #c3e6cb;\n}\n\n.evaluation-status.failed {\n background: #f8d7da;\n color: #721c24;\n border: 1px solid #f5c6cb;\n}\n\n.evaluation-score {\n font-size: 12px;\n color: #495057;\n text-align: center;\n font-weight: 500;\n}\n\n.evaluation-feedback {\n font-size: 12px;\n color: #6c757d;\n background: #f8f9fa;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #dee2e6;\n}\n\n/* Actions Column */\n.actions-column {\n padding: 20px;\n background: white;\n display: flex;\n flex-direction: column;\n gap: 12px;\n align-items: center;\n justify-content: flex-start;\n align-self: flex-start;\n}\n\n/* Add Test Case */\n.add-test-case {\n padding: 24px;\n text-align: center;\n background: #f9fafb;\n border-top: 1px solid #e5e7eb;\n}\n\n.hidden {\n display: none;\n}\n\n.error-message {\n background: #ffeaea;\n color: #b71c1c;\n border: 1px solid #f44336;\n padding: 0.75em 2.5em 0.75em 1em;\n border-radius: 4px;\n margin: 1em 0;\n position: relative;\n font-size: 1em;\n display: flex;\n align-items: center;\n gap: 1em;\n}\n\n.error-close {\n background: none;\n border: none;\n color: #b71c1c;\n font-size: 1.25em;\n font-weight: bold;\n cursor: pointer;\n position: absolute;\n right: 0.75em;\n top: 50%;\n transform: translateY(-50%);\n line-height: 1;\n padding: 0;\n}\n\n/* Responsive Design */\n@media (max-width: 1200px) {\n .test-case-row {\n grid-template-columns: 1fr;\n gap: 0;\n }\n \n .column-headers {\n display: none;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n border-right: none;\n border-bottom: 1px solid #e5e7eb;\n }\n \n .actions-column {\n flex-direction: row;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .test-runner-header {\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n }\n \n .header-left, .header-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n \n .btn {\n font-size: 12px;\n padding: 8px 12px;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n padding: 16px;\n }\n \n .test-case-row {\n min-height: auto;\n }\n}","import { Component, State, Prop, h, EventEmitter, Event } from '@stencil/core';\nimport { LLMEvaluationEngine } from '../../lib/evaluation/evaluation-engine';\nimport { EvaluationRequest, EvaluationResult } from '../../lib/evaluation/types';\nimport { v4 as uuidv4 } from 'uuid';\nimport { ErrorMessage } from '../error-message/error-message';\nimport { RateLimitedFetcher } from '../../lib/rate-limited-fetcher/rate-limited-fetcher';\n\nexport interface TestCase {\n id: string;\n question: string;\n expectedKeywords: string[];\n expectedSourceLinks: string[];\n output?: string;\n isRunning?: boolean;\n error?: string;\n evaluationResult?: EvaluationResult;\n responseTime?: number; // Time taken by the callback to execute in milliseconds\n}\n\nexport interface LLMRequestPayload {\n prompt: string;\n resolve: (result: string) => void;\n reject: (err: any) => void;\n}\n\n@Component({\n tag: 'llm-test-runner',\n styleUrl: 'llm-test-runner.css',\n shadow: true,\n})\nexport class LLMTestRunner {\n @Event() llmRequest: EventEmitter<LLMRequestPayload>;\n @Prop() delayMs?: number = 500; \n @State() testCases: TestCase[] = [\n {\n id: '1',\n question: '',\n expectedKeywords: [],\n expectedSourceLinks: [],\n isRunning: false\n }\n ];\n @State() isRunningAll: boolean = false;\n @State() error: string = '';\n\n private fileInput!: HTMLInputElement;\n @State() isExportingTestSuite: boolean = false;\n @State() isExportingTestResults: boolean = false;\n\n private evaluationEngine: LLMEvaluationEngine;\n\n componentWillLoad() {\n this.evaluationEngine = new LLMEvaluationEngine();\n }\n\n componentDidLoad() {\n }\n\n disconnectedCallback() {\n }\n\n private addNewTestCase() {\n const newTestCase: TestCase = {\n id: this.generateId(),\n question: '',\n expectedKeywords: [],\n expectedSourceLinks: [],\n isRunning: false\n };\n this.testCases = [...this.testCases, newTestCase];\n }\n\n private updateTestCase(id: string, updates: Partial<TestCase>) {\n this.testCases = this.testCases.map(tc => \n tc.id === id ? { ...tc, ...updates } : tc\n );\n }\n\n private async runSingleTest(testCase: TestCase): Promise<void> {\n console.log('🚀 Starting test for question:', testCase.question);\n const startTime = Date.now();\n this.updateTestCase(testCase.id, { isRunning: true });\n return new Promise<void>((resolve, reject) => {\n this.llmRequest.emit({\n prompt: testCase.question,\n resolve: async (aiResponse: string) => {\n console.log('✅ AI call completed for test case:', testCase.id);\n const endTime = Date.now();\n const responseTime = endTime - startTime;\n this.updateTestCase(testCase.id, {\n isRunning: false,\n output: aiResponse,\n error: null,\n responseTime: responseTime\n });\n\n await this.evaluateResponse({\n ...testCase,\n output: aiResponse,\n responseTime: responseTime\n });\n resolve()\n },\n reject: (error: any) => {\n console.error('❌ Error in runSingleTest:', error);\n this.updateTestCase(testCase.id, {\n isRunning: false,\n output: null,\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n reject(error);\n }\n });\n })\n }\n\n private deleteTestCase(id: string) {\n this.testCases = this.testCases.filter(tc => tc.id !== id);\n }\n\n private addKeyword(testCaseId: string, keyword: string) {\n if (keyword.trim()) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n this.updateTestCase(testCaseId, {\n expectedKeywords: [...testCase.expectedKeywords, keyword.trim()]\n });\n }\n }\n }\n\n private removeKeyword(testCaseId: string, index: number) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n const newKeywords = testCase.expectedKeywords.filter((_, i) => i !== index);\n this.updateTestCase(testCaseId, { expectedKeywords: newKeywords });\n }\n }\n\n private addSourceLink(testCaseId: string, link: string) {\n if (link.trim()) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n this.updateTestCase(testCaseId, {\n expectedSourceLinks: [...testCase.expectedSourceLinks, link.trim()]\n });\n }\n }\n }\n\n private removeSourceLink(testCaseId: string, index: number) {\n const testCase = this.testCases.find(tc => tc.id === testCaseId);\n if (testCase) {\n const newLinks = testCase.expectedSourceLinks.filter((_, i) => i !== index);\n this.updateTestCase(testCaseId, { expectedSourceLinks: newLinks });\n }\n }\n\n private async evaluateResponse(testCase: TestCase): Promise<void> {\n if (!testCase.output) {\n console.warn('⚠️ No output to evaluate for test case:', testCase.id);\n return;\n }\n\n const evaluationRequest: EvaluationRequest = {\n testCaseId: testCase.id,\n question: testCase.question,\n expectedKeywords: testCase.expectedKeywords,\n expectedSourceLinks: testCase.expectedSourceLinks,\n actualResponse: testCase.output\n };\n\n await this.evaluationEngine.evaluateResponse(evaluationRequest, (result: EvaluationResult) => {\n console.log('📊 Evaluation result received:', result);\n \n this.updateTestCase(testCase.id, {\n evaluationResult: result\n });\n });\n }\n\n private async runAllTests() {\n this.isRunningAll = true;\n const tasks = [];\n for (const testCase of this.testCases) {\n if (!testCase.isRunning && testCase.question.trim()) {\n tasks.push(() => this.runSingleTest(testCase).catch(err => {\n console.error(`⚠️ Test case ${testCase.id} failed`, err);\n }))\n }\n }\n try {\n const fetcher = new RateLimitedFetcher(this.delayMs);\n await fetcher.runAll(tasks);\n } catch (err) {\n console.error('⚠️ Error running all tests:', err);\n }\n this.isRunningAll = false;\n }\n\n private generateId(): string {\n return uuidv4();\n }\n\n private handleFileSelect(): void {\n this.fileInput.click();\n }\n\n private async handleFileChange(event: Event): Promise<void> {\n const target = event.target as HTMLInputElement;\n const file = target.files?.[0];\n\n // Immediately clear the input value to allow for a new upload.\n target.value = '';\n\n if (!file) {\n this.error = 'No file selected.';\n return;\n }\n\n const isJsonType = file.type === 'application/json';\n const isJsonExtension = file.name.toLowerCase().endsWith('.json');\n if (!isJsonType && !isJsonExtension) {\n this.error = 'Invalid file type. Please select a JSON file.';\n return;\n }\n\n this.error = '';\n\n try {\n const content = await this.readFileAsync(file);\n const fileContent = JSON.parse(content);\n\n if (!Array.isArray(fileContent)) {\n throw new Error(\"Invalid JSON structure. Expected a JSON array.\");\n } \n const importedTestCases: TestCase[] = fileContent.map((item) => ({\n id: this.generateId(),\n question: item.question || '',\n expectedKeywords: Array.isArray(item.expectedKeywords) ? item.expectedKeywords : [],\n expectedSourceLinks: Array.isArray(item.expectedSourceLinks) ? item.expectedSourceLinks : [],\n isRunning: false\n }));\n this.testCases = importedTestCases; \n } catch (err) {\n this.error = err?.message || 'Error processing file. Please ensure it is a valid JSON array.';\n console.error('File Processing Error:', err);\n }\n }\n\n private readFileAsync(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(reader.error);\n reader.readAsText(file);\n });\n }\n\n private downloadFile(content: string, filename: string, mimeType: string) {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n private async handleExportTestSuite() {\n this.isExportingTestSuite = true;\n try {\n // Exporting only input data (question, expected keywords, expected source links)\n const exportData = this.testCases.map(testCase => ({\n id: testCase.id,\n question: testCase.question,\n expectedKeywords: testCase.expectedKeywords,\n expectedSourceLinks: testCase.expectedSourceLinks\n }));\n\n const jsonContent = JSON.stringify(exportData, null, 2);\n\n // Added a small delay to show the loading state\n await new Promise(resolve => setTimeout(resolve, 500));\n\n this.downloadFile(jsonContent, 'test-suite.json', 'application/json');\n } finally {\n this.isExportingTestSuite = false;\n }\n }\n\n private async handleExportTestResults() {\n this.isExportingTestResults = true;\n try {\n // Create CSV content with the required fields\n const csvRows = [];\n \n // Add header row\n const headers = [\n 'Question',\n 'Expected Keywords',\n 'Generated Keywords',\n 'Keywords Match',\n 'Expected Source Links',\n 'Generated Source Links',\n 'Source Links Match',\n 'Response Time (s)'\n ];\n csvRows.push(headers.join(','));\n \n // Add data rows\n this.testCases.forEach(testCase => {\n const expectedKeywords = testCase.expectedKeywords.join('; ');\n const expectedSourceLinks = testCase.expectedSourceLinks.join('; ');\n \n let generatedKeywords = '';\n let generatedSourceLinks = '';\n let keywordsMatch = '';\n let sourceLinksMatch = '';\n \n if (testCase.evaluationResult) {\n const foundKeywords = testCase.evaluationResult.keywordMatches\n .filter(match => match.found)\n .map(match => match.keyword);\n const foundSourceLinks = testCase.evaluationResult.sourceLinkMatches\n .filter(match => match.found)\n .map(match => match.link);\n \n generatedKeywords = foundKeywords.join('; ');\n generatedSourceLinks = foundSourceLinks.join('; ');\n \n // Calculate match percentages\n const keywordMatchCount = testCase.evaluationResult.keywordMatches.filter(m => m.found).length;\n const totalKeywords = testCase.evaluationResult.keywordMatches.length;\n const sourceLinkMatchCount = testCase.evaluationResult.sourceLinkMatches.filter(m => m.found).length;\n const totalSourceLinks = testCase.evaluationResult.sourceLinkMatches.length;\n \n keywordsMatch = totalKeywords > 0 ? `${keywordMatchCount}/${totalKeywords}` : 'N/A';\n sourceLinksMatch = totalSourceLinks > 0 ? `${sourceLinkMatchCount}/${totalSourceLinks}` : 'N/A';\n }\n \n const responseTime = testCase.responseTime ? (testCase.responseTime / 1000).toFixed(3) : 'N/A';\n \n const row = [\n this.escapeCsvField(testCase.question),\n this.escapeCsvField(expectedKeywords),\n this.escapeCsvField(generatedKeywords),\n keywordsMatch,\n this.escapeCsvField(expectedSourceLinks),\n this.escapeCsvField(generatedSourceLinks), \n sourceLinksMatch,\n responseTime\n ];\n \n csvRows.push(row.join(','));\n });\n \n const csvContent = csvRows.join('\\n');\n \n // Added a small delay to show the loading state\n await new Promise(resolve => setTimeout(resolve, 500));\n \n this.downloadFile(csvContent, 'test-results.csv', 'text/csv');\n } finally {\n this.isExportingTestResults = false;\n }\n }\n\n private escapeCsvField(field: string): string {\n if (field.includes(',') || field.includes('\"') || field.includes('\\n')) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n }\n\n render() {\n return (\n <div class=\"test-runner-container\">\n <header class=\"test-runner-header\">\n <div class=\"header-left\">\n <input\n class=\"hidden\"\n type=\"file\"\n ref={(el) => (this.fileInput = el as HTMLInputElement)}\n onChange={(e) => this.handleFileChange(e)}\n accept=\".json,application/json\"\n />\n <button class=\"btn btn-secondary\" onClick={() => this.handleFileSelect()}>\n <span class=\"icon\">↑</span>\n Import Test Suite\n </button>\n <button\n class=\"btn btn-secondary\"\n onClick={() => this.handleExportTestSuite()}\n disabled={this.isExportingTestSuite}\n >\n <span class=\"icon\">\n {this.isExportingTestSuite ? '⏳' : '↓'}\n </span>\n {this.isExportingTestSuite ? 'Exporting...' : 'Export Test Suite'}\n </button>\n </div>\n \n <div class=\"header-right\">\n <button class=\"btn btn-secondary\">\n <span class=\"icon\">⚙️</span>\n Prompt Editor\n </button>\n <button \n class=\"btn btn-secondary\"\n onClick={() => this.handleExportTestResults()}\n disabled={this.isExportingTestResults}\n >\n <span class=\"icon\">\n {this.isExportingTestResults ? '⏳' : '↓'}\n </span>\n {this.isExportingTestResults ? 'Exporting...' : 'Export Test Results'}\n </button>\n <button \n class=\"btn btn-primary\" \n onClick={() => this.runAllTests()}\n disabled={this.isRunningAll}\n >\n {this.isRunningAll ? 'Running...' : 'Run All'}\n </button>\n </div>\n </header>\n <ErrorMessage\n message={this.error}\n onClear={() => (this.error = '')}\n />\n <div class=\"test-runner-content\">\n <div class=\"column-headers\">\n <div class=\"column-header\">Input</div>\n <div class=\"column-header\">Output</div>\n <div class=\"column-header\">Evaluation</div>\n <div class=\"column-header\">Actions</div>\n </div>\n\n <div class=\"test-cases\">\n {this.testCases.map((testCase) => (\n <div class=\"test-case-row\" key={testCase.id}>\n <div class=\"input-column\">\n <div class=\"input-group\">\n <label>Question</label>\n <textarea\n value={testCase.question}\n onInput={(e) => this.updateTestCase(testCase.id, { \n question: (e.target as HTMLTextAreaElement).value \n })}\n placeholder=\"Enter your question here...\"\n rows={3}\n ></textarea>\n </div>\n \n <div class=\"keywords-group\">\n <label>Expected keywords</label>\n <div class=\"tags-container\">\n {testCase.expectedKeywords.map((keyword, index) => (\n <span class=\"tag\" key={index}>\n {keyword}\n <button \n class=\"tag-remove\" \n onClick={() => this.removeKeyword(testCase.id, index)}\n >×</button>\n </span>\n ))}\n <input\n type=\"text\"\n placeholder=\"New item...\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n this.addKeyword(testCase.id, (e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n </div>\n \n <div class=\"links-group\">\n <label>Expected source links</label>\n <div class=\"links-container\">\n {testCase.expectedSourceLinks.map((link, index) => (\n <div class=\"link-item\" key={index}>\n <a href={link} target=\"_blank\" rel=\"noopener noreferrer\">{link}</a>\n <button \n class=\"link-remove\" \n onClick={() => this.removeSourceLink(testCase.id, index)}\n >×</button>\n </div>\n ))}\n <input\n type=\"url\"\n placeholder=\"New item...\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n this.addSourceLink(testCase.id, (e.target as HTMLInputElement).value);\n (e.target as HTMLInputElement).value = '';\n }\n }}\n />\n </div>\n </div>\n </div>\n\n <div class=\"output-column\">\n {testCase.output ? (\n <div class=\"output-content\">\n {testCase.output}\n </div>\n ) : (\n <div class=\"output-placeholder\">\n {testCase.isRunning ? 'Running...' : ''}\n </div>\n )}\n </div>\n\n <div class=\"evaluation-column\">\n {testCase.evaluationResult ? (\n <div class=\"evaluation-result\">\n <div class={`evaluation-status ${testCase.evaluationResult.passed ? 'passed' : 'failed'}`}>\n {testCase.evaluationResult.passed ? '✅ PASSED' : '❌ FAILED'}\n </div>\n <div class=\"evaluation-details\">\n Keywords: {testCase.evaluationResult.keywordMatches.filter(m => m.found).length}/{testCase.evaluationResult.keywordMatches.length} found\n </div>\n </div>\n ) : (\n <div class=\"evaluation-placeholder\">\n {testCase.isRunning ? 'Evaluating...' : ''}\n </div>\n )}\n </div>\n\n <div class=\"actions-column\">\n <button \n class=\"btn btn-icon btn-run\"\n onClick={() => this.runSingleTest(testCase)}\n disabled={testCase.isRunning || !testCase.question.trim()}\n title={!testCase.question.trim() ? \"Enter a question first\" : \"Run this test\"}\n >\n {testCase.isRunning ? '⏳' : '▶️'}\n </button>\n <button \n class=\"btn btn-icon btn-delete\"\n onClick={() => this.deleteTestCase(testCase.id)}\n title=\"Delete this test\"\n >\n 🗑️\n </button>\n </div>\n </div>\n ))}\n </div>\n\n <div class=\"add-test-case\">\n <button class=\"btn btn-outline\" onClick={() => this.addNewTestCase()}>\n + Add Question\n </button>\n </div>\n </div>\n </div>\n );\n }\n}"],"names":["uuidv4"],"mappings":"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","x_google_ignoreList":[1,2,3,4]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { p as promiseResolve, b as bootstrapLazy } from './index-
|
|
2
|
-
export { s as setNonce } from './index-
|
|
3
|
-
import { g as globalScripts } from './app-globals-
|
|
1
|
+
import { p as promiseResolve, b as bootstrapLazy } from './index-cncubhtM.js';
|
|
2
|
+
export { s as setNonce } from './index-cncubhtM.js';
|
|
3
|
+
import { g as globalScripts } from './app-globals-BOQOUavG.js';
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
|
-
Stencil Client Patch Browser v4.38.
|
|
6
|
+
Stencil Client Patch Browser v4.38.2 | MIT Licensed | https://stenciljs.com
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
var patchBrowser = () => {
|
|
@@ -17,6 +17,6 @@ var patchBrowser = () => {
|
|
|
17
17
|
|
|
18
18
|
patchBrowser().then(async (options) => {
|
|
19
19
|
await globalScripts();
|
|
20
|
-
return bootstrapLazy([["llm-test-runner",[[257,"llm-test-runner",{"
|
|
20
|
+
return bootstrapLazy([["llm-test-runner",[[257,"llm-test-runner",{"delayMs":[2,"delay-ms"],"testCases":[32],"isRunningAll":[32],"error":[32],"isExportingTestSuite":[32],"isExportingTestResults":[32]}]]]], options);
|
|
21
21
|
});
|
|
22
22
|
//# sourceMappingURL=llm-testrunner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-testrunner.js","sources":["../../node_modules/@stencil/core/internal/client/patch-browser.js","@lazy-browser-entrypoint?app-data=conditional"],"sourcesContent":["/*\n Stencil Client Patch Browser v4.38.
|
|
1
|
+
{"version":3,"file":"llm-testrunner.js","sources":["../../node_modules/@stencil/core/internal/client/patch-browser.js","@lazy-browser-entrypoint?app-data=conditional"],"sourcesContent":["/*\n Stencil Client Patch Browser v4.38.2 | MIT Licensed | https://stenciljs.com\n */\n\n// src/client/client-patch-browser.ts\nimport { BUILD, NAMESPACE } from \"@stencil/core/internal/app-data\";\nimport { consoleDevInfo, H, promiseResolve, win } from \"@stencil/core\";\nvar patchBrowser = () => {\n if (BUILD.isDev && !BUILD.isTesting) {\n consoleDevInfo(\"Running in development mode.\");\n }\n if (BUILD.cloneNodeFix) {\n patchCloneNodeFix(H.prototype);\n }\n const scriptElm = BUILD.scriptDataOpts ? win.document && Array.from(win.document.querySelectorAll(\"script\")).find(\n (s) => new RegExp(`/${NAMESPACE}(\\\\.esm)?\\\\.js($|\\\\?|#)`).test(s.src) || s.getAttribute(\"data-stencil-namespace\") === NAMESPACE\n ) : null;\n const importMeta = import.meta.url;\n const opts = BUILD.scriptDataOpts ? (scriptElm || {})[\"data-opts\"] || {} : {};\n if (importMeta !== \"\") {\n opts.resourcesUrl = new URL(\".\", importMeta).href;\n }\n return promiseResolve(opts);\n};\nvar patchCloneNodeFix = (HTMLElementPrototype) => {\n const nativeCloneNodeFn = HTMLElementPrototype.cloneNode;\n HTMLElementPrototype.cloneNode = function(deep) {\n if (this.nodeName === \"TEMPLATE\") {\n return nativeCloneNodeFn.call(this, deep);\n }\n const clonedNode = nativeCloneNodeFn.call(this, false);\n const srcChildNodes = this.childNodes;\n if (deep) {\n for (let i = 0; i < srcChildNodes.length; i++) {\n if (srcChildNodes[i].nodeType !== 2) {\n clonedNode.appendChild(srcChildNodes[i].cloneNode(true));\n }\n }\n }\n return clonedNode;\n };\n};\nexport {\n patchBrowser\n};\n","export { setNonce } from '@stencil/core';\nimport { bootstrapLazy } from '@stencil/core';\nimport { patchBrowser } from '@stencil/core/internal/client/patch-browser';\nimport { globalScripts } from '@stencil/core/internal/app-globals';\npatchBrowser().then(async (options) => {\n await globalScripts();\n return bootstrapLazy([/*!__STENCIL_LAZY_DATA__*/], options);\n});\n"],"names":[],"mappings":";;;;AAAA;AACA;AACA;;AAKA,IAAI,YAAY,GAAG,MAAM;AAUzB,EAAE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG;AACpC,EAAE,MAAM,IAAI,GAAiE,EAAE;AAC/E,EAAE,IAAI,UAAU,KAAK,EAAE,EAAE;AACzB,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,IAAI;AACrD;AACA,EAAE,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC;;ACnBD,YAAY,EAAE,CAAC,IAAI,CAAC,OAAO,OAAO,KAAK;AACvC,EAAE,MAAM,aAAa,EAAE;AACvB,EAAE,OAAO,aAAa,CAAC,4BAA4B,EAAE,OAAO,CAAC;AAC7D,CAAC,CAAC","x_google_ignoreList":[0]}
|
package/dist/esm/loader.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { b as bootstrapLazy } from './index-
|
|
2
|
-
export { s as setNonce } from './index-
|
|
3
|
-
import { g as globalScripts } from './app-globals-
|
|
1
|
+
import { b as bootstrapLazy } from './index-cncubhtM.js';
|
|
2
|
+
export { s as setNonce } from './index-cncubhtM.js';
|
|
3
|
+
import { g as globalScripts } from './app-globals-BOQOUavG.js';
|
|
4
4
|
|
|
5
5
|
const defineCustomElements = async (win, options) => {
|
|
6
6
|
if (typeof window === 'undefined') return undefined;
|
|
7
7
|
await globalScripts();
|
|
8
|
-
return bootstrapLazy([["llm-test-runner",[[257,"llm-test-runner",{"
|
|
8
|
+
return bootstrapLazy([["llm-test-runner",[[257,"llm-test-runner",{"delayMs":[2,"delay-ms"],"testCases":[32],"isRunningAll":[32],"error":[32],"isExportingTestSuite":[32],"isExportingTestResults":[32]}]]]], options);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export { defineCustomElements };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{h as n,r as e}from"./p-DBp-rMPb.js";class t{constructor(){}async evaluateResponse(n,e){try{console.log("🔍 Starting evaluation for test case:",n.testCaseId);const t=await this.performEvaluation(n);console.log("Evaluation completed for test case:",n.testCaseId);console.log("Result:",t);e(t)}catch(t){console.error("Evaluation failed:",t);const o={testCaseId:n.testCaseId,passed:false,keywordMatches:[],sourceLinkMatches:[],timestamp:(new Date).toISOString()};e(o)}}async performEvaluation(n){const{testCaseId:e,expectedKeywords:t,expectedSourceLinks:o,actualResponse:a}=n;const r=this.evaluateKeywords(t,a);const i=this.evaluateSourceLinks(o,a);const s=r.length+i.length;const d=r.filter((n=>n.found)).length+i.filter((n=>n.found)).length;const c=d===s;return{testCaseId:e,passed:c,keywordMatches:r,sourceLinkMatches:i,timestamp:(new Date).toISOString()}}evaluateKeywords(n,e){const t=e.toLowerCase();return n.map((n=>{const e=n.toLowerCase();const o=t.includes(e);return{keyword:n,found:o}}))}evaluateSourceLinks(n,e){return n.map((n=>{const t=e.includes(n);return{link:n,found:t}}))}}var o=[];for(var a=0;a<256;++a){o.push((a+256).toString(16).slice(1))}function r(n,e=0){return(o[n[e+0]]+o[n[e+1]]+o[n[e+2]]+o[n[e+3]]+"-"+o[n[e+4]]+o[n[e+5]]+"-"+o[n[e+6]]+o[n[e+7]]+"-"+o[n[e+8]]+o[n[e+9]]+"-"+o[n[e+10]]+o[n[e+11]]+o[n[e+12]]+o[n[e+13]]+o[n[e+14]]+o[n[e+15]]).toLowerCase()}var i;var s=new Uint8Array(16);function d(){if(!i){i=typeof crypto!=="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto);if(!i){throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported")}}return i(s)}var c=typeof crypto!=="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto);var l={randomUUID:c};function p(n,e,t){if(l.randomUUID&&true&&!n){return l.randomUUID()}n=n||{};var o=n.random||(n.rng||d)();o[6]=o[6]&15|64;o[8]=o[8]&63|128;return r(o)}const u=({message:e,onClear:t})=>{if(!e){return null}return n("div",{class:"error-message"},n("span",null,e),t&&n("button",{class:"error-close",title:"Close",onClick:t},"×"))};const b=":host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n background-color: #f8fafc;\n min-height: 100vh;\n}\n\n.test-runner-container {\n padding: 20px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n margin: 20px 0;\n}\n\n.simple-test {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.test-cases {\n margin: 20px 0;\n}\n\n.test-case {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #eee;\n border-radius: 4px;\n background: #f9f9f9;\n}\n\n.test-case h3 {\n margin-top: 0;\n color: #333;\n}\n\n.test-case textarea {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: inherit;\n margin: 10px 0;\n}\n\n.test-case button {\n background: #007bff;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 4px;\n cursor: pointer;\n margin: 10px 5px 10px 0;\n}\n\n.test-case button:disabled {\n background: #ccc;\n cursor: not-allowed;\n}\n\n.output, .error {\n margin: 10px 0;\n padding: 10px;\n border-radius: 4px;\n}\n\n.output {\n background: #d4edda;\n border: 1px solid #c3e6cb;\n color: #155724;\n}\n\n.error {\n background: #f8d7da;\n border: 1px solid #f5c6cb;\n color: #721c24;\n}\n\n.test-runner-container {\n max-width: 1400px;\n margin: 0 auto;\n background: white;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);\n}\n\n/* Header Styles */\n.test-runner-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px 24px;\n background: /*linear-gradient(135deg, #667eea 0%, #764ba2 100%);*/ white;\n color: white;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.header-left, .header-right {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.header-center {\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.api-status {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.api-status-text {\n color: #28a745;\n font-weight: 500;\n font-size: 0.9rem;\n}\n\n/* Button Styles */\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n text-decoration: none;\n position: relative;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none !important;\n}\n\n.btn-primary {\n color: black;\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);\n}\n\n.btn-primary:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);\n}\n\n.btn-secondary {\n background: rgba(255, 255, 255, 0.2);\n color: blue;\n border: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.btn-outline {\n background: transparent;\n color: #6b7280;\n border: 2px solid #e5e7eb;\n}\n\n.btn-outline:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n transform: translateY(-1px);\n}\n\n.btn-icon {\n padding: 8px;\n min-width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n}\n\n.btn-run {\n color: white;\n}\n\n.btn-run:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.btn-delete {\n color: white;\n}\n\n.btn-delete:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.icon {\n font-size: 16px;\n}\n\n/* Main Content */\n.test-runner-content {\n padding: 0;\n}\n\n/* Column Headers */\n.column-headers {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n background: #e5e7eb;\n border-bottom: 2px solid #d1d5db;\n}\n\n.column-header {\n background: #f8fafc;\n padding: 16px 20px;\n font-weight: 600;\n color: #374151;\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Test Cases */\n.test-cases {\n background: white;\n}\n\n.test-case-row {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n border-bottom: 1px solid #e5e7eb;\n min-height: 200px;\n}\n\n.test-case-row:hover {\n background: #f9fafb;\n}\n\n/* Input Column */\n.input-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n}\n\n.input-group {\n margin-bottom: 16px;\n}\n\n.input-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.input-group textarea {\n width: 95%;\n padding: 12px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n font-size: 14px;\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n}\n\n.input-group textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n/* Keywords and Links */\n.keywords-group, .links-group {\n margin-bottom: 16px;\n}\n\n.keywords-group label, .links-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.tags-container, .links-container {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.tag {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: #dbeafe;\n color: #1e40af;\n padding: 4px 8px;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.tag-remove {\n background: none;\n border: none;\n color: #1e40af;\n cursor: pointer;\n font-size: 14px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.tag-remove:hover {\n background: rgba(30, 64, 175, 0.1);\n}\n\n.link-item {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #f0f9ff;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n}\n\n.link-item a {\n color: #0369a1;\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link-item a:hover {\n text-decoration: underline;\n}\n\n.link-remove {\n background: none;\n border: none;\n color: #0369a1;\n cursor: pointer;\n font-size: 12px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.link-remove:hover {\n background: rgba(3, 105, 161, 0.1);\n}\n\n.tags-container input, .links-container input {\n border: 1px solid #d1d5db;\n border-radius: 6px;\n padding: 6px 8px;\n font-size: 12px;\n outline: none;\n min-width: 120px;\n}\n\n.tags-container input:focus, .links-container input:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n\n/* Output Column */\n.output-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.output-content {\n background: #f8fafc;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 16px;\n font-size: 14px;\n line-height: 1.6;\n color: #374151;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex: 1;\n overflow-y: auto;\n max-height: 250px;\n overflow-x: scroll;\n}\n\n.output-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* Evaluation Column */\n.evaluation-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.evaluation-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n flex: 1;\n}\n\n.score-display {\n text-align: center;\n}\n\n.score-number {\n font-size: 24px;\n font-weight: 700;\n color: #111827;\n display: block;\n margin-bottom: 8px;\n}\n\n.score-bar {\n width: 100%;\n height: 8px;\n background: #e5e7eb;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.score-fill {\n height: 100%;\n background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);\n transition: width 0.3s ease;\n}\n\n.evaluation-details {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.detail-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.status {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n}\n\n.status.pass {\n background: #dcfce7;\n color: #166534;\n}\n\n.status.fail {\n background: #fef2f2;\n color: #dc2626;\n}\n\n.evaluation-text {\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n background: #f9fafb;\n padding: 8px;\n border-radius: 6px;\n border: 1px solid #e5e7eb;\n}\n\n.evaluation-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* New evaluation result styles */\n.evaluation-result {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.evaluation-status {\n font-weight: 600;\n font-size: 14px;\n padding: 8px 12px;\n border-radius: 4px;\n text-align: center;\n}\n\n.evaluation-status.passed {\n background: #d4edda;\n color: #155724;\n border: 1px solid #c3e6cb;\n}\n\n.evaluation-status.failed {\n background: #f8d7da;\n color: #721c24;\n border: 1px solid #f5c6cb;\n}\n\n.evaluation-score {\n font-size: 12px;\n color: #495057;\n text-align: center;\n font-weight: 500;\n}\n\n.evaluation-feedback {\n font-size: 12px;\n color: #6c757d;\n background: #f8f9fa;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #dee2e6;\n}\n\n/* Actions Column */\n.actions-column {\n padding: 20px;\n background: white;\n display: flex;\n flex-direction: column;\n gap: 12px;\n align-items: center;\n justify-content: flex-start;\n align-self: flex-start;\n}\n\n/* Add Test Case */\n.add-test-case {\n padding: 24px;\n text-align: center;\n background: #f9fafb;\n border-top: 1px solid #e5e7eb;\n}\n\n.hidden {\n display: none;\n}\n\n.error-message {\n background: #ffeaea;\n color: #b71c1c;\n border: 1px solid #f44336;\n padding: 0.75em 2.5em 0.75em 1em;\n border-radius: 4px;\n margin: 1em 0;\n position: relative;\n font-size: 1em;\n display: flex;\n align-items: center;\n gap: 1em;\n}\n\n.error-close {\n background: none;\n border: none;\n color: #b71c1c;\n font-size: 1.25em;\n font-weight: bold;\n cursor: pointer;\n position: absolute;\n right: 0.75em;\n top: 50%;\n transform: translateY(-50%);\n line-height: 1;\n padding: 0;\n}\n\n/* Responsive Design */\n@media (max-width: 1200px) {\n .test-case-row {\n grid-template-columns: 1fr;\n gap: 0;\n }\n \n .column-headers {\n display: none;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n border-right: none;\n border-bottom: 1px solid #e5e7eb;\n }\n \n .actions-column {\n flex-direction: row;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .test-runner-header {\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n }\n \n .header-left, .header-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n \n .btn {\n font-size: 12px;\n padding: 8px 12px;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n padding: 16px;\n }\n \n .test-case-row {\n min-height: auto;\n }\n}";const f=class{constructor(n){e(this,n)}apiKey;testCases=[{id:"1",question:"",expectedKeywords:[],expectedSourceLinks:[],isRunning:false}];isRunningAll=false;error="";fileInput;isExportingTestSuite=false;evaluationEngine;apiUrl="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent";async componentWillLoad(){this.evaluationEngine=new t;console.log("🔍 componentWillLoad - apiKey:",this.apiKey?"SET":"NOT SET");console.log("🔍 componentWillLoad - apiKey value:",this.apiKey);if(!this.apiKey){throw new Error('API key is required. Please provide the apiKey prop: <llm-test-runner apiKey="your-api-key" />')}}componentDidLoad(){}disconnectedCallback(){}addNewTestCase(){const n={id:this.generateId(),question:"",expectedKeywords:[],expectedSourceLinks:[],isRunning:false};this.testCases=[...this.testCases,n]}updateTestCase(n,e){this.testCases=this.testCases.map((t=>t.id===n?{...t,...e}:t))}async runSingleTest(n){console.log("🚀 Starting test for question:",n.question);this.updateTestCase(n.id,{isRunning:true});try{const e=await this.callGeminiAPI(n.question);console.log("✅ AI call completed for test case:",n.id);this.updateTestCase(n.id,{isRunning:false,output:e,error:null});await this.evaluateResponse({...n,output:e})}catch(e){console.error("❌ Error in runSingleTest:",e);this.updateTestCase(n.id,{isRunning:false,output:null,error:e instanceof Error?e.message:"Unknown error"})}}deleteTestCase(n){this.testCases=this.testCases.filter((e=>e.id!==n))}addKeyword(n,e){if(e.trim()){const t=this.testCases.find((e=>e.id===n));if(t){this.updateTestCase(n,{expectedKeywords:[...t.expectedKeywords,e.trim()]})}}}removeKeyword(n,e){const t=this.testCases.find((e=>e.id===n));if(t){const o=t.expectedKeywords.filter(((n,t)=>t!==e));this.updateTestCase(n,{expectedKeywords:o})}}addSourceLink(n,e){if(e.trim()){const t=this.testCases.find((e=>e.id===n));if(t){this.updateTestCase(n,{expectedSourceLinks:[...t.expectedSourceLinks,e.trim()]})}}}removeSourceLink(n,e){const t=this.testCases.find((e=>e.id===n));if(t){const o=t.expectedSourceLinks.filter(((n,t)=>t!==e));this.updateTestCase(n,{expectedSourceLinks:o})}}async callGeminiAPI(n){console.log("🔍 callGeminiAPI - apiKey:",this.apiKey?"SET":"NOT SET");console.log("🔍 callGeminiAPI - apiKey value:",this.apiKey?`${this.apiKey.substring(0,10)}...`:"undefined");if(!this.apiKey){throw new Error("API key is required. Please provide the apiKey prop.")}const e={contents:[{parts:[{text:n}]}]};const t=await fetch(`${this.apiUrl}?key=${this.apiKey}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const n=await t.json().catch((()=>({})));throw new Error(n.error?.message||`HTTP error! status: ${t.status}`)}const o=await t.json();if(o.candidates&&o.candidates[0]&&o.candidates[0].content){return o.candidates[0].content.parts[0].text}else{throw new Error("Unexpected response format from Gemini API")}}async evaluateResponse(n){if(!n.output){console.warn("⚠️ No output to evaluate for test case:",n.id);return}const e={testCaseId:n.id,question:n.question,expectedKeywords:n.expectedKeywords,expectedSourceLinks:n.expectedSourceLinks,actualResponse:n.output};await this.evaluationEngine.evaluateResponse(e,(e=>{console.log("📊 Evaluation result received:",e);this.updateTestCase(n.id,{evaluationResult:e})}))}async runAllTests(){this.isRunningAll=true;for(const n of this.testCases){if(!n.isRunning&&n.question.trim()){await this.runSingleTest(n);await new Promise((n=>setTimeout(n,1e3)))}}this.isRunningAll=false}generateId(){return p()}handleFileSelect(){this.fileInput.click()}async handleFileChange(n){const e=n.target;const t=e.files?.[0];e.value="";if(!t){this.error="No file selected.";return}const o=t.type==="application/json";const a=t.name.toLowerCase().endsWith(".json");if(!o&&!a){this.error="Invalid file type. Please select a JSON file.";return}this.error="";try{const n=await this.readFileAsync(t);const e=JSON.parse(n);if(!Array.isArray(e)){throw new Error("Invalid JSON structure. Expected a JSON array.")}const o=e.map((n=>({id:this.generateId(),question:n.question||"",expectedKeywords:Array.isArray(n.expectedKeywords)?n.expectedKeywords:[],expectedSourceLinks:Array.isArray(n.expectedSourceLinks)?n.expectedSourceLinks:[],isRunning:false})));this.testCases=o}catch(n){this.error=n?.message||"Error processing file. Please ensure it is a valid JSON array.";console.error("File Processing Error:",n)}}readFileAsync(n){return new Promise(((e,t)=>{const o=new FileReader;o.onload=()=>e(o.result);o.onerror=()=>t(o.error);o.readAsText(n)}))}downloadFile(n,e,t){const o=new Blob([n],{type:t});const a=URL.createObjectURL(o);const r=document.createElement("a");r.href=a;r.download=e;document.body.appendChild(r);r.click();document.body.removeChild(r);URL.revokeObjectURL(a)}async handleExportTestSuite(){this.isExportingTestSuite=true;try{const n=this.testCases.map((n=>({id:n.id,question:n.question,expectedKeywords:n.expectedKeywords,expectedSourceLinks:n.expectedSourceLinks})));const e=JSON.stringify(n,null,2);await new Promise((n=>setTimeout(n,500)));this.downloadFile(e,"test-suite.json","application/json")}finally{this.isExportingTestSuite=false}}render(){return n("div",{key:"dc5b661334cb4d4bb9bf51fb25363fe08ad938e3",class:"test-runner-container"},n("header",{key:"eede0d7f00c0bf5ff794fe2e53bad7a239f2d4d4",class:"test-runner-header"},n("div",{key:"19327ae6f7408843110a10f791e00ffdf3da6d64",class:"header-left"},n("input",{key:"b24227b27dc0f562318a67ca9c38b8793b55c644",class:"hidden",type:"file",ref:n=>this.fileInput=n,onChange:n=>this.handleFileChange(n),accept:".json,application/json"}),n("button",{key:"65adec40d3787115546c9b59e8691bc89c55c688",class:"btn btn-secondary",onClick:()=>this.handleFileSelect()},n("span",{key:"6fd40e85b4c99232330242b41199df1c4f947351",class:"icon"},"↑"),"Import Test Suite"),n("button",{key:"d7c4d6b7e3e9bb6951395a3c2c10b964ef2d2fca",class:"btn btn-secondary",onClick:()=>this.handleExportTestSuite(),disabled:this.isExportingTestSuite},n("span",{key:"bab67984c37b2744f35b4b2c34bfa3a500d196fb",class:"icon"},this.isExportingTestSuite?"⏳":"↓"),this.isExportingTestSuite?"Exporting...":"Export Test Suite")),n("div",{key:"74a7d6e148311151de86a66d7b146bd0d01c215c",class:"header-right"},n("button",{key:"3e9c33ef795755ebdf11fafa68801294b61a03d1",class:"btn btn-secondary"},n("span",{key:"89b3582178782392cbea1309ba32b5edc57f9392",class:"icon"},"⚙️"),"Prompt Editor"),n("button",{key:"e68e8edd5f532c7a8ec87746fa6d393c4297bc36",class:"btn btn-secondary"},n("span",{key:"7298d968bbc108cf060a0c309c9a8b2bce88aa90",class:"icon"},"↓"),"Export Test Results"),n("button",{key:"fb7d10cf9abda9968a2502bd8b4bf8e4cade11e8",class:"btn btn-primary",onClick:()=>this.runAllTests(),disabled:this.isRunningAll},this.isRunningAll?"Running...":"Run All"))),n(u,{key:"71eb4e561018a176a8c944107d734c32cd74334b",message:this.error,onClear:()=>this.error=""}),n("div",{key:"c120a8e40c6c6721dacd400266d4cd6270057c66",class:"test-runner-content"},n("div",{key:"7b864c6e0671d8be117b001fd87047fabdc851c0",class:"column-headers"},n("div",{key:"fcd6ae26d7b98422d800c4e3a4bcec587187be2b",class:"column-header"},"Input"),n("div",{key:"2855a067b11f8f9ddaf489daffb8a9fd2a317a44",class:"column-header"},"Output"),n("div",{key:"8b6d75b16005d19bacd8fa6d2008fa64c268e1ef",class:"column-header"},"Evaluation"),n("div",{key:"d14ea8cecb426c9692b16044124f3d23ddae671d",class:"column-header"},"Actions")),n("div",{key:"958aaba85391f7aadabd86c0395d0fe18b942ed4",class:"test-cases"},this.testCases.map((e=>n("div",{class:"test-case-row",key:e.id},n("div",{class:"input-column"},n("div",{class:"input-group"},n("label",null,"Question"),n("textarea",{value:e.question,onInput:n=>this.updateTestCase(e.id,{question:n.target.value}),placeholder:"Enter your question here...",rows:3})),n("div",{class:"keywords-group"},n("label",null,"Expected keywords"),n("div",{class:"tags-container"},e.expectedKeywords.map(((t,o)=>n("span",{class:"tag",key:o},t,n("button",{class:"tag-remove",onClick:()=>this.removeKeyword(e.id,o)},"×")))),n("input",{type:"text",placeholder:"New item...",onKeyDown:n=>{if(n.key==="Enter"){this.addKeyword(e.id,n.target.value);n.target.value=""}}}))),n("div",{class:"links-group"},n("label",null,"Expected source links"),n("div",{class:"links-container"},e.expectedSourceLinks.map(((t,o)=>n("div",{class:"link-item",key:o},n("a",{href:t,target:"_blank",rel:"noopener noreferrer"},t),n("button",{class:"link-remove",onClick:()=>this.removeSourceLink(e.id,o)},"×")))),n("input",{type:"url",placeholder:"New item...",onKeyDown:n=>{if(n.key==="Enter"){this.addSourceLink(e.id,n.target.value);n.target.value=""}}})))),n("div",{class:"output-column"},e.output?n("div",{class:"output-content"},e.output):n("div",{class:"output-placeholder"},e.isRunning?"Running...":"")),n("div",{class:"evaluation-column"},e.evaluationResult?n("div",{class:"evaluation-result"},n("div",{class:`evaluation-status ${e.evaluationResult.passed?"passed":"failed"}`},e.evaluationResult.passed?"✅ PASSED":"❌ FAILED"),n("div",{class:"evaluation-details"},"Keywords: ",e.evaluationResult.keywordMatches.filter((n=>n.found)).length,"/",e.evaluationResult.keywordMatches.length," found")):n("div",{class:"evaluation-placeholder"},e.isRunning?"Evaluating...":"")),n("div",{class:"actions-column"},n("button",{class:"btn btn-icon btn-run",onClick:()=>this.runSingleTest(e),disabled:e.isRunning||!e.question.trim(),title:!e.question.trim()?"Enter a question first":"Run this test"},e.isRunning?"⏳":"▶️"),n("button",{class:"btn btn-icon btn-delete",onClick:()=>this.deleteTestCase(e.id),title:"Delete this test"},"🗑️")))))),n("div",{key:"678932d3554c7ef2c86373bc4c972eb0f9894916",class:"add-test-case"},n("button",{key:"7f8447a4681214320647c55e06d1cfc0fc87b3fb",class:"btn btn-outline",onClick:()=>this.addNewTestCase()},"+ Add Question"))))}};f.style=b;export{f as LLMTestRunner};
|
|
1
|
+
import{h as n,r as e,c as t}from"./p-cncubhtM.js";class o{constructor(){}async evaluateResponse(n,e){try{console.log("🔍 Starting evaluation for test case:",n.testCaseId);const t=await this.performEvaluation(n);console.log("Evaluation completed for test case:",n.testCaseId);console.log("Result:",t);e(t)}catch(t){console.error("Evaluation failed:",t);const o={testCaseId:n.testCaseId,passed:false,keywordMatches:[],sourceLinkMatches:[],timestamp:(new Date).toISOString()};e(o)}}async performEvaluation(n){const{testCaseId:e,expectedKeywords:t,expectedSourceLinks:o,actualResponse:s}=n;const a=this.evaluateKeywords(t,s);const r=this.evaluateSourceLinks(o,s);const i=a.length+r.length;const d=a.filter((n=>n.found)).length+r.filter((n=>n.found)).length;const c=d===i;return{testCaseId:e,passed:c,keywordMatches:a,sourceLinkMatches:r,timestamp:(new Date).toISOString()}}evaluateKeywords(n,e){const t=e.toLowerCase();return n.map((n=>{const e=n.toLowerCase();const o=t.includes(e);return{keyword:n,found:o}}))}evaluateSourceLinks(n,e){return n.map((n=>{const t=e.includes(n);return{link:n,found:t}}))}}var s=[];for(var a=0;a<256;++a){s.push((a+256).toString(16).slice(1))}function r(n,e=0){return(s[n[e+0]]+s[n[e+1]]+s[n[e+2]]+s[n[e+3]]+"-"+s[n[e+4]]+s[n[e+5]]+"-"+s[n[e+6]]+s[n[e+7]]+"-"+s[n[e+8]]+s[n[e+9]]+"-"+s[n[e+10]]+s[n[e+11]]+s[n[e+12]]+s[n[e+13]]+s[n[e+14]]+s[n[e+15]]).toLowerCase()}var i;var d=new Uint8Array(16);function c(){if(!i){i=typeof crypto!=="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto);if(!i){throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported")}}return i(d)}var l=typeof crypto!=="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto);var p={randomUUID:l};function u(n,e,t){if(p.randomUUID&&true&&!n){return p.randomUUID()}n=n||{};var o=n.random||(n.rng||c)();o[6]=o[6]&15|64;o[8]=o[8]&63|128;return r(o)}const f=({message:e,onClear:t})=>{if(!e){return null}return n("div",{class:"error-message"},n("span",null,e),t&&n("button",{class:"error-close",title:"Close",onClick:t},"×"))};class b{queue=[];delay;intervalId;constructor(n){this.delay=n}startQueue(){if(this.intervalId)return;this.intervalId=setInterval((()=>{const n=this.queue.shift();if(n)n();if(this.queue.length===0){this.stop()}}),this.delay)}schedule(n){return new Promise(((e,t)=>{this.queue.push((()=>{n().then(e).catch(t)}));this.startQueue()}))}stop(){if(this.intervalId){clearInterval(this.intervalId);this.intervalId=undefined}}async runAll(n){const e=n.map((n=>this.schedule(n)));return Promise.all(e)}}const x=":host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n background-color: #f8fafc;\n min-height: 100vh;\n}\n\n.test-runner-container {\n padding: 20px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n margin: 20px 0;\n}\n\n.simple-test {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.test-cases {\n margin: 20px 0;\n}\n\n.test-case {\n margin: 20px 0;\n padding: 20px;\n border: 1px solid #eee;\n border-radius: 4px;\n background: #f9f9f9;\n}\n\n.test-case h3 {\n margin-top: 0;\n color: #333;\n}\n\n.test-case textarea {\n width: 100%;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: inherit;\n margin: 10px 0;\n}\n\n.test-case button {\n background: #007bff;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 4px;\n cursor: pointer;\n margin: 10px 5px 10px 0;\n}\n\n.test-case button:disabled {\n background: #ccc;\n cursor: not-allowed;\n}\n\n.output, .error {\n margin: 10px 0;\n padding: 10px;\n border-radius: 4px;\n}\n\n.output {\n background: #d4edda;\n border: 1px solid #c3e6cb;\n color: #155724;\n}\n\n.error {\n background: #f8d7da;\n border: 1px solid #f5c6cb;\n color: #721c24;\n}\n\n.test-runner-container {\n max-width: 1400px;\n margin: 0 auto;\n background: white;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);\n}\n\n/* Header Styles */\n.test-runner-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px 24px;\n background: /*linear-gradient(135deg, #667eea 0%, #764ba2 100%);*/ white;\n color: white;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.header-left, .header-right {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.header-center {\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.api-status {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.api-status-text {\n color: #28a745;\n font-weight: 500;\n font-size: 0.9rem;\n}\n\n/* Button Styles */\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n text-decoration: none;\n position: relative;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none !important;\n}\n\n.btn-primary {\n color: black;\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);\n}\n\n.btn-primary:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);\n}\n\n.btn-secondary {\n background: rgba(255, 255, 255, 0.2);\n color: blue;\n border: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.btn-outline {\n background: transparent;\n color: #6b7280;\n border: 2px solid #e5e7eb;\n}\n\n.btn-outline:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n transform: translateY(-1px);\n}\n\n.btn-icon {\n padding: 8px;\n min-width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n}\n\n.btn-run {\n color: white;\n}\n\n.btn-run:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.btn-delete {\n color: white;\n}\n\n.btn-delete:hover:not(:disabled) {\n transform: translateY(-1px);\n}\n\n.icon {\n font-size: 16px;\n}\n\n/* Main Content */\n.test-runner-content {\n padding: 0;\n}\n\n/* Column Headers */\n.column-headers {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n background: #e5e7eb;\n border-bottom: 2px solid #d1d5db;\n}\n\n.column-header {\n background: #f8fafc;\n padding: 16px 20px;\n font-weight: 600;\n color: #374151;\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* Test Cases */\n.test-cases {\n background: white;\n}\n\n.test-case-row {\n display: grid;\n grid-template-columns: 1fr 1.5fr 0.5fr 120px;\n gap: 1px;\n border-bottom: 1px solid #e5e7eb;\n min-height: 200px;\n}\n\n.test-case-row:hover {\n background: #f9fafb;\n}\n\n/* Input Column */\n.input-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n}\n\n.input-group {\n margin-bottom: 16px;\n}\n\n.input-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.input-group textarea {\n width: 95%;\n padding: 12px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n font-size: 14px;\n resize: vertical;\n outline: none;\n transition: border-color 0.2s ease;\n font-family: inherit;\n}\n\n.input-group textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n/* Keywords and Links */\n.keywords-group, .links-group {\n margin-bottom: 16px;\n}\n\n.keywords-group label, .links-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n font-size: 14px;\n}\n\n.tags-container, .links-container {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.tag {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: #dbeafe;\n color: #1e40af;\n padding: 4px 8px;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.tag-remove {\n background: none;\n border: none;\n color: #1e40af;\n cursor: pointer;\n font-size: 14px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.tag-remove:hover {\n background: rgba(30, 64, 175, 0.1);\n}\n\n.link-item {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #f0f9ff;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n}\n\n.link-item a {\n color: #0369a1;\n text-decoration: none;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.link-item a:hover {\n text-decoration: underline;\n}\n\n.link-remove {\n background: none;\n border: none;\n color: #0369a1;\n cursor: pointer;\n font-size: 12px;\n padding: 0;\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.link-remove:hover {\n background: rgba(3, 105, 161, 0.1);\n}\n\n.tags-container input, .links-container input {\n border: 1px solid #d1d5db;\n border-radius: 6px;\n padding: 6px 8px;\n font-size: 12px;\n outline: none;\n min-width: 120px;\n}\n\n.tags-container input:focus, .links-container input:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);\n}\n\n/* Output Column */\n.output-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.output-content {\n background: #f8fafc;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 16px;\n font-size: 14px;\n line-height: 1.6;\n color: #374151;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex: 1;\n overflow-y: auto;\n max-height: 250px;\n overflow-x: scroll;\n}\n\n.output-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* Evaluation Column */\n.evaluation-column {\n padding: 20px;\n background: white;\n border-right: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n}\n\n.evaluation-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n flex: 1;\n}\n\n.score-display {\n text-align: center;\n}\n\n.score-number {\n font-size: 24px;\n font-weight: 700;\n color: #111827;\n display: block;\n margin-bottom: 8px;\n}\n\n.score-bar {\n width: 100%;\n height: 8px;\n background: #e5e7eb;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.score-fill {\n height: 100%;\n background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);\n transition: width 0.3s ease;\n}\n\n.evaluation-details {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.detail-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.status {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n}\n\n.status.pass {\n background: #dcfce7;\n color: #166534;\n}\n\n.status.fail {\n background: #fef2f2;\n color: #dc2626;\n}\n\n.evaluation-text {\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n background: #f9fafb;\n padding: 8px;\n border-radius: 6px;\n border: 1px solid #e5e7eb;\n}\n\n.evaluation-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9ca3af;\n font-style: italic;\n flex: 1;\n background: #f9fafb;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n}\n\n/* New evaluation result styles */\n.evaluation-result {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.evaluation-status {\n font-weight: 600;\n font-size: 14px;\n padding: 8px 12px;\n border-radius: 4px;\n text-align: center;\n}\n\n.evaluation-status.passed {\n background: #d4edda;\n color: #155724;\n border: 1px solid #c3e6cb;\n}\n\n.evaluation-status.failed {\n background: #f8d7da;\n color: #721c24;\n border: 1px solid #f5c6cb;\n}\n\n.evaluation-score {\n font-size: 12px;\n color: #495057;\n text-align: center;\n font-weight: 500;\n}\n\n.evaluation-feedback {\n font-size: 12px;\n color: #6c757d;\n background: #f8f9fa;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #dee2e6;\n}\n\n/* Actions Column */\n.actions-column {\n padding: 20px;\n background: white;\n display: flex;\n flex-direction: column;\n gap: 12px;\n align-items: center;\n justify-content: flex-start;\n align-self: flex-start;\n}\n\n/* Add Test Case */\n.add-test-case {\n padding: 24px;\n text-align: center;\n background: #f9fafb;\n border-top: 1px solid #e5e7eb;\n}\n\n.hidden {\n display: none;\n}\n\n.error-message {\n background: #ffeaea;\n color: #b71c1c;\n border: 1px solid #f44336;\n padding: 0.75em 2.5em 0.75em 1em;\n border-radius: 4px;\n margin: 1em 0;\n position: relative;\n font-size: 1em;\n display: flex;\n align-items: center;\n gap: 1em;\n}\n\n.error-close {\n background: none;\n border: none;\n color: #b71c1c;\n font-size: 1.25em;\n font-weight: bold;\n cursor: pointer;\n position: absolute;\n right: 0.75em;\n top: 50%;\n transform: translateY(-50%);\n line-height: 1;\n padding: 0;\n}\n\n/* Responsive Design */\n@media (max-width: 1200px) {\n .test-case-row {\n grid-template-columns: 1fr;\n gap: 0;\n }\n \n .column-headers {\n display: none;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n border-right: none;\n border-bottom: 1px solid #e5e7eb;\n }\n \n .actions-column {\n flex-direction: row;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .test-runner-header {\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n }\n \n .header-left, .header-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n \n .btn {\n font-size: 12px;\n padding: 8px 12px;\n }\n \n .input-column, .output-column, .evaluation-column, .actions-column {\n padding: 16px;\n }\n \n .test-case-row {\n min-height: auto;\n }\n}";const h=class{constructor(n){e(this,n);this.llmRequest=t(this,"llmRequest")}llmRequest;delayMs=500;testCases=[{id:"1",question:"",expectedKeywords:[],expectedSourceLinks:[],isRunning:false}];isRunningAll=false;error="";fileInput;isExportingTestSuite=false;isExportingTestResults=false;evaluationEngine;componentWillLoad(){this.evaluationEngine=new o}componentDidLoad(){}disconnectedCallback(){}addNewTestCase(){const n={id:this.generateId(),question:"",expectedKeywords:[],expectedSourceLinks:[],isRunning:false};this.testCases=[...this.testCases,n]}updateTestCase(n,e){this.testCases=this.testCases.map((t=>t.id===n?{...t,...e}:t))}async runSingleTest(n){console.log("🚀 Starting test for question:",n.question);const e=Date.now();this.updateTestCase(n.id,{isRunning:true});return new Promise(((t,o)=>{this.llmRequest.emit({prompt:n.question,resolve:async o=>{console.log("✅ AI call completed for test case:",n.id);const s=Date.now();const a=s-e;this.updateTestCase(n.id,{isRunning:false,output:o,error:null,responseTime:a});await this.evaluateResponse({...n,output:o,responseTime:a});t()},reject:e=>{console.error("❌ Error in runSingleTest:",e);this.updateTestCase(n.id,{isRunning:false,output:null,error:e instanceof Error?e.message:"Unknown error"});o(e)}})}))}deleteTestCase(n){this.testCases=this.testCases.filter((e=>e.id!==n))}addKeyword(n,e){if(e.trim()){const t=this.testCases.find((e=>e.id===n));if(t){this.updateTestCase(n,{expectedKeywords:[...t.expectedKeywords,e.trim()]})}}}removeKeyword(n,e){const t=this.testCases.find((e=>e.id===n));if(t){const o=t.expectedKeywords.filter(((n,t)=>t!==e));this.updateTestCase(n,{expectedKeywords:o})}}addSourceLink(n,e){if(e.trim()){const t=this.testCases.find((e=>e.id===n));if(t){this.updateTestCase(n,{expectedSourceLinks:[...t.expectedSourceLinks,e.trim()]})}}}removeSourceLink(n,e){const t=this.testCases.find((e=>e.id===n));if(t){const o=t.expectedSourceLinks.filter(((n,t)=>t!==e));this.updateTestCase(n,{expectedSourceLinks:o})}}async evaluateResponse(n){if(!n.output){console.warn("⚠️ No output to evaluate for test case:",n.id);return}const e={testCaseId:n.id,question:n.question,expectedKeywords:n.expectedKeywords,expectedSourceLinks:n.expectedSourceLinks,actualResponse:n.output};await this.evaluationEngine.evaluateResponse(e,(e=>{console.log("📊 Evaluation result received:",e);this.updateTestCase(n.id,{evaluationResult:e})}))}async runAllTests(){this.isRunningAll=true;const n=[];for(const e of this.testCases){if(!e.isRunning&&e.question.trim()){n.push((()=>this.runSingleTest(e).catch((n=>{console.error(`⚠️ Test case ${e.id} failed`,n)}))))}}try{const e=new b(this.delayMs);await e.runAll(n)}catch(n){console.error("⚠️ Error running all tests:",n)}this.isRunningAll=false}generateId(){return u()}handleFileSelect(){this.fileInput.click()}async handleFileChange(n){const e=n.target;const t=e.files?.[0];e.value="";if(!t){this.error="No file selected.";return}const o=t.type==="application/json";const s=t.name.toLowerCase().endsWith(".json");if(!o&&!s){this.error="Invalid file type. Please select a JSON file.";return}this.error="";try{const n=await this.readFileAsync(t);const e=JSON.parse(n);if(!Array.isArray(e)){throw new Error("Invalid JSON structure. Expected a JSON array.")}const o=e.map((n=>({id:this.generateId(),question:n.question||"",expectedKeywords:Array.isArray(n.expectedKeywords)?n.expectedKeywords:[],expectedSourceLinks:Array.isArray(n.expectedSourceLinks)?n.expectedSourceLinks:[],isRunning:false})));this.testCases=o}catch(n){this.error=n?.message||"Error processing file. Please ensure it is a valid JSON array.";console.error("File Processing Error:",n)}}readFileAsync(n){return new Promise(((e,t)=>{const o=new FileReader;o.onload=()=>e(o.result);o.onerror=()=>t(o.error);o.readAsText(n)}))}downloadFile(n,e,t){const o=new Blob([n],{type:t});const s=URL.createObjectURL(o);const a=document.createElement("a");a.href=s;a.download=e;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(s)}async handleExportTestSuite(){this.isExportingTestSuite=true;try{const n=this.testCases.map((n=>({id:n.id,question:n.question,expectedKeywords:n.expectedKeywords,expectedSourceLinks:n.expectedSourceLinks})));const e=JSON.stringify(n,null,2);await new Promise((n=>setTimeout(n,500)));this.downloadFile(e,"test-suite.json","application/json")}finally{this.isExportingTestSuite=false}}async handleExportTestResults(){this.isExportingTestResults=true;try{const n=[];const e=["Question","Expected Keywords","Generated Keywords","Keywords Match","Expected Source Links","Generated Source Links","Source Links Match","Response Time (s)"];n.push(e.join(","));this.testCases.forEach((e=>{const t=e.expectedKeywords.join("; ");const o=e.expectedSourceLinks.join("; ");let s="";let a="";let r="";let i="";if(e.evaluationResult){const n=e.evaluationResult.keywordMatches.filter((n=>n.found)).map((n=>n.keyword));const t=e.evaluationResult.sourceLinkMatches.filter((n=>n.found)).map((n=>n.link));s=n.join("; ");a=t.join("; ");const o=e.evaluationResult.keywordMatches.filter((n=>n.found)).length;const d=e.evaluationResult.keywordMatches.length;const c=e.evaluationResult.sourceLinkMatches.filter((n=>n.found)).length;const l=e.evaluationResult.sourceLinkMatches.length;r=d>0?`${o}/${d}`:"N/A";i=l>0?`${c}/${l}`:"N/A"}const d=e.responseTime?(e.responseTime/1e3).toFixed(3):"N/A";const c=[this.escapeCsvField(e.question),this.escapeCsvField(t),this.escapeCsvField(s),r,this.escapeCsvField(o),this.escapeCsvField(a),i,d];n.push(c.join(","))}));const t=n.join("\n");await new Promise((n=>setTimeout(n,500)));this.downloadFile(t,"test-results.csv","text/csv")}finally{this.isExportingTestResults=false}}escapeCsvField(n){if(n.includes(",")||n.includes('"')||n.includes("\n")){return`"${n.replace(/"/g,'""')}"`}return n}render(){return n("div",{key:"beb2ab78108fede00c5d759d1ac5c98ae7f037d0",class:"test-runner-container"},n("header",{key:"d09b424069227500ffffcab8724b48d91705baab",class:"test-runner-header"},n("div",{key:"368ef117a1c2cc0be0446e2d373e38e0af1c2040",class:"header-left"},n("input",{key:"74346a634b36e9d04f75817495834983d970306c",class:"hidden",type:"file",ref:n=>this.fileInput=n,onChange:n=>this.handleFileChange(n),accept:".json,application/json"}),n("button",{key:"e095ff9de105cb281e60f70de02fb0de8a0f6478",class:"btn btn-secondary",onClick:()=>this.handleFileSelect()},n("span",{key:"6f828f297867ae54baf1f89c9dbce611eba2056a",class:"icon"},"↑"),"Import Test Suite"),n("button",{key:"5682dc8f70edac9fdf33b394677a6859555c039b",class:"btn btn-secondary",onClick:()=>this.handleExportTestSuite(),disabled:this.isExportingTestSuite},n("span",{key:"a1e8d3170ce39e499160e578e3e02d39cd0da9d7",class:"icon"},this.isExportingTestSuite?"⏳":"↓"),this.isExportingTestSuite?"Exporting...":"Export Test Suite")),n("div",{key:"af96ea230b04f4f6ce8e3488a1f8f7f2a01e74c0",class:"header-right"},n("button",{key:"a90bfb3421f5e2e5112b3642ddf81cf19372e0ad",class:"btn btn-secondary"},n("span",{key:"79611828e5dc1ad53998a74fca10a1a05c5d2a4e",class:"icon"},"⚙️"),"Prompt Editor"),n("button",{key:"451367ce26183a58add0902e1de2d29b4933add4",class:"btn btn-secondary",onClick:()=>this.handleExportTestResults(),disabled:this.isExportingTestResults},n("span",{key:"11f791f2071b58e0800d14ac7e6bebba67421449",class:"icon"},this.isExportingTestResults?"⏳":"↓"),this.isExportingTestResults?"Exporting...":"Export Test Results"),n("button",{key:"3cbf7626ccc5f171cdc6fd7ce3750ea3786218aa",class:"btn btn-primary",onClick:()=>this.runAllTests(),disabled:this.isRunningAll},this.isRunningAll?"Running...":"Run All"))),n(f,{key:"ffdd72a1db1f45c5db158d2df0be64dc978fa930",message:this.error,onClear:()=>this.error=""}),n("div",{key:"64edd5c84c4b566f90d1860f6d6bf5aeafb7ae0f",class:"test-runner-content"},n("div",{key:"4be27d07a13d88611b5fc54b05e826c790fd2145",class:"column-headers"},n("div",{key:"eeb9ace5e63b80359d2decb1b42461f77cfe98cf",class:"column-header"},"Input"),n("div",{key:"c70cc5af0aaca2636eee298dfaa4da81799df6cf",class:"column-header"},"Output"),n("div",{key:"9ca69fddafe7267bf5b8b12601e2cf64d65a58e1",class:"column-header"},"Evaluation"),n("div",{key:"b800841828c22a6aad0345e58b47f71469594792",class:"column-header"},"Actions")),n("div",{key:"da186e25c2111474f65b89a017158bffdbc83e03",class:"test-cases"},this.testCases.map((e=>n("div",{class:"test-case-row",key:e.id},n("div",{class:"input-column"},n("div",{class:"input-group"},n("label",null,"Question"),n("textarea",{value:e.question,onInput:n=>this.updateTestCase(e.id,{question:n.target.value}),placeholder:"Enter your question here...",rows:3})),n("div",{class:"keywords-group"},n("label",null,"Expected keywords"),n("div",{class:"tags-container"},e.expectedKeywords.map(((t,o)=>n("span",{class:"tag",key:o},t,n("button",{class:"tag-remove",onClick:()=>this.removeKeyword(e.id,o)},"×")))),n("input",{type:"text",placeholder:"New item...",onKeyDown:n=>{if(n.key==="Enter"){this.addKeyword(e.id,n.target.value);n.target.value=""}}}))),n("div",{class:"links-group"},n("label",null,"Expected source links"),n("div",{class:"links-container"},e.expectedSourceLinks.map(((t,o)=>n("div",{class:"link-item",key:o},n("a",{href:t,target:"_blank",rel:"noopener noreferrer"},t),n("button",{class:"link-remove",onClick:()=>this.removeSourceLink(e.id,o)},"×")))),n("input",{type:"url",placeholder:"New item...",onKeyDown:n=>{if(n.key==="Enter"){this.addSourceLink(e.id,n.target.value);n.target.value=""}}})))),n("div",{class:"output-column"},e.output?n("div",{class:"output-content"},e.output):n("div",{class:"output-placeholder"},e.isRunning?"Running...":"")),n("div",{class:"evaluation-column"},e.evaluationResult?n("div",{class:"evaluation-result"},n("div",{class:`evaluation-status ${e.evaluationResult.passed?"passed":"failed"}`},e.evaluationResult.passed?"✅ PASSED":"❌ FAILED"),n("div",{class:"evaluation-details"},"Keywords: ",e.evaluationResult.keywordMatches.filter((n=>n.found)).length,"/",e.evaluationResult.keywordMatches.length," found")):n("div",{class:"evaluation-placeholder"},e.isRunning?"Evaluating...":"")),n("div",{class:"actions-column"},n("button",{class:"btn btn-icon btn-run",onClick:()=>this.runSingleTest(e),disabled:e.isRunning||!e.question.trim(),title:!e.question.trim()?"Enter a question first":"Run this test"},e.isRunning?"⏳":"▶️"),n("button",{class:"btn btn-icon btn-delete",onClick:()=>this.deleteTestCase(e.id),title:"Delete this test"},"🗑️")))))),n("div",{key:"2d4446b15a31adfbaf88b3d46fcbb3cf314f8861",class:"add-test-case"},n("button",{key:"737e9d856db0f9090923bef718bb9bfde974b950",class:"btn btn-outline",onClick:()=>this.addNewTestCase()},"+ Add Question"))))}};h.style=x;export{h as LLMTestRunner};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|