diff --git a/app/src/ai/agent/api/convert_conversation_tests.rs b/app/src/ai/agent/api/convert_conversation_tests.rs index 0433ee0be7..cd930f8d19 100644 --- a/app/src/ai/agent/api/convert_conversation_tests.rs +++ b/app/src/ai/agent/api/convert_conversation_tests.rs @@ -26,6 +26,7 @@ fn test_server_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/ai/agent/conversation.rs b/app/src/ai/agent/conversation.rs index a2a9b7cbbd..a3c01ee99f 100644 --- a/app/src/ai/agent/conversation.rs +++ b/app/src/ai/agent/conversation.rs @@ -652,8 +652,20 @@ impl AIConversation { self.conversation_usage_metadata.context_window_usage } + /// Total credits spent in the conversation, including both LLM inference + /// and platform credits. pub fn credits_spent(&self) -> f32 { - (self.conversation_usage_metadata.credits_spent * 10.0).round() / 10.0 + let total = self.conversation_usage_metadata.credits_spent + + self.conversation_usage_metadata.platform_credits_spent; + (total * 10.0).round() / 10.0 + } + + pub fn inference_credits_spent(&self) -> f32 { + self.conversation_usage_metadata.credits_spent + } + + pub fn platform_credits_spent(&self) -> f32 { + self.conversation_usage_metadata.platform_credits_spent } /// Test-only helper that sets the conversation's credit total directly. @@ -663,6 +675,7 @@ impl AIConversation { #[cfg(test)] pub(crate) fn set_credits_spent_for_test(&mut self, credits: f32) { self.conversation_usage_metadata.credits_spent = credits; + self.conversation_usage_metadata.platform_credits_spent = 0.0; } /// Test-only helper that simulates the root-task upgrade performed by the @@ -1881,6 +1894,8 @@ impl AIConversation { self.conversation_usage_metadata.context_window_usage = usage_metadata.context_window_usage; self.conversation_usage_metadata.credits_spent = usage_metadata.credits_spent; + self.conversation_usage_metadata.platform_credits_spent = + usage_metadata.platform_credits_spent; let llm_preferences = LLMPreferences::as_ref(ctx); self.conversation_usage_metadata.token_usage = footer_model_token_usage(&usage_metadata, llm_preferences); diff --git a/app/src/ai/agent_conversations_model_tests.rs b/app/src/ai/agent_conversations_model_tests.rs index a37b6f5e77..435eeb14d1 100644 --- a/app/src/ai/agent_conversations_model_tests.rs +++ b/app/src/ai/agent_conversations_model_tests.rs @@ -709,6 +709,7 @@ fn create_server_conversation_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/ai/agent_sdk/artifact_upload_tests.rs b/app/src/ai/agent_sdk/artifact_upload_tests.rs index 7e57507195..812fcc49af 100644 --- a/app/src/ai/agent_sdk/artifact_upload_tests.rs +++ b/app/src/ai/agent_sdk/artifact_upload_tests.rs @@ -38,6 +38,7 @@ fn create_conversation_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/ai/blocklist/controller/shared_session.rs b/app/src/ai/blocklist/controller/shared_session.rs index 473828fb13..8575651bb8 100644 --- a/app/src/ai/blocklist/controller/shared_session.rs +++ b/app/src/ai/blocklist/controller/shared_session.rs @@ -502,8 +502,8 @@ impl BlocklistAIController { .conversation(&conv_id) .map(|conversation| stream_finished::ConversationUsageMetadata { context_window_usage: conversation.context_window_usage(), - credits_spent: conversation.credits_spent(), - platform_credits_spent: 0.0, + credits_spent: conversation.inference_credits_spent(), + platform_credits_spent: conversation.platform_credits_spent(), summarized: conversation.was_summarized(), #[allow(deprecated)] token_usage: conversation diff --git a/app/src/ai/blocklist/history_model.rs b/app/src/ai/blocklist/history_model.rs index ecbb577ed4..fcd83d491f 100644 --- a/app/src/ai/blocklist/history_model.rs +++ b/app/src/ai/blocklist/history_model.rs @@ -133,7 +133,10 @@ impl AIConversationMetadata { .metadata_last_updated_ts .utc() .naive_utc(); - let credits_spent = Some(server_conversation_metadata.usage.credits_spent); + let credits_spent = Some( + server_conversation_metadata.usage.credits_spent + + server_conversation_metadata.usage.platform_credits_spent, + ); let server_conversation_token = Some( server_conversation_metadata .server_conversation_token diff --git a/app/src/ai/blocklist/history_model/conversation_loader.rs b/app/src/ai/blocklist/history_model/conversation_loader.rs index d02baf9182..928066befe 100644 --- a/app/src/ai/blocklist/history_model/conversation_loader.rs +++ b/app/src/ai/blocklist/history_model/conversation_loader.rs @@ -634,7 +634,7 @@ impl BlocklistAIHistoryModel { let credits_spent = conversation_data .as_ref() .and_then(|data| data.conversation_usage_metadata.as_ref()) - .map(|m| m.credits_spent); + .map(|m| m.credits_spent + m.platform_credits_spent); let artifacts = conversation_data .as_ref() .and_then(|data| data.artifacts_json.as_ref()) diff --git a/app/src/ai/blocklist/history_model_tests.rs b/app/src/ai/blocklist/history_model_tests.rs index c9370a6d43..de21dfe16c 100644 --- a/app/src/ai/blocklist/history_model_tests.rs +++ b/app/src/ai/blocklist/history_model_tests.rs @@ -564,6 +564,7 @@ fn create_server_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/ai/blocklist/orchestration_event_streamer_tests.rs b/app/src/ai/blocklist/orchestration_event_streamer_tests.rs index 59148f90b8..6d97cbdd39 100644 --- a/app/src/ai/blocklist/orchestration_event_streamer_tests.rs +++ b/app/src/ai/blocklist/orchestration_event_streamer_tests.rs @@ -234,6 +234,7 @@ fn make_server_metadata_with_harness( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/ai/blocklist/usage/conversation_usage_view.rs b/app/src/ai/blocklist/usage/conversation_usage_view.rs index 3b2a6bd5fd..cad78e4853 100644 --- a/app/src/ai/blocklist/usage/conversation_usage_view.rs +++ b/app/src/ai/blocklist/usage/conversation_usage_view.rs @@ -38,6 +38,7 @@ pub enum DisplayMode { pub struct ConversationUsageInfo { pub credits_spent: f32, + pub platform_credits_spent: f32, // Credits spent over the last block, where the block comprises // all agent outputs since the most recent user input. pub credits_spent_for_last_block: Option, @@ -299,7 +300,7 @@ impl ConversationUsageView { let total_credits_value = rollup .as_ref() .map(|r| r.total_credits) - .unwrap_or(self.usage_info.credits_spent); + .unwrap_or(self.usage_info.credits_spent + self.usage_info.platform_credits_spent); if self.display_mode == DisplayMode::Footer && self.usage_info.credits_spent_for_last_block.is_some() diff --git a/app/src/ai/blocklist/usage/conversation_usage_view_tests.rs b/app/src/ai/blocklist/usage/conversation_usage_view_tests.rs index 3fea40fc4a..a844cb0634 100644 --- a/app/src/ai/blocklist/usage/conversation_usage_view_tests.rs +++ b/app/src/ai/blocklist/usage/conversation_usage_view_tests.rs @@ -34,6 +34,7 @@ use crate::persistence::model::{ModelTokenUsage, PRIMARY_AGENT_CATEGORY}; fn placeholder_usage_info() -> ConversationUsageInfo { ConversationUsageInfo { credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, tool_calls: 0, models: Vec::new(), diff --git a/app/src/pane_group/mod_tests.rs b/app/src/pane_group/mod_tests.rs index 795a5df872..fd11e4002d 100644 --- a/app/src/pane_group/mod_tests.rs +++ b/app/src/pane_group/mod_tests.rs @@ -334,6 +334,7 @@ fn test_server_conversation_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/server/server_api/ai.rs b/app/src/server/server_api/ai.rs index 23afb99049..59696fd27b 100644 --- a/app/src/server/server_api/ai.rs +++ b/app/src/server/server_api/ai.rs @@ -2904,11 +2904,13 @@ fn convert_usage_metadata( summarized: bool, context_window_usage: f64, credits_spent: f64, + platform_credits_spent: f64, ) -> ConversationUsageMetadata { ConversationUsageMetadata { was_summarized: summarized, context_window_usage: context_window_usage as f32, credits_spent: credits_spent as f32, + platform_credits_spent: platform_credits_spent as f32, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), @@ -2923,6 +2925,7 @@ impl TryFrom for ServerAIConversationMetadata value.usage.usage_metadata.summarized, value.usage.usage_metadata.context_window_usage, value.usage.usage_metadata.credits_spent, + value.usage.usage_metadata.platform_credits_spent, ); let metadata = value.metadata.try_into()?; let permissions = value.permissions.try_into()?; @@ -2967,6 +2970,7 @@ impl TryFrom Res let usage_metadata = Some( api_response_event::stream_finished::ConversationUsageMetadata { context_window_usage: conversation.context_window_usage(), - credits_spent: conversation.credits_spent(), - platform_credits_spent: 0.0, + credits_spent: conversation.inference_credits_spent(), + platform_credits_spent: conversation.platform_credits_spent(), summarized: conversation.was_summarized(), #[allow(deprecated)] token_usage: conversation diff --git a/app/src/terminal/view.rs b/app/src/terminal/view.rs index 7a714987fe..2b279ff01a 100644 --- a/app/src/terminal/view.rs +++ b/app/src/terminal/view.rs @@ -6459,7 +6459,8 @@ impl TerminalView { conversation.wall_to_wall_response_time_since_last_query(); let conversation_usage_info = ConversationUsageInfo { - credits_spent: conversation.credits_spent(), + credits_spent: conversation.inference_credits_spent(), + platform_credits_spent: conversation.platform_credits_spent(), credits_spent_for_last_block: conversation.credits_spent_for_last_block(), tool_calls: tool_usage.total_tool_calls(), models: conversation.token_usage().to_vec(), diff --git a/app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs b/app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs index 526fc5dde1..1c67776dc8 100644 --- a/app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs +++ b/app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs @@ -274,6 +274,7 @@ fn server_conversation_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/terminal/view/shared_session/view_impl_tests.rs b/app/src/terminal/view/shared_session/view_impl_tests.rs index 2c66bf6bad..0de84d3333 100644 --- a/app/src/terminal/view/shared_session/view_impl_tests.rs +++ b/app/src/terminal/view/shared_session/view_impl_tests.rs @@ -580,6 +580,7 @@ fn server_conversation_metadata( was_summarized: false, context_window_usage: 0.0, credits_spent: 0.0, + platform_credits_spent: 0.0, credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), diff --git a/app/src/workspaces/gql_convert.rs b/app/src/workspaces/gql_convert.rs index bb2542b3a0..2e7d936b27 100644 --- a/app/src/workspaces/gql_convert.rs +++ b/app/src/workspaces/gql_convert.rs @@ -240,6 +240,7 @@ impl From<&gql_usage::ConversationUsage> for ConversationUsageInfo { fn from(gql: &gql_usage::ConversationUsage) -> Self { let persistence::model::ConversationUsageMetadata { credits_spent, + platform_credits_spent, token_usage: models, tool_usage_metadata: tool, context_window_usage, @@ -247,6 +248,7 @@ impl From<&gql_usage::ConversationUsage> for ConversationUsageInfo { } = (&gql.usage_metadata).into(); ConversationUsageInfo { credits_spent, + platform_credits_spent, credits_spent_for_last_block: None, tool_calls: tool.total_tool_calls(), models, diff --git a/crates/graphql/src/api/ai.rs b/crates/graphql/src/api/ai.rs index edf06bc710..75e1b2e657 100644 --- a/crates/graphql/src/api/ai.rs +++ b/crates/graphql/src/api/ai.rs @@ -166,5 +166,6 @@ pub struct ConversationUsage { pub struct ConversationUsageMetadata { pub context_window_usage: f64, pub credits_spent: f64, + pub platform_credits_spent: f64, pub summarized: bool, } diff --git a/crates/graphql/src/api/queries/get_conversation_usage.rs b/crates/graphql/src/api/queries/get_conversation_usage.rs index be51d937bc..140ef00316 100644 --- a/crates/graphql/src/api/queries/get_conversation_usage.rs +++ b/crates/graphql/src/api/queries/get_conversation_usage.rs @@ -25,6 +25,7 @@ query GetConversationUsage( usageMetadata { contextWindowUsage creditsSpent + platformCreditsSpent summarized tokenUsage { modelId totalTokens } warpTokenUsage { modelId totalTokens tokenUsageByCategory { category tokens } } @@ -114,6 +115,7 @@ pub struct ConversationUsage { pub struct ConversationUsageMetadata { pub context_window_usage: f64, pub credits_spent: f64, + pub platform_credits_spent: f64, pub summarized: bool, pub token_usage: Vec, pub warp_token_usage: Vec, @@ -170,6 +172,7 @@ impl From<&ConversationUsageMetadata> for persistence::model::ConversationUsageM was_summarized: gql.summarized, context_window_usage: gql.context_window_usage as f32, credits_spent: gql.credits_spent as f32, + platform_credits_spent: gql.platform_credits_spent as f32, credits_spent_for_last_block: None, token_usage: convert_token_usage(&gql.warp_token_usage, &gql.byok_token_usage), tool_usage_metadata: (&gql.tool_usage_metadata).into(), diff --git a/crates/persistence/src/model.rs b/crates/persistence/src/model.rs index dc722716c5..ae5c0e01ce 100644 --- a/crates/persistence/src/model.rs +++ b/crates/persistence/src/model.rs @@ -1358,6 +1358,8 @@ pub struct ConversationUsageMetadata { pub context_window_usage: f32, pub credits_spent: f32, #[serde(default)] + pub platform_credits_spent: f32, + #[serde(default)] pub credits_spent_for_last_block: Option, #[serde(default)] pub token_usage: Vec, diff --git a/crates/warp_graphql_schema/api/schema.graphql b/crates/warp_graphql_schema/api/schema.graphql index 0521480417..909dccf1bb 100644 --- a/crates/warp_graphql_schema/api/schema.graphql +++ b/crates/warp_graphql_schema/api/schema.graphql @@ -790,7 +790,12 @@ type ConversationUsageMetadata { """Token usage using a user's API key, keyed by model.""" byokTokenUsage: [TokenUsage!]! contextWindowUsage: Float! + + """The total number of inference credits spent so far in the conversation""" creditsSpent: Float! + + """The total number of platform credits spent so far in the conversation.""" + platformCreditsSpent: Float! summarized: Boolean! """ @@ -1379,6 +1384,7 @@ union FreeAvailableModelsResult = FreeAvailableModelsOutput | UserFacingError type GcpProviderConfig { projectNumber: String! + serviceAccountEmail: String workloadIdentityFederationPoolId: String! workloadIdentityFederationProviderId: String! } @@ -4365,4 +4371,4 @@ enum WriteToPtyAutonomyValue { ALWAYS_ASK ASK_ON_FIRST_WRITE RESPECT_USER_SETTING -} +} \ No newline at end of file diff --git a/skills-lock.json b/skills-lock.json index 02dd8bde28..800b703b99 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -61,6 +61,12 @@ "skillPath": ".agents/skills/resolve-merge-conflicts/SKILL.md", "computedHash": "5376b5692901c624e8f20a5a04aeea5f5a94f5168d29852a8a639aded6408f2e" }, + "respond-to-pr-comments-in-blocklist": { + "source": "warpdotdev/common-skills", + "sourceType": "github", + "skillPath": ".agents/skills/respond-to-pr-comments-in-blocklist/SKILL.md", + "computedHash": "f7408cf90c10397aa9048f14ab985a138641fc1e5f3245e290150437d62875f0" + }, "review-pr": { "source": "warpdotdev/common-skills", "sourceType": "github", @@ -71,7 +77,7 @@ "source": "warpdotdev/common-skills", "sourceType": "github", "skillPath": ".agents/skills/spec-driven-implementation/SKILL.md", - "computedHash": "e334d0f6f0e8fc39055314acad911f36d92d1919372b5e2973cc99d7f8c901b4" + "computedHash": "45793ca1e35b032ddfd2596f2e86fd6f6e938549373bfe4aeb74683486a179e4" }, "update-skill": { "source": "warpdotdev/common-skills", @@ -79,6 +85,12 @@ "skillPath": ".agents/skills/update-skill/SKILL.md", "computedHash": "1e23c5a5c37ed084eced7fa507031e3cdb8e23f09cd5d004e00efd6f66bf200f" }, + "validate-changes-match-specs": { + "source": "warpdotdev/common-skills", + "sourceType": "github", + "skillPath": ".agents/skills/validate-changes-match-specs/SKILL.md", + "computedHash": "9123fd70ced064bdd773fd8d2baa8d5d5291fef910eb2c028084074b3ac72c27" + }, "write-product-spec": { "source": "warpdotdev/common-skills", "sourceType": "github", @@ -89,7 +101,7 @@ "source": "warpdotdev/common-skills", "sourceType": "github", "skillPath": ".agents/skills/write-tech-spec/SKILL.md", - "computedHash": "3b5eb4ef021112d473984eca28412d372e87d9337ad5d9754f3ad3e01f94d39b" + "computedHash": "c7913bfd1ea2be7ce38d5beb7e923b96f5689f6145250af1d81b985e8be4a882" } } }