Documentation
¶
Overview ¶
Package btdby4 is a pure-Go estimator for whole LLM request payloads. It scores system, tools, text, images, visible and encrypted thinking, and tool I/O generically — ~90% accuracy against any provider or model, with nothing to configure beyond Options{Tight, IgnoreImages}. Counting is all you need for context windows, cost estimation, budget enforcement and automatic compaction — and estimating is much cheaper than encoding. JSON parsing uses goccy/go-json (pure Go, no CGO), which also builds for js/wasm.
Supported request shapes (each protocol has the same granularities: request, request-JSON, message/item, message/item-JSON, part/block, part/block-JSON, tool, tool-JSON):
Anthropic Messages CountAnthropicRequest / CountAnthropicMessage / CountAnthropicBlock / CountAnthropicTool (+JSON) OpenAI Chat CountChatRequest / CountChatMessage / CountChatPart / CountChatTool (+JSON) OpenAI Responses CountResponsesRequest / CountResponsesItem / CountResponsesPart / CountResponsesTool (+JSON)
The *JSON request entry points decode once and then count + linearize the prefix blocks in a single fused walk shared with the KV cache, so gateway calls pay one parse instead of two.
KV-cache simulation (KvLookup / KvInit / KvStatsSnapshot / KvClear): a high-performance prefix-cache simulator for gateways. Input is the raw request payload + protocol + namespace ("provider|model|api-key", assembled by the caller); output is cached/fresh/written token counts for billing cached input even when the provider does not report it. One prefix trie per protocol+namespace (or namespace only with KvSetSeparateProtocol(false)), 10-minute sliding TTL and a memory cap (default 400MB) with automatic LRU eviction — all tunable via KvInit. Token totals stay in stats for observability only.
Text is counted with the real BPE merge table; images follow the standard 28px-tile visual-token model. Pass Options{IgnoreImages: true} to count images as zero. Encrypted thinking (redacted_thinking, encrypted_content, thought_signature — whatever the provider or model hides inside any protocol) is detected and estimated automatically by EstimateThinkingTokens; there is nothing to configure.
Index ¶
- func CountAnthropicTool(tool AnthropicTool) (int, error)
- func CountAnthropicToolJSON(raw []byte) (int, error)
- func CountChatTool(tool ChatTool) (int, error)
- func CountChatToolJSON(raw []byte) (int, error)
- func CountImageBase64(s string) int
- func CountImageBytes(b []byte) int
- func CountImageSize(width, height int) int
- func CountResponsesTool(tool ResponsesTool) (int, error)
- func CountResponsesToolJSON(raw []byte) (int, error)
- func CountText(text string) (int, error)
- func EstimateThinkingTokens(enc string) int
- func KvClear(namespace string)
- func KvClearProtocol(protocol, namespace string)
- func KvInit(ttlSeconds int64, maxMB int64)
- func KvSetSeparateProtocol(separate bool)
- type AnthropicMessage
- type AnthropicRequest
- type AnthropicTool
- type BlockBreakdown
- func CountAnthropicBlock(block map[string]any, opts Options) (BlockBreakdown, error)
- func CountAnthropicBlockJSON(raw []byte, opts Options) (BlockBreakdown, error)
- func CountAnthropicMessage(msg AnthropicMessage, opts Options) (BlockBreakdown, error)
- func CountAnthropicMessageJSON(raw []byte, opts Options) (BlockBreakdown, error)
- func CountChatMessage(msg ChatMessage, opts Options) (BlockBreakdown, error)
- func CountChatMessageJSON(raw []byte, opts Options) (BlockBreakdown, error)
- func CountChatPart(part map[string]any, opts Options) (BlockBreakdown, error)
- func CountChatPartJSON(raw []byte, opts Options) (BlockBreakdown, error)
- func CountResponsesItem(item map[string]any, opts Options) (BlockBreakdown, error)
- func CountResponsesItemJSON(raw []byte, opts Options) (BlockBreakdown, error)
- func CountResponsesPart(part map[string]any, opts Options) (BlockBreakdown, error)
- func CountResponsesPartJSON(raw []byte, opts Options) (BlockBreakdown, error)
- type Breakdown
- func CountAnthropicRequest(req AnthropicRequest, opts Options) (Breakdown, error)
- func CountAnthropicRequestJSON(raw []byte, opts Options) (Breakdown, error)
- func CountChatRequest(req ChatRequest, opts Options) (Breakdown, error)
- func CountChatRequestJSON(raw []byte, opts Options) (Breakdown, error)
- func CountResponsesRequest(req ResponsesRequest, opts Options) (Breakdown, error)
- func CountResponsesRequestJSON(raw []byte, opts Options) (Breakdown, error)
- type ChatFunction
- type ChatFunctionDef
- type ChatMessage
- type ChatRequest
- type ChatTool
- type ChatToolCall
- type KvBlock
- type KvConfig
- type KvResult
- type KvStats
- type Options
- type ResponsesRequest
- type ResponsesTool
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CountAnthropicTool ¶
func CountAnthropicTool(tool AnthropicTool) (int, error)
CountAnthropicTool counts a tool definition.
func CountAnthropicToolJSON ¶
CountAnthropicToolJSON counts a tool definition from raw JSON.
func CountChatTool ¶
CountChatTool counts a tool definition.
func CountChatToolJSON ¶
CountChatToolJSON counts a tool definition from raw JSON.
func CountImageBase64 ¶
CountImageBase64 strips an optional data: prefix, base64-decodes, and counts visual tokens; 0 on error.
func CountImageBytes ¶
CountImageBytes decodes image bytes (PNG/JPEG/GIF) and counts visual tokens; 0 if undecodable.
func CountImageSize ¶
CountImageSize returns visual tokens for a W×H image (1568px fit, 28px tiles).
func CountResponsesTool ¶
func CountResponsesTool(tool ResponsesTool) (int, error)
CountResponsesTool counts a tool definition.
func CountResponsesToolJSON ¶
CountResponsesToolJSON counts a tool definition from raw JSON.
func EstimateThinkingTokens ¶
EstimateThinkingTokens estimates billed thinking tokens hidden behind an opaque encrypted payload (redacted_thinking, encrypted_content, thought_signature). Provider-agnostic: unwraps the base64 envelope to raw bytes and maps per envelope family. Empty input returns 0.
func KvClear ¶
func KvClear(namespace string)
KvClear removes one namespace, or everything when namespace == "". With protocol separation on (default), pass the plain namespace and all its protocol scopes are cleared; use KvClearProtocol for a single protocol scope.
func KvClearProtocol ¶
func KvClearProtocol(protocol, namespace string)
KvClearProtocol removes a single protocol scope of a namespace.
func KvInit ¶
KvInit configures the cache: ttlSeconds (default 600 = 10min) and maxMB (default 400). Call once at startup; zero/negative keeps the default for that field. It also clears existing entries, since TTL and size class are global.
func KvSetSeparateProtocol ¶
func KvSetSeparateProtocol(separate bool)
KvSetSeparateProtocol toggles protocol-scoped keying. Default true: protocol + namespace form the key. When false, only namespace keys the entries (same hash in different protocols shares the prefix).
Types ¶
type AnthropicMessage ¶
type AnthropicRequest ¶
type AnthropicRequest struct {
System any `json:"system,omitempty"`
Messages []AnthropicMessage `json:"messages"`
Tools []AnthropicTool `json:"tools,omitempty"`
}
type AnthropicTool ¶
type BlockBreakdown ¶
type BlockBreakdown struct {
Tokens int `json:"tokens"`
Images int `json:"images"`
TextTokens int `json:"text_tokens"`
ImageCount int `json:"image_count"`
}
func CountAnthropicBlock ¶
func CountAnthropicBlock(block map[string]any, opts Options) (BlockBreakdown, error)
CountAnthropicBlock counts a single content block.
func CountAnthropicBlockJSON ¶
func CountAnthropicBlockJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountAnthropicBlockJSON counts a single content block from raw JSON.
func CountAnthropicMessage ¶
func CountAnthropicMessage(msg AnthropicMessage, opts Options) (BlockBreakdown, error)
CountAnthropicMessage counts a single message.
func CountAnthropicMessageJSON ¶
func CountAnthropicMessageJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountAnthropicMessageJSON counts a single message from raw JSON.
func CountChatMessage ¶
func CountChatMessage(msg ChatMessage, opts Options) (BlockBreakdown, error)
CountChatMessage counts a single message.
func CountChatMessageJSON ¶
func CountChatMessageJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountChatMessageJSON counts a single message from raw JSON.
func CountChatPart ¶
func CountChatPart(part map[string]any, opts Options) (BlockBreakdown, error)
CountChatPart counts a single content part.
func CountChatPartJSON ¶
func CountChatPartJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountChatPartJSON counts a single content part from raw JSON.
func CountResponsesItem ¶
func CountResponsesItem(item map[string]any, opts Options) (BlockBreakdown, error)
CountResponsesItem counts a single input item.
func CountResponsesItemJSON ¶
func CountResponsesItemJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountResponsesItemJSON counts a single input item from raw JSON.
func CountResponsesPart ¶
func CountResponsesPart(part map[string]any, opts Options) (BlockBreakdown, error)
CountResponsesPart counts a single content part.
func CountResponsesPartJSON ¶
func CountResponsesPartJSON(raw []byte, opts Options) (BlockBreakdown, error)
CountResponsesPartJSON counts a single content part from raw JSON.
type Breakdown ¶
type Breakdown struct {
System int `json:"system"`
Messages int `json:"messages"`
Tools int `json:"tools"`
Images int `json:"images"`
ByMessage []int `json:"by_message,omitempty"`
ByTool []int `json:"by_tool,omitempty"`
TextTokens int `json:"text_tokens"`
Total int `json:"total"`
ImageCount int `json:"image_count"`
}
func CountAnthropicRequest ¶
func CountAnthropicRequest(req AnthropicRequest, opts Options) (Breakdown, error)
CountAnthropicRequest counts a full Anthropic Messages request.
func CountAnthropicRequestJSON ¶
CountAnthropicRequestJSON counts a full request from raw JSON. The request is decoded once and then counted + linearized in a single walk (kvCountAnthropicRequest) shared with the KV cache path.
func CountChatRequest ¶
func CountChatRequest(req ChatRequest, opts Options) (Breakdown, error)
CountChatRequest counts a full Chat Completions request. NOTE: chat hot path is fused in kvCountChatRequest (single walk); this stays for struct-based callers.
func CountChatRequestJSON ¶
CountChatRequestJSON counts a full request from raw JSON. The request is decoded once and then counted + linearized in a single walk (kvCountChatRequest) shared with the KV cache path: KvLookup reuses this so a gateway call pays one parse, not two.
func CountResponsesRequest ¶
func CountResponsesRequest(req ResponsesRequest, opts Options) (Breakdown, error)
CountResponsesRequest counts a full Responses request.
func CountResponsesRequestJSON ¶
CountResponsesRequestJSON counts a full request from raw JSON. The request is decoded once and then counted + linearized in a single walk (kvCountResponsesRequest) shared with the KV cache path.
type ChatFunction ¶
type ChatFunctionDef ¶
type ChatMessage ¶
type ChatMessage struct {
Role string `json:"role"`
Content any `json:"content,omitempty"`
ToolCalls []ChatToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
}
type ChatRequest ¶
type ChatRequest struct {
System any `json:"system,omitempty"`
Messages []ChatMessage `json:"messages"`
Tools []ChatTool `json:"tools,omitempty"`
}
type ChatTool ¶
type ChatTool struct {
Type string `json:"type,omitempty"`
Function ChatFunctionDef `json:"function"`
}
type ChatToolCall ¶
type ChatToolCall struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Function ChatFunction `json:"function"`
}
type KvBlock ¶
KvBlock is one canonical position of the prefix: the FNV-1a hash identifying the block content plus its content tokens (no safety margin — margin is folded back proportionally at the end so that total == cached + fresh always holds).
type KvConfig ¶
type KvConfig struct {
TTLSeconds int64 `json:"ttl_seconds"`
MaxMB int64 `json:"max_mb"`
SeparateProtocol bool `json:"separate_protocol"`
}
KvConfig reports the effective tunables (for kvInit echo / stats).
func KvConfigSnapshot ¶
func KvConfigSnapshot() KvConfig
KvConfigSnapshot returns the effective config.
type KvResult ¶
type KvResult struct {
Total int `json:"total"`
Cached int `json:"cached"`
Fresh int `json:"fresh"`
Written int `json:"written"`
Hit bool `json:"hit"`
HitRatio float64 `json:"hit_ratio"`
PrefixBlocks int `json:"prefix_blocks"`
TotalBlocks int `json:"total_blocks"`
Breakdown Breakdown `json:"breakdown"`
}
KvResult is the per-request simulation outcome.
func KvLookup ¶
KvLookup is the atomic check+update: it walks the namespace trie with the linearized blocks, reports cached/fresh, then commits the miss suffix as a new branch so the next request can hit it. Old branches stay alive until TTL/eviction — N conversations can coexist under one namespace.
type KvStats ¶
type KvStats struct {
Namespaces int `json:"namespaces"`
Nodes int64 `json:"nodes"`
Branches int64 `json:"branches"`
Tokens int64 `json:"tokens"`
Bytes int64 `json:"bytes"`
MaxBytes int64 `json:"max_bytes"`
// AvailableBytes is MaxBytes - Bytes (clamped at zero): how much
// cache memory is still free before LRU eviction kicks in.
AvailableBytes int64 `json:"available_bytes"`
// TTLSeconds is the configured sliding TTL (KvInit, default 600).
TTLSeconds int64 `json:"ttl_seconds"`
}
KvStats is a global introspection snapshot for the gateway.
func KvStatsSnapshot ¶
func KvStatsSnapshot() KvStats
KvStatsSnapshot returns a global snapshot (nodes/tokens/bytes are tracked incrementally; branches are counted by traversal as leaf nodes). It also runs the opportunistic maintenance (expiry sweep + memory-cap eviction), so a 1-minute polling loop doubles as the janitor: idle namespaces are collected even without traffic on them.
type Options ¶
type Options struct {
IgnoreImages bool
// Tight disables the built-in safety margin and returns the
// closest point estimate. Default (false) keeps the margin.
Tight bool
}
Options tunes counting. IgnoreImages counts images as zero.
Total always includes a small generic safety margin on top of the content count, so the default estimate lands at or above the provider-billed total in the common cases — install-and-use safe for budget enforcement and automatic compaction (compacting on Total fires before the real context fills up instead of after). TextTokens keeps the pure content count; set Tight: true to get the closest point estimate in Total as well (display/cost paths).
type ResponsesRequest ¶
type ResponsesRequest struct {
Instructions any `json:"instructions,omitempty"`
Input any `json:"input,omitempty"`
Tools []ResponsesTool `json:"tools,omitempty"`
}