Magazine P1:
- Images shown automatically during playback (assign_images_to_articles
links each figure to the article whose column it falls in; the client
shows the last image reached in reading order as playback advances).
- "Play all articles" queues every approved article and advances
automatically at the end of each rather than stopping.
- Word-level highlighting, explicitly documented as an ESTIMATE (no
forced-alignment/timestamp source exists): each word's share of the
sentence's real measured audio duration is proportional to its
character length. A checkbox turns it off; sentence highlighting
(measured, not estimated) is unaffected.
- Driving mode: full-screen overlay, two oversized controls, reuses
whatever is already playing/queued.
- Sidebars/pull-out boxes excluded from narration automatically
(mark_sidebars: an anomalously narrow column vs. the page's regular
grid). Fixed a real defect found while building this: a full-width
headline was left in the column-width analysis and could bridge two
genuinely separate columns into one, hiding a real sidebar column
entirely -- caught by a failing test before the fix.
Second OCR engine + fallback ladder:
- KDL Frontier Parser nano wired in via vision_ocr_page_chain (ordered
engine list, first usable result wins, tesseract last resort).
Confirmed live: ~3x faster than Baidu (~12s vs ~40s/page) but returns
plain text with NO coordinate markers, unlike Baidu's labeled regions.
_whole_page_region treats the whole response as one ambiguous
full-page block (gated by ocr_quality_score) rather than discarding
usable text just because it lacks layout structure.
OCR engines page + a real UI bug:
- The OCR & Document Parsing page had no .s-engines-page[data-page="ocr"]
markup at all -- renderLocalContainers already supported an OCR grid
generically, but $('dc-grid-ocr') was always null, so the card grid
silently never rendered. Added an Active OCR Engine panel mirroring
Active Language Model exactly, plus the Local grid -- both configured
engines now appear as ready-made cards, no "Add Custom" dialog needed.
- Fixed the audiobook pipeline's "Previous/Next" nav bar bleeding into
every .section-head in the document (confirmed live on the Engines
page: a "Cast Audiobook" button and "STEP 1/8" hint that made no sense
there). Changed from a blocklist (naming each unrelated section as
noticed) to an allowlist of the pipeline's three actual destinations.
Verified live end-to-end: play-all queue building and manual advance,
word-span markup, driving-mode toggle, OCR page showing both engines as
local cards, zero stray nav bars on the Engines page, no console errors.
20 new backend tests (156 total in the magazine module).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
881 lines
51 KiB
HTML
881 lines
51 KiB
HTML
<style>
|
|
.header-cat-block { display: none !important; }
|
|
.header-cat-block.is-active { display: flex !important; }
|
|
</style>
|
|
<div class="section-head engines-page-head">
|
|
<div class="engines-head-main">
|
|
<span class="section-icon"><span class="mdi mdi-brain"></span></span>
|
|
<div class="section-title">
|
|
<h2>Engines</h2>
|
|
<p>Local and cloud services for language generation, speech recognition, and synthesis.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="engines-head-context header-cat-block" data-engines-cat="llm">
|
|
<div class="engines-context-copy">
|
|
<h3 class="llm-sec-title">Language Models</h3>
|
|
<p class="llm-sec-note">Pick the <strong>Active Language Model</strong> below. Cards connect engines and apply a URL with <strong>Use as LLM</strong>.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="engines-head-context header-cat-block" data-engines-cat="stt">
|
|
<div class="engines-context-copy">
|
|
<h3 class="llm-sec-title">Speech Recognition</h3>
|
|
<p class="llm-sec-note">Docker stack containers appear first, with local runners and cloud APIs below.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="engines-head-context header-cat-block" data-engines-cat="tts">
|
|
<div class="engines-context-copy">
|
|
<h3 class="llm-sec-title">Text to Speech</h3>
|
|
<p class="llm-sec-note">Docker-detected engines and local presets are listed together. Apply a URL with <strong>Use as TTS</strong>.</p>
|
|
</div>
|
|
<div class="engines-context-actions">
|
|
<button class="btn-secondary" type="button" onclick="openAddEngineDialog('tts')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
<button class="btn-secondary dc-refresh-btn" type="button"><span class="mdi mdi-refresh"></span> Refresh</button>
|
|
<span class="llm-free-badge llm-free-local">100% Local</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="engines-head-context header-cat-block" data-engines-cat="image">
|
|
<div class="engines-context-copy">
|
|
<h3 class="llm-sec-title">Image Generation</h3>
|
|
<p class="llm-sec-note">Used to generate character profile pictures. Pick the active provider below and add its API key.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="engines-head-context header-cat-block" data-engines-cat="ocr">
|
|
<div class="engines-context-copy">
|
|
<h3 class="llm-sec-title">OCR & Document Parsing</h3>
|
|
<p class="llm-sec-note">Configure local OCR and layout-aware document parsers for scanned PDFs, images, and future Magazine projects.</p>
|
|
</div>
|
|
<div class="engines-context-actions">
|
|
<button class="btn-secondary" type="button" onclick="openAddEngineDialog('ocr')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
<button class="btn-secondary dc-refresh-btn" type="button"><span class="mdi mdi-refresh"></span> Refresh</button>
|
|
<span class="llm-free-badge llm-free-local">100% Local</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Language Models sub-page ──────────────────────────── -->
|
|
<div class="s-engines-page" data-page="llm">
|
|
|
|
<!-- ── Active Language Model (global picker) ──────────────── -->
|
|
<div class="llm-active-panel card">
|
|
<div class="llm-active-head">
|
|
<span class="llm-active-icon"><span class="mdi mdi-star-four-points"></span></span>
|
|
<div>
|
|
<h4 class="llm-active-title">Active Language Model</h4>
|
|
<p class="llm-active-sub">Default endpoint & model for all LLM tasks. Per-task dropdowns can still override it.</p>
|
|
</div>
|
|
</div>
|
|
<div class="llm-active-controls">
|
|
<label class="llm-active-lbl"><span class="mdi mdi-server-network"></span> Endpoint</label>
|
|
<select id="llm-active-url" class="llm-active-url-inp" style="appearance: auto; cursor: pointer; padding-right: 25px;">
|
|
<option value="">— Select an endpoint —</option>
|
|
</select>
|
|
<label class="llm-active-lbl"><span class="mdi mdi-brain"></span> Model</label>
|
|
<select id="llm-active-model" aria-label="Active language model"><option value="">— fetch models —</option></select>
|
|
<button id="llm-active-refresh" class="btn-secondary" type="button" title="Fetch models"><span class="mdi mdi-refresh"></span></button>
|
|
<span id="llm-active-status" class="llm-active-status"></span>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Local</h4>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('llm')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div id="dc-grid-llm" class="dc-grid" style="margin-bottom:14px"></div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:28px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase"><span class="mdi mdi-earth"></span> Cloud APIs <span class="llm-free-badge" style="margin-left:8px;vertical-align:middle;text-transform:none">Free tiers</span></h4>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('llm')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div class="llm-service-grid">
|
|
|
|
<div class="llm-card llm-card-highlight">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-lightning-bolt"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Groq</div>
|
|
<div class="llm-card-sub">Ultra-fast inference · OpenAI-compatible</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>30 000 tokens / min</span>
|
|
<span>14 400 req / day</span>
|
|
<span>Lowest latency</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="gsk_…" data-llm-key="groq_llm">
|
|
<a class="llm-link" href="https://console.groq.com" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.groq.com/openai/v1</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">llama-3.3-70b-versatile</span>
|
|
<span class="llm-model-tag">qwen-qwq-32b</span>
|
|
<span class="llm-model-tag">deepseek-r1-distill-llama-70b</span>
|
|
</div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="groq_llm" data-llm-endpoint="https://api.groq.com/openai/v1"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="groq_llm" data-llm-endpoint="https://api.groq.com/openai/v1" data-llm-model="llama-3.3-70b-versatile" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-earth"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">OpenRouter</div>
|
|
<div class="llm-card-sub">50+ free models · OpenAI-compatible</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>20 req / min</span>
|
|
<span>200 req / day (free models)</span>
|
|
<span>Single API for all models</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="sk-or-…" data-llm-key="openrouter">
|
|
<a class="llm-link" href="https://openrouter.ai" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://openrouter.ai/api/v1</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">qwen/qwen3-235b-a22b:free</span>
|
|
<span class="llm-model-tag">deepseek/deepseek-r1-0528:free</span>
|
|
<span class="llm-model-tag">mistralai/mistral-7b-instruct:free</span>
|
|
</div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="openrouter" data-llm-endpoint="https://openrouter.ai/api/v1"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="openrouter" data-llm-endpoint="https://openrouter.ai/api/v1" data-llm-model="qwen/qwen3-235b-a22b:free" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-diamond-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Google Gemini</div>
|
|
<div class="llm-card-sub">Gemini 2.5 Flash · Generous free tier</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>250 000 tokens / min</span>
|
|
<span>15 req / min free</span>
|
|
<span>1M context window</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="AIza…" data-llm-key="gemini">
|
|
<a class="llm-link" href="https://ai.google.dev" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://generativelanguage.googleapis.com/v1beta/openai/</code>
|
|
</div>
|
|
<div class="llm-info-note">Uses OpenAI-compat wrapper — use model <code>gemini-2.5-flash</code></div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="gemini" data-llm-endpoint="https://generativelanguage.googleapis.com/v1beta/openai/"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="gemini" data-llm-endpoint="https://generativelanguage.googleapis.com/v1beta/openai/" data-llm-model="gemini-2.5-flash" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-alpha-m-circle-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Mistral AI</div>
|
|
<div class="llm-card-sub">OpenAI-compatible · EU-based</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>1B tokens / month</span>
|
|
<span>2 req / min free</span>
|
|
<span>GDPR-compliant</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="Mistral key…" data-llm-key="mistral">
|
|
<a class="llm-link" href="https://mistral.ai" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.mistral.ai/v1</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">mistral-small-latest</span>
|
|
<span class="llm-model-tag">mistral-large-latest</span>
|
|
</div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="mistral" data-llm-endpoint="https://api.mistral.ai/v1"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="mistral" data-llm-endpoint="https://api.mistral.ai/v1" data-llm-model="mistral-small-latest" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-robot-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">OpenAI</div>
|
|
<div class="llm-card-sub">GPT models · pay-as-you-go</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-paid">Paid</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="sk-…" data-llm-key="openai_llm">
|
|
<a class="llm-link" href="https://platform.openai.com/api-keys" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.openai.com/v1</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">gpt-4o</span>
|
|
<span class="llm-model-tag">gpt-4o-mini</span>
|
|
<span class="llm-model-tag">o3-mini</span>
|
|
</div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="openai_llm" data-llm-endpoint="https://api.openai.com/v1"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="openai_llm" data-llm-endpoint="https://api.openai.com/v1" data-llm-model="gpt-4o-mini" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-triangle-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Anthropic</div>
|
|
<div class="llm-card-sub">Claude models · pay-as-you-go</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-paid">Paid</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="sk-ant-…" data-llm-key="anthropic_llm">
|
|
<a class="llm-link" href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.anthropic.com/v1</code>
|
|
</div>
|
|
<div class="llm-info-note">Not OpenAI-compatible — needs the Anthropic Messages API, not <code>/chat/completions</code></div>
|
|
<div class="dc-controls-row" style="margin-top:10px">
|
|
<button class="llm-local-ping llm-cloud-connect-btn" data-llm-key="anthropic_llm" data-llm-endpoint="https://api.anthropic.com/v1" data-llm-probe-type="anthropic"><span class="mdi mdi-lan-connect"></span> Connect</button>
|
|
<button class="llm-use-btn dc-use-btn llm-cloud-use-btn" data-llm-key="anthropic_llm" data-llm-endpoint="https://api.anthropic.com/v1" data-llm-model="claude-3-5-haiku-latest" data-llm-incompatible="1" style="margin-left:auto"><span class="mdi mdi-arrow-right-circle-outline"></span> Use as LLM</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h4 class="llm-local-cat" style="margin-top: 40px; text-transform: uppercase;">Additional Settings</h4>
|
|
<div class="card" style="margin-bottom:24px;">
|
|
<div class="s-page-head">
|
|
<h3>LLM Refinement Defaults</h3>
|
|
<p>Automatic text cleanup after transcription</p>
|
|
</div>
|
|
<div style="display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px;">
|
|
<div class="s-field">
|
|
<label>Auto-refine after transcription</label>
|
|
<select id="s-auto-refine">
|
|
<option value="off">Off — show raw transcription</option>
|
|
<option value="on">On — refine automatically</option>
|
|
</select>
|
|
<span class="s-hint">When on, the LLM refinement runs immediately after each capture.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Refinement model</label>
|
|
<input type="text" id="s-refine-model" placeholder="e.g. llama3.2, qwen3:8b, gpt-4o-mini">
|
|
<span class="s-hint">Model name sent to the active LLM URL. Leave empty to use the app's current default.</span>
|
|
</div>
|
|
</div>
|
|
<div class="s-toggle-grid">
|
|
<label class="s-toggle-row"><input type="checkbox" id="s-refine-fillers"><div><span class="s-toggle-title">Remove filler words</span><span class="s-hint">Strips um, uh, like, you know, sort of.</span></div></label>
|
|
<label class="s-toggle-row"><input type="checkbox" id="s-refine-repetitions"><div><span class="s-toggle-title">Remove repetitions</span><span class="s-hint">Collapses repeated words and phrases.</span></div></label>
|
|
<label class="s-toggle-row"><input type="checkbox" id="s-refine-corrections"><div><span class="s-toggle-title">Fix grammar & spelling</span><span class="s-hint">Light corrections without changing meaning.</span></div></label>
|
|
<label class="s-toggle-row"><input type="checkbox" id="s-refine-punctuation"><div><span class="s-toggle-title">Fix punctuation</span><span class="s-hint">Adds missing commas, periods, and sentence casing.</span></div></label>
|
|
</div>
|
|
<div class="s-page-actions" style="margin-top: 15px;">
|
|
<button class="btn-primary s-save-btn">Save settings</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /llm -->
|
|
|
|
<!-- ── Speech to Text sub-page ───────────────────────────── -->
|
|
<div class="s-engines-page" data-page="stt">
|
|
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Local</h4>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('stt')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div id="dc-grid-stt" class="dc-grid" style="margin-bottom:14px"></div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:28px;margin-bottom:12px">
|
|
<div style="display:flex;gap:8px;align-items:center">
|
|
<span class="mdi mdi-cloud-outline"></span>
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Cloud APIs</h4>
|
|
<span class="llm-free-badge">Free tiers</span>
|
|
</div>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('stt')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div class="llm-service-grid">
|
|
|
|
<div class="llm-card llm-card-highlight">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-lightning-bolt"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Groq Whisper</div>
|
|
<div class="llm-card-sub">whisper-large-v3-turbo · OpenAI-compatible</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>2 000 req / day</span>
|
|
<span><span class="mdi mdi-lightning-bolt"></span> Fastest cloud STT</span>
|
|
<span><span class="mdi mdi-memory"></span> Cloud · 0 VRAM</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="gsk_…" data-llm-key="groq_stt">
|
|
<a class="llm-link" href="https://console.groq.com" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.groq.com/openai/v1</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">whisper-large-v3-turbo</span>
|
|
<span class="llm-model-tag">whisper-large-v3</span>
|
|
<span class="llm-model-tag">distil-whisper-large-v3-en</span>
|
|
</div>
|
|
<div class="llm-info-note">Save key in <strong>Settings → Groq API key</strong> to enable <em>Groq Whisper</em> in the STT dropdown.</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-emoticon-happy-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">HuggingFace Inference</div>
|
|
<div class="llm-card-sub">Serverless Whisper models</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>~1 000 req / day</span>
|
|
<span>Slower cold starts</span>
|
|
<span>Many model variants</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="hf_…" data-llm-key="hf_stt">
|
|
<a class="llm-link" href="https://huggingface.co/settings/tokens" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api-inference.huggingface.co/models/openai/whisper-large-v3</code>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-content-copy"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">AssemblyAI</div>
|
|
<div class="llm-card-sub">High-accuracy transcription + speaker diarization</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>100 h lifetime</span>
|
|
<span>Speaker labels</span>
|
|
<span>Auto-chapters</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="AssemblyAI key…" data-llm-key="assemblyai">
|
|
<a class="llm-link" href="https://www.assemblyai.com" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.assemblyai.com/v2/transcript</code>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:28px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Additional Settings</h4>
|
|
</div>
|
|
<div class="card" style="margin-bottom: 24px;">
|
|
<div class="s-page-head">
|
|
<h3>STT Connections</h3>
|
|
<p>Set up endpoints for Speech-to-Text backends.</p>
|
|
</div>
|
|
<div class="settings-grid stt-settings-grid">
|
|
<div class="s-field stt-url-field">
|
|
<label>Active STT URL</label>
|
|
<input type="text" id="s-whisper-url" placeholder="http://host.docker.internal:8010">
|
|
<span class="s-hint">Default recognition endpoint. Expected: <code>POST /v1/audio/transcriptions</code>.</span>
|
|
<div class="btn-row settings-mini-actions">
|
|
<button class="btn-secondary" id="s-use-parakeet-asr" type="button">Use Parakeet</button>
|
|
<button class="btn-secondary" id="s-use-nvidia-router" type="button">Use router</button>
|
|
<button class="btn-secondary" id="s-use-faster-whisper" type="button">Use faster-whisper</button>
|
|
<button class="btn-secondary" id="s-use-whisper-cpp" type="button">Use whisper.cpp</button>
|
|
</div>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>faster-whisper-server URL <span class="s-label-note">(CTranslate2 GPU)</span></label>
|
|
<input type="text" id="s-faster-whisper-url" placeholder="http://host.docker.internal:8000">
|
|
<span class="s-hint">GPU-accelerated Whisper via CTranslate2. ~70× RT · 1.5 GB VRAM.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>whisper.cpp URL <span class="s-label-note">(CPU/CUDA)</span></label>
|
|
<input type="text" id="s-whisper-cpp-url" placeholder="http://host.docker.internal:8080">
|
|
<span class="s-hint">Lightweight C++ Whisper server. ~8–15× RT CPU · ~1 GB RAM.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Groq API key <span class="s-label-note">(Groq Whisper & LLM)</span></label>
|
|
<div class="s-key-row">
|
|
<input type="password" id="s-groq-api-key" placeholder="gsk_…" autocomplete="off">
|
|
<button type="button" class="s-eye-btn" data-target="s-groq-api-key" aria-label="Toggle API key visibility"><span class="mdi mdi-eye-outline"></span></button>
|
|
</div>
|
|
<span class="s-hint">Used for Groq Whisper STT and LLM. Free: 2 000 req/day.</span>
|
|
</div>
|
|
</div>
|
|
<div class="s-page-actions" style="margin-top: 15px;">
|
|
<button class="btn-primary s-save-btn">Save connections</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inline STT test panel -->
|
|
<div class="stt-test-panel" id="stt-test-panel">
|
|
<div class="stt-test-row">
|
|
<span class="stt-test-label">Quick test</span>
|
|
<select id="stt-test-backend" class="stt-test-select"></select>
|
|
<button id="stt-test-mic-btn" class="stt-test-mic" title="Hold to record, click again to stop">
|
|
<span class="mdi mdi-microphone"></span>
|
|
</button>
|
|
<span id="stt-test-status" class="stt-test-status">Ready</span>
|
|
</div>
|
|
<div id="stt-test-result" class="stt-test-result" style="display:none"></div>
|
|
</div>
|
|
<div class="card" style="margin-top: 24px;">
|
|
<div class="s-page-head">
|
|
<h3>STT Transcription Defaults</h3>
|
|
<p>Default language and preferred STT backend for captures.</p>
|
|
</div>
|
|
<div class="settings-grid settings-behavior-grid">
|
|
<div class="s-field">
|
|
<label>Default language</label>
|
|
<select id="s-stt-language">
|
|
<option value="">Auto-detect</option>
|
|
<option value="en">English</option>
|
|
<option value="de">German</option>
|
|
<option value="fr">French</option>
|
|
<option value="es">Spanish</option>
|
|
<option value="it">Italian</option>
|
|
<option value="pt">Portuguese</option>
|
|
<option value="nl">Dutch</option>
|
|
<option value="ja">Japanese</option>
|
|
<option value="zh">Chinese</option>
|
|
</select>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Preferred STT backend</label>
|
|
<select id="s-stt-preferred-backend">
|
|
<option value="">Use active STT URL (from Connections)</option>
|
|
<option value="groq">Groq Whisper — cloud, fastest</option>
|
|
<option value="faster-whisper">faster-whisper-server — GPU local</option>
|
|
<option value="whisper-cpp">whisper.cpp — CPU/CUDA local</option>
|
|
<option value="parakeet">NVIDIA Parakeet — GPU local</option>
|
|
</select>
|
|
<span class="s-hint">Overrides the active STT URL for STT-TTS panel captures.</span>
|
|
</div>
|
|
</div>
|
|
<div class="s-page-actions" style="margin-top: 15px;">
|
|
<button class="btn-primary s-save-btn">Save settings</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /stt -->
|
|
|
|
<!-- ── Text to Speech sub-page ───────────────────────────── -->
|
|
<div class="s-engines-page" data-page="tts">
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Local</h4>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('tts')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div id="dc-grid-tts" class="dc-grid" style="margin-bottom:14px"></div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:28px;margin-bottom:12px">
|
|
<div style="display:flex;gap:8px;align-items:center">
|
|
<span class="mdi mdi-cloud-outline"></span>
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Cloud APIs</h4>
|
|
<span class="llm-free-badge">Free tiers</span>
|
|
</div>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('tts')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div class="llm-service-grid">
|
|
|
|
<div class="llm-card llm-card-highlight">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-waveform"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">ElevenLabs</div>
|
|
<div class="llm-card-sub">High-quality voice cloning & synthesis</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>10 000 chars / month</span>
|
|
<span>2 500 char / request max</span>
|
|
<span>Voice cloning supported</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="xi-api-key…" data-llm-key="elevenlabs">
|
|
<a class="llm-link" href="https://elevenlabs.io" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.elevenlabs.io/v1/text-to-speech</code>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-fish"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Fish Audio</div>
|
|
<div class="llm-card-sub">Voice cloning & multilingual TTS</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>1 h audio / month</span>
|
|
<span>100 req / min</span>
|
|
<span>30+ languages</span>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="Fish Audio key…" data-llm-key="fish_audio">
|
|
<a class="llm-link" href="https://fish.audio" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.fish.audio/v1/tts</code>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-music-note"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Kokoro TTS</div>
|
|
<div class="llm-card-sub">82M model · HuggingFace Spaces demo</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-demo">Demo</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>Free web demo</span>
|
|
<span><$1 per 1M chars (paid)</span>
|
|
<span>High naturalness</span>
|
|
</div>
|
|
<div class="llm-info-box">
|
|
Use the HF Spaces web demo for quick tests, or run Kokoro locally via Docker for production use.
|
|
<br>
|
|
<a class="llm-link-inline" href="https://huggingface.co/spaces/hexgrad/Kokoro-TTS" target="_blank" rel="noopener"><span class="mdi mdi-arrow-right"></span> Open Kokoro HF Space</a>
|
|
</div>
|
|
</div>
|
|
|
|
</div> <!-- CLOSE llm-service-grid -->
|
|
|
|
<h4 class="llm-local-cat" style="margin-top: 40px; text-transform: uppercase;">Additional Settings</h4>
|
|
<div class="card" style="margin-bottom: 24px;">
|
|
<div class="s-page-head">
|
|
<h3>TTS Playback & API Behavior</h3>
|
|
<p>Controls how previews play and how OpenAI-compatible requests are shaped.</p>
|
|
</div>
|
|
<div class="settings-grid settings-behavior-grid">
|
|
<div class="s-field">
|
|
<label>TTS preview playback</label>
|
|
<select id="s-tts-stream-mode">
|
|
<option value="auto">Stream when available, then fall back</option>
|
|
<option value="streaming">Streaming only</option>
|
|
<option value="buffered">Buffered WAV only</option>
|
|
</select>
|
|
<span class="s-hint">Buffered keeps Save WAV available. Streaming starts sooner.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>OpenAI-compatible request style</label>
|
|
<select id="s-tts-backend">
|
|
<option value="openai">Qwen3-TTS / OpenAI</option>
|
|
<option value="localai">LocalAI (/tts)</option>
|
|
<option value="pocket">Pocket-TTS</option>
|
|
<option value="nvidia_magpie">NVIDIA Magpie / OpenAI</option>
|
|
</select>
|
|
<span class="s-hint">Controls payload shape for the normal TTS API URL.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" style="margin-bottom: 24px;">
|
|
<div class="s-page-head">
|
|
<h3>Default Playback Voice</h3>
|
|
<p>Pre-selected voice in the STT-TTS panel</p>
|
|
</div>
|
|
<div class="settings-grid settings-behavior-grid">
|
|
<div class="s-field">
|
|
<label>Default voice</label>
|
|
<select id="s-captures-default-voice">
|
|
<option value="">None — select manually</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="s-page-actions" style="margin-top: 15px;">
|
|
<button class="btn-primary s-save-btn">Save settings</button>
|
|
<button class="btn-secondary s-reload-btn">Reload settings</button>
|
|
</div>
|
|
|
|
</div><!-- /tts -->
|
|
|
|
<!-- ── Image Generation sub-page ─────────────────────────── -->
|
|
<div class="s-engines-page" data-page="image">
|
|
|
|
<div class="llm-active-panel card">
|
|
<div class="llm-active-head">
|
|
<span class="llm-active-icon"><span class="mdi mdi-star-four-points"></span></span>
|
|
<div>
|
|
<h4 class="llm-active-title">Active Image Generation Provider</h4>
|
|
<p class="llm-active-sub">Used for the "Generate Image" action on a character's profile picture.</p>
|
|
</div>
|
|
</div>
|
|
<div class="llm-active-controls">
|
|
<label class="llm-active-lbl"><span class="mdi mdi-cloud-outline"></span> Provider</label>
|
|
<select id="image-gen-provider">
|
|
<option value="">— None selected —</option>
|
|
<option value="openai">OpenAI (gpt-image-1)</option>
|
|
<option value="google">Google (Gemini / Imagen)</option>
|
|
<option value="openrouter">OpenRouter (image-capable models)</option>
|
|
<option value="pollinations">Pollinations.ai (free, no key)</option>
|
|
<option value="comfyui">Local ComfyUI</option>
|
|
</select>
|
|
<label class="llm-active-lbl"><span class="mdi mdi-image-outline"></span> Model</label>
|
|
<select id="image-gen-model-select" style="max-width:240px"></select>
|
|
<input type="text" id="image-gen-model" placeholder="Custom model ID" style="max-width:200px;display:none">
|
|
<span id="image-gen-status" class="llm-active-status"></span>
|
|
</div>
|
|
<p class="llm-active-sub" style="margin-top:8px">OpenRouter reuses the same API key as its LLM card below — no separate key needed here.</p>
|
|
</div>
|
|
|
|
<div class="card" id="comfyui-config-panel" hidden style="margin-top:16px">
|
|
<div class="s-page-head">
|
|
<h3><span class="mdi mdi-graph-outline"></span> Local ComfyUI Workflow</h3>
|
|
<p>Runs on your own GPU, with no paid API. Voice Creator includes validated high-quality local Flux workflows: a square profile portrait and a 3:2 concept-art sheet. Leave the advanced workflow fields blank to use them automatically. An advanced workflow below overrides profile portraits; concept-art sheets always retain their dedicated 3:2 workflow.</p>
|
|
</div>
|
|
<div style="display:flex; flex-direction:column; gap:14px">
|
|
<div class="s-field">
|
|
<label>ComfyUI URL</label>
|
|
<input type="text" id="comfyui-url" placeholder="http://host.docker.internal:8188">
|
|
<span class="s-hint">Base URL of your running ComfyUI instance (container port 8188 by default).</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Advanced workflow JSON (API format, optional override)</label>
|
|
<textarea id="comfyui-workflow" rows="6" spellcheck="false" placeholder='{"3": {"class_type": "KSampler", "inputs": {...}}, "6": {"class_type": "CLIPTextEncode", "inputs": {"text": "", ...}}, ...}' style="font-family:monospace; font-size:12px"></textarea>
|
|
<span class="s-hint">Leave blank for Voice Creator's validated Flux Dev portrait and concept-sheet workflows. Paste raw JSON exported from ComfyUI's "Export (API Format)" only when you deliberately want to replace both.</span>
|
|
</div>
|
|
<div style="display:flex; gap:14px; flex-wrap:wrap">
|
|
<div class="s-field" style="flex:1; min-width:160px">
|
|
<label>Prompt node ID</label>
|
|
<input type="text" id="comfyui-prompt-node-id" placeholder="e.g. 6">
|
|
<span class="s-hint">The node whose text input gets replaced with the character prompt each run.</span>
|
|
</div>
|
|
<div class="s-field" style="flex:1; min-width:160px">
|
|
<label>Prompt input field name</label>
|
|
<input type="text" id="comfyui-prompt-field" placeholder="text">
|
|
<span class="s-hint">Usually "text" (CLIPTextEncode) — check the node's "inputs" key in the JSON above.</span>
|
|
</div>
|
|
<div class="s-field" style="flex:1; min-width:160px">
|
|
<label>Output node ID</label>
|
|
<input type="text" id="comfyui-output-node-id" placeholder="e.g. 22">
|
|
<span class="s-hint">The SaveImage/PreviewImage node whose result to fetch.</span>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex; gap:10px; align-items:center; flex-wrap:wrap">
|
|
<button type="button" class="btn-secondary" id="comfyui-test-btn"><span class="mdi mdi-lan-connect"></span> Test connection</button>
|
|
<button type="button" class="btn-secondary" id="comfyui-test-gen-btn"><span class="mdi mdi-image-outline"></span> Test generate</button>
|
|
<span id="comfyui-test-status" class="llm-active-status"></span>
|
|
</div>
|
|
<div id="comfyui-test-preview" hidden><img id="comfyui-test-img" style="max-width:220px; border-radius:8px; border:1px solid var(--border)"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:flex;gap:8px;align-items:center;margin-top:28px;margin-bottom:12px">
|
|
<span class="mdi mdi-cloud-outline"></span>
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Cloud APIs</h4>
|
|
</div>
|
|
<div class="llm-service-grid">
|
|
|
|
<div class="llm-card llm-card-highlight">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-robot-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">OpenAI</div>
|
|
<div class="llm-card-sub">gpt-image-1 · Images API</div>
|
|
</div>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="sk-…" data-llm-key="openai_image">
|
|
<a class="llm-link" href="https://platform.openai.com/api-keys" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>https://api.openai.com/v1/images/generations</code>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-google"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Google</div>
|
|
<div class="llm-card-sub">Gemini 2.5 Flash Image / Imagen</div>
|
|
</div>
|
|
</div>
|
|
<div class="llm-field-row">
|
|
<label class="llm-label">API key</label>
|
|
<input type="password" class="llm-input" placeholder="AIza…" data-llm-key="google_image">
|
|
<a class="llm-link" href="https://aistudio.google.com/apikey" target="_blank" rel="noopener">Get key <span class="mdi mdi-open-in-new link-icon"></span></a>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>generativelanguage.googleapis.com</code>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-card">
|
|
<div class="llm-card-head">
|
|
<span class="llm-card-icon"><span class="mdi mdi-flower-outline"></span></span>
|
|
<div>
|
|
<div class="llm-card-name">Pollinations.ai</div>
|
|
<div class="llm-card-sub">Free · no API key · no account</div>
|
|
</div>
|
|
<span class="llm-tier-badge llm-tier-free">Free</span>
|
|
</div>
|
|
<div class="llm-card-stats">
|
|
<span>No signup</span>
|
|
<span>Shared free queue</span>
|
|
<span>Third-party public service</span>
|
|
</div>
|
|
<div class="llm-endpoint">
|
|
<span class="llm-endpoint-label">Endpoint</span>
|
|
<code>image.pollinations.ai</code>
|
|
</div>
|
|
<div class="llm-models">
|
|
<span class="llm-model-tag">flux</span>
|
|
</div>
|
|
<div class="llm-info-note">No key or setup needed — just pick it as the Active Provider above. No SLA/rate-limit guarantee since it's a shared free public queue; retries automatically on "queue full" errors.</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="llm-info-note" style="margin-top:16px;">Local ComfyUI (no API key, runs on your own GPU) is configured separately above, in the Active Image Generation Provider panel.</div>
|
|
|
|
<div class="s-page-actions" style="margin-top: 15px;">
|
|
<button class="btn-primary s-save-btn">Save settings</button>
|
|
<button class="btn-secondary s-reload-btn">Reload settings</button>
|
|
</div>
|
|
|
|
</div><!-- /image -->
|
|
|
|
<div class="s-engines-page" data-page="ocr">
|
|
|
|
<!-- ── Active OCR Engine (global picker) ───────────────────── -->
|
|
<div class="llm-active-panel card">
|
|
<div class="llm-active-head">
|
|
<span class="llm-active-icon"><span class="mdi mdi-star-four-points"></span></span>
|
|
<div>
|
|
<h4 class="llm-active-title">Active OCR Engine</h4>
|
|
<p class="llm-active-sub">Used by Magazine ingestion when a page needs OCR, or when its existing text scores poorly. Tesseract (bundled, no setup) is always the fallback if this is unreachable.</p>
|
|
</div>
|
|
</div>
|
|
<div class="llm-active-controls">
|
|
<label class="llm-active-lbl"><span class="mdi mdi-server-network"></span> Endpoint</label>
|
|
<select id="ocr-active-url" class="llm-active-url-inp" style="appearance: auto; cursor: pointer; padding-right: 25px;">
|
|
<option value="">— Select an endpoint —</option>
|
|
</select>
|
|
<label class="llm-active-lbl"><span class="mdi mdi-brain"></span> Model</label>
|
|
<select id="ocr-active-model" aria-label="Active OCR model"><option value="">— fetch models —</option></select>
|
|
<button id="ocr-active-refresh" class="btn-secondary" type="button" title="Fetch models"><span class="mdi mdi-refresh"></span></button>
|
|
<span id="ocr-active-status" class="llm-active-status"></span>
|
|
</div>
|
|
<label class="llm-active-lbl" style="margin-top:10px;display:flex;align-items:center;gap:6px;font-weight:normal">
|
|
<input type="checkbox" id="ocr-active-enabled"> Enable vision OCR escalation during ingestion
|
|
</label>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px;margin-bottom:12px">
|
|
<h4 class="llm-local-cat" style="margin:0;text-transform:uppercase">Local</h4>
|
|
<button class="btn-primary" type="button" onclick="openAddEngineDialog('ocr')"><span class="mdi mdi-plus"></span> Add Custom</button>
|
|
</div>
|
|
<div id="dc-grid-ocr" class="dc-grid" style="margin-bottom:14px"></div>
|
|
|
|
<div class="llm-info-note" style="margin-top:16px;">Two engines were confirmed live against a real scanned magazine: <strong>Baidu Unlimited-OCR</strong> gives the most accurate text and also labels running headers/footers/page numbers/titles directly (best default). <strong>KDL Frontier Parser nano</strong> answers faster but returns plain text with no layout coordinates or labels — a usable fallback, not a first choice.</div>
|
|
|
|
</div><!-- /ocr -->
|
|
|
|
<!-- ── Add Custom Engine dialog ─────────────────────────────── -->
|
|
<dialog id="add-engine-dlg" class="add-engine-dlg">
|
|
<div class="add-engine-form">
|
|
<h3 class="add-engine-title"><span class="mdi mdi-plus-circle-outline"></span> Add Custom Engine</h3>
|
|
<div class="add-engine-field">
|
|
<label for="aed-name">Name</label>
|
|
<input id="aed-name" type="text" placeholder="My TTS Server" spellcheck="false">
|
|
</div>
|
|
<div class="add-engine-field">
|
|
<label for="aed-role">Role</label>
|
|
<select id="aed-role">
|
|
<option value="tts">Text to Speech (TTS)</option>
|
|
<option value="stt">Speech to Text (STT)</option>
|
|
<option value="llm">Language Model (LLM)</option>
|
|
<option value="ocr">OCR & Document Parser</option>
|
|
</select>
|
|
</div>
|
|
<div class="add-engine-field">
|
|
<label for="aed-url">URL</label>
|
|
<input id="aed-url" type="text" placeholder="http://192.168.1.100:8080" spellcheck="false">
|
|
</div>
|
|
<div class="add-engine-field">
|
|
<label for="aed-container">Docker container name <span class="add-engine-opt">(optional — enables Stop / Start / Restart)</span></label>
|
|
<input id="aed-container" type="text" placeholder="my-tts-container" spellcheck="false">
|
|
</div>
|
|
<div class="add-engine-field">
|
|
<label for="aed-desc">Description <span class="add-engine-opt">(optional)</span></label>
|
|
<input id="aed-desc" type="text" placeholder="Short description">
|
|
</div>
|
|
<div class="add-engine-actions">
|
|
<button type="button" class="btn-secondary" id="aed-cancel">Cancel</button>
|
|
<button type="button" class="btn-primary" id="aed-save">Add Card</button>
|
|
</div>
|
|
</div>
|
|
</dialog>
|