gglib_core/ports/structured_llm.rs
1//! Error types for the structured-output subsystem.
2//!
3//! The implementation of `get_structured` lives in `gglib-agent`, where
4//! `futures-util` and stream-collection helpers are already available.
5//! This module holds only the shared error type so that callers in any crate
6//! can pattern-match on failure modes without depending on `gglib-agent`.
7//!
8//! # Variants
9//!
10//! | Variant | Meaning |
11//! |---------|---------|
12//! | [`StructuredOutputError::Stream`] | The LLM stream itself failed (network, timeout, etc.) |
13//! | [`StructuredOutputError::Parse`] | The collected text was not valid JSON or did not match the expected type |
14//! | [`StructuredOutputError::MaxRetriesExceeded`] | All retry attempts were exhausted without a successful parse |
15
16use thiserror::Error;
17
18/// Failure modes for a structured-output LLM call.
19///
20/// Produced by `gglib_agent::structured_output::get_structured` when the
21/// LLM cannot produce a valid JSON response within the allowed retry budget.
22#[derive(Debug, Error)]
23pub enum StructuredOutputError {
24 /// The underlying [`super::LlmCompletionPort::chat_stream`] call failed
25 /// before any response was collected.
26 #[error("LLM stream error: {0}")]
27 Stream(#[from] anyhow::Error),
28
29 /// The LLM produced output but it could not be parsed as valid JSON (or
30 /// as the expected type). Includes the raw text and the number of
31 /// attempts made so far.
32 #[error("JSON parse error after {attempts} attempt(s): {error}\nRaw output: {raw}")]
33 Parse {
34 /// The `serde_json` error message.
35 error: String,
36 /// The raw text that failed to parse.
37 raw: String,
38 /// How many parse attempts have been made (including this one).
39 attempts: u32,
40 },
41
42 /// All `max_retries + 1` attempts were exhausted without a successful
43 /// parse. The last parse error is included for diagnostics.
44 #[error("structured output failed after {max_retries} retries: {last_error}")]
45 MaxRetriesExceeded {
46 /// The retry limit that was set by the caller.
47 max_retries: u32,
48 /// The parse error from the final attempt.
49 last_error: String,
50 },
51}