Skip to main content

artificial_prompt/
chain.rs

1//! Simple **builder** that concatenates multiple values implementing
2//! [`IntoPrompt`](artificial_core::template::IntoPrompt).
3//!
4//! ```text
5//! ┌───────────────┐    IntoPrompt     ┌────────────────┐
6//! │ CurrentDate   │ ─────────────────►│ Vec<Message>   │
7//! ├───────────────┤                   ├────────────────┤
8//! │ StaticFragment│ ─────────────────►│ Vec<Message>   │
9//! ├───────────────┤                   ├────────────────┤
10//! │ …             │ ─────────────────►│ Vec<Message>   │
11//! └───────────────┘                   └────────────────┘
12//!            ▲                                     │
13//!            └────────── PromptChain::build() ◄────┘
14//! ```
15//!
16//! # Motivation
17//!
18//! In real-world prompts you often want to **compose** smaller, reusable
19//! *fragments*—for example:
20//!
21//! * a static role description,
22//! * the current date/time,
23//! * the active user profile,
24//! * recent chat history,
25//! * a final user instruction.
26//!
27//! `PromptChain` lets you line up these fragments in a clear, linear fashion
28//! **without** mutable vectors or verbose `extend()` calls.
29//!
30//! # Usage
31//!
32//! ```rust,ignore
33//! use artificial_prompt::chain::PromptChain;
34//! use artificial_types::fragments::{CurrentDateFragment, StaticFragment};
35//! use artificial_core::generic::{GenericMessage, GenericRole};
36//!
37//! let messages: Vec<GenericMessage> = PromptChain::new()
38//!     .with(StaticFragment::new("You are a helpful bot.", GenericRole::System))
39//!     .with(CurrentDateFragment::new())
40//!     .with(StaticFragment::new("Convert the text to uppercase.", GenericRole::User))
41//!     .build();
42//!
43//! assert_eq!(messages.len(), 3);
44//! ```
45//!
46//! The generic parameter `Message` allows back-ends to plug in their own, richer
47//! message types while reusing the same chaining logic.
48use artificial_core::template::IntoPrompt;
49
50/// Lightweight container that accumulates messages produced by
51/// [`IntoPrompt`] implementors.
52///
53/// The single `Vec` field is kept private so the only way to obtain the result
54/// is through [`Self::build`], ensuring the builder API remains fluent.
55pub struct PromptChain<Message>(Vec<Message>);
56
57impl<Message> Default for PromptChain<Message> {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl<Message> PromptChain<Message> {
64    /// Create an empty chain.
65    pub fn new() -> Self {
66        Self(vec![])
67    }
68
69    /// Append the messages produced by `with` to the chain.
70    ///
71    /// The method takes `self` **by value** to encourage concise
72    /// call-chaining:
73    ///
74    /// ```rust
75    /// # use artificial_prompt::chain::PromptChain;
76    /// # use artificial_core::generic::{GenericMessage, GenericRole};
77    /// #
78    /// # let msg = GenericMessage::new("hi".into(), GenericRole::User);
79    /// let vec = PromptChain::new()
80    ///     .with(msg)
81    ///     .build();
82    /// ```
83    pub fn with(mut self, with: impl IntoPrompt<Message = Message>) -> Self {
84        self.0.append(&mut with.into_prompt());
85        self
86    }
87
88    /// Consume the builder and return the accumulated messages.
89    pub fn build(self) -> Vec<Message> {
90        self.0
91    }
92}