mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
feat(ai): add summary strategy with brief and standard detail levels
Support two summary strategies (brief/standard) for AI-generated session summaries. Users can choose their preferred detail level in Settings > AI > Session Index. Standard (default) produces richer summaries covering people, events, and key details. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
eaff16d555
commit
6727ff609c
21 files changed
+173
-47
No files matched your search
@@ -25,12 +25,13 @@ export function registerSummaryRoutes(
|
||||
|
||||
server.post<{
|
||||
Params: { id: string }
|
||||
Body: { chatSessionId: number; locale?: string; forceRegenerate?: boolean }
|
||||
Body: { chatSessionId: number; locale?: string; forceRegenerate?: boolean; strategy?: 'brief' | 'standard' }
|
||||
}>('/_web/sessions/:id/summaries/generate', async (request, reply) => {
|
||||
const { chatSessionId, locale, forceRegenerate } = request.body
|
||||
const { chatSessionId, locale, forceRegenerate, strategy } = request.body
|
||||
const result = await summaryService.generateSummary(adapter, request.params.id, chatSessionId, deps, {
|
||||
locale,
|
||||
forceRegenerate,
|
||||
strategy,
|
||||
})
|
||||
if ('error' in result && !result.success) {
|
||||
return reply.code(400).send({ error: result.error })
|
||||
|
||||
@@ -83,9 +83,10 @@ export async function generateSessionSummary(
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale: string = 'zh-CN',
|
||||
forceRegenerate: boolean = false
|
||||
forceRegenerate: boolean = false,
|
||||
strategy?: 'brief' | 'standard'
|
||||
): Promise<{ success: boolean; summary?: string; error?: string }> {
|
||||
return generateCore(buildDeps(dbSessionId), chatSessionId, { locale, forceRegenerate })
|
||||
return generateCore(buildDeps(dbSessionId), chatSessionId, { locale, forceRegenerate, strategy })
|
||||
}
|
||||
|
||||
export async function generateSessionSummaries(
|
||||
|
||||
@@ -881,12 +881,20 @@ export function registerChatHandlers(ctx: IpcContext): void {
|
||||
*/
|
||||
ipcMain.handle(
|
||||
'session:generateSummary',
|
||||
async (_, dbSessionId: string, chatSessionId: number, locale?: string, forceRegenerate?: boolean) => {
|
||||
async (
|
||||
_,
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
) => {
|
||||
console.log('[IPC] session:generateSummary request received:', {
|
||||
dbSessionId,
|
||||
chatSessionId,
|
||||
locale,
|
||||
forceRegenerate,
|
||||
strategy,
|
||||
})
|
||||
try {
|
||||
const { generateSessionSummary } = await import('../ai/summary')
|
||||
@@ -894,7 +902,8 @@ export function registerChatHandlers(ctx: IpcContext): void {
|
||||
dbSessionId,
|
||||
chatSessionId,
|
||||
locale || 'zh-CN',
|
||||
forceRegenerate || false
|
||||
forceRegenerate || false,
|
||||
strategy
|
||||
)
|
||||
console.log('[IPC] session:generateSummary result:', result)
|
||||
return result
|
||||
|
||||
@@ -284,9 +284,10 @@ export const sessionApi = {
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
): Promise<{ success: boolean; summary?: string; error?: string }> => {
|
||||
return ipcRenderer.invoke('session:generateSummary', dbSessionId, chatSessionId, locale, forceRegenerate)
|
||||
return ipcRenderer.invoke('session:generateSummary', dbSessionId, chatSessionId, locale, forceRegenerate, strategy)
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+2
-1
@@ -1125,7 +1125,8 @@ interface SessionApi {
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
) => Promise<{ success: boolean; summary?: string; error?: string }>
|
||||
/** 批量生成会话摘要 */
|
||||
generateSummaries: (
|
||||
|
||||
@@ -32,6 +32,7 @@ export const localeConfigSchema = z.object({
|
||||
export const uiConfigSchema = z.object({
|
||||
default_session_tab: z.enum(['overview', 'ai-chat']).default('overview'),
|
||||
session_gap_threshold: z.number().int().min(60).max(86400).default(1800),
|
||||
summary_strategy: z.enum(['brief', 'standard']).default('standard'),
|
||||
})
|
||||
|
||||
export const configSchema = z.object({
|
||||
|
||||
@@ -120,7 +120,7 @@ export {
|
||||
filterValidMessages,
|
||||
splitIntoSegments,
|
||||
} from './summary'
|
||||
export type { SummaryDeps, SummaryMessage, SummaryOptions, SummaryResult } from './summary'
|
||||
export type { SummaryDeps, SummaryMessage, SummaryOptions, SummaryResult, SummaryStrategy } from './summary'
|
||||
|
||||
// LLM Config Store
|
||||
export { LLMConfigStore, MAX_CONFIG_COUNT } from './llm-config-store'
|
||||
|
||||
@@ -29,9 +29,12 @@ export interface SummaryDeps {
|
||||
}
|
||||
}
|
||||
|
||||
export type SummaryStrategy = 'brief' | 'standard'
|
||||
|
||||
export interface SummaryOptions {
|
||||
locale?: string
|
||||
forceRegenerate?: boolean
|
||||
strategy?: SummaryStrategy
|
||||
}
|
||||
|
||||
export interface SummaryResult {
|
||||
@@ -48,11 +51,18 @@ const SEGMENT_THRESHOLD = 8000
|
||||
|
||||
// ==================== Pure algorithms ====================
|
||||
|
||||
function getSummaryLengthLimit(messageCount: number): number {
|
||||
if (messageCount <= 10) return 50
|
||||
if (messageCount <= 30) return 80
|
||||
if (messageCount <= 100) return 120
|
||||
return 200
|
||||
function getSummaryLengthLimit(messageCount: number, strategy: SummaryStrategy = 'standard'): number {
|
||||
if (strategy === 'brief') {
|
||||
if (messageCount <= 10) return 50
|
||||
if (messageCount <= 30) return 80
|
||||
if (messageCount <= 100) return 120
|
||||
return 200
|
||||
}
|
||||
// standard: richer summaries covering who, what, and key details
|
||||
if (messageCount <= 10) return 100
|
||||
if (messageCount <= 30) return 200
|
||||
if (messageCount <= 100) return 350
|
||||
return 500
|
||||
}
|
||||
|
||||
const MEANINGFUL_SHORT_ZH = ['好的', '不是', '是的', '可以', '不行', '好吧', '明白', '知道', '同意']
|
||||
@@ -166,26 +176,44 @@ export function splitIntoSegments(
|
||||
|
||||
// ==================== Prompt builders ====================
|
||||
|
||||
function buildSummaryPrompt(content: string, lengthLimit: number, locale: string): string {
|
||||
if (locale.startsWith('zh')) {
|
||||
return `请用简洁的语言(${lengthLimit}字以内)总结以下对话的主要内容或话题。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${content}`
|
||||
function buildSummaryPrompt(content: string, lengthLimit: number, locale: string, strategy: SummaryStrategy): string {
|
||||
if (strategy === 'brief') {
|
||||
if (locale.startsWith('zh')) {
|
||||
return `请用简洁的语言(${lengthLimit}字以内)总结以下对话的主要内容或话题。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${content}`
|
||||
}
|
||||
return `Summarize the following conversation concisely (max ${lengthLimit} characters). Output only the summary, no prefix, explanation, or quotes.\n\n${content}`
|
||||
}
|
||||
return `Summarize the following conversation concisely (max ${lengthLimit} characters). Output only the summary, no prefix, explanation, or quotes.\n\n${content}`
|
||||
if (locale.startsWith('zh')) {
|
||||
return `请总结以下聊天记录(${lengthLimit}字以内),需包含:谁参与了讨论、讨论了什么事情、关键细节和结论。用2-4句话描述,不要罗列每条消息。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${content}`
|
||||
}
|
||||
return `Summarize the following chat log (max ${lengthLimit} characters). Include: who participated, what was discussed, key details and conclusions. Use 2-4 sentences, don't list individual messages. Output only the summary, no prefix, explanation, or quotes.\n\n${content}`
|
||||
}
|
||||
|
||||
function buildSubSummaryPrompt(content: string, locale: string): string {
|
||||
function buildSubSummaryPrompt(content: string, locale: string, strategy: SummaryStrategy): string {
|
||||
const limit = strategy === 'brief' ? 50 : 100
|
||||
if (locale.startsWith('zh')) {
|
||||
return `请用一句话(不超过50字)概括以下对话片段的主要内容。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${content}`
|
||||
return `请用一到两句话(不超过${limit}字)概括以下对话片段的主要内容,包括谁说了什么。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${content}`
|
||||
}
|
||||
return `Summarize this conversation segment in one sentence (max 50 characters). Output only the summary, no prefix or quotes.\n\n${content}`
|
||||
return `Summarize this conversation segment in 1-2 sentences (max ${limit} characters), including who said what. Output only the summary, no prefix or quotes.\n\n${content}`
|
||||
}
|
||||
|
||||
function buildMergePrompt(subSummaries: string[], lengthLimit: number, locale: string): string {
|
||||
function buildMergePrompt(
|
||||
subSummaries: string[],
|
||||
lengthLimit: number,
|
||||
locale: string,
|
||||
strategy: SummaryStrategy
|
||||
): string {
|
||||
const list = subSummaries.map((s, i) => `${i + 1}. ${s}`).join('\n')
|
||||
if (locale.startsWith('zh')) {
|
||||
return `以下是一段对话的多个片段摘要,请将它们合并成一个完整的总结(${lengthLimit}字以内)。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${list}`
|
||||
if (strategy === 'brief') {
|
||||
if (locale.startsWith('zh')) {
|
||||
return `以下是一段对话的多个片段摘要,请将它们合并成一个完整的总结(${lengthLimit}字以内)。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${list}`
|
||||
}
|
||||
return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Output only the summary, no prefix or quotes.\n\n${list}`
|
||||
}
|
||||
return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Output only the summary, no prefix or quotes.\n\n${list}`
|
||||
if (locale.startsWith('zh')) {
|
||||
return `以下是一段对话的多个片段摘要,请将它们合并成一个完整的总结(${lengthLimit}字以内)。保留参与者、事件和关键细节。只输出摘要内容,不要添加任何前缀、解释或引号。\n\n${list}`
|
||||
}
|
||||
return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Preserve participants, events, and key details. Output only the summary, no prefix or quotes.\n\n${list}`
|
||||
}
|
||||
|
||||
// ==================== Post-processing ====================
|
||||
@@ -209,7 +237,7 @@ export async function generateSessionSummary(
|
||||
chatSessionId: number,
|
||||
options: SummaryOptions = {}
|
||||
): Promise<SummaryResult> {
|
||||
const { locale = 'zh-CN', forceRegenerate = false } = options
|
||||
const { locale = 'zh-CN', forceRegenerate = false, strategy = 'standard' } = options
|
||||
const log = deps.logger
|
||||
|
||||
try {
|
||||
@@ -232,20 +260,22 @@ export async function generateSessionSummary(
|
||||
return { success: false, error: deps.t('summary.tooFewValidMessages', { count: MIN_MESSAGE_COUNT }) }
|
||||
}
|
||||
|
||||
const lengthLimit = getSummaryLengthLimit(validMessages.length)
|
||||
const lengthLimit = getSummaryLengthLimit(validMessages.length, strategy)
|
||||
const maxTokens = strategy === 'brief' ? 300 : 600
|
||||
const subMaxTokens = strategy === 'brief' ? 100 : 200
|
||||
const content = formatMessages(validMessages)
|
||||
|
||||
log?.info(
|
||||
'Summary',
|
||||
`Generating summary: sessionId=${chatSessionId}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
|
||||
`Generating summary: sessionId=${chatSessionId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
|
||||
)
|
||||
|
||||
let summary: string
|
||||
if (content.length <= SEGMENT_THRESHOLD) {
|
||||
summary = await deps.llmComplete(
|
||||
deps.t('summary.systemPromptDirect'),
|
||||
buildSummaryPrompt(content, lengthLimit, locale),
|
||||
{ temperature: 0.3, maxTokens: 300 }
|
||||
buildSummaryPrompt(content, lengthLimit, locale, strategy),
|
||||
{ temperature: 0.3, maxTokens }
|
||||
)
|
||||
summary = summary.trim()
|
||||
} else {
|
||||
@@ -257,8 +287,8 @@ export async function generateSessionSummary(
|
||||
const segContent = formatMessages(segment)
|
||||
const sub = await deps.llmComplete(
|
||||
deps.t('summary.systemPromptDirect'),
|
||||
buildSubSummaryPrompt(segContent, locale),
|
||||
{ temperature: 0.3, maxTokens: 100 }
|
||||
buildSubSummaryPrompt(segContent, locale, strategy),
|
||||
{ temperature: 0.3, maxTokens: subMaxTokens }
|
||||
)
|
||||
subSummaries.push(sub.trim())
|
||||
}
|
||||
@@ -268,8 +298,8 @@ export async function generateSessionSummary(
|
||||
} else {
|
||||
summary = await deps.llmComplete(
|
||||
deps.t('summary.systemPromptMerge'),
|
||||
buildMergePrompt(subSummaries, lengthLimit, locale),
|
||||
{ temperature: 0.3, maxTokens: 300 }
|
||||
buildMergePrompt(subSummaries, lengthLimit, locale, strategy),
|
||||
{ temperature: 0.3, maxTokens }
|
||||
)
|
||||
summary = summary.trim()
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ export {
|
||||
filterValidMessages,
|
||||
splitIntoSegments,
|
||||
} from './ai'
|
||||
export type { SummaryDeps, SummaryMessage, SummaryOptions, SummaryResult } from './ai'
|
||||
export type { SummaryDeps, SummaryMessage, SummaryOptions, SummaryResult, SummaryStrategy } from './ai'
|
||||
|
||||
// LLM Config Store
|
||||
export { LLMConfigStore, MAX_CONFIG_COUNT } from './ai'
|
||||
|
||||
@@ -196,4 +196,5 @@ export interface Preferences {
|
||||
export interface UiConfig {
|
||||
default_session_tab: 'overview' | 'ai-chat'
|
||||
session_gap_threshold: number
|
||||
summary_strategy?: 'brief' | 'standard'
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { ref, computed, watch, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { useSessionIndexService } from '@/services'
|
||||
import { getSummaryStrategy } from '@/composables/useUiConfig'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
@@ -271,7 +272,13 @@ async function startGenerate() {
|
||||
if (shouldStop.value) break
|
||||
|
||||
try {
|
||||
const result = await useSessionIndexService().generateSummary(props.sessionId, session.id, locale.value, false)
|
||||
const result = await useSessionIndexService().generateSummary(
|
||||
props.sessionId,
|
||||
session.id,
|
||||
locale.value,
|
||||
false,
|
||||
getSummaryStrategy()
|
||||
)
|
||||
|
||||
if (result.success) {
|
||||
// 成功:显示摘要内容
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
*/
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import UITabs from '@/components/UI/Tabs.vue'
|
||||
import { useDataService, useSessionIndexService } from '@/services'
|
||||
import { getSessionGapThreshold, patchUiConfig } from '@/composables/useUiConfig'
|
||||
import {
|
||||
getSessionGapThreshold,
|
||||
getSummaryStrategy,
|
||||
patchUiConfig,
|
||||
type SummaryStrategy,
|
||||
} from '@/composables/useUiConfig'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -22,6 +28,13 @@ interface SessionIndexStatus {
|
||||
const DEFAULT_GAP_MINUTES = 30 // 默认30分钟
|
||||
const sessionGapMinutes = ref(DEFAULT_GAP_MINUTES)
|
||||
|
||||
// 摘要策略
|
||||
const summaryStrategy = ref<SummaryStrategy>('standard')
|
||||
const summaryStrategyItems = computed(() => [
|
||||
{ label: t('settings.storage.session.summaryBrief'), value: 'brief' },
|
||||
{ label: t('settings.storage.session.summaryStandard'), value: 'standard' },
|
||||
])
|
||||
|
||||
// 批量生成相关状态
|
||||
const allSessionsStatus = ref<SessionIndexStatus[]>([])
|
||||
const isLoadingSessionStatus = ref(false)
|
||||
@@ -53,6 +66,11 @@ function loadSessionThreshold() {
|
||||
sessionGapMinutes.value = Math.round(threshold / 60)
|
||||
}
|
||||
|
||||
function onSummaryStrategyChange(val: string | number) {
|
||||
summaryStrategy.value = val as SummaryStrategy
|
||||
patchUiConfig({ summary_strategy: val as SummaryStrategy })
|
||||
}
|
||||
|
||||
// 加载所有会话的索引状态
|
||||
async function loadSessionIndexStatus() {
|
||||
isLoadingSessionStatus.value = true
|
||||
@@ -168,6 +186,7 @@ async function batchRegenerateAll() {
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadSessionThreshold()
|
||||
summaryStrategy.value = getSummaryStrategy()
|
||||
loadSessionIndexStatus()
|
||||
})
|
||||
</script>
|
||||
@@ -222,6 +241,26 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 摘要策略设置 -->
|
||||
<div
|
||||
class="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-800/50"
|
||||
>
|
||||
<div>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">
|
||||
{{ t('settings.storage.session.summaryStrategy') }}
|
||||
</span>
|
||||
<p class="text-xs text-gray-400">
|
||||
{{ t('settings.storage.session.summaryStrategyHelp') }}
|
||||
</p>
|
||||
</div>
|
||||
<UITabs
|
||||
:model-value="summaryStrategy"
|
||||
:items="summaryStrategyItems"
|
||||
size="xs"
|
||||
@update:model-value="onSummaryStrategyChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 会话索引统计 -->
|
||||
<div class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-800/50">
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -69,6 +69,15 @@ function setupConfigWatchers(svc: ReturnType<typeof usePreferencesService>): voi
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => getUiConfig().summary_strategy,
|
||||
(val) => {
|
||||
svc.saveUiConfig({ summary_strategy: val }).catch((err) => {
|
||||
console.warn('[PreferencesSync] Failed to save ui config:', err)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => settingsStore.locale,
|
||||
(val) => {
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
import { ref } from 'vue'
|
||||
import type { UiConfig } from '@/services/preferences/types'
|
||||
|
||||
export type SummaryStrategy = 'brief' | 'standard'
|
||||
|
||||
const _uiConfig = ref<UiConfig>({
|
||||
default_session_tab: 'overview',
|
||||
session_gap_threshold: 1800,
|
||||
summary_strategy: 'standard',
|
||||
})
|
||||
|
||||
export function getUiConfig(): UiConfig {
|
||||
@@ -27,3 +30,7 @@ export function patchUiConfig(partial: Partial<UiConfig>): void {
|
||||
export function getSessionGapThreshold(): number {
|
||||
return _uiConfig.value.session_gap_threshold
|
||||
}
|
||||
|
||||
export function getSummaryStrategy(): SummaryStrategy {
|
||||
return _uiConfig.value.summary_strategy ?? 'standard'
|
||||
}
|
||||
@@ -274,7 +274,11 @@
|
||||
"notGeneratedCount": "{count} not generated",
|
||||
"loadingStatus": "Loading...",
|
||||
"batchGenerate": "Generate Missing",
|
||||
"batchRegenerate": "Regenerate All"
|
||||
"batchRegenerate": "Regenerate All",
|
||||
"summaryStrategy": "Summary Detail Level",
|
||||
"summaryStrategyHelp": "Controls how detailed AI-generated summaries will be",
|
||||
"summaryBrief": "Brief",
|
||||
"summaryStandard": "Standard"
|
||||
}
|
||||
},
|
||||
"aiPrompt": {
|
||||
|
||||
@@ -274,7 +274,11 @@
|
||||
"notGeneratedCount": "未生成 {count} 件",
|
||||
"loadingStatus": "読み込み中...",
|
||||
"batchGenerate": "未生成分を作成",
|
||||
"batchRegenerate": "すべて再生成"
|
||||
"batchRegenerate": "すべて再生成",
|
||||
"summaryStrategy": "要約の詳細度",
|
||||
"summaryStrategyHelp": "AI が要約を生成する際の詳細さを設定します",
|
||||
"summaryBrief": "簡潔",
|
||||
"summaryStandard": "標準"
|
||||
}
|
||||
},
|
||||
"aiPrompt": {
|
||||
|
||||
@@ -274,7 +274,11 @@
|
||||
"notGeneratedCount": "未生成 {count} 个",
|
||||
"loadingStatus": "加载中...",
|
||||
"batchGenerate": "生成未索引",
|
||||
"batchRegenerate": "全部重新生成"
|
||||
"batchRegenerate": "全部重新生成",
|
||||
"summaryStrategy": "摘要详细程度",
|
||||
"summaryStrategyHelp": "影响 AI 生成摘要时的详细程度",
|
||||
"summaryBrief": "简要",
|
||||
"summaryStandard": "标准"
|
||||
}
|
||||
},
|
||||
"aiPrompt": {
|
||||
|
||||
@@ -274,7 +274,11 @@
|
||||
"notGeneratedCount": "未產生 {count} 個",
|
||||
"loadingStatus": "載入中...",
|
||||
"batchGenerate": "產生未索引項目",
|
||||
"batchRegenerate": "全部重新產生"
|
||||
"batchRegenerate": "全部重新產生",
|
||||
"summaryStrategy": "摘要詳細程度",
|
||||
"summaryStrategyHelp": "影響 AI 生成摘要時的詳細程度",
|
||||
"summaryBrief": "簡要",
|
||||
"summaryStandard": "標準"
|
||||
}
|
||||
},
|
||||
"aiPrompt": {
|
||||
|
||||
@@ -52,9 +52,10 @@ export class ElectronSessionIndexAdapter implements SessionIndexAdapter {
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
): Promise<SummaryResult> {
|
||||
return window.sessionApi.generateSummary(dbSessionId, chatSessionId, locale, forceRegenerate)
|
||||
return window.sessionApi.generateSummary(dbSessionId, chatSessionId, locale, forceRegenerate, strategy)
|
||||
}
|
||||
|
||||
generateSummaries(dbSessionId: string, chatSessionIds: number[], locale?: string): Promise<BatchSummaryResult> {
|
||||
|
||||
@@ -100,13 +100,14 @@ export class FetchSessionIndexAdapter implements SessionIndexAdapter {
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
): Promise<SummaryResult> {
|
||||
try {
|
||||
const resp = await fetch(`/_web/sessions/${dbSessionId}/summaries/generate`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chatSessionId, locale, forceRegenerate }),
|
||||
body: JSON.stringify({ chatSessionId, locale, forceRegenerate, strategy }),
|
||||
})
|
||||
const result = await resp.json()
|
||||
if (!resp.ok) {
|
||||
|
||||
@@ -52,7 +52,8 @@ export interface SessionIndexAdapter {
|
||||
dbSessionId: string,
|
||||
chatSessionId: number,
|
||||
locale?: string,
|
||||
forceRegenerate?: boolean
|
||||
forceRegenerate?: boolean,
|
||||
strategy?: 'brief' | 'standard'
|
||||
): Promise<SummaryResult>
|
||||
|
||||
generateSummaries(dbSessionId: string, chatSessionIds: number[], locale?: string): Promise<BatchSummaryResult>
|
||||
|
||||
Reference in new issue
Block a user