refactor: 清理 parser worker rag merger 的历史类型问题

This commit is contained in:
digua
2026-03-25 22:43:12 +08:00
parent b6fdc3887e
commit 7eaba396ec
29 changed files with 264 additions and 103 deletions
+5 -4
View File
@@ -5,7 +5,7 @@
import { getActiveConfig, buildPiModel } from '../llm'
import { getAllTools, createActivateSkillTool } from '../tools'
import type { ToolContext, OwnerInfo } from '../tools/types'
import type { ToolContext } from '../tools/types'
import { getHistoryForAgent } from '../conversations'
import { aiLogger, isDebugMode } from '../logger'
import { t as i18nT } from '../../i18n'
@@ -16,7 +16,7 @@ import {
type Usage as PiUsage,
} from '@mariozechner/pi-ai'
import type { AgentConfig, AgentStreamChunk, AgentResult, TokenUsage, SkillContext } from './types'
import type { AgentConfig, AgentStreamChunk, AgentResult, SkillContext } from './types'
import type { AssistantConfig } from '../assistant/types'
import { buildSystemPrompt } from './prompt-builder'
import { extractThinkingContent, stripToolCallTags } from './content-parser'
@@ -25,7 +25,7 @@ import { AgentEventHandler } from './event-handler'
type SimpleHistoryMessage = { role: 'user' | 'assistant'; content: string }
// Re-export types for external consumers
export type { AgentConfig, AgentStreamChunk, AgentResult, TokenUsage, AgentRuntimeStatus } from './types'
export type { AgentConfig, AgentStreamChunk, AgentResult, TokenUsage, AgentRuntimeStatus, SkillContext } from './types'
/**
* Agent 执行器类
@@ -120,7 +120,8 @@ export class Agent {
if (newMessages.length > 0) {
const parts: string[] = []
for (const m of newMessages) {
const msg = m as Record<string, unknown>
// pi-ai 的 Message 联合类型较严格,这里仅用于调试日志读取动态字段。
const msg = m as unknown as Record<string, unknown>
parts.push(`--- ${msg.role} ---`)
const content = msg.content as
| Array<{ type: string; text?: string; name?: string; arguments?: unknown }>
+19 -7
View File
@@ -5,8 +5,9 @@
* 通过 pluginQuery 执行参数化 SQL 并格式化结果。
*/
import { Type, type TObject, type TProperties } from '@mariozechner/pi-ai'
import type { AgentTool } from '@mariozechner/pi-agent-core'
import { Type } from '@mariozechner/pi-ai'
import type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core'
import type { TSchema } from '@sinclair/typebox'
import type { ToolContext } from '../tools/types'
import type { CustomSqlToolDef, JsonSchemaObject } from './types'
import * as workerManager from '../../worker/workerManager'
@@ -17,8 +18,8 @@ import { t as i18nT } from '../../i18n'
*
* 仅覆盖 SQL 工具参数定义的常见类型(string / number / integer / boolean)。
*/
export function jsonSchemaToTypeBox(schema: JsonSchemaObject): TObject<TProperties> {
const props: TProperties = {}
export function jsonSchemaToTypeBox(schema: JsonSchemaObject) {
const props: Record<string, TSchema> = {}
for (const [key, prop] of Object.entries(schema.properties)) {
const isRequired = schema.required?.includes(key) ?? false
@@ -77,13 +78,21 @@ export function createSqlTool(def: CustomSqlToolDef, context: ToolContext): Agen
label: def.name,
description: def.description,
parameters: schema,
execute: async (_toolCallId: string, params: Record<string, unknown>) => {
execute: async (
_toolCallId: string,
params: Record<string, unknown>,
_signal?: AbortSignal,
_onUpdate?: unknown
): Promise<AgentToolResult<{ rows: Record<string, unknown>[]; rowCount: number }>> => {
const rows = await workerManager.pluginQuery(context.sessionId, def.execution.query, params)
const fallback = resolveTemplate(def.name, 'fallback', def.execution.fallback)
if (!rows || rows.length === 0) {
return { content: [{ type: 'text' as const, text: fallback }] }
return {
content: [{ type: 'text' as const, text: fallback }],
details: { rows: [], rowCount: 0 },
}
}
const rowTemplate = resolveTemplate(def.name, 'rowTemplate', def.execution.rowTemplate)
@@ -102,7 +111,10 @@ export function createSqlTool(def: CustomSqlToolDef, context: ToolContext): Agen
lines.push(formatRow(rowTemplate, row as Record<string, unknown>))
}
return { content: [{ type: 'text' as const, text: lines.join('\n') }] }
return {
content: [{ type: 'text' as const, text: lines.join('\n') }],
details: { rows, rowCount: rows.length },
}
},
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
import * as fs from 'fs'
import * as path from 'path'
import { getLogsDir, ensureDir } from '../paths'
import { getLogsDir } from '../paths'
let debugMode = false
+2 -2
View File
@@ -308,7 +308,7 @@ export function getSessionChunks(dbPath: string, options: ChunkingOptions = {}):
return chunks
} catch (error) {
logger.error('[Chunking] Failed to get session chunks:', error)
logger.error('Chunking', 'Failed to get session chunks', error)
return []
} finally {
if (db) {
@@ -372,7 +372,7 @@ export function getSessionChunk(dbPath: string, sessionId: number): Chunk | null
},
}
} catch (error) {
logger.error('[Chunking] Failed to get single session chunk:', error)
logger.error('Chunking', 'Failed to get single session chunk', error)
return null
} finally {
if (db) {
+3 -2
View File
@@ -62,7 +62,7 @@ async function rewriteQuery(query: string, abortSignal?: AbortSignal): Promise<s
return rewritten || query
} catch (error) {
logger.warn('[Semantic Pipeline] Query rewrite failed, using original query:', error)
logger.warn('Semantic Pipeline', 'Query rewrite failed, using original query', error)
return query
}
}
@@ -197,7 +197,8 @@ export async function executeSemanticPipeline(options: SemanticPipelineOptions):
chunkVectors.set(chunk.id, vector)
if (vectorStore) {
await vectorStore.add(chunk.id, vector, chunk.metadata as Record<string, unknown>)
// ChunkMetadata 是结构化对象,这里仅在向量存储层按通用元数据字典处理。
await vectorStore.add(chunk.id, vector, chunk.metadata as unknown as Record<string, unknown>)
}
}
}
+4 -4
View File
@@ -32,7 +32,7 @@ export async function getVectorStore(): Promise<IVectorStore | null> {
activeStore = await createVectorStore(config.vectorStore)
return activeStore
} catch (error) {
logger.error('[Store Manager] Failed to create store:', error)
logger.error('Store Manager', 'Failed to create store', error)
return null
}
}
@@ -44,13 +44,13 @@ async function createVectorStore(config: VectorStoreConfig): Promise<IVectorStor
switch (config.type) {
case 'memory': {
const capacity = config.memoryCacheSize || 10000
logger.info(`[Store Manager] Using memory store, capacity: ${capacity}`)
logger.info('Store Manager', `Using memory store, capacity: ${capacity}`)
return new MemoryVectorStore(capacity)
}
case 'sqlite': {
const dbPath = config.dbPath || path.join(getVectorStoreDir(), 'embeddings.db')
logger.info(`[Store Manager] Using SQLite store: ${dbPath}`)
logger.info('Store Manager', `Using SQLite store: ${dbPath}`)
return new SQLiteVectorStore(dbPath)
}
@@ -72,7 +72,7 @@ export async function resetVectorStore(): Promise<void> {
if (activeStore) {
await activeStore.close()
activeStore = null
logger.info('[Store Manager] Store reset')
logger.info('Store Manager', 'Store reset')
}
}
+2 -2
View File
@@ -224,7 +224,7 @@ export class MemoryVectorStore implements IVectorStore {
this.cache.clear()
this.head = null
this.tail = null
logger.info('[Memory Store] All vectors cleared')
logger.info('Memory Store', 'All vectors cleared')
}
/**
@@ -258,6 +258,6 @@ export class MemoryVectorStore implements IVectorStore {
*/
async close(): Promise<void> {
// 内存存储无需关闭操作
logger.info('[Memory Store] Closed')
logger.info('Memory Store', 'Closed')
}
}
+6 -5
View File
@@ -79,7 +79,7 @@ export class SQLiteVectorStore implements IVectorStore {
// 索引可能已存在
}
logger.info(`[SQLite Store] Initialized: ${this.dbPath}`)
logger.info('SQLite Store', `Initialized: ${this.dbPath}`)
}
/**
@@ -107,8 +107,9 @@ export class SQLiteVectorStore implements IVectorStore {
VALUES (?, ?, ?, ?)
`)
const insertMany = this.db.transaction((items: typeof items) => {
for (const item of items) {
type VectorBatchItem = { id: string; vector: number[]; metadata?: Record<string, unknown> }
const insertMany = this.db.transaction((batchItems: VectorBatchItem[]) => {
for (const item of batchItems) {
const buffer = vectorToBuffer(item.vector)
insert.run(item.id, buffer, item.vector.length, item.metadata ? JSON.stringify(item.metadata) : null)
}
@@ -180,7 +181,7 @@ export class SQLiteVectorStore implements IVectorStore {
*/
async clear(): Promise<void> {
this.db.exec('DELETE FROM vectors')
logger.info('[SQLite Store] All vectors cleared')
logger.info('SQLite Store', 'All vectors cleared')
}
/**
@@ -214,6 +215,6 @@ export class SQLiteVectorStore implements IVectorStore {
*/
async close(): Promise<void> {
this.db.close()
logger.info('[SQLite Store] Closed')
logger.info('SQLite Store', 'Closed')
}
}
+15 -6
View File
@@ -142,7 +142,7 @@ function wrapWithPreprocessing(tool: AgentTool<any>, context: ToolContext): Agen
const originalExecute = tool.execute
return {
...tool,
execute: async (toolCallId: string, params: any) => {
execute: async (toolCallId: string, params: any, _signal?: AbortSignal, _onUpdate?: unknown) => {
const result = await originalExecute(toolCallId, params)
const details = result.details as Record<string, unknown> | undefined
@@ -160,8 +160,8 @@ function wrapWithPreprocessing(tool: AgentTool<any>, context: ToolContext): Agen
const formatted = processed.map((m) => formatMessageCompact(m, context.locale))
const finalDetails = { ...details, messages: formatted, returned: processed.length }
delete finalDetails.rawMessages
const { rawMessages: _rawMessages, ...restDetails } = details
const finalDetails = { ...restDetails, messages: formatted, returned: processed.length }
let textContent = formatToolResultAsText(finalDetails)
if (nameMapLine) {
@@ -242,6 +242,7 @@ export function createActivateSkillTool(
return {
name: 'activate_skill',
label: 'activate_skill',
description: isZh
? '激活一个分析技能,获取该技能的详细执行指导'
: 'Activate an analysis skill and get its detailed execution instructions',
@@ -255,11 +256,12 @@ export function createActivateSkillTool(
},
required: ['skill_id'],
},
execute: async (_toolCallId: string, params: { skill_id: string }) => {
execute: async (_toolCallId: string, params: { skill_id: string }, _signal?: AbortSignal, _onUpdate?: unknown) => {
const skill: SkillDef | null = getSkillConfig(params.skill_id)
if (!skill) {
return {
content: [{ type: 'text' as const, text: isZh ? '技能不存在' : 'Skill not found' }],
details: { skillId: params.skill_id, found: false },
}
}
@@ -267,7 +269,10 @@ export function createActivateSkillTool(
const scopeMsg = isZh
? `该技能仅适用于${skill.chatScope === 'group' ? '群聊' : '私聊'}场景`
: `This skill is only applicable to ${skill.chatScope === 'group' ? 'group chat' : 'private chat'} scenarios`
return { content: [{ type: 'text' as const, text: scopeMsg }] }
return {
content: [{ type: 'text' as const, text: scopeMsg }],
details: { skillId: params.skill_id, found: true, applicable: false },
}
}
if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
@@ -276,7 +281,10 @@ export function createActivateSkillTool(
const msg = isZh
? `当前助手缺少该技能所需的工具:${missing.join(', ')}`
: `Current assistant lacks tools required by this skill: ${missing.join(', ')}`
return { content: [{ type: 'text' as const, text: msg }] }
return {
content: [{ type: 'text' as const, text: msg }],
details: { skillId: params.skill_id, found: true, applicable: false, missingTools: missing },
}
}
}
@@ -286,6 +294,7 @@ export function createActivateSkillTool(
return {
content: [{ type: 'text' as const, text: `${skill.prompt}${actionPrompt}` }],
details: { skillId: params.skill_id, found: true, applicable: true },
}
},
}
+3 -1
View File
@@ -26,7 +26,9 @@ export const i18nTexts = {
},
}
export function t(key: keyof typeof i18nTexts, locale?: string): string | string[] {
type TextEntryKey = Exclude<keyof typeof i18nTexts, 'dailySummary'>
export function t(key: TextEntryKey, locale?: string): string | string[] {
const text = i18nTexts[key]
if (typeof text === 'object' && 'zh' in text && 'en' in text) {
return isChineseLocale(locale) ? text.zh : text.en