Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.

Commit f5b50e4

Browse files
committed
feat(run/chat): display token usage
Signed-off-by: Dorin Geman <[email protected]>
1 parent 84afdb4 commit f5b50e4

File tree

4 files changed

+36
-7
lines changed

4 files changed

+36
-7
lines changed

commands/run.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func chatWithMarkdown(cmd *cobra.Command, client *desktop.Client, backend, model
271271
// Simple case: just stream as plain text
272272
return client.Chat(backend, model, prompt, apiKey, func(content string) {
273273
cmd.Print(content)
274-
})
274+
}, false)
275275
}
276276

277277
// For markdown: use streaming buffer to render code blocks as they complete
@@ -289,7 +289,7 @@ func chatWithMarkdown(cmd *cobra.Command, client *desktop.Client, backend, model
289289
} else if rendered != "" {
290290
cmd.Print(rendered)
291291
}
292-
})
292+
}, true)
293293
if err != nil {
294294
return err
295295
}

desktop/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,9 @@ type OpenAIChatResponse struct {
4040
Index int `json:"index"`
4141
FinishReason string `json:"finish_reason"`
4242
} `json:"choices"`
43+
Usage *struct {
44+
CompletionTokens int `json:"completion_tokens"`
45+
PromptTokens int `json:"prompt_tokens"`
46+
TotalTokens int `json:"total_tokens"`
47+
} `json:"usage,omitempty"`
4348
}

desktop/desktop.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ func (c *Client) fullModelID(id string) (string, error) {
365365
}
366366

367367
// Chat performs a chat request and streams the response content with selective markdown rendering.
368-
func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(string)) error {
368+
func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(string), shouldUseMarkdown bool) error {
369369
model = normalizeHuggingFaceModelName(model)
370370
if !strings.Contains(strings.Trim(model, "/"), "/") {
371371
// Do an extra API call to check if the model parameter isn't a model ID.
@@ -422,7 +422,14 @@ func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(str
422422
)
423423

424424
printerState := chatPrinterNone
425-
reasoningFmt := color.New(color.FgWhite).Add(color.Italic)
425+
reasoningFmt := color.New().Add(color.Italic)
426+
427+
var finalUsage *struct {
428+
CompletionTokens int `json:"completion_tokens"`
429+
PromptTokens int `json:"prompt_tokens"`
430+
TotalTokens int `json:"total_tokens"`
431+
}
432+
426433
scanner := bufio.NewScanner(resp.Body)
427434
for scanner.Scan() {
428435
line := scanner.Text()
@@ -445,6 +452,10 @@ func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(str
445452
return fmt.Errorf("error parsing stream response: %w", err)
446453
}
447454

455+
if streamResp.Usage != nil {
456+
finalUsage = streamResp.Usage
457+
}
458+
448459
if len(streamResp.Choices) > 0 {
449460
if streamResp.Choices[0].Delta.ReasoningContent != "" {
450461
chunk := streamResp.Choices[0].Delta.ReasoningContent
@@ -454,14 +465,14 @@ func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(str
454465
if printerState != chatPrinterReasoning {
455466
const thinkingHeader = "Thinking:\n"
456467
if reasoningFmt != nil {
457-
outputFunc(reasoningFmt.Sprint(thinkingHeader))
468+
reasoningFmt.Print(thinkingHeader)
458469
} else {
459470
outputFunc(thinkingHeader)
460471
}
461472
}
462473
printerState = chatPrinterReasoning
463474
if reasoningFmt != nil {
464-
outputFunc(reasoningFmt.Sprint(chunk))
475+
reasoningFmt.Print(chunk)
465476
} else {
466477
outputFunc(chunk)
467478
}
@@ -481,6 +492,19 @@ func (c *Client) Chat(backend, model, prompt, apiKey string, outputFunc func(str
481492
return fmt.Errorf("error reading response stream: %w", err)
482493
}
483494

495+
if finalUsage != nil {
496+
usageInfo := fmt.Sprintf("\n\nToken usage: %d prompt + %d completion = %d total",
497+
finalUsage.PromptTokens,
498+
finalUsage.CompletionTokens,
499+
finalUsage.TotalTokens)
500+
501+
usageFmt := color.New(color.FgHiBlack)
502+
if !shouldUseMarkdown {
503+
usageFmt.DisableColor()
504+
}
505+
outputFunc(usageFmt.Sprint(usageInfo))
506+
}
507+
484508
return nil
485509
}
486510

desktop/desktop_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func TestChatHuggingFaceModel(t *testing.T) {
6363
Body: io.NopCloser(bytes.NewBufferString("data: {\"choices\":[{\"delta\":{\"content\":\"Hello there!\"}}]}\n")),
6464
}, nil)
6565

66-
err := client.Chat("", modelName, prompt, "", func(s string) {})
66+
err := client.Chat("", modelName, prompt, "", func(s string) {}, false)
6767
assert.NoError(t, err)
6868
}
6969

0 commit comments

Comments
 (0)