Skip to main content

mockforge_foundation/intelligent_behavior/
mod.rs

1//! Foundational types for intelligent behavior
2//!
3//! These types are shared between `mockforge-core` (which defines the richer
4//! behavior rules, state machines, and MockAI implementation) and consumers
5//! that only need the base request/response types and personas.
6//!
7//! Kept minimal: only pure data with no cross-crate dependencies.
8
9pub mod config;
10pub mod mockai;
11pub mod rule_types;
12pub mod session;
13pub mod session_state;
14pub mod types;
15
16pub use config::{
17    BehaviorModelConfig, IntelligentBehaviorConfig, PerformanceConfig, PersonasConfig,
18    VectorStoreConfig,
19};
20pub use mockai::MockAiBehavior;
21pub use session::{SessionManager, SessionTracking, SessionTrackingMethod};
22pub use session_state::{InteractionRecord, SessionState};
23pub use types::BehaviorRules;
24
25use chrono::{DateTime, Utc};
26use serde::{Deserialize, Serialize};
27use serde_json::Value;
28use std::collections::HashMap;
29
30/// A persona defines consistent data patterns across endpoints
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
33pub struct Persona {
34    /// Persona name (e.g., "commercial_midwest", "hobbyist_urban")
35    pub name: String,
36
37    /// Persona traits (key-value pairs, e.g., "apiary_count": "20-40", "hive_count": "800-1500")
38    #[serde(default)]
39    pub traits: HashMap<String, String>,
40}
41
42impl Persona {
43    /// Get a numeric trait value, parsing ranges like "20-40" or single values.
44    /// Returns the midpoint for ranges, or the value for single numbers.
45    pub fn get_numeric_trait(&self, key: &str) -> Option<u64> {
46        self.traits.get(key).and_then(|value| {
47            if let Some((min_str, max_str)) = value.split_once('-') {
48                if let (Ok(min), Ok(max)) =
49                    (min_str.trim().parse::<u64>(), max_str.trim().parse::<u64>())
50                {
51                    return Some((min + max) / 2);
52                }
53            }
54            value.parse::<u64>().ok()
55        })
56    }
57
58    /// Get a trait value as string.
59    pub fn get_trait(&self, key: &str) -> Option<&String> {
60        self.traits.get(key)
61    }
62}
63
64/// LLM generation request — passed to `LlmClient::generate`.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct LlmGenerationRequest {
67    /// System prompt (instructions to the model).
68    pub system_prompt: String,
69    /// User prompt (constructed from request context).
70    pub user_prompt: String,
71    /// Sampling temperature (0.0–2.0).
72    #[serde(default = "default_temperature")]
73    pub temperature: f64,
74    /// Maximum tokens to generate.
75    #[serde(default = "default_max_tokens")]
76    pub max_tokens: usize,
77    /// Expected response schema (JSON Schema).
78    pub schema: Option<Value>,
79    /// Sampling seed for deterministic generation (#852). Forwarded to
80    /// providers that support one; pair with a fixed temperature for
81    /// reproducible output in CI.
82    #[serde(default)]
83    pub seed: Option<i64>,
84}
85
86impl LlmGenerationRequest {
87    /// Create a new LLM generation request.
88    pub fn new(system_prompt: impl Into<String>, user_prompt: impl Into<String>) -> Self {
89        Self {
90            system_prompt: system_prompt.into(),
91            user_prompt: user_prompt.into(),
92            temperature: default_temperature(),
93            max_tokens: default_max_tokens(),
94            schema: None,
95            seed: None,
96        }
97    }
98
99    /// Set temperature.
100    #[must_use]
101    pub fn with_temperature(mut self, temperature: f64) -> Self {
102        self.temperature = temperature;
103        self
104    }
105
106    /// Set max tokens.
107    #[must_use]
108    pub fn with_max_tokens(mut self, max_tokens: usize) -> Self {
109        self.max_tokens = max_tokens;
110        self
111    }
112
113    /// Set expected schema.
114    #[must_use]
115    pub fn with_schema(mut self, schema: Value) -> Self {
116        self.schema = Some(schema);
117        self
118    }
119}
120
121fn default_temperature() -> f64 {
122    0.7
123}
124
125fn default_max_tokens() -> usize {
126    1024
127}
128
129/// HTTP request for MockAI processing.
130#[derive(Debug, Clone)]
131pub struct Request {
132    /// HTTP method.
133    pub method: String,
134    /// Request path.
135    pub path: String,
136    /// Request body.
137    pub body: Option<Value>,
138    /// Query parameters.
139    pub query_params: HashMap<String, String>,
140    /// Headers.
141    pub headers: HashMap<String, String>,
142}
143
144/// HTTP response from MockAI.
145#[derive(Debug, Clone)]
146pub struct Response {
147    /// HTTP status code.
148    pub status_code: u16,
149    /// Response body.
150    pub body: Value,
151    /// Response headers.
152    pub headers: HashMap<String, String>,
153}
154
155/// Captured HTTP request/response exchange used for behavioral analysis.
156#[derive(Debug, Clone)]
157pub struct HttpExchange {
158    /// HTTP method.
159    pub method: String,
160    /// Request path.
161    pub path: String,
162    /// Query parameters (raw query string).
163    pub query_params: Option<String>,
164    /// Request headers (JSON string).
165    pub headers: String,
166    /// Request body (optional).
167    pub body: Option<String>,
168    /// Request body encoding.
169    pub body_encoding: String,
170    /// Response status code.
171    pub status_code: Option<i32>,
172    /// Response headers (JSON string).
173    pub response_headers: Option<String>,
174    /// Response body (optional).
175    pub response_body: Option<String>,
176    /// Response body encoding.
177    pub response_body_encoding: Option<String>,
178    /// Timestamp.
179    pub timestamp: DateTime<Utc>,
180}