reneco-advanced-input-module 0.0.25 → 0.0.27
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/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/voice-input-module.cjs.entry.js +73 -28
- package/dist/cjs/voice-input-module.cjs.entry.js.map +1 -1
- package/dist/cjs/voice-input-module.cjs.js +1 -1
- package/dist/cjs/voice-input-module.entry.cjs.js.map +1 -1
- package/dist/collection/components/voice-input-module/voice-input-module.css +26 -3
- package/dist/collection/components/voice-input-module/voice-input-module.js +65 -23
- package/dist/collection/components/voice-input-module/voice-input-module.js.map +1 -1
- package/dist/collection/services/llm.service.js +8 -4
- package/dist/collection/services/llm.service.js.map +1 -1
- package/dist/collection/types/form-schema.types.js.map +1 -1
- package/dist/components/voice-input-module.js +74 -28
- package/dist/components/voice-input-module.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/voice-input-module.entry.js +73 -28
- package/dist/esm/voice-input-module.entry.js.map +1 -1
- package/dist/esm/voice-input-module.js +1 -1
- package/dist/types/components/voice-input-module/voice-input-module.d.ts +4 -0
- package/dist/types/types/form-schema.types.d.ts +1 -0
- package/dist/voice-input-module/p-20ef690a.entry.js +3 -0
- package/dist/voice-input-module/p-20ef690a.entry.js.map +1 -0
- package/dist/voice-input-module/voice-input-module.entry.esm.js.map +1 -1
- package/dist/voice-input-module/voice-input-module.esm.js +1 -1
- package/package.json +1 -1
- package/www/build/p-14ee7fca.js +2 -0
- package/www/build/p-20ef690a.entry.js +3 -0
- package/www/build/p-20ef690a.entry.js.map +1 -0
- package/www/build/voice-input-module.entry.esm.js.map +1 -1
- package/www/build/voice-input-module.esm.js +1 -1
- package/www/index.html +9 -9
- package/dist/voice-input-module/p-334644ec.entry.js +0 -3
- package/dist/voice-input-module/p-334644ec.entry.js.map +0 -1
- package/www/build/p-334644ec.entry.js +0 -3
- package/www/build/p-334644ec.entry.js.map +0 -1
- package/www/build/p-dfdcd9a6.js +0 -2
package/dist/cjs/loader.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ var index = require('./index-BtdDZbi5.js');
|
|
|
5
5
|
const defineCustomElements = async (win, options) => {
|
|
6
6
|
if (typeof window === 'undefined') return undefined;
|
|
7
7
|
await index.globalScripts();
|
|
8
|
-
return index.bootstrapLazy([["ocr-file-uploader.cjs",[[257,"ocr-file-uploader",{"batch":[4],"callback":[16],"jsonSchema":[1,"json-schema"],"theme":[1],"parsedTheme":[32]},null,{"theme":["parseTheme"]}]]],["voice-input-module.cjs",[[257,"voice-input-module",{"formJson":[1,"form-json"],"serviceConfig":[1,"service-config"],"apiKey":[1,"api-key"],"apiProxyUrl":[1,"api-proxy-url"],"transcriptionModel":[1,"transcription-model"],"completionModel":[1,"completion-model"],"transcriptionProvider":[1,"transcription-provider"],"completionProvider":[1,"completion-provider"],"context":[1],"classificationRootUrl":[1,"classification-root-url"],"language":[1],"inputTypes":[1,"input-types"],"theme":[1],"debug":[4],"renderForm":[4,"render-form"],"displayStatus":[4,"display-status"],"isRecording":[32],"isProcessing":[32],"statusMessage":[32],"hasError":[32],"hasTransientError":[32],"transcription":[32],"filledData":[32],"debugInfo":[32],"isReadonlyMode":[32],"isInitializing":[32],"convertXmlToJson":[64],"convertJsonToXml":[64],"convertXmlToJsonLegacy":[64],"convertJsonToXmlLegacy":[64]},null,{"formJson":["initializeServices"],"serviceConfig":["initializeServices"],"theme":["initializeServices"],"transcriptionProvider":["initializeServices"],"completionProvider":["initializeServices"],"transcriptionModel":["initializeServices"],"completionModel":["initializeServices"]}]]]], options);
|
|
8
|
+
return index.bootstrapLazy([["ocr-file-uploader.cjs",[[257,"ocr-file-uploader",{"batch":[4],"callback":[16],"jsonSchema":[1,"json-schema"],"theme":[1],"parsedTheme":[32]},null,{"theme":["parseTheme"]}]]],["voice-input-module.cjs",[[257,"voice-input-module",{"formJson":[1,"form-json"],"serviceConfig":[1,"service-config"],"apiKey":[1,"api-key"],"apiProxyUrl":[1,"api-proxy-url"],"transcriptionModel":[1,"transcription-model"],"completionModel":[1,"completion-model"],"transcriptionProvider":[1,"transcription-provider"],"completionProvider":[1,"completion-provider"],"context":[1],"classificationRootUrl":[1,"classification-root-url"],"language":[1],"inputTypes":[1,"input-types"],"theme":[1],"debug":[4],"renderForm":[4,"render-form"],"displayStatus":[4,"display-status"],"isRecording":[32],"isProcessing":[32],"statusMessage":[32],"hasError":[32],"hasTransientError":[32],"transcription":[32],"filledData":[32],"confidenceScores":[32],"debugInfo":[32],"isReadonlyMode":[32],"isInitializing":[32],"convertXmlToJson":[64],"convertJsonToXml":[64],"convertXmlToJsonLegacy":[64],"convertJsonToXmlLegacy":[64]},null,{"formJson":["initializeServices"],"serviceConfig":["initializeServices"],"theme":["initializeServices"],"transcriptionProvider":["initializeServices"],"completionProvider":["initializeServices"],"transcriptionModel":["initializeServices"],"completionModel":["initializeServices"]}]]]], options);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
exports.setNonce = index.setNonce;
|
|
@@ -563,8 +563,9 @@ class OpenAILLMService {
|
|
|
563
563
|
14. CRITICAL: Each field has a unique ID shown as "ID:xxx" at the start of its description. You MUST include this exact ID in your response for each field you fill. The ID is the ONLY way to distinguish between fields with the same name
|
|
564
564
|
15. CRITICAL - DEFAULT VALUES: Some fields have default values shown as "default=xxx". If the user does NOT mention a specific value for that field in the transcription, you MUST return the default value. Default values should be preserved unless explicitly overridden by the user
|
|
565
565
|
16. CRITICAL - PATTERN VALIDATION: Some fields have a regex pattern shown as "pattern=xxx". The value you extract MUST match this pattern exactly. If the transcription is too ambiguous to produce a value that matches the pattern, leave the field EMPTY
|
|
566
|
+
17. CONFIDENCE SCORE: For each field you fill, add a "confidence" property (number between 0.01 and 0.99, maximum 2 decimal places) reflecting how certain you are about the extracted value. Use values close to 0.99 for values that are explicitly and unambiguously stated, lower values when you had to infer, interpret, paraphrase, or when the match was approximate.
|
|
566
567
|
|
|
567
|
-
Respond with JSON in this exact format: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
568
|
+
Respond with JSON in this exact format: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
568
569
|
let userPrompt = `
|
|
569
570
|
Voice transcription: "${data}"
|
|
570
571
|
|
|
@@ -637,8 +638,9 @@ class OpenAILLMService {
|
|
|
637
638
|
13. Use readonly fields as contextual hints to understand form structure and field grouping, but never fill them
|
|
638
639
|
14. CRITICAL - UNKNOWN/NO/N-A VALUES: The interpretation of words like "Unknown", "Inconnu", "No", "Non", "N/A", "Non applicable" depends ENTIRELY on whether the field has those words as explicit options. Rule: if the user says "field X is Unknown" (or any similar phrasing) AND "Unknown" (or a close variant) exists in the options list for that field, then SELECT that option - it is a valid value. If the user says such words for a field that does NOT have them as options (string, number, date, etc.), then it means the user has no value to provide - leave the field empty, do NOT fill it with the literal text
|
|
639
640
|
15. CRITICAL - PATTERN VALIDATION: Some fields have a regex pattern shown as "pattern=xxx". The value you extract MUST match this pattern exactly. If the transcription is too ambiguous to produce a value that matches the pattern, leave the field EMPTY
|
|
641
|
+
16. CONFIDENCE SCORE: For each field you fill, add its name as a key in a top-level "confidence" object with a score from 0.01 to 0.99 (max 2 decimal places). Use values close to 0.99 for clearly stated values, lower when you had to infer, interpret, or approximate.
|
|
640
642
|
|
|
641
|
-
Respond with JSON in this exact format: {"schema": {...}}`;
|
|
643
|
+
Respond with JSON in this exact format: {"schema": {...}, "confidence": {"field_name": 0.87}}`;
|
|
642
644
|
let userPrompt = `
|
|
643
645
|
Voice transcription: "${data}"
|
|
644
646
|
|
|
@@ -731,7 +733,7 @@ class AnthropicLLMService {
|
|
|
731
733
|
Form fields:
|
|
732
734
|
${JSON.stringify(finalSchema, null, 2)}
|
|
733
735
|
|
|
734
|
-
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
736
|
+
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
735
737
|
const body = this.useProxy
|
|
736
738
|
? { model: this.model, messages: [{ role: 'user', content: userPrompt }], system: systemPrompt }
|
|
737
739
|
: { model: this.model, max_tokens: 4096, system: systemPrompt, messages: [{ role: 'user', content: userPrompt }] };
|
|
@@ -757,6 +759,7 @@ Rules:
|
|
|
757
759
|
8. DATE/DATETIME OUT OF RANGE: if a date falls outside min/max constraints, leave the field EMPTY
|
|
758
760
|
9. PATTERN VALIDATION: if a field has a pattern, the value MUST match it exactly, otherwise leave empty
|
|
759
761
|
10. UNKNOWN VALUES: if the user says "Unknown" and it exists as an option, select it; otherwise leave empty
|
|
762
|
+
11. CONFIDENCE SCORE: For each field you fill, add a "confidence" property (number between 0.01 and 0.99, max 2 decimal places) reflecting how certain you are about the extracted value. Use values close to 0.99 for clearly stated values, lower when you had to infer or approximate.
|
|
760
763
|
Respond ONLY with valid JSON.`;
|
|
761
764
|
}
|
|
762
765
|
}
|
|
@@ -790,7 +793,7 @@ class MistralLLMService {
|
|
|
790
793
|
Form fields:
|
|
791
794
|
${JSON.stringify(finalSchema, null, 2)}
|
|
792
795
|
|
|
793
|
-
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
796
|
+
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
794
797
|
const response = await fetch(endpoint, {
|
|
795
798
|
method: 'POST',
|
|
796
799
|
headers,
|
|
@@ -820,6 +823,7 @@ Rules:
|
|
|
820
823
|
8. DATE/DATETIME OUT OF RANGE: if a date falls outside min/max constraints, leave the field EMPTY
|
|
821
824
|
9. PATTERN VALIDATION: if a field has a pattern, the value MUST match it exactly, otherwise leave empty
|
|
822
825
|
10. UNKNOWN VALUES: if the user says "Unknown" and it exists as an option, select it; otherwise leave empty
|
|
826
|
+
11. CONFIDENCE SCORE: For each field you fill, add a "confidence" property (number between 0.01 and 0.99, max 2 decimal places) reflecting how certain you are about the extracted value. Use values close to 0.99 for clearly stated values, lower when you had to infer or approximate.
|
|
823
827
|
Respond ONLY with valid JSON.`;
|
|
824
828
|
}
|
|
825
829
|
}
|
|
@@ -4327,7 +4331,7 @@ const defineCustomElements = async (win, options) => {
|
|
|
4327
4331
|
|
|
4328
4332
|
(function(){if("undefined"!==typeof window&&void 0!==window.Reflect&&void 0!==window.customElements){var a=HTMLElement;window.HTMLElement=function(){return Reflect.construct(a,[],this.constructor)};HTMLElement.prototype=a.prototype;HTMLElement.prototype.constructor=HTMLElement;Object.setPrototypeOf(HTMLElement,a);}})();
|
|
4329
4333
|
|
|
4330
|
-
const voiceInputModuleCss = ":host{display:block;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif}.row-audio-area{display:flex;flex-direction:row;align-items:center;gap:8px;flex-wrap:wrap;justify-content:center}.voice-recorder-container{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;margin:0 auto}.voice-recorder-container-debug{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;margin:0 auto}.upload-record-container{display:inline-flex;cursor:pointer}.upload-record-button{display:flex;align-items:center;justify-content:center;transition:transform 0.2s ease, box-shadow 0.2s ease;user-select:none}.upload-record-button svg{width:100%;height:100%;transition:transform 0.2s ease}.upload-record-container:hover .upload-record-button svg{transform:scale(1.05)}.
|
|
4334
|
+
const voiceInputModuleCss = ":host{display:block;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif}.row-audio-area{display:flex;flex-direction:row;align-items:center;gap:8px;flex-wrap:wrap;justify-content:center}.voice-recorder-container{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;margin:0 auto}.voice-recorder-container-debug{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:1rem;border:1px solid #e5e7eb;border-radius:0.5rem;background:#ffffff;margin:0 auto}.upload-record-container{display:inline-flex;cursor:pointer}.upload-record-button{display:flex;align-items:center;justify-content:center;transition:transform 0.2s ease, box-shadow 0.2s ease;user-select:none}.upload-record-button svg{width:100%;height:100%;transition:transform 0.2s ease}.upload-record-container:hover .upload-record-button svg{transform:scale(1.05)}.record-button{display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:#ee4444;color:white;cursor:pointer;transition:all 0.2s ease;position:relative;overflow:hidden;flex-shrink:0}.record-button:hover:not(:disabled){background:#dd3333;transform:scale(1.05)}.record-button:disabled{opacity:0.6;cursor:not-allowed}.record-button.recording{background:#dd3333;animation:pulse 1.5s infinite}.record-button.processing{background:#3b82f6}@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(239, 68, 68, 0.7)}70%{box-shadow:0 0 0 10px rgba(239, 68, 68, 0)}100%{box-shadow:0 0 0 0 rgba(239, 68, 68, 0)}}.record-icon{width:24px;height:24px;fill:currentColor}.status-text{font-size:0.875rem;color:#6b7280;text-align:center;min-height:1.25rem}.status-text.error{color:#ee4444}.status-text.success{color:#10b981}.debug-panel{width:100%;margin-top:1rem;padding:1rem;background:#f9fafb;border:1px solid #e5e7eb;border-radius:0.375rem;font-family:'Monaco', 'Menlo', 'Ubuntu Mono', monospace;font-size:0.75rem}.debug-title{font-weight:600;margin-bottom:0.5rem;color:#374151}.debug-content{white-space:pre-wrap;word-break:break-word;color:#6b7280;max-height:200px;overflow-y:auto}.permissions-warning{padding:0.75rem;background:#fef3c7;border:1px solid #f59e0b;border-radius:0.375rem;color:#92400e;font-size:0.875rem;text-align:center}.form-preview{width:100%;margin-top:1rem;padding:1rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:0.375rem}.form-preview-title{font-weight:600;margin-bottom:0.5rem;color:#1e293b}.form-field{margin-bottom:0.5rem;font-size:0.875rem}.field-name{font-weight:500;color:#475569}.field-value{color:#64748b;margin-left:0.5rem}.field-value.filled{color:#059669;font-weight:500}.voice-filled-form{display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:0.5rem;margin-top:1rem}.form-group{display:flex;flex-direction:column;gap:0.15rem}.form-group.full-width{grid-column:1 / -1}.form-header{grid-column:1 / -1;font-weight:600;font-size:1rem;color:#1e293b;margin-top:0.75rem;margin-bottom:0.25rem;padding-bottom:0.25rem;border-bottom:2px solid #e2e8f0}.form-label{font-weight:500;color:#374151;font-size:0.8rem}.required{color:#ee4444;margin-left:0.125rem}.form-input{padding:0.4rem;border:1px solid #d1d5db;border-radius:0.375rem;font-size:0.8rem;transition:border-color 0.2s ease}.form-input:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.form-checkbox{width:auto;padding:0;margin:0}select.form-input{background-color:white;cursor:pointer}input[type=\"date\"].form-input{cursor:pointer}.readonly-select{background:#f9fafb;border:1px solid #d1d5db;border-radius:0.375rem;padding:0.5rem}.select-placeholder{font-size:0.875rem;color:#6b7280;margin-bottom:0.5rem}.select-options-list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:0.25rem;max-height:150px;overflow-y:auto}.select-option{background:#e5e7eb;padding:0.25rem 0.5rem;border-radius:0.25rem;font-size:0.875rem;color:#374151}.multiselect-container{display:grid;grid-template-columns:repeat(2, 1fr);gap:0.25rem;padding:0.5rem;border:1px solid #d1d5db;border-radius:0.375rem;background:white}.multiselect-option{display:flex;align-items:center;gap:0.5rem;padding:0.5rem;border-radius:0.25rem;cursor:pointer;transition:background 0.2s ease}.multiselect-option:hover{background:#f3f4f6}.multiselect-option input[type=\"checkbox\"]{width:18px;height:18px;cursor:pointer}.multiselect-option input[type=\"checkbox\"]:checked+.multiselect-label{font-weight:600;color:#059669}.multiselect-label{font-size:0.875rem;color:#374151;user-select:none}.confidence-badge{display:inline-flex;align-items:center;margin-left:4px;padding:1px 5px;border-radius:3px;font-size:0.65rem;font-weight:700;vertical-align:middle;line-height:1.4;cursor:help}.confidence-medium{background:#fef3c7;color:#92400e;border:1px solid #fde68a}.confidence-low{background:#fee2e2;color:#991b1b;border:1px solid #fca5a5}@keyframes skeleton-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}.skeleton-circle{border-radius:50%;flex-shrink:0;background:linear-gradient(\r\n 90deg,\r\n #e2e8f0 25%,\r\n #f8fafc 50%,\r\n #e2e8f0 75%\r\n );background-size:200% 100%;animation:skeleton-shimmer 1.5s ease-in-out infinite}.skeleton-status{height:14px;border-radius:7px;width:140px;background:linear-gradient(\r\n 90deg,\r\n #e2e8f0 25%,\r\n #f8fafc 50%,\r\n #e2e8f0 75%\r\n );background-size:200% 100%;animation:skeleton-shimmer 1.5s ease-in-out infinite;animation-delay:0.15s}ocr-file-uploader{display:block;width:100% !important;height:100% !important;max-width:100% !important;max-height:100% !important}ocr-file-uploader *{max-width:100% !important;max-height:100% !important}";
|
|
4331
4335
|
|
|
4332
4336
|
// Initialize OCR
|
|
4333
4337
|
defineCustomElements();
|
|
@@ -4367,6 +4371,7 @@ const VoiceFormRecorder = class {
|
|
|
4367
4371
|
this.hasTransientError = false;
|
|
4368
4372
|
this.transcription = '';
|
|
4369
4373
|
this.filledData = null;
|
|
4374
|
+
this.confidenceScores = null;
|
|
4370
4375
|
this.debugInfo = {};
|
|
4371
4376
|
this.isReadonlyMode = true; // Start in readonly preview mode
|
|
4372
4377
|
this.isInitializing = true;
|
|
@@ -4614,6 +4619,7 @@ const VoiceFormRecorder = class {
|
|
|
4614
4619
|
return;
|
|
4615
4620
|
this.isProcessing = true;
|
|
4616
4621
|
this.hasTransientError = false;
|
|
4622
|
+
this.confidenceScores = null;
|
|
4617
4623
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.processing) || (this.language == 'en' ? 'Processing document...' : 'Traitement du document ...');
|
|
4618
4624
|
try {
|
|
4619
4625
|
const extractedData = (ocrData === null || ocrData === void 0 ? void 0 : ocrData.content) || ocrData;
|
|
@@ -4631,10 +4637,11 @@ const VoiceFormRecorder = class {
|
|
|
4631
4637
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
4632
4638
|
}
|
|
4633
4639
|
this.filledData = this.extractFilledData(filledSchema);
|
|
4634
|
-
this.
|
|
4640
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
4641
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
4635
4642
|
this.parsedSchema = this.filledData;
|
|
4636
4643
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
4637
|
-
this.formFilled.emit({ success: true, data: this.filledData, jsonForm });
|
|
4644
|
+
this.formFilled.emit({ success: true, data: this.filledData, jsonForm, confidence: this.confidenceScores });
|
|
4638
4645
|
}
|
|
4639
4646
|
catch (error) {
|
|
4640
4647
|
console.error('OCR processing error:', error);
|
|
@@ -4653,6 +4660,7 @@ const VoiceFormRecorder = class {
|
|
|
4653
4660
|
return;
|
|
4654
4661
|
this.isProcessing = true;
|
|
4655
4662
|
this.hasTransientError = false;
|
|
4663
|
+
this.confidenceScores = null;
|
|
4656
4664
|
this.updateDebugInfo('Audio Captured', { size: audioFile.size, type: audioFile.type });
|
|
4657
4665
|
try {
|
|
4658
4666
|
// Validate audio file before sending to API
|
|
@@ -4673,7 +4681,6 @@ const VoiceFormRecorder = class {
|
|
|
4673
4681
|
this.formFilled.emit({ success: false, error: 'Audio file too large' });
|
|
4674
4682
|
return;
|
|
4675
4683
|
}
|
|
4676
|
-
// Step 1: Transcription
|
|
4677
4684
|
let transcription;
|
|
4678
4685
|
try {
|
|
4679
4686
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.transcribing) || (this.language == 'en' ? 'Transcribing speech...' : 'Transcription du texte ...');
|
|
@@ -4692,7 +4699,6 @@ const VoiceFormRecorder = class {
|
|
|
4692
4699
|
this.formFilled.emit({ success: false, error: 'No speech detected', transcription: '' });
|
|
4693
4700
|
return;
|
|
4694
4701
|
}
|
|
4695
|
-
// Step 2: LLM form filling
|
|
4696
4702
|
let filledSchema;
|
|
4697
4703
|
try {
|
|
4698
4704
|
this.statusMessage = ((_b = this.parsedTheme.texts) === null || _b === void 0 ? void 0 : _b.filling) || (this.language == 'en' ? 'Filling form fields...' : 'Remplissage du formulaire ...');
|
|
@@ -4703,10 +4709,11 @@ const VoiceFormRecorder = class {
|
|
|
4703
4709
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
4704
4710
|
}
|
|
4705
4711
|
this.filledData = this.extractFilledData(filledSchema);
|
|
4706
|
-
this.
|
|
4712
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
4713
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
4707
4714
|
this.parsedSchema = this.filledData;
|
|
4708
4715
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
4709
|
-
this.formFilled.emit({ success: true, data: this.filledData, transcription });
|
|
4716
|
+
this.formFilled.emit({ success: true, data: this.filledData, transcription, confidence: this.confidenceScores });
|
|
4710
4717
|
}
|
|
4711
4718
|
catch (error) {
|
|
4712
4719
|
console.error('Audio processing error:', error);
|
|
@@ -4730,7 +4737,8 @@ const VoiceFormRecorder = class {
|
|
|
4730
4737
|
try {
|
|
4731
4738
|
const audioBlob = await this.audioRecorder.stopRecording();
|
|
4732
4739
|
const audioContent = new File([audioBlob], 'audio.wav', { type: 'audio/wav' });
|
|
4733
|
-
//
|
|
4740
|
+
// Reset isProcessing so the guard inside processAudioContent doesn't block this internal call
|
|
4741
|
+
this.isProcessing = false;
|
|
4734
4742
|
await this.processAudioContent(audioContent);
|
|
4735
4743
|
}
|
|
4736
4744
|
catch (error) {
|
|
@@ -4747,12 +4755,52 @@ const VoiceFormRecorder = class {
|
|
|
4747
4755
|
this.recordingStateChanged.emit({ isRecording: false, state: 'idle' });
|
|
4748
4756
|
}
|
|
4749
4757
|
}
|
|
4758
|
+
extractConfidenceScores(filledData) {
|
|
4759
|
+
const scores = {};
|
|
4760
|
+
if (!filledData)
|
|
4761
|
+
return scores;
|
|
4762
|
+
// Fields array format: [{"id": "...", "name": "...", "value": "...", "confidence": 0.87}]
|
|
4763
|
+
if (filledData.fields && Array.isArray(filledData.fields)) {
|
|
4764
|
+
filledData.fields.forEach((field) => {
|
|
4765
|
+
const raw = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4766
|
+
if (!isNaN(raw)) {
|
|
4767
|
+
const clamped = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
4768
|
+
// Store under both id and name so label-based lookups always work
|
|
4769
|
+
if (field.id)
|
|
4770
|
+
scores[field.id] = clamped;
|
|
4771
|
+
if (field.name)
|
|
4772
|
+
scores[field.name] = clamped;
|
|
4773
|
+
}
|
|
4774
|
+
});
|
|
4775
|
+
}
|
|
4776
|
+
// Simple schema format: {"schema": {...}, "confidence": {"field_name": 0.87}}
|
|
4777
|
+
if (filledData.confidence && typeof filledData.confidence === 'object') {
|
|
4778
|
+
Object.entries(filledData.confidence).forEach(([k, v]) => {
|
|
4779
|
+
const raw = typeof v === 'number' ? v : parseFloat(v);
|
|
4780
|
+
if (!isNaN(raw)) {
|
|
4781
|
+
scores[k] = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
4782
|
+
}
|
|
4783
|
+
});
|
|
4784
|
+
}
|
|
4785
|
+
return scores;
|
|
4786
|
+
}
|
|
4787
|
+
getFieldConfidence(primary, fallback) {
|
|
4788
|
+
var _a, _b;
|
|
4789
|
+
if (!this.confidenceScores)
|
|
4790
|
+
return null;
|
|
4791
|
+
return (_a = this.confidenceScores[primary]) !== null && _a !== void 0 ? _a : (fallback ? ((_b = this.confidenceScores[fallback]) !== null && _b !== void 0 ? _b : null) : null);
|
|
4792
|
+
}
|
|
4793
|
+
renderConfidenceIndicator(confidence) {
|
|
4794
|
+
if (confidence === null || confidence >= 0.95)
|
|
4795
|
+
return null;
|
|
4796
|
+
const pct = Math.round(confidence * 100);
|
|
4797
|
+
const isApproximate = confidence >= 0.85;
|
|
4798
|
+
return (index.h("span", { class: `confidence-badge ${isApproximate ? 'confidence-medium' : 'confidence-low'}`, title: this.language === 'en' ? `Confidence: ${pct}%` : `Confiance : ${pct}%` }, pct, "%"));
|
|
4799
|
+
}
|
|
4750
4800
|
extractFilledData(filledData) {
|
|
4751
|
-
// console.log("extractFilledData", filledData);
|
|
4752
4801
|
const updatedSchema = JSON.parse(JSON.stringify(this.parsedSchema));
|
|
4753
4802
|
switch (this.context) {
|
|
4754
4803
|
case "ecoteka":
|
|
4755
|
-
// console.log("TODO extractFilledData", filledData);
|
|
4756
4804
|
case "ng":
|
|
4757
4805
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4758
4806
|
// Map AI response back to original schema structure
|
|
@@ -4762,12 +4810,13 @@ const VoiceFormRecorder = class {
|
|
|
4762
4810
|
if (!originalField.Settings)
|
|
4763
4811
|
originalField.Settings = {};
|
|
4764
4812
|
originalField.Settings.Default_Value = field.value;
|
|
4813
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4814
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
4765
4815
|
}
|
|
4766
4816
|
});
|
|
4767
4817
|
}
|
|
4768
4818
|
break;
|
|
4769
4819
|
case "ecoll-veto":
|
|
4770
|
-
// console.log("TODO extractFilledData", filledData, updatedSchema);
|
|
4771
4820
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4772
4821
|
// Map AI response back to original schema structure
|
|
4773
4822
|
filledData.fields.forEach((field) => {
|
|
@@ -4776,6 +4825,8 @@ const VoiceFormRecorder = class {
|
|
|
4776
4825
|
originalField = updatedSchema[1].items.find((child) => child.label === field.name);
|
|
4777
4826
|
if (originalField && field.value !== undefined && field.value !== null && field.value !== '') {
|
|
4778
4827
|
updatedSchema[2][originalField.name] = field.value;
|
|
4828
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4829
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
4779
4830
|
}
|
|
4780
4831
|
});
|
|
4781
4832
|
}
|
|
@@ -4785,11 +4836,14 @@ const VoiceFormRecorder = class {
|
|
|
4785
4836
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4786
4837
|
Object.entries(filledData.fields).forEach(([fieldID, field]) => {
|
|
4787
4838
|
if (field.value !== undefined && field.value !== null && field.value !== '') {
|
|
4839
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4840
|
+
const confidence = isNaN(rawConf) ? null : rawConf;
|
|
4788
4841
|
if (field.id) {
|
|
4789
4842
|
const schemaField = updatedSchema.fields[field.id];
|
|
4790
4843
|
if (schemaField) {
|
|
4791
4844
|
schemaField.value = field.value;
|
|
4792
4845
|
schemaField.default = field.value;
|
|
4846
|
+
schemaField.confidence = confidence;
|
|
4793
4847
|
}
|
|
4794
4848
|
}
|
|
4795
4849
|
else {
|
|
@@ -4799,6 +4853,7 @@ const VoiceFormRecorder = class {
|
|
|
4799
4853
|
if (schemaField.title === fieldName) {
|
|
4800
4854
|
schemaField.value = field.value;
|
|
4801
4855
|
schemaField.default = field.value;
|
|
4856
|
+
schemaField.confidence = confidence;
|
|
4802
4857
|
break;
|
|
4803
4858
|
}
|
|
4804
4859
|
}
|
|
@@ -4808,15 +4863,12 @@ const VoiceFormRecorder = class {
|
|
|
4808
4863
|
}
|
|
4809
4864
|
break;
|
|
4810
4865
|
}
|
|
4811
|
-
// console.log("extractFilledData result", updatedSchema);
|
|
4812
4866
|
return updatedSchema;
|
|
4813
4867
|
}
|
|
4814
4868
|
async trimSchemaForAI(schema) {
|
|
4815
4869
|
var _a, _b;
|
|
4816
|
-
// console.log("trimSchemaForAI", schema);
|
|
4817
4870
|
switch (this.context) {
|
|
4818
4871
|
case "ecoteka":
|
|
4819
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
4820
4872
|
case "ng":
|
|
4821
4873
|
const trimmed = { fields: [] };
|
|
4822
4874
|
schema.Children.forEach((child) => {
|
|
@@ -4835,10 +4887,8 @@ const VoiceFormRecorder = class {
|
|
|
4835
4887
|
}
|
|
4836
4888
|
trimmed.fields.push(fieldData);
|
|
4837
4889
|
});
|
|
4838
|
-
// console.log("Schema apres transformation, contexte NG:", trimmed);
|
|
4839
4890
|
return trimmed;
|
|
4840
4891
|
case "ecoll-veto":
|
|
4841
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
4842
4892
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
4843
4893
|
if (mergedItemsSchema) {
|
|
4844
4894
|
const trimmedSchema = {
|
|
@@ -4883,7 +4933,6 @@ const VoiceFormRecorder = class {
|
|
|
4883
4933
|
default: '',
|
|
4884
4934
|
};
|
|
4885
4935
|
});
|
|
4886
|
-
// console.log("Schema apres transformation, contexte Track:", trimmedSchema);
|
|
4887
4936
|
return trimmedSchema;
|
|
4888
4937
|
}
|
|
4889
4938
|
case "track":
|
|
@@ -5080,13 +5129,11 @@ const VoiceFormRecorder = class {
|
|
|
5080
5129
|
return (index.h("div", { class: "form-preview" }, index.h("div", { class: "form-preview-title" }, title), index.h("form", { class: "voice-filled-form" }, this.renderFormFields())));
|
|
5081
5130
|
}
|
|
5082
5131
|
renderFormFields() {
|
|
5083
|
-
// console.log("renderFormFields", this.parsedSchema);
|
|
5084
5132
|
if (!this.parsedSchema)
|
|
5085
5133
|
return null;
|
|
5086
5134
|
const largeFieldTypes = ['select', 'multiselect', 'classification', 'thesaurus', 'position'];
|
|
5087
5135
|
switch (this.context) {
|
|
5088
5136
|
case "ecoteka":
|
|
5089
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
5090
5137
|
case "ng":
|
|
5091
5138
|
return this.parsedSchema.Children.map((child) => {
|
|
5092
5139
|
var _a, _b, _c;
|
|
@@ -5098,7 +5145,7 @@ const VoiceFormRecorder = class {
|
|
|
5098
5145
|
const isRequired = child.Required || false;
|
|
5099
5146
|
const fieldValue = (_b = child.Settings) === null || _b === void 0 ? void 0 : _b.Default_Value;
|
|
5100
5147
|
const isLargeField = largeFieldTypes.includes(fieldType) || (child.Children && child.Children.length > 5);
|
|
5101
|
-
return (index.h("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && index.h("span", { class: "required" }, "*")), this.renderFormField(fieldName, {
|
|
5148
|
+
return (index.h("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && index.h("span", { class: "required" }, "*"), this.renderConfidenceIndicator(this.getFieldConfidence(fieldLabel, fieldName))), this.renderFormField(fieldName, {
|
|
5102
5149
|
type: fieldType,
|
|
5103
5150
|
title: fieldLabel,
|
|
5104
5151
|
required: isRequired,
|
|
@@ -5106,8 +5153,6 @@ const VoiceFormRecorder = class {
|
|
|
5106
5153
|
}, fieldValue)));
|
|
5107
5154
|
}).filter(Boolean);
|
|
5108
5155
|
case "ecoll-veto":
|
|
5109
|
-
// NOTE STEP 2
|
|
5110
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
5111
5156
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
5112
5157
|
return Object.entries(mergedItemsSchema).map(([key, field]) => {
|
|
5113
5158
|
var _a, _b;
|
|
@@ -5138,7 +5183,7 @@ const VoiceFormRecorder = class {
|
|
|
5138
5183
|
// console.error("Error fetching classification data:", error);
|
|
5139
5184
|
// }
|
|
5140
5185
|
// }
|
|
5141
|
-
return (index.h("div", { class: "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && index.h("span", { class: "required" }, "*")), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : fieldValue))));
|
|
5186
|
+
return (index.h("div", { class: "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && index.h("span", { class: "required" }, "*"), this.renderConfidenceIndicator(this.getFieldConfidence(fieldLabel, fieldName))), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : fieldValue))));
|
|
5142
5187
|
});
|
|
5143
5188
|
case "track":
|
|
5144
5189
|
default:
|
|
@@ -5147,7 +5192,7 @@ const VoiceFormRecorder = class {
|
|
|
5147
5192
|
if (field.type === 'header') {
|
|
5148
5193
|
return (index.h("div", { class: "form-header", key: fieldName }, field.title || fieldName));
|
|
5149
5194
|
}
|
|
5150
|
-
return (index.h("div", { class: "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && index.h("span", { class: "required" }, "*")), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : field.value))));
|
|
5195
|
+
return (index.h("div", { class: "form-group", key: fieldName }, index.h("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && index.h("span", { class: "required" }, "*"), this.renderConfidenceIndicator(this.getFieldConfidence(fieldName))), this.renderFormField(fieldName, field, ((_b = (_a = this.filledData) === null || _a === void 0 ? void 0 : _a[fieldName]) !== null && _b !== void 0 ? _b : field.value))));
|
|
5151
5196
|
});
|
|
5152
5197
|
}
|
|
5153
5198
|
}
|