Handy/src-tauri/src/apple_intelligence.rs
Chirag Agggarwal 0cb8ab2162
feat: implement structured outputs for post-processing providers (#706)
* 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>
2026-02-17 12:14:00 +08:00

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);
}
}