feat: implement browser open functionality and related tests
This commit is contained in:
97
electron/gateway/browser-shortcut.ts
Normal file
97
electron/gateway/browser-shortcut.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import logManager from '@electron/service/logger';
|
||||
import { extractBrowserOpenIntent, openUrlInBrowser } from '@electron/service/browser-open-service';
|
||||
import { appendTranscriptLine } from '@electron/utils/token-usage-writer';
|
||||
import type { RawMessage } from '@runtime/shared/chat-model';
|
||||
import { sessionStore } from './session-store';
|
||||
import type { GatewayEvent } from './types';
|
||||
|
||||
function buildBrowserOpenResponseText(result: { pageUrl: string; title?: string }): string {
|
||||
const suffix = result.title ? `(${result.title})` : '';
|
||||
return `已为你打开 ${result.pageUrl}${suffix}`;
|
||||
}
|
||||
|
||||
function buildBrowserOpenErrorText(error: unknown): string {
|
||||
return `打开失败:${error instanceof Error ? error.message : String(error)}`;
|
||||
}
|
||||
|
||||
async function processBrowserOpen(
|
||||
sessionKey: string,
|
||||
runId: string,
|
||||
url: string,
|
||||
signal: AbortSignal,
|
||||
broadcast: (event: GatewayEvent) => void,
|
||||
) {
|
||||
let assistantText = '';
|
||||
|
||||
try {
|
||||
const result = await openUrlInBrowser(url, { signal });
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
}
|
||||
assistantText = buildBrowserOpenResponseText(result);
|
||||
} catch (error) {
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
}
|
||||
assistantText = buildBrowserOpenErrorText(error);
|
||||
}
|
||||
|
||||
sessionStore.clearActiveRun(sessionKey);
|
||||
|
||||
const finalMessage: RawMessage = {
|
||||
role: 'assistant',
|
||||
content: assistantText,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
sessionStore.appendMessage(sessionKey, finalMessage);
|
||||
|
||||
appendTranscriptLine(sessionKey, {
|
||||
type: 'message',
|
||||
timestamp: new Date().toISOString(),
|
||||
message: {
|
||||
role: 'assistant',
|
||||
content: assistantText,
|
||||
tool: 'browser.open_url',
|
||||
},
|
||||
});
|
||||
|
||||
broadcast({
|
||||
type: 'chat:final',
|
||||
sessionKey,
|
||||
runId,
|
||||
message: finalMessage,
|
||||
});
|
||||
}
|
||||
|
||||
export function maybeHandleBrowserOpenMessage(
|
||||
sessionKey: string,
|
||||
runId: string,
|
||||
message: RawMessage,
|
||||
broadcast: (event: GatewayEvent) => void,
|
||||
): boolean {
|
||||
const browserIntent = typeof message.content === 'string'
|
||||
? extractBrowserOpenIntent(message.content)
|
||||
: null;
|
||||
|
||||
if (!browserIntent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
sessionStore.setActiveRun(sessionKey, runId, abortController);
|
||||
|
||||
processBrowserOpen(sessionKey, runId, browserIntent.url, abortController.signal, broadcast).catch(
|
||||
(error) => {
|
||||
logManager.error('Unexpected error in processBrowserOpen:', error);
|
||||
sessionStore.clearActiveRun(sessionKey);
|
||||
broadcast({
|
||||
type: 'chat:error',
|
||||
sessionKey,
|
||||
runId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { createProvider } from '@electron/providers';
|
||||
import type { BaseProvider } from '@electron/providers/BaseProvider';
|
||||
import { providerApiService } from '@electron/service/provider-api-service';
|
||||
@@ -8,6 +8,7 @@ import type { RawMessage } from '@runtime/shared/chat-model';
|
||||
import { sessionStore } from '../session-store';
|
||||
import type { GatewayEvent, GatewayRpcParams, GatewayRpcReturns } from '../types';
|
||||
import { appendTranscriptLine } from '@electron/utils/token-usage-writer';
|
||||
import { maybeHandleBrowserOpenMessage } from '../browser-shortcut';
|
||||
|
||||
export interface GatewayChatMessage {
|
||||
role: 'system' | 'user' | 'assistant' | 'tool';
|
||||
@@ -131,6 +132,10 @@ export function handleChatSend(
|
||||
},
|
||||
});
|
||||
|
||||
if (maybeHandleBrowserOpenMessage(sessionKey, runId, userMessage, broadcast)) {
|
||||
return { runId };
|
||||
}
|
||||
|
||||
// 2. Resolve provider account
|
||||
const accountId = options?.providerAccountId || providerApiService.getDefault().accountId;
|
||||
if (!accountId) {
|
||||
|
||||
Reference in New Issue
Block a user