1use crate::{
14 Capability, CapabilityRegistry, ConnectionProvider, ConnectionProviderRegistry, DriverRegistry,
15 EgressService, EmailSender, UtilityLlmService,
16 traits::{DisabledSessionFileSystemFactory, SessionFileSystemFactory},
17};
18use serde_json::Value;
19use std::sync::Arc;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27pub enum BuiltInHarnessRole {
28 Base,
31 Default,
34 Chat,
36}
37
38#[derive(Debug, Clone)]
40pub struct BuiltInCapabilityDefinition {
41 pub id: String,
43 pub config: Value,
45}
46
47impl BuiltInCapabilityDefinition {
48 pub fn new(id: impl Into<String>) -> Self {
50 Self {
51 id: id.into(),
52 config: serde_json::json!({}),
53 }
54 }
55
56 pub fn with_config(id: impl Into<String>, config: Value) -> Self {
58 Self {
59 id: id.into(),
60 config,
61 }
62 }
63}
64
65#[derive(Debug, Clone)]
70pub struct BuiltInHarnessDefinition {
71 pub name: String,
73 pub display_name: String,
75 pub description: String,
77 pub system_prompt: String,
79 pub parent_name: Option<String>,
81 pub tags: Vec<String>,
83 pub capabilities: Vec<BuiltInCapabilityDefinition>,
85 pub roles: Vec<BuiltInHarnessRole>,
87}
88
89impl BuiltInHarnessDefinition {
90 pub fn new(
92 name: impl Into<String>,
93 display_name: impl Into<String>,
94 description: impl Into<String>,
95 system_prompt: impl Into<String>,
96 ) -> Self {
97 Self {
98 name: name.into(),
99 display_name: display_name.into(),
100 description: description.into(),
101 system_prompt: system_prompt.into(),
102 parent_name: None,
103 tags: Vec::new(),
104 capabilities: Vec::new(),
105 roles: Vec::new(),
106 }
107 }
108
109 pub fn with_tags<I, S>(mut self, tags: I) -> Self
111 where
112 I: IntoIterator<Item = S>,
113 S: Into<String>,
114 {
115 self.tags = tags.into_iter().map(Into::into).collect();
116 self
117 }
118
119 pub fn with_parent_name(mut self, parent_name: impl Into<String>) -> Self {
121 self.parent_name = Some(parent_name.into());
122 self
123 }
124
125 pub fn with_capabilities<I>(mut self, capabilities: I) -> Self
127 where
128 I: IntoIterator<Item = BuiltInCapabilityDefinition>,
129 {
130 self.capabilities = capabilities.into_iter().collect();
131 self
132 }
133
134 pub fn with_roles<I>(mut self, roles: I) -> Self
136 where
137 I: IntoIterator<Item = BuiltInHarnessRole>,
138 {
139 self.roles = roles.into_iter().collect();
140 self
141 }
142
143 pub fn has_role(&self, role: BuiltInHarnessRole) -> bool {
145 self.roles.contains(&role)
146 }
147}
148
149#[derive(Clone)]
183pub struct PlatformDefinition {
184 capability_registry: CapabilityRegistry,
185 driver_registry: DriverRegistry,
186 connection_providers: ConnectionProviderRegistry,
187 built_in_harnesses: Vec<BuiltInHarnessDefinition>,
188 egress_service: Arc<dyn EgressService>,
189 email_sender: Arc<dyn EmailSender>,
190 utility_llm_service: Arc<dyn UtilityLlmService>,
191 session_file_system_factory: Arc<dyn SessionFileSystemFactory>,
192}
193
194impl PlatformDefinition {
195 pub fn new(capability_registry: CapabilityRegistry, driver_registry: DriverRegistry) -> Self {
197 Self {
198 capability_registry,
199 driver_registry,
200 connection_providers: ConnectionProviderRegistry::new(),
201 built_in_harnesses: Vec::new(),
202 egress_service: Arc::new(crate::DirectEgressService::default()),
203 email_sender: Arc::new(crate::DisabledEmailSender),
204 utility_llm_service: Arc::new(crate::DisabledUtilityLlmService),
205 session_file_system_factory: Arc::new(DisabledSessionFileSystemFactory),
206 }
207 }
208
209 pub fn builder() -> PlatformDefinitionBuilder {
211 PlatformDefinitionBuilder::new()
212 }
213
214 pub fn capability_registry(&self) -> &CapabilityRegistry {
216 &self.capability_registry
217 }
218
219 pub fn capability_registry_mut(&mut self) -> &mut CapabilityRegistry {
221 &mut self.capability_registry
222 }
223
224 pub fn driver_registry(&self) -> &DriverRegistry {
226 &self.driver_registry
227 }
228
229 pub fn driver_registry_mut(&mut self) -> &mut DriverRegistry {
231 &mut self.driver_registry
232 }
233
234 pub fn connection_providers(&self) -> &ConnectionProviderRegistry {
236 &self.connection_providers
237 }
238
239 pub fn connection_providers_mut(&mut self) -> &mut ConnectionProviderRegistry {
241 &mut self.connection_providers
242 }
243
244 pub fn built_in_harnesses(&self) -> &[BuiltInHarnessDefinition] {
246 &self.built_in_harnesses
247 }
248
249 pub fn built_in_harnesses_mut(&mut self) -> &mut Vec<BuiltInHarnessDefinition> {
251 &mut self.built_in_harnesses
252 }
253
254 pub fn egress_service(&self) -> Arc<dyn EgressService> {
256 self.egress_service.clone()
257 }
258
259 pub fn email_sender(&self) -> Arc<dyn EmailSender> {
261 self.email_sender.clone()
262 }
263
264 pub fn utility_llm_service(&self) -> Arc<dyn UtilityLlmService> {
266 self.utility_llm_service.clone()
267 }
268
269 pub fn session_file_system_factory(&self) -> Arc<dyn SessionFileSystemFactory> {
271 self.session_file_system_factory.clone()
272 }
273
274 pub fn add_built_in_harness(&mut self, harness: BuiltInHarnessDefinition) {
276 self.built_in_harnesses.push(harness);
277 }
278
279 pub fn harness_for_role(&self, role: BuiltInHarnessRole) -> Option<&BuiltInHarnessDefinition> {
281 self.built_in_harnesses.iter().find(|h| h.has_role(role))
282 }
283}
284
285impl Default for PlatformDefinition {
286 fn default() -> Self {
287 Self::new(CapabilityRegistry::new(), DriverRegistry::new())
288 }
289}
290
291impl std::fmt::Debug for PlatformDefinition {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 let harness_keys: Vec<_> = self.built_in_harnesses.iter().map(|h| &h.name).collect();
294 f.debug_struct("PlatformDefinition")
295 .field("capabilities", &self.capability_registry)
296 .field("drivers", &self.driver_registry.registered_providers())
297 .field("connection_providers", &self.connection_providers)
298 .field("built_in_harnesses", &harness_keys)
299 .field("egress_service", &self.egress_service.name())
300 .field("email_sender", &self.email_sender.name())
301 .field("utility_llm_service", &self.utility_llm_service.name())
302 .field(
303 "session_file_system_factory",
304 &self.session_file_system_factory.name(),
305 )
306 .finish()
307 }
308}
309
310pub struct PlatformDefinitionBuilder {
312 platform: PlatformDefinition,
313}
314
315impl PlatformDefinitionBuilder {
316 pub fn new() -> Self {
318 Self {
319 platform: PlatformDefinition::default(),
320 }
321 }
322
323 pub fn capability_registry(mut self, registry: CapabilityRegistry) -> Self {
325 self.platform.capability_registry = registry;
326 self
327 }
328
329 pub fn capability(mut self, capability: impl Capability + 'static) -> Self {
331 self.platform.capability_registry.register(capability);
332 self
333 }
334
335 pub fn driver_registry(mut self, registry: DriverRegistry) -> Self {
337 self.platform.driver_registry = registry;
338 self
339 }
340
341 pub fn connection_providers(mut self, registry: ConnectionProviderRegistry) -> Self {
343 self.platform.connection_providers = registry;
344 self
345 }
346
347 pub fn connection_provider(mut self, provider: impl ConnectionProvider + 'static) -> Self {
349 self.platform.connection_providers.register(provider);
350 self
351 }
352
353 pub fn built_in_harnesses<I>(mut self, harnesses: I) -> Self
355 where
356 I: IntoIterator<Item = BuiltInHarnessDefinition>,
357 {
358 self.platform.built_in_harnesses = harnesses.into_iter().collect();
359 self
360 }
361
362 pub fn add_built_in_harness(mut self, harness: BuiltInHarnessDefinition) -> Self {
364 self.platform.built_in_harnesses.push(harness);
365 self
366 }
367
368 pub fn egress_service(mut self, service: Arc<dyn EgressService>) -> Self {
370 self.platform.egress_service = service;
371 self
372 }
373
374 pub fn email_sender(mut self, sender: Arc<dyn EmailSender>) -> Self {
376 self.platform.email_sender = sender;
377 self
378 }
379
380 pub fn utility_llm_service(mut self, service: Arc<dyn UtilityLlmService>) -> Self {
382 self.platform.utility_llm_service = service;
383 self
384 }
385
386 pub fn session_file_system_factory(
388 mut self,
389 factory: Arc<dyn SessionFileSystemFactory>,
390 ) -> Self {
391 self.platform.session_file_system_factory = factory;
392 self
393 }
394
395 pub fn build(self) -> PlatformDefinition {
397 self.platform
398 }
399}
400
401impl Default for PlatformDefinitionBuilder {
402 fn default() -> Self {
403 Self::new()
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410 use crate::connection_provider::{
411 ConnectionFormSchema, ConnectionType, ConnectionValidation, FieldType, FormField,
412 };
413 use crate::{CapabilityStatus, CurrentTimeCapability};
414 use async_trait::async_trait;
415
416 struct TestProvider;
417
418 #[async_trait]
419 impl ConnectionProvider for TestProvider {
420 fn provider_id(&self) -> &str {
421 "test_provider"
422 }
423
424 fn display_name(&self) -> &str {
425 "Test Provider"
426 }
427
428 fn description(&self) -> &str {
429 "Test connection provider"
430 }
431
432 fn icon(&self) -> &str {
433 "plug"
434 }
435
436 fn connection_type(&self) -> ConnectionType {
437 ConnectionType::ApiKey
438 }
439
440 fn form_schema(&self) -> Option<ConnectionFormSchema> {
441 Some(ConnectionFormSchema {
442 fields: vec![FormField {
443 name: "api_key".to_string(),
444 label: "API Key".to_string(),
445 field_type: FieldType::Password,
446 required: true,
447 placeholder: None,
448 help_text: None,
449 }],
450 instructions_markdown: "Enter the API key.".to_string(),
451 })
452 }
453
454 async fn validate(&self, _credential: &str) -> Result<ConnectionValidation, String> {
455 Ok(ConnectionValidation {
456 provider_username: Some("test-user".to_string()),
457 provider_metadata: None,
458 })
459 }
460 }
461
462 #[test]
463 fn test_platform_definition_builder() {
464 let mut drivers = DriverRegistry::new();
465 crate::llmsim_driver::register_driver(&mut drivers);
466
467 let platform = PlatformDefinition::builder()
468 .driver_registry(drivers.clone())
469 .capability(CurrentTimeCapability)
470 .connection_provider(TestProvider)
471 .add_built_in_harness(
472 BuiltInHarnessDefinition::new(
473 "minimal",
474 "Minimal",
475 "Minimal harness",
476 "You are helpful.",
477 )
478 .with_roles([BuiltInHarnessRole::Base, BuiltInHarnessRole::Default]),
479 )
480 .build();
481
482 assert!(platform.capability_registry().has("current_time"));
483 assert!(platform.connection_providers().has("test_provider"));
484 assert_eq!(
485 platform
486 .harness_for_role(BuiltInHarnessRole::Base)
487 .unwrap()
488 .name,
489 "minimal"
490 );
491 assert!(
492 platform
493 .driver_registry()
494 .has_driver(&crate::ProviderType::LlmSim)
495 );
496 }
497
498 #[test]
499 fn test_platform_definition_mutation() {
500 let mut platform = PlatformDefinition::default();
501 platform
502 .capability_registry_mut()
503 .register(CurrentTimeCapability);
504 platform.connection_providers_mut().register(TestProvider);
505 platform.add_built_in_harness(
506 BuiltInHarnessDefinition::new("chat", "Chat", "Chat harness", "You are helpful.")
507 .with_roles([BuiltInHarnessRole::Chat]),
508 );
509
510 let info = crate::CapabilityInfo::from_core(
511 platform
512 .capability_registry()
513 .get("current_time")
514 .expect("current_time registered")
515 .as_ref(),
516 );
517 assert_eq!(info.status, CapabilityStatus::Available);
518 assert!(platform.connection_providers().has("test_provider"));
519 assert_eq!(
520 platform
521 .harness_for_role(BuiltInHarnessRole::Chat)
522 .unwrap()
523 .name,
524 "chat"
525 );
526 }
527}