fix: P2 audit items — use API client for stop, replace raw buttons, remove deprecated doc refs

- Replace raw fetch in handleStop with synthesesApi.stop()
- Add stop() method to synthesesApi
- Replace raw <button> elements in GenerateSynthesis with Button component (generate, retry, stop)
- Remove deprecated LLM link extraction schema reference from technical_specs.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
oabrivard 2 months ago
parent b124d73c2a
commit b60a55993c

@ -761,7 +761,7 @@ All calls use structured JSON output (response_schema defines the expected shape
`llm/schema.rs` builds JSON Schema definitions for:
- Classification/summarization: `{title, summary, category, is_article}`
- Web search: `{category_0: [{title, url, summary}], ...}` with per-category arrays
- Source link extraction schema is deprecated (link extraction mode is no longer configurable).
- Source link extraction: handled via heuristic HTML parsing (no LLM schema).
### Error Mapping

@ -85,6 +85,10 @@ export const synthesesApi = {
sendEmail: (id: string, email: string): Promise<void> =>
api.post<void>(`/syntheses/${id}/send-email`, { email } satisfies SendEmailRequest),
/** POST /syntheses/generate/:jobId/stop -- request cancellation of an in-progress generation job. */
stop: (jobId: string): Promise<void> =>
api.post<void>(`/syntheses/generate/${jobId}/stop`),
/** Download the synthesis as a Markdown file via {@link fetchFile} + {@link triggerDownload}. */
exportMarkdown: async (id: string): Promise<void> => {
const response = await fetchFile(`/syntheses/${id}/export/markdown`);

@ -19,6 +19,7 @@ import type { UserSettings, ProviderConfig, ProgressEvent } from '~/types';
import { createSSEConnection, type SSEConnection, type SSEStatus } from '~/utils/sse';
import { providerSupportsWebSearch } from '~/utils/providers';
import LoadingSpinner from '~/components/ui/LoadingSpinner';
import Button from '~/components/ui/Button';
/** Metadata for a single generation pipeline step. */
interface StepInfo {
@ -238,11 +239,7 @@ const GenerateSynthesis: Component = () => {
const id = jobId();
if (!id) return;
try {
await fetch(`/api/v1/syntheses/generate/${id}/stop`, {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
credentials: 'same-origin',
});
await synthesesApi.stop(id);
} catch {
// ignore errors — the pipeline will stop on its own
}
@ -422,13 +419,13 @@ const GenerateSynthesis: Component = () => {
</p>
{/* Stop generation button */}
<button
type="button"
<Button
variant="danger"
onClick={handleStop}
class="mt-4 px-4 py-2 text-sm font-medium text-red-700 bg-red-50 border border-red-300 rounded-md hover:bg-red-100"
class="mt-4"
>
{t('generate.stop')}
</button>
</Button>
</div>
</Show>
@ -437,28 +434,20 @@ const GenerateSynthesis: Component = () => {
<Show
when={!error()}
fallback={
<button
type="button"
onClick={handleRetry}
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm w-full sm:w-auto"
>
<Button onClick={handleRetry}>
{t('generate.retry')}
</button>
</Button>
}
>
<button
type="button"
<Button
onClick={handleGenerate}
disabled={generating() || success() || !selectedThemeId()}
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed w-full sm:w-auto"
loading={generating() && !success()}
>
<Show when={generating() && !success()}>
<Loader2 class="animate-spin -ml-1 mr-2 h-5 w-5 text-white" />
</Show>
{generating() && !success()
? t('generate.inProgress')
: t('generate.launch')}
</button>
</Button>
</Show>
</div>
</div>

Loading…
Cancel
Save