use std::pin::Pin;
use async_trait::async_trait;
use futures::Stream;
use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
use crate::provider::{Provider, ProviderConfig};
use crate::types::{CompletionRequest, CompletionResponse, StreamChunk};
#[allow(dead_code)]
const RUNWARE_API_URL: &str = "https://api.runware.ai/v1";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VideoModel {
RunwayGen45,
Kling20,
Pika10,
HailuoMini,
LeonardoUltra,
}
impl VideoModel {
pub fn model_id(&self) -> &'static str {
match self {
Self::RunwayGen45 => "runway-gen-4.5",
Self::Kling20 => "kling-2.0",
Self::Pika10 => "pika-1.0",
Self::HailuoMini => "hailuo-mini",
Self::LeonardoUltra => "leonardo-ultra",
}
}
}
#[allow(dead_code)]
pub struct RunwareProvider {
config: ProviderConfig,
}
impl RunwareProvider {
pub fn new(config: ProviderConfig) -> Result<Self> {
if config.api_key.is_none() {
return Err(Error::config("Runware API key is required"));
}
Ok(Self { config })
}
pub fn from_env() -> Result<Self> {
let config = ProviderConfig::from_env("RUNWARE_API_KEY");
Self::new(config)
}
pub fn with_api_key(api_key: impl Into<String>) -> Result<Self> {
let config = ProviderConfig::new(api_key);
Self::new(config)
}
#[allow(dead_code)]
fn api_url(&self) -> &str {
self.config.base_url.as_deref().unwrap_or(RUNWARE_API_URL)
}
pub async fn generate(
&self,
#[allow(unused_variables)] prompt: &str,
_model: VideoModel,
_duration: Option<u32>,
_width: Option<u32>,
_height: Option<u32>,
) -> Result<VideoGenerationResult> {
Err(Error::invalid_request(
"Video generation is coming soon. API integration in progress.",
))
}
}
#[async_trait]
impl Provider for RunwareProvider {
fn name(&self) -> &str {
"runware"
}
fn supports_streaming(&self) -> bool {
true
}
async fn complete(&self, _request: CompletionRequest) -> Result<CompletionResponse> {
Err(Error::invalid_request(
"Video generation is not available yet. Integration in progress.",
))
}
async fn complete_stream(
&self,
_request: CompletionRequest,
) -> Result<Pin<Box<dyn Stream<Item = Result<StreamChunk>> + Send>>> {
Err(Error::invalid_request(
"Video generation streaming is not available yet. Integration in progress.",
))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VideoGenerationResult {
pub task_id: String,
pub video_url: Option<String>,
pub status: String,
pub error: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_video_models() {
assert_eq!(VideoModel::RunwayGen45.model_id(), "runway-gen-4.5");
assert_eq!(VideoModel::Kling20.model_id(), "kling-2.0");
assert_eq!(VideoModel::Pika10.model_id(), "pika-1.0");
assert_eq!(VideoModel::HailuoMini.model_id(), "hailuo-mini");
assert_eq!(VideoModel::LeonardoUltra.model_id(), "leonardo-ultra");
}
#[test]
fn test_provider_creation() {
let provider = RunwareProvider::with_api_key("test-key").unwrap();
assert_eq!(provider.name(), "runware");
assert!(provider.supports_streaming());
}
#[test]
fn test_provider_requires_api_key() {
let config = ProviderConfig::default();
let result = RunwareProvider::new(config);
assert!(result.is_err());
}
}