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}