| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| //! Root-authorized accounting reads; all data comes from physical EventLog facts. | |
| use maka_agent_facts::accounting::Activity; | |
| use maka_agent_facts::accounting::Group; | |
| use maka_agent_facts::accounting::ModelSummary; | |
| use maka_agent_facts::accounting::ModelTotals; | |
| use maka_agent_facts::accounting::Pending; | |
| use maka_agent_facts::accounting::ProviderSummary; | |
| use maka_agent_facts::accounting::Selection; | |
| use maka_agent_facts::accounting::ToolSummary; | |
| use maka_agent_facts::accounting::ToolTotals; | |
| use schemars::JsonSchema; | |
| use serde::{Deserialize, Serialize}; | |
| use serde_json::Value; | |
| #[derive(Clone, Deserialize, JsonSchema)] | |
| #[serde(rename_all = "camelCase", deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsageRange")] | |
| pub struct Range { | |
| #[schemars(range(min = 0, max = 9_007_199_254_740_991u64))] | |
| pub from: f64, | |
| #[schemars(range(min = 0, max = 9_007_199_254_740_991u64))] | |
| pub to: f64, | |
| #[serde(deserialize_with = "Option::deserialize")] | |
| #[schemars(with = "maka_plugin_core::wire_schema::RequiredNullable<String>")] | |
| pub session_id: Option<String>, | |
| #[serde(deserialize_with = "Option::deserialize")] | |
| #[schemars(with = "maka_plugin_core::wire_schema::RequiredNullable<u64>")] | |
| pub through: Option<u64>, | |
| } | |
| impl From<Range> for maka_event_log::usage::Query { | |
| fn from(value: Range) -> Self { | |
| Self { | |
| from: value.from, | |
| to: value.to, | |
| session_id: value.session_id, | |
| through: value.through, | |
| } | |
| } | |
| } | |
| #[derive(Deserialize, JsonSchema)] | |
| #[serde(deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsageRead")] | |
| pub struct Read { | |
| pub range: Range, | |
| pub selection: Selection, | |
| #[schemars(range(max = 9_007_199_254_740_991u64))] | |
| pub offset: u64, | |
| } | |
| #[derive(Serialize, JsonSchema)] | |
| #[serde(rename_all = "camelCase", deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsagePage")] | |
| pub struct Page { | |
| pub through: u64, | |
| #[schemars(length(max = 32))] | |
| pub attempts: Vec<Activity>, | |
| pub total: u64, | |
| pub next_offset: Option<u64>, | |
| } | |
| #[derive(Deserialize, JsonSchema)] | |
| #[serde(deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsageSummaryRead")] | |
| pub struct SummaryRead { | |
| pub range: Range, | |
| pub group: Group, | |
| #[schemars(range(max = 9_007_199_254_740_991u64))] | |
| pub offset: u64, | |
| } | |
| #[derive(Serialize, JsonSchema)] | |
| #[serde(rename_all = "camelCase", deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsageTotals")] | |
| pub struct Totals { | |
| pub models: ModelTotals, | |
| pub tools: ToolTotals, | |
| pub pending: Pending, | |
| #[schemars(length(max = 32))] | |
| pub by_provider: Vec<ProviderSummary>, | |
| #[schemars(length(max = 32))] | |
| pub by_model: Vec<ModelSummary>, | |
| #[schemars(length(max = 32))] | |
| pub by_tool: Vec<ToolSummary>, | |
| } | |
| #[derive(Serialize, JsonSchema)] | |
| #[serde(rename_all = "camelCase", deny_unknown_fields)] | |
| #[schemars(rename = "SettingsUsageSummary")] | |
| pub struct Report { | |
| pub through: u64, | |
| pub summary: Totals, | |
| pub next_offset: Option<u64>, | |
| } | |
| #[derive(Serialize, JsonSchema)] | |
| #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] | |
| #[schemars(rename = "SettingsPricingResult")] | |
| pub enum PricingResult { | |
| Updated { | |
| result: maka_agent_facts::pricing::Updated, | |
| }, | |
| Rejected, | |
| } | |
| pub(crate) async fn page(log: &maka_event_log::EventLog, input: Value) -> Result<Value, String> { | |
| let input: Read = serde_json::from_value(input).map_err(|_| "settings_input_invalid")?; | |
| let page = log | |
| .usage_activity(input.range.into(), input.selection, input.offset, 32) | |
| .await | |
| .map_err(|_| "settings_usage_unavailable")?; | |
| super::connections::encode(Page { | |
| through: page.through, | |
| attempts: page.attempts, | |
| total: page.total, | |
| next_offset: page.next_offset, | |
| }) | |
| } | |
| pub(crate) async fn summary(log: &maka_event_log::EventLog, input: Value) -> Result<Value, String> { | |
| let input: SummaryRead = serde_json::from_value(input).map_err(|_| "settings_input_invalid")?; | |
| let report = log | |
| .usage_summary_page(input.range.into(), input.group, input.offset) | |
| .await | |
| .map_err(|_| "settings_usage_unavailable")?; | |
| let summary = report.summary; | |
| super::connections::encode(Report { | |
| through: report.through, | |
| next_offset: report.next_offset, | |
| summary: Totals { | |
| models: summary.models, | |
| tools: summary.tools, | |
| pending: summary.pending, | |
| by_provider: summary.by_provider, | |
| by_model: summary.by_model, | |
| by_tool: summary.by_tool, | |
| }, | |
| }) | |
| } | |
| pub(crate) async fn prices( | |
| store: &maka_config::ConfigurationStore, | |
| input: Value, | |
| ) -> Result<Value, String> { | |
| let input = serde_json::from_value(input).map_err(|_| "settings_input_invalid")?; | |
| super::connections::encode( | |
| store | |
| .query_pricing(input) | |
| .await | |
| .map_err(|_| "settings_prices_unavailable")?, | |
| ) | |
| } | |
| pub(crate) async fn update_price( | |
| store: &maka_config::ConfigurationStore, | |
| input: Value, | |
| ) -> Result<Value, String> { | |
| let Ok(input) = serde_json::from_value(input) else { | |
| return super::connections::encode(PricingResult::Rejected); | |
| }; | |
| let result = match store.update_pricing(input).await { | |
| Ok(result) => PricingResult::Updated { result }, | |
| Err(maka_config::ConfigError::Invalid(_)) => PricingResult::Rejected, | |
| Err(_) => return Err("settings_effect_unknown".into()), | |
| }; | |
| super::connections::encode(result) | |
| } |