diff options
Diffstat (limited to 'libpanto/src/provider.zig')
| -rw-r--r-- | libpanto/src/provider.zig | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/libpanto/src/provider.zig b/libpanto/src/provider.zig index 1a8ee4e..6ccbefe 100644 --- a/libpanto/src/provider.zig +++ b/libpanto/src/provider.zig @@ -14,6 +14,28 @@ pub const ContentBlockType = enum { ToolResult, }; +/// Heuristic detector for provider context-overflow rejections, applied to +/// an HTTP 400 error response body. Both OpenAI-compatible and Anthropic +/// APIs reject oversized requests on the input side with HTTP 400 and a +/// recognizable message; matching it lets the agent compact and retry +/// instead of surfacing a hard error. +/// +/// Markers (case-sensitive substrings, as the wire emits them): +/// - OpenAI: `context_length_exceeded`, `maximum context length` +/// - Anthropic: `prompt is too long` +pub fn isContextOverflowBody(body: []const u8) bool { + const markers = [_][]const u8{ + "context_length_exceeded", + "maximum context length", + "prompt is too long", + "context window", + }; + for (markers) |m| { + if (std.mem.indexOf(u8, body, m) != null) return true; + } + return false; +} + /// Vtable for receiving streaming events from a Provider. /// /// The lifecycle callbacks (`onMessageStart` ... `onMessageComplete`) return @@ -143,3 +165,12 @@ pub const StreamFn = *const fn ( conv: *conversation.Conversation, receiver: *Receiver, ) anyerror!void; + +test "isContextOverflowBody - matches known markers, rejects others" { + const t2 = std.testing; + try t2.expect(isContextOverflowBody("{\"error\":{\"code\":\"context_length_exceeded\"}}")); + try t2.expect(isContextOverflowBody("This model's maximum context length is 8192 tokens")); + try t2.expect(isContextOverflowBody("prompt is too long: 250000 tokens > 200000 maximum")); + try t2.expect(!isContextOverflowBody("{\"error\":{\"code\":\"invalid_api_key\"}}")); + try t2.expect(!isContextOverflowBody("rate limit exceeded")); +} |
