From b7e3c1ef5fe8fef1684f593229ffeb947837f329 Mon Sep 17 00:00:00 2001 From: appleboy Date: Sat, 20 Dec 2025 11:20:32 +0800 Subject: [PATCH 1/2] feat: report detailed token usage metrics in action outputs and logs - Add token usage metrics (prompt_tokens, completion_tokens, total_tokens) to action outputs and documentation - Output additional details if available, such as cached prompt tokens, reasoning tokens, accepted/rejected prediction tokens - Update action to print detailed token usage information in the logs Signed-off-by: appleboy --- README.md | 15 +++++++++++---- README.zh-CN.md | 15 +++++++++++---- README.zh-TW.md | 15 +++++++++++---- action.yml | 14 ++++++++++++++ main.go | 31 +++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b080e0c..68e0477 100644 --- a/README.md +++ b/README.md @@ -92,10 +92,17 @@ A GitHub Action to interact with OpenAI-compatible LLM services, supporting cust ## Outputs -| Output | Description | -| ---------- | --------------------------------------------------------------------------------------------- | -| `response` | The raw response from the LLM (always available) | -| `` | When using tool_schema, each field from the function arguments JSON becomes a separate output | +| Output | Description | +| -------------------------------------- | --------------------------------------------------------------------------------------------- | +| `response` | The raw response from the LLM (always available) | +| `prompt_tokens` | Number of tokens in the prompt | +| `completion_tokens` | Number of tokens in the completion | +| `total_tokens` | Total number of tokens used | +| `prompt_cached_tokens` | Number of cached tokens in the prompt (cost saving, if available) | +| `completion_reasoning_tokens` | Number of reasoning tokens for o1/o3 models (if available) | +| `completion_accepted_prediction_tokens`| Number of accepted prediction tokens (if available) | +| `completion_rejected_prediction_tokens`| Number of rejected prediction tokens (if available) | +| `` | When using tool_schema, each field from the function arguments JSON becomes a separate output | **Output Behavior:** diff --git a/README.zh-CN.md b/README.zh-CN.md index 1ff3feb..59de402 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -92,10 +92,17 @@ ## 输出参数 -| 输出 | 说明 | -| ---------- | ----------------------------------------------------------------- | -| `response` | 来自 LLM 的原始响应(始终可用) | -| `` | 使用 tool_schema 时,函数参数 JSON 中的每个字段都会成为独立的输出 | +| 输出 | 说明 | +| --------------------------------------- | ----------------------------------------------------------------- | +| `response` | 来自 LLM 的原始响应(始终可用) | +| `prompt_tokens` | 提示词的 token 数量 | +| `completion_tokens` | 回复的 token 数量 | +| `total_tokens` | 总 token 使用量 | +| `prompt_cached_tokens` | 提示词中的缓存 token 数量(节省成本,如可用) | +| `completion_reasoning_tokens` | 推理 token 数量,用于 o1/o3 模型(如可用) | +| `completion_accepted_prediction_tokens` | 已接受的预测 token 数量(如可用) | +| `completion_rejected_prediction_tokens` | 已拒绝的预测 token 数量(如可用) | +| `` | 使用 tool_schema 时,函数参数 JSON 中的每个字段都会成为独立的输出 | **输出行为:** diff --git a/README.zh-TW.md b/README.zh-TW.md index 04ad667..32d428a 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -92,10 +92,17 @@ ## 輸出參數 -| 輸出 | 說明 | -| ---------- | ----------------------------------------------------------------- | -| `response` | 來自 LLM 的原始回應(始終可用) | -| `` | 使用 tool_schema 時,函數參數 JSON 中的每個欄位都會成為獨立的輸出 | +| 輸出 | 說明 | +| --------------------------------------- | ----------------------------------------------------------------- | +| `response` | 來自 LLM 的原始回應(始終可用) | +| `prompt_tokens` | 提示詞的 token 數量 | +| `completion_tokens` | 回覆的 token 數量 | +| `total_tokens` | 總 token 使用量 | +| `prompt_cached_tokens` | 提示詞中的快取 token 數量(節省成本,如可用) | +| `completion_reasoning_tokens` | 推理 token 數量,用於 o1/o3 模型(如可用) | +| `completion_accepted_prediction_tokens` | 已接受的預測 token 數量(如可用) | +| `completion_rejected_prediction_tokens` | 已拒絕的預測 token 數量(如可用) | +| `` | 使用 tool_schema 時,函數參數 JSON 中的每個欄位都會成為獨立的輸出 | **輸出行為:** diff --git a/action.yml b/action.yml index 567a2ca..7000d32 100644 --- a/action.yml +++ b/action.yml @@ -53,6 +53,20 @@ inputs: outputs: response: description: 'The response from the LLM' + prompt_tokens: + description: 'Number of tokens in the prompt' + completion_tokens: + description: 'Number of tokens in the completion' + total_tokens: + description: 'Total number of tokens used' + prompt_cached_tokens: + description: 'Number of cached tokens in the prompt (cost saving)' + completion_reasoning_tokens: + description: 'Number of reasoning tokens (for o1/o3 models)' + completion_accepted_prediction_tokens: + description: 'Number of accepted prediction tokens' + completion_rejected_prediction_tokens: + description: 'Number of rejected prediction tokens' runs: using: 'docker' diff --git a/main.go b/main.go index ab6ef6b..f82654d 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strconv" "github.com/appleboy/com/gh" openai "github.com/sashabaranov/go-openai" @@ -140,6 +141,19 @@ func run() error { fmt.Println(response) fmt.Println("--- End Response ---") + // Print token usage + fmt.Println("--- Token Usage ---") + fmt.Printf("Prompt Tokens: %d\n", resp.Usage.PromptTokens) + fmt.Printf("Completion Tokens: %d\n", resp.Usage.CompletionTokens) + fmt.Printf("Total Tokens: %d\n", resp.Usage.TotalTokens) + if resp.Usage.PromptTokensDetails != nil { + fmt.Printf("Cached Tokens: %d\n", resp.Usage.PromptTokensDetails.CachedTokens) + } + if resp.Usage.CompletionTokensDetails != nil { + fmt.Printf("Reasoning Tokens: %d\n", resp.Usage.CompletionTokensDetails.ReasoningTokens) + } + fmt.Println("--- End Token Usage ---") + // Set GitHub Actions output var toolArgs map[string]string if toolMeta != nil { @@ -161,6 +175,23 @@ func run() error { ) } + // Add token usage to output + output["prompt_tokens"] = strconv.Itoa(resp.Usage.PromptTokens) + output["completion_tokens"] = strconv.Itoa(resp.Usage.CompletionTokens) + output["total_tokens"] = strconv.Itoa(resp.Usage.TotalTokens) + + // Add prompt token details if available + if resp.Usage.PromptTokensDetails != nil { + output["prompt_cached_tokens"] = strconv.Itoa(resp.Usage.PromptTokensDetails.CachedTokens) + } + + // Add completion token details if available + if d := resp.Usage.CompletionTokensDetails; d != nil { + output["completion_reasoning_tokens"] = strconv.Itoa(d.ReasoningTokens) + output["completion_accepted_prediction_tokens"] = strconv.Itoa(d.AcceptedPredictionTokens) + output["completion_rejected_prediction_tokens"] = strconv.Itoa(d.RejectedPredictionTokens) + } + if err := gh.SetOutput(output); err != nil { return fmt.Errorf("failed to set output: %v", err) } From b7e315dac1cded4186a13efdca545d73d5a3c742 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Sat, 20 Dec 2025 11:26:00 +0800 Subject: [PATCH 2/2] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index f82654d..1191cea 100644 --- a/main.go +++ b/main.go @@ -149,8 +149,10 @@ func run() error { if resp.Usage.PromptTokensDetails != nil { fmt.Printf("Cached Tokens: %d\n", resp.Usage.PromptTokensDetails.CachedTokens) } - if resp.Usage.CompletionTokensDetails != nil { - fmt.Printf("Reasoning Tokens: %d\n", resp.Usage.CompletionTokensDetails.ReasoningTokens) + if d := resp.Usage.CompletionTokensDetails; d != nil { + fmt.Printf("Reasoning Tokens: %d\n", d.ReasoningTokens) + fmt.Printf("Accepted Prediction Tokens: %d\n", d.AcceptedPredictionTokens) + fmt.Printf("Rejected Prediction Tokens: %d\n", d.RejectedPredictionTokens) } fmt.Println("--- End Token Usage ---")