summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrail Finder <wohilas@gmail.com>2026-03-02 14:54:20 +0300
committerGrail Finder <wohilas@gmail.com>2026-03-02 14:54:20 +0300
commitfcc71987bfbad0c3a16a6bc509a206bd995e2a2f (patch)
treea1b8844852044a63819e1eb656f596cdff2598b5
parent8458edf5a874903a108f361a4a4795a445254061 (diff)
Feat: token use estimation
-rw-r--r--bot.go6
-rw-r--r--helpfuncs.go75
2 files changed, 80 insertions, 1 deletions
diff --git a/bot.go b/bot.go
index 5d9cf03..5ae215f 100644
--- a/bot.go
+++ b/bot.go
@@ -63,7 +63,9 @@ var (
"google/gemma-3-27b-it:free",
"meta-llama/llama-3.3-70b-instruct:free",
}
- LocalModels = []string{}
+ LocalModels = []string{}
+ localModelsData *models.LCPModels
+ orModelsData *models.ORModels
)
var thinkBlockRE = regexp.MustCompile(`(?s)<think>.*?</think>`)
@@ -355,6 +357,7 @@ func fetchORModels(free bool) ([]string, error) {
if err := json.NewDecoder(resp.Body).Decode(data); err != nil {
return nil, err
}
+ orModelsData = data
freeModels := data.ListModels(free)
return freeModels, nil
}
@@ -416,6 +419,7 @@ func fetchLCPModelsWithStatus() (*models.LCPModels, error) {
if err := json.NewDecoder(resp.Body).Decode(data); err != nil {
return nil, err
}
+ localModelsData = data
return data, nil
}
diff --git a/helpfuncs.go b/helpfuncs.go
index b63995c..7d7b65b 100644
--- a/helpfuncs.go
+++ b/helpfuncs.go
@@ -11,6 +11,7 @@ import (
"path"
"path/filepath"
"slices"
+ "strconv"
"strings"
"time"
"unicode"
@@ -376,9 +377,83 @@ func makeStatusLine() string {
roleInject := fmt.Sprintf(" | [%s:-:b]role injection[-:-:-] (alt+7)", boolColors[injectRole])
statusLine += roleInject
}
+ // context tokens
+ contextTokens := getContextTokens()
+ maxCtx := getMaxContextTokens()
+ if maxCtx == 0 {
+ maxCtx = 16384
+ }
+ if contextTokens > 0 {
+ contextInfo := fmt.Sprintf(" | context: [cyan:-:b]%d/%d[-:-:-]", contextTokens, maxCtx)
+ statusLine += contextInfo
+ }
return statusLine + imageInfo + shellModeInfo
}
+func getContextTokens() int {
+ if chatBody == nil || chatBody.Messages == nil {
+ return 0
+ }
+ total := 0
+ for _, msg := range chatBody.Messages {
+ if msg.Stats != nil {
+ total += msg.Stats.Tokens
+ }
+ }
+ return total
+}
+
+const deepseekContext = 128000
+
+func getMaxContextTokens() int {
+ if chatBody == nil || chatBody.Model == "" {
+ return 0
+ }
+ modelName := chatBody.Model
+ if strings.Contains(cfg.CurrentAPI, "openrouter") {
+ if orModelsData != nil {
+ for _, m := range orModelsData.Data {
+ if m.ID == modelName {
+ return m.ContextLength
+ }
+ }
+ }
+ } else if strings.Contains(cfg.CurrentAPI, "deepseek") {
+ return deepseekContext
+ } else {
+ if localModelsData != nil {
+ for _, m := range localModelsData.Data {
+ if m.ID == modelName {
+ for _, arg := range m.Status.Args {
+ if strings.HasPrefix(arg, "--ctx-size") {
+ if strings.Contains(arg, "=") {
+ val := strings.Split(arg, "=")[1]
+ if n, err := strconv.Atoi(val); err == nil {
+ return n
+ }
+ } else {
+ idx := -1
+ for i, a := range m.Status.Args {
+ if a == "--ctx-size" && i+1 < len(m.Status.Args) {
+ idx = i + 1
+ break
+ }
+ }
+ if idx != -1 {
+ if n, err := strconv.Atoi(m.Status.Args[idx]); err == nil {
+ return n
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0
+}
+
// set of roles within card definition and mention in chat history
func listChatRoles() []string {
currentChat, ok := chatMap[activeChatName]