perf improvements + /login fix
This commit is contained in:
@@ -9,6 +9,7 @@ import { findToolByName, type Tools, type ToolUseContext } from '../../Tool.js'
|
||||
import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js'
|
||||
import type { AssistantMessage, Message } from '../../types/message.js'
|
||||
import { createChildAbortController } from '../../utils/abortController.js'
|
||||
import { getMaxToolUseConcurrency } from './toolConcurrency.js'
|
||||
import { runToolUse } from './toolExecution.js'
|
||||
|
||||
type MessageUpdate = {
|
||||
@@ -31,6 +32,20 @@ type TrackedTool = {
|
||||
contextModifiers?: Array<(context: ToolUseContext) => ToolUseContext>
|
||||
}
|
||||
|
||||
const EPHEMERAL_PROGRESS_TYPES = new Set([
|
||||
'bash_progress',
|
||||
'powershell_progress',
|
||||
'mcp_progress',
|
||||
'sleep_progress',
|
||||
])
|
||||
|
||||
function isEphemeralProgressMessage(message: Message): boolean {
|
||||
return (
|
||||
message.type === 'progress' &&
|
||||
EPHEMERAL_PROGRESS_TYPES.has(message.data.type)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes tools as they stream in with concurrency control.
|
||||
* - Concurrent-safe tools can execute in parallel with other concurrent-safe tools
|
||||
@@ -128,10 +143,13 @@ export class StreamingToolExecutor {
|
||||
*/
|
||||
private canExecuteTool(isConcurrencySafe: boolean): boolean {
|
||||
const executingTools = this.tools.filter(t => t.status === 'executing')
|
||||
return (
|
||||
executingTools.length === 0 ||
|
||||
(isConcurrencySafe && executingTools.every(t => t.isConcurrencySafe))
|
||||
)
|
||||
if (executingTools.length === 0) {
|
||||
return true
|
||||
}
|
||||
if (!isConcurrencySafe || !executingTools.every(t => t.isConcurrencySafe)) {
|
||||
return false
|
||||
}
|
||||
return executingTools.length < getMaxToolUseConcurrency()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,7 +384,17 @@ export class StreamingToolExecutor {
|
||||
if (update.message) {
|
||||
// Progress messages go to pendingProgress for immediate yielding
|
||||
if (update.message.type === 'progress') {
|
||||
tool.pendingProgress.push(update.message)
|
||||
const lastPending = tool.pendingProgress.at(-1)
|
||||
if (
|
||||
isEphemeralProgressMessage(update.message) &&
|
||||
lastPending?.type === 'progress' &&
|
||||
lastPending.data.type === update.message.data.type
|
||||
) {
|
||||
tool.pendingProgress[tool.pendingProgress.length - 1] =
|
||||
update.message
|
||||
} else {
|
||||
tool.pendingProgress.push(update.message)
|
||||
}
|
||||
// Signal that progress is available
|
||||
if (this.progressAvailableResolve) {
|
||||
this.progressAvailableResolve()
|
||||
|
||||
8
src/services/tools/toolConcurrency.ts
Normal file
8
src/services/tools/toolConcurrency.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const DEFAULT_MAX_TOOL_USE_CONCURRENCY = 4
|
||||
|
||||
export function getMaxToolUseConcurrency(): number {
|
||||
const parsed = parseInt(process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY || '', 10)
|
||||
return Number.isFinite(parsed) && parsed > 0
|
||||
? parsed
|
||||
: DEFAULT_MAX_TOOL_USE_CONCURRENCY
|
||||
}
|
||||
33
src/services/tools/toolOrchestration.test.ts
Normal file
33
src/services/tools/toolOrchestration.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { afterEach, expect, test } from 'bun:test'
|
||||
import {
|
||||
DEFAULT_MAX_TOOL_USE_CONCURRENCY,
|
||||
getMaxToolUseConcurrency,
|
||||
} from './toolConcurrency.js'
|
||||
|
||||
const ORIGINAL_CONCURRENCY = process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY
|
||||
|
||||
afterEach(() => {
|
||||
if (ORIGINAL_CONCURRENCY === undefined) {
|
||||
delete process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY
|
||||
} else {
|
||||
process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY = ORIGINAL_CONCURRENCY
|
||||
}
|
||||
})
|
||||
|
||||
test('defaults tool concurrency to a bounded budget', () => {
|
||||
delete process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY
|
||||
expect(getMaxToolUseConcurrency()).toBe(DEFAULT_MAX_TOOL_USE_CONCURRENCY)
|
||||
})
|
||||
|
||||
test('allows an explicit positive concurrency override', () => {
|
||||
process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY = '7'
|
||||
expect(getMaxToolUseConcurrency()).toBe(7)
|
||||
})
|
||||
|
||||
test('ignores invalid concurrency overrides', () => {
|
||||
process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY = '0'
|
||||
expect(getMaxToolUseConcurrency()).toBe(DEFAULT_MAX_TOOL_USE_CONCURRENCY)
|
||||
|
||||
process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY = 'not-a-number'
|
||||
expect(getMaxToolUseConcurrency()).toBe(DEFAULT_MAX_TOOL_USE_CONCURRENCY)
|
||||
})
|
||||
@@ -4,12 +4,10 @@ import { findToolByName, type ToolUseContext } from '../../Tool.js'
|
||||
import type { AssistantMessage, Message } from '../../types/message.js'
|
||||
import { all } from '../../utils/generators.js'
|
||||
import { type MessageUpdateLazy, runToolUse } from './toolExecution.js'
|
||||
|
||||
function getMaxToolUseConcurrency(): number {
|
||||
return (
|
||||
parseInt(process.env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY || '', 10) || 10
|
||||
)
|
||||
}
|
||||
export {
|
||||
DEFAULT_MAX_TOOL_USE_CONCURRENCY,
|
||||
getMaxToolUseConcurrency,
|
||||
} from './toolConcurrency.js'
|
||||
|
||||
export type MessageUpdate = {
|
||||
message?: Message
|
||||
|
||||
Reference in New Issue
Block a user