From 53e79bda13829f0dcf7d9c16dcb3d382f82bf0a6 Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Tue, 3 Feb 2026 13:42:21 +0100 Subject: [PATCH 1/3] feat(config): add system prompt sections and DeepSeek `base_url` This change introduces `system_prompt_sections` to `AssistantConfig`, allowing users to define system prompts as a list of tagged and ordered sections. This improves the modularity and mergeability of system prompts across different configuration files. Additionally, the DeepSeek provider now supports a `base_url` setting to allow pointing to custom API endpoints or local proxies. The `AppConfig` has been updated to make `inherit` and `config_load_paths` optional fields, preventing unnecessary default values from appearing in serialized configurations. Extensive documentation comments were also added to most configuration structs and fields to improve schema generation. Signed-off-by: Jean Mertz --- crates/jp_config/src/assignment.rs | 20 ++ crates/jp_config/src/assistant.rs | 46 ++- .../jp_config/src/assistant/instructions.rs | 14 + crates/jp_config/src/assistant/sections.rs | 299 ++++++++++++++++++ crates/jp_config/src/conversation.rs | 7 + .../jp_config/src/conversation/attachment.rs | 6 +- crates/jp_config/src/conversation/title.rs | 3 + .../src/conversation/title/generate.rs | 6 + crates/jp_config/src/conversation/tool.rs | 37 ++- .../jp_config/src/conversation/tool/style.rs | 13 + crates/jp_config/src/editor.rs | 2 - crates/jp_config/src/lib.rs | 25 +- crates/jp_config/src/model.rs | 7 +- crates/jp_config/src/model/id.rs | 4 + crates/jp_config/src/model/parameters.rs | 14 +- crates/jp_config/src/providers.rs | 6 + crates/jp_config/src/providers/llm.rs | 9 + .../jp_config/src/providers/llm/anthropic.rs | 8 - .../jp_config/src/providers/llm/deepseek.rs | 7 + .../jp_config/src/providers/llm/llamacpp.rs | 3 + crates/jp_config/src/providers/llm/ollama.rs | 3 + crates/jp_config/src/providers/llm/openai.rs | 2 + .../jp_config/src/providers/llm/openrouter.rs | 2 + crates/jp_config/src/providers/mcp.rs | 4 + .../jp_config__tests__app_config_fields.snap | 2 + ...ig__tests__partial_app_config_default.snap | 4 + ...ts__partial_app_config_default_values.snap | 14 +- ...s__partial_app_config_empty_serialize.snap | 4 + crates/jp_config/src/style.rs | 9 + crates/jp_config/src/style/code.rs | 4 +- crates/jp_config/src/style/reasoning.rs | 11 +- crates/jp_config/src/style/typewriter.rs | 6 +- crates/jp_config/src/types/string.rs | 9 + crates/jp_config/src/types/vec.rs | 3 + crates/jp_config/src/util.rs | 6 + 35 files changed, 565 insertions(+), 54 deletions(-) create mode 100644 crates/jp_config/src/assistant/sections.rs diff --git a/crates/jp_config/src/assignment.rs b/crates/jp_config/src/assignment.rs index 55d488e6..2721f925 100644 --- a/crates/jp_config/src/assignment.rs +++ b/crates/jp_config/src/assignment.rs @@ -490,6 +490,26 @@ impl KvAssignment { self.try_f32().map(Some) } + /// Try to parse the value as a signed 32-bit integer. + pub(crate) fn try_i32(self) -> Result { + let Self { key, value, .. } = self; + + match value { + #[expect(clippy::cast_possible_truncation)] + KvValue::Json(Value::Number(v)) if v.is_i64() => Ok(v.as_i64().expect("is i64") as i32), + KvValue::Json(_) => type_error(&key, &value, &["number", "string"]), + KvValue::String(v) => Ok(v + .parse() + .map_err(|err| KvAssignmentError::new(key.full_path.clone(), err))?), + } + } + + /// Convenience method for [`Self::try_i32`] that wraps the `Ok` value into + /// `Some`. + pub(crate) fn try_some_i32(self) -> Result, KvAssignmentError> { + self.try_i32().map(Some) + } + /// Try to parse the value as a JSON array of partial configs, and set or /// merge the elements. pub(crate) fn try_vec( diff --git a/crates/jp_config/src/assistant.rs b/crates/jp_config/src/assistant.rs index 1fbc8be7..7eea0504 100644 --- a/crates/jp_config/src/assistant.rs +++ b/crates/jp_config/src/assistant.rs @@ -6,6 +6,7 @@ //! improved performance. pub mod instructions; +pub mod sections; pub mod tool_choice; use schematic::{Config, TransformResult}; @@ -14,6 +15,7 @@ use crate::{ assignment::{AssignKeyValue, AssignResult, KvAssignment, missing_key}, assistant::{ instructions::{InstructionsConfig, PartialInstructionsConfig}, + sections::{PartialSectionConfig, SectionConfig}, tool_choice::ToolChoice, }, delta::{PartialConfigDelta, delta_opt, delta_opt_partial}, @@ -30,14 +32,28 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Config)] #[config(rename_all = "snake_case")] pub struct AssistantConfig { - /// Optional name of the assistant. + /// The name of the assistant. + /// + /// This is purely cosmetic and currently not used in the UI. pub name: Option, /// The system prompt to use for the assistant. + /// + /// The system prompt is the initial instruction given to the assistant to + /// define its behavior, tone, and role. #[setting(nested, default = default_system_prompt, merge = string_with_strategy)] pub system_prompt: Option, + /// A list of system prompt sections for the assistant. + #[setting(nested, default = default_sections, merge = vec_with_strategy)] + pub system_prompt_sections: MergeableVec, + /// A list of instructions for the assistant. + /// + /// Instructions are similar to system prompts but are organized into a list + /// of titled sections. This allows for better organization and easier + /// overriding or extending of specific instructions when merging multiple + /// configurations. #[setting(nested, default = default_instructions, merge = vec_with_strategy)] pub instructions: MergeableVec, @@ -57,6 +73,9 @@ impl AssignKeyValue for PartialAssistantConfig { "name" => self.name = kv.try_some_string()?, "system_prompt" => self.system_prompt = kv.try_some_object_or_from_str()?, _ if kv.p("instructions") => kv.try_vec_of_nested(self.instructions.as_mut())?, + _ if kv.p("system_prompt_sections") => { + kv.try_vec_of_nested(self.system_prompt_sections.as_mut())?; + } "tool_choice" => self.tool_choice = kv.try_some_from_str()?, _ if kv.p("model") => self.model.assign(kv)?, _ => return missing_key(&kv), @@ -71,13 +90,17 @@ impl PartialConfigDelta for PartialAssistantConfig { Self { name: delta_opt(self.name.as_ref(), next.name), system_prompt: delta_opt_partial(self.system_prompt.as_ref(), next.system_prompt), - instructions: { - next.instructions - .into_iter() - .filter(|v| !self.instructions.contains(v)) - .collect::>() - .into() - }, + instructions: next + .instructions + .into_iter() + .filter(|v| !self.instructions.contains(v)) + .collect::>() + .into(), + system_prompt_sections: next + .system_prompt_sections + .into_iter() + .filter(|v| !self.system_prompt_sections.contains(v)) + .collect(), tool_choice: delta_opt(self.tool_choice.as_ref(), next.tool_choice), model: self.model.delta(next.model), } @@ -92,6 +115,7 @@ impl ToPartial for AssistantConfig { name: partial_opts(self.name.as_ref(), defaults.name), system_prompt: partial_opt_config(self.system_prompt.as_ref(), defaults.system_prompt), instructions: self.instructions.to_partial(), + system_prompt_sections: self.system_prompt_sections.to_partial(), tool_choice: partial_opt(&self.tool_choice, defaults.tool_choice), model: self.model.to_partial(), } @@ -123,6 +147,12 @@ fn default_instructions(_: &()) -> TransformResult TransformResult> { + Ok(MergeableVec::Vec(vec![])) +} + /// The default system prompt for the assistant. #[expect(clippy::trivially_copy_pass_by_ref, clippy::unnecessary_wraps)] fn default_system_prompt(_: &()) -> TransformResult> { diff --git a/crates/jp_config/src/assistant/instructions.rs b/crates/jp_config/src/assistant/instructions.rs index f7e20331..36ece951 100644 --- a/crates/jp_config/src/assistant/instructions.rs +++ b/crates/jp_config/src/assistant/instructions.rs @@ -19,10 +19,14 @@ use crate::{ #[config(default, rename_all = "snake_case")] pub struct InstructionsConfig { /// The title of the instructions. + /// + /// This is used to organize instructions into sections. #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, /// An optional description of the instructions. + /// + /// This is used to provide more context about the instructions. #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, @@ -37,9 +41,13 @@ pub struct InstructionsConfig { pub position: isize, /// The list of instructions. + /// + /// Each item is a separate instruction. pub items: Vec, /// A list of examples to go with the instructions. + /// + /// Examples are used to demonstrate how to follow the instructions. #[setting(nested)] pub examples: Vec, } @@ -253,12 +261,18 @@ impl FromStr for PartialExampleConfig { #[config(rename_all = "snake_case")] pub struct ContrastConfig { /// The good example. + /// + /// This is an example of how to follow the instruction. pub good: String, /// The bad example. + /// + /// This is an example of how NOT to follow the instruction. pub bad: String, /// Why is the good example better than the bad example? + /// + /// This is optional, but recommended to provide more context. pub reason: Option, } diff --git a/crates/jp_config/src/assistant/sections.rs b/crates/jp_config/src/assistant/sections.rs new file mode 100644 index 00000000..bcdcd523 --- /dev/null +++ b/crates/jp_config/src/assistant/sections.rs @@ -0,0 +1,299 @@ +//! System prompt sections. + +use std::str::FromStr; + +use schematic::Config; +use serde::{Deserialize, Serialize}; + +use crate::{ + BoxedError, + assignment::{AssignKeyValue, AssignResult, KvAssignment, missing_key}, + delta::{PartialConfigDelta, delta_opt}, + partial::{ToPartial, partial_opt, partial_opts}, +}; + +/// Command configuration, either as a string or a complete configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config)] +#[config(rename_all = "snake_case", serde(untagged))] +#[serde(untagged)] +pub enum SectionConfigOrString { + /// A single string, which is interpreted as the full content of the + /// section. + String(String), + + /// A complete section configuration. + #[setting(nested)] + Config(SectionConfig), +} + +impl AssignKeyValue for PartialSectionConfigOrString { + fn assign(&mut self, kv: KvAssignment) -> AssignResult { + match kv.key_string().as_str() { + "" => *self = kv.try_object_or_from_str()?, + _ => match self { + Self::String(_) => return missing_key(&kv), + Self::Config(config) => config.assign(kv)?, + }, + } + + Ok(()) + } +} + +impl PartialConfigDelta for PartialSectionConfigOrString { + fn delta(&self, next: Self) -> Self { + match (self, next) { + (Self::Config(prev), Self::Config(next)) => Self::Config(prev.delta(next)), + (_, next) => next, + } + } +} + +impl ToPartial for SectionConfigOrString { + fn to_partial(&self) -> Self::Partial { + match self { + Self::String(v) => Self::Partial::String(v.to_owned()), + Self::Config(v) => Self::Partial::Config(v.to_partial()), + } + } +} + +impl FromStr for PartialSectionConfigOrString { + type Err = BoxedError; + + fn from_str(s: &str) -> Result { + Ok(Self::String(s.to_owned())) + } +} + +/// A list of sections for a system prompt. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config)] +#[config(default, rename_all = "snake_case")] +pub struct SectionConfig { + /// The content of the section. + pub content: String, + + /// Optional tag surrounding the section. + #[serde(skip_serializing_if = "Option::is_none")] + pub tag: Option, + + /// The position of the section. + /// + /// A lower position will be shown first. This is useful when merging + /// multiple sections, and you want to make sure the most important + /// sections are shown first. + /// + /// Defaults to `0`. + #[setting(default = 0)] + pub position: i32, +} + +impl AssignKeyValue for PartialSectionConfig { + fn assign(&mut self, kv: KvAssignment) -> AssignResult { + match kv.key_string().as_str() { + "" => *self = kv.try_object_or_from_str()?, + "tag" => self.tag = kv.try_some_string()?, + "content" => self.content = kv.try_some_string()?, + "position" => self.position = kv.try_some_i32()?, + _ => return missing_key(&kv), + } + + Ok(()) + } +} + +impl ToPartial for SectionConfig { + fn to_partial(&self) -> Self::Partial { + let defaults = Self::Partial::default(); + + Self::Partial { + tag: partial_opts(self.tag.as_ref(), defaults.tag), + content: partial_opt(&self.content, defaults.content), + position: partial_opt(&self.position, defaults.position), + } + } +} + +impl PartialConfigDelta for PartialSectionConfig { + fn delta(&self, next: Self) -> Self { + Self { + tag: delta_opt(self.tag.as_ref(), next.tag), + content: delta_opt(self.content.as_ref(), next.content), + position: delta_opt(self.position.as_ref(), next.position), + } + } +} + +impl SectionConfig { + /// Add a tag to the section. + #[must_use] + pub fn with_tag(mut self, tag: impl Into) -> Self { + self.tag = Some(tag.into()); + self + } + + /// Add content to the section. + #[must_use] + pub fn with_content(mut self, content: impl Into) -> Self { + self.content = content.into(); + self + } +} + +impl FromStr for PartialSectionConfig { + type Err = BoxedError; + + fn from_str(s: &str) -> Result { + Ok(Self { + content: Some(s.to_owned()), + ..Default::default() + }) + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// +// #[test] +// fn test_instructions_assign() { +// let mut p = PartialSectionConfig::default(); +// +// let kv = KvAssignment::try_from_cli("title", "foo").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.title, Some("foo".into())); +// +// let kv = KvAssignment::try_from_cli("description", "bar").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.description, Some("bar".into())); +// +// let kv = KvAssignment::try_from_cli("items", "baz").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.items, Some(vec!["baz".into()])); +// +// let kv = KvAssignment::try_from_cli("items+", "quux").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.items, Some(vec!["baz".into(), "quux".into()])); +// +// let kv = KvAssignment::try_from_cli("items.0", "quuz").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.items, Some(vec!["quuz".into(), "quux".into()])); +// +// let kv = KvAssignment::try_from_cli("examples", "qux").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.examples, vec![PartialExampleConfig::Generic( +// "qux".into() +// )]); +// +// let kv = KvAssignment::try_from_cli("examples+", "quuz").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.examples, vec![ +// PartialExampleConfig::Generic("qux".into()), +// PartialExampleConfig::Generic("quuz".into()) +// ]); +// +// let kv = KvAssignment::try_from_cli("examples.0", "quuz").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p.examples, vec![ +// PartialExampleConfig::Generic("quuz".into()), +// PartialExampleConfig::Generic("quuz".into()) +// ]); +// } +// +// #[test] +// fn test_example_assign() { +// let mut p = PartialExampleConfig::default(); +// +// let kv = KvAssignment::try_from_cli("", "bar").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialExampleConfig::Generic("bar".into())); +// +// let kv = KvAssignment::try_from_cli(":", r#""bar""#).unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialExampleConfig::Generic("bar".into())); +// +// let kv = KvAssignment::try_from_cli(":", r#"{"good":"bar","bad":"baz"}"#).unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!( +// p, +// PartialExampleConfig::Contrast(PartialContrastConfig { +// good: Some("bar".into()), +// bad: Some("baz".into()), +// reason: None, +// }) +// ); +// +// let kv = KvAssignment::try_from_cli("nope", "nope").unwrap(); +// assert_eq!(&p.assign(kv).unwrap_err().to_string(), "nope: unknown key"); +// } +// +// #[test] +// fn test_contrast_assign() { +// let mut p = PartialContrastConfig::default(); +// +// let kv = KvAssignment::try_from_cli("good", "bar").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialContrastConfig { +// good: Some("bar".into()), +// bad: None, +// reason: None, +// }); +// +// let kv = KvAssignment::try_from_cli("bad", "baz").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialContrastConfig { +// good: Some("bar".into()), +// bad: Some("baz".into()), +// reason: None, +// }); +// +// let kv = KvAssignment::try_from_cli("reason", "qux").unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialContrastConfig { +// good: Some("bar".into()), +// bad: Some("baz".into()), +// reason: Some("qux".into()), +// }); +// +// let kv = KvAssignment::try_from_cli(":", r#"{"good":"one","bad":null}"#).unwrap(); +// p.assign(kv).unwrap(); +// assert_eq!(p, PartialContrastConfig { +// good: Some("one".into()), +// bad: None, +// reason: None, +// }); +// +// let kv = KvAssignment::try_from_cli("nope", "nope").unwrap(); +// assert_eq!(&p.assign(kv).unwrap_err().to_string(), "nope: unknown key"); +// } +// +// #[test] +// fn test_instructions_to_xml() { +// let i = SectionConfig { +// title: Some("foo".to_owned()), +// description: Some("bar".to_owned()), +// position: 0, +// items: vec![ +// "foo".to_owned(), +// "bar bar".to_owned(), +// "baz]]> baz".to_owned(), +// ], +// examples: vec![ +// ExampleConfig::Generic("foo".to_owned()), +// ExampleConfig::Contrast(ContrastConfig { +// good: "bar".to_owned(), +// bad: "baz".to_owned(), +// reason: Some("qux".to_owned()), +// }), +// ExampleConfig::Contrast(ContrastConfig { +// good: "quux".to_owned(), +// bad: "quuz".to_owned(), +// reason: None, +// }), +// ], +// }; +// +// let xml = i.try_to_xml().unwrap(); +// insta::assert_snapshot!(xml); +// } +// } diff --git a/crates/jp_config/src/conversation.rs b/crates/jp_config/src/conversation.rs index d9cd9e49..e3b9373c 100644 --- a/crates/jp_config/src/conversation.rs +++ b/crates/jp_config/src/conversation.rs @@ -22,14 +22,21 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct ConversationConfig { /// Title configuration. + /// + /// This section configures how conversation titles are generated. #[setting(nested)] pub title: TitleConfig, /// Tool configuration. + /// + /// This section configures tool usage within conversations. #[setting(nested)] pub tools: ToolsConfig, /// Attachment configuration. + /// + /// This section defines attachments (files, resources) that are added to + /// conversations. #[setting(nested, merge = schematic::merge::append_vec)] pub attachments: Vec, } diff --git a/crates/jp_config/src/conversation/attachment.rs b/crates/jp_config/src/conversation/attachment.rs index 95bc77d9..427f8193 100644 --- a/crates/jp_config/src/conversation/attachment.rs +++ b/crates/jp_config/src/conversation/attachment.rs @@ -14,7 +14,7 @@ use crate::{ partial::{ToPartial, partial_opt}, }; -/// Reasoning configuration. +/// Attachment configuration. #[derive(Debug, Clone, PartialEq, Config)] #[config(serde(untagged))] pub enum AttachmentConfig { @@ -108,10 +108,14 @@ impl From for PartialAttachmentConfig { #[derive(Debug, Clone, PartialEq, Config)] pub struct AttachmentObjectConfig { /// The type of the attachment. + /// + /// e.g. `file`, `http`, etc. #[setting(required, rename = "type")] pub kind: String, /// The url path of the attachment. + /// + /// The path part of the URL. #[setting(required)] pub path: String, diff --git a/crates/jp_config/src/conversation/title.rs b/crates/jp_config/src/conversation/title.rs index 6b6cbaad..e93ab0a2 100644 --- a/crates/jp_config/src/conversation/title.rs +++ b/crates/jp_config/src/conversation/title.rs @@ -16,6 +16,9 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct TitleConfig { /// Title generation configuration. + /// + /// Configures how and when titles are automatically generated for new + /// conversations. #[setting(nested)] pub generate: GenerateConfig, } diff --git a/crates/jp_config/src/conversation/title/generate.rs b/crates/jp_config/src/conversation/title/generate.rs index cebb331a..59e34282 100644 --- a/crates/jp_config/src/conversation/title/generate.rs +++ b/crates/jp_config/src/conversation/title/generate.rs @@ -14,10 +14,16 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct GenerateConfig { /// Whether to automatically generate titles for conversations. + /// + /// If true, a title will be generated based on the first prompt of the + /// conversation. #[setting(default = true)] pub auto: bool, /// Model configuration specific to title generation. + /// + /// By default, the main assistant model is used. You can override this to + /// use a faster or cheaper model specifically for title generation. #[setting(nested)] pub model: Option, } diff --git a/crates/jp_config/src/conversation/tool.rs b/crates/jp_config/src/conversation/tool.rs index 84d99f9e..1226f623 100644 --- a/crates/jp_config/src/conversation/tool.rs +++ b/crates/jp_config/src/conversation/tool.rs @@ -26,10 +26,14 @@ pub mod style; #[config(rename_all = "snake_case", allow_unknown_fields)] pub struct ToolsConfig { /// Global config + /// + /// This section configures global defaults for all tools. #[setting(nested, rename = "*")] pub defaults: ToolsDefaultsConfig, /// Tool config + /// + /// This section configures individual tools. The key is the tool ID. #[setting(nested, flatten, merge = merge_nested_indexmap)] tools: IndexMap, } @@ -124,13 +128,25 @@ impl ToolsConfig { #[config(rename_all = "snake_case")] pub struct ToolsDefaultsConfig { /// Whether the tool is enabled. + /// + /// If false, the tool will not be available to the assistant. pub enable: Option, /// How to run the tool. + /// + /// - `ask`: Ask for confirmation before running the tool. + /// - `unattended`: Run the tool without asking for confirmation. + /// - `edit`: Open an editor to edit the tool call before running it. + /// - `skip`: Skip running the tool. #[setting(required)] pub run: RunMode, /// How to deliver the results of the tool to the assistant. + /// + /// - `unattended`: Always deliver the results of the tool call. + /// - `ask`: Ask for confirmation before delivering the results. + /// - `edit`: Open an editor to edit the result before delivering it. + /// - `skip`: Skip delivering the results. #[setting(default)] pub result: ResultMode, @@ -182,18 +198,29 @@ impl ToPartial for ToolsDefaultsConfig { #[config(rename_all = "snake_case")] pub struct ToolConfig { /// The source of the tool. + /// + /// - `builtin`: Use a built-in tool. + /// - `local`: Use a locally defined tool (shell command). + /// - `mcp`: Use a tool from an MCP server. #[setting(required)] pub source: ToolSource, /// Whether the tool is enabled. + /// + /// If false, the tool will not be available to the assistant. pub enable: Option, /// The command to run. Only used for local tools. + /// + /// Can be a simple string (e.g. `ls -la`) or a structured object with + /// `program`, `args`, and `shell` properties. #[setting(nested)] pub command: Option, - /// The description of the tool. This will override any existing - /// description, such as the one from an MCP server, or a built-in tool. + /// The description of the tool. + /// + /// This will override any existing description, such as the one from an + /// MCP server, or a built-in tool. pub description: Option, /// The parameters expected by the tool. @@ -212,12 +239,18 @@ pub struct ToolConfig { pub parameters: IndexMap, /// How to run the tool. + /// + /// Overrides the global default. pub run: Option, /// How to deliver the results of the tool to the assistant. + /// + /// Overrides the global default. pub result: Option, /// How to display the results of the tool in the terminal. + /// + /// Overrides the global default. #[setting(nested)] pub style: Option, diff --git a/crates/jp_config/src/conversation/tool/style.rs b/crates/jp_config/src/conversation/tool/style.rs index ca1848f3..808ec665 100644 --- a/crates/jp_config/src/conversation/tool/style.rs +++ b/crates/jp_config/src/conversation/tool/style.rs @@ -18,14 +18,27 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct DisplayStyleConfig { /// How to display the results of the tool call. + /// + /// - `full`: Show the full tool call results inline. + /// - `off`: Never show the tool call results inline. + /// - ``: Show the first N lines of the tool call results inline. #[setting(default)] pub inline_results: InlineResults, /// How to display the link to the file containing the tool call results. + /// + /// - `full`: Show the full file path. + /// - `osc8`: Show a clickable link (OSC8 escape sequence). + /// - `off`: Do not show the file path. #[setting(default)] pub results_file_link: LinkStyle, /// How to display the tool call parameters. + /// + /// - `json`: Show as JSON. + /// - `function_call`: Show as a function call (e.g. `tool_name(arg=val)`). + /// - `off`: Do not show parameters. + /// - ``: Use a custom command to format the parameters. #[setting(default)] pub parameters: ParametersStyle, } diff --git a/crates/jp_config/src/editor.rs b/crates/jp_config/src/editor.rs index 2cbe18de..2b8819c4 100644 --- a/crates/jp_config/src/editor.rs +++ b/crates/jp_config/src/editor.rs @@ -26,8 +26,6 @@ pub struct EditorConfig { /// /// Defaults to `JP_EDITOR`, `VISUAL`, and `EDITOR`. /// - /// # Safety - /// /// Note that for security reasons, the value of these environment variables /// are split by whitespace, and only the first element is used for the /// command. Meaning, you cannot set `JP_EDITOR="subl -w"`, because it will diff --git a/crates/jp_config/src/lib.rs b/crates/jp_config/src/lib.rs index 7ef0dbd2..ceebba68 100644 --- a/crates/jp_config/src/lib.rs +++ b/crates/jp_config/src/lib.rs @@ -83,27 +83,18 @@ type BoxedError = Box; #[config(rename_all = "snake_case")] pub struct AppConfig { /// Inherit from a local ancestor or global configuration file. - #[setting(default = true)] + #[setting(optional)] pub inherit: bool, - /// Paths from which configuration files can be loaded without specifying - /// the full path to the file. + /// Directories to search for additional configuration files. /// - /// Paths are relative to both the workspace root, and the user's workspace - /// override directory. + /// Files in these directories can be loaded on demand using the `--cfg` + /// flag. Use this to organize reusable configurations, such as personas or + /// tool sets. /// - /// For example, a path of `.jp/config.d` will be resolved to - /// `/.jp/config.d`, and - /// `$XDG_DATA_HOME/jp/workspace//.jp/config.d`. - /// - /// If a file exists at both locations, the user's workspace file will be - /// merged on top of the workspace file. - /// - /// Files in these paths are **NOT** loaded by default, but can instead be - /// referenced by their basename, optionally without a file extension. For - /// example, a file named `my-agent.toml` in a config load path can be - /// loaded using `--cfg my-agent`. - #[setting(default = vec![], merge = schematic::merge::append_vec, transform = util::vec_dedup)] + /// For example, to load `.jp/agents/dev.toml`, add `.jp/agents` to this + /// list and run `jp query --cfg dev`. + #[setting(optional, merge = schematic::merge::append_vec, transform = util::vec_dedup)] pub config_load_paths: Vec, /// Extends the configuration from the given files. diff --git a/crates/jp_config/src/model.rs b/crates/jp_config/src/model.rs index 83c8c6d3..70d63fe0 100644 --- a/crates/jp_config/src/model.rs +++ b/crates/jp_config/src/model.rs @@ -17,13 +17,18 @@ use crate::{ /// Assistant-specific configuration. #[derive(Debug, Clone, PartialEq, Config)] -#[config(rename_all = "snake_case")] +#[config(rename_all = "snake_case", allow_unknown_fields)] pub struct ModelConfig { /// The model ID. + /// + /// This identifies the LLM model to use. It can be a full ID (e.g. + /// `anthropic/claude-3-opus-20240229`) or an alias. #[setting(nested)] pub id: ModelIdOrAliasConfig, /// The model parameters. + /// + /// Configuration for model parameters such as temperature, max tokens, etc. #[setting(nested)] pub parameters: ParametersConfig, } diff --git a/crates/jp_config/src/model/id.rs b/crates/jp_config/src/model/id.rs index be87ff4f..2ffc4780 100644 --- a/crates/jp_config/src/model/id.rs +++ b/crates/jp_config/src/model/id.rs @@ -166,10 +166,14 @@ impl PartialModelIdOrAliasConfig { #[config(rename_all = "snake_case", no_deserialize_derive)] pub struct ModelIdConfig { /// The provider to supply the model. + /// + /// e.g. `anthropic`, `openai`, `ollama`, etc. #[setting(required)] pub provider: ProviderId, /// The actual model name. + /// + /// e.g. `claude-3-opus-20240229`, `gpt-4-turbo`, `llama3`, etc. #[setting(required)] pub name: Name, } diff --git a/crates/jp_config/src/model/parameters.rs b/crates/jp_config/src/model/parameters.rs index d1f28881..b4900f45 100644 --- a/crates/jp_config/src/model/parameters.rs +++ b/crates/jp_config/src/model/parameters.rs @@ -37,15 +37,17 @@ pub struct ParametersConfig { /// Reasoning configuration. /// - /// If `None`, the model uses reasoning with reasonable defaults if it - /// supports it, otherwise disabled. If set to `Some`, the model uses the - /// provided configuration. + /// - `off`: Reasoning is disabled. + /// - `auto`: Reasoning is enabled with defaults (if supported). + /// - ``: Reasoning is enabled with the specified effort. #[setting(nested)] pub reasoning: Option, /// Temperature of the model. /// - /// ... + /// Controls the randomness of the output. Higher values (e.g. 0.8) make + /// the output more random, while lower values (e.g. 0.2) make it more + /// deterministic. pub temperature: Option, /// Control the randomness and diversity of the generated text. Also @@ -71,11 +73,11 @@ pub struct ParametersConfig { /// The `stop_words` parameter can be set to specific sequences, such as a /// period or specific word, to stop the model from generating text when it /// encounters these sequences. - #[setting(default, merge = schematic::merge::append_vec)] + #[setting(default, merge = schematic::merge::append_vec, skip_serializing_if = "Vec::is_empty")] pub stop_words: Vec, /// Other non-typed parameters that some models might support. - #[setting(default, flatten, merge = schematic::merge::merge_iter)] + #[setting(default, flatten, merge = schematic::merge::merge_iter, skip_serializing_if = "Map::is_empty")] pub other: Map, } diff --git a/crates/jp_config/src/providers.rs b/crates/jp_config/src/providers.rs index 063d12e4..fc5cc72a 100644 --- a/crates/jp_config/src/providers.rs +++ b/crates/jp_config/src/providers.rs @@ -22,10 +22,16 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct ProviderConfig { /// LLM provider configurations. + /// + /// Configuration for different LLM providers (e.g. Anthropic, OpenAI, + /// Ollama). #[setting(nested)] pub llm: LlmProviderConfig, /// MCP provider configurations. + /// + /// Configuration for Model Context Protocol (MCP) servers. The key is the + /// server ID. #[setting(nested, merge = merge_nested_indexmap)] pub mcp: IndexMap, } diff --git a/crates/jp_config/src/providers/llm.rs b/crates/jp_config/src/providers/llm.rs index b4285e93..2ca581fc 100644 --- a/crates/jp_config/src/providers/llm.rs +++ b/crates/jp_config/src/providers/llm.rs @@ -33,6 +33,15 @@ use crate::{ #[config(default, rename_all = "snake_case")] pub struct LlmProviderConfig { /// Aliases for specific provider/model combinations. + /// + /// This allows you to define short names for models. + /// + /// For example: + /// + /// ```toml + /// [providers.llm.aliases] + /// haiku = { provider = "anthropic", name = "claude-3-haiku-20240307" } + /// ``` #[setting(nested, merge = merge_nested_indexmap)] pub aliases: IndexMap, diff --git a/crates/jp_config/src/providers/llm/anthropic.rs b/crates/jp_config/src/providers/llm/anthropic.rs index dbd13499..712d19fd 100644 --- a/crates/jp_config/src/providers/llm/anthropic.rs +++ b/crates/jp_config/src/providers/llm/anthropic.rs @@ -27,14 +27,6 @@ pub struct AnthropicConfig { /// This is enabled by default, but even when enabled, if you explicitly set /// the model's `max_tokens` parameter, the request will not be chained when /// that limit is reached. This allows for better cost control. - /// - /// Further note that if/when an API response contains a tool call request, - /// no chaining will be performed, as the API expects the next request to - /// contain the tool call results. - /// - /// Finally, be aware that there is a performance penalty for enabling this - /// feature, as the client has to copy the received messages in order to - /// append them to the next request. #[setting(default = true)] pub chain_on_max_tokens: bool, diff --git a/crates/jp_config/src/providers/llm/deepseek.rs b/crates/jp_config/src/providers/llm/deepseek.rs index 3242d275..db921d36 100644 --- a/crates/jp_config/src/providers/llm/deepseek.rs +++ b/crates/jp_config/src/providers/llm/deepseek.rs @@ -15,6 +15,10 @@ pub struct DeepseekConfig { /// Environment variable that contains the API key. #[setting(default = "DEEPSEEK_API_KEY")] pub api_key_env: String, + + /// The base URL to use for API requests. + #[setting(default = "https://api.deepseek.com")] + pub base_url: String, } impl AssignKeyValue for PartialDeepseekConfig { @@ -22,6 +26,7 @@ impl AssignKeyValue for PartialDeepseekConfig { match kv.key_string().as_str() { "" => *self = kv.try_object()?, "api_key_env" => self.api_key_env = kv.try_some_string()?, + "base_url" => self.base_url = kv.try_some_string()?, _ => return missing_key(&kv), } @@ -33,6 +38,7 @@ impl PartialConfigDelta for PartialDeepseekConfig { fn delta(&self, next: Self) -> Self { Self { api_key_env: delta_opt(self.api_key_env.as_ref(), next.api_key_env), + base_url: delta_opt(self.base_url.as_ref(), next.base_url), } } } @@ -43,6 +49,7 @@ impl ToPartial for DeepseekConfig { Self::Partial { api_key_env: partial_opt(&self.api_key_env, defaults.api_key_env), + base_url: partial_opt(&self.base_url, defaults.base_url), } } } diff --git a/crates/jp_config/src/providers/llm/llamacpp.rs b/crates/jp_config/src/providers/llm/llamacpp.rs index 8122066d..1bb6502a 100644 --- a/crates/jp_config/src/providers/llm/llamacpp.rs +++ b/crates/jp_config/src/providers/llm/llamacpp.rs @@ -13,6 +13,9 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct LlamacppConfig { /// The base URL to use for API requests. + /// + /// The default is `http://127.0.0.1:8080`, which is the default URL for + /// `llama.cpp` server. #[setting(default = "http://127.0.0.1:8080")] pub base_url: String, } diff --git a/crates/jp_config/src/providers/llm/ollama.rs b/crates/jp_config/src/providers/llm/ollama.rs index daaac9b1..e85b8faa 100644 --- a/crates/jp_config/src/providers/llm/ollama.rs +++ b/crates/jp_config/src/providers/llm/ollama.rs @@ -13,6 +13,9 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct OllamaConfig { /// The base URL to use for API requests. + /// + /// The default is `http://localhost:11434`, which is the default URL for + /// Ollama. #[setting(default = "http://localhost:11434")] pub base_url: String, } diff --git a/crates/jp_config/src/providers/llm/openai.rs b/crates/jp_config/src/providers/llm/openai.rs index cc8758b7..2027fcea 100644 --- a/crates/jp_config/src/providers/llm/openai.rs +++ b/crates/jp_config/src/providers/llm/openai.rs @@ -23,6 +23,8 @@ pub struct OpenaiConfig { pub base_url: String, /// Environment variable that contains the API base URL key. + /// + /// If set, the value of this environment variable will override `base_url`. #[setting(default = "OPENAI_BASE_URL")] pub base_url_env: String, } diff --git a/crates/jp_config/src/providers/llm/openrouter.rs b/crates/jp_config/src/providers/llm/openrouter.rs index b2e2b555..a0f70202 100644 --- a/crates/jp_config/src/providers/llm/openrouter.rs +++ b/crates/jp_config/src/providers/llm/openrouter.rs @@ -21,6 +21,8 @@ pub struct OpenrouterConfig { pub app_name: String, /// Optional HTTP referrer to send with requests. + /// + /// This is used by Openrouter to identify the application. pub app_referrer: Option, /// The base URL to use for API requests. diff --git a/crates/jp_config/src/providers/mcp.rs b/crates/jp_config/src/providers/mcp.rs index 9c449509..23ae4205 100644 --- a/crates/jp_config/src/providers/mcp.rs +++ b/crates/jp_config/src/providers/mcp.rs @@ -61,6 +61,10 @@ pub struct StdioConfig { pub arguments: Vec, /// The environment variables to expose to the command. + /// + /// By default, the command inherits the environment of the parent process. + /// You can use this to add additional environment variables, or override + /// existing ones. #[setting(default, merge = schematic::merge::append_vec)] pub variables: Vec, diff --git a/crates/jp_config/src/snapshots/jp_config__tests__app_config_fields.snap b/crates/jp_config/src/snapshots/jp_config__tests__app_config_fields.snap index 3cbe574b..12fbf634 100644 --- a/crates/jp_config/src/snapshots/jp_config__tests__app_config_fields.snap +++ b/crates/jp_config/src/snapshots/jp_config__tests__app_config_fields.snap @@ -31,6 +31,7 @@ expression: "AppConfig::fields()" "providers.llm.google.api_key_env", "providers.llm.google.base_url", "providers.llm.deepseek.api_key_env", + "providers.llm.deepseek.base_url", "providers.llm.anthropic.api_key_env", "providers.llm.anthropic.base_url", "providers.llm.anthropic.beta_headers", @@ -50,6 +51,7 @@ expression: "AppConfig::fields()" "assistant.instructions", "assistant.name", "assistant.system_prompt", + "assistant.system_prompt_sections", "assistant.tool_choice", "assistant.model.id", "assistant.model.parameters.max_tokens", diff --git a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default.snap b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default.snap index 12f5b1b2..e1d95045 100644 --- a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default.snap +++ b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default.snap @@ -9,6 +9,9 @@ PartialAppConfig { assistant: PartialAssistantConfig { name: None, system_prompt: None, + system_prompt_sections: Vec( + [], + ), instructions: Vec( [], ), @@ -91,6 +94,7 @@ PartialAppConfig { }, deepseek: PartialDeepseekConfig { api_key_env: None, + base_url: None, }, google: PartialGoogleConfig { api_key_env: None, diff --git a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default_values.snap b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default_values.snap index 58c9bcb0..6a3a60f2 100644 --- a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default_values.snap +++ b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_default_values.snap @@ -5,12 +5,8 @@ expression: "PartialAppConfig::default_values(&())" Ok( Some( PartialAppConfig { - inherit: Some( - true, - ), - config_load_paths: Some( - [], - ), + inherit: None, + config_load_paths: None, extends: Some( [ Path( @@ -34,6 +30,9 @@ Ok( }, ), ), + system_prompt_sections: Vec( + [], + ), instructions: Merged( MergedVec { value: [ @@ -168,6 +167,9 @@ Ok( api_key_env: Some( "DEEPSEEK_API_KEY", ), + base_url: Some( + "https://api.deepseek.com", + ), }, google: PartialGoogleConfig { api_key_env: Some( diff --git a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_empty_serialize.snap b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_empty_serialize.snap index 48449646..7c9b2b0b 100644 --- a/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_empty_serialize.snap +++ b/crates/jp_config/src/snapshots/jp_config__tests__partial_app_config_empty_serialize.snap @@ -9,6 +9,9 @@ PartialAppConfig { assistant: PartialAssistantConfig { name: None, system_prompt: None, + system_prompt_sections: Vec( + [], + ), instructions: Vec( [], ), @@ -91,6 +94,7 @@ PartialAppConfig { }, deepseek: PartialDeepseekConfig { api_key_env: None, + base_url: None, }, google: PartialGoogleConfig { api_key_env: None, diff --git a/crates/jp_config/src/style.rs b/crates/jp_config/src/style.rs index e662418c..ef08750d 100644 --- a/crates/jp_config/src/style.rs +++ b/crates/jp_config/src/style.rs @@ -27,18 +27,27 @@ use crate::{ #[config(rename_all = "snake_case")] pub struct StyleConfig { /// Fenced code block style. + /// + /// Configures how code blocks in the assistant's response are rendered. #[setting(nested)] pub code: CodeConfig, /// Reasoning content style. + /// + /// Configures how the assistant's reasoning process (thinking) is + /// displayed. #[setting(nested)] pub reasoning: ReasoningConfig, /// Tool call content style. + /// + /// Configures how tool calls are displayed. #[setting(nested)] pub tool_call: ToolCallConfig, /// Typewriter style. + /// + /// Configures the typing animation effect. #[setting(nested)] pub typewriter: TypewriterConfig, } diff --git a/crates/jp_config/src/style/code.rs b/crates/jp_config/src/style/code.rs index 0bb4443e..eabfd5e8 100644 --- a/crates/jp_config/src/style/code.rs +++ b/crates/jp_config/src/style/code.rs @@ -29,7 +29,9 @@ pub struct CodeConfig { /// Show a link to the file containing the source code in code blocks. /// - /// Can be one of: `off`, `full`, `osc8`. + /// - `off`: Do not show the link. + /// - `full`: Show the full file path. + /// - `osc8`: Show a clickable link (OSC8 escape sequence). /// /// See: #[setting(default = "osc8")] diff --git a/crates/jp_config/src/style/reasoning.rs b/crates/jp_config/src/style/reasoning.rs index ca01a572..f9fb7748 100644 --- a/crates/jp_config/src/style/reasoning.rs +++ b/crates/jp_config/src/style/reasoning.rs @@ -16,13 +16,22 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Config)] #[config(rename_all = "snake_case")] pub struct ReasoningConfig { - /// Whether to show reasoning blocks. + /// How to display the reasoning content. + /// + /// - `full`: Show all reasoning content (default). + /// - `hidden`: Do not show reasoning content. + /// - `summary`: Show a summary of the reasoning (requires `summary_model`). + /// - `static`: Show a static "reasoning..." message. + /// - `progress`: Show "reasoning..." with animated dots. + /// - ``: Show the first N characters of the reasoning content. #[setting(default)] pub display: ReasoningDisplayConfig, /// The model to use for summarizing reasoning blocks. /// /// Defaults to the assistant's default model, but with reasoning disabled. + /// + /// Only used when `display` is set to `summary`. #[setting(nested)] pub summary_model: Option, } diff --git a/crates/jp_config/src/style/typewriter.rs b/crates/jp_config/src/style/typewriter.rs index 732d0aad..b1a02cbb 100644 --- a/crates/jp_config/src/style/typewriter.rs +++ b/crates/jp_config/src/style/typewriter.rs @@ -17,6 +17,8 @@ use crate::{ pub struct TypewriterConfig { /// Delay between printing characters. /// + /// The default is `3` milliseconds. + /// /// You can use one of the following formats: /// - `10` for 10 milliseconds /// - `5m` for 5 milliseconds @@ -25,7 +27,9 @@ pub struct TypewriterConfig { #[config(default = "3")] pub text_delay: DelayDuration, - /// Delay between printing characters. + /// Delay between printing code-block characters. + /// + /// The default is `500` microseconds. /// /// You can use one of the following formats: /// - `10` for 10 milliseconds diff --git a/crates/jp_config/src/types/string.rs b/crates/jp_config/src/types/string.rs index 0d24323d..c32b86f9 100644 --- a/crates/jp_config/src/types/string.rs +++ b/crates/jp_config/src/types/string.rs @@ -128,10 +128,19 @@ pub struct MergedString { pub value: String, /// The merge strategy. + /// + /// - `append`: Append this string to the existing string (default). + /// - `prepend`: Prepend this string to the existing string. + /// - `replace`: Replace the existing string with this one. #[setting(default)] pub strategy: MergedStringStrategy, /// The separator to use between the previous value and the new value. + /// + /// - `none`: No separator (default). + /// - `space`: Single space separator. + /// - `line`: New line separator. + /// - `paragraph`: Paragraph separator (two new lines). #[setting(default)] pub separator: MergedStringSeparator, diff --git a/crates/jp_config/src/types/vec.rs b/crates/jp_config/src/types/vec.rs index b7d08a70..3ae58d21 100644 --- a/crates/jp_config/src/types/vec.rs +++ b/crates/jp_config/src/types/vec.rs @@ -210,6 +210,9 @@ pub struct MergedVec { pub value: Vec, /// The merge strategy. + /// + /// - `append`: Append the vec to the previous value. + /// - `replace`: Replace the previous value with the new value. #[setting(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")] // The strategy is wrapped in an `Option`, because otherwise diff --git a/crates/jp_config/src/util.rs b/crates/jp_config/src/util.rs index 8bf28e61..35ff2fcd 100644 --- a/crates/jp_config/src/util.rs +++ b/crates/jp_config/src/util.rs @@ -191,6 +191,12 @@ pub fn build(mut partial: PartialAppConfig) -> Result { .instructions .sort_by(|a, b| a.position.cmp(&b.position)); + // Sort sections by position. + config + .assistant + .system_prompt_sections + .sort_by(|a, b| a.position.cmp(&b.position)); + Ok(config) } From 2c84ebfd03aaf6d71129b55c873341522b440bde Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Tue, 3 Feb 2026 13:51:57 +0100 Subject: [PATCH 2/3] fixup! feat(config): add system prompt sections and DeepSeek `base_url` Signed-off-by: Jean Mertz --- crates/jp_config/src/assistant/sections.rs | 167 +++--------------- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...edacted_thinking__conversation_stream.snap | 3 +- ...request_chaining__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...mini_3_reasoning__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...r_event_metadata__conversation_stream.snap | 3 +- ...r_event_metadata__conversation_stream.snap | 3 +- ...r_event_metadata__conversation_stream.snap | 3 +- ...pletion_nostream__conversation_stream.snap | 3 +- ...ompletion_stream__conversation_stream.snap | 3 +- ...urn_conversation__conversation_stream.snap | 3 +- ...t_tool_call_auto__conversation_stream.snap | 3 +- ...ol_call_function__conversation_stream.snap | 3 +- ...ol_call_nostream__conversation_stream.snap | 3 +- ...l_call_reasoning__conversation_stream.snap | 3 +- ...red_no_reasoning__conversation_stream.snap | 3 +- ...quired_reasoning__conversation_stream.snap | 3 +- ...tool_call_stream__conversation_stream.snap | 3 +- ...tool_call_strict__conversation_stream.snap | 3 +- ...r_event_metadata__conversation_stream.snap | 3 +- 74 files changed, 167 insertions(+), 219 deletions(-) diff --git a/crates/jp_config/src/assistant/sections.rs b/crates/jp_config/src/assistant/sections.rs index bcdcd523..3d8872d2 100644 --- a/crates/jp_config/src/assistant/sections.rs +++ b/crates/jp_config/src/assistant/sections.rs @@ -151,149 +151,24 @@ impl FromStr for PartialSectionConfig { } } -// #[cfg(test)] -// mod tests { -// use super::*; -// -// #[test] -// fn test_instructions_assign() { -// let mut p = PartialSectionConfig::default(); -// -// let kv = KvAssignment::try_from_cli("title", "foo").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.title, Some("foo".into())); -// -// let kv = KvAssignment::try_from_cli("description", "bar").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.description, Some("bar".into())); -// -// let kv = KvAssignment::try_from_cli("items", "baz").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.items, Some(vec!["baz".into()])); -// -// let kv = KvAssignment::try_from_cli("items+", "quux").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.items, Some(vec!["baz".into(), "quux".into()])); -// -// let kv = KvAssignment::try_from_cli("items.0", "quuz").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.items, Some(vec!["quuz".into(), "quux".into()])); -// -// let kv = KvAssignment::try_from_cli("examples", "qux").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.examples, vec![PartialExampleConfig::Generic( -// "qux".into() -// )]); -// -// let kv = KvAssignment::try_from_cli("examples+", "quuz").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.examples, vec![ -// PartialExampleConfig::Generic("qux".into()), -// PartialExampleConfig::Generic("quuz".into()) -// ]); -// -// let kv = KvAssignment::try_from_cli("examples.0", "quuz").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p.examples, vec![ -// PartialExampleConfig::Generic("quuz".into()), -// PartialExampleConfig::Generic("quuz".into()) -// ]); -// } -// -// #[test] -// fn test_example_assign() { -// let mut p = PartialExampleConfig::default(); -// -// let kv = KvAssignment::try_from_cli("", "bar").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialExampleConfig::Generic("bar".into())); -// -// let kv = KvAssignment::try_from_cli(":", r#""bar""#).unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialExampleConfig::Generic("bar".into())); -// -// let kv = KvAssignment::try_from_cli(":", r#"{"good":"bar","bad":"baz"}"#).unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!( -// p, -// PartialExampleConfig::Contrast(PartialContrastConfig { -// good: Some("bar".into()), -// bad: Some("baz".into()), -// reason: None, -// }) -// ); -// -// let kv = KvAssignment::try_from_cli("nope", "nope").unwrap(); -// assert_eq!(&p.assign(kv).unwrap_err().to_string(), "nope: unknown key"); -// } -// -// #[test] -// fn test_contrast_assign() { -// let mut p = PartialContrastConfig::default(); -// -// let kv = KvAssignment::try_from_cli("good", "bar").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialContrastConfig { -// good: Some("bar".into()), -// bad: None, -// reason: None, -// }); -// -// let kv = KvAssignment::try_from_cli("bad", "baz").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialContrastConfig { -// good: Some("bar".into()), -// bad: Some("baz".into()), -// reason: None, -// }); -// -// let kv = KvAssignment::try_from_cli("reason", "qux").unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialContrastConfig { -// good: Some("bar".into()), -// bad: Some("baz".into()), -// reason: Some("qux".into()), -// }); -// -// let kv = KvAssignment::try_from_cli(":", r#"{"good":"one","bad":null}"#).unwrap(); -// p.assign(kv).unwrap(); -// assert_eq!(p, PartialContrastConfig { -// good: Some("one".into()), -// bad: None, -// reason: None, -// }); -// -// let kv = KvAssignment::try_from_cli("nope", "nope").unwrap(); -// assert_eq!(&p.assign(kv).unwrap_err().to_string(), "nope: unknown key"); -// } -// -// #[test] -// fn test_instructions_to_xml() { -// let i = SectionConfig { -// title: Some("foo".to_owned()), -// description: Some("bar".to_owned()), -// position: 0, -// items: vec![ -// "foo".to_owned(), -// "bar bar".to_owned(), -// "baz]]> baz".to_owned(), -// ], -// examples: vec![ -// ExampleConfig::Generic("foo".to_owned()), -// ExampleConfig::Contrast(ContrastConfig { -// good: "bar".to_owned(), -// bad: "baz".to_owned(), -// reason: Some("qux".to_owned()), -// }), -// ExampleConfig::Contrast(ContrastConfig { -// good: "quux".to_owned(), -// bad: "quuz".to_owned(), -// reason: None, -// }), -// ], -// }; -// -// let xml = i.try_to_xml().unwrap(); -// insta::assert_snapshot!(xml); -// } -// } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_instructions_assign() { + let mut p = PartialSectionConfig::default(); + + let kv = KvAssignment::try_from_cli("tag", "foo").unwrap(); + p.assign(kv).unwrap(); + assert_eq!(p.tag, Some("foo".into())); + + let kv = KvAssignment::try_from_cli("content", "bar").unwrap(); + p.assign(kv).unwrap(); + assert_eq!(p.content, Some("bar".into())); + + let kv = KvAssignment::try_from_cli("position", "1").unwrap(); + p.assign(kv).unwrap(); + assert_eq!(p.position, Some(1)); + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_nostream__conversation_stream.snap index b1d1e109..0d845359 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_stream__conversation_stream.snap index c9a2d9e8..415e8cdf 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_multi_turn_conversation__conversation_stream.snap index 9a361a3b..68731550 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_redacted_thinking__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_redacted_thinking__conversation_stream.snap index cd3f2809..adf958ae 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_redacted_thinking__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_redacted_thinking__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_request_chaining__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_request_chaining__conversation_stream.snap index 08ceb5e0..821e53a6 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_request_chaining__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_request_chaining__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_auto__conversation_stream.snap index 872cdf28..d2009d9b 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_function__conversation_stream.snap index cbce2dc4..1196fd56 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_nostream__conversation_stream.snap index 0e8c02a8..9b2cf936 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_reasoning__conversation_stream.snap index 494b1a24..5789e6d6 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_no_reasoning__conversation_stream.snap index 7494c43d..47a23cc8 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_reasoning__conversation_stream.snap index 7102d098..2f514019 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_stream__conversation_stream.snap index 64c7eff2..34e14e59 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_strict__conversation_stream.snap index 581bdacb..379eb4a8 100644 --- a/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/anthropic/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_chat_completion_nostream__conversation_stream.snap index 4a0a5d9c..7708dc7a 100644 --- a/crates/jp_llm/tests/fixtures/google/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_chat_completion_stream__conversation_stream.snap index 06218366..ff933b48 100644 --- a/crates/jp_llm/tests/fixtures/google/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_gemini_3_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_gemini_3_reasoning__conversation_stream.snap index 0240326b..fe7cc8b7 100644 --- a/crates/jp_llm/tests/fixtures/google/test_gemini_3_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_gemini_3_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_multi_turn_conversation__conversation_stream.snap index 057ae59a..4d24881f 100644 --- a/crates/jp_llm/tests/fixtures/google/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_auto__conversation_stream.snap index 070c2307..4d2511e0 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_function__conversation_stream.snap index 02fe33f7..d7084219 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_nostream__conversation_stream.snap index 22276aaa..2e49b5ca 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_reasoning__conversation_stream.snap index 7be96491..442017ea 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_required_no_reasoning__conversation_stream.snap index d96b7620..fd347fe3 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_required_reasoning__conversation_stream.snap index 67db9e9e..09f1c0e4 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_stream__conversation_stream.snap index 946da56f..7c4af99c 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/google/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/google/test_tool_call_strict__conversation_stream.snap index b70419f4..0ed58581 100644 --- a/crates/jp_llm/tests/fixtures/google/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/google/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_nostream__conversation_stream.snap index ee0b1c3a..184979a3 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_stream__conversation_stream.snap index 0defdd58..ff28456a 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_multi_turn_conversation__conversation_stream.snap index 585700a7..ce9dc0fe 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_auto__conversation_stream.snap index ea58f446..7607c727 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_function__conversation_stream.snap index 7881944e..ceaf048f 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_nostream__conversation_stream.snap index 97e96ea6..6a6c234f 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_reasoning__conversation_stream.snap index a13afe80..a3bbf62f 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_no_reasoning__conversation_stream.snap index 5fbf610c..99da54ca 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_reasoning__conversation_stream.snap index 8235d830..ce47893a 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_stream__conversation_stream.snap index 46cb1418..ddc5a90c 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_strict__conversation_stream.snap index 754dd049..fc16b259 100644 --- a/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/llamacpp/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_nostream__conversation_stream.snap index 4c84f37d..89cdf023 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_stream__conversation_stream.snap index 4c84f37d..89cdf023 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_multi_turn_conversation__conversation_stream.snap index d80df640..57a765bb 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_auto__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_function__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_nostream__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_reasoning__conversation_stream.snap index 566f88c4..f9ebe99b 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_no_reasoning__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_reasoning__conversation_stream.snap index 566f88c4..f9ebe99b 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_stream__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_strict__conversation_stream.snap index 6c27951a..1ff2f2c2 100644 --- a/crates/jp_llm/tests/fixtures/ollama/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/ollama/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_chat_completion_nostream__conversation_stream.snap index a884a265..eb76fe91 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_chat_completion_stream__conversation_stream.snap index bf20f3a2..1696762b 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_multi_turn_conversation__conversation_stream.snap index 1c4dcab7..7a8e0250 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_auto__conversation_stream.snap index dced2dd4..45a66d94 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_function__conversation_stream.snap index 99e6c06b..cb53ceef 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_nostream__conversation_stream.snap index 5676a6a4..e85f41dc 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_reasoning__conversation_stream.snap index 483d0b00..d6d145cc 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_no_reasoning__conversation_stream.snap index bed820fb..e28d4c40 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_reasoning__conversation_stream.snap index a7cd2e5e..0587913f 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_stream__conversation_stream.snap index 9ba648fd..19d9d123 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openai/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openai/test_tool_call_strict__conversation_stream.snap index be231370..c3b8b397 100644 --- a/crates/jp_llm/tests/fixtures/openai/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openai/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/anthropic_test_sub_provider_event_metadata__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/anthropic_test_sub_provider_event_metadata__conversation_stream.snap index 2b767f8c..b7d449d6 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/anthropic_test_sub_provider_event_metadata__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/anthropic_test_sub_provider_event_metadata__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/google_test_sub_provider_event_metadata__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/google_test_sub_provider_event_metadata__conversation_stream.snap index 76372a55..12fb4c66 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/google_test_sub_provider_event_metadata__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/google_test_sub_provider_event_metadata__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/minimax_test_sub_provider_event_metadata__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/minimax_test_sub_provider_event_metadata__conversation_stream.snap index 61157055..cd61023f 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/minimax_test_sub_provider_event_metadata__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/minimax_test_sub_provider_event_metadata__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_nostream__conversation_stream.snap index 49838959..25af08e6 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_stream__conversation_stream.snap index 22a0b427..09d050ce 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_chat_completion_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_multi_turn_conversation__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_multi_turn_conversation__conversation_stream.snap index c2620bbb..ab8292a9 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_multi_turn_conversation__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_multi_turn_conversation__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_auto__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_auto__conversation_stream.snap index 6d0d6e94..7a817cc3 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_auto__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_auto__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_function__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_function__conversation_stream.snap index e2a3b4ec..93f3491c 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_function__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_function__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_nostream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_nostream__conversation_stream.snap index 540fcb0d..49a08080 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_nostream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_nostream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_reasoning__conversation_stream.snap index c014857d..7cc8aaf6 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_no_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_no_reasoning__conversation_stream.snap index e1791bc8..53c5add1 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_no_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_no_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_reasoning__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_reasoning__conversation_stream.snap index dc616c6f..d2478ccf 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_reasoning__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_required_reasoning__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_stream__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_stream__conversation_stream.snap index ec5f339c..56813a6d 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_stream__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_stream__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_strict__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_strict__conversation_stream.snap index a05336d3..021c3d90 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_strict__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/test_tool_call_strict__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_llm/tests/fixtures/openrouter/x-ai_test_sub_provider_event_metadata__conversation_stream.snap b/crates/jp_llm/tests/fixtures/openrouter/x-ai_test_sub_provider_event_metadata__conversation_stream.snap index 20f2164e..4406d837 100644 --- a/crates/jp_llm/tests/fixtures/openrouter/x-ai_test_sub_provider_event_metadata__conversation_stream.snap +++ b/crates/jp_llm/tests/fixtures/openrouter/x-ai_test_sub_provider_event_metadata__conversation_stream.snap @@ -86,7 +86,8 @@ expression: v "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", From 2be969f4ccee6513934ee5b9188c93eddaf87f37 Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Tue, 3 Feb 2026 13:56:20 +0100 Subject: [PATCH 3/3] fixup! feat(config): add system prompt sections and DeepSeek `base_url` Signed-off-by: Jean Mertz --- ...__tests__conversation_stream_serialization_roundtrip-2.snap | 3 ++- ...am__tests__conversation_stream_serialization_roundtrip.snap | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip-2.snap b/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip-2.snap index 1a47e3d5..c6f59c94 100644 --- a/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip-2.snap +++ b/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip-2.snap @@ -85,7 +85,8 @@ expression: "&stream" "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "", diff --git a/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip.snap b/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip.snap index 51c46b3e..a65360e6 100644 --- a/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip.snap +++ b/crates/jp_conversation/src/snapshots/jp_conversation__stream__tests__conversation_stream_serialization_roundtrip.snap @@ -85,7 +85,8 @@ expression: "&stream" "beta_headers": [] }, "deepseek": { - "api_key_env": "" + "api_key_env": "", + "base_url": "" }, "google": { "api_key_env": "",