camel_component_llm/
bundle.rs1use std::sync::Arc;
2
3use camel_component_api::{CamelError, ComponentBundle, ComponentRegistrar};
4
5use crate::LlmComponent;
6use crate::config::LlmGlobalConfig;
7use crate::provider_factory::{ProviderMap, build_provider_map};
8
9pub struct LlmBundle {
10 config: LlmGlobalConfig,
11 providers: ProviderMap,
12}
13
14impl ComponentBundle for LlmBundle {
15 fn config_key() -> &'static str {
16 "llm"
17 }
18
19 fn from_toml(value: toml::Value) -> Result<Self, CamelError> {
20 let config: LlmGlobalConfig = value
21 .try_into()
22 .map_err(|e: toml::de::Error| CamelError::Config(e.to_string()))?;
23 config.validate()?;
25 let providers = build_provider_map(&config).map_err(CamelError::from)?;
27 Ok(Self { config, providers })
28 }
29
30 fn register_all(self, ctx: &mut dyn ComponentRegistrar) {
31 let component = LlmComponent::from_parts(self.config, self.providers);
32 ctx.register_component_dyn(Arc::new(component));
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use camel_component_api::{ComponentBundle, ComponentRegistrar};
40 use std::sync::Arc;
41
42 struct TestRegistrar {
43 schemes: Vec<String>,
44 }
45
46 impl ComponentRegistrar for TestRegistrar {
47 fn register_component_dyn(&mut self, component: Arc<dyn camel_component_api::Component>) {
48 self.schemes.push(component.scheme().to_string());
49 }
50 }
51
52 #[test]
53 fn bundle_from_toml_registers_llm_scheme() {
54 let toml_str = r#"
55[providers.test]
56type = "mock"
57response = "echo"
58"#;
59 let value: toml::Value = toml::from_str(toml_str).expect("parse toml");
60 let bundle = LlmBundle::from_toml(value).expect("bundle");
61 let mut registrar = TestRegistrar { schemes: vec![] };
62 bundle.register_all(&mut registrar);
63 assert_eq!(registrar.schemes, vec!["llm"]);
64 }
65
66 #[test]
67 fn config_key_is_llm() {
68 assert_eq!(LlmBundle::config_key(), "llm");
69 }
70
71 #[test]
72 fn from_toml_fails_on_unsupported_provider() {
73 let toml_str = r#"
76[providers.unknown-test]
77type = "anthropic"
78api_key = "test-key"
79default_model = "claude-3"
80"#;
81 let value: toml::Value = toml::from_str(toml_str).expect("parse toml");
82 let result = LlmBundle::from_toml(value);
83 assert!(
84 result.is_err(),
85 "from_toml should fail for unsupported provider type"
86 );
87 }
88}