Skip to content

Commit cc203a4

Browse files
fix: various fixes
1 parent ca2b7e3 commit cc203a4

File tree

6 files changed

+55
-18
lines changed

6 files changed

+55
-18
lines changed

lib/deduplicator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ export function detectDuplicates(
1818
): DuplicateDetectionResult {
1919
const signatureMap = new Map<string, string[]>()
2020

21+
const protectedToolsLower = protectedTools.map(t => t.toLowerCase())
2122
const deduplicatableIds = unprunedToolCallIds.filter(id => {
2223
const metadata = toolMetadata.get(id)
23-
return !metadata || !protectedTools.includes(metadata.tool)
24+
return !metadata || !protectedToolsLower.includes(metadata.tool.toLowerCase())
2425
})
2526

2627
for (const id of deduplicatableIds) {

lib/fetch-wrapper/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export function installFetchWrapper(
5353
const body = JSON.parse(init.body)
5454
const inputUrl = typeof input === 'string' ? input : 'URL object'
5555
let modified = false
56+
// Track tool IDs cached from this request for session-scoped deduplication
57+
const cachedToolIds: string[] = []
5658

5759
// Try each format handler in order
5860
// OpenAI Chat Completions & Anthropic style (body.messages)
@@ -61,6 +63,9 @@ export function installFetchWrapper(
6163
if (result.modified) {
6264
modified = true
6365
}
66+
if (result.cachedToolIds) {
67+
cachedToolIds.push(...result.cachedToolIds)
68+
}
6469
}
6570

6671
// Google/Gemini style (body.contents)
@@ -77,15 +82,17 @@ export function installFetchWrapper(
7782
if (result.modified) {
7883
modified = true
7984
}
85+
if (result.cachedToolIds) {
86+
cachedToolIds.push(...result.cachedToolIds)
87+
}
8088
}
8189

82-
// Run deduplication after handlers have populated toolParameters cache
90+
// Run deduplication only on tool IDs from the current request (session-scoped)
8391
const sessionId = state.lastSeenSessionId
84-
if (sessionId && state.toolParameters.size > 0) {
85-
const toolIds = Array.from(state.toolParameters.keys())
92+
if (sessionId && cachedToolIds.length > 1) {
8693
const alreadyPruned = state.prunedIds.get(sessionId) ?? []
8794
const alreadyPrunedLower = new Set(alreadyPruned.map(id => id.toLowerCase()))
88-
const unpruned = toolIds.filter(id => !alreadyPrunedLower.has(id.toLowerCase()))
95+
const unpruned = cachedToolIds.filter(id => !alreadyPrunedLower.has(id.toLowerCase()))
8996
if (unpruned.length > 1) {
9097
const { duplicateIds } = detectDuplicates(state.toolParameters, unpruned, config.protectedTools)
9198
if (duplicateIds.length > 0) {

lib/fetch-wrapper/openai-chat.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export async function handleOpenAIChatAndAnthropic(
2121
return { modified: false, body }
2222
}
2323

24-
// Cache tool parameters from messages
25-
cacheToolParametersFromMessages(body.messages, ctx.state)
24+
// Cache tool parameters from messages and track which IDs were cached
25+
const cachedToolIds = cacheToolParametersFromMessages(body.messages, ctx.state)
2626

2727
let modified = false
2828

@@ -64,7 +64,7 @@ export async function handleOpenAIChatAndAnthropic(
6464
const { allSessions, allPrunedIds } = await getAllPrunedIds(ctx.client, ctx.state, ctx.logger)
6565

6666
if (toolMessages.length === 0 || allPrunedIds.size === 0) {
67-
return { modified, body }
67+
return { modified, body, cachedToolIds }
6868
}
6969

7070
let replacedCount = 0
@@ -125,8 +125,8 @@ export async function handleOpenAIChatAndAnthropic(
125125
)
126126
}
127127

128-
return { modified: true, body }
128+
return { modified: true, body, cachedToolIds }
129129
}
130130

131-
return { modified, body }
131+
return { modified, body, cachedToolIds }
132132
}

lib/fetch-wrapper/openai-responses.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export async function handleOpenAIResponses(
2121
return { modified: false, body }
2222
}
2323

24-
// Cache tool parameters from input
25-
cacheToolParametersFromInput(body.input, ctx.state)
24+
// Cache tool parameters from input and track which IDs were cached
25+
const cachedToolIds = cacheToolParametersFromInput(body.input, ctx.state)
2626

2727
let modified = false
2828

@@ -52,13 +52,13 @@ export async function handleOpenAIResponses(
5252
const functionOutputs = body.input.filter((item: any) => item.type === 'function_call_output')
5353

5454
if (functionOutputs.length === 0) {
55-
return { modified, body }
55+
return { modified, body, cachedToolIds }
5656
}
5757

5858
const { allSessions, allPrunedIds } = await getAllPrunedIds(ctx.client, ctx.state, ctx.logger)
5959

6060
if (allPrunedIds.size === 0) {
61-
return { modified, body }
61+
return { modified, body, cachedToolIds }
6262
}
6363

6464
let replacedCount = 0
@@ -99,8 +99,8 @@ export async function handleOpenAIResponses(
9999
)
100100
}
101101

102-
return { modified: true, body }
102+
return { modified: true, body, cachedToolIds }
103103
}
104104

105-
return { modified, body }
105+
return { modified, body, cachedToolIds }
106106
}

lib/fetch-wrapper/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export interface FetchHandlerResult {
2828
modified: boolean
2929
/** The potentially modified body object */
3030
body: any
31+
/** Tool call IDs that were cached from this request (for session-scoped deduplication) */
32+
cachedToolIds?: string[]
3133
}
3234

3335
/** Session data returned from getAllPrunedIds */

lib/tool-cache.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
import type { PluginState } from "./state"
22

3+
/** Maximum number of tool parameters to cache to prevent unbounded memory growth */
4+
const MAX_TOOL_PARAMETERS_CACHE_SIZE = 500
5+
6+
/**
7+
* Ensures the toolParameters cache doesn't exceed the maximum size.
8+
* Removes oldest entries (first inserted) when limit is exceeded.
9+
*/
10+
function trimToolParametersCache(state: PluginState): void {
11+
if (state.toolParameters.size > MAX_TOOL_PARAMETERS_CACHE_SIZE) {
12+
const excess = state.toolParameters.size - MAX_TOOL_PARAMETERS_CACHE_SIZE
13+
const keys = Array.from(state.toolParameters.keys())
14+
for (let i = 0; i < excess; i++) {
15+
state.toolParameters.delete(keys[i])
16+
}
17+
}
18+
}
19+
320
/**
421
* Cache tool parameters from OpenAI Chat Completions style messages.
522
* Extracts tool call IDs and their parameters from assistant messages with tool_calls.
23+
* Returns the list of tool call IDs that were cached from this request.
624
*/
725
export function cacheToolParametersFromMessages(
826
messages: any[],
927
state: PluginState
10-
): void {
28+
): string[] {
29+
const cachedIds: string[] = []
1130
for (const message of messages) {
1231
if (message.role !== 'assistant' || !Array.isArray(message.tool_calls)) {
1332
continue
@@ -26,21 +45,26 @@ export function cacheToolParametersFromMessages(
2645
tool: toolCall.function.name,
2746
parameters: params
2847
})
48+
cachedIds.push(toolCall.id)
2949
} catch (error) {
3050
// Silently ignore parse errors
3151
}
3252
}
3353
}
54+
trimToolParametersCache(state)
55+
return cachedIds
3456
}
3557

3658
/**
3759
* Cache tool parameters from OpenAI Responses API format.
3860
* Extracts from input array items with type='function_call'.
61+
* Returns the list of tool call IDs that were cached from this request.
3962
*/
4063
export function cacheToolParametersFromInput(
4164
input: any[],
4265
state: PluginState
43-
): void {
66+
): string[] {
67+
const cachedIds: string[] = []
4468
for (const item of input) {
4569
if (item.type !== 'function_call' || !item.call_id || !item.name) {
4670
continue
@@ -54,8 +78,11 @@ export function cacheToolParametersFromInput(
5478
tool: item.name,
5579
parameters: params
5680
})
81+
cachedIds.push(item.call_id)
5782
} catch (error) {
5883
// Silently ignore parse errors
5984
}
6085
}
86+
trimToolParametersCache(state)
87+
return cachedIds
6188
}

0 commit comments

Comments
 (0)