use openscript::{Value, Error, Result};
use async_openai::{
Client,
types::{
CreateChatCompletionRequestArgs,
CreateImageRequestArgs,
ChatCompletionRequestMessage,
ChatCompletionRequestUserMessageArgs,
ChatCompletionRequestSystemMessageArgs,
ImageSize,
ImageResponseFormat,
}
};
pub async fn ai_complete(prompt: &str, model: &str, max_tokens: Option<u16>, temperature: f32) -> Result<Value> {
let client = Client::new();
let request = CreateChatCompletionRequestArgs::default()
.model(model)
.messages(vec![
ChatCompletionRequestMessage::User(
ChatCompletionRequestUserMessageArgs::default()
.content(prompt.to_string())
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build message: {}", e)))?
)
])
.temperature(temperature)
.max_tokens(max_tokens.unwrap_or(150))
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build request: {}", e)))?;
let response = client.chat().create(request).await
.map_err(|e| Error::runtime_error(format!("OpenAI API error: {}", e)))?;
if let Some(choice) = response.choices.first() {
Ok(Value::String(choice.message.content.clone().unwrap_or_default()))
} else {
Err(Error::runtime_error("No response from OpenAI"))
}
}
pub async fn ai_chat(messages: &[(String, String)], model: &str) -> Result<Value> {
let client = Client::new();
let mut request_messages = Vec::new();
for (role, content) in messages {
let message = match role.as_str() {
"system" => ChatCompletionRequestMessage::System(
ChatCompletionRequestSystemMessageArgs::default()
.content(content.clone())
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build message: {}", e)))?
),
"user" | _ => ChatCompletionRequestMessage::User(
ChatCompletionRequestUserMessageArgs::default()
.content(content.clone())
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build message: {}", e)))?
),
};
request_messages.push(message);
}
let request = CreateChatCompletionRequestArgs::default()
.model(model)
.messages(request_messages)
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build request: {}", e)))?;
let response = client.chat().create(request).await
.map_err(|e| Error::runtime_error(format!("OpenAI API error: {}", e)))?;
if let Some(choice) = response.choices.first() {
Ok(Value::String(choice.message.content.clone().unwrap_or_default()))
} else {
Err(Error::runtime_error("No response from OpenAI"))
}
}
pub async fn ai_image(prompt: &str, size: &str, count: u8) -> Result<Value> {
let client = Client::new();
let image_size = match size {
"256x256" => ImageSize::S256x256,
"512x512" => ImageSize::S512x512,
"1024x1024" => ImageSize::S1024x1024,
_ => ImageSize::S512x512,
};
let request = CreateImageRequestArgs::default()
.prompt(prompt)
.n(count)
.size(image_size)
.response_format(ImageResponseFormat::Url)
.build()
.map_err(|e| Error::runtime_error(format!("Failed to build request: {}", e)))?;
let response = client.images().create(request).await
.map_err(|e| Error::runtime_error(format!("OpenAI API error: {}", e)))?;
let urls: Vec<Value> = response.data.into_iter()
.filter_map(|img| match img.as_ref() {
async_openai::types::Image::Url { url, .. } => Some(Value::String(url.clone())),
_ => None,
})
.collect();
if count == 1 && !urls.is_empty() {
Ok(urls.into_iter().next().unwrap())
} else {
Ok(Value::Array(urls))
}
}
pub async fn ai_image_edit(_image_path: &str, _prompt: &str, _mask_path: Option<&str>) -> Result<Value> {
Err(Error::runtime_error("ai_image_edit() is not yet implemented"))
}
pub async fn ai_code(language: &str, description: &str, model: &str) -> Result<Value> {
let prompt = format!(
"Generate {} code for the following description:\n\n{}\n\nProvide only the code without explanations.",
language, description
);
ai_complete(&prompt, model, Some(500), 0.3).await
}
pub async fn ai_analyze(text: &str, analysis_type: &str, model: &str) -> Result<Value> {
let prompt = format!(
"Analyze the following text for {}:\n\n{}\n\nProvide a detailed analysis.",
analysis_type, text
);
ai_complete(&prompt, model, Some(300), 0.5).await
}
#[cfg(test)]
mod tests {
}