| 314 | #[instrument(skip(self, py), fields(prompt_len = prompt.len()))] |
| 315 | #[pyo3(signature = (prompt, max_tokens=None, temperature=None, enable_prompt_caching=false))] |
| 316 | fn complete( |
| 317 | &self, |
| 318 | prompt: String, |
| 319 | max_tokens: Option<i64>, |
| 320 | temperature: Option<f64>, |
| 321 | enable_prompt_caching: bool, |
| 322 | py: Python<'_>, |
| 323 | ) -> PyResult<String> { |
| 324 | // Validate input |
| 325 | if prompt.is_empty() { |
| 326 | return Err(validation_error( |
| 327 | "prompt", |
| 328 | Some(&prompt), |
| 329 | "Prompt cannot be empty", |
| 330 | )); |
| 331 | } |
| 332 | |
| 333 | // Validate max_tokens |
| 334 | let validated_max_tokens = if let Some(tokens) = max_tokens { |
| 335 | if tokens <= 0 { |
| 336 | return Err(validation_error( |
| 337 | "max_tokens", |
| 338 | Some(&tokens.to_string()), |
| 339 | "max_tokens must be greater than 0", |
| 340 | )); |
| 341 | } |
| 342 | if tokens > u32::MAX as i64 { |
| 343 | return Err(validation_error( |
| 344 | "max_tokens", |
| 345 | Some(&tokens.to_string()), |
| 346 | "max_tokens is too large", |
| 347 | )); |
| 348 | } |
| 349 | Some(tokens as u32) |
| 350 | } else { |
| 351 | None |
| 352 | }; |
| 353 | |
| 354 | // Validate temperature |
| 355 | let validated_temperature = if let Some(temp) = temperature { |
| 356 | if !(0.0..=2.0).contains(&temp) { |
| 357 | return Err(validation_error( |
| 358 | "temperature", |
| 359 | Some(&temp.to_string()), |
| 360 | "temperature must be between 0.0 and 2.0", |
| 361 | )); |
| 362 | } |
| 363 | Some(temp as f32) |
| 364 | } else { |
| 365 | None |
| 366 | }; |
| 367 | |
| 368 | let provider = Arc::clone(&self.provider); |
| 369 | let circuit_breaker = Arc::clone(&self.circuit_breaker); |
| 370 | let stats = Arc::clone(&self.stats); |
| 371 | let config = self.config.clone(); |
| 372 | |
| 373 | // CRITICAL FIX: Release GIL during async execution for true parallelism |