11package tokens
22
3- import (
4- "context"
5- )
6-
7- // Message represents a message for token counting
8- type Message struct {
9- Role string `json:"role"`
10- Content string `json:"content"`
11- }
12-
13- // TokenCount represents the result of token counting
14- type TokenCount struct {
15- InputTokens int `json:"input_tokens"`
16- }
17-
18- // TokenCounter interface for provider-specific token counting
19- type TokenCounter interface {
20- // CountTokens counts tokens for the given messages and model
21- CountTokens (ctx context.Context , messages []Message , model string ) (* TokenCount , error )
22- // SupportsModel returns true if this counter supports the given model
23- SupportsModel (model string ) bool
24- // ProviderName returns the name of the provider this counter is for
25- ProviderName () string
26- }
27-
283// EstimateTokens provides a rough estimate of tokens in text
29- // This is a fallback when no provider-specific counter is available
304func EstimateTokens (text string ) int {
315 // Rough approximation: ~4 characters per token for most models
32- // This is not accurate but gives a reasonable estimate
336 return len (text ) / 4
34- }
35-
36- // EstimateTokensFromMessages estimates tokens from a slice of messages
37- func EstimateTokensFromMessages (messages []Message ) int {
38- totalChars := 0
39- for _ , msg := range messages {
40- totalChars += len (msg .Content )
41- totalChars += len (msg .Role ) + 10 // Add some overhead for role and formatting
42- }
43- return EstimateTokens (string (rune (totalChars )))
44- }
45-
46- // Registry holds all registered token counters
47- type Registry struct {
48- counters map [string ]TokenCounter
49- }
50-
51- // NewRegistry creates a new token counter registry
52- func NewRegistry () * Registry {
53- return & Registry {
54- counters : make (map [string ]TokenCounter ),
55- }
56- }
57-
58- // Register adds a token counter to the registry
59- func (r * Registry ) Register (counter TokenCounter ) {
60- r .counters [counter .ProviderName ()] = counter
61- }
62-
63- // GetCounter returns a token counter for the given provider
64- func (r * Registry ) GetCounter (provider string ) (TokenCounter , bool ) {
65- counter , exists := r .counters [provider ]
66- return counter , exists
67- }
68-
69- // CountTokens attempts to count tokens using a provider-specific counter,
70- // falling back to estimation if no counter is available
71- func (r * Registry ) CountTokens (ctx context.Context , provider string , messages []Message , model string ) (* TokenCount , error ) {
72- if counter , exists := r .GetCounter (provider ); exists && counter .SupportsModel (model ) {
73- return counter .CountTokens (ctx , messages , model )
74- }
75-
76- // Fallback to estimation
77- estimatedTokens := EstimateTokensFromMessages (messages )
78- return & TokenCount {
79- InputTokens : estimatedTokens ,
80- }, nil
81- }
82-
83- // Global registry instance
84- var globalRegistry = NewRegistry ()
85-
86- // GetGlobalRegistry returns the global token counter registry
87- func GetGlobalRegistry () * Registry {
88- return globalRegistry
89- }
90-
91- // RegisterCounter registers a token counter with the global registry
92- func RegisterCounter (counter TokenCounter ) {
93- globalRegistry .Register (counter )
94- }
95-
96- // CountTokensGlobal counts tokens using the global registry
97- func CountTokensGlobal (ctx context.Context , provider string , messages []Message , model string ) (* TokenCount , error ) {
98- return globalRegistry .CountTokens (ctx , provider , messages , model )
997}
0 commit comments