systemprompt-ai 0.64.0

Provider-agnostic LLM integration for systemprompt.io AI governance — Anthropic, OpenAI, Gemini, and local models unified behind one governed pipeline with cost tracking and audit.
Documentation
//! `Extension` registration for the AI domain: schemas, migrations,
//! dependencies.
//!
//! Copyright (c) systemprompt.io — Business Source License 1.1.
//! See <https://systemprompt.io> for licensing details.

use systemprompt_extension::prelude::*;

#[derive(Debug, Clone, Copy, Default)]
pub struct AiExtension;

impl Extension for AiExtension {
    fn metadata(&self) -> ExtensionMetadata {
        ExtensionMetadata {
            id: "ai",
            name: "AI",
            version: env!("CARGO_PKG_VERSION"),
        }
    }

    fn schemas(&self) -> Vec<SchemaDefinition> {
        let mut schemas = request_schemas();
        schemas.extend(gateway_schemas());
        schemas
    }

    fn migrations(&self) -> Vec<Migration> {
        extension_migrations!()
    }

    fn dependencies(&self) -> Vec<&'static str> {
        vec!["users", "mcp"]
    }
}

register_extension!(AiExtension);

fn request_schemas() -> Vec<SchemaDefinition> {
    vec![
        SchemaDefinition::sql_only(include_str!("../schema/ai_request_message_count.sql")),
        SchemaDefinition::new("ai_requests", include_str!("../schema/ai_requests.sql"))
            .with_required_columns(columns(&[
                "id",
                "provider",
                "model",
                "created_at",
                "client_kind",
                "wire_protocol",
                "client_attestation",
                "finish_reason",
                "served_provider",
                "api_key_id",
            ])),
        SchemaDefinition::new(
            "ai_request_attributions",
            include_str!("../schema/ai_request_attributions.sql"),
        )
        .with_required_columns(columns(&["request_id", "dimension", "value", "source"])),
        SchemaDefinition::new(
            "ai_request_client_evidence",
            include_str!("../schema/ai_request_client_evidence.sql"),
        )
        .with_required_columns(columns(&["ai_request_id", "kind_source"])),
        SchemaDefinition::new(
            "ai_request_messages",
            include_str!("../schema/ai_request_messages.sql"),
        )
        .with_required_columns(columns(&["id", "request_id", "role"])),
        SchemaDefinition::new(
            "ai_request_tool_calls",
            include_str!("../schema/ai_request_tool_calls.sql"),
        )
        .with_required_columns(columns(&["id", "request_id", "tool_name"])),
        SchemaDefinition::new(
            "ai_tool_catalogs",
            include_str!("../schema/ai_tool_catalogs.sql"),
        )
        .with_required_columns(columns(&["sha256", "tools"])),
        SchemaDefinition::new(
            "ai_request_payloads",
            include_str!("../schema/ai_request_payloads.sql"),
        )
        .with_required_columns(columns(&["ai_request_id", "offered_tools_sha256"])),
    ]
}

fn gateway_schemas() -> Vec<SchemaDefinition> {
    vec![
        SchemaDefinition::new(
            "ai_safety_findings",
            include_str!("../schema/ai_safety_findings.sql"),
        )
        .with_required_columns(columns(&["id", "ai_request_id", "severity"])),
        SchemaDefinition::new(
            "ai_quota_buckets",
            include_str!("../schema/ai_quota_buckets.sql"),
        )
        .with_required_columns(columns(&[
            "id",
            "subject_kind",
            "subject_id",
            "window_start",
        ])),
        SchemaDefinition::new(
            "ai_gateway_policies",
            include_str!("../schema/ai_gateway_policies.sql"),
        )
        .with_required_columns(columns(&["id", "name", "spec"])),
        SchemaDefinition::new(
            "ai_gateway_thought_signatures",
            include_str!("../schema/ai_gateway_thought_signatures.sql"),
        )
        .with_required_columns(columns(&[
            "conversation_id",
            "tool_use_id",
            "signature",
            "expires_at",
        ])),
        SchemaDefinition::sql_only(include_str!("../schema/tool_call_ledger.sql")),
    ]
}

fn columns(names: &[&str]) -> Vec<String> {
    names.iter().map(|name| (*name).to_owned()).collect()
}