openscript_sdk 0.1.0

Standard library and AI integration for OpenScript
Documentation
//! OpenAI integration for OpenScript
//!
//! This module provides functions for interacting with OpenAI's API,
//! including GPT text completion, DALL-E image generation, and more.

use openscript::{Value, Error, Result};
use async_openai::{
    Client,
    types::{
        CreateChatCompletionRequestArgs,
        CreateImageRequestArgs,
        ChatCompletionRequestMessage,
        ChatCompletionRequestUserMessageArgs,
        ChatCompletionRequestSystemMessageArgs,
        ImageSize,
        ImageResponseFormat,
    }
};

// Async implementation functions

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> {
    // TODO: Implement image editing functionality
    // This requires handling file uploads and the edit endpoint
    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 {
    // TODO: Re-enable once command registration is implemented
    // #[test]
    // fn test_register_commands() {
    //     let mut interpreter = Interpreter::new();
    //     assert!(register_commands(&mut interpreter).is_ok());
    //     
    //     // Check that commands are registered
    //     assert!(interpreter.get_global("ai_complete").is_some());
    //     assert!(interpreter.get_global("ai_chat").is_some());
    //     assert!(interpreter.get_global("ai_image").is_some());
    //     assert!(interpreter.get_global("ai_code").is_some());
    //     assert!(interpreter.get_global("ai_analyze").is_some());
    // }
}