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