Skip to main content

cooklang_import/converters/
mod.rs

1mod anthropic;
2mod azure_openai;
3mod google;
4mod ollama;
5mod open_ai;
6mod prompt;
7
8pub use anthropic::AnthropicConverter;
9pub use azure_openai::AzureOpenAiConverter;
10pub use google::GoogleConverter;
11pub use ollama::OllamaConverter;
12pub use open_ai::OpenAiConverter;
13pub use prompt::{inject_recipe, COOKLANG_CONVERTER_PROMPT};
14
15use async_trait::async_trait;
16use serde::Serialize;
17use std::error::Error;
18
19/// Metadata about token usage from LLM conversion
20#[derive(Debug, Clone, Default, Serialize)]
21pub struct TokenUsage {
22    /// Number of tokens in the input/prompt
23    pub input_tokens: Option<u32>,
24    /// Number of tokens in the output/completion
25    pub output_tokens: Option<u32>,
26}
27
28/// Metadata about the conversion operation
29#[derive(Debug, Clone, Default, Serialize)]
30pub struct ConversionMetadata {
31    /// The model version/name that was used for conversion
32    pub model_version: Option<String>,
33    /// Token usage information
34    pub tokens_used: TokenUsage,
35    /// Time taken for the conversion in milliseconds
36    pub latency_ms: u64,
37}
38
39/// Result of a conversion operation including the converted text and metadata
40#[derive(Debug, Clone)]
41pub struct ConversionResult {
42    /// The converted Cooklang text
43    pub content: String,
44    /// Metadata about the conversion
45    pub metadata: ConversionMetadata,
46}
47
48/// Unified trait for all converters that transform recipe text to Cooklang format
49#[async_trait]
50pub trait Converter: Send + Sync {
51    /// Get the converter name (e.g., "open_ai", "anthropic")
52    fn name(&self) -> &str;
53
54    /// Convert recipe ingredients and instructions to Cooklang format
55    async fn convert(
56        &self,
57        ingredients_and_instructions: &str,
58    ) -> Result<ConversionResult, Box<dyn Error + Send + Sync>>;
59}
60
61/// Factory function to create a converter by name
62///
63/// # Arguments
64/// * `name` - The converter name (e.g., "open_ai", "anthropic")
65/// * `config` - Provider configuration
66///
67/// # Returns
68/// * `Some(Box<dyn Converter>)` if the converter exists
69/// * `None` if the converter name is not recognized
70pub fn create_converter(
71    name: &str,
72    config: &crate::config::ProviderConfig,
73) -> Option<Box<dyn Converter>> {
74    match name {
75        "open_ai" => OpenAiConverter::new(config)
76            .ok()
77            .map(|c| Box::new(c) as Box<dyn Converter>),
78        "anthropic" => AnthropicConverter::new(config)
79            .ok()
80            .map(|c| Box::new(c) as Box<dyn Converter>),
81        "azure_openai" => AzureOpenAiConverter::new(config)
82            .ok()
83            .map(|c| Box::new(c) as Box<dyn Converter>),
84        "google" => GoogleConverter::new(config)
85            .ok()
86            .map(|c| Box::new(c) as Box<dyn Converter>),
87        "ollama" => OllamaConverter::new(config)
88            .ok()
89            .map(|c| Box::new(c) as Box<dyn Converter>),
90        _ => None,
91    }
92}