Skip to main content

systemprompt_ai/services/gateway/
spec.rs

1//! Declarative gateway-policy specification.
2//!
3//! A [`GatewayPolicySpec`] is the `spec` payload of a row in
4//! `ai_gateway_policies`. It is the inference-model allow-list (a security
5//! control) plus per-call token ceilings, quota windows, and safety-scanner
6//! configuration. The same type is parsed from the version-controlled
7//! `services/ai/gateway-policies.yaml` and from the JSONB `spec` column, so
8//! the on-disk config and the persisted row share one schema.
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
13#[serde(deny_unknown_fields)]
14pub struct QuotaWindow {
15    pub window_seconds: i32,
16    pub max_requests: Option<i64>,
17    pub max_input_tokens: Option<i64>,
18    pub max_output_tokens: Option<i64>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22#[serde(deny_unknown_fields)]
23pub struct SafetyConfig {
24    #[serde(default)]
25    pub scanners: Vec<String>,
26    #[serde(default)]
27    pub block_categories: Vec<String>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, Default)]
31#[serde(deny_unknown_fields)]
32pub struct GatewayPolicySpec {
33    #[serde(default)]
34    pub allowed_models: Option<Vec<String>>,
35    #[serde(default)]
36    pub max_input_tokens_per_call: Option<u32>,
37    #[serde(default)]
38    pub max_tool_depth: Option<u32>,
39    #[serde(default)]
40    pub quota_windows: Vec<QuotaWindow>,
41    #[serde(default)]
42    pub safety: SafetyConfig,
43}
44
45impl GatewayPolicySpec {
46    #[must_use]
47    pub fn permissive() -> Self {
48        Self::default()
49    }
50
51    #[must_use]
52    pub fn model_allowed(&self, model: &str) -> bool {
53        self.allowed_models
54            .as_deref()
55            .is_none_or(|list| list.iter().any(|m| m == model))
56    }
57}