Skip to main content

systemprompt_cli/commands/infrastructure/logs/request/
mod.rs

1mod list;
2mod show;
3mod stats;
4
5use anyhow::Result;
6use clap::Subcommand;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use systemprompt_runtime::DatabaseContext;
10
11use super::types::{MessageRow, ToolCallRow};
12use crate::shared::render_result;
13use crate::CliConfig;
14
15#[derive(Debug, Subcommand)]
16pub enum RequestCommands {
17    #[command(
18        about = "List recent AI requests",
19        after_help = "EXAMPLES:\n  systemprompt infra logs request list\n  systemprompt infra \
20                      logs request list --model gpt-4 --since 1h"
21    )]
22    List(list::ListArgs),
23
24    #[command(
25        about = "Show AI request details",
26        after_help = "EXAMPLES:\n  systemprompt infra logs request show abc123\n  systemprompt \
27                      infra logs request show abc123 --messages --tools"
28    )]
29    Show(show::ShowArgs),
30
31    #[command(
32        about = "Show aggregate AI request statistics",
33        after_help = "EXAMPLES:\n  systemprompt infra logs request stats\n  systemprompt infra \
34                      logs request stats --since 24h"
35    )]
36    Stats(stats::StatsArgs),
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40pub struct RequestListRow {
41    pub request_id: String,
42    pub timestamp: String,
43    pub provider: String,
44    pub model: String,
45    pub tokens: String,
46    pub cost: String,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub latency_ms: Option<i64>,
49    pub status: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
53pub struct RequestListOutput {
54    pub requests: Vec<RequestListRow>,
55    pub total: u64,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
59pub struct RequestShowOutput {
60    pub request_id: String,
61    pub provider: String,
62    pub model: String,
63    pub input_tokens: i32,
64    pub output_tokens: i32,
65    pub cost_dollars: f64,
66    pub latency_ms: i64,
67    pub status: String,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub error_message: Option<String>,
70    pub messages: Vec<MessageRow>,
71    pub linked_mcp_calls: Vec<ToolCallRow>,
72}
73
74pub async fn execute(command: RequestCommands, config: &CliConfig) -> Result<()> {
75    match command {
76        RequestCommands::List(args) => {
77            let result = list::execute(args, config).await?;
78            render_result(&result);
79            Ok(())
80        },
81        RequestCommands::Show(args) => {
82            let result = show::execute(args, config).await?;
83            render_result(&result);
84            Ok(())
85        },
86        RequestCommands::Stats(args) => stats::execute(args, config).await,
87    }
88}
89
90pub async fn execute_with_pool(
91    command: RequestCommands,
92    db_ctx: &DatabaseContext,
93    config: &CliConfig,
94) -> Result<()> {
95    match command {
96        RequestCommands::List(args) => {
97            let result = list::execute_with_pool(args, db_ctx, config).await?;
98            render_result(&result);
99            Ok(())
100        },
101        RequestCommands::Show(args) => {
102            let result = show::execute_with_pool(args, db_ctx, config).await?;
103            render_result(&result);
104            Ok(())
105        },
106        RequestCommands::Stats(args) => stats::execute_with_pool(args, db_ctx, config).await,
107    }
108}