feat(tools): implement CoreDataProvider and WorkerDataProvider

Add ToolDataProvider implementations for both platforms:
- CoreDataProvider: wraps @openchatlab/core sync queries for Server/MCP
- WorkerDataProvider: wraps workerManager async IPC for Electron
This commit is contained in:
digua committed 2026-05-15 19:05:00 +08:00
1 parent 21a0ce36f4
commit b1df81b064
3 files changed
+185

No files matched your search

@@ -0,0 +1,94 @@
/**
* WorkerDataProvider
*
* 基于 workerManager 的 ToolDataProvider 实现。
* 通过 Worker IPC 异步访问 SQLite,供 Electron Agent 使用。
*/
import * as workerManager from '../../worker/workerManager'
import type {
ToolDataProvider,
SearchMessagesResult,
MemberStatItem,
SchemaTableInfo,
TimeFilter,
} from '@openchatlab/tools'
export class WorkerDataProvider implements ToolDataProvider {
constructor(private sessionId: string) {}
async searchMessages(
keywords: string[],
options?: { timeFilter?: TimeFilter; limit?: number; senderId?: number }
): Promise<SearchMessagesResult> {
const result = await workerManager.searchMessages(
this.sessionId,
keywords,
options?.timeFilter,
options?.limit ?? 50,
0,
options?.senderId
)
return {
messages: result.messages.map((m) => ({
id: m.id,
senderName: m.senderName,
senderPlatformId: m.senderPlatformId,
content: m.content,
timestamp: m.timestamp,
})),
total: result.total,
}
}
async getRecentMessages(options?: { timeFilter?: TimeFilter; limit?: number }): Promise<SearchMessagesResult> {
const result = await workerManager.getRecentMessages(this.sessionId, options?.timeFilter, options?.limit ?? 50)
return {
messages: result.messages.map((m) => ({
id: m.id,
senderName: m.senderName,
senderPlatformId: m.senderPlatformId,
content: m.content,
timestamp: m.timestamp,
})),
total: result.total,
}
}
async getMemberStats(options?: { timeFilter?: TimeFilter; top?: number }): Promise<MemberStatItem[]> {
const top = options?.top ?? 20
const members = await workerManager.getMemberActivity(this.sessionId, options?.timeFilter)
return members.slice(0, top).map((m: any) => ({
name: m.name,
messageCount: m.messageCount,
percentage: m.percentage,
}))
}
async getTimeStats(type: 'hourly' | 'weekday' | 'daily', options?: { timeFilter?: TimeFilter }): Promise<unknown[]> {
const filter = options?.timeFilter
switch (type) {
case 'weekday':
return workerManager.getWeekdayActivity(this.sessionId, filter)
case 'daily':
return workerManager.getDailyActivity(this.sessionId, filter)
case 'hourly':
default:
return workerManager.getHourlyActivity(this.sessionId, filter)
}
}
async executeSql(sql: string): Promise<unknown> {
return workerManager.executeRawSQL(this.sessionId, sql)
}
async getSchema(): Promise<SchemaTableInfo[]> {
const tables = await workerManager.getSchema(this.sessionId)
return tables.map((t) => ({
name: t.name,
sql: t.columns.map((c) => `${c.name} ${c.type}${c.pk ? ' PK' : ''}${c.notnull ? ' NOT NULL' : ''}`).join(', '),
}))
}
}
+1
View File
@@ -6,6 +6,7 @@
*/
export { TOOL_REGISTRY, getToolByName } from './registry'
export { CoreDataProvider } from './providers/core-data-provider'
export type {
ToolDefinition,
ToolExecutionContext,
@@ -0,0 +1,90 @@
/**
* CoreDataProvider
*
* 基于 @openchatlab/core 同步查询函数的 ToolDataProvider 实现。
* 供 Server / MCP 使用,通过 DatabaseAdapter 直接访问 SQLite。
*/
import type { DatabaseAdapter } from '@openchatlab/core'
import {
searchMessagesLike,
getRecentMessages,
getMemberActivity,
getHourlyActivity,
getWeekdayActivity,
getDailyActivity,
executeReadonlySql,
getDatabaseSchema,
} from '@openchatlab/core'
import type { ToolDataProvider, SearchMessagesResult, MemberStatItem, SchemaTableInfo, TimeFilter } from '../types'
export class CoreDataProvider implements ToolDataProvider {
constructor(private db: DatabaseAdapter) {}
async searchMessages(
keywords: string[],
options?: { timeFilter?: TimeFilter; limit?: number; senderId?: number }
): Promise<SearchMessagesResult> {
const keyword = keywords.join(' ')
const result = searchMessagesLike(this.db, keyword, { limit: options?.limit ?? 50 })
return {
messages: result.messages.map((m) => ({
id: m.id,
senderId: m.senderId,
senderName: m.senderName,
senderPlatformId: m.senderPlatformId,
content: m.content,
timestamp: m.timestamp,
})),
total: result.total ?? result.messages.length,
}
}
async getRecentMessages(options?: { timeFilter?: TimeFilter; limit?: number }): Promise<SearchMessagesResult> {
const messages = getRecentMessages(this.db, { limit: options?.limit ?? 50 })
return {
messages: messages.map((m) => ({
id: m.id,
senderId: m.senderId,
senderName: m.senderName,
senderPlatformId: m.senderPlatformId,
content: m.content,
timestamp: m.timestamp,
})),
total: messages.length,
}
}
async getMemberStats(options?: { timeFilter?: TimeFilter; top?: number }): Promise<MemberStatItem[]> {
const top = options?.top ?? 20
const members = getMemberActivity(this.db, options?.timeFilter)
return members.slice(0, top).map((m) => ({
name: m.name,
messageCount: m.messageCount,
percentage: m.percentage,
}))
}
async getTimeStats(type: 'hourly' | 'weekday' | 'daily', options?: { timeFilter?: TimeFilter }): Promise<unknown[]> {
const filter = options?.timeFilter
switch (type) {
case 'weekday':
return getWeekdayActivity(this.db, filter)
case 'daily':
return getDailyActivity(this.db, filter)
case 'hourly':
default:
return getHourlyActivity(this.db, filter)
}
}
async executeSql(sql: string): Promise<unknown> {
return executeReadonlySql(this.db, sql)
}
async getSchema(): Promise<SchemaTableInfo[]> {
return getDatabaseSchema(this.db)
}
}