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/esm/loader.js
CHANGED
|
@@ -4,7 +4,7 @@ export { s as setNonce } from './index-a7QWC-5I.js';
|
|
|
4
4
|
const defineCustomElements = async (win, options) => {
|
|
5
5
|
if (typeof window === 'undefined') return undefined;
|
|
6
6
|
await globalScripts();
|
|
7
|
-
return bootstrapLazy([["ocr-file-uploader",[[257,"ocr-file-uploader",{"batch":[4],"callback":[16],"jsonSchema":[1,"json-schema"],"theme":[1],"parsedTheme":[32]},null,{"theme":["parseTheme"]}]]],["voice-input-module",[[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);
|
|
7
|
+
return bootstrapLazy([["ocr-file-uploader",[[257,"ocr-file-uploader",{"batch":[4],"callback":[16],"jsonSchema":[1,"json-schema"],"theme":[1],"parsedTheme":[32]},null,{"theme":["parseTheme"]}]]],["voice-input-module",[[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);
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export { defineCustomElements };
|
|
@@ -561,8 +561,9 @@ class OpenAILLMService {
|
|
|
561
561
|
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
|
|
562
562
|
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
|
|
563
563
|
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
|
|
564
|
+
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.
|
|
564
565
|
|
|
565
|
-
Respond with JSON in this exact format: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
566
|
+
Respond with JSON in this exact format: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
566
567
|
let userPrompt = `
|
|
567
568
|
Voice transcription: "${data}"
|
|
568
569
|
|
|
@@ -635,8 +636,9 @@ class OpenAILLMService {
|
|
|
635
636
|
13. Use readonly fields as contextual hints to understand form structure and field grouping, but never fill them
|
|
636
637
|
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
|
|
637
638
|
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
|
|
639
|
+
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.
|
|
638
640
|
|
|
639
|
-
Respond with JSON in this exact format: {"schema": {...}}`;
|
|
641
|
+
Respond with JSON in this exact format: {"schema": {...}, "confidence": {"field_name": 0.87}}`;
|
|
640
642
|
let userPrompt = `
|
|
641
643
|
Voice transcription: "${data}"
|
|
642
644
|
|
|
@@ -729,7 +731,7 @@ class AnthropicLLMService {
|
|
|
729
731
|
Form fields:
|
|
730
732
|
${JSON.stringify(finalSchema, null, 2)}
|
|
731
733
|
|
|
732
|
-
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
734
|
+
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
733
735
|
const body = this.useProxy
|
|
734
736
|
? { model: this.model, messages: [{ role: 'user', content: userPrompt }], system: systemPrompt }
|
|
735
737
|
: { model: this.model, max_tokens: 4096, system: systemPrompt, messages: [{ role: 'user', content: userPrompt }] };
|
|
@@ -755,6 +757,7 @@ Rules:
|
|
|
755
757
|
8. DATE/DATETIME OUT OF RANGE: if a date falls outside min/max constraints, leave the field EMPTY
|
|
756
758
|
9. PATTERN VALIDATION: if a field has a pattern, the value MUST match it exactly, otherwise leave empty
|
|
757
759
|
10. UNKNOWN VALUES: if the user says "Unknown" and it exists as an option, select it; otherwise leave empty
|
|
760
|
+
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.
|
|
758
761
|
Respond ONLY with valid JSON.`;
|
|
759
762
|
}
|
|
760
763
|
}
|
|
@@ -788,7 +791,7 @@ class MistralLLMService {
|
|
|
788
791
|
Form fields:
|
|
789
792
|
${JSON.stringify(finalSchema, null, 2)}
|
|
790
793
|
|
|
791
|
-
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value"}]}`;
|
|
794
|
+
Respond with JSON: {"fields": [{"id": "field_id", "name": "field_name", "value": "extracted_value", "confidence": 0.87}]}`;
|
|
792
795
|
const response = await fetch(endpoint, {
|
|
793
796
|
method: 'POST',
|
|
794
797
|
headers,
|
|
@@ -818,6 +821,7 @@ Rules:
|
|
|
818
821
|
8. DATE/DATETIME OUT OF RANGE: if a date falls outside min/max constraints, leave the field EMPTY
|
|
819
822
|
9. PATTERN VALIDATION: if a field has a pattern, the value MUST match it exactly, otherwise leave empty
|
|
820
823
|
10. UNKNOWN VALUES: if the user says "Unknown" and it exists as an option, select it; otherwise leave empty
|
|
824
|
+
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.
|
|
821
825
|
Respond ONLY with valid JSON.`;
|
|
822
826
|
}
|
|
823
827
|
}
|
|
@@ -4325,7 +4329,7 @@ const defineCustomElements = async (win, options) => {
|
|
|
4325
4329
|
|
|
4326
4330
|
(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);}})();
|
|
4327
4331
|
|
|
4328
|
-
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)}.
|
|
4332
|
+
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}";
|
|
4329
4333
|
|
|
4330
4334
|
// Initialize OCR
|
|
4331
4335
|
defineCustomElements();
|
|
@@ -4365,6 +4369,7 @@ const VoiceFormRecorder = class {
|
|
|
4365
4369
|
this.hasTransientError = false;
|
|
4366
4370
|
this.transcription = '';
|
|
4367
4371
|
this.filledData = null;
|
|
4372
|
+
this.confidenceScores = null;
|
|
4368
4373
|
this.debugInfo = {};
|
|
4369
4374
|
this.isReadonlyMode = true; // Start in readonly preview mode
|
|
4370
4375
|
this.isInitializing = true;
|
|
@@ -4612,6 +4617,7 @@ const VoiceFormRecorder = class {
|
|
|
4612
4617
|
return;
|
|
4613
4618
|
this.isProcessing = true;
|
|
4614
4619
|
this.hasTransientError = false;
|
|
4620
|
+
this.confidenceScores = null;
|
|
4615
4621
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.processing) || (this.language == 'en' ? 'Processing document...' : 'Traitement du document ...');
|
|
4616
4622
|
try {
|
|
4617
4623
|
const extractedData = (ocrData === null || ocrData === void 0 ? void 0 : ocrData.content) || ocrData;
|
|
@@ -4629,10 +4635,11 @@ const VoiceFormRecorder = class {
|
|
|
4629
4635
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
4630
4636
|
}
|
|
4631
4637
|
this.filledData = this.extractFilledData(filledSchema);
|
|
4632
|
-
this.
|
|
4638
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
4639
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
4633
4640
|
this.parsedSchema = this.filledData;
|
|
4634
4641
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
4635
|
-
this.formFilled.emit({ success: true, data: this.filledData, jsonForm });
|
|
4642
|
+
this.formFilled.emit({ success: true, data: this.filledData, jsonForm, confidence: this.confidenceScores });
|
|
4636
4643
|
}
|
|
4637
4644
|
catch (error) {
|
|
4638
4645
|
console.error('OCR processing error:', error);
|
|
@@ -4651,6 +4658,7 @@ const VoiceFormRecorder = class {
|
|
|
4651
4658
|
return;
|
|
4652
4659
|
this.isProcessing = true;
|
|
4653
4660
|
this.hasTransientError = false;
|
|
4661
|
+
this.confidenceScores = null;
|
|
4654
4662
|
this.updateDebugInfo('Audio Captured', { size: audioFile.size, type: audioFile.type });
|
|
4655
4663
|
try {
|
|
4656
4664
|
// Validate audio file before sending to API
|
|
@@ -4671,7 +4679,6 @@ const VoiceFormRecorder = class {
|
|
|
4671
4679
|
this.formFilled.emit({ success: false, error: 'Audio file too large' });
|
|
4672
4680
|
return;
|
|
4673
4681
|
}
|
|
4674
|
-
// Step 1: Transcription
|
|
4675
4682
|
let transcription;
|
|
4676
4683
|
try {
|
|
4677
4684
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.transcribing) || (this.language == 'en' ? 'Transcribing speech...' : 'Transcription du texte ...');
|
|
@@ -4690,7 +4697,6 @@ const VoiceFormRecorder = class {
|
|
|
4690
4697
|
this.formFilled.emit({ success: false, error: 'No speech detected', transcription: '' });
|
|
4691
4698
|
return;
|
|
4692
4699
|
}
|
|
4693
|
-
// Step 2: LLM form filling
|
|
4694
4700
|
let filledSchema;
|
|
4695
4701
|
try {
|
|
4696
4702
|
this.statusMessage = ((_b = this.parsedTheme.texts) === null || _b === void 0 ? void 0 : _b.filling) || (this.language == 'en' ? 'Filling form fields...' : 'Remplissage du formulaire ...');
|
|
@@ -4701,10 +4707,11 @@ const VoiceFormRecorder = class {
|
|
|
4701
4707
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
4702
4708
|
}
|
|
4703
4709
|
this.filledData = this.extractFilledData(filledSchema);
|
|
4704
|
-
this.
|
|
4710
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
4711
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
4705
4712
|
this.parsedSchema = this.filledData;
|
|
4706
4713
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
4707
|
-
this.formFilled.emit({ success: true, data: this.filledData, transcription });
|
|
4714
|
+
this.formFilled.emit({ success: true, data: this.filledData, transcription, confidence: this.confidenceScores });
|
|
4708
4715
|
}
|
|
4709
4716
|
catch (error) {
|
|
4710
4717
|
console.error('Audio processing error:', error);
|
|
@@ -4728,7 +4735,8 @@ const VoiceFormRecorder = class {
|
|
|
4728
4735
|
try {
|
|
4729
4736
|
const audioBlob = await this.audioRecorder.stopRecording();
|
|
4730
4737
|
const audioContent = new File([audioBlob], 'audio.wav', { type: 'audio/wav' });
|
|
4731
|
-
//
|
|
4738
|
+
// Reset isProcessing so the guard inside processAudioContent doesn't block this internal call
|
|
4739
|
+
this.isProcessing = false;
|
|
4732
4740
|
await this.processAudioContent(audioContent);
|
|
4733
4741
|
}
|
|
4734
4742
|
catch (error) {
|
|
@@ -4745,12 +4753,52 @@ const VoiceFormRecorder = class {
|
|
|
4745
4753
|
this.recordingStateChanged.emit({ isRecording: false, state: 'idle' });
|
|
4746
4754
|
}
|
|
4747
4755
|
}
|
|
4756
|
+
extractConfidenceScores(filledData) {
|
|
4757
|
+
const scores = {};
|
|
4758
|
+
if (!filledData)
|
|
4759
|
+
return scores;
|
|
4760
|
+
// Fields array format: [{"id": "...", "name": "...", "value": "...", "confidence": 0.87}]
|
|
4761
|
+
if (filledData.fields && Array.isArray(filledData.fields)) {
|
|
4762
|
+
filledData.fields.forEach((field) => {
|
|
4763
|
+
const raw = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4764
|
+
if (!isNaN(raw)) {
|
|
4765
|
+
const clamped = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
4766
|
+
// Store under both id and name so label-based lookups always work
|
|
4767
|
+
if (field.id)
|
|
4768
|
+
scores[field.id] = clamped;
|
|
4769
|
+
if (field.name)
|
|
4770
|
+
scores[field.name] = clamped;
|
|
4771
|
+
}
|
|
4772
|
+
});
|
|
4773
|
+
}
|
|
4774
|
+
// Simple schema format: {"schema": {...}, "confidence": {"field_name": 0.87}}
|
|
4775
|
+
if (filledData.confidence && typeof filledData.confidence === 'object') {
|
|
4776
|
+
Object.entries(filledData.confidence).forEach(([k, v]) => {
|
|
4777
|
+
const raw = typeof v === 'number' ? v : parseFloat(v);
|
|
4778
|
+
if (!isNaN(raw)) {
|
|
4779
|
+
scores[k] = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
4780
|
+
}
|
|
4781
|
+
});
|
|
4782
|
+
}
|
|
4783
|
+
return scores;
|
|
4784
|
+
}
|
|
4785
|
+
getFieldConfidence(primary, fallback) {
|
|
4786
|
+
var _a, _b;
|
|
4787
|
+
if (!this.confidenceScores)
|
|
4788
|
+
return null;
|
|
4789
|
+
return (_a = this.confidenceScores[primary]) !== null && _a !== void 0 ? _a : (fallback ? ((_b = this.confidenceScores[fallback]) !== null && _b !== void 0 ? _b : null) : null);
|
|
4790
|
+
}
|
|
4791
|
+
renderConfidenceIndicator(confidence) {
|
|
4792
|
+
if (confidence === null || confidence >= 0.95)
|
|
4793
|
+
return null;
|
|
4794
|
+
const pct = Math.round(confidence * 100);
|
|
4795
|
+
const isApproximate = confidence >= 0.85;
|
|
4796
|
+
return (h$1("span", { class: `confidence-badge ${isApproximate ? 'confidence-medium' : 'confidence-low'}`, title: this.language === 'en' ? `Confidence: ${pct}%` : `Confiance : ${pct}%` }, pct, "%"));
|
|
4797
|
+
}
|
|
4748
4798
|
extractFilledData(filledData) {
|
|
4749
|
-
// console.log("extractFilledData", filledData);
|
|
4750
4799
|
const updatedSchema = JSON.parse(JSON.stringify(this.parsedSchema));
|
|
4751
4800
|
switch (this.context) {
|
|
4752
4801
|
case "ecoteka":
|
|
4753
|
-
// console.log("TODO extractFilledData", filledData);
|
|
4754
4802
|
case "ng":
|
|
4755
4803
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4756
4804
|
// Map AI response back to original schema structure
|
|
@@ -4760,12 +4808,13 @@ const VoiceFormRecorder = class {
|
|
|
4760
4808
|
if (!originalField.Settings)
|
|
4761
4809
|
originalField.Settings = {};
|
|
4762
4810
|
originalField.Settings.Default_Value = field.value;
|
|
4811
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4812
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
4763
4813
|
}
|
|
4764
4814
|
});
|
|
4765
4815
|
}
|
|
4766
4816
|
break;
|
|
4767
4817
|
case "ecoll-veto":
|
|
4768
|
-
// console.log("TODO extractFilledData", filledData, updatedSchema);
|
|
4769
4818
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4770
4819
|
// Map AI response back to original schema structure
|
|
4771
4820
|
filledData.fields.forEach((field) => {
|
|
@@ -4774,6 +4823,8 @@ const VoiceFormRecorder = class {
|
|
|
4774
4823
|
originalField = updatedSchema[1].items.find((child) => child.label === field.name);
|
|
4775
4824
|
if (originalField && field.value !== undefined && field.value !== null && field.value !== '') {
|
|
4776
4825
|
updatedSchema[2][originalField.name] = field.value;
|
|
4826
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4827
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
4777
4828
|
}
|
|
4778
4829
|
});
|
|
4779
4830
|
}
|
|
@@ -4783,11 +4834,14 @@ const VoiceFormRecorder = class {
|
|
|
4783
4834
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
4784
4835
|
Object.entries(filledData.fields).forEach(([fieldID, field]) => {
|
|
4785
4836
|
if (field.value !== undefined && field.value !== null && field.value !== '') {
|
|
4837
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
4838
|
+
const confidence = isNaN(rawConf) ? null : rawConf;
|
|
4786
4839
|
if (field.id) {
|
|
4787
4840
|
const schemaField = updatedSchema.fields[field.id];
|
|
4788
4841
|
if (schemaField) {
|
|
4789
4842
|
schemaField.value = field.value;
|
|
4790
4843
|
schemaField.default = field.value;
|
|
4844
|
+
schemaField.confidence = confidence;
|
|
4791
4845
|
}
|
|
4792
4846
|
}
|
|
4793
4847
|
else {
|
|
@@ -4797,6 +4851,7 @@ const VoiceFormRecorder = class {
|
|
|
4797
4851
|
if (schemaField.title === fieldName) {
|
|
4798
4852
|
schemaField.value = field.value;
|
|
4799
4853
|
schemaField.default = field.value;
|
|
4854
|
+
schemaField.confidence = confidence;
|
|
4800
4855
|
break;
|
|
4801
4856
|
}
|
|
4802
4857
|
}
|
|
@@ -4806,15 +4861,12 @@ const VoiceFormRecorder = class {
|
|
|
4806
4861
|
}
|
|
4807
4862
|
break;
|
|
4808
4863
|
}
|
|
4809
|
-
// console.log("extractFilledData result", updatedSchema);
|
|
4810
4864
|
return updatedSchema;
|
|
4811
4865
|
}
|
|
4812
4866
|
async trimSchemaForAI(schema) {
|
|
4813
4867
|
var _a, _b;
|
|
4814
|
-
// console.log("trimSchemaForAI", schema);
|
|
4815
4868
|
switch (this.context) {
|
|
4816
4869
|
case "ecoteka":
|
|
4817
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
4818
4870
|
case "ng":
|
|
4819
4871
|
const trimmed = { fields: [] };
|
|
4820
4872
|
schema.Children.forEach((child) => {
|
|
@@ -4833,10 +4885,8 @@ const VoiceFormRecorder = class {
|
|
|
4833
4885
|
}
|
|
4834
4886
|
trimmed.fields.push(fieldData);
|
|
4835
4887
|
});
|
|
4836
|
-
// console.log("Schema apres transformation, contexte NG:", trimmed);
|
|
4837
4888
|
return trimmed;
|
|
4838
4889
|
case "ecoll-veto":
|
|
4839
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
4840
4890
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
4841
4891
|
if (mergedItemsSchema) {
|
|
4842
4892
|
const trimmedSchema = {
|
|
@@ -4881,7 +4931,6 @@ const VoiceFormRecorder = class {
|
|
|
4881
4931
|
default: '',
|
|
4882
4932
|
};
|
|
4883
4933
|
});
|
|
4884
|
-
// console.log("Schema apres transformation, contexte Track:", trimmedSchema);
|
|
4885
4934
|
return trimmedSchema;
|
|
4886
4935
|
}
|
|
4887
4936
|
case "track":
|
|
@@ -5078,13 +5127,11 @@ const VoiceFormRecorder = class {
|
|
|
5078
5127
|
return (h$1("div", { class: "form-preview" }, h$1("div", { class: "form-preview-title" }, title), h$1("form", { class: "voice-filled-form" }, this.renderFormFields())));
|
|
5079
5128
|
}
|
|
5080
5129
|
renderFormFields() {
|
|
5081
|
-
// console.log("renderFormFields", this.parsedSchema);
|
|
5082
5130
|
if (!this.parsedSchema)
|
|
5083
5131
|
return null;
|
|
5084
5132
|
const largeFieldTypes = ['select', 'multiselect', 'classification', 'thesaurus', 'position'];
|
|
5085
5133
|
switch (this.context) {
|
|
5086
5134
|
case "ecoteka":
|
|
5087
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
5088
5135
|
case "ng":
|
|
5089
5136
|
return this.parsedSchema.Children.map((child) => {
|
|
5090
5137
|
var _a, _b, _c;
|
|
@@ -5096,7 +5143,7 @@ const VoiceFormRecorder = class {
|
|
|
5096
5143
|
const isRequired = child.Required || false;
|
|
5097
5144
|
const fieldValue = (_b = child.Settings) === null || _b === void 0 ? void 0 : _b.Default_Value;
|
|
5098
5145
|
const isLargeField = largeFieldTypes.includes(fieldType) || (child.Children && child.Children.length > 5);
|
|
5099
|
-
return (h$1("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h$1("span", { class: "required" }, "*")), this.renderFormField(fieldName, {
|
|
5146
|
+
return (h$1("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h$1("span", { class: "required" }, "*"), this.renderConfidenceIndicator(this.getFieldConfidence(fieldLabel, fieldName))), this.renderFormField(fieldName, {
|
|
5100
5147
|
type: fieldType,
|
|
5101
5148
|
title: fieldLabel,
|
|
5102
5149
|
required: isRequired,
|
|
@@ -5104,8 +5151,6 @@ const VoiceFormRecorder = class {
|
|
|
5104
5151
|
}, fieldValue)));
|
|
5105
5152
|
}).filter(Boolean);
|
|
5106
5153
|
case "ecoll-veto":
|
|
5107
|
-
// NOTE STEP 2
|
|
5108
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
5109
5154
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
5110
5155
|
return Object.entries(mergedItemsSchema).map(([key, field]) => {
|
|
5111
5156
|
var _a, _b;
|
|
@@ -5136,7 +5181,7 @@ const VoiceFormRecorder = class {
|
|
|
5136
5181
|
// console.error("Error fetching classification data:", error);
|
|
5137
5182
|
// }
|
|
5138
5183
|
// }
|
|
5139
|
-
return (h$1("div", { class: "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h$1("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))));
|
|
5184
|
+
return (h$1("div", { class: "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h$1("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))));
|
|
5140
5185
|
});
|
|
5141
5186
|
case "track":
|
|
5142
5187
|
default:
|
|
@@ -5145,7 +5190,7 @@ const VoiceFormRecorder = class {
|
|
|
5145
5190
|
if (field.type === 'header') {
|
|
5146
5191
|
return (h$1("div", { class: "form-header", key: fieldName }, field.title || fieldName));
|
|
5147
5192
|
}
|
|
5148
|
-
return (h$1("div", { class: "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && h$1("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))));
|
|
5193
|
+
return (h$1("div", { class: "form-group", key: fieldName }, h$1("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && h$1("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))));
|
|
5149
5194
|
});
|
|
5150
5195
|
}
|
|
5151
5196
|
}
|