feat: add /usage command to show model info and token usage
Adds token usage tracking to AgentInstance using atomic counters (TotalPromptTokens, TotalCompletionTokens, TotalRequests) that accumulate after each LLM call. The /usage command displays current model name, max tokens, temperature, and session token statistics. Available in all interactive modes like /help. https://claude.ai/code/session_01PoHBfe7eKmhHvVF12W3xZ2
This commit is contained in:
parent
cad9d820bf
commit
5f3fc4c133
3 changed files with 79 additions and 2 deletions
|
|
@ -109,6 +109,7 @@ func agentCmd() {
|
|||
} else {
|
||||
fmt.Printf("%s Interactive mode (Ctrl+C to exit)\n", logo)
|
||||
fmt.Println(" /help - show detailed help")
|
||||
fmt.Println(" /usage - show model info and token usage")
|
||||
fmt.Println(" /cmd - switch to command mode")
|
||||
fmt.Println(" /pico - switch to chat mode")
|
||||
fmt.Println(" /hipico - AI assistance in command mode")
|
||||
|
|
@ -163,11 +164,15 @@ func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
|
|||
return
|
||||
}
|
||||
|
||||
// /help works in all modes
|
||||
// /help and /usage work in all modes
|
||||
if input == "/help" {
|
||||
printHelp()
|
||||
continue
|
||||
}
|
||||
if input == "/usage" {
|
||||
printUsage(agentLoop)
|
||||
continue
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case modePico:
|
||||
|
|
@ -284,11 +289,15 @@ func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
|
|||
return
|
||||
}
|
||||
|
||||
// /help works in all modes
|
||||
// /help and /usage work in all modes
|
||||
if input == "/help" {
|
||||
printHelp()
|
||||
continue
|
||||
}
|
||||
if input == "/usage" {
|
||||
printUsage(agentLoop)
|
||||
continue
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case modePico:
|
||||
|
|
@ -383,6 +392,7 @@ PicoClaw has three interactive modes:
|
|||
|
||||
Commands (available in all modes):
|
||||
/help Show this help message
|
||||
/usage Show model info and token usage
|
||||
exit Exit PicoClaw
|
||||
quit Exit PicoClaw
|
||||
Ctrl+C Exit PicoClaw
|
||||
|
|
@ -410,6 +420,35 @@ Examples:
|
|||
`, logo, logo, logo, logo)
|
||||
}
|
||||
|
||||
// printUsage displays current model information and accumulated token usage.
|
||||
func printUsage(agentLoop *agent.AgentLoop) {
|
||||
info := agentLoop.GetUsageInfo()
|
||||
if info == nil {
|
||||
fmt.Println("No usage information available.")
|
||||
return
|
||||
}
|
||||
fmt.Printf(`%s Usage
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
Model: %s
|
||||
Max tokens: %d
|
||||
Temperature: %.1f
|
||||
|
||||
Token usage (this session):
|
||||
Prompt tokens: %d
|
||||
Completion tokens:%d
|
||||
Total tokens: %d
|
||||
Requests: %d
|
||||
`, logo,
|
||||
info["model"],
|
||||
info["max_tokens"],
|
||||
info["temperature"],
|
||||
info["prompt_tokens"],
|
||||
info["completion_tokens"],
|
||||
info["total_tokens"],
|
||||
info["requests"],
|
||||
)
|
||||
}
|
||||
|
||||
// executeShellCommand runs a shell command in the current working directory
|
||||
// and prints the output. It also handles the cd command to change directories.
|
||||
func executeShellCommand(input string) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sipeed/picoclaw/pkg/config"
|
||||
"github.com/sipeed/picoclaw/pkg/providers"
|
||||
|
|
@ -31,6 +32,21 @@ type AgentInstance struct {
|
|||
Subagents *config.SubagentsConfig
|
||||
SkillsFilter []string
|
||||
Candidates []providers.FallbackCandidate
|
||||
|
||||
// Accumulated token usage counters (atomic for concurrent safety)
|
||||
TotalPromptTokens atomic.Int64
|
||||
TotalCompletionTokens atomic.Int64
|
||||
TotalRequests atomic.Int64
|
||||
}
|
||||
|
||||
// AddUsage accumulates token usage from a single LLM response.
|
||||
func (a *AgentInstance) AddUsage(usage *providers.UsageInfo) {
|
||||
if usage == nil {
|
||||
return
|
||||
}
|
||||
a.TotalPromptTokens.Add(int64(usage.PromptTokens))
|
||||
a.TotalCompletionTokens.Add(int64(usage.CompletionTokens))
|
||||
a.TotalRequests.Add(1)
|
||||
}
|
||||
|
||||
// NewAgentInstance creates an agent instance from config.
|
||||
|
|
|
|||
|
|
@ -594,6 +594,9 @@ func (al *AgentLoop) runLLMIteration(
|
|||
return "", iteration, fmt.Errorf("LLM call failed after retries: %w", err)
|
||||
}
|
||||
|
||||
// Accumulate token usage
|
||||
agent.AddUsage(response.Usage)
|
||||
|
||||
// Check if no tool calls - we're done
|
||||
if len(response.ToolCalls) == 0 {
|
||||
finalContent = response.Content
|
||||
|
|
@ -852,6 +855,25 @@ func (al *AgentLoop) GetStartupInfo() map[string]any {
|
|||
return info
|
||||
}
|
||||
|
||||
// GetUsageInfo returns accumulated token usage for the default agent.
|
||||
func (al *AgentLoop) GetUsageInfo() map[string]any {
|
||||
agent := al.registry.GetDefaultAgent()
|
||||
if agent == nil {
|
||||
return nil
|
||||
}
|
||||
promptTokens := agent.TotalPromptTokens.Load()
|
||||
completionTokens := agent.TotalCompletionTokens.Load()
|
||||
return map[string]any{
|
||||
"model": agent.Model,
|
||||
"max_tokens": agent.MaxTokens,
|
||||
"temperature": agent.Temperature,
|
||||
"prompt_tokens": promptTokens,
|
||||
"completion_tokens": completionTokens,
|
||||
"total_tokens": promptTokens + completionTokens,
|
||||
"requests": agent.TotalRequests.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
// formatMessagesForLog formats messages for logging
|
||||
func formatMessagesForLog(messages []providers.Message) string {
|
||||
if len(messages) == 0 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue