feat(tools): localize tool descriptions by locale for every runtime

Tool and parameter descriptions were handed to the LLM in Chinese on every
runtime regardless of locale: the English metadata table in
packages/tools/src/tool-metadata.ts had no caller, the desktop translateTool
looked up i18n keys that exist in no locale file, and the MCP server had no
locale at all.

Fill the table for every tool whose definition text is Chinese (37 entries,
only translating what the definitions write in Chinese so the two copies
cannot drift), and route the four consumers through getLocalizedToolMetadata:
the cross-chat agent adapters, the CLI Web agent adapter, the desktop shared
tool adapter (replacing the dead translateTool), and the MCP server, which
now resolves its locale from config.locale.lang or the system locale for both
the standalone binary and the chatlab mcp subcommand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chris ChengandClaude Opus 5 authored and digua committed 2026-09-22 16:54:06 +08:00
1 parent e295a80bdf
commit 6e27968bb0
15 files changed
+362 -78

No files matched your search

+1 -1
View File
@@ -227,7 +227,7 @@ export function createCliRunAgentStream(
timeFilter: params.timeFilter,
maxMessagesLimit: params.maxMessagesLimit,
}),
{ maxToolResultTokens }
{ maxToolResultTokens, locale }
)
: []
+14 -6
View File
@@ -12,7 +12,12 @@ import type {
RawMessage,
ToolTimeRange,
} from '@openchatlab/tools'
import { CoreDataProvider, executeToolForAgent, toAgentToolParameters } from '@openchatlab/tools'
import {
CoreDataProvider,
executeToolForAgent,
getLocalizedToolMetadata,
toAgentToolParameters,
} from '@openchatlab/tools'
import type { DatabaseAdapter } from '@openchatlab/core'
import {
applyPreprocessingPipeline,
@@ -56,6 +61,8 @@ export interface ServerToolContext {
export interface AdaptToolsOptions {
maxToolResultTokens?: number
/** Request locale; decides whether the LLM sees Chinese or English tool descriptions. */
locale?: string
}
export function adaptToolsForAgent(
@@ -66,13 +73,14 @@ export function adaptToolsForAgent(
const tokenBudget = options?.maxToolResultTokens ?? DEFAULT_MAX_TOOL_RESULT_TOKENS
const chartSchemaGateState = createChartSchemaGateState()
return tools.map((tool) =>
wrapWithChartSchemaGate(
return tools.map((tool) => {
const localized = getLocalizedToolMetadata(tool, options?.locale)
return wrapWithChartSchemaGate(
{
name: tool.name,
label: tool.name,
description: tool.description,
parameters: toAgentToolParameters(tool.inputSchema) as any,
description: localized.description,
parameters: toAgentToolParameters(localized.inputSchema) as any,
executionMode: tool.executionMode,
async execute(_toolCallId: string, params: unknown, signal, onUpdate) {
const ctx = getContext()
@@ -122,5 +130,5 @@ export function adaptToolsForAgent(
},
chartSchemaGateState
)
)
})
}
+2 -2
View File
@@ -12,7 +12,7 @@ import {
hasPendingElectronDataWarning,
verifyCliDataPath,
} from '@openchatlab/node-runtime'
import { startMcpServer } from 'chatlab-mcp'
import { resolveMcpLocale, startMcpServer } from 'chatlab-mcp'
import { getVersion } from './version'
import { resolveCliPath } from './paths'
import { assertCliDataDirCompatible } from './runtime-compat'
@@ -43,5 +43,5 @@ export function initMcpRuntime(): DatabaseManager {
export async function startCliMcpServer(): Promise<void> {
const dbManager = initMcpRuntime()
await startMcpServer({ version: getVersion(), dbManager })
await startMcpServer({ version: getVersion(), dbManager, locale: resolveMcpLocale(loadConfig()) })
}