Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src-tauri/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ async fn post_process_transcription(settings: &AppSettings, transcription: &str)
.cloned()
.unwrap_or_default();

// Disable reasoning for providers where post-processing rarely benefits from it.
// - custom: top-level reasoning_effort (works for local OpenAI-compat servers)
// - openrouter: nested reasoning object; exclude:true also keeps reasoning text
// out of the response so it can't pollute structured-output JSON parsing
let (reasoning_effort, reasoning) = match provider.id.as_str() {
"custom" => (Some("none".to_string()), None),
"openrouter" => (
None,
Some(crate::llm_client::ReasoningConfig {
effort: Some("none".to_string()),
exclude: Some(true),
}),
),
_ => (None, None),
};

if provider.supports_structured_output {
debug!("Using structured outputs for provider '{}'", provider.id);

Expand Down Expand Up @@ -195,6 +211,8 @@ async fn post_process_transcription(settings: &AppSettings, transcription: &str)
user_content,
Some(system_prompt),
Some(json_schema),
reasoning_effort.clone(),
reasoning.clone(),
)
.await
{
Expand Down Expand Up @@ -244,8 +262,15 @@ async fn post_process_transcription(settings: &AppSettings, transcription: &str)
let processed_prompt = prompt.replace("${output}", transcription);
debug!("Processed prompt length: {} chars", processed_prompt.len());

match crate::llm_client::send_chat_completion(&provider, api_key, &model, processed_prompt)
.await
match crate::llm_client::send_chat_completion(
&provider,
api_key,
&model,
processed_prompt,
reasoning_effort,
reasoning,
)
.await
{
Ok(Some(content)) => {
let content = strip_invisible_chars(&content);
Expand Down
32 changes: 31 additions & 1 deletion src-tauri/src/llm_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,24 @@ struct ResponseFormat {
json_schema: JsonSchema,
}

#[derive(Debug, Serialize, Clone, Default)]
pub struct ReasoningConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub effort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude: Option<bool>,
}

#[derive(Debug, Serialize)]
struct ChatCompletionRequest {
model: String,
messages: Vec<ChatMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
reasoning_effort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
reasoning: Option<ReasoningConfig>,
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -101,20 +113,36 @@ pub async fn send_chat_completion(
api_key: String,
model: &str,
prompt: String,
reasoning_effort: Option<String>,
reasoning: Option<ReasoningConfig>,
) -> Result<Option<String>, String> {
send_chat_completion_with_schema(provider, api_key, model, prompt, None, None).await
send_chat_completion_with_schema(
provider,
api_key,
model,
prompt,
None,
None,
reasoning_effort,
reasoning,
)
.await
}

/// Send a chat completion request with structured output support
/// When json_schema is provided, uses structured outputs mode
/// system_prompt is used as the system message when provided
/// reasoning_effort sets the OpenAI-style top-level field (e.g., "none", "low", "medium", "high")
/// reasoning sets the OpenRouter-style nested object (effort + exclude)
pub async fn send_chat_completion_with_schema(
provider: &PostProcessProvider,
api_key: String,
model: &str,
user_content: String,
system_prompt: Option<String>,
json_schema: Option<Value>,
reasoning_effort: Option<String>,
reasoning: Option<ReasoningConfig>,
) -> Result<Option<String>, String> {
let base_url = provider.base_url.trim_end_matches('/');
let url = format!("{}/chat/completions", base_url);
Expand Down Expand Up @@ -154,6 +182,8 @@ pub async fn send_chat_completion_with_schema(
model: model.to_string(),
messages,
response_format,
reasoning_effort,
reasoning,
};

let response = client
Expand Down
Loading