use core::fmt;
use std::collections::HashMap;
use tracing::{span, Level};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use super::TemplateDataSource;
pub struct Template {
content: String,
data_source: Option<Box<dyn TemplateDataSource>>,
}
impl Template {
pub fn new<D: TemplateDataSource + 'static>(content: &str, data_source: D) -> Self {
Self {
content: content.to_string(),
data_source: Some(Box::new(data_source)),
}
}
pub fn simple<T>(content: T) -> Self
where
T: Into<String>,
{
Self {
content: content.into(),
data_source: None,
}
}
pub async fn compile(&self, data: &HashMap<String, String>) -> String {
let trace_span = span!(
Level::INFO,
"Template Compilation",
"langfuse.observation.type" = "event",
"langfuse.observation.metadata.tool_name" = "TemplateCompilation"
);
let trace_input = serde_json::to_string_pretty(&data).unwrap_or_default();
trace_span.set_attribute("langfuse.observation.input", trace_input);
let mut filled_content = self.content.clone();
if let Some(source) = &self.data_source {
let generated_data = source.get_values().await;
for (key, value) in generated_data {
let placeholder = format!("{{{{{key}}}}}");
filled_content = filled_content.replace(&placeholder, &value);
}
}
for (key, value) in data {
let placeholder = format!("{{{{{key}}}}}");
filled_content = filled_content.replace(&placeholder, value);
}
trace_span.set_attribute("langfuse.observation.output", filled_content.clone());
filled_content
}
}
impl Clone for Template {
fn clone(&self) -> Self {
Self {
content: self.content.clone(),
data_source: match &self.data_source {
None => None,
Some(data_source) => Some(data_source.clone_data_source()),
},
}
}
}
impl fmt::Debug for Template {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Template")
.field("content", &self.content)
.field(
"data_source",
&self
.data_source
.as_ref()
.map(|_| "Some(Box<dyn TemplateDataSource>)")
.unwrap_or("None"),
)
.finish()
}
}