2026-05-02 13:23:54 +08:00
|
|
|
use aether_ai_serving::{run_ai_authenticated_decision_input, AiAuthenticatedDecisionInputPort};
|
2026-05-05 11:23:54 +08:00
|
|
|
use aether_scheduler_core::ClientSessionAffinity;
|
2026-05-02 13:23:54 +08:00
|
|
|
use async_trait::async_trait;
|
|
|
|
|
|
|
|
|
|
use crate::ai_serving::{ExecutionRuntimeAuthContext, GatewayAuthApiKeySnapshot, PlannerAppState};
|
|
|
|
|
use crate::clock::current_unix_secs;
|
|
|
|
|
use crate::{AppState, GatewayError};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub(crate) struct ResolvedLocalDecisionAuthInput {
|
|
|
|
|
pub(crate) auth_context: ExecutionRuntimeAuthContext,
|
|
|
|
|
pub(crate) auth_snapshot: GatewayAuthApiKeySnapshot,
|
|
|
|
|
pub(crate) required_capabilities: Option<serde_json::Value>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub(crate) struct LocalRequestedModelDecisionInput {
|
|
|
|
|
pub(crate) auth_context: ExecutionRuntimeAuthContext,
|
|
|
|
|
pub(crate) requested_model: String,
|
|
|
|
|
pub(crate) auth_snapshot: GatewayAuthApiKeySnapshot,
|
|
|
|
|
pub(crate) required_capabilities: Option<serde_json::Value>,
|
2026-05-03 00:49:22 +08:00
|
|
|
pub(crate) request_auth_channel: Option<String>,
|
2026-05-05 11:23:54 +08:00
|
|
|
pub(crate) client_session_affinity: Option<ClientSessionAffinity>,
|
2026-05-02 13:23:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub(crate) struct LocalAuthenticatedDecisionInput {
|
|
|
|
|
pub(crate) auth_context: ExecutionRuntimeAuthContext,
|
|
|
|
|
pub(crate) auth_snapshot: GatewayAuthApiKeySnapshot,
|
|
|
|
|
pub(crate) required_capabilities: Option<serde_json::Value>,
|
2026-05-05 12:25:07 +08:00
|
|
|
pub(crate) client_session_affinity: Option<ClientSessionAffinity>,
|
2026-05-02 13:23:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GatewayAuthenticatedDecisionInputPort<'a> {
|
|
|
|
|
state: PlannerAppState<'a>,
|
|
|
|
|
now_unix_secs: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl AiAuthenticatedDecisionInputPort for GatewayAuthenticatedDecisionInputPort<'_> {
|
|
|
|
|
type AuthContext = ExecutionRuntimeAuthContext;
|
|
|
|
|
type AuthSnapshot = GatewayAuthApiKeySnapshot;
|
|
|
|
|
type RequiredCapabilities = serde_json::Value;
|
|
|
|
|
type ResolvedInput = ResolvedLocalDecisionAuthInput;
|
|
|
|
|
type Error = GatewayError;
|
|
|
|
|
|
|
|
|
|
async fn read_auth_snapshot(
|
|
|
|
|
&self,
|
|
|
|
|
auth_context: &Self::AuthContext,
|
|
|
|
|
) -> Result<Option<Self::AuthSnapshot>, Self::Error> {
|
|
|
|
|
self.state
|
|
|
|
|
.read_auth_api_key_snapshot(
|
|
|
|
|
&auth_context.user_id,
|
|
|
|
|
&auth_context.api_key_id,
|
|
|
|
|
self.now_unix_secs,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn resolve_required_capabilities(
|
|
|
|
|
&self,
|
|
|
|
|
auth_context: &Self::AuthContext,
|
|
|
|
|
requested_model: Option<&str>,
|
|
|
|
|
explicit_required_capabilities: Option<&Self::RequiredCapabilities>,
|
|
|
|
|
) -> Result<Option<Self::RequiredCapabilities>, Self::Error> {
|
|
|
|
|
Ok(self
|
|
|
|
|
.state
|
|
|
|
|
.resolve_request_candidate_required_capabilities(
|
|
|
|
|
&auth_context.user_id,
|
|
|
|
|
&auth_context.api_key_id,
|
|
|
|
|
requested_model,
|
|
|
|
|
explicit_required_capabilities,
|
|
|
|
|
)
|
|
|
|
|
.await)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_resolved_input(
|
|
|
|
|
&self,
|
|
|
|
|
auth_context: Self::AuthContext,
|
|
|
|
|
auth_snapshot: Self::AuthSnapshot,
|
|
|
|
|
required_capabilities: Option<Self::RequiredCapabilities>,
|
|
|
|
|
) -> Self::ResolvedInput {
|
|
|
|
|
ResolvedLocalDecisionAuthInput {
|
|
|
|
|
auth_context,
|
|
|
|
|
auth_snapshot,
|
|
|
|
|
required_capabilities,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn build_local_requested_model_decision_input(
|
|
|
|
|
resolved_input: ResolvedLocalDecisionAuthInput,
|
|
|
|
|
requested_model: String,
|
|
|
|
|
) -> LocalRequestedModelDecisionInput {
|
|
|
|
|
LocalRequestedModelDecisionInput {
|
|
|
|
|
auth_context: resolved_input.auth_context,
|
|
|
|
|
requested_model,
|
|
|
|
|
auth_snapshot: resolved_input.auth_snapshot,
|
|
|
|
|
required_capabilities: resolved_input.required_capabilities,
|
2026-05-03 00:49:22 +08:00
|
|
|
request_auth_channel: None,
|
2026-05-05 11:23:54 +08:00
|
|
|
client_session_affinity: None,
|
2026-05-02 13:23:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn build_local_authenticated_decision_input(
|
|
|
|
|
resolved_input: ResolvedLocalDecisionAuthInput,
|
|
|
|
|
) -> LocalAuthenticatedDecisionInput {
|
|
|
|
|
LocalAuthenticatedDecisionInput {
|
|
|
|
|
auth_context: resolved_input.auth_context,
|
|
|
|
|
auth_snapshot: resolved_input.auth_snapshot,
|
|
|
|
|
required_capabilities: resolved_input.required_capabilities,
|
2026-05-05 12:25:07 +08:00
|
|
|
client_session_affinity: None,
|
2026-05-02 13:23:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn resolve_local_authenticated_decision_input(
|
|
|
|
|
state: &AppState,
|
|
|
|
|
auth_context: ExecutionRuntimeAuthContext,
|
|
|
|
|
requested_model: Option<&str>,
|
|
|
|
|
explicit_required_capabilities: Option<&serde_json::Value>,
|
|
|
|
|
) -> Result<Option<ResolvedLocalDecisionAuthInput>, GatewayError> {
|
|
|
|
|
let port = GatewayAuthenticatedDecisionInputPort {
|
|
|
|
|
state: PlannerAppState::new(state),
|
|
|
|
|
now_unix_secs: current_unix_secs(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
run_ai_authenticated_decision_input(
|
|
|
|
|
&port,
|
|
|
|
|
auth_context,
|
|
|
|
|
requested_model,
|
|
|
|
|
explicit_required_capabilities,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
}
|