Skip to content

Commit 2652e23

Browse files
committed
feat: add token usage tracking and configuration options
- Updated `wecoding-sdk-go` dependency from version `0.8.8` to `0.8.10`. - Removed unused `json` import and simplified completion output handling. - Added `Usage` field to `CompletionOutput` and `StreamCompletionOutput` types. - Introduced `ShowTokenUsages` configuration option to display token usage in responses. - Added `TokenUsage` field to the `Chat` struct to track token usage. - Refactored `saveConversation` method to include token usage details in the output. - Added markdown rendering for conversation save messages. Signed-off-by: codiing-hui <wecoding@yeah.net>
1 parent e366f29 commit 2652e23

File tree

7 files changed

+81
-54
lines changed

7 files changed

+81
-54
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ require (
2121
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0
2222
github.com/coding-hui/common v0.8.7
2323
github.com/coding-hui/go-prompt v0.2.8
24-
github.com/coding-hui/wecoding-sdk-go v0.8.8
24+
github.com/coding-hui/wecoding-sdk-go v0.8.10
2525
github.com/erikgeiser/promptkit v0.9.0
2626
github.com/fatih/color v1.18.0
2727
github.com/ghodss/yaml v1.0.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ github.com/coding-hui/common v0.8.7 h1:f9iHZcdQLgRFW/nIJHVfBINuzzCKsUcwVIhfboxXe
8181
github.com/coding-hui/common v0.8.7/go.mod h1:KaNHOP9IYLgHzouuvR8/IijT4tdFyKMzB5ghI+ShtIM=
8282
github.com/coding-hui/go-prompt v0.2.8 h1:SjoClQQmsRxytxn2o3OaUB4V/PNOk8NPjPHpqNss1GQ=
8383
github.com/coding-hui/go-prompt v0.2.8/go.mod h1:ZZeEnbY5BcUJ7xBpRM2iPp8GJr5+lxRU4l0Tcd8bvW0=
84-
github.com/coding-hui/wecoding-sdk-go v0.8.8 h1:fSR4c+05tfn0bfbZG2SKKxTcocBGirnTWF6K96nCCKk=
85-
github.com/coding-hui/wecoding-sdk-go v0.8.8/go.mod h1:nWank9mlVeuHG2UEhHo/HwGr0p/ZnH4/3a5mjpLOOBw=
84+
github.com/coding-hui/wecoding-sdk-go v0.8.10 h1:u3TrDFJLthnC9IIviUSRc5uRKst1fw9LRXLmZ/gVEDg=
85+
github.com/coding-hui/wecoding-sdk-go v0.8.10/go.mod h1:nWank9mlVeuHG2UEhHo/HwGr0p/ZnH4/3a5mjpLOOBw=
8686
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
8787
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
8888
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=

internal/ai/ai.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ai
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76
"strings"
87

@@ -77,17 +76,14 @@ func (e *Engine) CreateCompletion(ctx context.Context, messages []llms.ChatMessa
7776

7877
e.appendAssistantMessage(content)
7978

80-
var output CompletionOutput
81-
err = json.Unmarshal([]byte(content), &output)
82-
if err != nil {
83-
output = CompletionOutput{
84-
Command: "",
85-
Explanation: content,
86-
Executable: false,
87-
}
88-
}
79+
e.running = false
8980

90-
return &output, nil
81+
return &CompletionOutput{
82+
Command: "",
83+
Explanation: content,
84+
Executable: false,
85+
Usage: rsp.Usage,
86+
}, nil
9187
}
9288

9389
func (e *Engine) CreateStreamCompletion(ctx context.Context, messages []llms.ChatMessage) (*StreamCompletionOutput, error) {
@@ -135,6 +131,7 @@ func (e *Engine) CreateStreamCompletion(ctx context.Context, messages []llms.Cha
135131
Content: "",
136132
Last: true,
137133
Executable: executable,
134+
Usage: rsp.Usage,
138135
}
139136
}
140137
e.running = false
@@ -145,6 +142,7 @@ func (e *Engine) CreateStreamCompletion(ctx context.Context, messages []llms.Cha
145142
Content: output,
146143
Last: true,
147144
Executable: executable,
145+
Usage: rsp.Usage,
148146
}, nil
149147
}
150148

internal/ai/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ type CompletionOutput struct {
2121
Command string `json:"cmd"`
2222
Explanation string `json:"exp"`
2323
Executable bool `json:"exec"`
24+
25+
Usage llms.Usage `json:"usage"`
2426
}
2527

2628
func (c CompletionOutput) GetCommand() string {
@@ -46,6 +48,8 @@ type StreamCompletionOutput struct {
4648
Last bool
4749
Interrupt bool
4850
Executable bool
51+
52+
Usage llms.Usage `json:"usage"`
4953
}
5054

5155
func (c StreamCompletionOutput) GetContent() string {
@@ -63,3 +67,7 @@ func (c StreamCompletionOutput) IsInterrupt() bool {
6367
func (c StreamCompletionOutput) IsExecutable() bool {
6468
return c.Executable
6569
}
70+
71+
func (c StreamCompletionOutput) GetUsage() llms.Usage {
72+
return c.Usage
73+
}

internal/options/config.go

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,34 +80,36 @@ var Help = map[string]string{
8080
"datastore": "Configure the datastore to use.",
8181
"auto-coder": "Configure the auto coder to use.",
8282
"auto-commit": "Automatically commit code changes after generation.",
83+
"show-token-usage": "Show token usage in the response.",
8384
"verbose": "Verbose mode. 0: no verbose, 1: debug verbose",
8485
}
8586

8687
// Config is a structure used to configure a AI.
8788
// Its members are sorted roughly in order of importance for composers.
8889
type Config struct {
89-
Model string `yaml:"default-model" env:"MODEL"`
90-
API string `yaml:"default-api" env:"API"`
91-
Raw bool `yaml:"raw" env:"RAW"`
92-
Quiet bool `yaml:"quiet" env:"QUIET"`
93-
MaxTokens int `yaml:"max-tokens" env:"MAX_TOKENS"`
94-
MaxInputChars int `yaml:"max-input-chars" env:"MAX_INPUT_CHARS"`
95-
Temperature float64 `yaml:"temp" env:"TEMP"`
96-
Stop []string `yaml:"stop" env:"STOP"`
97-
TopP float64 `yaml:"topp" env:"TOPP"`
98-
TopK int `yaml:"topk" env:"TOPK"`
99-
NoLimit bool `yaml:"no-limit" env:"NO_LIMIT"`
100-
NoCache bool `yaml:"no-cache" env:"NO_CACHE"`
101-
MaxRetries int `yaml:"max-retries" env:"MAX_RETRIES"`
102-
WordWrap int `yaml:"word-wrap" env:"WORD_WRAP"`
103-
Fanciness uint `yaml:"fanciness" env:"FANCINESS"`
104-
LoadingText string `yaml:"loading-text" env:"LOADING_TEXT"`
105-
FormatText FormatText `yaml:"format-text"`
106-
FormatAs string `yaml:"format-as" env:"FORMAT_AS"`
107-
Verbose int `yaml:"verbose" env:"VERBOSE"`
108-
APIs APIs `yaml:"apis"`
109-
DataStore DataStore `yaml:"datastore"`
110-
AutoCoder AutoCoder `yaml:"auto-coder"`
90+
Model string `yaml:"default-model" env:"MODEL"`
91+
API string `yaml:"default-api" env:"API"`
92+
Raw bool `yaml:"raw" env:"RAW"`
93+
Quiet bool `yaml:"quiet" env:"QUIET"`
94+
MaxTokens int `yaml:"max-tokens" env:"MAX_TOKENS"`
95+
MaxInputChars int `yaml:"max-input-chars" env:"MAX_INPUT_CHARS"`
96+
Temperature float64 `yaml:"temp" env:"TEMP"`
97+
Stop []string `yaml:"stop" env:"STOP"`
98+
TopP float64 `yaml:"topp" env:"TOPP"`
99+
TopK int `yaml:"topk" env:"TOPK"`
100+
NoLimit bool `yaml:"no-limit" env:"NO_LIMIT"`
101+
NoCache bool `yaml:"no-cache" env:"NO_CACHE"`
102+
MaxRetries int `yaml:"max-retries" env:"MAX_RETRIES"`
103+
WordWrap int `yaml:"word-wrap" env:"WORD_WRAP"`
104+
Fanciness uint `yaml:"fanciness" env:"FANCINESS"`
105+
LoadingText string `yaml:"loading-text" env:"LOADING_TEXT"`
106+
FormatText FormatText `yaml:"format-text"`
107+
FormatAs string `yaml:"format-as" env:"FORMAT_AS"`
108+
Verbose int `yaml:"verbose" env:"VERBOSE"`
109+
APIs APIs `yaml:"apis"`
110+
DataStore DataStore `yaml:"datastore"`
111+
AutoCoder AutoCoder `yaml:"auto-coder"`
112+
ShowTokenUsages bool `yaml:"show-token-usage" env:"SHOW_TOKEN_USAGES"`
111113

112114
DefaultPromptMode string `yaml:"default-prompt-mode,omitempty"`
113115
ConversationID string `yaml:"convo-id,omitempty"`

internal/options/config_template.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ loading-text: Generating
4747
theme: charm
4848
# {{ index .Help "max-input-chars" }}
4949
max-input-chars: 12250
50+
# {{ index .Help "show-token-usage" }}
51+
show-token-usage: true
5052
# {{ index .Help "max-tokens" }}
5153
# max-tokens: 100
5254
# {{ index .Help "datastore" }}

internal/ui/chat/chat.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Chat struct {
3838
Error *errbook.AiError
3939
Output string
4040
GlamOutput string
41+
TokenUsage llms.Usage
4142

4243
state state
4344
opts *Options
@@ -82,7 +83,7 @@ func NewChat(cfg *options.Config, opts ...Option) *Chat {
8283
}
8384
}
8485

85-
// Run starts the chat.
86+
// Run starts the c.
8687
func (c *Chat) Run() error {
8788
if _, err := tea.NewProgram(c).Run(); err != nil {
8889
return errbook.Wrap("Couldn't start Bubble Tea program.", err)
@@ -110,7 +111,7 @@ func (c *Chat) Run() error {
110111
}
111112

112113
if c.config.CacheWriteToID != "" {
113-
return saveConversation(c)
114+
return c.saveConversation()
114115
}
115116

116117
return nil
@@ -158,6 +159,7 @@ func (c *Chat) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
158159
}
159160
if msg.IsLast() {
160161
c.state = doneState
162+
c.TokenUsage = msg.GetUsage()
161163
return c, c.quit
162164
}
163165
cmds = append(cmds, c.awaitChatCompletedCmd())
@@ -330,9 +332,20 @@ func (c *Chat) readStdinCmd() tea.Msg {
330332
}
331333
}
332334

333-
func saveConversation(chat *Chat) error {
334-
if chat.config.NoCache {
335-
if !chat.config.Quiet {
335+
func (c *Chat) renderMarkdown(raw string) string {
336+
if c.config.Raw {
337+
return raw
338+
}
339+
rendered, err := c.glam.Render(raw)
340+
if err != nil {
341+
return raw
342+
}
343+
return rendered
344+
}
345+
346+
func (c *Chat) saveConversation() error {
347+
if c.config.NoCache {
348+
if !c.config.Quiet {
336349
fmt.Fprintf(
337350
os.Stderr,
338351
"\nConversation was not saved because %s or %s is set.\n",
@@ -344,9 +357,9 @@ func saveConversation(chat *Chat) error {
344357
}
345358

346359
ctx := context.Background()
347-
convoStore := chat.engine.GetConvoStore()
348-
writeToID := chat.config.CacheWriteToID
349-
writeToTitle := strings.TrimSpace(chat.config.CacheWriteToTitle)
360+
convoStore := c.engine.GetConvoStore()
361+
writeToID := c.config.CacheWriteToID
362+
writeToTitle := strings.TrimSpace(c.config.CacheWriteToTitle)
350363

351364
if convo.MatchSha1(writeToTitle) || writeToTitle == "" {
352365
messages, err := convoStore.Messages(ctx, writeToID)
@@ -363,28 +376,32 @@ func saveConversation(chat *Chat) error {
363376
if err := convoStore.PersistentMessages(ctx, writeToID); err != nil {
364377
return errbook.Wrap(fmt.Sprintf(
365378
"There was a problem writing %s to the cache. Use %s / %s to disable it.",
366-
chat.config.CacheWriteToID,
379+
c.config.CacheWriteToID,
367380
console.StderrStyles().InlineCode.Render("--no-cache"),
368381
console.StderrStyles().InlineCode.Render("NO_CACHE"),
369382
), err)
370383
}
371384

372-
if err := convoStore.SaveConversation(ctx, writeToID, writeToTitle, chat.config.Model); err != nil {
385+
if err := convoStore.SaveConversation(ctx, writeToID, writeToTitle, c.config.Model); err != nil {
373386
return errbook.Wrap(fmt.Sprintf(
374387
"There was a problem writing %s to the cache. Use %s / %s to disable it.",
375-
chat.config.CacheWriteToID,
388+
c.config.CacheWriteToID,
376389
console.StderrStyles().InlineCode.Render("--no-cache"),
377390
console.StderrStyles().InlineCode.Render("NO_CACHE"),
378391
), err)
379392
}
380393

381-
if !chat.config.Quiet {
382-
fmt.Fprintln(
383-
os.Stderr,
384-
"\nConversation saved:",
385-
console.StderrStyles().InlineCode.Render(chat.config.CacheWriteToID[:convo.Sha1short]),
386-
console.StderrStyles().Comment.Render(writeToTitle),
387-
)
394+
if !c.config.Quiet {
395+
content := fmt.Sprintf("\n**Conversation successfully saved:** `%s` `%s`\n", c.config.CacheWriteToID[:convo.Sha1short], writeToTitle)
396+
if c.config.ShowTokenUsages {
397+
content += fmt.Sprintf("\nFirst Token: `%.3fs` | Avg: `%.3f/s` | Total: `%.3fs` | Tokens: `%d`",
398+
c.TokenUsage.FirstTokenTime.Seconds(),
399+
c.TokenUsage.AverageTokensPerSecond,
400+
c.TokenUsage.TotalTime.Seconds(),
401+
c.TokenUsage.TotalTokens,
402+
)
403+
}
404+
_, _ = fmt.Fprint(os.Stderr, c.renderMarkdown(content))
388405
}
389406

390407
return nil

0 commit comments

Comments
 (0)