Skip to main content

systemprompt_models/profile/gateway/
override_rule.rs

1//! Declarative system-prompt override rules.
2//!
3//! A [`SystemPromptRule`] optionally matches on the resolved upstream
4//! `provider` and the requested `model_pattern` (the same exact / prefix `foo*`
5//! / suffix `*foo` / catch-all `*` grammar as [`super::route::GatewayRoute`]),
6//! and applies a [`OverrideRuleAction`] to the inbound request's system prompt
7//! before it is forwarded upstream: `replace` substitutes a fixed `prompt`,
8//! `strip` removes the prompt entirely. Rules are pure data; resolution and
9//! application live in the gateway dispatch layer.
10//!
11//! Copyright (c) systemprompt.io — Business Source License 1.1.
12//! See <https://systemprompt.io> for licensing details.
13
14use serde::{Deserialize, Serialize};
15use systemprompt_identifiers::ProviderId;
16
17use super::error::{GatewayProfileError, GatewayResult};
18use super::route::match_pattern;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
21#[serde(rename_all = "snake_case")]
22pub enum OverrideRuleAction {
23    Replace,
24    Strip,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
28#[serde(deny_unknown_fields)]
29pub struct SystemPromptRule {
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub provider: Option<ProviderId>,
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub model_pattern: Option<String>,
34    pub action: OverrideRuleAction,
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    pub prompt: Option<String>,
37}
38
39impl SystemPromptRule {
40    #[must_use]
41    pub fn matches(&self, provider: &ProviderId, model: &str) -> bool {
42        let provider_ok = self
43            .provider
44            .as_ref()
45            .is_none_or(|p| p.as_str() == provider.as_str());
46        let model_ok = self
47            .model_pattern
48            .as_deref()
49            .is_none_or(|pat| match_pattern(pat, model));
50        provider_ok && model_ok
51    }
52
53    pub const fn validate(&self) -> GatewayResult<()> {
54        match self.action {
55            OverrideRuleAction::Replace if self.prompt.is_none() => {
56                Err(GatewayProfileError::OverrideReplaceMissingPrompt)
57            },
58            OverrideRuleAction::Strip if self.prompt.is_some() => {
59                Err(GatewayProfileError::OverrideStripWithPrompt)
60            },
61            _ => Ok(()),
62        }
63    }
64}