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
|
@@ -59,9 +59,6 @@
|
|
|
59
59
|
transform: scale(1.05);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
.upload-record-button svg path {
|
|
63
|
-
/* Color applied via inline style from theme */
|
|
64
|
-
}
|
|
65
62
|
|
|
66
63
|
.record-button {
|
|
67
64
|
display: flex;
|
|
@@ -341,6 +338,32 @@ input[type="date"].form-input {
|
|
|
341
338
|
user-select: none;
|
|
342
339
|
}
|
|
343
340
|
|
|
341
|
+
/* Confidence score badges */
|
|
342
|
+
.confidence-badge {
|
|
343
|
+
display: inline-flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
margin-left: 4px;
|
|
346
|
+
padding: 1px 5px;
|
|
347
|
+
border-radius: 3px;
|
|
348
|
+
font-size: 0.65rem;
|
|
349
|
+
font-weight: 700;
|
|
350
|
+
vertical-align: middle;
|
|
351
|
+
line-height: 1.4;
|
|
352
|
+
cursor: help;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.confidence-medium {
|
|
356
|
+
background: #fef3c7;
|
|
357
|
+
color: #92400e;
|
|
358
|
+
border: 1px solid #fde68a;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.confidence-low {
|
|
362
|
+
background: #fee2e2;
|
|
363
|
+
color: #991b1b;
|
|
364
|
+
border: 1px solid #fca5a5;
|
|
365
|
+
}
|
|
366
|
+
|
|
344
367
|
/* Skeleton loading state */
|
|
345
368
|
@keyframes skeleton-shimmer {
|
|
346
369
|
0% { background-position: -200% 0; }
|
|
@@ -40,6 +40,7 @@ export class VoiceFormRecorder {
|
|
|
40
40
|
this.hasTransientError = false;
|
|
41
41
|
this.transcription = '';
|
|
42
42
|
this.filledData = null;
|
|
43
|
+
this.confidenceScores = null;
|
|
43
44
|
this.debugInfo = {};
|
|
44
45
|
this.isReadonlyMode = true; // Start in readonly preview mode
|
|
45
46
|
this.isInitializing = true;
|
|
@@ -287,6 +288,7 @@ export class VoiceFormRecorder {
|
|
|
287
288
|
return;
|
|
288
289
|
this.isProcessing = true;
|
|
289
290
|
this.hasTransientError = false;
|
|
291
|
+
this.confidenceScores = null;
|
|
290
292
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.processing) || (this.language == 'en' ? 'Processing document...' : 'Traitement du document ...');
|
|
291
293
|
try {
|
|
292
294
|
const extractedData = (ocrData === null || ocrData === void 0 ? void 0 : ocrData.content) || ocrData;
|
|
@@ -304,10 +306,11 @@ export class VoiceFormRecorder {
|
|
|
304
306
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
305
307
|
}
|
|
306
308
|
this.filledData = this.extractFilledData(filledSchema);
|
|
307
|
-
this.
|
|
309
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
310
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
308
311
|
this.parsedSchema = this.filledData;
|
|
309
312
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
310
|
-
this.formFilled.emit({ success: true, data: this.filledData, jsonForm });
|
|
313
|
+
this.formFilled.emit({ success: true, data: this.filledData, jsonForm, confidence: this.confidenceScores });
|
|
311
314
|
}
|
|
312
315
|
catch (error) {
|
|
313
316
|
console.error('OCR processing error:', error);
|
|
@@ -326,6 +329,7 @@ export class VoiceFormRecorder {
|
|
|
326
329
|
return;
|
|
327
330
|
this.isProcessing = true;
|
|
328
331
|
this.hasTransientError = false;
|
|
332
|
+
this.confidenceScores = null;
|
|
329
333
|
this.updateDebugInfo('Audio Captured', { size: audioFile.size, type: audioFile.type });
|
|
330
334
|
try {
|
|
331
335
|
// Validate audio file before sending to API
|
|
@@ -346,7 +350,6 @@ export class VoiceFormRecorder {
|
|
|
346
350
|
this.formFilled.emit({ success: false, error: 'Audio file too large' });
|
|
347
351
|
return;
|
|
348
352
|
}
|
|
349
|
-
// Step 1: Transcription
|
|
350
353
|
let transcription;
|
|
351
354
|
try {
|
|
352
355
|
this.statusMessage = ((_a = this.parsedTheme.texts) === null || _a === void 0 ? void 0 : _a.transcribing) || (this.language == 'en' ? 'Transcribing speech...' : 'Transcription du texte ...');
|
|
@@ -365,7 +368,6 @@ export class VoiceFormRecorder {
|
|
|
365
368
|
this.formFilled.emit({ success: false, error: 'No speech detected', transcription: '' });
|
|
366
369
|
return;
|
|
367
370
|
}
|
|
368
|
-
// Step 2: LLM form filling
|
|
369
371
|
let filledSchema;
|
|
370
372
|
try {
|
|
371
373
|
this.statusMessage = ((_b = this.parsedTheme.texts) === null || _b === void 0 ? void 0 : _b.filling) || (this.language == 'en' ? 'Filling form fields...' : 'Remplissage du formulaire ...');
|
|
@@ -376,10 +378,11 @@ export class VoiceFormRecorder {
|
|
|
376
378
|
throw Object.assign(new Error(llmError.message), { phase: 'llm' });
|
|
377
379
|
}
|
|
378
380
|
this.filledData = this.extractFilledData(filledSchema);
|
|
379
|
-
this.
|
|
381
|
+
this.confidenceScores = this.extractConfidenceScores(filledSchema);
|
|
382
|
+
this.updateDebugInfo('Form Filled', { filledSchema, extractedData: this.filledData, confidence: this.confidenceScores });
|
|
380
383
|
this.parsedSchema = this.filledData;
|
|
381
384
|
this.statusMessage = ((_c = this.parsedTheme.texts) === null || _c === void 0 ? void 0 : _c.completed) || (this.language == 'en' ? 'Form completed!' : 'Formulaire rempli !');
|
|
382
|
-
this.formFilled.emit({ success: true, data: this.filledData, transcription });
|
|
385
|
+
this.formFilled.emit({ success: true, data: this.filledData, transcription, confidence: this.confidenceScores });
|
|
383
386
|
}
|
|
384
387
|
catch (error) {
|
|
385
388
|
console.error('Audio processing error:', error);
|
|
@@ -403,7 +406,8 @@ export class VoiceFormRecorder {
|
|
|
403
406
|
try {
|
|
404
407
|
const audioBlob = await this.audioRecorder.stopRecording();
|
|
405
408
|
const audioContent = new File([audioBlob], 'audio.wav', { type: 'audio/wav' });
|
|
406
|
-
//
|
|
409
|
+
// Reset isProcessing so the guard inside processAudioContent doesn't block this internal call
|
|
410
|
+
this.isProcessing = false;
|
|
407
411
|
await this.processAudioContent(audioContent);
|
|
408
412
|
}
|
|
409
413
|
catch (error) {
|
|
@@ -420,12 +424,52 @@ export class VoiceFormRecorder {
|
|
|
420
424
|
this.recordingStateChanged.emit({ isRecording: false, state: 'idle' });
|
|
421
425
|
}
|
|
422
426
|
}
|
|
427
|
+
extractConfidenceScores(filledData) {
|
|
428
|
+
const scores = {};
|
|
429
|
+
if (!filledData)
|
|
430
|
+
return scores;
|
|
431
|
+
// Fields array format: [{"id": "...", "name": "...", "value": "...", "confidence": 0.87}]
|
|
432
|
+
if (filledData.fields && Array.isArray(filledData.fields)) {
|
|
433
|
+
filledData.fields.forEach((field) => {
|
|
434
|
+
const raw = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
435
|
+
if (!isNaN(raw)) {
|
|
436
|
+
const clamped = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
437
|
+
// Store under both id and name so label-based lookups always work
|
|
438
|
+
if (field.id)
|
|
439
|
+
scores[field.id] = clamped;
|
|
440
|
+
if (field.name)
|
|
441
|
+
scores[field.name] = clamped;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
// Simple schema format: {"schema": {...}, "confidence": {"field_name": 0.87}}
|
|
446
|
+
if (filledData.confidence && typeof filledData.confidence === 'object') {
|
|
447
|
+
Object.entries(filledData.confidence).forEach(([k, v]) => {
|
|
448
|
+
const raw = typeof v === 'number' ? v : parseFloat(v);
|
|
449
|
+
if (!isNaN(raw)) {
|
|
450
|
+
scores[k] = Math.min(0.99, Math.max(0.01, Math.round(raw * 100) / 100));
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return scores;
|
|
455
|
+
}
|
|
456
|
+
getFieldConfidence(primary, fallback) {
|
|
457
|
+
var _a, _b;
|
|
458
|
+
if (!this.confidenceScores)
|
|
459
|
+
return null;
|
|
460
|
+
return (_a = this.confidenceScores[primary]) !== null && _a !== void 0 ? _a : (fallback ? ((_b = this.confidenceScores[fallback]) !== null && _b !== void 0 ? _b : null) : null);
|
|
461
|
+
}
|
|
462
|
+
renderConfidenceIndicator(confidence) {
|
|
463
|
+
if (confidence === null || confidence >= 0.95)
|
|
464
|
+
return null;
|
|
465
|
+
const pct = Math.round(confidence * 100);
|
|
466
|
+
const isApproximate = confidence >= 0.85;
|
|
467
|
+
return (h("span", { class: `confidence-badge ${isApproximate ? 'confidence-medium' : 'confidence-low'}`, title: this.language === 'en' ? `Confidence: ${pct}%` : `Confiance : ${pct}%` }, pct, "%"));
|
|
468
|
+
}
|
|
423
469
|
extractFilledData(filledData) {
|
|
424
|
-
// console.log("extractFilledData", filledData);
|
|
425
470
|
const updatedSchema = JSON.parse(JSON.stringify(this.parsedSchema));
|
|
426
471
|
switch (this.context) {
|
|
427
472
|
case "ecoteka":
|
|
428
|
-
// console.log("TODO extractFilledData", filledData);
|
|
429
473
|
case "ng":
|
|
430
474
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
431
475
|
// Map AI response back to original schema structure
|
|
@@ -435,12 +479,13 @@ export class VoiceFormRecorder {
|
|
|
435
479
|
if (!originalField.Settings)
|
|
436
480
|
originalField.Settings = {};
|
|
437
481
|
originalField.Settings.Default_Value = field.value;
|
|
482
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
483
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
438
484
|
}
|
|
439
485
|
});
|
|
440
486
|
}
|
|
441
487
|
break;
|
|
442
488
|
case "ecoll-veto":
|
|
443
|
-
// console.log("TODO extractFilledData", filledData, updatedSchema);
|
|
444
489
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
445
490
|
// Map AI response back to original schema structure
|
|
446
491
|
filledData.fields.forEach((field) => {
|
|
@@ -449,6 +494,8 @@ export class VoiceFormRecorder {
|
|
|
449
494
|
originalField = updatedSchema[1].items.find((child) => child.label === field.name);
|
|
450
495
|
if (originalField && field.value !== undefined && field.value !== null && field.value !== '') {
|
|
451
496
|
updatedSchema[2][originalField.name] = field.value;
|
|
497
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
498
|
+
originalField.confidence = isNaN(rawConf) ? null : rawConf;
|
|
452
499
|
}
|
|
453
500
|
});
|
|
454
501
|
}
|
|
@@ -458,11 +505,14 @@ export class VoiceFormRecorder {
|
|
|
458
505
|
if (filledData === null || filledData === void 0 ? void 0 : filledData.fields) {
|
|
459
506
|
Object.entries(filledData.fields).forEach(([fieldID, field]) => {
|
|
460
507
|
if (field.value !== undefined && field.value !== null && field.value !== '') {
|
|
508
|
+
const rawConf = typeof field.confidence === 'number' ? field.confidence : parseFloat(field.confidence);
|
|
509
|
+
const confidence = isNaN(rawConf) ? null : rawConf;
|
|
461
510
|
if (field.id) {
|
|
462
511
|
const schemaField = updatedSchema.fields[field.id];
|
|
463
512
|
if (schemaField) {
|
|
464
513
|
schemaField.value = field.value;
|
|
465
514
|
schemaField.default = field.value;
|
|
515
|
+
schemaField.confidence = confidence;
|
|
466
516
|
}
|
|
467
517
|
}
|
|
468
518
|
else {
|
|
@@ -472,6 +522,7 @@ export class VoiceFormRecorder {
|
|
|
472
522
|
if (schemaField.title === fieldName) {
|
|
473
523
|
schemaField.value = field.value;
|
|
474
524
|
schemaField.default = field.value;
|
|
525
|
+
schemaField.confidence = confidence;
|
|
475
526
|
break;
|
|
476
527
|
}
|
|
477
528
|
}
|
|
@@ -481,15 +532,12 @@ export class VoiceFormRecorder {
|
|
|
481
532
|
}
|
|
482
533
|
break;
|
|
483
534
|
}
|
|
484
|
-
// console.log("extractFilledData result", updatedSchema);
|
|
485
535
|
return updatedSchema;
|
|
486
536
|
}
|
|
487
537
|
async trimSchemaForAI(schema) {
|
|
488
538
|
var _a, _b;
|
|
489
|
-
// console.log("trimSchemaForAI", schema);
|
|
490
539
|
switch (this.context) {
|
|
491
540
|
case "ecoteka":
|
|
492
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
493
541
|
case "ng":
|
|
494
542
|
const trimmed = { fields: [] };
|
|
495
543
|
schema.Children.forEach((child) => {
|
|
@@ -508,10 +556,8 @@ export class VoiceFormRecorder {
|
|
|
508
556
|
}
|
|
509
557
|
trimmed.fields.push(fieldData);
|
|
510
558
|
});
|
|
511
|
-
// console.log("Schema apres transformation, contexte NG:", trimmed);
|
|
512
559
|
return trimmed;
|
|
513
560
|
case "ecoll-veto":
|
|
514
|
-
// console.log("TODO trimSchemaForAI", schema)
|
|
515
561
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
516
562
|
if (mergedItemsSchema) {
|
|
517
563
|
const trimmedSchema = {
|
|
@@ -558,7 +604,6 @@ export class VoiceFormRecorder {
|
|
|
558
604
|
default: '',
|
|
559
605
|
};
|
|
560
606
|
});
|
|
561
|
-
// console.log("Schema apres transformation, contexte Track:", trimmedSchema);
|
|
562
607
|
return trimmedSchema;
|
|
563
608
|
}
|
|
564
609
|
case "track":
|
|
@@ -755,13 +800,11 @@ export class VoiceFormRecorder {
|
|
|
755
800
|
return (h("div", { class: "form-preview" }, h("div", { class: "form-preview-title" }, title), h("form", { class: "voice-filled-form" }, this.renderFormFields())));
|
|
756
801
|
}
|
|
757
802
|
renderFormFields() {
|
|
758
|
-
// console.log("renderFormFields", this.parsedSchema);
|
|
759
803
|
if (!this.parsedSchema)
|
|
760
804
|
return null;
|
|
761
805
|
const largeFieldTypes = ['select', 'multiselect', 'classification', 'thesaurus', 'position'];
|
|
762
806
|
switch (this.context) {
|
|
763
807
|
case "ecoteka":
|
|
764
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
765
808
|
case "ng":
|
|
766
809
|
return this.parsedSchema.Children.map((child) => {
|
|
767
810
|
var _a, _b, _c;
|
|
@@ -773,7 +816,7 @@ export class VoiceFormRecorder {
|
|
|
773
816
|
const isRequired = child.Required || false;
|
|
774
817
|
const fieldValue = (_b = child.Settings) === null || _b === void 0 ? void 0 : _b.Default_Value;
|
|
775
818
|
const isLargeField = largeFieldTypes.includes(fieldType) || (child.Children && child.Children.length > 5);
|
|
776
|
-
return (h("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h("span", { class: "required" }, "*")), this.renderFormField(fieldName, {
|
|
819
|
+
return (h("div", { class: isLargeField ? "form-group full-width" : "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && h("span", { class: "required" }, "*"), this.renderConfidenceIndicator(this.getFieldConfidence(fieldLabel, fieldName))), this.renderFormField(fieldName, {
|
|
777
820
|
type: fieldType,
|
|
778
821
|
title: fieldLabel,
|
|
779
822
|
required: isRequired,
|
|
@@ -781,8 +824,6 @@ export class VoiceFormRecorder {
|
|
|
781
824
|
}, fieldValue)));
|
|
782
825
|
}).filter(Boolean);
|
|
783
826
|
case "ecoll-veto":
|
|
784
|
-
// NOTE STEP 2
|
|
785
|
-
// console.log("TODO renderFormFields", this.parsedSchema);
|
|
786
827
|
const mergedItemsSchema = (this.parsedSchema[0].items).concat(this.parsedSchema[1].items);
|
|
787
828
|
return Object.entries(mergedItemsSchema).map(([key, field]) => {
|
|
788
829
|
var _a, _b;
|
|
@@ -813,7 +854,7 @@ export class VoiceFormRecorder {
|
|
|
813
854
|
// console.error("Error fetching classification data:", error);
|
|
814
855
|
// }
|
|
815
856
|
// }
|
|
816
|
-
return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && 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))));
|
|
857
|
+
return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, fieldLabel, isRequired && 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))));
|
|
817
858
|
});
|
|
818
859
|
case "track":
|
|
819
860
|
default:
|
|
@@ -822,7 +863,7 @@ export class VoiceFormRecorder {
|
|
|
822
863
|
if (field.type === 'header') {
|
|
823
864
|
return (h("div", { class: "form-header", key: fieldName }, field.title || fieldName));
|
|
824
865
|
}
|
|
825
|
-
return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && 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))));
|
|
866
|
+
return (h("div", { class: "form-group", key: fieldName }, h("label", { htmlFor: fieldName, class: "form-label" }, field.title || fieldName, field.required && 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))));
|
|
826
867
|
});
|
|
827
868
|
}
|
|
828
869
|
}
|
|
@@ -1281,6 +1322,7 @@ export class VoiceFormRecorder {
|
|
|
1281
1322
|
"hasTransientError": {},
|
|
1282
1323
|
"transcription": {},
|
|
1283
1324
|
"filledData": {},
|
|
1325
|
+
"confidenceScores": {},
|
|
1284
1326
|
"debugInfo": {},
|
|
1285
1327
|
"isReadonlyMode": {},
|
|
1286
1328
|
"isInitializing": {}
|