Phase 1 — Critical Fixes: - Fix SOAP instructions not clearing on Clear button - Show transcription provider (AWS/OpenAI) in UI toast - Fix silent transcription failures in dictation and SOAP modules - Add IndexedDB audio backup system (24hr retention, retry from Settings) - Prevent duplicate encounter saves with idempotency keys - Add Save/Load/New bar to SOAP note generator Phase 2 — Features: - Dragon-like AI memory: auto-track user corrections, inject into prompts - Per-section template categories (SOAP, HPI, well visit, sick visit) - Bigger textarea for SOAP instructions - S3 document upload/management (AWS S3, Backblaze B2, MinIO compatible) - Faster transcription via lower bitrate recording (16kbps opus) Phase 3 — APK & CI/CD: - GitHub Actions: Docker build+push on version tags - GitHub Actions: TWA APK build for Obtainium auto-updates - Android TWA project with foreground service for background recording - Enhanced PWA manifest with shortcuts and maskable icons
86 lines
2.9 KiB
JavaScript
86 lines
2.9 KiB
JavaScript
// ============================================================
|
|
// CORRECTION TRACKER — Dragon-like AI learning from user edits
|
|
// Tracks when users edit AI-generated output and saves corrections
|
|
// so the AI can learn user preferences over time.
|
|
// ============================================================
|
|
|
|
(function() {
|
|
|
|
// Store original AI outputs per element
|
|
var _originals = {};
|
|
|
|
// Track an output element: store original text when AI generates it
|
|
window.trackAIOutput = function(elementId, originalText) {
|
|
if (!elementId || !originalText) return;
|
|
_originals[elementId] = originalText.trim();
|
|
};
|
|
|
|
// Save correction when user is done editing (call on save or blur)
|
|
window.saveCorrection = function(elementId, section) {
|
|
var original = _originals[elementId];
|
|
if (!original) return;
|
|
var el = document.getElementById(elementId);
|
|
if (!el) return;
|
|
var current = (el.innerText || el.textContent || '').trim();
|
|
if (!current || current === original) return;
|
|
|
|
// Only save if there's a meaningful difference (not just whitespace)
|
|
var origWords = original.split(/\s+/).length;
|
|
var currWords = current.split(/\s+/).length;
|
|
var wordDiff = Math.abs(origWords - currWords);
|
|
// Require at least some meaningful change
|
|
if (wordDiff < 2 && original.length > 100) {
|
|
// Check character-level difference
|
|
var charDiff = Math.abs(original.length - current.length);
|
|
if (charDiff < 20) return; // too minor
|
|
}
|
|
|
|
// Find the most significant changed section (not full text)
|
|
var origSnippet = extractDiffSnippet(original, current);
|
|
var corrSnippet = extractDiffSnippet(current, original);
|
|
|
|
if (!origSnippet || !corrSnippet) {
|
|
origSnippet = original.substring(0, 500);
|
|
corrSnippet = current.substring(0, 500);
|
|
}
|
|
|
|
fetch('/api/memories/correction', {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({
|
|
section: section || 'encounter',
|
|
original_snippet: origSnippet,
|
|
corrected_snippet: corrSnippet
|
|
})
|
|
}).then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success && !data.skipped) {
|
|
console.log('[CorrectionTracker] Saved correction for', section);
|
|
}
|
|
}).catch(function() {});
|
|
|
|
// Clear so we don't re-save
|
|
delete _originals[elementId];
|
|
};
|
|
|
|
// Extract the most changed portion between two texts
|
|
function extractDiffSnippet(text1, text2) {
|
|
var lines1 = text1.split('\n');
|
|
var lines2 = text2.split('\n');
|
|
var changed = [];
|
|
var maxLen = Math.max(lines1.length, lines2.length);
|
|
|
|
for (var i = 0; i < maxLen; i++) {
|
|
var l1 = (lines1[i] || '').trim();
|
|
var l2 = (lines2[i] || '').trim();
|
|
if (l1 !== l2 && l1) {
|
|
changed.push(l1);
|
|
}
|
|
}
|
|
|
|
if (changed.length === 0) return null;
|
|
return changed.slice(0, 10).join('\n').substring(0, 1000);
|
|
}
|
|
|
|
console.log('Correction tracker loaded');
|
|
})();
|