feat: ship v0.7.0 with AI knowledge context integration
Add semi-automatic knowledge suggestions and user-confirmed injection across chat, interactive, auto, and wizard modes; fix writing flows to persist full systemPrompt in contextJson. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,15 +1,24 @@
|
||||
import { stripHtml } from './word-count'
|
||||
import type { AiContextBinding, AiContextBuildResult, ContextPreviewItem } from '../../shared/types'
|
||||
import type {
|
||||
AiContextBinding,
|
||||
AiContextBuildResult,
|
||||
ContextPreviewItem,
|
||||
KnowledgeType
|
||||
} from '../../shared/types'
|
||||
import type { BookRegistryService } from './book-registry'
|
||||
import { KnowledgeRepository } from '../db/repositories/knowledge.repo'
|
||||
|
||||
const CHAPTER_CHAR_LIMIT = 2000
|
||||
const OTHER_CHAR_LIMIT = 500
|
||||
const KNOWLEDGE_CHAR_LIMIT = 500
|
||||
const KNOWLEDGE_TOTAL_LIMIT = 4000
|
||||
const TOTAL_CHAR_LIMIT = 16 * 1024
|
||||
|
||||
interface BlockCandidate {
|
||||
sortKey: number
|
||||
text: string
|
||||
preview: ContextPreviewItem
|
||||
isKnowledge?: boolean
|
||||
}
|
||||
|
||||
function truncate(text: string, max: number): string {
|
||||
@@ -17,12 +26,33 @@ function truncate(text: string, max: number): string {
|
||||
return `${text.slice(0, max)}…`
|
||||
}
|
||||
|
||||
function knowledgeTypeLabel(type: KnowledgeType): string {
|
||||
const map: Record<KnowledgeType, string> = {
|
||||
character: '角色',
|
||||
foreshadow: '伏笔',
|
||||
location: '地点',
|
||||
relationship: '关系',
|
||||
fact: '事实'
|
||||
}
|
||||
return map[type]
|
||||
}
|
||||
|
||||
function formatBlock(kind: ContextPreviewItem['kind'], title: string, body: string): string {
|
||||
const label =
|
||||
kind === 'chapter' ? '章节' : kind === 'outline' ? '大纲' : kind === 'setting' ? '设定' : '灵感'
|
||||
kind === 'chapter'
|
||||
? '章节'
|
||||
: kind === 'outline'
|
||||
? '大纲'
|
||||
: kind === 'setting'
|
||||
? '设定'
|
||||
: '灵感'
|
||||
return `【${label}】${title}\n${body}`
|
||||
}
|
||||
|
||||
function formatKnowledgeBlock(type: KnowledgeType, title: string, body: string): string {
|
||||
return `【知识·${knowledgeTypeLabel(type)}】${title}\n${body}`
|
||||
}
|
||||
|
||||
export class AiContextBuilderService {
|
||||
build(
|
||||
binding: AiContextBinding,
|
||||
@@ -34,6 +64,7 @@ export class AiContextBuilderService {
|
||||
if (!meta) throw new Error('Book not found')
|
||||
|
||||
const candidates: BlockCandidate[] = []
|
||||
const knowledgeIds = binding.knowledgeIds ?? []
|
||||
|
||||
for (const id of binding.chapterIds) {
|
||||
const chapter = registry.getChapterRepo(bookId).get(id)
|
||||
@@ -104,6 +135,38 @@ export class AiContextBuilderService {
|
||||
})
|
||||
}
|
||||
|
||||
const knowledgeRepo = new KnowledgeRepository(registry.getDb(bookId))
|
||||
const knowledgeCandidates: BlockCandidate[] = []
|
||||
for (const id of knowledgeIds) {
|
||||
const entry = knowledgeRepo.get(id)
|
||||
if (!entry || entry.status !== 'approved') continue
|
||||
const body = truncate(stripHtml(entry.content || entry.title), KNOWLEDGE_CHAR_LIMIT)
|
||||
knowledgeCandidates.push({
|
||||
sortKey: entry.importance * 1000,
|
||||
isKnowledge: true,
|
||||
text: formatKnowledgeBlock(entry.type, entry.title, body),
|
||||
preview: {
|
||||
kind: 'knowledge',
|
||||
id,
|
||||
title: entry.title,
|
||||
excerpt: body,
|
||||
charCount: body.length,
|
||||
knowledgeType: entry.type
|
||||
}
|
||||
})
|
||||
}
|
||||
knowledgeCandidates.sort((a, b) => b.sortKey - a.sortKey)
|
||||
|
||||
let knowledgeChars = 0
|
||||
const trimmedKnowledge: BlockCandidate[] = []
|
||||
for (const k of knowledgeCandidates) {
|
||||
const next = knowledgeChars + k.text.length + (trimmedKnowledge.length > 0 ? 2 : 0)
|
||||
if (next > KNOWLEDGE_TOTAL_LIMIT) break
|
||||
trimmedKnowledge.push(k)
|
||||
knowledgeChars = next
|
||||
}
|
||||
|
||||
candidates.push(...trimmedKnowledge)
|
||||
candidates.sort((a, b) => b.sortKey - a.sortKey)
|
||||
|
||||
const selected: BlockCandidate[] = []
|
||||
|
||||
Reference in New Issue
Block a user