Skip to main content

lmm_agent/
types.rs

1// Copyright 2026 Mahmoud Harmouch.
2//
3// Licensed under the MIT license
4// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
5// option. This file may not be copied, modified, or distributed
6// except according to those terms.
7
8//! # Agent domain types.
9//!
10//! Foundational value types shared across all agent implementations.
11//!
12//! All types here are:
13//! - `Clone` + `Debug` + `PartialEq`
14//! - `serde::{Serialize, Deserialize}` where serialisation makes sense
15//! - `Send + Sync` - safe to move across async task boundaries
16//!
17//! ## Attribution
18//!
19//! Adapted from the `autogpt` crate's `common/utils.rs`:
20//! <https://github.com/wiseaidotdev/autogpt/blob/main/autogpt/src/common/utils.rs>
21
22use chrono::prelude::*;
23use serde::{Deserialize, Serialize};
24use serde_json::Value;
25use std::borrow::Cow;
26use std::collections::{HashMap, HashSet};
27use std::fmt;
28
29/// The current operational status of an agent through its lifecycle.
30///
31/// # Examples
32///
33/// ```
34/// use lmm_agent::types::Status;
35///
36/// let s = Status::default();
37/// assert_eq!(s, Status::Idle);
38/// ```
39#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
40pub enum Status {
41    /// Agent is waiting for a task to be assigned.
42    #[default]
43    Idle,
44    /// Agent is actively processing a task.
45    Active,
46    /// Agent is validating its own outputs.
47    InUnitTesting,
48    /// Agent has finished all assigned tasks.
49    Completed,
50    /// Agent is running the closed-loop [`ThinkLoop`] reasoning cycle.
51    Thinking,
52}
53
54impl fmt::Display for Status {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Self::Idle => write!(f, "Idle"),
58            Self::Active => write!(f, "Active"),
59            Self::InUnitTesting => write!(f, "InUnitTesting"),
60            Self::Completed => write!(f, "Completed"),
61            Self::Thinking => write!(f, "Thinking"),
62        }
63    }
64}
65
66/// A single message exchanged between an agent and a user/system/tool.
67///
68/// # Examples
69///
70/// ```
71/// use lmm_agent::types::Message;
72///
73/// let msg = Message {
74///     role: "user".into(),
75///     content: "Hello, agent!".into(),
76/// };
77/// assert_eq!(msg.role.as_ref(), "user");
78/// ```
79#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
80pub struct Message {
81    /// Who produced this message (e.g. `"user"`, `"assistant"`, `"system"`).
82    pub role: Cow<'static, str>,
83    /// The message text.
84    pub content: Cow<'static, str>,
85}
86
87impl Message {
88    /// Constructs a new [`Message`] with the given role and content.
89    pub fn new(role: impl Into<Cow<'static, str>>, content: impl Into<Cow<'static, str>>) -> Self {
90        Self {
91            role: role.into(),
92            content: content.into(),
93        }
94    }
95}
96
97/// A structured knowledge base mapping fact identifiers to their explanations.
98///
99/// # Examples
100///
101/// ```
102/// use lmm_agent::types::Knowledge;
103/// use std::borrow::Cow;
104///
105/// let mut kb = Knowledge::default();
106/// kb.facts.insert(Cow::Borrowed("Rust"), Cow::Borrowed("A systems language."));
107/// assert_eq!(kb.facts.len(), 1);
108/// ```
109#[derive(Debug, PartialEq, Eq, Default, Clone, Serialize, Deserialize)]
110pub struct Knowledge {
111    /// Map from fact key to natural-language description.
112    pub facts: HashMap<Cow<'static, str>, Cow<'static, str>>,
113}
114
115impl Knowledge {
116    /// Inserts or overwrites a fact.
117    pub fn insert(
118        &mut self,
119        key: impl Into<Cow<'static, str>>,
120        value: impl Into<Cow<'static, str>>,
121    ) {
122        self.facts.insert(key.into(), value.into());
123    }
124
125    /// Looks up a fact by key.
126    pub fn get(&self, key: &str) -> Option<&Cow<'static, str>> {
127        self.facts.get(key as &str)
128    }
129}
130
131/// The name of a built-in or custom tool the agent can invoke.
132#[derive(Debug, PartialEq, Eq, Default, Clone, Hash)]
133pub enum ToolName {
134    /// Full-text web search (default).
135    #[default]
136    Search,
137    Browser,
138    News,
139    Wiki,
140    Calc,
141    Math,
142    Format,
143    Exec,
144    Code,
145    Regex,
146    Read,
147    Write,
148    Pdf,
149    Summarize,
150    Email,
151    Calendar,
152    Translate,
153    Sentiment,
154    Classify,
155    Memory,
156    Plan,
157    Spawn,
158    Judge,
159    Plugin(String),
160}
161
162/// A callable tool the agent can invoke at runtime.
163///
164/// The invocation is synchronous and deterministic - side-effectful tools
165/// (e.g. shell commands) should be wrapped in a `Plugin` variant tool with
166/// appropriate error handling inside `invoke`.
167///
168/// # Examples
169///
170/// ```
171/// use lmm_agent::types::{Tool, ToolName};
172///
173/// let echo = Tool {
174///     name: ToolName::Plugin("echo".to_string()),
175///     description: "Echoes input back.".into(),
176///     invoke: |input| input.to_string(),
177/// };
178/// assert_eq!((echo.invoke)("hello"), "hello");
179/// ```
180#[derive(Clone)]
181pub struct Tool {
182    /// The name/kind of this tool.
183    pub name: ToolName,
184    /// Human-readable description shown in planning context.
185    pub description: Cow<'static, str>,
186    /// Synchronous invocation function: `input → output`.
187    pub invoke: fn(&str) -> String,
188}
189
190impl fmt::Debug for Tool {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        f.debug_struct("Tool")
193            .field("name", &self.name)
194            .field("description", &self.description)
195            .finish_non_exhaustive()
196    }
197}
198
199impl PartialEq for Tool {
200    fn eq(&self, other: &Self) -> bool {
201        self.name == other.name && self.description == other.description
202    }
203}
204
205impl Default for Tool {
206    fn default() -> Self {
207        Self {
208            name: ToolName::default(),
209            description: Cow::Borrowed(""),
210            invoke: |_| String::new(),
211        }
212    }
213}
214
215/// A single goal within the agent's current plan.
216///
217/// # Examples
218///
219/// ```
220/// use lmm_agent::types::Goal;
221///
222/// let g = Goal { description: "Research Rust".into(), priority: 1, completed: false };
223/// assert!(!g.completed);
224/// ```
225#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
226pub struct Goal {
227    /// Short text describing what must be accomplished.
228    pub description: String,
229    /// Urgency level: lower values = higher urgency.
230    pub priority: u8,
231    /// Whether this goal has been achieved.
232    pub completed: bool,
233}
234
235/// An ordered sequence of [`Goal`]s the agent is working through.
236///
237/// # Examples
238///
239/// ```
240/// use lmm_agent::types::{Planner, Goal};
241///
242/// let mut p = Planner::default();
243/// p.current_plan.push(Goal { description: "Init".into(), priority: 0, completed: false });
244/// assert_eq!(p.current_plan.len(), 1);
245/// ```
246#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
247pub struct Planner {
248    /// Goals in execution order.
249    pub current_plan: Vec<Goal>,
250}
251
252impl Planner {
253    /// Returns the number of completed goals.
254    pub fn completed_count(&self) -> usize {
255        self.current_plan.iter().filter(|g| g.completed).count()
256    }
257
258    /// Returns `true` when every goal is complete.
259    pub fn is_done(&self) -> bool {
260        self.current_plan.iter().all(|g| g.completed)
261    }
262}
263
264/// The personality profile that shapes how an agent behaves and responds.
265///
266/// # Examples
267///
268/// ```
269/// use lmm_agent::types::Profile;
270///
271/// let p = Profile {
272///     name: "ResearchBot".into(),
273///     traits: vec!["curious".into(), "precise".into()],
274///     behavior_script: None,
275/// };
276/// assert_eq!(p.name.as_ref(), "ResearchBot");
277/// ```
278#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
279pub struct Profile {
280    /// Display name for this persona.
281    pub name: Cow<'static, str>,
282    /// Adjectives describing the agent's style.
283    pub traits: Vec<Cow<'static, str>>,
284    /// Optional DSL / prompt script controlling fine-grained behaviour.
285    pub behavior_script: Option<Cow<'static, str>>,
286}
287
288/// Introspection data that allows an agent to evaluate its own performance.
289pub struct Reflection {
290    /// Rolling log of recent activities or observations.
291    pub recent_logs: Vec<Cow<'static, str>>,
292    /// A function that returns a natural-language assessment of the agent.
293    pub evaluation_fn: fn(&dyn crate::traits::agent::Agent) -> Cow<'static, str>,
294}
295
296impl fmt::Debug for Reflection {
297    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298        f.debug_struct("Reflection")
299            .field("recent_logs", &self.recent_logs)
300            .finish_non_exhaustive()
301    }
302}
303
304impl PartialEq for Reflection {
305    fn eq(&self, other: &Self) -> bool {
306        self.recent_logs == other.recent_logs
307    }
308}
309
310impl Clone for Reflection {
311    fn clone(&self) -> Self {
312        Self {
313            recent_logs: self.recent_logs.clone(),
314            evaluation_fn: self.evaluation_fn,
315        }
316    }
317}
318
319impl Default for Reflection {
320    fn default() -> Self {
321        Self {
322            recent_logs: vec![],
323            evaluation_fn: default_eval_fn,
324        }
325    }
326}
327
328/// A task pinned to a specific wall-clock time.
329#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
330pub struct ScheduledTask {
331    /// UTC timestamp at which the task should run.
332    pub time: DateTime<Utc>,
333    /// The task payload.
334    pub task: Task,
335}
336
337/// Manages a queue of time-triggered tasks.
338///
339/// # Examples
340///
341/// ```
342/// use lmm_agent::types::TaskScheduler;
343///
344/// let sched = TaskScheduler::default();
345/// assert!(sched.scheduled_tasks.is_empty());
346/// ```
347#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
348pub struct TaskScheduler {
349    /// Queue of pending scheduled tasks.
350    pub scheduled_tasks: Vec<ScheduledTask>,
351}
352
353/// An atomic capability the agent possesses.
354#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
355pub enum Capability {
356    /// Can generate code from a natural-language description.
357    #[default]
358    CodeGen,
359    /// Can design user-interface layouts.
360    UIDesign,
361    /// Can execute live web searches.
362    WebSearch,
363    /// Can query SQL databases.
364    SQLAccess,
365    /// Can control robotic actuators.
366    RobotControl,
367    /// Can integrate with external REST / gRPC APIs.
368    ApiIntegration,
369    /// Can convert text to speech audio.
370    TextToSpeech,
371    /// Custom capability identified by a string label.
372    Custom(String),
373}
374
375/// Tracks recent exchanges and focuses the agent on relevant topics.
376///
377/// # Examples
378///
379/// ```
380/// use lmm_agent::types::ContextManager;
381///
382/// let ctx = ContextManager::default();
383/// assert!(ctx.recent_messages.is_empty());
384/// assert!(ctx.focus_topics.is_empty());
385/// ```
386#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
387pub struct ContextManager {
388    /// The most recent messages in the conversation window.
389    pub recent_messages: Vec<Message>,
390    /// Topics the agent is currently focused on.
391    pub focus_topics: Vec<Cow<'static, str>>,
392}
393
394/// Scope permissions for a task - controls what operations the agent may
395/// perform while executing.
396///
397/// # Examples
398///
399/// ```
400/// use lmm_agent::types::Scope;
401///
402/// let s = Scope { crud: true, auth: false, external: true };
403/// assert!(s.crud);
404/// ```
405#[derive(Debug, PartialEq, Eq, Default, Clone, Copy, Hash, Serialize, Deserialize)]
406pub struct Scope {
407    /// Allow Create / Read / Update / Delete operations.
408    pub crud: bool,
409    /// Allow authentication / authorisation related actions.
410    pub auth: bool,
411    /// Allow reaching external services or URLs.
412    pub external: bool,
413}
414
415/// Describes an HTTP route produced or consumed by the agent.
416#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
417pub struct Route {
418    /// Whether the route segment is dynamic (e.g. `"/items/{id}"`).
419    pub dynamic: Cow<'static, str>,
420    /// HTTP method (`"GET"`, `"POST"`, ...).
421    pub method: Cow<'static, str>,
422    /// Example request body as a JSON value.
423    pub body: Value,
424    /// Example response body as a JSON value.
425    pub response: Value,
426    /// Route path.
427    pub path: Cow<'static, str>,
428}
429
430/// The primary unit of work handed to an agent for execution.
431///
432/// # Examples
433///
434/// ```
435/// use lmm_agent::types::Task;
436///
437/// let task = Task::from_description("Summarise the Rust book.");
438/// assert!(!task.description.is_empty());
439/// ```
440#[derive(Debug, PartialEq, Eq, Default, Clone, Hash, Serialize, Deserialize)]
441pub struct Task {
442    /// Human-readable description of what must be accomplished.
443    pub description: Cow<'static, str>,
444    /// Optional permission scope.
445    pub scope: Option<Scope>,
446    /// External URLs the agent may need to consult.
447    pub urls: Option<Vec<Cow<'static, str>>>,
448    /// Generated or supplied frontend source code.
449    pub frontend_code: Option<Cow<'static, str>>,
450    /// Generated or supplied backend source code.
451    pub backend_code: Option<Cow<'static, str>>,
452    /// API endpoint schema discovered or generated by the agent.
453    pub api_schema: Option<Vec<Route>>,
454}
455
456// ThinkResult
457
458/// The outcome of one [`crate::agent::LmmAgent::think()`] invocation.
459///
460/// Returned by the closed-loop [`crate::cognition::r#loop::ThinkLoop`] controller.
461///
462/// # Examples
463///
464/// ```rust
465/// #[tokio::main]
466/// async fn main() {
467///     use lmm_agent::agent::LmmAgent;
468///
469///     let mut agent = LmmAgent::new("Tester".into(), "Test.".into());
470///     let result = agent.think("What is Rust ownership?").await.unwrap();
471///     assert!(result.steps > 0);
472/// }
473/// ```
474#[derive(Debug, Clone, PartialEq)]
475pub struct ThinkResult {
476    /// `true` when the Jaccard error fell below the convergence threshold.
477    pub converged: bool,
478
479    /// Number of feedback iterations executed.
480    pub steps: usize,
481
482    /// Final Jaccard distance between goal and last observation, ∈ [0, 1].
483    pub final_error: f64,
484
485    /// Snapshot of hot-store contents at termination (newest-first).
486    pub memory_snapshot: Vec<String>,
487
488    /// The per-step signals produced during the run.
489    pub signals: Vec<crate::cognition::signal::CognitionSignal>,
490}
491
492/// The active learning paradigms for the HELM engine.
493///
494/// Each variant corresponds to one sub-module of [`crate::cognition::learning`].
495/// The `Smart` variant enables all paradigms simultaneously.
496///
497/// # Examples
498///
499/// ```
500/// use lmm_agent::types::LearningMode;
501/// use std::collections::HashSet;
502///
503/// let all = LearningMode::all();
504/// assert!(all.contains(&LearningMode::QTable));
505/// assert!(all.contains(&LearningMode::Informal));
506/// ```
507#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
508pub enum LearningMode {
509    /// Tabular Bellman TD(0) Q-learning.
510    QTable,
511    /// Prototype-based task meta-adaptation.
512    MetaAdapt,
513    /// Knowledge distillation from cold store to KnowledgeIndex.
514    Distill,
515    /// Self-federated Q-table aggregation across agents.
516    Federated,
517    /// Elastic memory guard (Fisher-analog importance pinning).
518    Elastic,
519    /// Informal PMI co-occurrence mining.
520    Informal,
521    /// Enables all paradigms (alias for the full set).
522    Smart,
523}
524
525impl LearningMode {
526    /// Returns a `HashSet` containing all seven `LearningMode` variants.
527    pub fn all() -> HashSet<LearningMode> {
528        [
529            LearningMode::QTable,
530            LearningMode::MetaAdapt,
531            LearningMode::Distill,
532            LearningMode::Federated,
533            LearningMode::Elastic,
534            LearningMode::Informal,
535            LearningMode::Smart,
536        ]
537        .into_iter()
538        .collect()
539    }
540}
541
542/// A single SARS (State-Action-Reward-State') experience tuple for Q-learning.
543///
544/// # Examples
545///
546/// ```
547/// use lmm_agent::types::ExperienceRecord;
548/// use lmm_agent::cognition::learning::q_table::ActionKey;
549///
550/// let xp = ExperienceRecord { state: 1, action: ActionKey::Narrow, reward: 0.7, next_state: 2 };
551/// assert_eq!(xp.reward, 0.7);
552/// ```
553#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
554pub struct ExperienceRecord {
555    /// FNV-1a hash of the current state (query token bag).
556    pub state: u64,
557    /// The query-refinement action applied.
558    pub action: crate::cognition::learning::q_table::ActionKey,
559    /// Reward received (from `CognitionSignal::reward`).
560    pub reward: f64,
561    /// FNV-1a hash of the next state (observation token bag).
562    pub next_state: u64,
563}
564
565/// A serialisable snapshot of an agent's Q-table and reward total for federated exchange.
566///
567/// # Examples
568///
569/// ```
570/// use lmm_agent::types::AgentSnapshot;
571/// use lmm_agent::cognition::learning::q_table::QTable;
572///
573/// let snap = AgentSnapshot {
574///     agent_id: "agent-1".into(),
575///     q_table: QTable::new(0.1, 0.9, 0.0, 1.0, 0.0),
576///     total_reward: 3.5,
577/// };
578/// assert_eq!(snap.agent_id, "agent-1");
579/// ```
580#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct AgentSnapshot {
582    /// Unique identifier of the originating agent.
583    pub agent_id: String,
584    /// The agent's full Q-table at snapshot time.
585    pub q_table: crate::cognition::learning::q_table::QTable,
586    /// Total accumulated reward the agent achieved.
587    pub total_reward: f64,
588}
589
590impl Task {
591    /// Constructs a minimal [`Task`] from a plain description string.
592    pub fn from_description(description: impl Into<Cow<'static, str>>) -> Self {
593        Self {
594            description: description.into(),
595            ..Default::default()
596        }
597    }
598}
599
600// Default evaluation function
601
602/// Default introspective evaluation function.
603///
604/// Summarises goal completion progress from the agent's planner and returns a
605/// human-readable [`Cow<'static, str>`].
606///
607/// ## Attribution
608///
609/// Adapted from autogpt's `common/utils.rs`:
610/// <https://github.com/wiseaidotdev/autogpt/blob/main/autogpt/src/common/utils.rs>
611pub fn default_eval_fn(agent: &dyn crate::traits::agent::Agent) -> Cow<'static, str> {
612    if let Some(planner) = agent.planner() {
613        let total = planner.current_plan.len();
614        let completed = planner.completed_count();
615        let in_progress = total - completed;
616
617        let mut summary = format!(
618            "\n- Total Goals: {total}\n- Completed: {completed}\n- In Progress: {in_progress}\n\nGoals Summary:\n"
619        );
620
621        for goal in &planner.current_plan {
622            let state = if goal.completed { "✓" } else { "○" };
623            summary.push_str(&format!(
624                "  [{state}] (priority {}) {}\n",
625                goal.priority, goal.description
626            ));
627        }
628
629        Cow::Owned(summary)
630    } else {
631        Cow::Borrowed("No planner configured.")
632    }
633}