mirror of
https://github.com/mii443/rust-genai.git
synced 2025-08-22 16:25:27 +00:00
. clippy clean
. remove example 'images' alias to example c07-image . MessageContent::text_into_string/str return None when Parts (to avoid leak)
This commit is contained in:
@ -9,10 +9,6 @@ keywords = ["generative-ai","openai","chatgpt","gemini","ollama"]
|
||||
homepage = "https://github.com/jeremychone/rust-genai"
|
||||
repository = "https://github.com/jeremychone/rust-genai"
|
||||
|
||||
[[example]]
|
||||
name = "images"
|
||||
path = "examples/c07-image.rs"
|
||||
|
||||
[lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
# unused = { level = "allow", priority = -1 } # For exploratory dev.
|
||||
|
@ -5,6 +5,7 @@ use genai::chat::{ChatMessage, ChatRequest, ContentPart, ImageSource};
|
||||
use genai::Client;
|
||||
|
||||
const MODEL: &str = "gpt-4o-mini";
|
||||
const IMAGE_URL: &str = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -14,22 +15,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let mut chat_req = ChatRequest::default().with_system("Answer in one sentence");
|
||||
// This is similar to sending initial system chat messages (which will be cumulative with system chat messages)
|
||||
chat_req = chat_req.append_message(ChatMessage::user(
|
||||
vec![
|
||||
ContentPart::Text(question.to_string()),
|
||||
ContentPart::Image {
|
||||
content: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg".to_string(),
|
||||
content_type: "image/png".to_string(),
|
||||
source: ImageSource::Url,
|
||||
}
|
||||
]
|
||||
));
|
||||
chat_req = chat_req.append_message(ChatMessage::user(vec![
|
||||
ContentPart::Text(question.to_string()),
|
||||
ContentPart::Image {
|
||||
content: IMAGE_URL.to_string(),
|
||||
content_type: "image/jpg".to_string(),
|
||||
source: ImageSource::Url,
|
||||
},
|
||||
]));
|
||||
|
||||
println!("\n--- Question:\n{question}");
|
||||
let chat_res = client.exec_chat_stream(MODEL, chat_req.clone(), None).await?;
|
||||
|
||||
println!("\n--- Answer: (streaming)");
|
||||
let assistant_answer = print_chat_stream(chat_res, None).await?;
|
||||
let _assistant_answer = print_chat_stream(chat_res, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ impl MessageContent {
|
||||
}
|
||||
|
||||
/// Create a new MessageContent from provided content parts
|
||||
pub fn from_parts(parts: impl Into<Vec<ContentPart>>) -> Self { MessageContent::Parts(parts.into()) }
|
||||
pub fn from_parts(parts: impl Into<Vec<ContentPart>>) -> Self {
|
||||
MessageContent::Parts(parts.into())
|
||||
}
|
||||
|
||||
/// Create a new MessageContent with the ToolCalls variant
|
||||
pub fn from_tool_calls(tool_calls: Vec<ToolCall>) -> Self {
|
||||
@ -39,17 +41,12 @@ impl MessageContent {
|
||||
impl MessageContent {
|
||||
/// Returns the MessageContent as &str, only if it is MessageContent::Text
|
||||
/// Otherwise, it returns None.
|
||||
/// NOTE: As of now, it always returns Some(..) because MessageContent has only the Text variant.
|
||||
/// However, this is in preparation for future expansions.
|
||||
///
|
||||
/// NOTE: When multi parts content, this will return None and won't concatenate the text parts.
|
||||
pub fn text_as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
MessageContent::Text(content) => Some(content.as_str()),
|
||||
MessageContent::Parts(parts) => {
|
||||
Some(parts.iter().filter_map(|part| match part {
|
||||
ContentPart::Text(content) => Some(content.clone()),
|
||||
_ => None,
|
||||
}).collect::<Vec<String>>().join("\n").leak()) // TODO revisit this, should we leak &str?
|
||||
},
|
||||
MessageContent::Parts(_) => None,
|
||||
MessageContent::ToolCalls(_) => None,
|
||||
MessageContent::ToolResponses(_) => None,
|
||||
}
|
||||
@ -58,17 +55,11 @@ impl MessageContent {
|
||||
/// Consumes the MessageContent and returns it as &str,
|
||||
/// only if it is MessageContent::Text; otherwise, it returns None.
|
||||
///
|
||||
/// NOTE: As of now, it always returns Some(..) because MessageContent has only the Text variant.
|
||||
/// However, this is in preparation for future expansions.
|
||||
/// NOTE: When multi parts content, this will return None and won't concatenate the text parts.
|
||||
pub fn text_into_string(self) -> Option<String> {
|
||||
match self {
|
||||
MessageContent::Text(content) => Some(content),
|
||||
MessageContent::Parts(parts) => {
|
||||
Some(parts.into_iter().filter_map(|part| match part {
|
||||
ContentPart::Text(content) => Some(content),
|
||||
_ => None,
|
||||
}).collect::<Vec<String>>().join("\n"))
|
||||
},
|
||||
MessageContent::Parts(_) => None,
|
||||
MessageContent::ToolCalls(_) => None,
|
||||
MessageContent::ToolResponses(_) => None,
|
||||
}
|
||||
@ -112,7 +103,9 @@ impl From<ToolResponse> for MessageContent {
|
||||
}
|
||||
|
||||
impl From<Vec<ContentPart>> for MessageContent {
|
||||
fn from(parts: Vec<ContentPart>) -> Self { MessageContent::Parts(parts) }
|
||||
fn from(parts: Vec<ContentPart>) -> Self {
|
||||
MessageContent::Parts(parts)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: --- Froms
|
||||
@ -137,13 +130,10 @@ impl<'a> From<&'a str> for ContentPart {
|
||||
|
||||
// endregion: --- Froms
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, From)]
|
||||
pub enum ImageSource {
|
||||
Url,
|
||||
Base64
|
||||
|
||||
// No `Local` location, this would require handling errors like "file not found" etc.
|
||||
// Such file can be easily provided by user as Base64, also can implement convenient
|
||||
// TryFrom<File> to Base64 version. All LLMs accepts local Images only as Base64
|
||||
Base64, // No `Local` location, this would require handling errors like "file not found" etc.
|
||||
// Such file can be easily provided by user as Base64, also can implement convenient
|
||||
// TryFrom<File> to Base64 version. All LLMs accepts local Images only as Base64
|
||||
}
|
||||
|
Reference in New Issue
Block a user