Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src-tauri/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub struct AppState {
/// The upgrade UI subscribes to it and re-syncs from a snapshot on mount,
/// so download progress survives settings-page navigation and reloads.
pub update_state: crate::update::AppUpdateStateHandle,
/// Quota tracking and fetch manager for supported agents.
pub quota_manager: Arc<crate::quota::QuotaManager>,
/// Headless `codeg-cli` runs. Built in both runtimes at startup; its
/// routes are served under `/api` and on the loopback listener.
pub cli_runs: Arc<crate::cli_run::CliRunService>,
Expand Down Expand Up @@ -124,6 +126,10 @@ pub fn default_chat_channel_manager() -> ChatChannelManager {
ChatChannelManager::new()
}

pub fn default_quota_manager() -> Arc<crate::quota::QuotaManager> {
Arc::new(crate::quota::QuotaManager::new())
}

/// Build the delegation broker + token registry + per-process UDS socket
/// path. Shared between codeg-server bootstrap and the Tauri `setup` block
/// so both modes apply identical depth limit + timeout defaults.
Expand Down Expand Up @@ -300,6 +306,7 @@ impl AppState {
computer_service: std::sync::OnceLock::new(),
system_op_lock: default_system_op_lock(),
update_state: default_update_state(),
quota_manager: Arc::new(crate::quota::QuotaManager::new()),
cli_runs,
cli_access,
}
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/bin/codeg_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ async fn async_main(
computer_service: std::sync::OnceLock::new(),
system_op_lock: codeg_lib::app_state::default_system_op_lock(),
update_state: codeg_lib::app_state::default_update_state(),
quota_manager: codeg_lib::app_state::default_quota_manager(),
cli_runs,
cli_access,
});
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub mod pet;
pub mod project_boot;
pub mod question;
pub mod quick_messages;
pub mod quota;
#[cfg(feature = "tauri-runtime")]
pub mod remote_proxy;
#[cfg(feature = "tauri-runtime")]
Expand Down
109 changes: 109 additions & 0 deletions src-tauri/src/commands/quota.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#[cfg(feature = "tauri-runtime")]
use std::sync::Arc;

use crate::app_error::AppCommandError;
use crate::db::service::agent_setting_service;
use crate::db::AppDatabase;
use crate::models::{AgentQuotaInfo, AgentType};
use crate::parsers::AgentEnv;
use crate::quota::QuotaManager;

async fn resolve_agent_quota_context(
db: Option<&AppDatabase>,
agent_type_str: &str,
) -> (AgentEnv, Option<i32>) {
let Some(db) = db else {
return (AgentEnv::inherited(), None);
};
let Ok(parsed_type) = serde_json::from_value::<AgentType>(serde_json::Value::String(
agent_type_str.to_string(),
)) else {
return (AgentEnv::inherited(), None);
};

let setting = agent_setting_service::get_by_agent_type(&db.conn, parsed_type)
.await
.ok()
.flatten();
let model_provider_id = setting.as_ref().and_then(|s| s.model_provider_id);
let local_config_json = crate::commands::acp::load_agent_local_config_json(parsed_type);
let runtime_env = crate::commands::acp::build_runtime_env_from_setting(
parsed_type,
setting.as_ref(),
local_config_json.as_deref(),
);

(AgentEnv::from_overrides(runtime_env), model_provider_id)
}

pub async fn get_agent_quota_core(
quota_manager: &QuotaManager,
agent_type: &str,
) -> Result<AgentQuotaInfo, AppCommandError> {
get_agent_quota_with_db_core(quota_manager, None, agent_type).await
}

pub async fn get_agent_quota_with_db_core(
quota_manager: &QuotaManager,
db: Option<&AppDatabase>,
agent_type: &str,
) -> Result<AgentQuotaInfo, AppCommandError> {
let (env, model_provider_id) = resolve_agent_quota_context(db, agent_type).await;
quota_manager
.get_quota_for_env(agent_type, &env, model_provider_id)
.await
.map_err(|e| AppCommandError::external_command("Failed to get agent quota", e))
}

pub async fn refresh_agent_quota_core(
quota_manager: &QuotaManager,
agent_type: &str,
) -> Result<AgentQuotaInfo, AppCommandError> {
refresh_agent_quota_with_db_core(quota_manager, None, agent_type).await
}

pub async fn refresh_agent_quota_with_db_core(
quota_manager: &QuotaManager,
db: Option<&AppDatabase>,
agent_type: &str,
) -> Result<AgentQuotaInfo, AppCommandError> {
let (env, model_provider_id) = resolve_agent_quota_context(db, agent_type).await;
quota_manager
.fetch_quota_for_env(agent_type, &env, model_provider_id)
.await
.map_err(|e| AppCommandError::external_command("Failed to refresh agent quota", e))
}

#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn get_agent_quota(
#[cfg(feature = "tauri-runtime")] quota_manager: tauri::State<'_, Arc<QuotaManager>>,
#[cfg(feature = "tauri-runtime")] db: tauri::State<'_, AppDatabase>,
agent_type: String,
) -> Result<AgentQuotaInfo, AppCommandError> {
#[cfg(feature = "tauri-runtime")]
{
get_agent_quota_with_db_core(&quota_manager, Some(&db), &agent_type).await
}
#[cfg(not(feature = "tauri-runtime"))]
{
let _ = agent_type;
Err(AppCommandError::configuration_invalid("tauri-only command"))
}
}

#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn refresh_agent_quota(
#[cfg(feature = "tauri-runtime")] quota_manager: tauri::State<'_, Arc<QuotaManager>>,
#[cfg(feature = "tauri-runtime")] db: tauri::State<'_, AppDatabase>,
agent_type: String,
) -> Result<AgentQuotaInfo, AppCommandError> {
#[cfg(feature = "tauri-runtime")]
{
refresh_agent_quota_with_db_core(&quota_manager, Some(&db), &agent_type).await
}
#[cfg(not(feature = "tauri-runtime"))]
{
let _ = agent_type;
Err(AppCommandError::configuration_invalid("tauri-only command"))
}
}
5 changes: 5 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub mod pets;
#[cfg(feature = "tauri-runtime")]
pub mod preferences;
pub mod process;
pub mod quota;
#[cfg(all(feature = "tauri-runtime", target_os = "macos"))]
mod relaunch;
pub mod runtime_lock;
Expand Down Expand Up @@ -118,6 +119,7 @@ mod tauri_app {
folders, logging as logging_commands, mcp as mcp_commands,
model_provider as model_provider_commands, notification, pet as pet_commands, project_boot,
question as question_commands, quick_messages as quick_messages_commands,
quota as quota_commands,
remote_proxy as remote_proxy_commands,
remote_workspace as remote_workspace_commands, science as science_commands,
session_info as session_info_commands,
Expand Down Expand Up @@ -755,6 +757,7 @@ mod tauri_app {
// embedded web server's AppState so HTTP and webview clients see the
// same download progress; lets the upgrade UI survive navigation.
.manage(crate::update::new_update_state_handle())
.manage(std::sync::Arc::new(crate::quota::QuotaManager::new()))
.setup(|app| {
let app_data_dir = app.path().app_data_dir()?;

Expand Down Expand Up @@ -2300,6 +2303,8 @@ mod tauri_app {
token_usage_commands::token_usage_facets,
token_usage_commands::token_usage_status,
token_usage_commands::token_usage_sync,
quota_commands::get_agent_quota,
quota_commands::refresh_agent_quota,
work_task_commands::work_task_list,
work_task_commands::work_task_get,
work_task_commands::work_task_events,
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod message;
pub mod model_provider;
pub mod pet;
pub mod quick_message;
pub mod quota;
pub mod remote_workspace_connection;
pub mod system;
pub mod token_usage;
Expand Down Expand Up @@ -43,6 +44,7 @@ pub use message::{
MessageTurn, TurnRole, TurnUsage, UnifiedMessage,
};
pub use quick_message::QuickMessageInfo;
pub use quota::{AgentQuotaInfo, QuotaWindow, SpendLimit};
pub use remote_workspace_connection::{
RemoteWorkspaceConnectionInfo, RemoteWorkspaceHeader, ToHeaderMap,
};
Expand Down
Loading
Loading