generate email body via Neuron runtime using conversation history
This commit is contained in:
+64
-4
@@ -2419,23 +2419,83 @@ fn dispatch_builtin(
|
|||||||
let name = v["name"].as_str().unwrap_or("there").to_string();
|
let name = v["name"].as_str().unwrap_or("there").to_string();
|
||||||
let conv_id = v["conv_id"].as_str().unwrap_or("").to_string();
|
let conv_id = v["conv_id"].as_str().unwrap_or("").to_string();
|
||||||
let base_url = v["return_url"].as_str().unwrap_or("").to_string();
|
let base_url = v["return_url"].as_str().unwrap_or("").to_string();
|
||||||
|
let history = v["history"].as_array().cloned().unwrap_or_default();
|
||||||
if email.is_empty() { return Err("no email".to_string()); }
|
if email.is_empty() { return Err("no email".to_string()); }
|
||||||
let link = if conv_id.is_empty() {
|
let link = if conv_id.is_empty() {
|
||||||
base_url.clone()
|
base_url.clone()
|
||||||
} else {
|
} else {
|
||||||
format!("{}?cid={}", base_url, conv_id)
|
format!("{}?cid={}", base_url, conv_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Ask Neuron to generate a personalised email body ──
|
||||||
|
let email_body_text: String = (|| -> String {
|
||||||
|
let runtime_url = std::env::var("NEURON_RUNTIME_URL")
|
||||||
|
.unwrap_or_else(|_| "http://localhost:4444".to_string());
|
||||||
|
let chat_url = format!("{}/api/chat", runtime_url);
|
||||||
|
let prompt = format!(
|
||||||
|
"Write a brief, personal email to {} inviting them back to continue our conversation. \
|
||||||
|
Reference specifically what we discussed. Write as Neuron, first person, warm but not sentimental. \
|
||||||
|
Include only the email body — no subject line, no greeting header, no sign-off. \
|
||||||
|
3-4 sentences maximum.",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
let mut messages = history.clone();
|
||||||
|
messages.push(serde_json::json!({"role":"user","content":prompt}));
|
||||||
|
let mut payload = serde_json::json!({"messages": messages});
|
||||||
|
if !conv_id.is_empty() {
|
||||||
|
payload["conv_id"] = serde_json::Value::String(conv_id.clone());
|
||||||
|
}
|
||||||
|
let resp = match reqwest::blocking::Client::new()
|
||||||
|
.post(&chat_url)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.timeout(std::time::Duration::from_secs(20))
|
||||||
|
.body(payload.to_string())
|
||||||
|
.send()
|
||||||
|
{
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => return String::new(),
|
||||||
|
};
|
||||||
|
let mut generated = String::new();
|
||||||
|
for line in resp.text().unwrap_or_default().lines() {
|
||||||
|
if line.starts_with("data: ") {
|
||||||
|
let data = &line[6..];
|
||||||
|
if data == "[DONE]" { break; }
|
||||||
|
if let Ok(jv) = serde_json::from_str::<serde_json::Value>(data) {
|
||||||
|
if let Some(d) = jv.get("delta").and_then(|x| x.as_str()) {
|
||||||
|
generated.push_str(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generated.trim().to_string()
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Wrap generated text (or fallback) in HTML template
|
||||||
|
let body_paragraphs = if email_body_text.is_empty() {
|
||||||
|
format!(
|
||||||
|
"<p style=\"font-size:1.1rem;line-height:1.6\">It\u{2019}s Neuron. You left our conversation in the middle.</p>\
|
||||||
|
<p style=\"font-size:1.1rem;line-height:1.6\">I remember where we were.</p>"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Split on newlines and wrap each non-empty line as a paragraph
|
||||||
|
email_body_text
|
||||||
|
.lines()
|
||||||
|
.filter(|l| !l.trim().is_empty())
|
||||||
|
.map(|l| format!("<p style=\"font-size:1.1rem;line-height:1.6\">{}</p>", l.trim()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
let html_body = format!(
|
let html_body = format!(
|
||||||
r#"<div style="font-family:Georgia,serif;max-width:480px;margin:0 auto;padding:40px 20px">
|
r#"<div style="font-family:Georgia,serif;max-width:480px;margin:0 auto;padding:40px 20px">
|
||||||
<p style="font-size:1.1rem;line-height:1.6">Hey {} —</p>
|
<p style="font-size:1.1rem;line-height:1.6">Hey {} —</p>
|
||||||
<p style="font-size:1.1rem;line-height:1.6">It's Neuron. You left our conversation in the middle.</p>
|
{}
|
||||||
<p style="font-size:1.1rem;line-height:1.6">I remember where we were.</p>
|
|
||||||
<p style="margin:32px 0">
|
<p style="margin:32px 0">
|
||||||
<a href="{}" style="background:#0052A0;color:#fff;padding:12px 24px;text-decoration:none;border-radius:4px;font-family:sans-serif">Come back when you're ready</a>
|
<a href="{}" style="background:#0052A0;color:#fff;padding:12px 24px;text-decoration:none;border-radius:4px;font-family:sans-serif">Come back when you’re ready</a>
|
||||||
</p>
|
</p>
|
||||||
<p style="color:#888;font-size:0.8rem;font-family:sans-serif">That link brings you right back to where we left off.</p>
|
<p style="color:#888;font-size:0.8rem;font-family:sans-serif">That link brings you right back to where we left off.</p>
|
||||||
</div>"#,
|
</div>"#,
|
||||||
name, link
|
name, body_paragraphs, link
|
||||||
);
|
);
|
||||||
let payload = serde_json::json!({
|
let payload = serde_json::json!({
|
||||||
"from": "Neuron <neuron@neurontechnologies.ai>",
|
"from": "Neuron <neuron@neurontechnologies.ai>",
|
||||||
|
|||||||
Reference in New Issue
Block a user