Skip to main content

cognee_database/entities/
session_record.rs

1//! SeaORM entity for the `session_records` table (LIB-03).
2//!
3//! Mirrors Python's `SessionRecord` SQLAlchemy model at
4//! `cognee/modules/session_lifecycle/models.py:10-86` byte-for-byte:
5//!
6//! - Composite primary key on `(session_id, user_id)`.
7//! - `status` defaults to `"running"`. The `"abandoned"` value is **never**
8//!   written to the row — it is inferred at read time by LIB-05's
9//!   `effective_status` SQL expression based on `last_activity_at`.
10//! - Aggregate counters (`tokens_in`, `tokens_out`, `cost_usd`,
11//!   `error_count`) accumulate via `LLMGateway` / `SessionManager` hooks.
12//!
13//! UUIDs (`user_id`, `dataset_id`) are persisted as 32-char hex strings to
14//! match the rest of the schema (see `uuid_hex.rs`); LIB-05's trait
15//! converts `uuid::Uuid` ↔ `String` at the repository boundary.
16
17use sea_orm::entity::prelude::*;
18use serde::{Deserialize, Serialize};
19
20#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
21#[sea_orm(table_name = "session_records")]
22pub struct Model {
23    /// Caller-provided session id (e.g. `"cc_myproj_ab12cd34ef56"`).
24    /// Scoped per user — same string from two different users is two
25    /// different sessions.
26    #[sea_orm(primary_key, auto_increment = false)]
27    pub session_id: String,
28    /// Owning user id, hex-encoded UUID.
29    #[sea_orm(primary_key, auto_increment = false)]
30    pub user_id: String,
31
32    /// Optional dataset scope (hex UUID); used by ACL filtering when
33    /// listing/aggregating.
34    pub dataset_id: Option<String>,
35
36    /// Stored status. `"abandoned"` is inferred at read time, never
37    /// written (see LIB-05).
38    pub status: String,
39
40    pub started_at: DateTimeUtc,
41    pub last_activity_at: DateTimeUtc,
42    pub ended_at: Option<DateTimeUtc>,
43
44    pub tokens_in: i32,
45    pub tokens_out: i32,
46    pub cost_usd: f64,
47
48    pub error_count: i32,
49
50    pub last_model: Option<String>,
51}
52
53#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
54pub enum Relation {}
55
56impl ActiveModelBehavior for ActiveModel {}
57
58impl Model {
59    /// Serialize to a JSON object whose key ordering matches Python's
60    /// [`SessionRecord.to_dict()`](https://github.com/topoteretes/cognee/blob/main/cognee/modules/session_lifecycle/models.py#L68-L86).
61    ///
62    /// Used by LIB-05's repository / E-09's HTTP DTO. Field order is
63    /// load-bearing: clients (e.g. the dashboard) snapshot the response
64    /// shape in test fixtures.
65    pub fn to_dict(&self) -> serde_json::Value {
66        // Insertion order matters for Python parity: callers (E-09's HTTP
67        // DTO + dashboard test snapshots) compare the rendered JSON
68        // shape literally. The `cognee-database` crate enables
69        // `serde_json/preserve_order` so `json!` emits keys in the
70        // order they appear here.
71        serde_json::json!({
72            "session_id": self.session_id,
73            "user_id": self.user_id,
74            "dataset_id": self.dataset_id,
75            "status": self.status,
76            "started_at": self.started_at.to_rfc3339(),
77            "last_activity_at": self.last_activity_at.to_rfc3339(),
78            "ended_at": self.ended_at.map(|t| t.to_rfc3339()),
79            "tokens_in": self.tokens_in,
80            "tokens_out": self.tokens_out,
81            "cost_usd": self.cost_usd,
82            "error_count": self.error_count,
83            "last_model": self.last_model,
84        })
85    }
86}