| /* | |
| * 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. | |
| */ | |
| mod diagnostics; | |
| pub(crate) mod oauth; | |
| use futures_util::future::BoxFuture; | |
| use maka_agent_engine::{ModelSource, PreparedModel, RunError}; | |
| use maka_agent_facts::composition::SourceRevision; | |
| use maka_agent_facts::configuration::ModelCapabilities; | |
| use maka_agent_facts::configuration::ModelInfo; | |
| use maka_agent_facts::configuration::ModelSelection; | |
| use maka_agent_facts::context::ModelRequestContext; | |
| use maka_agent_facts::execution::EditingTools; | |
| use maka_agent_facts::execution::ModelBinding; | |
| use maka_agent_facts::execution::ThinkingLevel; | |
| use maka_agent_facts::execution::ToolMode; | |
| use maka_agent_ports::provider::Connection; | |
| use maka_agent_ports::provider::Definition; | |
| use maka_agent_ports::provider::Model; | |
| use maka_agent_ports::provider::Resolve; | |
| use maka_config::{ConfigurationStore, ConnectionObservation}; | |
| use maka_model::{ProviderAuth, ProviderConfig}; | |
| use serde::{Deserialize, Serialize}; | |
| use serde_json::Value; | |
| use std::sync::Arc; | |
| use tracing::Instrument; | |
| /// Only parameters actually supplied to execution. Catalog labels, tests and | |
| /// other models are observations, not a mutable authority over an accepted run. | |
| #[derive(Clone, PartialEq, Serialize, Deserialize)] | |
| #[serde(deny_unknown_fields)] | |
| struct ExecutionBasis { | |
| adapter: String, | |
| protocol: maka_agent_facts::model::request::ProviderKind, | |
| base_url: String, | |
| capabilities: ModelCapabilities, | |
| options: Value, | |
| main_output_limit: Option<u64>, | |
| body_overlay: Option<Value>, | |
| } | |
| impl ExecutionBasis { | |
| fn new(model: &Model, row: &maka_agent_facts::configuration::ConnectionCatalogEntry) -> Self { | |
| Self { | |
| adapter: model.adapter.clone(), | |
| protocol: model.protocol.clone(), | |
| base_url: model.base_url.clone(), | |
| capabilities: model.info.capabilities.unwrap_or_default(), | |
| options: model.provider_options.clone(), | |
| main_output_limit: model.main_output_limit, | |
| body_overlay: row.request_body_overlay.clone(), | |
| } | |
| } | |
| } | |
| /// Accepted route facts, without credentials or live activation handles. | |
| #[derive(Clone, PartialEq, Serialize, Deserialize)] | |
| #[serde(deny_unknown_fields)] | |
| pub(crate) struct AcceptedModel { | |
| pub binding: ModelBinding, | |
| pub thinking_level: Option<ThinkingLevel>, | |
| /// Execution policy is captured at message admission. Later model settings | |
| /// do not reinterpret queued work. | |
| pub tool_mode: ToolMode, | |
| #[serde( | |
| default, | |
| skip_serializing_if = "maka_agent_facts::execution::CodeLanguage::is_javascript" | |
| )] | |
| pub code_language: maka_agent_facts::execution::CodeLanguage, | |
| /// None captures an unfiltered tool selection. | |
| #[serde(deserialize_with = "Option::deserialize")] | |
| pub editing_tools: Option<EditingTools>, | |
| context: ModelRequestContext, | |
| /// Public defaults at admission only; account reports and overrides stay live. | |
| #[serde(deserialize_with = "Option::deserialize")] | |
| public_model: Option<ModelInfo>, | |
| provider: maka_agent_facts::provider::Identity, | |
| provider_configuration: Value, | |
| basis: ExecutionBasis, | |
| } | |
| #[derive(Clone)] | |
| pub(crate) struct Credentials { | |
| pub executor: maka_model::ModelExecutor, | |
| pub refresh: Arc<oauth::Authority>, | |
| } | |
| pub(crate) struct Source { | |
| configuration: Arc<ConfigurationStore>, | |
| credentials: Credentials, | |
| pub accepted: AcceptedModel, | |
| definition: Arc<Definition>, | |
| session: String, | |
| revision: SourceRevision, | |
| } | |
| impl Source { | |
| pub async fn select( | |
| configuration: Arc<ConfigurationStore>, | |
| catalog: Arc<maka_providers::catalog::Catalog>, | |
| credentials: Credentials, | |
| selection: ModelSelection, | |
| thinking_level: Option<ThinkingLevel>, | |
| session: String, | |
| mut revision: SourceRevision, | |
| ) -> Result<(Self, PreparedModel), RunError> { | |
| let span = tracing::warn_span!("agent_model_preparation", session_id = %session, | |
| connection_id = %selection.connection_id, activation = %revision.activation); | |
| async move { | |
| let public_guard = catalog.snapshot.read().await; | |
| let snapshot = public_guard.as_ref().map_err(|_| unavailable())?.clone(); | |
| let material = configuration | |
| .observe_selected_model(selection.clone()) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::configuration("selected_model_observation", &error, Some(&selection.connection_id)); | |
| unavailable() | |
| })? | |
| .ok_or_else(unavailable)?; | |
| drop(public_guard); | |
| let row = &material.connection; | |
| let public_model = snapshot.get(&row.provider.name, &selection.model_id).cloned(); | |
| let snapshot = Arc::new(maka_providers::catalog::Snapshot::for_model( | |
| &row.provider.name, &selection.model_id, public_model.clone(), | |
| ).map_err(|_| unavailable())?); | |
| let definition = | |
| Arc::new(maka_providers::definition(&row.provider, snapshot).map_err(|_| { | |
| tracing::warn!(stage = "provider_definition", cause = "provider_definition_unavailable", | |
| provider = %row.provider.name, "Model preparation failed"); | |
| unavailable() | |
| })?); | |
| let model = resolve(&definition, row, &selection.model_id, thinking_level).await?; | |
| revision.name = row.provider.name.clone(); | |
| let source = Self { | |
| configuration, | |
| credentials, | |
| accepted: AcceptedModel { | |
| context: request_context(row, &model.info)?, | |
| public_model, | |
| editing_tools: Some(editing_tools(row, &selection.model_id, &model.base_url)), | |
| tool_mode: ToolMode::for_model( | |
| &selection.model_id, | |
| &model.base_url, | |
| row.model_overrides | |
| .as_ref() | |
| .and_then(|models| models.get(&selection.model_id)) | |
| .and_then(|overrides| overrides.code_mode), | |
| ), | |
| code_language: row.model_overrides.as_ref() | |
| .and_then(|models| models.get(&selection.model_id)) | |
| .and_then(|overrides| overrides.code_mode_language) | |
| .unwrap_or_default(), | |
| binding: ModelBinding { | |
| connection_id: row.connection_id.clone(), | |
| connection_slug: row.slug.clone(), | |
| model: selection.model_id, | |
| }, | |
| thinking_level, | |
| provider: row.provider.clone(), | |
| provider_configuration: row.configuration.clone(), | |
| basis: ExecutionBasis::new(&model, row), | |
| }, | |
| definition, | |
| session, | |
| revision, | |
| }; | |
| let prepared = source.prepare(material, model).await?; | |
| Ok((source, prepared)) | |
| }.instrument(span).await | |
| } | |
| pub fn restore( | |
| configuration: Arc<ConfigurationStore>, | |
| credentials: Credentials, | |
| accepted: AcceptedModel, | |
| session: String, | |
| mut revision: SourceRevision, | |
| ) -> Result<Self, RunError> { | |
| let _span = tracing::warn_span!("agent_model_preparation", session_id = %session, | |
| connection_id = %accepted.binding.connection_id, activation = %revision.activation) | |
| .entered(); | |
| let snapshot = maka_providers::catalog::Snapshot::for_model( | |
| &accepted.provider.name, | |
| &accepted.binding.model, | |
| accepted.public_model.clone(), | |
| ) | |
| .map_err(|_| unavailable())?; | |
| let definition = | |
| Arc::new(maka_providers::definition(&accepted.provider, Arc::new(snapshot)).map_err(|_| { | |
| tracing::warn!(stage = "restore_provider_definition", cause = "provider_definition_unavailable", | |
| provider = %accepted.provider.name, "Model preparation failed"); | |
| unavailable() | |
| })?); | |
| revision.name = accepted.provider.name.clone(); | |
| Ok(Self { | |
| configuration, | |
| credentials, | |
| accepted, | |
| definition, | |
| session, | |
| revision, | |
| }) | |
| } | |
| async fn prepare( | |
| &self, | |
| material: ConnectionObservation, | |
| model: Model, | |
| ) -> Result<PreparedModel, RunError> { | |
| let capabilities = model.info.capabilities.unwrap_or_default(); | |
| if capabilities.chat == Some(false) { | |
| tracing::warn!( | |
| stage = "model_capabilities", | |
| cause = "chat_unsupported", | |
| "Model preparation failed" | |
| ); | |
| return Err(unavailable()); | |
| } | |
| let row = &material.connection; | |
| let network = maka_network::Policy::from_host_settings( | |
| &material.network.proxy, | |
| material.network.password.as_deref(), | |
| ) | |
| .map_err(|error| { | |
| diagnostics::network( | |
| "network_policy", | |
| &error, | |
| &self.accepted.binding.connection_id, | |
| ); | |
| unavailable() | |
| })?; | |
| let credential = match material.credential { | |
| Some(snapshot) if snapshot.credential().refresh_at.is_some() => Some( | |
| self.credentials | |
| .refresh | |
| .bind(snapshot) | |
| .map_err(|_| RunError::InvalidInput("agent_authentication_unavailable".into()))? | |
| .resolve_native( | |
| self.accepted.provider.clone(), | |
| self.definition.clone(), | |
| maka_agent_ports::provider::Context { | |
| transport: self.credentials.executor.transport(&network).map_err( | |
| |error| { | |
| diagnostics::model( | |
| "credential_transport", | |
| &error, | |
| &self.accepted.binding.connection_id, | |
| ); | |
| unavailable() | |
| }, | |
| )?, | |
| cancellation: tokio_util::sync::CancellationToken::new(), | |
| interaction: None, | |
| }, | |
| ) | |
| .await | |
| .map_err(|_| RunError::InvalidInput("agent_authentication_unavailable".into()))? | |
| .credential() | |
| .clone(), | |
| ), | |
| Some(snapshot) => Some(snapshot.credential().clone()), | |
| None => None, | |
| }; | |
| let credentials = self | |
| .definition | |
| .authorize(connection(row), credential, self.session.clone()) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::provider( | |
| "provider_authorize", | |
| &error, | |
| &row.connection_id, | |
| &row.provider.name, | |
| ); | |
| RunError::InvalidInput("agent_authentication_unavailable".into()) | |
| })?; | |
| let auth = match credentials { | |
| maka_agent_ports::model::Credentials::ApiKey(key) => ProviderAuth::ApiKey(key), | |
| maka_agent_ports::model::Credentials::RequestHeaders(headers) => { | |
| ProviderAuth::RequestHeaders(headers) | |
| } | |
| }; | |
| Ok(PreparedModel { | |
| provider_id: self.accepted.provider.name.clone(), | |
| provider: ProviderConfig { | |
| adapter: Some(model.adapter), | |
| capabilities, | |
| kind: model.protocol, | |
| model: self.accepted.binding.model.clone(), | |
| base_url: model.base_url, | |
| auth, | |
| headers: material.request_headers, | |
| network, | |
| body_overlay: row | |
| .request_body_overlay | |
| .as_ref() | |
| .map(|value| value.as_object().expect("validated overlay").clone()), | |
| }, | |
| options: model.provider_options, | |
| context: Some(self.accepted.context.clone()), | |
| main_output_limit: model.main_output_limit, | |
| supports_vision: capabilities.vision.unwrap_or(false), | |
| revision: self.revision.clone(), | |
| }) | |
| } | |
| } | |
| fn request_context( | |
| row: &maka_agent_facts::configuration::ConnectionCatalogEntry, | |
| model: &ModelInfo, | |
| ) -> Result<ModelRequestContext, RunError> { | |
| if matches!((model.context_window, model.input_limit), (Some(capacity), Some(input)) if input > capacity) | |
| { | |
| tracing::warn!(stage = "request_context", cause = "input_limit_exceeds_context", connection_id = %row.connection_id, "Model preparation failed"); | |
| return Err(unavailable()); | |
| } | |
| let input = match (model.context_window, model.input_limit) { | |
| (Some(capacity), Some(input)) => Some(capacity.min(input)), | |
| (capacity, input) => capacity.or(input), | |
| }; | |
| let context = ModelRequestContext { | |
| provider_id: row.provider.name.clone(), | |
| context_window: input, | |
| model_context_window: model.context_window, | |
| declared_window: row | |
| .model_overrides | |
| .as_ref() | |
| .and_then(|models| models.get(&model.id)) | |
| .and_then(|profile| profile.compaction_threshold) | |
| .or_else(|| input.map(|limit| ((u128::from(limit) * 85 / 100) as u64).max(1))), | |
| }; | |
| context.validate().map_err(|_| { | |
| tracing::warn!(stage = "request_context", cause = "request_context_invalid", connection_id = %row.connection_id, "Model preparation failed"); | |
| unavailable() | |
| })?; | |
| Ok(context) | |
| } | |
| impl Source { | |
| fn preparation_span(&self) -> tracing::Span { | |
| tracing::warn_span!("agent_model_preparation", session_id = %self.session, | |
| connection_id = %self.accepted.binding.connection_id, activation = %self.revision.activation, | |
| provider = %self.accepted.provider.name) | |
| } | |
| async fn observe(&self) -> Result<(ConnectionObservation, Model), RunError> { | |
| let material = self | |
| .configuration | |
| .observe_model(self.accepted.binding.clone()) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::configuration( | |
| "accepted_model_observation", | |
| &error, | |
| Some(&self.accepted.binding.connection_id), | |
| ); | |
| unavailable() | |
| })? | |
| .ok_or_else(|| { | |
| tracing::warn!( | |
| stage = "accepted_model_observation", | |
| cause = "accepted_source_unavailable", | |
| "Model preparation failed" | |
| ); | |
| unavailable() | |
| })?; | |
| let row = &material.connection; | |
| if row.provider != self.accepted.provider { | |
| tracing::warn!( | |
| stage = "accepted_model_observation", | |
| cause = "accepted_provider_changed", | |
| "Model preparation failed" | |
| ); | |
| return Err(unavailable()); | |
| } | |
| if row.configuration != self.accepted.provider_configuration { | |
| tracing::warn!( | |
| stage = "accepted_model_observation", | |
| cause = "accepted_configuration_changed", | |
| "Model preparation failed" | |
| ); | |
| return Err(unavailable()); | |
| } | |
| let model = resolve( | |
| &self.definition, | |
| row, | |
| &self.accepted.binding.model, | |
| self.accepted.thinking_level, | |
| ) | |
| .await?; | |
| if ExecutionBasis::new(&model, row) != self.accepted.basis { | |
| tracing::warn!( | |
| stage = "accepted_model_observation", | |
| cause = "accepted_execution_basis_changed", | |
| "Model preparation failed" | |
| ); | |
| return Err(unavailable()); | |
| } | |
| Ok((material, model)) | |
| } | |
| /// The original accepted route, with the least declared reasoning for a short auxiliary task. | |
| pub(crate) async fn capture_auxiliary(&self) -> Result<PreparedModel, RunError> { | |
| async { | |
| let (material, model) = self.observe().await?; | |
| let least = ThinkingLevel::ALL | |
| .into_iter() | |
| .find(|level| model.thinking_levels.contains(level)); | |
| let model = match least { | |
| Some(level) => { | |
| resolve( | |
| &self.definition, | |
| &material.connection, | |
| &self.accepted.binding.model, | |
| Some(level), | |
| ) | |
| .await? | |
| } | |
| None => model, | |
| }; | |
| self.prepare(material, model).await | |
| } | |
| .instrument(self.preparation_span()) | |
| .await | |
| } | |
| } | |
| impl ModelSource for Source { | |
| fn capture(&self) -> BoxFuture<'_, Result<PreparedModel, RunError>> { | |
| Box::pin( | |
| async move { | |
| let (material, model) = self.observe().await?; | |
| self.prepare(material, model).await | |
| } | |
| .instrument(self.preparation_span()), | |
| ) | |
| } | |
| } | |
| /// Resolve tool compatibility without preparing credentials or starting execution. | |
| pub(crate) async fn candidate_editing_tools( | |
| configuration: &Arc<ConfigurationStore>, | |
| catalog: &maka_providers::catalog::Catalog, | |
| selection: ModelSelection, | |
| thinking_level: Option<ThinkingLevel>, | |
| ) -> Result<EditingTools, RunError> { | |
| let public_guard = catalog.snapshot.read().await; | |
| let snapshot = public_guard.as_ref().map_err(|_| unavailable())?.clone(); | |
| let material = configuration | |
| .observe_selected_model(selection.clone()) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::configuration( | |
| "candidate_model_observation", | |
| &error, | |
| Some(&selection.connection_id), | |
| ); | |
| unavailable() | |
| })? | |
| .ok_or_else(unavailable)?; | |
| drop(public_guard); | |
| let row = &material.connection; | |
| let definition = maka_providers::definition(&row.provider, snapshot).map_err(|_| { | |
| tracing::warn!(stage = "provider_definition", cause = "provider_definition_unavailable", connection_id = %row.connection_id, | |
| provider = %row.provider.name, "Model preparation failed"); | |
| unavailable() | |
| })?; | |
| let model = resolve(&definition, row, &selection.model_id, thinking_level).await?; | |
| Ok(editing_tools(row, &selection.model_id, &model.base_url)) | |
| } | |
| fn editing_tools( | |
| row: &maka_agent_facts::configuration::ConnectionCatalogEntry, | |
| model: &str, | |
| base_url: &str, | |
| ) -> EditingTools { | |
| EditingTools::for_model( | |
| model, | |
| base_url, | |
| row.model_overrides | |
| .as_ref() | |
| .and_then(|models| models.get(model)) | |
| .and_then(|overrides| overrides.apply_patch), | |
| ) | |
| } | |
| async fn resolve( | |
| definition: &Definition, | |
| row: &maka_agent_facts::configuration::ConnectionCatalogEntry, | |
| model: &str, | |
| thinking_level: Option<ThinkingLevel>, | |
| ) -> Result<Model, RunError> { | |
| definition | |
| .prepare(Resolve { | |
| connection: connection(row), | |
| model: row | |
| .models | |
| .iter() | |
| .find(|info| info.id == model) | |
| .cloned() | |
| .unwrap_or_else(|| ModelInfo::new(model)), | |
| overrides: row | |
| .model_overrides | |
| .as_ref() | |
| .and_then(|models| models.get(model)) | |
| .cloned(), | |
| thinking_level, | |
| }) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::provider( | |
| "provider_prepare", | |
| &error, | |
| &row.connection_id, | |
| &row.provider.name, | |
| ); | |
| unavailable() | |
| }) | |
| } | |
| fn connection(row: &maka_agent_facts::configuration::ConnectionCatalogEntry) -> Connection { | |
| Connection { | |
| id: row.connection_id.clone(), | |
| revision: row.revision, | |
| configuration: row.configuration.clone(), | |
| } | |
| } | |
| fn unavailable() -> RunError { | |
| RunError::InvalidInput("agent_model_unavailable".into()) | |
| } | |
| /// Capture the actual Root rate decision for every physical provider request. | |
| pub(crate) struct Prices(pub Arc<ConfigurationStore>); | |
| impl maka_agent_engine::pricing::Pricing for Prices { | |
| fn quote<'a>( | |
| &'a self, | |
| provider: &'a str, | |
| model: &'a str, | |
| ) -> BoxFuture<'a, Result<maka_agent_facts::pricing::Quote, RunError>> { | |
| Box::pin( | |
| async move { | |
| self.0 | |
| .quote_model(provider.into(), model.into()) | |
| .await | |
| .map_err(|error| { | |
| diagnostics::configuration("model_pricing", &error, None); | |
| match error { | |
| maka_config::ConfigError::CommitUnknown => { | |
| maka_agent_facts::event::CommitError::OutcomeUnknown( | |
| error.to_string(), | |
| ) | |
| .into() | |
| } | |
| _ => RunError::Internal("model pricing unavailable".into()), | |
| } | |
| }) | |
| } | |
| .instrument(tracing::warn_span!( | |
| "agent_model_pricing", | |
| provider, | |
| model_id = model | |
| )), | |
| ) | |
| } | |
| } |