feat(llm): inject system prompt + think/keep_alive/options into Ollama requests (T5.2)

This commit is contained in:
iamdoubz committed 2026-07-04 09:21:27 -05:00
1 parent 0165d4752c
commit 34e511bcf6
1 file changed
+43 -5
+43 -5
View File
@@ -57,7 +57,7 @@ const RESPONSE_FORMAT_INSTRUCTIONS: &str = "Respond in Markdown with exactly thr
are bullet lists (each line starting with \"- \"); write \"- None\" if a section has nothing \
to report. Do not add any other top-level sections.";
fn build_messages(prompt: &Prompt) -> Vec<serde_json::Value> {
fn build_messages(prompt: &Prompt, user_system: Option<&str>) -> Vec<serde_json::Value> {
let mut user = String::new();
if !prompt.metadata.is_empty() {
user.push_str(&prompt.metadata);
@@ -69,8 +69,19 @@ fn build_messages(prompt: &Prompt) -> Vec<serde_json::Value> {
}
user.push_str("Transcript:\n");
user.push_str(&prompt.transcript);
// A user's custom system prompt (Ollama advanced config) is *combined with*,
// not a replacement for, WA's output-format contract — otherwise summary /
// action-item parsing would break.
let mut system = String::new();
if let Some(us) = user_system.map(str::trim).filter(|s| !s.is_empty()) {
system.push_str(us);
system.push_str("\n\n");
}
system.push_str(RESPONSE_FORMAT_INSTRUCTIONS);
vec![
serde_json::json!({ "role": "system", "content": RESPONSE_FORMAT_INSTRUCTIONS }),
serde_json::json!({ "role": "system", "content": system }),
serde_json::json!({ "role": "user", "content": user }),
]
}
@@ -218,6 +229,9 @@ async fn stream_lines(
pub struct OllamaProvider {
pub endpoint: String, // e.g. http://localhost:11434
pub model: String,
/// Advanced tuning (`Settings.llm_advanced`): `{ system?, think?, keep_alive?,
/// options: {…} }`. Sparse — only non-default overrides. `Null` = all defaults.
pub advanced: serde_json::Value,
}
#[derive(Deserialize)]
@@ -278,11 +292,33 @@ impl LlmProvider for OllamaProvider {
}
async fn summarize(&self, prompt: Prompt, out: TokenSink) -> Result<Summary, LlmError> {
let body = serde_json::json!({
let user_system = self.advanced.get("system").and_then(|v| v.as_str());
let mut body = serde_json::json!({
"model": self.model,
"messages": build_messages(&prompt),
"messages": build_messages(&prompt, user_system),
"stream": true,
});
// Fold in the user's advanced tuning. `advanced` already holds only
// non-default values, so anything present is an intentional override.
if let Some(adv) = self.advanced.as_object() {
if let Some(think) = adv.get("think").filter(|v| !v.is_null()) {
body["think"] = think.clone();
}
if let Some(keep_alive) = adv
.get("keep_alive")
.and_then(|v| v.as_str())
.filter(|s| !s.trim().is_empty())
{
body["keep_alive"] = serde_json::json!(keep_alive);
}
if let Some(options) = adv
.get("options")
.and_then(|v| v.as_object())
.filter(|o| !o.is_empty())
{
body["options"] = serde_json::Value::Object(options.clone());
}
}
let resp = reqwest::Client::new()
.post(format!("{}/api/chat", self.base()))
.json(&body)
@@ -409,7 +445,9 @@ impl LlmProvider for OpenAiCompatProvider {
async fn summarize(&self, prompt: Prompt, out: TokenSink) -> Result<Summary, LlmError> {
let body = serde_json::json!({
"model": self.model,
"messages": build_messages(&prompt),
// Advanced tuning is Ollama-only; the OpenAI-compatible path uses no
// custom system prompt (its own server/model config governs that).
"messages": build_messages(&prompt, None),
"stream": true,
});
let req = self.auth(