Skip to content

Commit dcd7804

Browse files
committed
fix: nudge logic now tracks tools since last prune instead of total history
1 parent 9e741e3 commit dcd7804

File tree

4 files changed

+56
-32
lines changed

4 files changed

+56
-32
lines changed

lib/api-formats/synth-instruction.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,76 @@ export function resetToolTrackerCount(tracker: ToolTracker): void {
1515
}
1616

1717
/**
18-
* Counts total tool results in OpenAI/Anthropic messages (without tracker).
19-
* Used for determining if nudge threshold is met.
18+
* Track new tool results in OpenAI/Anthropic messages.
19+
* Increments toolResultCount only for tools not already seen.
20+
* Returns the number of NEW tools found (since last call).
2021
*/
21-
export function countToolResults(messages: any[]): number {
22-
let count = 0
22+
export function trackNewToolResults(messages: any[], tracker: ToolTracker): number {
23+
let newCount = 0
2324
for (const m of messages) {
24-
if (m.role === 'tool') {
25-
count++
25+
if (m.role === 'tool' && m.tool_call_id) {
26+
if (!tracker.seenToolResultIds.has(m.tool_call_id)) {
27+
tracker.seenToolResultIds.add(m.tool_call_id)
28+
tracker.toolResultCount++
29+
newCount++
30+
}
2631
} else if (m.role === 'user' && Array.isArray(m.content)) {
2732
for (const part of m.content) {
28-
if (part.type === 'tool_result') {
29-
count++
33+
if (part.type === 'tool_result' && part.tool_use_id) {
34+
if (!tracker.seenToolResultIds.has(part.tool_use_id)) {
35+
tracker.seenToolResultIds.add(part.tool_use_id)
36+
tracker.toolResultCount++
37+
newCount++
38+
}
3039
}
3140
}
3241
}
3342
}
34-
return count
43+
return newCount
3544
}
3645

3746
/**
38-
* Counts total tool results in Gemini contents (without tracker).
47+
* Track new tool results in Gemini contents.
48+
* Uses position-based tracking since Gemini doesn't have tool call IDs.
49+
* Returns the number of NEW tools found (since last call).
3950
*/
40-
export function countToolResultsGemini(contents: any[]): number {
41-
let count = 0
51+
export function trackNewToolResultsGemini(contents: any[], tracker: ToolTracker): number {
52+
let newCount = 0
53+
let positionCounter = 0
4254
for (const content of contents) {
4355
if (!Array.isArray(content.parts)) continue
4456
for (const part of content.parts) {
4557
if (part.functionResponse) {
46-
count++
58+
// Use position-based ID since Gemini doesn't have tool_call_id
59+
const positionId = `gemini_pos_${positionCounter}`
60+
positionCounter++
61+
if (!tracker.seenToolResultIds.has(positionId)) {
62+
tracker.seenToolResultIds.add(positionId)
63+
tracker.toolResultCount++
64+
newCount++
65+
}
4766
}
4867
}
4968
}
50-
return count
69+
return newCount
5170
}
5271

5372
/**
54-
* Counts total tool results in OpenAI Responses API input (without tracker).
73+
* Track new tool results in OpenAI Responses API input.
74+
* Returns the number of NEW tools found (since last call).
5575
*/
56-
export function countToolResultsResponses(input: any[]): number {
57-
let count = 0
76+
export function trackNewToolResultsResponses(input: any[], tracker: ToolTracker): number {
77+
let newCount = 0
5878
for (const item of input) {
59-
if (item.type === 'function_call_output') {
60-
count++
79+
if (item.type === 'function_call_output' && item.call_id) {
80+
if (!tracker.seenToolResultIds.has(item.call_id)) {
81+
tracker.seenToolResultIds.add(item.call_id)
82+
tracker.toolResultCount++
83+
newCount++
84+
}
6185
}
6286
}
63-
return count
87+
return newCount
6488
}
6589

6690
// ============================================================================

lib/fetch-wrapper/gemini.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getAllPrunedIds,
55
fetchSessionMessages
66
} from "./types"
7-
import { injectSynthGemini, countToolResultsGemini } from "../api-formats/synth-instruction"
7+
import { injectSynthGemini, trackNewToolResultsGemini } from "../api-formats/synth-instruction"
88
import { buildPrunableToolsList, buildEndInjection, injectPrunableListGemini } from "../api-formats/prunable-list"
99

1010
/**
@@ -45,9 +45,9 @@ export async function handleGemini(
4545
)
4646

4747
if (prunableList) {
48-
// Check if nudge should be included
49-
const toolResultCount = countToolResultsGemini(body.contents)
50-
const includeNudge = ctx.config.nudge_freq > 0 && toolResultCount > ctx.config.nudge_freq
48+
// Track new tool results and check if nudge threshold is met
49+
trackNewToolResultsGemini(body.contents, ctx.toolTracker)
50+
const includeNudge = ctx.config.nudge_freq > 0 && ctx.toolTracker.toolResultCount > ctx.config.nudge_freq
5151

5252
const endInjection = buildEndInjection(prunableList, includeNudge)
5353
if (injectPrunableListGemini(body.contents, endInjection)) {

lib/fetch-wrapper/openai-chat.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
getMostRecentActiveSession
77
} from "./types"
88
import { cacheToolParametersFromMessages } from "../state/tool-cache"
9-
import { injectSynth, countToolResults } from "../api-formats/synth-instruction"
9+
import { injectSynth, trackNewToolResults } from "../api-formats/synth-instruction"
1010
import { buildPrunableToolsList, buildEndInjection, injectPrunableList } from "../api-formats/prunable-list"
1111

1212
/**
@@ -50,9 +50,9 @@ export async function handleOpenAIChatAndAnthropic(
5050
)
5151

5252
if (prunableList) {
53-
// Check if nudge should be included
54-
const toolResultCount = countToolResults(body.messages)
55-
const includeNudge = ctx.config.nudge_freq > 0 && toolResultCount > ctx.config.nudge_freq
53+
// Track new tool results and check if nudge threshold is met
54+
trackNewToolResults(body.messages, ctx.toolTracker)
55+
const includeNudge = ctx.config.nudge_freq > 0 && ctx.toolTracker.toolResultCount > ctx.config.nudge_freq
5656

5757
const endInjection = buildEndInjection(prunableList, includeNudge)
5858
if (injectPrunableList(body.messages, endInjection)) {

lib/fetch-wrapper/openai-responses.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
getMostRecentActiveSession
77
} from "./types"
88
import { cacheToolParametersFromInput } from "../state/tool-cache"
9-
import { injectSynthResponses, countToolResultsResponses } from "../api-formats/synth-instruction"
9+
import { injectSynthResponses, trackNewToolResultsResponses } from "../api-formats/synth-instruction"
1010
import { buildPrunableToolsList, buildEndInjection, injectPrunableListResponses } from "../api-formats/prunable-list"
1111

1212
/**
@@ -50,9 +50,9 @@ export async function handleOpenAIResponses(
5050
)
5151

5252
if (prunableList) {
53-
// Check if nudge should be included
54-
const toolResultCount = countToolResultsResponses(body.input)
55-
const includeNudge = ctx.config.nudge_freq > 0 && toolResultCount > ctx.config.nudge_freq
53+
// Track new tool results and check if nudge threshold is met
54+
trackNewToolResultsResponses(body.input, ctx.toolTracker)
55+
const includeNudge = ctx.config.nudge_freq > 0 && ctx.toolTracker.toolResultCount > ctx.config.nudge_freq
5656

5757
const endInjection = buildEndInjection(prunableList, includeNudge)
5858
if (injectPrunableListResponses(body.input, endInjection)) {

0 commit comments

Comments
 (0)