mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 05:12:03 +08:00
refactor: align project directory architecture
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct CursorRunTraceSummary {
|
||||
pub request_id: String,
|
||||
pub conversation_id: Option<String>,
|
||||
pub route: String,
|
||||
pub model_id: Option<String>,
|
||||
pub status: String,
|
||||
pub request_bytes: i64,
|
||||
pub response_bytes: i64,
|
||||
pub response_event_count: i64,
|
||||
pub http_status: Option<i64>,
|
||||
pub received_at_ms: i64,
|
||||
pub first_response_at_ms: Option<i64>,
|
||||
pub finished_at_ms: Option<i64>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CursorRunTraceArtifact {
|
||||
pub seq: i64,
|
||||
pub artifact_type: String,
|
||||
pub source: String,
|
||||
pub metadata: serde_json::Value,
|
||||
pub created_at_ms: i64,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{ProviderType, Usage};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NewLlmCall {
|
||||
pub call_id: String,
|
||||
pub run_id: String,
|
||||
pub conversation_id: String,
|
||||
pub provider_call_index: i64,
|
||||
pub model_hash: String,
|
||||
pub provider_type: ProviderType,
|
||||
pub provider_url: String,
|
||||
pub request_type: ProviderType,
|
||||
pub request_url: String,
|
||||
pub model_id: String,
|
||||
pub display_name: String,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub fast: bool,
|
||||
pub message_count: usize,
|
||||
pub tool_count: usize,
|
||||
pub detailed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct LlmCallUsageAnchor {
|
||||
pub request_type: ProviderType,
|
||||
pub usage: Usage,
|
||||
pub message_count: usize,
|
||||
pub tool_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallSummary {
|
||||
pub call_id: String,
|
||||
pub run_id: String,
|
||||
pub conversation_id: String,
|
||||
pub provider_call_index: i64,
|
||||
pub model_hash: Option<String>,
|
||||
pub provider_type: String,
|
||||
pub provider_url: String,
|
||||
pub request_type: String,
|
||||
pub request_url: String,
|
||||
pub model_id: String,
|
||||
pub display_name: String,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub fast: Option<bool>,
|
||||
pub status: String,
|
||||
pub finish_reason: Option<String>,
|
||||
pub created_at_ms: i64,
|
||||
pub request_started_at_ms: Option<i64>,
|
||||
pub response_headers_at_ms: Option<i64>,
|
||||
pub first_event_at_ms: Option<i64>,
|
||||
pub first_text_at_ms: Option<i64>,
|
||||
pub first_valid_response_at_ms: Option<i64>,
|
||||
pub finished_at_ms: Option<i64>,
|
||||
pub queue_ms: Option<i64>,
|
||||
pub ttfb_ms: Option<i64>,
|
||||
pub ttft_ms: Option<i64>,
|
||||
pub ttfr_ms: Option<i64>,
|
||||
pub duration_ms: Option<i64>,
|
||||
pub input_tokens: Option<i64>,
|
||||
pub output_tokens: Option<i64>,
|
||||
pub total_tokens: Option<i64>,
|
||||
pub cache_read_tokens: Option<i64>,
|
||||
pub cache_write_tokens: Option<i64>,
|
||||
pub reasoning_tokens: Option<i64>,
|
||||
pub usage: Option<serde_json::Value>,
|
||||
pub message_count: i64,
|
||||
pub tool_count: i64,
|
||||
pub request_bytes: Option<i64>,
|
||||
pub response_bytes: i64,
|
||||
pub stream_event_count: i64,
|
||||
pub http_status: Option<i64>,
|
||||
pub error_kind: Option<String>,
|
||||
pub error_message: Option<String>,
|
||||
pub detailed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallRequest {
|
||||
pub headers: serde_json::Value,
|
||||
pub body: serde_json::Value,
|
||||
pub byte_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallResponseChunk {
|
||||
pub seq: i64,
|
||||
pub received_offset_ms: i64,
|
||||
pub data: String,
|
||||
pub byte_count: i64,
|
||||
}
|
||||
@@ -1,31 +1,25 @@
|
||||
mod configuration;
|
||||
mod conversation;
|
||||
mod cursor_trace;
|
||||
mod inference;
|
||||
mod llm_call;
|
||||
mod message;
|
||||
mod model_spec;
|
||||
mod overview;
|
||||
mod observability;
|
||||
mod projection;
|
||||
mod run;
|
||||
mod runtime_tag;
|
||||
mod token_count;
|
||||
mod tool;
|
||||
mod tool_result_replay;
|
||||
mod usage;
|
||||
|
||||
pub use configuration::*;
|
||||
pub use conversation::*;
|
||||
pub use cursor_trace::*;
|
||||
pub use inference::*;
|
||||
pub use llm_call::*;
|
||||
pub use message::*;
|
||||
pub use model_spec::*;
|
||||
pub use overview::*;
|
||||
pub use observability::*;
|
||||
pub use projection::*;
|
||||
pub use run::*;
|
||||
pub use runtime_tag::*;
|
||||
pub(crate) use token_count::*;
|
||||
pub use tool::*;
|
||||
pub(crate) use tool_result_replay::limit_tool_result_text;
|
||||
pub use usage::*;
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
use super::ProviderType;
|
||||
|
||||
mod usage {
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ProviderType;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Usage {
|
||||
pub input_tokens: Option<u64>,
|
||||
pub output_tokens: Option<u64>,
|
||||
pub total_tokens: Option<u64>,
|
||||
pub cache_read_tokens: Option<u64>,
|
||||
pub cache_write_tokens: Option<u64>,
|
||||
pub reasoning_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Returns the provider-visible input context without counting cached tokens twice.
|
||||
pub(crate) fn context_input_tokens(self, provider: ProviderType) -> Option<u64> {
|
||||
let input = self.input_tokens?;
|
||||
match provider {
|
||||
ProviderType::OpenAiChat | ProviderType::OpenAiResponses => Some(input),
|
||||
ProviderType::Anthropic => input
|
||||
.checked_add(self.cache_read_tokens.unwrap_or_default())?
|
||||
.checked_add(self.cache_write_tokens.unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Usage {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.input_tokens = sum(self.input_tokens, rhs.input_tokens);
|
||||
self.output_tokens = sum(self.output_tokens, rhs.output_tokens);
|
||||
self.total_tokens = sum(self.total_tokens, rhs.total_tokens);
|
||||
self.cache_read_tokens = sum(self.cache_read_tokens, rhs.cache_read_tokens);
|
||||
self.cache_write_tokens = sum(self.cache_write_tokens, rhs.cache_write_tokens);
|
||||
self.reasoning_tokens = sum(self.reasoning_tokens, rhs.reasoning_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fn sum(left: Option<u64>, right: Option<u64>) -> Option<u64> {
|
||||
left?.checked_add(right?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Usage;
|
||||
use crate::model::ProviderType;
|
||||
|
||||
#[test]
|
||||
fn openai_context_input_does_not_double_count_cached_tokens() {
|
||||
let usage = Usage {
|
||||
input_tokens: Some(140_649),
|
||||
cache_read_tokens: Some(120_000),
|
||||
cache_write_tokens: Some(10_000),
|
||||
..Usage::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::OpenAiResponses),
|
||||
Some(140_649)
|
||||
);
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::OpenAiChat),
|
||||
Some(140_649)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anthropic_context_input_includes_disjoint_cache_tokens() {
|
||||
let usage = Usage {
|
||||
input_tokens: Some(10_649),
|
||||
cache_read_tokens: Some(120_000),
|
||||
cache_write_tokens: Some(10_000),
|
||||
..Usage::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::Anthropic),
|
||||
Some(140_649)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_total_only_reports_fields_known_for_every_cycle() {
|
||||
let mut total = Usage {
|
||||
input_tokens: Some(10),
|
||||
output_tokens: Some(2),
|
||||
total_tokens: Some(12),
|
||||
cache_read_tokens: None,
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: Some(1),
|
||||
};
|
||||
total += Usage {
|
||||
input_tokens: Some(20),
|
||||
output_tokens: Some(3),
|
||||
total_tokens: Some(23),
|
||||
cache_read_tokens: Some(8),
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
};
|
||||
assert_eq!(total.input_tokens, Some(30));
|
||||
assert_eq!(total.output_tokens, Some(5));
|
||||
assert_eq!(total.total_tokens, Some(35));
|
||||
assert_eq!(total.cache_read_tokens, None);
|
||||
assert_eq!(total.cache_write_tokens, None);
|
||||
assert_eq!(total.reasoning_tokens, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use usage::*;
|
||||
|
||||
mod llm_call {
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{ProviderType, Usage};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NewLlmCall {
|
||||
pub call_id: String,
|
||||
pub run_id: String,
|
||||
pub conversation_id: String,
|
||||
pub provider_call_index: i64,
|
||||
pub model_hash: String,
|
||||
pub provider_type: ProviderType,
|
||||
pub provider_url: String,
|
||||
pub request_type: ProviderType,
|
||||
pub request_url: String,
|
||||
pub model_id: String,
|
||||
pub display_name: String,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub fast: bool,
|
||||
pub message_count: usize,
|
||||
pub tool_count: usize,
|
||||
pub detailed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct LlmCallUsageAnchor {
|
||||
pub request_type: ProviderType,
|
||||
pub usage: Usage,
|
||||
pub message_count: usize,
|
||||
pub tool_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallSummary {
|
||||
pub call_id: String,
|
||||
pub run_id: String,
|
||||
pub conversation_id: String,
|
||||
pub provider_call_index: i64,
|
||||
pub model_hash: Option<String>,
|
||||
pub provider_type: String,
|
||||
pub provider_url: String,
|
||||
pub request_type: String,
|
||||
pub request_url: String,
|
||||
pub model_id: String,
|
||||
pub display_name: String,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub fast: Option<bool>,
|
||||
pub status: String,
|
||||
pub finish_reason: Option<String>,
|
||||
pub created_at_ms: i64,
|
||||
pub request_started_at_ms: Option<i64>,
|
||||
pub response_headers_at_ms: Option<i64>,
|
||||
pub first_event_at_ms: Option<i64>,
|
||||
pub first_text_at_ms: Option<i64>,
|
||||
pub first_valid_response_at_ms: Option<i64>,
|
||||
pub finished_at_ms: Option<i64>,
|
||||
pub queue_ms: Option<i64>,
|
||||
pub ttfb_ms: Option<i64>,
|
||||
pub ttft_ms: Option<i64>,
|
||||
pub ttfr_ms: Option<i64>,
|
||||
pub duration_ms: Option<i64>,
|
||||
pub input_tokens: Option<i64>,
|
||||
pub output_tokens: Option<i64>,
|
||||
pub total_tokens: Option<i64>,
|
||||
pub cache_read_tokens: Option<i64>,
|
||||
pub cache_write_tokens: Option<i64>,
|
||||
pub reasoning_tokens: Option<i64>,
|
||||
pub usage: Option<serde_json::Value>,
|
||||
pub message_count: i64,
|
||||
pub tool_count: i64,
|
||||
pub request_bytes: Option<i64>,
|
||||
pub response_bytes: i64,
|
||||
pub stream_event_count: i64,
|
||||
pub http_status: Option<i64>,
|
||||
pub error_kind: Option<String>,
|
||||
pub error_message: Option<String>,
|
||||
pub detailed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallRequest {
|
||||
pub headers: serde_json::Value,
|
||||
pub body: serde_json::Value,
|
||||
pub byte_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct LlmCallResponseChunk {
|
||||
pub seq: i64,
|
||||
pub received_offset_ms: i64,
|
||||
pub data: String,
|
||||
pub byte_count: i64,
|
||||
}
|
||||
}
|
||||
pub use llm_call::*;
|
||||
|
||||
mod cursor_trace {
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct CursorRunTraceSummary {
|
||||
pub request_id: String,
|
||||
pub conversation_id: Option<String>,
|
||||
pub route: String,
|
||||
pub model_id: Option<String>,
|
||||
pub status: String,
|
||||
pub request_bytes: i64,
|
||||
pub response_bytes: i64,
|
||||
pub response_event_count: i64,
|
||||
pub http_status: Option<i64>,
|
||||
pub received_at_ms: i64,
|
||||
pub first_response_at_ms: Option<i64>,
|
||||
pub finished_at_ms: Option<i64>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CursorRunTraceArtifact {
|
||||
pub seq: i64,
|
||||
pub artifact_type: String,
|
||||
pub source: String,
|
||||
pub metadata: serde_json::Value,
|
||||
pub created_at_ms: i64,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
}
|
||||
pub use cursor_trace::*;
|
||||
|
||||
mod overview {
|
||||
//! Read-only usage aggregates rendered by the desktop overview page.
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct OverviewMetrics {
|
||||
pub llm_calls: i64,
|
||||
pub successful_calls: i64,
|
||||
pub failed_calls: i64,
|
||||
pub token_usage: i64,
|
||||
pub prompt_tokens: i64,
|
||||
pub input_tokens: i64,
|
||||
pub cache_read_tokens: i64,
|
||||
pub cache_write_tokens: i64,
|
||||
pub output_tokens: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenUsageGranularity {
|
||||
Minute,
|
||||
Hour,
|
||||
#[default]
|
||||
Day,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct TokenUsageBucket {
|
||||
pub bucket_start_ms: i64,
|
||||
pub input_tokens: i64,
|
||||
pub cache_read_tokens: i64,
|
||||
pub cache_write_tokens: i64,
|
||||
pub output_tokens: i64,
|
||||
}
|
||||
|
||||
impl TokenUsageBucket {
|
||||
pub fn total_tokens(&self) -> i64 {
|
||||
self.input_tokens
|
||||
.saturating_add(self.cache_read_tokens)
|
||||
.saturating_add(self.cache_write_tokens)
|
||||
.saturating_add(self.output_tokens)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct Overview {
|
||||
pub metrics: OverviewMetrics,
|
||||
pub token_usage_granularity: TokenUsageGranularity,
|
||||
pub token_usage_series: Vec<TokenUsageBucket>,
|
||||
}
|
||||
}
|
||||
pub use overview::*;
|
||||
@@ -1,50 +0,0 @@
|
||||
//! Read-only usage aggregates rendered by the desktop overview page.
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct OverviewMetrics {
|
||||
pub llm_calls: i64,
|
||||
pub successful_calls: i64,
|
||||
pub failed_calls: i64,
|
||||
pub token_usage: i64,
|
||||
pub prompt_tokens: i64,
|
||||
pub input_tokens: i64,
|
||||
pub cache_read_tokens: i64,
|
||||
pub cache_write_tokens: i64,
|
||||
pub output_tokens: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenUsageGranularity {
|
||||
Minute,
|
||||
Hour,
|
||||
#[default]
|
||||
Day,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct TokenUsageBucket {
|
||||
pub bucket_start_ms: i64,
|
||||
pub input_tokens: i64,
|
||||
pub cache_read_tokens: i64,
|
||||
pub cache_write_tokens: i64,
|
||||
pub output_tokens: i64,
|
||||
}
|
||||
|
||||
impl TokenUsageBucket {
|
||||
pub fn total_tokens(&self) -> i64 {
|
||||
self.input_tokens
|
||||
.saturating_add(self.cache_read_tokens)
|
||||
.saturating_add(self.cache_write_tokens)
|
||||
.saturating_add(self.output_tokens)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
||||
pub struct Overview {
|
||||
pub metrics: OverviewMetrics,
|
||||
pub token_usage_granularity: TokenUsageGranularity,
|
||||
pub token_usage_series: Vec<TokenUsageBucket>,
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ProviderType;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Usage {
|
||||
pub input_tokens: Option<u64>,
|
||||
pub output_tokens: Option<u64>,
|
||||
pub total_tokens: Option<u64>,
|
||||
pub cache_read_tokens: Option<u64>,
|
||||
pub cache_write_tokens: Option<u64>,
|
||||
pub reasoning_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Returns the provider-visible input context without counting cached tokens twice.
|
||||
pub(crate) fn context_input_tokens(self, provider: ProviderType) -> Option<u64> {
|
||||
let input = self.input_tokens?;
|
||||
match provider {
|
||||
ProviderType::OpenAiChat | ProviderType::OpenAiResponses => Some(input),
|
||||
ProviderType::Anthropic => input
|
||||
.checked_add(self.cache_read_tokens.unwrap_or_default())?
|
||||
.checked_add(self.cache_write_tokens.unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Usage {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.input_tokens = sum(self.input_tokens, rhs.input_tokens);
|
||||
self.output_tokens = sum(self.output_tokens, rhs.output_tokens);
|
||||
self.total_tokens = sum(self.total_tokens, rhs.total_tokens);
|
||||
self.cache_read_tokens = sum(self.cache_read_tokens, rhs.cache_read_tokens);
|
||||
self.cache_write_tokens = sum(self.cache_write_tokens, rhs.cache_write_tokens);
|
||||
self.reasoning_tokens = sum(self.reasoning_tokens, rhs.reasoning_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fn sum(left: Option<u64>, right: Option<u64>) -> Option<u64> {
|
||||
left?.checked_add(right?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Usage;
|
||||
use crate::model::ProviderType;
|
||||
|
||||
#[test]
|
||||
fn openai_context_input_does_not_double_count_cached_tokens() {
|
||||
let usage = Usage {
|
||||
input_tokens: Some(140_649),
|
||||
cache_read_tokens: Some(120_000),
|
||||
cache_write_tokens: Some(10_000),
|
||||
..Usage::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::OpenAiResponses),
|
||||
Some(140_649)
|
||||
);
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::OpenAiChat),
|
||||
Some(140_649)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anthropic_context_input_includes_disjoint_cache_tokens() {
|
||||
let usage = Usage {
|
||||
input_tokens: Some(10_649),
|
||||
cache_read_tokens: Some(120_000),
|
||||
cache_write_tokens: Some(10_000),
|
||||
..Usage::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
usage.context_input_tokens(ProviderType::Anthropic),
|
||||
Some(140_649)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_total_only_reports_fields_known_for_every_cycle() {
|
||||
let mut total = Usage {
|
||||
input_tokens: Some(10),
|
||||
output_tokens: Some(2),
|
||||
total_tokens: Some(12),
|
||||
cache_read_tokens: None,
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: Some(1),
|
||||
};
|
||||
total += Usage {
|
||||
input_tokens: Some(20),
|
||||
output_tokens: Some(3),
|
||||
total_tokens: Some(23),
|
||||
cache_read_tokens: Some(8),
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
};
|
||||
assert_eq!(total.input_tokens, Some(30));
|
||||
assert_eq!(total.output_tokens, Some(5));
|
||||
assert_eq!(total.total_tokens, Some(35));
|
||||
assert_eq!(total.cache_read_tokens, None);
|
||||
assert_eq!(total.cache_write_tokens, None);
|
||||
assert_eq!(total.reasoning_tokens, None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user