mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-02 22:41:26 +08:00
✨ feat(ai): 补齐 Cursor 与 CodeBuddy 会话态聊天链路
- 新增 SessionChatProvider 接口,补齐非流式对话的会话态复用能力 - 为 Cursor Agent 和 CodeBuddy CLI 同步实现流式与非流式会话续接及状态持久化 - CustomProvider 补充会话态透传,统一 custom provider 的会话复用行为 - Service 新增 AIChatSendInSession,聊天主链路非流式回退改走带 session 的发送接口 - 保留原 AIChatSend 无状态语义,避免标题生成和记忆压缩污染主会话上下文 - 补充前后端定向测试,覆盖会话恢复、续接发送和前端回退分流
This commit is contained in:
@@ -38,8 +38,8 @@ describe('aiChatPayloadDispatch', () => {
|
||||
expect(setSending).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('appends a non-stream assistant message when only AIChatSend is available', async () => {
|
||||
const AIChatSend = vi.fn().mockResolvedValue({
|
||||
it('appends a non-stream assistant message when session-aware send is available', async () => {
|
||||
const AIChatSendInSession = vi.fn().mockResolvedValue({
|
||||
success: true,
|
||||
content: 'done',
|
||||
reasoning_content: 'thinking',
|
||||
@@ -51,7 +51,7 @@ describe('aiChatPayloadDispatch', () => {
|
||||
(globalThis as any).window = {
|
||||
go: {
|
||||
aiservice: {
|
||||
Service: { AIChatSend },
|
||||
Service: { AIChatSendInSession },
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -67,6 +67,7 @@ describe('aiChatPayloadDispatch', () => {
|
||||
});
|
||||
|
||||
expect(result).toBe('send');
|
||||
expect(AIChatSendInSession).toHaveBeenCalledWith('session-1', [{ role: 'user', content: 'hello' }], []);
|
||||
expect(addAIChatMessage).toHaveBeenCalledWith('session-1', expect.objectContaining({
|
||||
id: 'msg-send',
|
||||
role: 'assistant',
|
||||
@@ -79,7 +80,7 @@ describe('aiChatPayloadDispatch', () => {
|
||||
});
|
||||
|
||||
it('settles the pending assistant message when falling back to non-stream send', async () => {
|
||||
const AIChatSend = vi.fn().mockResolvedValue({
|
||||
const AIChatSendInSession = vi.fn().mockResolvedValue({
|
||||
success: true,
|
||||
content: 'done',
|
||||
reasoning_content: 'thinking',
|
||||
@@ -91,7 +92,7 @@ describe('aiChatPayloadDispatch', () => {
|
||||
(globalThis as any).window = {
|
||||
go: {
|
||||
aiservice: {
|
||||
Service: { AIChatSend },
|
||||
Service: { AIChatSendInSession },
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -108,6 +109,7 @@ describe('aiChatPayloadDispatch', () => {
|
||||
});
|
||||
|
||||
expect(result).toBe('send');
|
||||
expect(AIChatSendInSession).toHaveBeenCalledWith('session-1', [{ role: 'user', content: 'hello' }], []);
|
||||
expect(addAIChatMessage).not.toHaveBeenCalled();
|
||||
expect(updateAIChatMessage).toHaveBeenCalledWith('session-1', 'assistant-connecting', expect.objectContaining({
|
||||
content: 'done',
|
||||
@@ -119,6 +121,44 @@ describe('aiChatPayloadDispatch', () => {
|
||||
expect(setSending).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('falls back to stateless AIChatSend when session-aware send is unavailable', async () => {
|
||||
const AIChatSend = vi.fn().mockResolvedValue({
|
||||
success: true,
|
||||
content: 'done',
|
||||
reasoning_content: 'thinking',
|
||||
});
|
||||
const addAIChatMessage = vi.fn();
|
||||
const setSending = vi.fn();
|
||||
|
||||
(globalThis as any).window = {
|
||||
go: {
|
||||
aiservice: {
|
||||
Service: { AIChatSend },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await dispatchAIChatPayload({
|
||||
sid: 'session-1',
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
tools: [],
|
||||
addAIChatMessage,
|
||||
setSending,
|
||||
nextMessageId: () => 'msg-send',
|
||||
});
|
||||
|
||||
expect(result).toBe('send');
|
||||
expect(AIChatSend).toHaveBeenCalledWith([{ role: 'user', content: 'hello' }], []);
|
||||
expect(addAIChatMessage).toHaveBeenCalledWith('session-1', expect.objectContaining({
|
||||
id: 'msg-send',
|
||||
role: 'assistant',
|
||||
content: 'done',
|
||||
thinking: 'thinking',
|
||||
reasoning_content: 'thinking',
|
||||
}));
|
||||
expect(setSending).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('emits the unavailable message when the AI service is missing', async () => {
|
||||
const addAIChatMessage = vi.fn();
|
||||
const setSending = vi.fn();
|
||||
|
||||
@@ -8,6 +8,7 @@ import { sanitizeErrorMsg } from '../../utils/aiChatRuntime';
|
||||
|
||||
interface AIChatService {
|
||||
AIChatStream?: (sid: string, messages: any[], tools: AIChatToolDefinition[]) => Promise<any>;
|
||||
AIChatSendInSession?: (sid: string, messages: any[], tools: AIChatToolDefinition[]) => Promise<any>;
|
||||
AIChatSend?: (messages: any[], tools: AIChatToolDefinition[]) => Promise<any>;
|
||||
}
|
||||
|
||||
@@ -92,8 +93,10 @@ export const dispatchAIChatPayload = async ({
|
||||
return 'stream';
|
||||
}
|
||||
|
||||
if (service?.AIChatSend) {
|
||||
const result = await service.AIChatSend(messages, tools);
|
||||
if (service?.AIChatSendInSession || service?.AIChatSend) {
|
||||
const result = service?.AIChatSendInSession
|
||||
? await service.AIChatSendInSession(sid, messages, tools)
|
||||
: await service!.AIChatSend!(messages, tools);
|
||||
const rawError = result?.error || '未知错误';
|
||||
const cleanError = sanitizeErrorMsg(rawError);
|
||||
|
||||
|
||||
2
frontend/wailsjs/go/aiservice/Service.d.ts
vendored
2
frontend/wailsjs/go/aiservice/Service.d.ts
vendored
@@ -8,6 +8,8 @@ export function AIChatCancel(arg1:string):Promise<void>;
|
||||
|
||||
export function AIChatSend(arg1:Array<ai.Message>,arg2:Array<ai.Tool>):Promise<Record<string, any>>;
|
||||
|
||||
export function AIChatSendInSession(arg1:string,arg2:Array<ai.Message>,arg3:Array<ai.Tool>):Promise<Record<string, any>>;
|
||||
|
||||
export function AIChatStream(arg1:string,arg2:Array<ai.Message>,arg3:Array<ai.Tool>):Promise<void>;
|
||||
|
||||
export function AICheckSQL(arg1:string):Promise<ai.SafetyResult>;
|
||||
|
||||
@@ -14,6 +14,10 @@ export function AIChatSend(arg1, arg2) {
|
||||
return window['go']['aiservice']['Service']['AIChatSend'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function AIChatSendInSession(arg1, arg2, arg3) {
|
||||
return window['go']['aiservice']['Service']['AIChatSendInSession'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function AIChatStream(arg1, arg2, arg3) {
|
||||
return window['go']['aiservice']['Service']['AIChatStream'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user