wiki-pathology at DuckDuckGo

Pathology

Pathology is the study of disease. The word pathology also refers to the study of disease in general, incorporating a wide range of biology research fields and medical practices. More at Wikipedia

Content fetched from html.duckduckgo.com
    #awc-container {
        --bg: #09090b; --surface: #18181b; --primary: #6366f1; --accent: #10b981; --danger: #ef4444;
        --text: #f4f4f5; --muted: #a1a1aa;
        font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text);
        height: 600px; width: 100%; max-width: 800px; margin: 40px auto; border-radius: 12px;
        overflow: hidden; position: relative; display: flex; flex-direction: column;
        box-shadow: 0 10px 30px rgba(0,0,0,0.5); border: 1px solid #27272a;
    }
    #awc-loader { position: absolute; inset: 0; background: var(--bg); z-index: 100; display: flex; flex-direction: column; align-items: center; justify-content: center; }
    .awc-ring { width: 40px; height: 40px; border: 3px solid var(--surface); border-top-color: var(--primary); border-radius: 50%; animation: awc-spin 1s linear infinite; margin-bottom: 15px; }
    @keyframes awc-spin { to { transform: rotate(360deg); } }

    #awc-app { display:none; height:100%; flex-direction:column; }
    #awc-header { padding: 10px 15px; background: rgba(24, 24, 27, 0.95); border-bottom: 1px solid #27272a; display: flex; justify-content: space-between; align-items: center; }

    /* Fine-Tuning Panel */
    #awc-finetune-panel {
        background: #18181b; border-bottom: 1px solid #27272a; padding: 10px 15px;
        display: flex; gap: 10px; align-items: center; font-size: 12px;
    }
    #awc-bias-input {
        background: #09090b; border: 1px solid #3f3f46; color: white; padding: 5px 10px;
        border-radius: 4px; flex-grow: 1; font-family: 'JetBrains Mono', monospace;
    }
    #awc-set-bias-btn {
        background: var(--primary); color: white; border: none; padding: 5px 10px;
        border-radius: 4px; cursor: pointer; font-size: 11px; text-transform: uppercase;
    }

    #awc-visualizer { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; background: radial-gradient(circle at center, #1e1e24 0%, #09090b 70%); }
    .awc-avatar { width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #312e81, #4338ca); display: flex; align-items: center; justify-content: center; font-size: 28px; transition: all 0.3s; }
    .awc-avatar.listening { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0.2); background: #ef4444; transform: scale(1.1); }
    .awc-avatar.speaking { box-shadow: 0 0 20px 5px rgba(16, 185, 129, 0.3); animation: awc-breathe 2s infinite; }
    @keyframes awc-breathe { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }

    #awc-transcript { margin-top: 15px; text-align: center; padding: 0 20px; font-size: 14px; min-height: 40px; color: #e4e4e7; max-width: 90%; }

    #awc-footer { padding: 15px; background: #18181b; border-top: 1px solid #27272a; display: flex; justify-content: center; gap: 15px; }
    .awc-btn { width: 45px; height: 45px; border-radius: 50%; border: none; cursor: pointer; color: white; font-size: 18px; display: flex; align-items: center; justify-content: center; transition: transform 0.2s; background: #27272a; border: 1px solid #3f3f46; }
    .awc-btn.active { background: var(--danger); border-color: var(--danger); animation: awc-pulse-red 1.5s infinite; }
    @keyframes awc-pulse-red { 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); } }

    .awc-status-pill { font-size: 10px; padding: 3px 8px; border-radius: 20px; background: #27272a; display: flex; gap: 5px; align-items: center; text-transform: uppercase; letter-spacing: 0.5px; }
    .awc-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); }
    .awc-dot.busy { background: var(--primary); animation: awc-pulse 1s infinite; }


    <div id="awc-container">
        <div id="awc-loader">
            <div class="awc-ring"></div>
            <div style="font-size:12px;color:var(--muted)" id="awc-load-text">Initializing Whisper Engine...</div>
        </div>

        <div id="awc-app">
            <!-- Fine-Tuning / Biasing Header -->
            <div id="awc-finetune-panel">
                <span style="color:var(--muted);white-space:nowrap">🧠 Vocabulary Bias:</span>

                <button id="awc-set-bias-btn">Update</button>
            </div>

            <div id="awc-header">
                <span style="font-weight:600;font-size:14px">πŸ“ž Context Assistant</span>
                <div class="awc-status-pill">
                    <div class="awc-dot" id="awc-status-dot"></div>
                    <span id="awc-status-text">IDLE</span>
                </div>
            </div>

            <div id="awc-visualizer">
                <div class="awc-avatar" id="awc-avatar">πŸ€–</div>
                <div id="awc-transcript">Tap microphone to speak...</div>
            </div>

            <div id="awc-footer">
                <button class="awc-btn" id="awc-mic" title="Speak">🎀</button>
            </div>
        </div>
    </div>


    import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.0';
    import * as webllm from "https://esm.run/@mlc-ai/web-llm";

    // --- CONFIGURATION ---
    const WHISPER_MODEL = "Xenova/whisper-tiny.en"; // Tiny English for speed
    const LLM_MODEL = "SmolLM2-360M-Instruct-q4f16_1-MLC";

    // --- STATE ---
    let transcriber = null;
    let llmEngine = null;
    let isLoaded = false;
    let isListening = false;
    let isSpeaking = false;
    let mediaRecorder = null;
    let audioChunks = [];
    let synth = window.speechSynthesis;
    let conversationHistory = [];
    let vocabularyHint = document.getElementById('awc-bias-input').value;

    // --- DOM ELEMENTS ---
    const els = {
        loader: document.getElementById('awc-loader'),
        loadText: document.getElementById('awc-load-text'),
        app: document.getElementById('awc-app'),
        micBtn: document.getElementById('awc-mic'),
        avatar: document.getElementById('awc-avatar'),
        transcript: document.getElementById('awc-transcript'),
        statusDot: document.getElementById('awc-status-dot'),
        statusText: document.getElementById('awc-status-text'),
        biasInput: document.getElementById('awc-bias-input'),
        setBiasBtn: document.getElementById('awc-set-bias-btn')
    };

    // --- INITIALIZATION ---
    async function init() {
        try {
            // Load Whisper
            els.loadText.innerText = "Loading Whisper ASR...";
            transcriber = await pipeline('automatic-speech-recognition', WHISPER_MODEL);

            // Load LLM
            els.loadText.innerText = "Loading Neural Core...";
            llmEngine = await webllm.CreateMLCEngine(LLM_MODEL, {
                initProgressCallback: (p) =&gt; {
                    if(p.progress === 1 &amp;&amp; !isLoaded) finishLoading();
                }
            });
        } catch (e) {
            console.error(e);
            els.loadText.innerText = "Error loading models. Falling back to System Speech...";
            fallbackToSystemSpeech();
        }
    }

    function finishLoading() {
        isLoaded = true;
        els.loader.style.display = 'none';
        els.app.style.display = 'flex';

        // Set Initial Persona
        const pageTitle = document.querySelector('h1') ? document.querySelector('h1').innerText : "General Topic";
        conversationHistory.push({
            role: "system",
            content: `You are an expert assistant discussing "${pageTitle}". Use concise language.`
        });
    }

    // --- FALLBACK SYSTEM SPEECH ---
    let systemRecognition = null;
    function fallbackToSystemSpeech() {
        if ('webkitSpeechRecognition' in window) {
            systemRecognition = new webkitSpeechRecognition();
            systemRecognition.continuous = false;
            systemRecognition.lang = 'en-US';
            systemRecognition.onresult = (e) =&gt; handleUserMessage(e.results[0][0].transcript);
            systemRecognition.onend = () =&gt; {
                isListening = false;
                els.micBtn.classList.remove('active');
                els.avatar.classList.remove('listening');
            };
            els.loader.style.display = 'none';
            els.app.style.display = 'flex';
            els.loadText.innerText = "System Speech Ready";
            setTimeout(finishLoading, 1000); // Still try to load LLM in background
        } else {
            els.loadText.innerText = "Browser not supported.";
        }
    }

    // --- RECORDING LOGIC (WHISPER) ---
    els.micBtn.onclick = async () =&gt; {
        if (!isLoaded &amp;&amp; !systemRecognition) return;

        if (isListening) {
            stopRecording();
        } else {
            startRecording();
        }
    };

    async function startRecording() {
        isListening = true;
        audioChunks = [];
        els.micBtn.classList.add('active');
        els.avatar.classList.add('listening');
        setStatus("LISTENING", "busy");
        els.transcript.innerText = "Listening...";

        try {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            mediaRecorder = new MediaRecorder(stream);
            mediaRecorder.ondataavailable = e =&gt; audioChunks.push(e.data);
            mediaRecorder.onstop = () =&gt; processAudio();
            mediaRecorder.start();
        } catch (err) {
            console.error(err);
            setStatus("ERROR", "");
            isListening = false;
        }
    }

    function stopRecording() {
        if (mediaRecorder &amp;&amp; mediaRecorder.state === 'recording') {
            mediaRecorder.stop();
            mediaRecorder.stream.getTracks().forEach(track =&gt; track.stop());
        } else if (systemRecognition) {
            systemRecognition.stop();
        }
    }

    async function processAudio() {
        setStatus("TRANSCRIBING", "busy");
        els.avatar.classList.remove('listening');

        const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
        const audioURL = URL.createObjectURL(audioBlob);

        try {
            // Use Vocabulary Hint for Biasing
            const result = await transcriber(audioURL, {
                language: "english",
                task: "transcribe",
                // This is the "Fine-Tuning" simulation via decoding hints
                language_detection_hint: vocabularyHint 
            });

            const text = result.text.trim();
            els.transcript.innerText = text;
            handleUserMessage(text);
        } catch (e) {
            console.error(e);
            els.transcript.innerText = "Transcription failed.";
        } finally {
            URL.revokeObjectURL(audioURL);
        }
    }

    // --- AGENT LOGIC ---
    async function handleUserMessage(text) {
        if(!llmEngine) return; // Wait for LLM if using Whisper

        setStatus("THINKING", "busy");
        conversationHistory.push({ role: "user", content: text });

        try {
            const stream = await llmEngine.chat.completions.create({
                messages: conversationHistory,
                stream: true,
                temperature: 0.7
            });

            let fullResponse = "";
            for await (const chunk of stream) {
                const delta = chunk.choices[0]?.delta?.content || "";
                fullResponse += delta;
                els.transcript.innerText = fullResponse;
            }

            speak(fullResponse);
            conversationHistory.push({ role: "assistant", content: fullResponse });
        } catch (e) {
            els.transcript.innerText = "LLM Error: " + e.message;
        }
    }

    // --- TEXT TO SPEECH ---
    function speak(text) {
        if (!text) return;
        synth.cancel();
        const utterance = new SpeechSynthesisUtterance(text);
        utterance.rate = 1.1;
        utterance.onstart = () =&gt; {
            isSpeaking = true;
            els.avatar.classList.add('speaking');
            setStatus("SPEAKING", "busy");
        };
        utterance.onend = () =&gt; {
            isSpeaking = false;
            els.avatar.classList.remove('speaking');
            setStatus("IDLE", "");
        };
        synth.speak(utterance);
    }

    // --- BIASING UI ---
    els.setBiasBtn.onclick = () =&gt; {
        vocabularyHint = els.biasInput.value;
        els.setBiasBtn.innerText = "Saved!";
        setTimeout(() =&gt; els.setBiasBtn.innerText = "Update", 2000);
    };

    function setStatus(text, type) {
        els.statusText.innerText = text;
        els.statusDot.className = `awc-dot ${type}`;
    }

    // Start
    init();