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

Commit 62f3cf0

Browse files
committed
Add support for cached tokens
1 parent a417c71 commit 62f3cf0

File tree

9 files changed

+61
-9
lines changed

9 files changed

+61
-9
lines changed

app/models/ai_api_audit_log.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,10 @@ def prev_log_id
4343
# feature_name :string(255)
4444
# language_model :string(255)
4545
# feature_context :jsonb
46+
# cached_tokens :integer
47+
#
48+
# Indexes
49+
#
50+
# index_ai_api_audit_logs_on_created_at_and_feature_name (created_at,feature_name)
51+
# index_ai_api_audit_logs_on_created_at_and_language_model (created_at,language_model)
52+
#

app/serializers/ai_usage_serializer.rb

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,35 @@ class AiUsageSerializer < ApplicationSerializer
44
attributes :data, :features, :models, :summary
55

66
def data
7-
object.tokens_by_period.map {|key, value| [key, value]}
7+
object.tokens_by_period.as_json(
8+
only: %i[period total_tokens total_cached_tokens total_request_tokens total_response_tokens],
9+
)
810
end
911

1012
def features
11-
object.feature_breakdown.as_json(only: %i[feature_name usage_count total_tokens])
13+
object.feature_breakdown.as_json(
14+
only: %i[
15+
feature_name
16+
usage_count
17+
total_tokens
18+
total_cached_tokens
19+
total_request_tokens
20+
total_response_tokens
21+
],
22+
)
1223
end
1324

1425
def models
15-
object.model_breakdown.as_json(only: %i[llm usage_count total_tokens])
26+
object.model_breakdown.as_json(
27+
only: %i[
28+
llm
29+
usage_count
30+
total_tokens
31+
total_cached_tokens
32+
total_request_tokens
33+
total_response_tokens
34+
],
35+
)
1636
end
1737

1838
def summary

assets/javascripts/discourse/components/ai-usage.gjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ export default class AiUsage extends Component {
5959
return {
6060
type: "line",
6161
data: {
62-
labels: this.data.data.map((pair) => pair[0]),
62+
labels: this.data.data.map((row) => row.period),
6363
datasets: [
6464
{
6565
label: "Tokens",
66-
data: this.data.data.map((pair) => pair[1]),
66+
data: this.data.data.map((row) => row.total_tokens),
6767
fill: false,
6868
borderColor: "rgb(75, 192, 192)",
6969
tension: 0.1,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class AddCachedTokensToAiApiAuditLog < ActiveRecord::Migration[7.2]
4+
def change
5+
add_column :ai_api_audit_logs, :cached_tokens, :integer
6+
add_index :ai_api_audit_logs, [:created_at, :feature_name]
7+
add_index :ai_api_audit_logs, [:created_at, :language_model]
8+
end
9+
end

lib/completions/endpoints/open_ai.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def prepare_request(payload)
9797
def final_log_update(log)
9898
log.request_tokens = processor.prompt_tokens if processor.prompt_tokens
9999
log.response_tokens = processor.completion_tokens if processor.completion_tokens
100+
log.cached_tokens = processor.cached_tokens if processor.cached_tokens
100101
end
101102

102103
def decode(response_raw)

lib/completions/open_ai_message_processor.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# frozen_string_literal: true
22
module DiscourseAi::Completions
33
class OpenAiMessageProcessor
4-
attr_reader :prompt_tokens, :completion_tokens
4+
attr_reader :prompt_tokens, :completion_tokens, :cached_tokens
55

66
def initialize(partial_tool_calls: false)
77
@tool = nil
88
@tool_arguments = +""
99
@prompt_tokens = nil
1010
@completion_tokens = nil
11+
@cached_tokens = nil
1112
@partial_tool_calls = partial_tool_calls
1213
end
1314

@@ -121,6 +122,7 @@ def process_arguments
121122
def update_usage(json)
122123
@prompt_tokens ||= json.dig(:usage, :prompt_tokens)
123124
@completion_tokens ||= json.dig(:usage, :completion_tokens)
125+
@cached_tokens ||= json.dig(:usage, :prompt_tokens_details, :cached_tokens)
124126
end
125127
end
126128
end

lib/completions/report.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ def guess_period(period)
2828

2929
def tokens_by_period(period = nil)
3030
period = guess_period(period)
31-
base_query.group("DATE_TRUNC('#{period}', created_at)").sum(
32-
"request_tokens + response_tokens",
31+
base_query.group("DATE_TRUNC('#{period}', created_at)").select(
32+
"DATE_TRUNC('#{period}', created_at) as period",
33+
"SUM(request_tokens + response_tokens) as total_tokens",
34+
"SUM(COALESCE(cached_tokens,0)) as total_cached_tokens",
35+
"SUM(request_tokens) as total_request_tokens",
36+
"SUM(response_tokens) as total_response_tokens",
3337
)
3438
end
3539

@@ -38,6 +42,9 @@ def feature_breakdown
3842
"feature_name",
3943
"COUNT(*) as usage_count",
4044
"SUM(request_tokens + response_tokens) as total_tokens",
45+
"SUM(COALESCE(cached_tokens,0)) as total_cached_tokens",
46+
"SUM(request_tokens) as total_request_tokens",
47+
"SUM(response_tokens) as total_response_tokens",
4148
)
4249
end
4350

@@ -46,6 +53,9 @@ def model_breakdown
4653
"language_model as llm",
4754
"COUNT(*) as usage_count",
4855
"SUM(request_tokens + response_tokens) as total_tokens",
56+
"SUM(COALESCE(cached_tokens,0)) as total_cached_tokens",
57+
"SUM(request_tokens) as total_request_tokens",
58+
"SUM(response_tokens) as total_response_tokens",
4959
)
5060
end
5161

spec/fixtures/bot/openai_artifact_call.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ data: {"id":"chatcmpl-ATimVYagKnCWQ0VXY0Hn2SDjRuN6B","object":"chat.completion.c
292292

293293
data: {"id":"chatcmpl-ATimVYagKnCWQ0VXY0Hn2SDjRuN6B","object":"chat.completion.chunk","created":1731647015,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_45cf54deae","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null}
294294

295-
data: {"id":"chatcmpl-ATimVYagKnCWQ0VXY0Hn2SDjRuN6B","object":"chat.completion.chunk","created":1731647015,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_45cf54deae","choices":[],"usage":{"prompt_tokens":735,"completion_tokens":156,"total_tokens":891,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}
295+
data: {"id":"chatcmpl-ATimVYagKnCWQ0VXY0Hn2SDjRuN6B","object":"chat.completion.chunk","created":1731647015,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_45cf54deae","choices":[],"usage":{"prompt_tokens":735,"completion_tokens":156,"total_tokens":891,"prompt_tokens_details":{"cached_tokens":33,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}
296296

297297
data: [DONE]
298298

spec/lib/completions/endpoints/open_ai_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,9 @@ def request_body(prompt, stream: false, tool_call: false)
657657
end
658658
end
659659
end
660+
661+
audit_log = AiApiAuditLog.order("id desc").first
662+
expect(audit_log.cached_tokens).to eq(33)
660663
end
661664

662665
it "properly handles spaces in tools payload and partial tool calls" do

0 commit comments

Comments
 (0)