mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-04 03:01:42 +08:00
fix: 修复查询工具消息引用与参数校验
This commit is contained in:
@@ -47,6 +47,29 @@ describe('LocalQueryApiService', () => {
|
||||
expect(result.messages?.[0]).not.toHaveProperty('text')
|
||||
})
|
||||
|
||||
it('round-trips opaque refs from messages, search, and overview through context', async () => {
|
||||
fixture.contacts.splice(1)
|
||||
const queried = await service.messages({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, order: 'asc', limit: 1 })
|
||||
const queriedRef = queried.messages?.[0]?.messageRef
|
||||
expect(queriedRef).toEqual(expect.any(String))
|
||||
await expect(service.context({ messageRef: queriedRef!, before: 0, after: 0 })).resolves.toMatchObject({
|
||||
status: 'completed',
|
||||
anchor: { messageRef: queriedRef }
|
||||
})
|
||||
|
||||
knowledge.search.mockResolvedValueOnce({ state: 'ready', evidence: [{ conversationId: 'md5-bobo', messageId: 'local:m1', timestamp: 1, sender: 'BOBO', sourceKind: 'text', text: '你好' }], conversationRetrieval: { totalMessages: 2, chunkCount: 1, complete: true }, voiceCoverage: undefined })
|
||||
const searched = await service.search({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, query: '你好' })
|
||||
const searchedRef = searched.evidence?.[0]?.messageRef
|
||||
expect(searchedRef).toBe(queriedRef)
|
||||
await expect(service.context({ messageRef: searchedRef!, before: 0, after: 0 })).resolves.toMatchObject({ status: 'completed' })
|
||||
|
||||
knowledge.search.mockResolvedValueOnce({ state: 'ready', evidence: [{ conversationId: 'md5-bobo', messageId: 'local:m1', timestamp: 1, sender: 'BOBO', sourceKind: 'text', text: '你好' }], conversationRetrieval: { totalMessages: 2, chunkCount: 1, complete: true }, voiceCoverage: undefined })
|
||||
const overview = await service.overview({ target: { query: 'BOBO' }, timeRange: { kind: 'all' } })
|
||||
const overviewRef = overview.evidence?.[0]?.messageRef
|
||||
expect(overviewRef).toBe(queriedRef)
|
||||
await expect(service.context({ messageRef: overviewRef!, before: 0, after: 0 })).resolves.toMatchObject({ status: 'completed' })
|
||||
})
|
||||
|
||||
it('sorts overview evidence while preserving the selected set and exposes sampling', async () => {
|
||||
fixture.contacts.splice(1)
|
||||
knowledge.search.mockResolvedValueOnce({ state: 'ready', evidence: [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { QueryAgentPocService, type QueryAgentProvider } from '../../src/main/services/query-agent-poc-service'
|
||||
import { QueryAgentPocService, type QueryAgentProvider, validateToolArguments } from '../../src/main/services/query-agent-poc-service'
|
||||
|
||||
function provider(responses: Array<Awaited<ReturnType<QueryAgentProvider['chatWithTools']>>>, configured = true): QueryAgentProvider {
|
||||
return {
|
||||
@@ -28,7 +28,7 @@ describe('QueryAgentPocService', () => {
|
||||
const responses = Array.from({ length: 6 }, () => ({ success: true, toolCalls: [{ id: 'x', name: 'unknown', arguments: '{}' }] }))
|
||||
const result = await new QueryAgentPocService(provider(responses), execute).run('test')
|
||||
expect(result.toolCallCount).toBe(5)
|
||||
expect(result.traces.every((trace) => trace.status === 'invalid_request')).toBe(true)
|
||||
expect(result.traces.every((trace) => trace.status === 'invalid_tool_arguments')).toBe(true)
|
||||
expect(result.error).toContain('最大工具调用次数')
|
||||
expect(execute).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -39,4 +39,34 @@ describe('QueryAgentPocService', () => {
|
||||
expect(result.error).toContain('尚未配置')
|
||||
expect(configuredProvider.chatWithTools).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('accepts schema-valid variants and rejects invalid arguments before executing a tool', async () => {
|
||||
const execute = vi.fn(async () => ({ status: 'completed', returnedCount: 0 }))
|
||||
const valid = await new QueryAgentPocService(provider([
|
||||
{ success: true, toolCalls: [{ id: 'valid', name: 'search_messages', arguments: JSON.stringify({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, query: '答应', variants: ['承诺', '保证', '说好', '一定'] }) }] },
|
||||
{ success: true, data: 'done' }
|
||||
]), execute).run('test')
|
||||
expect(valid.traces[0]).toMatchObject({ status: 'completed', toolName: 'search_messages' })
|
||||
expect(execute).toHaveBeenCalledTimes(1)
|
||||
|
||||
execute.mockClear()
|
||||
const invalid = await new QueryAgentPocService(provider([
|
||||
{ success: true, toolCalls: [{ id: 'invalid', name: 'search_messages', arguments: JSON.stringify({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, query: '答应', variants: ['1', '2', '3', '4', '5'] }) }] },
|
||||
{ success: true, data: '修正后完成' }
|
||||
]), execute).run('test')
|
||||
expect(invalid.traces[0]).toMatchObject({ status: 'invalid_tool_arguments' })
|
||||
expect(invalid.traces[0].resultCount).toBeUndefined()
|
||||
expect(execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('enforces generic nested tool schema constraints', () => {
|
||||
const base = { target: { query: 'BOBO' }, timeRange: { kind: 'all' }, query: 'x' }
|
||||
expect(validateToolArguments('search_messages', { ...base, variants: ['1', '2', '3', '4'] }).error).toBeUndefined()
|
||||
expect(validateToolArguments('search_messages', { ...base, variants: ['1', '2', '3', '4', '5'] }).error).toMatchObject({ field: 'variants', constraint: 'maxItems', expected: 4, actual: 5 })
|
||||
expect(validateToolArguments('search_messages', { ...base, extra: true }).error).toMatchObject({ field: 'extra', constraint: 'additionalProperties' })
|
||||
expect(validateToolArguments('query_messages', { target: { query: 'BOBO' }, timeRange: { kind: 'all' }, direction: 'sideways' }).error).toMatchObject({ field: 'direction', constraint: 'enum' })
|
||||
expect(validateToolArguments('query_messages', { target: { query: '' }, timeRange: { kind: 'all' } }).error).toMatchObject({ field: 'target.query', constraint: 'minLength' })
|
||||
expect(validateToolArguments('query_messages', { target: { query: 'BOBO' }, timeRange: { kind: 'all' }, limit: 0 }).error).toMatchObject({ field: 'limit', constraint: 'minimum' })
|
||||
expect(validateToolArguments('message_context', { messageRef: 'opaque', before: 51 }).error).toMatchObject({ field: 'before', constraint: 'maximum' })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user