* feat: implement structured outputs for Cerebras, OpenRouter, OpenAI, and Apple Intelligence
- Add structured output support with JSON schema in llm_client.rs
- Update actions.rs to use system prompt + user content approach
- Remove legacy ${output} variable substitution for supported providers
- Update Apple Intelligence Swift code to accept system prompts
- All providers now use consistent structured output format
- Remove duplicate check_apple_intelligence_availability function
* wip changes
* fix(structured-outputs): address PR #706 review comments
- Add settings migration to sync supports_structured_output field for existing providers
- Fix fallback behavior: structured output failures now fall through to legacy mode
- Clone api_key to prevent ownership issues in fallback path
- Clean up build_system_prompt(): remove placeholder entirely
(instead of replacing with 'the user's message' which reads awkwardly)
- Add warn import from log crate
* refactor(structured-outputs): apply best practice improvements
- Optimize settings migration: use single match instead of double iteration
- Add TRANSCRIPTION_FIELD constant to replace magic strings
- Keep Apple Intelligence behavior unchanged (no API fallback for privacy)
Addresses code review feedback on PR #706:
1. More efficient provider lookup in ensure_post_process_defaults()
2. Eliminates hardcoded 'transcription' string in JSON parsing
3. Maintains privacy-first approach for Apple Intelligence
* fix groq output
---------
Co-authored-by: CJ Pais <cj@cjpais.com>
84 lines
2.4 KiB
Rust
84 lines
2.4 KiB
Rust
use std::ffi::{CStr, CString};
|
|
use std::os::raw::{c_char, c_int};
|
|
|
|
// Define the response structure from Swift
|
|
#[repr(C)]
|
|
pub struct AppleLLMResponse {
|
|
pub response: *mut c_char,
|
|
pub success: c_int,
|
|
pub error_message: *mut c_char,
|
|
}
|
|
|
|
// Link to the Swift functions
|
|
extern "C" {
|
|
pub fn is_apple_intelligence_available() -> c_int;
|
|
pub fn free_apple_llm_response(response: *mut AppleLLMResponse);
|
|
}
|
|
|
|
// Safe wrapper functions
|
|
pub fn check_apple_intelligence_availability() -> bool {
|
|
unsafe { is_apple_intelligence_available() == 1 }
|
|
}
|
|
|
|
// Link to the Swift function for system prompt support
|
|
extern "C" {
|
|
pub fn process_text_with_system_prompt_apple(
|
|
system_prompt: *const c_char,
|
|
user_content: *const c_char,
|
|
max_tokens: i32,
|
|
) -> *mut AppleLLMResponse;
|
|
}
|
|
|
|
/// Process text with Apple Intelligence using separate system prompt and user content
|
|
pub fn process_text_with_system_prompt(
|
|
system_prompt: &str,
|
|
user_content: &str,
|
|
max_tokens: i32,
|
|
) -> Result<String, String> {
|
|
let system_cstr = CString::new(system_prompt).map_err(|e| e.to_string())?;
|
|
let user_cstr = CString::new(user_content).map_err(|e| e.to_string())?;
|
|
|
|
let response_ptr = unsafe {
|
|
process_text_with_system_prompt_apple(system_cstr.as_ptr(), user_cstr.as_ptr(), max_tokens)
|
|
};
|
|
|
|
if response_ptr.is_null() {
|
|
return Err("Null response from Apple LLM".to_string());
|
|
}
|
|
|
|
let response = unsafe { &*response_ptr };
|
|
|
|
let result = if response.success == 1 {
|
|
if response.response.is_null() {
|
|
Ok(String::new())
|
|
} else {
|
|
let c_str = unsafe { CStr::from_ptr(response.response) };
|
|
let rust_str = c_str.to_string_lossy().into_owned();
|
|
Ok(rust_str)
|
|
}
|
|
} else {
|
|
let error_c_str = if !response.error_message.is_null() {
|
|
unsafe { CStr::from_ptr(response.error_message) }
|
|
} else {
|
|
CStr::from_bytes_with_nul(b"Unknown error\0").unwrap()
|
|
};
|
|
let error_msg = error_c_str.to_string_lossy().into_owned();
|
|
Err(error_msg)
|
|
};
|
|
|
|
// Clean up the response
|
|
unsafe { free_apple_llm_response(response_ptr) };
|
|
|
|
result
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_availability() {
|
|
let available = check_apple_intelligence_availability();
|
|
println!("Apple Intelligence available: {}", available);
|
|
}
|
|
}
|