Skip to main content

systemprompt_agent/models/
context.rs

1//! Conversational context models: contexts, their messages, per-user views
2//! with aggregate statistics, and create/update request shapes.
3
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use systemprompt_identifiers::{ContextId, McpExecutionId, MessageId, SkillId, TaskId};
7
8pub use systemprompt_models::{
9    CreateContextRequest, UpdateContextRequest, UserContext, UserContextWithStats,
10};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ContextMessage {
14    pub message_id: MessageId,
15    pub role: String,
16    pub created_at: DateTime<Utc>,
17    pub sequence_number: i32,
18    pub parts: Vec<super::a2a::Part>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ContextDetail {
23    pub context: UserContext,
24    pub messages: Vec<ContextMessage>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(tag = "type", rename_all = "snake_case")]
29pub enum ContextStateEvent {
30    ToolExecutionCompleted {
31        context_id: ContextId,
32        execution_id: McpExecutionId,
33        tool_name: String,
34        server_name: String,
35        output: Option<String>,
36        #[serde(skip_serializing_if = "Option::is_none")]
37        artifact: Option<super::a2a::Artifact>,
38        status: String,
39        timestamp: DateTime<Utc>,
40    },
41    TaskStatusChanged {
42        task: super::a2a::Task,
43        context_id: ContextId,
44        timestamp: DateTime<Utc>,
45    },
46    ArtifactCreated {
47        artifact: super::a2a::Artifact,
48        task_id: TaskId,
49        context_id: ContextId,
50        timestamp: DateTime<Utc>,
51    },
52    SkillLoaded {
53        skill_id: SkillId,
54        skill_name: String,
55        description: String,
56        request_context: systemprompt_models::execution::context::RequestContext,
57        tool_name: Option<String>,
58        timestamp: DateTime<Utc>,
59    },
60    ContextCreated {
61        context_id: ContextId,
62        context: UserContext,
63        timestamp: DateTime<Utc>,
64    },
65    ContextUpdated {
66        context_id: ContextId,
67        name: String,
68        timestamp: DateTime<Utc>,
69    },
70    ContextDeleted {
71        context_id: ContextId,
72        timestamp: DateTime<Utc>,
73    },
74    Heartbeat {
75        timestamp: DateTime<Utc>,
76    },
77    CurrentAgent {
78        context_id: ContextId,
79        agent_name: Option<String>,
80        timestamp: DateTime<Utc>,
81    },
82}
83
84impl ContextStateEvent {
85    pub fn context_id(&self) -> Option<&str> {
86        match self {
87            Self::SkillLoaded {
88                request_context, ..
89            } => Some(request_context.context_id().as_str()),
90            Self::Heartbeat { .. } => None,
91            Self::ToolExecutionCompleted { context_id, .. }
92            | Self::TaskStatusChanged { context_id, .. }
93            | Self::ArtifactCreated { context_id, .. }
94            | Self::ContextCreated { context_id, .. }
95            | Self::ContextUpdated { context_id, .. }
96            | Self::ContextDeleted { context_id, .. }
97            | Self::CurrentAgent { context_id, .. } => Some(context_id.as_str()),
98        }
99    }
100
101    pub const fn timestamp(&self) -> DateTime<Utc> {
102        match self {
103            Self::ToolExecutionCompleted { timestamp, .. }
104            | Self::TaskStatusChanged { timestamp, .. }
105            | Self::ArtifactCreated { timestamp, .. }
106            | Self::SkillLoaded { timestamp, .. }
107            | Self::ContextCreated { timestamp, .. }
108            | Self::ContextUpdated { timestamp, .. }
109            | Self::ContextDeleted { timestamp, .. }
110            | Self::Heartbeat { timestamp }
111            | Self::CurrentAgent { timestamp, .. } => *timestamp,
112        }
113    }
114}