refactor: extract generation orchestration

Change-Id: I17002ff04b0d79d8ec27da3dbb4b08df9bf3659e
This commit is contained in:
wujunchen 2026-04-26 17:45:43 +08:00
parent 9ed36570b5
commit b199f56a35
3 changed files with 90 additions and 83 deletions

50
main.js

File diff suppressed because one or more lines are too long

61
main.ts
View file

@ -1,13 +1,13 @@
'use strict';
import { MarkdownView, Modal, Notice, Plugin, requestUrl, TFile } from 'obsidian';
import { MarkdownView, Modal, Notice, Plugin, TFile } from 'obsidian';
import { findLineForAnchor } from './src/anchor';
import { serializeCacheFile, shouldConfirmRegenerate, touchCacheEntry } from './src/cache';
import { CacheManager } from './src/cache-manager';
import { activeIndexAfterCardDelete, removeCardAt, updateCardAt } from './src/cards';
import { resolveCliPath, summarizeViaClaudeCode, summarizeViaCodex } from './src/cli';
import { resolveCliPath } from './src/cli';
import { cancellationNoticeKey, summarizeDocument } from './src/generation';
import {
classifyGenerationError,
type GenerationJob,
GenerationJobAlreadyRunningError,
GenerationJobCancelledError,
GenerationJobManager,
@ -22,7 +22,6 @@ import {
buildOpenAiChatBody,
buildOpenAiResponsesBody,
summarizeViaApi,
summarizeViaApiStreaming,
supportsStreaming,
tokenLimitFieldForOpenAiChat,
} from './src/providers';
@ -34,7 +33,6 @@ import {
DEFAULT_SETTINGS,
generationFingerprint,
getApiBaseUrl,
isApiBackend,
modelForApi,
normalizeSettings,
pruneCacheEntries,
@ -54,59 +52,6 @@ import { addIconButton, addTextButton, copyToClipboard } from './src/ui-helpers'
import { folderPathsForTarget } from './src/vault';
import { ParallelReaderView, VIEW_TYPE_PARALLEL } from './src/view';
/* ---------- Helpers ---------- */
async function summarizeDocument(
content: string,
settings: PluginSettings,
job: GenerationJob,
onStreamProgress?: (progress: StreamProgress) => void,
) {
const { system, user } = buildPrompts(content, settings);
let cards: RawCard[];
switch (settings.backend) {
case 'claude-code':
cards = await summarizeViaClaudeCode(system, user, settings, job);
break;
case 'codex':
cards = await summarizeViaCodex(system, user, settings, job);
break;
default: {
const useStreaming = supportsStreaming(settings);
if (useStreaming) {
const abortController = new AbortController();
job.onCancel(() => abortController.abort());
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController.signal);
} else {
cards = await summarizeViaApi(requestUrl, system, user, settings);
}
break;
}
}
const resolved: ResolvedCard[] = cards.map((c) => ({
title: c.title,
level: 2,
anchor: c.anchor,
gist: c.gist,
startLine: findLineForAnchor(content, c.anchor),
bullets: c.bullets,
}));
resolved.sort((a, b) => {
if (a.startLine < 0 && b.startLine < 0) return 0;
if (a.startLine < 0) return 1;
if (b.startLine < 0) return -1;
return a.startLine - b.startLine;
});
return resolved;
}
function cancellationNoticeKey(settings: PluginSettings | null, job: GenerationJob | null) {
if (job?.phase === 'generating' && settings && isApiBackend(settings.backend)) {
return 'cancelRequestedApiInFlight';
}
return 'cancelRequested';
}
/* ---------- Plugin ---------- */
class ParallelReaderPlugin extends Plugin {

62
src/generation.ts Normal file
View file

@ -0,0 +1,62 @@
'use strict';
import { requestUrl } from 'obsidian';
import { findLineForAnchor } from './anchor';
import { summarizeViaClaudeCode, summarizeViaCodex } from './cli';
import type { GenerationJob } from './generation-job-manager';
import { buildPrompts } from './prompt';
import { summarizeViaApi, summarizeViaApiStreaming, supportsStreaming } from './providers';
import { isApiBackend } from './settings';
import type { StreamProgress } from './streaming';
import type { PluginSettings, RawCard, ResolvedCard } from './types';
export async function summarizeDocument(
content: string,
settings: PluginSettings,
job: GenerationJob,
onStreamProgress?: (progress: StreamProgress) => void,
) {
const { system, user } = buildPrompts(content, settings);
let cards: RawCard[];
switch (settings.backend) {
case 'claude-code':
cards = await summarizeViaClaudeCode(system, user, settings, job);
break;
case 'codex':
cards = await summarizeViaCodex(system, user, settings, job);
break;
default: {
const useStreaming = supportsStreaming(settings);
if (useStreaming) {
const abortController = new AbortController();
job.onCancel(() => abortController.abort());
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController.signal);
} else {
cards = await summarizeViaApi(requestUrl, system, user, settings);
}
break;
}
}
const resolved: ResolvedCard[] = cards.map((c) => ({
title: c.title,
level: 2,
anchor: c.anchor,
gist: c.gist,
startLine: findLineForAnchor(content, c.anchor),
bullets: c.bullets,
}));
resolved.sort((a, b) => {
if (a.startLine < 0 && b.startLine < 0) return 0;
if (a.startLine < 0) return 1;
if (b.startLine < 0) return -1;
return a.startLine - b.startLine;
});
return resolved;
}
export function cancellationNoticeKey(settings: PluginSettings | null, job: GenerationJob | null) {
if (job?.phase === 'generating' && settings && isApiBackend(settings.backend)) {
return 'cancelRequestedApiInFlight';
}
return 'cancelRequested';
}