Files
jsc-marketplace/plugins/cockpit/companion/inject.ts
T

41 lines
1.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { redactSecrets } from './secrets.ts'
export const BUDGET = { card: 400, mood: 100, memory: 800, care: 200 } as const
export type Section = keyof typeof BUDGET
/** 粗估 token:CJK 一字約一 token,其餘約四字元一 token */
export function estimateTokens(text: string): number {
let cjk = 0
for (const ch of text) if (/[⺀-鿿＀-￯ -〿]/.test(ch)) cjk++
return cjk + Math.ceil((text.length - cjk) / 4)
}
export type Item = { text: string; score: number }
/** 在預算內保留高分項目,維持原順序;超出時先丟低分 */
export function fitBudget(items: Item[], budget: number): string[] {
const kept = new Set(items.map((_, i) => i))
const order = items.map((it, i) => ({ i, score: it.score })).sort((a, b) => a.score - b.score || b.i - a.i)
let total = items.reduce((n, it) => n + estimateTokens(it.text), 0)
for (const { i } of order) {
if (total <= budget) break
total -= estimateTokens(items[i]!.text)
kept.delete(i)
}
return items.filter((_, i) => kept.has(i)).map(it => it.text)
}
export type Sections = Record<Section, Item[]>
/** 穩定內容(角色卡)在前,變動內容(心情、記憶、關心情境)在後 */
export function assemble(s: Sections): string {
const part = (title: string, key: Section) => {
const lines = fitBudget(s[key], BUDGET[key]).map(redactSecrets)
return lines.length ? `## ${title}\n${lines.join('\n')}` : ''
}
const body = [part('角色', 'card'), part('互動記憶', 'memory'), part('目前心情', 'mood'), part('關心情境', 'care')]
.filter(Boolean)
.join('\n\n')
return body ? `<companion>\n${body}\n</companion>` : ''
}