Skip to main content

tokio_prompt_orchestrator/session/
mod.rs

1//! # Session Context Manager
2//!
3//! Tracks multi-turn conversation history per `SessionId` and enriches
4//! incoming `PromptRequest`s with relevant prior context before they enter
5//! the pipeline.
6//!
7//! ## Overview
8//!
9//! Many LLM applications need conversational memory: the model must "know"
10//! what was said in earlier turns of the same chat session.  Without
11//! session tracking every request arrives context-free and the user has to
12//! repeat themselves.
13//!
14//! The [`SessionContext`] type provides:
15//!
16//! - **Per-session history** stored in a `DashMap` (lock-free concurrent
17//!   hash map).  Each entry is a bounded ring buffer of
18//!   [`ConversationTurn`]s.
19//! - **Auto-injection**: [`SessionContext::enrich`] prepends the last
20//!   `context_window` turns to the prompt input, formatted as a human-readable
21//!   dialogue transcript.
22//! - **Response recording**: [`SessionContext::record_response`] appends the
23//!   model's answer after a successful inference call.
24//! - **Sliding window**: only the most recent `max_turns` turns are kept;
25//!   older entries are evicted automatically to bound memory use.
26//! - **Session expiry**: inactive sessions are evicted after a configurable
27//!   TTL, preventing unbounded map growth.
28//! - **Optional summarisation stub**: when history grows beyond
29//!   `summarise_after_turns`, callers receive a
30//!   [`SessionAction::RequestSummary`] signal.  The caller is responsible
31//!   for sending a summarisation request through the pipeline and replacing
32//!   history with the returned summary via [`SessionContext::summarise`].
33//!
34//! ## Example
35//!
36//! ```no_run
37//! use std::collections::HashMap;
38//! use std::time::Duration;
39//! use tokio_prompt_orchestrator::{SessionId, PromptRequest};
40//! use tokio_prompt_orchestrator::session::{SessionContext, SessionConfig};
41//!
42//! #[tokio::main]
43//! async fn main() {
44//!     let ctx = SessionContext::new(SessionConfig::default());
45//!
46//!     let session = SessionId::new("alice");
47//!
48//!     // First turn — no prior history, prompt is unchanged.
49//!     let req = PromptRequest {
50//!         session: session.clone(),
51//!         request_id: "r1".into(),
52//!         input: "What is the capital of France?".into(),
53//!         meta: HashMap::new(),
54//!         deadline: None,
55//!     };
56//!     let (enriched, _action) = ctx.enrich(req).await;
57//!     assert_eq!(enriched.input, "What is the capital of France?");
58//!
59//!     // Record the model's response.
60//!     ctx.record_response(&session, "The capital of France is Paris.").await;
61//!
62//!     // Second turn — prior history is prepended automatically.
63//!     let req2 = PromptRequest {
64//!         session: session.clone(),
65//!         request_id: "r2".into(),
66//!         input: "What is its population?".into(),
67//!         meta: HashMap::new(),
68//!         deadline: None,
69//!     };
70//!     let (enriched2, _action) = ctx.enrich(req2).await;
71//!     assert!(enriched2.input.contains("Paris"));
72//! }
73//! ```
74
75pub mod budget;
76pub mod context;
77
78pub use budget::{
79    BudgetError, BudgetOutcome, SessionBudget, SessionBudgetSnapshot, SessionLimits,
80};
81pub use context::{
82    ConversationTurn, SessionAction, SessionConfig, SessionContext, SessionStats, TurnRole,
83};