refactor(text): extract shared summarization service with tts/note/notification modes
Move server-side summarization out of lib/tts/summarization.js into a shared lib/text module exposing tts, note and notification modes. TTS and notification runtimes now delegate to the shared service, note mode is used to distill a selected excerpt into a short project note. Expose the new endpoint via /api/text/summarize and route it through the common request middleware. Client voice/summarize.ts accepts a mode option and points at the new endpoint with an absolute URL that respects the desktop server origin. VS Code webview swaps the stubbed tts summarize path for /api/text/summarize and hardens URL parsing with window.location.href as the base.
This commit is contained in:
@@ -1,12 +1,24 @@
|
||||
/**
|
||||
* Text summarization utility for TTS
|
||||
* Text summarization utility
|
||||
*
|
||||
* Calls the server-side summarization endpoint which uses
|
||||
* Calls the server-side text summarization endpoint which uses
|
||||
* the opencode.ai zen API with gpt-5-nano.
|
||||
*/
|
||||
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
|
||||
const resolveSummarizeUrl = (): string => {
|
||||
if (typeof window === 'undefined') {
|
||||
return '/api/text/summarize';
|
||||
}
|
||||
|
||||
const desktopServer = (window as typeof window & {
|
||||
__OPENCHAMBER_DESKTOP_SERVER__?: { origin: string };
|
||||
}).__OPENCHAMBER_DESKTOP_SERVER__;
|
||||
const baseOrigin = desktopServer?.origin || window.location.origin;
|
||||
return new URL('/api/text/summarize', baseOrigin).toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Summarize text using the server-side zen API endpoint
|
||||
*
|
||||
@@ -21,25 +33,32 @@ export async function summarizeText(
|
||||
threshold?: number;
|
||||
/** Max characters for the summary output */
|
||||
maxLength?: number;
|
||||
/** Summarization mode */
|
||||
mode?: 'tts' | 'note';
|
||||
}
|
||||
): Promise<string> {
|
||||
const store = useConfigStore.getState();
|
||||
const threshold = options?.threshold ?? store.summarizeCharacterThreshold;
|
||||
const maxLength = options?.maxLength ?? store.summarizeMaxLength;
|
||||
const mode = options?.mode ?? 'tts';
|
||||
const normalizedSource = text.replace(/\s+/g, ' ').trim();
|
||||
|
||||
// Don't summarize if text is under threshold
|
||||
if (text.length <= threshold) {
|
||||
if (mode === 'note') {
|
||||
throw new Error('Note summarization threshold bypass is not allowed');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
try {
|
||||
const zenModel = store.settingsZenModel;
|
||||
const response = await fetch('/api/tts/summarize', {
|
||||
const response = await fetch(resolveSummarizeUrl(), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ text, threshold, maxLength, ...(zenModel ? { zenModel } : {}) }),
|
||||
body: JSON.stringify({ text, threshold, maxLength, mode, ...(zenModel ? { zenModel } : {}) }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -56,14 +75,27 @@ export async function summarizeText(
|
||||
summaryLength?: number;
|
||||
};
|
||||
|
||||
if (data.summarized && data.summary) {
|
||||
return data.summary;
|
||||
if (typeof data.summary === 'string' && data.summary.trim().length > 0) {
|
||||
const summary = data.summary.trim();
|
||||
if (mode === 'note') {
|
||||
const normalizedSummary = summary.replace(/\s+/g, ' ').trim();
|
||||
if (normalizedSummary === normalizedSource) {
|
||||
throw new Error('Note distillation returned source text unchanged');
|
||||
}
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
// Return original text if not summarized
|
||||
if (mode === 'note') {
|
||||
throw new Error('Note summarization returned no distilled result');
|
||||
}
|
||||
// Return original text if the server produced nothing usable
|
||||
return text;
|
||||
} catch (err) {
|
||||
console.error('[summarize] Failed to summarize:', err);
|
||||
if (mode === 'note') {
|
||||
throw err instanceof Error ? err : new Error('Note summarization failed');
|
||||
}
|
||||
// Return original text on error
|
||||
return text;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user