Skip to main content

weft_core/defaults/transforms/
mod.rs

1pub mod anthropic;
2pub mod openai;
3
4pub use anthropic::AnthropicTransform;
5pub use openai::OpenAITransform;
6
7use crate::layers::TransformLayer;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11/// Registry that selects a [`TransformLayer`] by provider `format` at runtime.
12///
13/// Previously the pipeline hard-wired `OpenAITransform`, so providers declaring
14/// `format = "anthropic"` were silently transformed as OpenAI. This registry
15/// dispatches on the provider's declared format instead.
16pub struct TransformRegistry {
17    map: HashMap<String, Arc<dyn TransformLayer>>,
18    fallback: Arc<dyn TransformLayer>,
19}
20
21impl TransformRegistry {
22    /// Build the default registry seeded with the built-in transforms.
23    pub fn with_defaults() -> Self {
24        let openai: Arc<dyn TransformLayer> = Arc::new(OpenAITransform);
25        let mut map: HashMap<String, Arc<dyn TransformLayer>> = HashMap::new();
26        map.insert("openai".to_string(), openai.clone());
27        map.insert("anthropic".to_string(), Arc::new(AnthropicTransform));
28        Self {
29            map,
30            fallback: openai,
31        }
32    }
33
34    /// Resolve the transform for a provider's declared `format`.
35    /// Unknown formats fall back to OpenAI (with a warning) to preserve the
36    /// previous behaviour rather than failing the request.
37    pub fn for_format(&self, format: &str) -> Arc<dyn TransformLayer> {
38        match self.map.get(format) {
39            Some(t) => t.clone(),
40            None => {
41                tracing::warn!(
42                    format = %format,
43                    "No transform registered for provider format; falling back to OpenAI transform"
44                );
45                self.fallback.clone()
46            }
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn dispatches_distinct_transforms_per_format() {
57        let reg = TransformRegistry::with_defaults();
58        let openai = reg.for_format("openai");
59        let anthropic = reg.for_format("anthropic");
60        // openai and anthropic must be DIFFERENT transforms — the original bug
61        // was that everything resolved to OpenAI regardless of format.
62        assert!(
63            !Arc::ptr_eq(&openai, &anthropic),
64            "anthropic format must not resolve to the OpenAI transform"
65        );
66    }
67
68    #[test]
69    fn unknown_format_falls_back_to_openai() {
70        let reg = TransformRegistry::with_defaults();
71        let openai = reg.for_format("openai");
72        let unknown = reg.for_format("some-unregistered-format");
73        assert!(
74            Arc::ptr_eq(&openai, &unknown),
75            "unknown format should fall back to the OpenAI transform"
76        );
77    }
78}
79
80