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}