Skip to content
Closed
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
30 changes: 29 additions & 1 deletion packages/core/src/core/turn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,9 @@ export class Turn {
fnCall.id ??
`${fnCall.name}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
const name = fnCall.name || 'undefined_tool_name';
const args = (fnCall.args || {}) as Record<string, unknown>;
const args = sanitizeToolArgs(
(fnCall.args || {}) as Record<string, unknown>,
);

const toolCallRequest: ToolCallRequestInfo = {
callId,
Expand Down Expand Up @@ -399,3 +401,29 @@ function getCitations(resp: GenerateContentResponse): string[] {
return citation.uri!;
});
}

/**
* Sanitizes tool arguments to fix common model output issues.
* Specifically removes spaces between Chinese characters and numbers in file paths.
* This fixes issue #2032 where the model adds spaces like "测试 1 文件.txt" instead of "测试1文件.txt"
*/
function sanitizeToolArgs(
args: Record<string, unknown>,
): Record<string, unknown> {
const sanitized: Record<string, unknown> = {};

for (const [key, value] of Object.entries(args)) {
if (typeof value === 'string') {
// Remove spaces between Chinese characters and numbers
// This regex matches: Chinese char + space + digit, or digit + space + Chinese char
sanitized[key] = value.replace(
/([\u4e00-\u9fa5])\s+(\d)|(\d)\s+([\u4e00-\u9fa5])/g,
'$1$2$3$4',
);
} else {
sanitized[key] = value;
}
}

return sanitized;
}
28 changes: 27 additions & 1 deletion packages/core/src/subagents/subagent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ export class SubAgentScope {
const requests: ToolCallRequestInfo[] = authorizedCalls.map((fc) => {
const toolName = String(fc.name || 'unknown');
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
const args = (fc.args ?? {}) as Record<string, unknown>;
const args = sanitizeToolArgs((fc.args ?? {}) as Record<string, unknown>);
const request: ToolCallRequestInfo = {
callId,
name: toolName,
Expand Down Expand Up @@ -1002,3 +1002,29 @@ Important Rules:
return finalPrompt;
}
}

/**
* Sanitizes tool arguments to fix common model output issues.
* Specifically removes spaces between Chinese characters and numbers in file paths.
* This fixes issue #2032 where the model adds spaces like "测试 1 文件.txt" instead of "测试1文件.txt"
*/
function sanitizeToolArgs(
args: Record<string, unknown>,
): Record<string, unknown> {
const sanitized: Record<string, unknown> = {};

for (const [key, value] of Object.entries(args)) {
if (typeof value === 'string') {
// Remove spaces between Chinese characters and numbers
// This regex matches: Chinese char + space + digit, or digit + space + Chinese char
sanitized[key] = value.replace(
/([\u4e00-\u9fa5])\s+(\d)|(\d)\s+([\u4e00-\u9fa5])/g,
'$1$2$3$4',
);
} else {
sanitized[key] = value;
}
}

return sanitized;
}
16 changes: 16 additions & 0 deletions packages/core/src/utils/filesearch/crawler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import path from 'node:path';
import { fdir } from 'fdir';
import type { Ignore } from './ignore.js';
import * as cache from './crawlCache.js';
import { createDebugLogger } from '../debugLogger.js';

const debugLogger = createDebugLogger('CRAWLER');

export interface CrawlOptions {
// The directory to start the crawl from.
Expand All @@ -21,13 +24,17 @@ export interface CrawlOptions {
// Caching options.
cache: boolean;
cacheTtl: number;
// Maximum number of files to crawl to prevent OOM (default: 100000)
maxFiles?: number;
}

function toPosixPath(p: string) {
return p.split(path.sep).join(path.posix.sep);
}

export async function crawl(options: CrawlOptions): Promise<string[]> {
const maxFiles = options.maxFiles ?? 100000;

if (options.cache) {
const cacheKey = cache.getCacheKey(
options.crawlDirectory,
Expand Down Expand Up @@ -61,6 +68,15 @@ export async function crawl(options: CrawlOptions): Promise<string[]> {
}

results = await api.crawl(options.crawlDirectory).withPromise();

// Limit the number of files to prevent OOM in large projects
if (results.length > maxFiles) {
debugLogger.warn(
`Project contains ${results.length} files, limiting to ${maxFiles} to prevent memory issues. ` +
`Consider using .qwenignore to exclude unnecessary directories.`,
);
results = results.slice(0, maxFiles);
}
} catch (_e) {
// The directory probably doesn't exist.
return [];
Expand Down