systemprompt_cli/commands/admin/evals/
promote.rs1use anyhow::{Context, Result};
7use clap::Args;
8use systemprompt_evaluation::{NewCaseParams, SampleFilter};
9use systemprompt_identifiers::AiRequestId;
10
11use super::shared::eval_context;
12use crate::context::CommandContext;
13use crate::shared::CommandOutput;
14
15#[derive(Debug, Args)]
16pub struct PromoteArgs {
17 #[arg(help = "AI request id to promote into the golden case set")]
18 pub ai_request_id: String,
19
20 #[arg(long, help = "Case name (defaults to the request id)")]
21 pub name: Option<String>,
22
23 #[arg(long, help = "Expected behaviour the judge should score against")]
24 pub expectation: Option<String>,
25
26 #[arg(long, value_delimiter = ',', help = "Comma-separated tags")]
27 pub tags: Vec<String>,
28}
29
30pub async fn execute(args: PromoteArgs, ctx: &CommandContext) -> Result<CommandOutput> {
31 let eval = eval_context(ctx).await?;
32
33 let filter = SampleFilter::with_limit(1).ids(vec![args.ai_request_id.clone()]);
34 let sampled = eval
35 .evaluation
36 .sample(&filter)
37 .await?
38 .into_iter()
39 .next()
40 .with_context(|| {
41 format!(
42 "AI request {} not found or has no completed transcript",
43 args.ai_request_id
44 )
45 })?;
46
47 let prompt = sampled.canonical_prompt();
48 let case_id = eval
49 .evaluation
50 .promote_case(&NewCaseParams {
51 name: args.name.unwrap_or_else(|| args.ai_request_id.clone()),
52 prompt,
53 source_ai_request_id: Some(AiRequestId::new(args.ai_request_id)),
54 expectation: args.expectation,
55 tags: args.tags,
56 created_by: eval.admin_id.clone(),
57 prepared_body_sha256: sampled.prepared_body_sha256,
58 })
59 .await?;
60
61 Ok(CommandOutput::text(format!("Promoted case {case_id}")))
62}