Skip to main content

agentai_core/
prompt.rs

1//! # Prompts
2//!
3//! A [`Prompt`] is what you send to the model in a run: text, images or other media, in order.
4//! `Agent::run` and `Agent::run_stream` of `agentai` take anything that implements
5//! [`IntoPrompt`], so plain text works as before:
6//!
7//! ```ignore
8//! use agentai::{Agent, AgentResult, Media, Prompt};
9//!
10//! # async fn example(agent: Agent, png: Vec<u8>) -> Result<(), agentai::AgentError> {
11//! let result: AgentResult<String> = agent.run("Why is the sky blue?").await?;
12//!
13//! // Vision models also accept images
14//! let prompt = Prompt::text("What is in this picture?")
15//!     .with_image_url("https://example.com/cat.jpg")
16//!     .with_media(Media::from_bytes("image/png", &png));
17//! let result: AgentResult<String> = agent.run(prompt).await?;
18//! # Ok(())
19//! # }
20//! ```
21
22use crate::message::{Media, Message, Part, Role};
23
24/// Content of the user message sent in a run: text and media, in order.
25///
26/// Build it with [`Prompt::text`] and the `with_*` methods. Whether the model can read images
27/// or other media depends on the model; `agentai` does not check it, a model without vision
28/// support usually answers with an error.
29#[derive(Debug, Clone, Default, PartialEq)]
30pub struct Prompt {
31    parts: Vec<Part>,
32}
33
34impl Prompt {
35    /// Creates an empty prompt.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Creates a prompt with text.
41    pub fn text(text: impl Into<String>) -> Self {
42        Self::new().with_text(text)
43    }
44
45    /// Adds text.
46    pub fn with_text(mut self, text: impl Into<String>) -> Self {
47        self.parts.push(Part::Text(text.into()));
48        self
49    }
50
51    /// Adds an image, audio or other file.
52    pub fn with_media(mut self, media: Media) -> Self {
53        self.parts.push(Part::Media(media));
54        self
55    }
56
57    /// Adds an image the provider downloads from `url`. See [`Media::image_url`].
58    pub fn with_image_url(self, url: impl Into<String>) -> Self {
59        self.with_media(Media::image_url(url))
60    }
61
62    /// Adds an image from its bytes, for example `image/png`. See [`Media::from_bytes`].
63    pub fn with_image_bytes(
64        self,
65        content_type: impl Into<String>,
66        bytes: impl AsRef<[u8]>,
67    ) -> Self {
68        self.with_media(Media::from_bytes(content_type, bytes))
69    }
70
71    /// Returns the parts of the prompt: [`Part::Text`] and [`Part::Media`], in order.
72    pub fn parts(&self) -> &[Part] {
73        &self.parts
74    }
75}
76
77/// The user message sent to the model.
78impl From<Prompt> for Message {
79    fn from(prompt: Prompt) -> Self {
80        Message::new(Role::User, prompt.parts)
81    }
82}
83
84/// Anything that can be sent as a [`Prompt`]: text (`&str`, `String` and every other type that
85/// converts into `String`), a [`Prompt`] or a single [`Media`].
86pub trait IntoPrompt {
87    /// Converts the value into a prompt.
88    fn into_prompt(self) -> Prompt;
89}
90
91impl<T: Into<String>> IntoPrompt for T {
92    fn into_prompt(self) -> Prompt {
93        Prompt::text(self)
94    }
95}
96
97impl IntoPrompt for Prompt {
98    fn into_prompt(self) -> Prompt {
99        self
100    }
101}
102
103impl IntoPrompt for Media {
104    fn into_prompt(self) -> Prompt {
105        Prompt::new().with_media(self)
106    }
107}