Skip to content

feat(agent-tars): support "view logs" for trouble shooting #269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/agent-tars/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"openai": "^4.86.2",
"dotenv": "16.4.7",
"@agent-infra/shared": "workspace:*",
"@agent-infra/logger": "workspace:*",
"@agent-infra/mcp-client": "workspace:*",
"@agent-infra/search": "workspace:*",
"ws": "8.18.1",
Expand Down
12 changes: 10 additions & 2 deletions apps/agent-tars/src/main/customTools/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
import { MCPToolResult } from '@main/type';
import { tavily as tavilyCore } from '@tavily/core';
import { SettingStore } from '@main/store/setting';
import { logger } from '@main/utils/logger';
import { maskSensitiveData } from '@main/utils/maskSensitiveData';

export const tavily = tavilyCore;

const searchByTavily = async (options: { count: number; query: string }) => {
const currentSearchConfig = SettingStore.get('search');
const apiKey = process.env.TAVILY_API_KEY || currentSearchConfig?.apiKey;
const client = tavily({
apiKey: process.env.TAVILY_API_KEY || currentSearchConfig?.apiKey,
apiKey,
});
const searchOptions = {
maxResults: options.count,
Expand All @@ -33,6 +36,11 @@ export async function search(toolCall: ToolCall): Promise<MCPToolResult> {
const args = JSON.parse(toolCall.function.arguments);

try {
logger.info(
'Search query:',
maskSensitiveData({ query: args.query, count: args.count }),
);

if (!currentSearchConfig) {
const client = new SearchClient({
provider: SearchProviderEnum.DuckduckgoSearch,
Expand Down Expand Up @@ -105,7 +113,7 @@ export async function search(toolCall: ToolCall): Promise<MCPToolResult> {
},
];
} catch (e) {
console.error('Search error:', e);
logger.error('Search error:', e);
return [
{
isError: true,
Expand Down
20 changes: 17 additions & 3 deletions apps/agent-tars/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { updateElectronApp, UpdateSourceType } from 'update-electron-app';
import { electronApp, optimizer, is } from '@electron-toolkit/utils';
import { ipcRoutes } from './ipcRoutes';
import icon from '../../resources/icon.png?asset';
import MenuBuilder from './menu';
import { logger } from './utils/logger';

class AppUpdater {
constructor() {
Expand Down Expand Up @@ -65,6 +67,7 @@ function createWindow(): void {

mainWindow.on('ready-to-show', () => {
mainWindow.show();
logger.info('Application window is ready and shown');
});

mainWindow.webContents.setWindowOpenHandler((details) => {
Expand All @@ -79,16 +82,21 @@ function createWindow(): void {
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
}
// Set up the application menu
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
}

const initializeApp = async () => {
logger.info('Initializing application');

if (process.platform === 'darwin') {
app.setAccessibilitySupportEnabled(true);
const { ensurePermissions } = await import('@main/utils/systemPermissions');

const ensureScreenCapturePermission = ensurePermissions();
console.info(
'ensureScreenCapturePermission',
logger.info(
'Screen capture permission status:',
ensureScreenCapturePermission,
);
}
Expand All @@ -98,6 +106,8 @@ const initializeApp = async () => {
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
logger.info('Application is ready');

// Set app user model id for windows
electronApp.setAppUserModelId('com.electron');

Expand All @@ -113,7 +123,10 @@ app.whenReady().then(async () => {
await initializeApp();

// IPC test
ipcMain.on('ping', () => console.log('pong'));
ipcMain.on('ping', () => {
logger.info('Received ping event');
logger.info('pong');
});
registerIpcMain(ipcRoutes);

createWindow();
Expand All @@ -129,6 +142,7 @@ app.whenReady().then(async () => {
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
logger.info('All windows closed');
if (process.platform !== 'darwin') {
app.quit();
}
Expand Down
37 changes: 29 additions & 8 deletions apps/agent-tars/src/main/ipcRoutes/action.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { MCPServerName, ToolCall } from '@agent-infra/shared';
import { executeCustomTool, listCustomTools } from '@main/customTools';
import { createMcpClient, getOmegaDir } from '@main/mcp/client';
Expand All @@ -15,6 +16,8 @@ import {
normalizeMessages,
parseArtifacts,
} from '@main/utils/normalizeOmegaData';
import { logger } from '@main/utils/logger';
import { extractToolNames } from '@main/utils/extractToolNames';

export interface MCPTool {
id: string;
Expand Down Expand Up @@ -44,7 +47,7 @@ export const actionRoute = t.router({
listTools: t.procedure.handle(async () => {
const mcpClient = await createMcpClient();
const tools = mcpToolsToAzureTools(await mcpClient.listTools());
console.log('toolstools', tools);
logger.info('[actionRoute.listTools] tools', extractToolNames(tools));
const customTools = listCustomTools();
return [
...tools.map((tool) => tool.function),
Expand Down Expand Up @@ -74,7 +77,11 @@ export const actionRoute = t.router({
for (const toolCall of input.toolCalls) {
const mcpTool = toolUseToMcpTool(tools, toolCall);
if (mcpTool) {
console.log('i will execute tool', mcpTool.name, mcpTool.inputSchema);
logger.info(
'[actionRoute.executeTool] i will execute mcp tool',
mcpTool.name,
mcpTool.inputSchema || {},
);
try {
const result = await mcpClient.callTool({
client: mcpTool.serverName as MCPServerName,
Expand All @@ -83,16 +90,26 @@ export const actionRoute = t.router({
});
results.push(result);
} catch (e) {
console.error('execute tool error', mcpTool, e);
logger.error(
'[actionRoute.executeTool] execute tool error',
mcpTool.name,
e,
);
results.push({
isError: true,
content: [JSON.stringify(e)],
});
}
} else {
console.log('executeCustomTool_toolCall', toolCall);
logger.info(
'[actionRoute.executeTool] executeCustomTool_toolCall',
toolCall,
);
const result = await executeCustomTool(toolCall);
console.log('executeCustomTool_result', result);
logger.info(
'[actionRoute.executeTool] executeCustomTool_result',
result ? 'success' : 'no result',
);
if (result) {
results.push(...result);
}
Expand All @@ -102,6 +119,7 @@ export const actionRoute = t.router({
}),

saveBrowserSnapshot: t.procedure.input<void>().handle(async () => {
logger.info('[actionRoute.saveBrowserSnapshot] start');
const mcpClient = await createMcpClient();
try {
const result = await mcpClient.callTool({
Expand All @@ -128,7 +146,10 @@ export const actionRoute = t.router({
await fs.writeFile(filepath, imageBuffer);
return { filepath };
} catch (e) {
console.error('Failed to save screenshot:', e);
logger.error(
'[actionRoute.saveBrowserSnapshot] Failed to save screenshot:',
e,
);
throw e;
}
}),
Expand Down Expand Up @@ -183,7 +204,7 @@ export const actionRoute = t.router({
await fs.remove(tempPath);

if (!res.ok) {
console.error('Upload failed:', await res.text());
logger.error('Upload failed:', await res.text());
throw new Error('文件上传失败');
}

Expand All @@ -195,7 +216,7 @@ export const actionRoute = t.router({
await shell.openExternal(data.url);
return data.url;
} catch (error) {
console.error('Upload failed:', error);
logger.error('Upload failed:', error);
throw error;
}
} else {
Expand Down
3 changes: 2 additions & 1 deletion apps/agent-tars/src/main/ipcRoutes/agent.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { initIpc } from '@ui-tars/electron-ipc/main';
import { logger } from '@main/utils/logger';

const t = initIpc.create();

export const agentRoute = t.router({
runAgent: t.procedure.input<void>().handle(async () => {
console.log('runAgent');
logger.info('runAgent');
return 'Hello';
}),
});
5 changes: 3 additions & 2 deletions apps/agent-tars/src/main/ipcRoutes/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { setAllowedDirectories, getAllowedDirectories } from '@main/mcp/client';
import path from 'path';
import os from 'os';
import fs from 'fs-extra';
import { logger } from '@main/utils/logger';

const t = initIpc.create();

Expand Down Expand Up @@ -40,15 +41,15 @@ export const fileSystemRoute = t.router({
const content = await fs.readFile(input.filePath, 'utf8');
return content;
} catch (error) {
console.error('Failed to read file:', error);
logger.error('Failed to read file:', error);
return null;
}
}),
getAllowedDirectories: t.procedure.input<void>().handle(async () => {
try {
return await getAllowedDirectories();
} catch (error) {
console.error('Failed to get allowed directories:', error);
logger.error('Failed to get allowed directories:', error);
const omegaDir = path.join(os.homedir(), '.omega');
return [omegaDir];
}
Expand Down
37 changes: 30 additions & 7 deletions apps/agent-tars/src/main/ipcRoutes/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { BrowserWindow } from 'electron';
import { createLLM, LLMConfig } from '@main/llmProvider';
import { ProviderFactory } from '@main/llmProvider/ProviderFactory';
import { SettingStore } from '@main/store/setting';
import { logger } from '@main/utils/logger';
import { maskSensitiveData } from '@main/utils/maskSensitiveData';
import { extractToolNames } from '@main/utils/extractToolNames';

const t = initIpc.create();

Expand Down Expand Up @@ -42,6 +45,7 @@ export const llmRoute = t.router({
requestId: string;
}>()
.handle(async ({ input }) => {
logger.info('[llmRoute.askLLMText] input', input);
const messages = input.messages.map((msg) => new Message(msg));
const llm = createLLM(currentLLMConfigRef.current);
const response = await llm.askLLMText({
Expand All @@ -59,11 +63,18 @@ export const llmRoute = t.router({
requestId: string;
}>()
.handle(async ({ input }) => {
logger.info('[llmRoute.askLLMTool] input', input);
const messages = input.messages.map((msg) => new Message(msg));
const llm = createLLM(currentLLMConfigRef.current);
console.log('current llm config', currentLLMConfigRef.current);
console.log('current search config', SettingStore.get('search'));
console.log('input.tools', input.tools);
logger.info(
'[llmRoute.askLLMTool] Current LLM Config',
maskSensitiveData(currentLLMConfigRef.current),
);
logger.info(
'[llmRoute.askLLMTool] Current Search Config',
maskSensitiveData(SettingStore.get('search')),
);
logger.info('[llmRoute.askLLMTool] tools', extractToolNames(input.tools));
const response = await llm.askTool({
messages,
tools: input.tools,
Expand All @@ -80,9 +91,13 @@ export const llmRoute = t.router({
requestId: string;
}>()
.handle(async ({ input }) => {
logger.info('[llmRoute.askLLMTextStream] input', input);
const messages = input.messages.map((msg) => new Message(msg));
const { requestId } = input;
console.log('current llm config', currentLLMConfigRef.current);
logger.info(
'[llmRoute.askLLMTextStream] Current LLM Config',
maskSensitiveData(currentLLMConfigRef.current),
);
const llm = createLLM(currentLLMConfigRef.current);

(async () => {
Expand Down Expand Up @@ -120,12 +135,16 @@ export const llmRoute = t.router({
updateLLMConfig: t.procedure
.input<ModelSettings>()
.handle(async ({ input }) => {
logger.info('[llmRoute.updateLLMConfig] input', maskSensitiveData(input));
try {
SettingStore.set('model', input);
currentLLMConfigRef.current = getLLMProviderConfig(input);
return true;
} catch (error) {
console.error('Failed to update LLM configuration:', error);
logger.error(
'[llmRoute.updateLLMConfig] Failed to update LLM configuration:',
error,
);
return false;
}
}),
Expand All @@ -134,19 +153,23 @@ export const llmRoute = t.router({
try {
return ProviderFactory.getAvailableProviders();
} catch (error) {
console.error('Failed to get available providers:', error);
logger.error(
'[llmRoute.getAvailableProviders] Failed to get available providers:',
error,
);
return [];
}
}),
abortRequest: t.procedure
.input<{ requestId: string }>()
.handle(async ({ input }) => {
logger.info('[llmRoute.abortRequest] input', input);
try {
const llm = createLLM(currentLLMConfigRef.current);
llm.abortRequest(input.requestId);
return true;
} catch (error) {
console.error('Failed to abort request:', error);
logger.error('[llmRoute.abortRequest] Failed to abort request:', error);
return false;
}
}),
Expand Down
11 changes: 10 additions & 1 deletion apps/agent-tars/src/main/ipcRoutes/search.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { initIpc } from '@ui-tars/electron-ipc/main';
import { SearchSettings } from '@agent-infra/shared';
import { SettingStore } from '@main/store/setting';
import { logger } from '@main/utils/logger';
import { maskSensitiveData } from '@main/utils/maskSensitiveData';

const t = initIpc.create();

Expand All @@ -9,10 +11,17 @@ export const searchRoute = t.router({
.input<SearchSettings>()
.handle(async ({ input }) => {
try {
logger.info(
'[searchRoute.updateSearchConfig] Updating search configuration:',
maskSensitiveData(input),
);
SettingStore.set('search', input);
return true;
} catch (error) {
console.error('Failed to update search configuration:', error);
logger.error(
'[searchRoute.updateSearchConfig] Failed to update search configuration:',
error,
);
return false;
}
}),
Expand Down
Loading