feat: STT transcripts, model catalog+fuzzy resolve, UI audio player & auto-refresh
Core: - ModelCatalog: periodic background sync of provider models + fuzzy resolve. Misspelled model ids map to the nearest match (cheapest on ties); no match raises ModelResolutionError with cheapest-first suggestions for the agent. mcp-server: - nexus_stt now writes the transcript to a Markdown file in the media dir so it appears in the gallery and is readable in the modal. - media handler resolves model via catalog before generating; returns a note when substituting, or a BAD_REQUEST with available models when nothing fits. - context wires a ModelCatalog per media provider with auto-sync. admin: - /media serves correct Content-Type (incl. text/markdown for transcripts). - Gallery: audio play button on cards, dedicated audio player in modal, text/transcript tiles that fetch and render the .md in the modal. - Header: auto-refresh toggle (5/10/30s) that refreshes the active tab without a full page reload; pauses while a modal is open.
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
KeyCipher,
|
||||
KeyResolver,
|
||||
MediaStorage,
|
||||
ModelCatalog,
|
||||
ProviderRegistry,
|
||||
ProvidersStore,
|
||||
createNordRouterProviders,
|
||||
@@ -20,6 +21,8 @@ export interface AppContext {
|
||||
storage: MediaStorage;
|
||||
journal: Journal;
|
||||
providersStore: ProvidersStore;
|
||||
/** Model catalogs keyed by provider name (for sync + fuzzy resolution). */
|
||||
catalogs: Map<string, ModelCatalog>;
|
||||
/** Effective default provider name after resolving DB/env. */
|
||||
defaultProvider: string;
|
||||
}
|
||||
@@ -47,6 +50,7 @@ export function buildContext(): AppContext {
|
||||
|
||||
const resolved = resolver.resolveAll(BUILTINS);
|
||||
const defaultProvider = resolver.resolveDefault(resolved);
|
||||
const catalogs = new Map<string, ModelCatalog>();
|
||||
|
||||
for (const p of resolved) {
|
||||
if (!p.enabled) {
|
||||
@@ -69,6 +73,12 @@ export function buildContext(): AppContext {
|
||||
Object.defineProperty(nr.search, 'name', { value: p.name });
|
||||
registry.registerMedia(nr.media);
|
||||
registry.registerSearch(nr.search);
|
||||
// Model catalog with periodic background sync + fuzzy resolution.
|
||||
if (p.apiKey) {
|
||||
const catalog = new ModelCatalog(nr.media);
|
||||
catalog.startAutoSync();
|
||||
catalogs.set(p.name, catalog);
|
||||
}
|
||||
log.info(`registered provider "${p.name}" (media + search, key: ${p.keySource})`);
|
||||
} catch (err) {
|
||||
log.warn(`failed to register "${p.name}"`, err instanceof Error ? err.message : String(err));
|
||||
@@ -82,5 +92,5 @@ export function buildContext(): AppContext {
|
||||
log.info('registered local STT provider');
|
||||
log.info(`default provider: ${defaultProvider}`);
|
||||
|
||||
return { config, registry, storage, journal, providersStore, defaultProvider };
|
||||
return { config, registry, storage, journal, providersStore, catalogs, defaultProvider };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user