systemprompt_generator/prerender/
context.rs1use std::path::PathBuf;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5use systemprompt_database::DbPool;
6use systemprompt_extension::ExtensionRegistry;
7use systemprompt_models::{AppPaths, ContentConfigRaw, FullWebConfig};
8use systemprompt_provider_contracts::ContentDataProvider;
9use systemprompt_template_provider::{DynTemplateLoader, DynTemplateProvider, FileSystemLoader};
10use systemprompt_templates::{
11 CoreTemplateProvider, EmbeddedDefaultsProvider, TemplateRegistry, TemplateRegistryBuilder,
12};
13use tokio::fs;
14
15use crate::templates::{get_templates_path, load_web_config};
16
17pub struct PrerenderContext {
18 pub db_pool: DbPool,
19 pub config: ContentConfigRaw,
20 pub web_config: FullWebConfig,
21 pub template_registry: TemplateRegistry,
22 pub dist_dir: PathBuf,
23 pub content_data_providers: Vec<Arc<dyn ContentDataProvider>>,
24}
25
26impl std::fmt::Debug for PrerenderContext {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("PrerenderContext")
29 .field("config", &self.config)
30 .field("web_config", &self.web_config)
31 .field("template_registry", &self.template_registry)
32 .field("dist_dir", &self.dist_dir)
33 .field(
34 "content_data_providers_count",
35 &self.content_data_providers.len(),
36 )
37 .finish_non_exhaustive()
38 }
39}
40
41pub async fn load_prerender_context(db_pool: DbPool) -> Result<PrerenderContext> {
42 let paths = AppPaths::get().map_err(|e| anyhow::anyhow!("{}", e))?;
43 let config_path = paths.system().content_config();
44
45 let yaml_content = fs::read_to_string(&config_path)
46 .await
47 .context("Failed to read content config")?;
48 let config: ContentConfigRaw =
49 serde_yaml::from_str(&yaml_content).context("Failed to parse content config")?;
50
51 let web_config = load_web_config()
52 .await
53 .context("Failed to load web config")?;
54
55 tracing::debug!(config_path = %config_path.display(), "Loaded config");
56
57 let template_dir = get_templates_path(&web_config);
58 if !template_dir.exists() {
59 return Err(anyhow::anyhow!(
60 "Template directory not found: {}. Configure profile.paths.web_path or \
61 web_config.yaml paths.templates",
62 template_dir.display()
63 ));
64 }
65 let extension_template_path = template_dir;
66
67 let extension_provider = CoreTemplateProvider::discover_with_priority(
68 &extension_template_path,
69 CoreTemplateProvider::EXTENSION_PRIORITY,
70 )
71 .await
72 .context("Failed to discover extension templates")?;
73
74 let embedded_defaults = EmbeddedDefaultsProvider;
75
76 let loader = FileSystemLoader::with_path(&extension_template_path);
77
78 let extension_provider: DynTemplateProvider = Arc::new(extension_provider);
79 let embedded_defaults: DynTemplateProvider = Arc::new(embedded_defaults);
80 let loader: DynTemplateLoader = Arc::new(loader);
81
82 let mut registry_builder = TemplateRegistryBuilder::new()
83 .with_provider(extension_provider)
84 .with_provider(embedded_defaults)
85 .with_loader(loader);
86
87 let extensions = ExtensionRegistry::discover();
88 tracing::debug!(
89 extension_count = extensions.extensions().len(),
90 "Discovered extensions for prerender context"
91 );
92
93 let mut content_data_providers: Vec<Arc<dyn ContentDataProvider>> = Vec::new();
94
95 for ext in extensions.extensions() {
96 let providers = ext.page_data_providers();
97 let prerenderers = ext.page_prerenderers();
98 let content_providers = ext.content_data_providers();
99 tracing::debug!(
100 extension_id = %ext.metadata().id,
101 page_provider_count = providers.len(),
102 page_prerenderer_count = prerenderers.len(),
103 content_data_provider_count = content_providers.len(),
104 component_count = ext.component_renderers().len(),
105 extender_count = ext.template_data_extenders().len(),
106 "Extension providers discovered"
107 );
108
109 for component in ext.component_renderers() {
110 registry_builder = registry_builder.with_component(component);
111 }
112 for extender in ext.template_data_extenders() {
113 registry_builder = registry_builder.with_extender(extender);
114 }
115 for provider in providers {
116 registry_builder = registry_builder.with_page_provider(provider);
117 }
118 for prerenderer in prerenderers {
119 registry_builder = registry_builder.with_page_prerenderer(prerenderer);
120 }
121 content_data_providers.extend(content_providers);
122 }
123
124 content_data_providers.sort_by_key(|p| p.priority());
125
126 let template_registry = registry_builder
127 .build_and_init()
128 .await
129 .context("Failed to initialize template registry")?;
130
131 let dist_dir = AppPaths::get()
132 .map_err(|e| anyhow::anyhow!("{}", e))?
133 .web()
134 .dist()
135 .to_path_buf();
136
137 Ok(PrerenderContext {
138 db_pool,
139 config,
140 web_config,
141 template_registry,
142 dist_dir,
143 content_data_providers,
144 })
145}