discourse/plugins/discourse-ai/spec/serializers/ai_usage_serializer_spec.rb
Keegan George 131a6e1afb
DEV: Adjustments in usage page for LLM's with credit allocations (#36566)
## 🔍 Overview
This update some changes when showcasing feature cost for LLMs that has
credit allocations

## 📸 Screenshots

_Included in plan instead of cost when a feature comes solely from a
credit allocation model_
<img width="1107" height="463" alt="Screenshot 2025-12-08 at 16 47 53"
src="https://github.com/user-attachments/assets/24391a07-2af2-4d56-b8b9-b770ebdb132d"
/>

_A subtable in a tooltip with line items for mixed costs_
<img width="1110" height="460" alt="Screenshot 2025-12-08 at 16 48 19"
src="https://github.com/user-attachments/assets/ceb94f00-9a1a-49f8-9e46-7ff544113d55"
/>
2025-12-09 09:55:29 -08:00

128 lines
4.3 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe AiUsageSerializer do
fab!(:user)
fab!(:claude_model) do
Fabricate(
:llm_model,
name: "claude-3-opus",
provider: "anthropic",
input_cost: 15.0,
output_cost: 75.0,
)
end
fab!(:gpt_model) do
Fabricate(:llm_model, name: "gpt-4", provider: "open_ai", input_cost: 10.0, output_cost: 30.0)
end
before { enable_current_plugin }
describe "#feature_models" do
it "returns a hash mapping feature names to model breakdowns" do
AiApiRequestStat.create!(
provider_id: AiApiAuditLog::Provider::Anthropic,
user_id: user.id,
llm_id: claude_model.id,
language_model: "claude-3-opus",
feature_name: "ai_bot",
request_tokens: 1000,
response_tokens: 500,
created_at: 1.day.ago,
)
AiApiRequestStat.create!(
provider_id: AiApiAuditLog::Provider::OpenAI,
user_id: user.id,
llm_id: gpt_model.id,
language_model: "gpt-4",
feature_name: "ai_bot",
request_tokens: 2000,
response_tokens: 1000,
created_at: 1.day.ago,
)
report = DiscourseAi::Completions::Report.new(start_date: 2.days.ago, end_date: Time.current)
serialized = described_class.new(report, root: false)
json = JSON.parse(serialized.to_json)
expect(json).to have_key("feature_models")
expect(json["feature_models"]).to be_a(Hash)
expect(json["feature_models"]).to have_key("ai_bot")
expect(json["feature_models"]["ai_bot"].size).to eq(2)
end
it "includes credit_allocation for models with LlmCreditAllocation" do
seeded_model = Fabricate(:seeded_model)
Fabricate(:llm_credit_allocation, llm_model: seeded_model)
AiApiRequestStat.create!(
provider_id: AiApiAuditLog::Provider::Anthropic,
user_id: user.id,
llm_id: seeded_model.id,
language_model: seeded_model.name,
feature_name: "ai_helper",
request_tokens: 500,
response_tokens: 250,
created_at: 1.day.ago,
)
report = DiscourseAi::Completions::Report.new(start_date: 2.days.ago, end_date: Time.current)
serialized = described_class.new(report, root: false)
json = JSON.parse(serialized.to_json)
ai_helper_models = json["feature_models"]["ai_helper"]
seeded_model_data = ai_helper_models.find { |m| m["llm_id"].to_i == seeded_model.id }
expect(seeded_model_data).to have_key("credit_allocation")
expect(seeded_model_data["credit_allocation"]).to be_present
end
it "does not include credit_allocation for models without LlmCreditAllocation" do
AiApiRequestStat.create!(
provider_id: AiApiAuditLog::Provider::Anthropic,
user_id: user.id,
llm_id: claude_model.id,
language_model: "claude-3-opus",
feature_name: "ai_bot",
request_tokens: 1000,
response_tokens: 500,
created_at: 1.day.ago,
)
report = DiscourseAi::Completions::Report.new(start_date: 2.days.ago, end_date: Time.current)
serialized = described_class.new(report, root: false)
json = JSON.parse(serialized.to_json)
ai_bot_models = json["feature_models"]["ai_bot"]
claude_model_data = ai_bot_models.find { |m| m["llm_id"].to_i == claude_model.id }
expect(claude_model_data).not_to have_key("credit_allocation")
end
it "includes spending data for each model in the breakdown" do
AiApiRequestStat.create!(
provider_id: AiApiAuditLog::Provider::Anthropic,
user_id: user.id,
llm_id: claude_model.id,
language_model: "claude-3-opus",
feature_name: "ai_bot",
request_tokens: 1000,
response_tokens: 500,
created_at: 1.day.ago,
)
report = DiscourseAi::Completions::Report.new(start_date: 2.days.ago, end_date: Time.current)
serialized = described_class.new(report, root: false)
json = JSON.parse(serialized.to_json)
ai_bot_models = json["feature_models"]["ai_bot"]
model_data = ai_bot_models.first
expect(model_data).to have_key("input_spending")
expect(model_data).to have_key("output_spending")
expect(model_data).to have_key("total_tokens")
expect(model_data).to have_key("usage_count")
end
end
end