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}