Skip to main content

systemprompt_evaluation/repository/
cases.rs

1//! Repository for evaluation cases.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use sqlx::PgPool;
7use std::sync::Arc;
8use systemprompt_database::DbPool;
9use systemprompt_identifiers::{AiRequestId, EvalCaseId, UserId};
10
11use crate::error::Result;
12use crate::models::{EvalCase, NewCaseParams};
13
14#[derive(Debug, Clone)]
15pub struct EvalCaseRepository {
16    pool: Arc<PgPool>,
17}
18
19impl EvalCaseRepository {
20    pub fn new(db: &DbPool) -> Result<Self> {
21        Ok(Self {
22            pool: db.write_pool_arc()?,
23        })
24    }
25
26    pub async fn create(&self, params: &NewCaseParams) -> Result<EvalCaseId> {
27        let id = EvalCaseId::generate();
28        let prompt_body = serde_json::to_value(&params.prompt)?;
29        let canonical_messages = serde_json::to_value(&params.prompt.messages)?;
30        sqlx::query!(
31            r#"
32            INSERT INTO eval_cases (
33                id, name, prompt_body, source_ai_request_id, expectation,
34                tags, created_by, canonical_messages, system_prompt,
35                offered_tools, provider, model, prepared_body_sha256
36            )
37            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
38            "#,
39            id.as_str(),
40            params.name,
41            prompt_body,
42            params
43                .source_ai_request_id
44                .as_ref()
45                .map(AiRequestId::as_str),
46            params.expectation.as_deref(),
47            &params.tags,
48            params.created_by.as_str(),
49            canonical_messages,
50            params.prompt.system_prompt.as_deref(),
51            params.prompt.offered_tools.as_ref(),
52            params.prompt.provider,
53            params.prompt.model,
54            params.prepared_body_sha256.as_deref()
55        )
56        .execute(self.pool.as_ref())
57        .await?;
58        Ok(id)
59    }
60
61    pub async fn list_enabled(&self) -> Result<Vec<EvalCase>> {
62        let rows = sqlx::query!(
63            r#"
64            SELECT id, name, prompt_body, source_ai_request_id, expectation,
65                   tags, enabled, created_by, created_at, repair_hint,
66                   canonical_messages, system_prompt, offered_tools,
67                   provider, model, prepared_body_sha256
68            FROM eval_cases
69            WHERE enabled = TRUE
70            ORDER BY created_at DESC
71            "#
72        )
73        .fetch_all(self.pool.as_ref())
74        .await?;
75
76        Ok(rows
77            .into_iter()
78            .map(|row| EvalCase {
79                id: EvalCaseId::new(row.id),
80                name: row.name,
81                prompt_body: row.prompt_body,
82                source_ai_request_id: row.source_ai_request_id.map(AiRequestId::new),
83                expectation: row.expectation,
84                tags: row.tags,
85                enabled: row.enabled,
86                created_by: UserId::new(row.created_by),
87                created_at: row.created_at,
88                repair_hint: row.repair_hint,
89                canonical_messages: row.canonical_messages,
90                system_prompt: row.system_prompt,
91                offered_tools: row.offered_tools,
92                provider: row.provider,
93                model: row.model,
94                prepared_body_sha256: row.prepared_body_sha256,
95            })
96            .collect())
97    }
98
99    pub async fn set_enabled(&self, id: &EvalCaseId, enabled: bool) -> Result<()> {
100        sqlx::query!(
101            "UPDATE eval_cases SET enabled = $2 WHERE id = $1",
102            id.as_str(),
103            enabled
104        )
105        .execute(self.pool.as_ref())
106        .await?;
107        Ok(())
108    }
109
110    pub async fn set_repair_hint(&self, id: &EvalCaseId, repair_hint: &str) -> Result<()> {
111        sqlx::query!(
112            "UPDATE eval_cases SET repair_hint = $2 WHERE id = $1",
113            id.as_str(),
114            repair_hint
115        )
116        .execute(self.pool.as_ref())
117        .await?;
118        Ok(())
119    }
120}