mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-25 08:00:16 +08:00
refactor: 清理 parser worker rag merger 的历史类型问题
This commit is contained in:
@@ -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 }>
|
||||
|
||||
@@ -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 },
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user