feat(llm): inject system prompt + think/keep_alive/options into Ollama requests (T5.2)
This commit is contained in:
1 parent
0165d4752c
commit
34e511bcf6
1 file changed
+43
-5
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user