mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-26 02:04:37 +08:00
refactor(core): extract remaining inline SQL from electron adapters
- Extract getPrivateChatMemberAvatar to @openchatlab/core - Extract getExportSessionData (meta/members/messages SQL) to @openchatlab/core - Extract loadSessionMessages for summary generation to @openchatlab/core - Replace inline SQL in summary buildDeps with shared core query functions - Extract createCompressionLlmAdapter factory to @openchatlab/node-runtime - Simplify server and electron compression adapter creation via shared factory
This commit is contained in:
1 parent
53fa23f488
commit
31359712b3
11 files changed
+246
-173
No files matched your search
@@ -7,8 +7,10 @@
|
||||
|
||||
import Database from 'better-sqlite3'
|
||||
import { completeSimple, type PiTextContent } from '@openchatlab/node-runtime'
|
||||
import { loadSessionMessages, getChatSessionSummary, saveChatSessionSummary } from '@openchatlab/core'
|
||||
import { getFastModelConfig, buildPiModel } from '../llm'
|
||||
import { getDbPath, openDatabase } from '../../database/core'
|
||||
import { wrapAsDatabaseAdapter } from '../../worker/core'
|
||||
import { aiLogger } from '../logger'
|
||||
import { t } from '../../i18n'
|
||||
import {
|
||||
@@ -24,18 +26,7 @@ function buildDeps(dbSessionId: string): SummaryDeps {
|
||||
const db = openDatabase(dbSessionId, true)
|
||||
if (!db) return null
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
COALESCE(mb.group_nickname, mb.account_name, mb.platform_id) as senderName,
|
||||
m.content
|
||||
FROM message_context mc
|
||||
JOIN message m ON m.id = mc.message_id
|
||||
JOIN member mb ON mb.id = m.sender_id
|
||||
WHERE mc.session_id = ?
|
||||
ORDER BY m.ts ASC
|
||||
LIMIT ?
|
||||
`
|
||||
return db.prepare(sql).all(chatSessionId, limit) as Array<{ senderName: string; content: string | null }>
|
||||
return loadSessionMessages(wrapAsDatabaseAdapter(db), chatSessionId, limit)
|
||||
} catch (error) {
|
||||
aiLogger.error('Summary', `Failed to get session messages: ${error}`)
|
||||
return null
|
||||
@@ -46,7 +37,7 @@ function buildDeps(dbSessionId: string): SummaryDeps {
|
||||
const dbPath = getDbPath(dbSessionId)
|
||||
const db = new Database(dbPath)
|
||||
try {
|
||||
db.prepare('UPDATE chat_session SET summary = ? WHERE id = ?').run(summary, chatSessionId)
|
||||
saveChatSessionSummary(wrapAsDatabaseAdapter(db), chatSessionId, summary)
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
@@ -56,10 +47,7 @@ function buildDeps(dbSessionId: string): SummaryDeps {
|
||||
const db = openDatabase(dbSessionId, true)
|
||||
if (!db) return null
|
||||
try {
|
||||
const row = db.prepare('SELECT summary FROM chat_session WHERE id = ?').get(chatSessionId) as
|
||||
| { summary: string | null }
|
||||
| undefined
|
||||
return row?.summary || null
|
||||
return getChatSessionSummary(wrapAsDatabaseAdapter(db), chatSessionId)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
||||
+11
-33
@@ -20,6 +20,7 @@ import * as skillManager from '../ai/skills'
|
||||
import {
|
||||
checkAndCompress,
|
||||
manualCompress,
|
||||
createCompressionLlmAdapter,
|
||||
type CompressionConfig,
|
||||
type CompressionLlmAdapter,
|
||||
completeSimple,
|
||||
@@ -43,38 +44,15 @@ const compressionLogger = {
|
||||
error: (cat: string, msg: string, extra?: Record<string, unknown>) => aiLogger.error(cat, msg, extra),
|
||||
}
|
||||
|
||||
function createCompressionLlmAdapter(
|
||||
activeAIConfig: AIServiceConfig,
|
||||
onCompressing?: () => void
|
||||
): CompressionLlmAdapter {
|
||||
function buildCompressionAdapter(activeAIConfig: AIServiceConfig, onCompressing?: () => void): CompressionLlmAdapter {
|
||||
const modelDef = findModelDefinition(activeAIConfig.provider, activeAIConfig.model || '')
|
||||
const contextWindow = modelDef?.contextWindow ?? DEFAULT_CONTEXT_WINDOW
|
||||
const piModel = buildPiModel(activeAIConfig)
|
||||
|
||||
return {
|
||||
contextWindow,
|
||||
compress: async (prompt: string, maxTokens: number) => {
|
||||
onCompressing?.()
|
||||
try {
|
||||
const result = await completeSimple(
|
||||
piModel,
|
||||
{
|
||||
systemPrompt: undefined,
|
||||
messages: [{ role: 'user', content: [{ type: 'text', text: prompt }], timestamp: Date.now() }] as any,
|
||||
},
|
||||
{ apiKey: activeAIConfig.apiKey, maxTokens }
|
||||
)
|
||||
const text = result.content
|
||||
.filter((item): item is PiTextContent => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('')
|
||||
return text || null
|
||||
} catch (error) {
|
||||
aiLogger.warn('Compression', 'LLM compression attempt failed', { error: String(error) })
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
return createCompressionLlmAdapter({
|
||||
piModel: buildPiModel(activeAIConfig),
|
||||
apiKey: activeAIConfig.apiKey,
|
||||
contextWindow: modelDef?.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
||||
onCompressing,
|
||||
onError: (error) => aiLogger.warn('Compression', 'LLM compression attempt failed', { error: String(error) }),
|
||||
})
|
||||
}
|
||||
|
||||
function toPiSimpleMessages(messages: Array<{ role: string; content: string }>, timestamp: number): PiMessage[] {
|
||||
@@ -993,7 +971,7 @@ export function registerAIHandlers({ win }: IpcContext): void {
|
||||
context.conversationId,
|
||||
compressionConfig,
|
||||
systemPromptForCompression,
|
||||
createCompressionLlmAdapter(activeAIConfig, () => {
|
||||
buildCompressionAdapter(activeAIConfig, () => {
|
||||
win.webContents.send('agent:streamChunk', {
|
||||
requestId,
|
||||
chunk: {
|
||||
@@ -1250,7 +1228,7 @@ export function registerAIHandlers({ win }: IpcContext): void {
|
||||
conversationId,
|
||||
compressionConfig,
|
||||
systemPrompt,
|
||||
createCompressionLlmAdapter(activeAIConfig),
|
||||
buildCompressionAdapter(activeAIConfig),
|
||||
getConversationManager(),
|
||||
compressionLogger
|
||||
)
|
||||
|
||||
@@ -181,6 +181,8 @@ export async function mergeFilesWithTempDb(
|
||||
|
||||
import Database from 'better-sqlite3'
|
||||
import { getDbPath } from '../database/core'
|
||||
import { getExportSessionData } from '@openchatlab/core'
|
||||
import { wrapAsDatabaseAdapter } from '../worker/core'
|
||||
|
||||
export async function exportSessionToTempFile(sessionId: string): Promise<string> {
|
||||
const dbPath = getDbPath(sessionId)
|
||||
@@ -191,74 +193,26 @@ export async function exportSessionToTempFile(sessionId: string): Promise<string
|
||||
const db = new Database(dbPath, { readonly: true })
|
||||
|
||||
try {
|
||||
const meta = db.prepare('SELECT * FROM meta').get() as {
|
||||
name: string
|
||||
platform: string
|
||||
type: string
|
||||
group_id?: string
|
||||
group_avatar?: string
|
||||
}
|
||||
|
||||
if (!meta) {
|
||||
throw new Error('Cannot read session meta')
|
||||
}
|
||||
|
||||
const members = db.prepare('SELECT platform_id, account_name, group_nickname, avatar FROM member').all() as Array<{
|
||||
platform_id: string
|
||||
account_name?: string
|
||||
group_nickname?: string
|
||||
avatar?: string
|
||||
}>
|
||||
|
||||
const messages = db
|
||||
.prepare(
|
||||
`SELECT
|
||||
m.platform_id as sender,
|
||||
msg.sender_account_name as accountName,
|
||||
msg.sender_group_nickname as groupNickname,
|
||||
msg.ts as timestamp,
|
||||
msg.type,
|
||||
msg.content
|
||||
FROM message msg
|
||||
JOIN member m ON msg.sender_id = m.id
|
||||
ORDER BY msg.ts`
|
||||
)
|
||||
.all() as Array<{
|
||||
sender: string
|
||||
accountName?: string
|
||||
groupNickname?: string
|
||||
timestamp: number
|
||||
type: number
|
||||
content?: string
|
||||
}>
|
||||
const data = getExportSessionData(wrapAsDatabaseAdapter(db))
|
||||
|
||||
const chatLabData: ChatLabFormat = {
|
||||
chatlab: {
|
||||
version: '0.0.1',
|
||||
exportedAt: Math.floor(Date.now() / 1000),
|
||||
generator: 'ChatLab Export',
|
||||
description: `Exported from session: ${meta.name}`,
|
||||
description: `Exported from session: ${data.meta.name}`,
|
||||
},
|
||||
meta: {
|
||||
name: meta.name,
|
||||
platform: meta.platform as ChatPlatform,
|
||||
type: meta.type as ChatType,
|
||||
groupId: meta.group_id,
|
||||
groupAvatar: meta.group_avatar,
|
||||
name: data.meta.name,
|
||||
platform: data.meta.platform as ChatPlatform,
|
||||
type: data.meta.type as ChatType,
|
||||
groupId: data.meta.groupId,
|
||||
groupAvatar: data.meta.groupAvatar,
|
||||
},
|
||||
members: members.map((m) => ({
|
||||
platformId: m.platform_id,
|
||||
accountName: m.account_name || m.platform_id,
|
||||
groupNickname: m.group_nickname || undefined,
|
||||
avatar: m.avatar,
|
||||
})),
|
||||
messages: messages.map((msg) => ({
|
||||
sender: msg.sender,
|
||||
accountName: msg.accountName || msg.sender,
|
||||
groupNickname: msg.groupNickname || undefined,
|
||||
timestamp: msg.timestamp,
|
||||
members: data.members,
|
||||
messages: data.messages.map((msg) => ({
|
||||
...msg,
|
||||
type: msg.type as ChatLabMessage['type'],
|
||||
content: msg.content ?? null,
|
||||
})),
|
||||
}
|
||||
|
||||
@@ -267,7 +221,7 @@ export async function exportSessionToTempFile(sessionId: string): Promise<string
|
||||
const tempFilePath = path.join(tempDir, `export_${sessionId}_${Date.now()}.json`)
|
||||
fs.writeFileSync(tempFilePath, JSON.stringify(chatLabData, null, 2), 'utf-8')
|
||||
|
||||
console.log(`[Merger] Exporting session to temp file: ${tempFilePath}, message count: ${messages.length}`)
|
||||
console.log(`[Merger] Exporting session to temp file: ${tempFilePath}, message count: ${data.messages.length}`)
|
||||
|
||||
return tempFilePath
|
||||
} finally {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
buildSessionInfo,
|
||||
getSummaryCount,
|
||||
getLastPlatformMessageId,
|
||||
getPrivateChatMemberAvatar,
|
||||
type SessionOverview,
|
||||
} from '@openchatlab/core'
|
||||
import type { DatabaseAdapter } from '@openchatlab/core'
|
||||
@@ -48,41 +49,6 @@ function isChatSessionDb(db: Database.Database): boolean {
|
||||
return requiredTableCount.cnt === 3
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私聊对方成员的头像
|
||||
* 逻辑参考 private-chat/index.vue 的 otherMemberAvatar
|
||||
*/
|
||||
function getPrivateChatMemberAvatar(
|
||||
db: Database.Database,
|
||||
sessionName: string,
|
||||
ownerId: string | null | undefined
|
||||
): string | null {
|
||||
const members = db
|
||||
.prepare(
|
||||
`SELECT
|
||||
m.platform_id as platformId,
|
||||
COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,
|
||||
m.avatar
|
||||
FROM member m
|
||||
WHERE COALESCE(m.account_name, '') != '系统消息'
|
||||
ORDER BY (SELECT COUNT(*) FROM message WHERE sender_id = m.id) DESC`
|
||||
)
|
||||
.all() as Array<{ platformId: string; name: string; avatar: string | null }>
|
||||
|
||||
if (members.length === 0) return null
|
||||
|
||||
if (ownerId) {
|
||||
const other = members.find((m) => m.platformId !== ownerId)
|
||||
if (other?.avatar) return other.avatar
|
||||
}
|
||||
|
||||
const sameName = members.find((m) => m.name === sessionName)
|
||||
if (sameName?.avatar) return sameName.avatar
|
||||
|
||||
const firstWithAvatar = members.find((m) => m.avatar)
|
||||
return firstWithAvatar?.avatar || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve overview from cache (with compute-on-miss), or fall back to live SQL via core.
|
||||
*/
|
||||
@@ -135,7 +101,7 @@ export function getAllSessions(): any[] {
|
||||
|
||||
let memberAvatar: string | null = null
|
||||
if (meta.type === 'private') {
|
||||
memberAvatar = getPrivateChatMemberAvatar(db, meta.name, meta.ownerId)
|
||||
memberAvatar = getPrivateChatMemberAvatar(asCoreDb(db), meta.name, meta.ownerId)
|
||||
}
|
||||
|
||||
sessions.push({
|
||||
|
||||
@@ -39,12 +39,15 @@ export {
|
||||
getChatSessionList,
|
||||
getSessionsByTimeRange,
|
||||
getRecentChatSessions,
|
||||
loadSessionMessages,
|
||||
getChatSessionSummary,
|
||||
saveChatSessionSummary,
|
||||
updateSessionGapThreshold,
|
||||
clearSessionIndex,
|
||||
generateSessionIndex,
|
||||
generateIncrementalSessionIndex,
|
||||
getPrivateChatMemberAvatar,
|
||||
getExportSessionData,
|
||||
getTimeRange,
|
||||
getAvailableYears,
|
||||
getMemberActivity,
|
||||
|
||||
@@ -602,6 +602,33 @@ export function getChatSessionList(db: DatabaseAdapter): ChatSessionItem[] {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load messages for a chat session (for summary generation).
|
||||
*/
|
||||
export function loadSessionMessages(
|
||||
db: DatabaseAdapter,
|
||||
chatSessionId: number,
|
||||
limit: number = 500
|
||||
): Array<{ senderName: string; content: string | null }> | null {
|
||||
try {
|
||||
return db
|
||||
.prepare(
|
||||
`SELECT
|
||||
COALESCE(mb.group_nickname, mb.account_name, mb.platform_id) as senderName,
|
||||
m.content
|
||||
FROM message_context mc
|
||||
JOIN message m ON m.id = mc.message_id
|
||||
JOIN member mb ON mb.id = m.sender_id
|
||||
WHERE mc.session_id = ?
|
||||
ORDER BY m.ts ASC
|
||||
LIMIT ?`
|
||||
)
|
||||
.all(chatSessionId, limit) as unknown as Array<{ senderName: string; content: string | null }>
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get summary text for a single chat session.
|
||||
*/
|
||||
@@ -811,3 +838,131 @@ export function generateIncrementalSessionIndex(
|
||||
return newSessionCount
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== Export data ====================
|
||||
|
||||
export interface ExportSessionData {
|
||||
meta: {
|
||||
name: string
|
||||
platform: string
|
||||
type: string
|
||||
groupId?: string
|
||||
groupAvatar?: string
|
||||
}
|
||||
members: Array<{
|
||||
platformId: string
|
||||
accountName: string
|
||||
groupNickname?: string
|
||||
avatar?: string
|
||||
}>
|
||||
messages: Array<{
|
||||
sender: string
|
||||
accountName: string
|
||||
groupNickname?: string
|
||||
timestamp: number
|
||||
type: number
|
||||
content: string | null
|
||||
}>
|
||||
}
|
||||
|
||||
/**
|
||||
* Query all data needed for session export (meta + members + messages).
|
||||
*/
|
||||
export function getExportSessionData(db: DatabaseAdapter): ExportSessionData {
|
||||
const meta = db.prepare('SELECT * FROM meta').get() as {
|
||||
name: string
|
||||
platform: string
|
||||
type: string
|
||||
group_id?: string
|
||||
group_avatar?: string
|
||||
}
|
||||
if (!meta) throw new Error('Cannot read session meta')
|
||||
|
||||
const members = db.prepare('SELECT platform_id, account_name, group_nickname, avatar FROM member').all() as Array<{
|
||||
platform_id: string
|
||||
account_name?: string
|
||||
group_nickname?: string
|
||||
avatar?: string
|
||||
}>
|
||||
|
||||
const messages = db
|
||||
.prepare(
|
||||
`SELECT
|
||||
m.platform_id as sender,
|
||||
msg.sender_account_name as accountName,
|
||||
msg.sender_group_nickname as groupNickname,
|
||||
msg.ts as timestamp,
|
||||
msg.type,
|
||||
msg.content
|
||||
FROM message msg
|
||||
JOIN member m ON msg.sender_id = m.id
|
||||
ORDER BY msg.ts`
|
||||
)
|
||||
.all() as unknown as Array<{
|
||||
sender: string
|
||||
accountName?: string
|
||||
groupNickname?: string
|
||||
timestamp: number
|
||||
type: number
|
||||
content?: string
|
||||
}>
|
||||
|
||||
return {
|
||||
meta: {
|
||||
name: meta.name,
|
||||
platform: meta.platform,
|
||||
type: meta.type,
|
||||
groupId: meta.group_id,
|
||||
groupAvatar: meta.group_avatar,
|
||||
},
|
||||
members: members.map((m) => ({
|
||||
platformId: m.platform_id,
|
||||
accountName: m.account_name || m.platform_id,
|
||||
groupNickname: m.group_nickname || undefined,
|
||||
avatar: m.avatar,
|
||||
})),
|
||||
messages: messages.map((msg) => ({
|
||||
sender: msg.sender,
|
||||
accountName: msg.accountName || msg.sender,
|
||||
groupNickname: msg.groupNickname || undefined,
|
||||
timestamp: msg.timestamp,
|
||||
type: msg.type,
|
||||
content: msg.content ?? null,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get private chat partner's avatar.
|
||||
* Finds the "other" member (not the owner) in a private chat, falling back to name match or first with avatar.
|
||||
*/
|
||||
export function getPrivateChatMemberAvatar(
|
||||
db: DatabaseAdapter,
|
||||
sessionName: string,
|
||||
ownerId: string | null | undefined
|
||||
): string | null {
|
||||
const members = db
|
||||
.prepare(
|
||||
`SELECT
|
||||
m.platform_id as platformId,
|
||||
COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,
|
||||
m.avatar
|
||||
FROM member m
|
||||
WHERE COALESCE(m.account_name, '') != '系统消息'
|
||||
ORDER BY (SELECT COUNT(*) FROM message WHERE sender_id = m.id) DESC`
|
||||
)
|
||||
.all() as unknown as Array<{ platformId: string; name: string; avatar: string | null }>
|
||||
|
||||
if (members.length === 0) return null
|
||||
|
||||
if (ownerId) {
|
||||
const other = members.find((m) => m.platformId !== ownerId)
|
||||
if (other?.avatar) return other.avatar
|
||||
}
|
||||
|
||||
const sameName = members.find((m) => m.name === sessionName)
|
||||
if (sameName?.avatar) return sameName.avatar
|
||||
|
||||
const firstWithAvatar = members.find((m) => m.avatar)
|
||||
return firstWithAvatar?.avatar || null
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { completeSimple, type Model as PiModel, type TextContent as PiTextContent } from '@mariozechner/pi-ai'
|
||||
import type { CompressionLlmAdapter } from './types'
|
||||
|
||||
export interface CreateCompressionLlmAdapterOptions {
|
||||
piModel: PiModel
|
||||
apiKey: string
|
||||
contextWindow?: number
|
||||
onCompressing?: () => void
|
||||
onError?: (error: unknown) => void
|
||||
}
|
||||
|
||||
const DEFAULT_CONTEXT_WINDOW = 128000
|
||||
|
||||
/**
|
||||
* Create a CompressionLlmAdapter from a PiModel and API key.
|
||||
* Shared factory used by both Electron and Server.
|
||||
*/
|
||||
export function createCompressionLlmAdapter(options: CreateCompressionLlmAdapterOptions): CompressionLlmAdapter {
|
||||
const { piModel, apiKey, onCompressing, onError } = options
|
||||
const contextWindow = options.contextWindow ?? piModel.contextWindow ?? DEFAULT_CONTEXT_WINDOW
|
||||
|
||||
return {
|
||||
contextWindow,
|
||||
compress: async (prompt: string, maxTokens: number) => {
|
||||
onCompressing?.()
|
||||
try {
|
||||
const result = await completeSimple(
|
||||
piModel,
|
||||
{
|
||||
systemPrompt: undefined,
|
||||
messages: [{ role: 'user', content: [{ type: 'text', text: prompt }], timestamp: Date.now() }] as any,
|
||||
},
|
||||
{ apiKey, maxTokens }
|
||||
)
|
||||
const text = result.content
|
||||
.filter((item): item is PiTextContent => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('')
|
||||
return text || null
|
||||
} catch (error) {
|
||||
onError?.(error)
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@
|
||||
|
||||
export type { CompressionConfig, CompressionResult, CompressionLogger, CompressionLlmAdapter } from './types'
|
||||
export { checkAndCompress, manualCompress } from './compressor'
|
||||
export { createCompressionLlmAdapter, type CreateCompressionLlmAdapterOptions } from './adapter-factory'
|
||||
@@ -55,7 +55,8 @@ export type { ActivateSkillToolOptions, ActivateSkillTool, ActivateSkillToolResu
|
||||
|
||||
// Compression
|
||||
export type { CompressionConfig, CompressionResult, CompressionLogger, CompressionLlmAdapter } from './compression'
|
||||
export { checkAndCompress, manualCompress } from './compression'
|
||||
export { checkAndCompress, manualCompress, createCompressionLlmAdapter } from './compression'
|
||||
export type { CreateCompressionLlmAdapterOptions } from './compression'
|
||||
|
||||
// Preprocessor
|
||||
export type {
|
||||
|
||||
@@ -96,7 +96,8 @@ export type {
|
||||
|
||||
// Compression
|
||||
export type { CompressionConfig, CompressionResult, CompressionLogger, CompressionLlmAdapter } from './ai'
|
||||
export { checkAndCompress, manualCompress } from './ai'
|
||||
export { checkAndCompress, manualCompress, createCompressionLlmAdapter } from './ai'
|
||||
export type { CreateCompressionLlmAdapterOptions } from './ai'
|
||||
|
||||
// SkillManager
|
||||
export { SkillManager } from './ai'
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
import {
|
||||
runAgentCore,
|
||||
completeSimple,
|
||||
checkAndCompress,
|
||||
buildSystemPrompt,
|
||||
createAiTranslate,
|
||||
createCompressionLlmAdapter,
|
||||
AgentEventHandler,
|
||||
formatAIError,
|
||||
type AgentStreamChunk,
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
type SimpleHistoryMessage,
|
||||
type AIConversationManager,
|
||||
type CompressionConfig,
|
||||
type CompressionLlmAdapter,
|
||||
type PiTextContent,
|
||||
type AgentTool,
|
||||
type DataSnapshot,
|
||||
type OwnerInfo,
|
||||
@@ -103,29 +101,11 @@ export async function runServerAgent(options: RunAgentOptions): Promise<void> {
|
||||
})
|
||||
|
||||
if (compressionConfig?.enabled) {
|
||||
const llmAdapter: CompressionLlmAdapter = {
|
||||
contextWindow: piModel.contextWindow ?? 128000,
|
||||
compress: async (prompt: string, maxTokens: number) => {
|
||||
handler.emitStatus('compressing', [])
|
||||
try {
|
||||
const result = await completeSimple(
|
||||
piModel,
|
||||
{
|
||||
systemPrompt: undefined,
|
||||
messages: [{ role: 'user', content: [{ type: 'text', text: prompt }], timestamp: Date.now() }] as any,
|
||||
},
|
||||
{ apiKey: llmConfig.apiKey, maxTokens }
|
||||
)
|
||||
const text = result.content
|
||||
.filter((item): item is PiTextContent => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('')
|
||||
return text || null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
const llmAdapter = createCompressionLlmAdapter({
|
||||
piModel,
|
||||
apiKey: llmConfig.apiKey,
|
||||
onCompressing: () => handler.emitStatus('compressing', []),
|
||||
})
|
||||
const compressionResult = await checkAndCompress(
|
||||
conversationId,
|
||||
compressionConfig,
|
||||
|
||||
Reference in new issue
Block a user