Skip to main content

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

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