Merge pull request #2014 from badgerbees/fix/context-pruning-guards
fix(agent): include SystemParts in token estimation and add reasoning guards
This commit is contained in:
commit
848f9dd2e9
2 changed files with 40 additions and 5 deletions
|
|
@ -90,14 +90,29 @@ func findSafeBoundary(history []providers.Message, targetIndex int) int {
|
||||||
// including Content, ReasoningContent, ToolCalls arguments, ToolCallID
|
// including Content, ReasoningContent, ToolCalls arguments, ToolCallID
|
||||||
// metadata, and Media items. Uses a heuristic of 2.5 characters per token.
|
// metadata, and Media items. Uses a heuristic of 2.5 characters per token.
|
||||||
func estimateMessageTokens(msg providers.Message) int {
|
func estimateMessageTokens(msg providers.Message) int {
|
||||||
chars := utf8.RuneCountInString(msg.Content)
|
contentChars := utf8.RuneCountInString(msg.Content)
|
||||||
|
|
||||||
// ReasoningContent (extended thinking / chain-of-thought) can be
|
// SystemParts are structured system blocks used for cache-aware adapters.
|
||||||
// substantial and is stored in session history via AddFullMessage.
|
// They carry the same content as Content, but in multiple blocks.
|
||||||
if msg.ReasoningContent != "" {
|
// We estimate them as an alternative representation, not additive.
|
||||||
chars += utf8.RuneCountInString(msg.ReasoningContent)
|
systemPartsChars := 0
|
||||||
|
if len(msg.SystemParts) > 0 {
|
||||||
|
for _, part := range msg.SystemParts {
|
||||||
|
systemPartsChars += utf8.RuneCountInString(part.Text)
|
||||||
|
}
|
||||||
|
// Per-part overhead for JSON structure (type, text, cache_control).
|
||||||
|
const perPartOverhead = 20
|
||||||
|
systemPartsChars += len(msg.SystemParts) * perPartOverhead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the larger of the two representations to stay conservative.
|
||||||
|
chars := contentChars
|
||||||
|
if systemPartsChars > chars {
|
||||||
|
chars = systemPartsChars
|
||||||
|
}
|
||||||
|
|
||||||
|
chars += utf8.RuneCountInString(msg.ReasoningContent)
|
||||||
|
|
||||||
for _, tc := range msg.ToolCalls {
|
for _, tc := range msg.ToolCalls {
|
||||||
chars += len(tc.ID) + len(tc.Type)
|
chars += len(tc.ID) + len(tc.Type)
|
||||||
if tc.Function != nil {
|
if tc.Function != nil {
|
||||||
|
|
|
||||||
|
|
@ -529,6 +529,26 @@ func TestEstimateMessageTokens_MediaItems(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEstimateMessageTokens_SystemParts(t *testing.T) {
|
||||||
|
plain := providers.Message{Role: "system", Content: "instructions"}
|
||||||
|
withParts := providers.Message{
|
||||||
|
Role: "system",
|
||||||
|
Content: "instructions",
|
||||||
|
SystemParts: []providers.ContentBlock{
|
||||||
|
{Type: "text", Text: "some more system context"},
|
||||||
|
{Type: "text", Text: "even more cached blocks"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
plainTokens := estimateMessageTokens(plain)
|
||||||
|
partsTokens := estimateMessageTokens(withParts)
|
||||||
|
|
||||||
|
if partsTokens <= plainTokens {
|
||||||
|
t.Errorf("system message with SystemParts (%d) should exceed plain message (%d)",
|
||||||
|
partsTokens, plainTokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- estimateToolDefsTokens tests ---
|
// --- estimateToolDefsTokens tests ---
|
||||||
|
|
||||||
func TestEstimateToolDefsTokens(t *testing.T) {
|
func TestEstimateToolDefsTokens(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue