///|
/// Lux IR explicit JSON deserialization
///
/// All `from_json()` inputs match outputs of `to_json()` in serialize.mbt.

///|
/// === Helpers ===
fn get_str(m : Map[String, Json], key : String) -> Result[String, String] {
  match m.get(key) {
    Some(String(s)) => Ok(s)
    Some(_) => Err("expected string for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_str_opt(m : Map[String, Json], key : String) -> String? {
  match m.get(key) {
    Some(String(s)) => Some(s)
    _ => None
  }
}

///|
fn get_int(m : Map[String, Json], key : String) -> Result[Int, String] {
  match m.get(key) {
    Some(Number(n, ..)) => Ok(n.to_int())
    Some(_) => Err("expected number for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_int_opt(m : Map[String, Json], key : String) -> Int? {
  match m.get(key) {
    Some(Number(n, ..)) => Some(n.to_int())
    _ => None
  }
}

///|
fn get_double_opt(m : Map[String, Json], key : String) -> Double? {
  match m.get(key) {
    Some(Number(n, ..)) => Some(n)
    _ => None
  }
}

///|
fn get_bool(m : Map[String, Json], key : String) -> Result[Bool, String] {
  match m.get(key) {
    Some(True) => Ok(true)
    Some(False) => Ok(false)
    Some(_) => Err("expected boolean for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_support_level(
  m : Map[String, Json],
  key : String,
) -> Result[SupportLevel, String] {
  match m.get(key) {
    Some(v) =>
      match SupportLevel::from_json(v) {
        Ok(l) => Ok(l)
        Err(_) => Err("invalid support level for " + key)
      }
    None => Err("missing required field " + key)
  }
}

///|
fn get_bool_opt(m : Map[String, Json], key : String) -> Bool? {
  match m.get(key) {
    Some(True) => Some(true)
    Some(False) => Some(false)
    _ => None
  }
}

///|
fn get_str_arr_opt(m : Map[String, Json], key : String) -> Array[String]? {
  match m.get(key) {
    Some(Array(items)) => {
      let result : Array[String] = []
      let mut i = 0
      while i < items.length() {
        match items[i] {
          String(s) => result.push(s)
          _ => return None
        }
        i = i + 1
      }
      Some(result)
    }
    _ => None
  }
}

///|
fn parse_content_arr(
  items : Array[Json],
) -> Result[Array[LucentContent], String] {
  let result : Array[LucentContent] = []
  let mut i = 0
  while i < items.length() {
    match LucentContent::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_content_opt(items : Array[Json]) -> Array[LucentContent]? {
  match parse_content_arr(items) {
    Ok(v) => Some(v)
    Err(_) => None
  }
}

///|
fn parse_tool_arr(items : Array[Json]) -> Result[Array[LucentTool], String] {
  let result : Array[LucentTool] = []
  let mut i = 0
  while i < items.length() {
    match LucentTool::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_item_arr(
  items : Array[Json],
) -> Result[Array[LucentConversationItem], String] {
  let result : Array[LucentConversationItem] = []
  let mut i = 0
  while i < items.length() {
    match LucentConversationItem::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_choice_arr(items : Array[Json]) -> Result[Array[LucentChoice], String] {
  let result : Array[LucentChoice] = []
  let mut i = 0
  while i < items.length() {
    match LucentChoice::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_annotation_arr(
  items : Array[Json],
) -> Result[Array[LucentAnnotation], String] {
  let result : Array[LucentAnnotation] = []
  let mut i = 0
  while i < items.length() {
    match LucentAnnotation::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_provider_arr(
  items : Array[Json],
) -> Result[Array[ProviderCapability], String] {
  let result : Array[ProviderCapability] = []
  let mut i = 0
  while i < items.length() {
    match ProviderCapability::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_modality_arr(
  items : Array[Json],
) -> Result[Array[LucentModality], String] {
  let result : Array[LucentModality] = []
  let mut i = 0
  while i < items.length() {
    match LucentModality::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn parse_safety_arr(
  items : Array[Json],
) -> Result[Array[LucentSafetyRating], String] {
  let result : Array[LucentSafetyRating] = []
  let mut i = 0
  while i < items.length() {
    match LucentSafetyRating::from_json(items[i]) {
      Ok(v) => result.push(v)
      Err(e) => return Err(e)
    }
    i = i + 1
  }
  Ok(result)
}

///|
fn get_arr_content(
  m : Map[String, Json],
  key : String,
) -> Result[Array[LucentContent], String] {
  match m.get(key) {
    Some(Array(items)) => parse_content_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_arr_content_opt(
  m : Map[String, Json],
  key : String,
) -> Array[LucentContent]? {
  match m.get(key) {
    Some(Array(items)) => parse_content_opt(items)
    _ => None
  }
}

///|
fn get_arr_item(
  m : Map[String, Json],
  key : String,
) -> Result[Array[LucentConversationItem], String] {
  match m.get(key) {
    Some(Array(items)) => parse_item_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_arr_choice(
  m : Map[String, Json],
  key : String,
) -> Result[Array[LucentChoice], String] {
  match m.get(key) {
    Some(Array(items)) => parse_choice_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_arr_annotation(
  m : Map[String, Json],
  key : String,
) -> Result[Array[LucentAnnotation], String] {
  match m.get(key) {
    Some(Array(items)) => parse_annotation_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_arr_provider(
  m : Map[String, Json],
  key : String,
) -> Result[Array[ProviderCapability], String] {
  match m.get(key) {
    Some(Array(items)) => parse_provider_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
fn get_arr_modality(
  m : Map[String, Json],
  key : String,
) -> Result[Array[LucentModality], String] {
  match m.get(key) {
    Some(Array(items)) => parse_modality_arr(items)
    Some(_) => Err("expected array for " + key)
    None => Err("missing required field " + key)
  }
}

///|
/// @deprecated 保留未用
// fn get_arr_safety(
//   m : Map[String, Json],
//   key : String,
// ) -> Result[Array[LucentSafetyRating], String] {
//   match m.get(key) {
//     Some(Array(items)) => parse_safety_arr(items)
//     Some(_) => Err("expected array for " + key)
//     None => Err("missing required field " + key)
//   }
// }

///|
/// === LucentModality ===
pub fn LucentModality::from_json(jv : Json) -> Result[LucentModality, String] {
  match jv {
    String(s) =>
      match LucentModality::from_string(s) {
        Some(m) => Ok(m)
        None => Ok(Native(s))
      }
    _ => Err("expected string for LucentModality")
  }
}

///|
/// === SupportLevel ===
pub fn SupportLevel::from_json(jv : Json) -> Result[SupportLevel, String] {
  match jv {
    String(s) =>
      match SupportLevel::from_string(s) {
        Some(l) => Ok(l)
        None => Err("unknown support level: " + s)
      }
    _ => Err("expected string for support level")
  }
}

///|
/// === LucentMediaSource ===
pub fn LucentMediaSource::from_json(
  jv : Json,
) -> Result[LucentMediaSource, String] {
  match jv {
    Object(m) => {
      let typ = match get_str(m, "type") {
        Ok(t) => t
        Err(e) => return Err(e)
      }
      let data : String = match get_str_opt(m, "data") {
        Some(d) => d
        None => ""
      }
      match typ {
        "inline" => Ok(Inline(data))
        "url" => Ok(Url(data))
        "file_uri" => Ok(FileUri(data))
        _ => Err("unknown media source type: " + typ)
      }
    }
    _ => Err("expected object for LucentMediaSource")
  }
}

///|
/// === LucentMultimedia ===
pub fn LucentMultimedia::from_json(
  jv : Json,
) -> Result[LucentMultimedia, String] {
  match jv {
    Object(m) => {
      let media_type = match get_str(m, "media_type") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let source = match m.get("source") {
        Some(src) =>
          match LucentMediaSource::from_json(src) {
            Ok(s) => s
            Err(e) => return Err(e)
          }
        None => return Err("missing source")
      }
      Ok({ media_type, source })
    }
    _ => Err("expected object for LucentMultimedia")
  }
}

///|
/// === LucentAnnotationKind ===
pub fn LucentAnnotationKind::from_json(
  jv : Json,
) -> Result[LucentAnnotationKind, String] {
  match jv {
    String(s) =>
      match s {
        "url" => Ok(Url)
        "file_citation" => Ok(FileCitation)
        "web_search_citation" => Ok(WebSearchCitation)
        _ => Ok(Native(s))
      }
    _ => Err("expected string for LucentAnnotationKind")
  }
}

///|
/// === LucentAnnotation ===
pub fn LucentAnnotation::from_json(
  jv : Json,
) -> Result[LucentAnnotation, String] {
  match jv {
    Object(m) => {
      let km = match m.get("kind") {
        Some(k) =>
          match LucentAnnotationKind::from_json(k) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing kind")
      }
      let text = get_str_opt(m, "text")
      let reference = get_str_opt(m, "ref")
      let start = get_int_opt(m, "start")
      let end_ = get_int_opt(m, "end")
      Ok({ kind: km, text, reference, start, end: end_ })
    }
    _ => Err("expected object for LucentAnnotation")
  }
}

///|
/// === LucentThinking ===
pub fn LucentThinking::from_json(jv : Json) -> Result[LucentThinking, String] {
  match jv {
    Object(m) => {
      let text = match get_str(m, "text") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let signature = get_str_opt(m, "signature")
      let redacted = match get_bool(m, "redacted") {
        Ok(b) => b
        Err(e) => return Err(e)
      }
      let summary_opt : Array[LucentContent]? = get_arr_content_opt(
        m, "summary",
      )
      Ok({ text, signature, redacted, summary: summary_opt })
    }
    _ => Err("expected object for LucentThinking")
  }
}

///|
/// === LucentToolUse ===
pub fn LucentToolUse::from_json(jv : Json) -> Result[LucentToolUse, String] {
  match jv {
    Object(m) => {
      let id = match get_str(m, "id") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let name = match get_str(m, "name") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let arguments_json = match get_str(m, "arguments") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      Ok({ id, name, arguments_json, arguments_value: None })
    }
    _ => Err("expected object for LucentToolUse")
  }
}

///|
/// === LucentToolResult ===
pub fn LucentToolResult::from_json(
  jv : Json,
) -> Result[LucentToolResult, String] {
  match jv {
    Object(m) => {
      let tool_use_id = match get_str(m, "tool_use_id") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let content = match get_arr_content(m, "content") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      let is_error = match get_bool(m, "is_error") {
        Ok(b) => b
        Err(e) => return Err(e)
      }
      Ok({ tool_use_id, content, is_error })
    }
    _ => Err("expected object for LucentToolResult")
  }
}

///|
/// === LucentAgentAction ===
pub fn LucentAgentAction::from_json(
  jv : Json,
) -> Result[LucentAgentAction, String] {
  match jv {
    Object(m) => {
      let kind = match get_str(m, "kind") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let id = match get_str(m, "id") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let call_id = get_str_opt(m, "call_id")
      let name = get_str_opt(m, "name")
      let arguments_json = get_str_opt(m, "arguments")
      let result = get_str_opt(m, "result")
      let provider_payload : Json? = match m.get("provider_payload") {
        Some(j) => Some(j)
        None => None
      }
      Ok({ kind, id, call_id, name, arguments_json, result, provider_payload })
    }
    _ => Err("expected object for LucentAgentAction")
  }
}

///|
/// === LucentContent ===
pub fn LucentContent::from_json(jv : Json) -> Result[LucentContent, String] {
  match jv {
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) =>
          match tp {
            "text" => {
              let text = match get_str(m, "text") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              let anns : Array[LucentAnnotation]? = match m.get("annotations") {
                Some(Array(items)) =>
                  match parse_annotation_arr(items) {
                    Ok(arr) => Some(arr)
                    Err(_) => None
                  }
                _ => None
              }
              Ok(Text(text, anns))
            }
            "tool_use" => {
              let tu = match m.get("tool_use") {
                Some(j) =>
                  match LucentToolUse::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing tool_use")
              }
              Ok(ToolUse(tu))
            }
            "tool_result" => {
              let tr = match m.get("tool_result") {
                Some(j) =>
                  match LucentToolResult::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing tool_result")
              }
              Ok(ToolResult(tr))
            }
            "thinking" => {
              let th = match m.get("thinking") {
                Some(j) =>
                  match LucentThinking::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing thinking")
              }
              Ok(Thinking(th))
            }
            "refusal" => {
              let text = match get_str(m, "refusal") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(Refusal(text))
            }
            "image" => {
              let mm = match m.get("image") {
                Some(j) =>
                  match LucentMultimedia::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing image")
              }
              Ok(Image(mm))
            }
            "audio" => {
              let mm = match m.get("audio") {
                Some(j) =>
                  match LucentMultimedia::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing audio")
              }
              Ok(Audio(mm))
            }
            "video" => {
              let mm = match m.get("video") {
                Some(j) =>
                  match LucentMultimedia::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing video")
              }
              Ok(Video(mm))
            }
            "file" => {
              let mm = match m.get("file") {
                Some(j) =>
                  match LucentMultimedia::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing file")
              }
              Ok(File(mm))
            }
            "native" => {
              let tag = match get_str(m, "native_tag") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              let payload : Json = match m.get("native_payload") {
                Some(j) => j
                None => Json::null()
              }
              Ok(Native(tag, payload))
            }
            _ => Err("unknown content type")
          }
        Some(_) => Err("expected string for type")
        None => Err("missing type in LucentContent")
      }
    _ => Err("expected object for LucentContent")
  }
}

///|
/// === LucentMessage ===
pub fn LucentMessage::from_json(jv : Json) -> Result[LucentMessage, String] {
  match jv {
    Object(m) => {
      let role = match m.get("role") {
        Some(r) =>
          match LucentRole::from_json(r) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing role")
      }
      let content = match get_arr_content(m, "content") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      let reasoning = match m.get("reasoning") {
        Some(r) =>
          match LucentThinking::from_json(r) {
            Ok(v) => Some(v)
            Err(e) => return Err(e)
          }
        None => None
      }
      Ok({ role, content, phase: get_str_opt(m, "phase"), reasoning })
    }
    _ => Err("expected object for LucentMessage")
  }
}

///|
/// === LucentConversationItem ===
pub fn LucentConversationItem::from_json(
  jv : Json,
) -> Result[LucentConversationItem, String] {
  match jv {
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) =>
          match tp {
            "message" => {
              let msg = match m.get("message") {
                Some(j) =>
                  match LucentMessage::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing message")
              }
              Ok(Message(msg))
            }
            "tool_call" => {
              let tu = match m.get("tool_call") {
                Some(j) =>
                  match LucentToolUse::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing tool_call")
              }
              Ok(ToolCall(tu))
            }
            "tool_result" => {
              let tr = match m.get("tool_result") {
                Some(j) =>
                  match LucentToolResult::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing tool_result")
              }
              Ok(ToolResult(tr))
            }
            "reasoning" => {
              let th = match m.get("reasoning") {
                Some(j) =>
                  match LucentThinking::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing reasoning")
              }
              Ok(Reasoning(th))
            }
            "agent_action" => {
              let aa = match m.get("agent_action") {
                Some(j) =>
                  match LucentAgentAction::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing agent_action")
              }
              Ok(AgentAction(aa))
            }
            _ => Err("unknown conversation item type")
          }
        Some(_) => Err("expected string for type")
        None => Err("missing type in LucentConversationItem")
      }
    _ => Err("expected object for LucentConversationItem")
  }
}

///|
/// === LucentTool ===
pub fn LucentTool::from_json(jv : Json) -> Result[LucentTool, String] {
  match jv {
    Object(m) => {
      let name = match get_str(m, "name") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let description = get_str_opt(m, "description")
      let parameters_json = match m.get("parameters") {
        Some(p) => p.stringify()
        None => return Err("missing parameters")
      }
      let strict = get_bool_opt(m, "strict")
      let kind = match get_str_opt(m, "kind") {
        Some("function") => Function
        Some("file_search") => FileSearch
        Some("web_search") => WebSearch
        Some("code_interpreter") => CodeInterpreter
        Some("computer_use") => ComputerUse
        Some("code_execution") => CodeExecution
        Some("shell") => Shell
        Some("apply_patch") => ApplyPatch
        Some("mcp") => MCP
        Some(s) => Native(s)
        _ => Function
      }
      Ok({ name, description, parameters_json, strict, kind })
    }
    _ => Err("expected object for LucentTool")
  }
}

///|
/// === LucentToolChoice ===
pub fn LucentToolChoice::from_json(
  jv : Json,
) -> Result[LucentToolChoice, String] {
  match jv {
    String(s) =>
      match s {
        "auto" => Ok(Auto)
        "none" => Ok(None)
        "required" => Ok(Required)
        _ => Err("unknown tool_choice")
      }
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) if tp == "specific_tool" =>
          match m.get("name") {
            Some(String(name)) => Ok(SpecificTool(name))
            Some(_) => Err("expected string for name")
            None => Err("missing name for specific_tool")
          }
        Some(_) => Err("expected string for type")
        None => Err("missing type in tool_choice object")
      }
    _ => Err("expected string or object for LucentToolChoice")
  }
}

///|
/// === LucentStructuredOutput ===
pub fn LucentStructuredOutput::from_json(
  jv : Json,
) -> Result[LucentStructuredOutput, String] {
  match jv {
    String(s) =>
      match s {
        "json_object" => Ok(JsonObject)
        "text" => Ok(Text)
        _ => Err("unknown structured_output")
      }
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) if tp == "json_schema" =>
          match m.get("schema") {
            Some(schema) => Ok(JsonSchema(schema.stringify()))
            None => Err("missing schema for json_schema")
          }
        Some(_) => Err("expected string for type")
        None => Err("missing type in structured_output object")
      }
    _ => Err("expected string or object for LucentStructuredOutput")
  }
}

///|
/// === LucentReasoningEffort ===
pub fn LucentReasoningEffort::from_json(
  jv : Json,
) -> Result[LucentReasoningEffort, String] {
  match jv {
    String(s) =>
      match s {
        "low" => Ok(Low)
        "medium" => Ok(Medium)
        "high" => Ok(High)
        "xhigh" => Ok(XHigh)
        _ => Err("unknown reasoning effort")
      }
    _ => Err("expected string for LucentReasoningEffort")
  }
}

///|
/// === LucentReasoningSummary ===
pub fn LucentReasoningSummary::from_json(
  jv : Json,
) -> Result[LucentReasoningSummary, String] {
  match jv {
    String(s) =>
      match s {
        "auto" => Ok(Auto)
        "concise" => Ok(Concise)
        "detailed" => Ok(Detailed)
        _ => Err("unknown reasoning summary")
      }
    _ => Err("expected string for LucentReasoningSummary")
  }
}

///|
/// === LucentOptions ===
pub fn LucentOptions::from_json(jv : Json) -> Result[LucentOptions, String] {
  match jv {
    Object(m) => {
      let temperature = get_double_opt(m, "temperature")
      let top_p = get_double_opt(m, "top_p")
      let top_k = get_int_opt(m, "top_k")
      let max_output_tokens = get_int_opt(m, "max_output_tokens")
      let stop = get_str_arr_opt(m, "stop")
      let stream = match get_bool(m, "stream") {
        Ok(b) => b
        Err(e) => return Err(e)
      }
      let candidate_count = get_int_opt(m, "candidate_count")
      let structured_output : LucentStructuredOutput? = match
        m.get("structured_output") {
        Some(so) =>
          match LucentStructuredOutput::from_json(so) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let store = get_bool_opt(m, "store")
      let extras = match m.get("extras") {
        Some(Object(entries)) => Some(entries)
        _ => None
      }
      Ok({
        temperature,
        top_p,
        top_k,
        max_output_tokens,
        stop,
        stream,
        candidate_count,
        structured_output,
        store,
        extras,
      })
    }
    _ => Err("expected object for LucentOptions")
  }
}

///|
/// === LucentReasoningConfig ===
pub fn LucentReasoningConfig::from_json(
  jv : Json,
) -> Result[LucentReasoningConfig, String] {
  match jv {
    Object(m) => {
      let enabled = match get_bool(m, "enabled") {
        Ok(b) => b
        Err(e) => return Err(e)
      }
      let budget_tokens = get_int_opt(m, "budget_tokens")
      let effort : LucentReasoningEffort? = match m.get("effort") {
        Some(e) =>
          match LucentReasoningEffort::from_json(e) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let summary : LucentReasoningSummary? = match m.get("summary") {
        Some(s) =>
          match LucentReasoningSummary::from_json(s) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      Ok({ enabled, budget_tokens, effort, summary })
    }
    _ => Err("expected object for LucentReasoningConfig")
  }
}

///|
/// === LucentCapabilities ===
pub fn LucentCapabilities::from_json(
  jv : Json,
) -> Result[LucentCapabilities, String] {
  match jv {
    Object(m) => {
      let tool_calling = match get_support_level(m, "tool_calling") {
        Ok(l) => l
        Err(e) => return Err(e)
      }
      let parallel_tool_calls = match
        get_support_level(m, "parallel_tool_calls") {
        Ok(l) => l
        Err(e) => return Err(e)
      }
      let reasoning = match get_support_level(m, "reasoning") {
        Ok(l) => l
        Err(e) => return Err(e)
      }
      let multimodal_input = match get_support_level(m, "multimodal_input") {
        Ok(l) => l
        Err(e) => return Err(e)
      }
      let structured_output = match get_support_level(m, "structured_output") {
        Ok(l) => l
        Err(e) => return Err(e)
      }
      let input_modalities = match get_arr_modality(m, "input_modalities") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      let output_modalities = match get_arr_modality(m, "output_modalities") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      Ok({
        tool_calling,
        parallel_tool_calls,
        reasoning,
        multimodal_input,
        structured_output,
        input_modalities,
        output_modalities,
      })
    }
    _ => Err("expected object for LucentCapabilities")
  }
}

///|
/// === LucentRequest ===
pub fn LucentRequest::from_json(jv : Json) -> Result[LucentRequest, String] {
  match jv {
    Object(m) => {
      let schema_version = match get_str(m, "schema_version") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let model = match get_str(m, "model") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let instructions : Array[LucentContent]? = match m.get("instructions") {
        Some(Array(items)) => parse_content_opt(items)
        _ => None
      }
      let conversation = match get_arr_item(m, "conversation") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      let tools : Array[LucentTool]? = match m.get("tools") {
        Some(Array(items)) =>
          match parse_tool_arr(items) {
            Ok(arr) => Some(arr)
            Err(_) => None
          }
        _ => None
      }
      let tool_choice : LucentToolChoice? = match m.get("tool_choice") {
        Some(tc) =>
          match LucentToolChoice::from_json(tc) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let options = match m.get("options") {
        Some(o) =>
          match LucentOptions::from_json(o) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing options")
      }
      let capabilities : LucentCapabilities? = match m.get("capabilities") {
        Some(c) =>
          match LucentCapabilities::from_json(c) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let reasoning : LucentReasoningConfig? = match m.get("reasoning") {
        Some(r) =>
          match LucentReasoningConfig::from_json(r) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let metadata : Map[String, String]? = match m.get("metadata") {
        Some(Object(map)) => {
          let result : Map[String, String] = Map([])
          for entry in map {
            match entry {
              (k, v) =>
                match v {
                  String(s) => result[k] = s
                  _ => ()
                }
            }
          }
          Some(result)
        }
        _ => None
      }
      let extra : Map[String, Json]? = match m.get("extra") {
        Some(Object(map)) => Some(map)
        _ => None
      }
      Ok({
        schema_version,
        model,
        instructions,
        conversation,
        tools,
        tool_choice,
        options,
        capabilities,
        reasoning,
        metadata,
        extra,
      })
    }
    _ => Err("expected object for LucentRequest")
  }
}

///|
/// === LucentUsage ===
pub fn LucentUsage::from_json(jv : Json) -> Result[LucentUsage, String] {
  match jv {
    Object(m) => {
      let prompt_tokens = match get_int(m, "prompt_tokens") {
        Ok(n) => n
        Err(e) => return Err(e)
      }
      let completion_tokens = match get_int(m, "completion_tokens") {
        Ok(n) => n
        Err(e) => return Err(e)
      }
      let total_tokens = match get_int(m, "total_tokens") {
        Ok(n) => n
        Err(e) => return Err(e)
      }
      let reasoning_tokens = get_int_opt(m, "reasoning_tokens")
      let cached_tokens = get_int_opt(m, "cached_tokens")
      let cache_creation_tokens = get_int_opt(m, "cache_creation_tokens")
      Ok({
        prompt_tokens,
        completion_tokens,
        total_tokens,
        reasoning_tokens,
        cached_tokens,
        cache_creation_tokens,
      })
    }
    _ => Err("expected object for LucentUsage")
  }
}

///|
/// === LucentFinishReason ===
pub fn LucentFinishReason::from_json(
  jv : Json,
) -> Result[LucentFinishReason, String] {
  match jv {
    String(s) => Ok(LucentFinishReason::from_string(s))
    _ => Err("expected string for LucentFinishReason")
  }
}

///|
/// === LucentSafetyRating ===
pub fn LucentSafetyRating::from_json(
  jv : Json,
) -> Result[LucentSafetyRating, String] {
  match jv {
    Object(m) => {
      let category = match get_str(m, "category") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let probability = match get_str(m, "probability") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      Ok({ category, probability })
    }
    _ => Err("expected object for LucentSafetyRating")
  }
}

///|
/// === LucentChoice ===
pub fn LucentChoice::from_json(jv : Json) -> Result[LucentChoice, String] {
  match jv {
    Object(m) => {
      let index = match get_int(m, "index") {
        Ok(n) => n
        Err(e) => return Err(e)
      }
      let message = match m.get("message") {
        Some(msg) =>
          match LucentMessage::from_json(msg) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing message")
      }
      let finish_reason = match m.get("finish_reason") {
        Some(fr) =>
          match LucentFinishReason::from_json(fr) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing finish_reason")
      }
      let safety_ratings : Array[LucentSafetyRating]? = match
        m.get("safety_ratings") {
        Some(Array(items)) =>
          match parse_safety_arr(items) {
            Ok(arr) => Some(arr)
            Err(_) => None
          }
        _ => None
      }
      Ok({ index, message, finish_reason, safety_ratings })
    }
    _ => Err("expected object for LucentChoice")
  }
}

///|
/// === LucentResponse ===
pub fn LucentResponse::from_json(jv : Json) -> Result[LucentResponse, String] {
  match jv {
    Object(m) => {
      let schema_version = match get_str(m, "schema_version") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let id = match get_str(m, "id") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let model = match get_str(m, "model") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let created_at = get_int_opt(m, "created_at")
      let choices = match get_arr_choice(m, "choices") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      let usage : LucentUsage? = match m.get("usage") {
        Some(u) =>
          match LucentUsage::from_json(u) {
            Ok(v) => Some(v)
            Err(_) => None
          }
        None => None
      }
      let provider_payload : Json? = match m.get("provider_payload") {
        Some(j) => Some(j)
        None => None
      }
      Ok({
        schema_version,
        id,
        model,
        created_at,
        choices,
        usage,
        provider_payload,
      })
    }
    _ => Err("expected object for LucentResponse")
  }
}

///|
/// === LucentErrorKind ===
pub fn LucentErrorKind::from_json(jv : Json) -> Result[LucentErrorKind, String] {
  match jv {
    String(s) => Ok(LucentErrorKind::from_string(s))
    _ => Err("expected string for LucentErrorKind")
  }
}

///|
/// === LucentError ===
pub fn LucentError::from_json(jv : Json) -> Result[LucentError, String] {
  match jv {
    Object(m) => {
      let kind = match m.get("kind") {
        Some(k) =>
          match LucentErrorKind::from_json(k) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing kind")
      }
      let message = match get_str(m, "message") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let provider_code = get_str_opt(m, "provider_code")
      Ok({ kind, message, provider_code })
    }
    _ => Err("expected object for LucentError")
  }
}

///|
/// === LucentConversationMeta ===
pub fn LucentConversationMeta::from_json(
  jv : Json,
) -> Result[LucentConversationMeta, String] {
  match jv {
    Object(m) => {
      let id = get_str_opt(m, "id")
      let model = get_str_opt(m, "model")
      let usage_input = get_int_opt(m, "usage_input")
      Ok({ id, model, usage_input })
    }
    _ => Err("expected object for LucentConversationMeta")
  }
}

///|
/// === LucentBlockType ===
pub fn LucentBlockType::from_json(jv : Json) -> Result[LucentBlockType, String] {
  match jv {
    String(s) =>
      match s {
        "text" => Ok(Text)
        "thinking" => Ok(Thinking)
        "refusal" => Ok(Refusal)
        "image" => Ok(Image)
        "audio" => Ok(Audio)
        "video" => Ok(Video)
        _ => Ok(Native(s))
      }
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) if tp == "tool_call" => {
          let id = match m.get("id") {
            Some(String(i)) => i
            Some(_) => return Err("expected string for id")
            None => return Err("missing id")
          }
          let name = match m.get("name") {
            Some(String(n)) => n
            Some(_) => return Err("expected string for name")
            None => return Err("missing name")
          }
          Ok(ToolCall(id, name))
        }
        Some(_) => Err("expected string for type")
        None => Err("missing type")
      }
    _ => Err("expected string or object for LucentBlockType")
  }
}

///|
/// === LucentBlockDelta ===
pub fn LucentBlockDelta::from_json(
  jv : Json,
) -> Result[LucentBlockDelta, String] {
  match jv {
    Object(m) =>
      match m.get("type") {
        Some(String(tp)) =>
          match tp {
            "text" => {
              let text = match get_str(m, "text") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(TextDelta(text))
            }
            "thinking" => {
              let text = match get_str(m, "text") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(ThinkingDelta(text))
            }
            "signature" => {
              let sig = match get_str(m, "signature") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(SignatureDelta(sig))
            }
            "tool_arguments" => {
              let args = match get_str(m, "arguments") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(ToolArgumentsDelta(args))
            }
            "refusal" => {
              let text = match get_str(m, "text") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              Ok(RefusalDelta(text))
            }
            "native" => {
              let tag = match get_str(m, "native_tag") {
                Ok(s) => s
                Err(e) => return Err(e)
              }
              let payload : Json = match m.get("native_payload") {
                Some(j) => j
                None => Json::null()
              }
              Ok(NativeDelta(tag, payload))
            }
            _ => Err("unknown block delta type")
          }
        Some(_) => Err("expected string for type")
        None => Err("missing type in LucentBlockDelta")
      }
    _ => Err("expected object for LucentBlockDelta")
  }
}

///|
/// === LucentStreamEvent ===
pub fn LucentStreamEvent::from_json(
  jv : Json,
) -> Result[LucentStreamEvent, String] {
  match jv {
    Object(m) =>
      match m.get("event") {
        Some(String(ev)) =>
          match ev {
            "conversation_start" => {
              let meta = match m.get("meta") {
                Some(j) =>
                  match LucentConversationMeta::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing meta")
              }
              Ok(ConversationStart(meta))
            }
            "item_start" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              let item = match m.get("item") {
                Some(j) =>
                  match LucentConversationItem::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing item")
              }
              Ok(ItemStart(idx, item))
            }
            "block_start" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              let bt = match m.get("block_type") {
                Some(j) =>
                  match LucentBlockType::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing block_type")
              }
              Ok(BlockStart(idx, bt))
            }
            "block_delta" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              let delta = match m.get("delta") {
                Some(j) =>
                  match LucentBlockDelta::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing delta")
              }
              Ok(BlockDelta(idx, delta))
            }
            "block_end" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              Ok(BlockEnd(idx))
            }
            "item_end" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              Ok(ItemEnd(idx))
            }
            "block_discard" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              Ok(BlockDiscard(idx))
            }
            "annotations" => {
              let idx = match get_int(m, "index") {
                Ok(n) => n
                Err(e) => return Err(e)
              }
              let annotations = match get_arr_annotation(m, "annotations") {
                Ok(v) => v
                Err(e) => return Err(e)
              }
              Ok(Annotations(idx, annotations))
            }
            "finish" => {
              let fr = match m.get("finish_reason") {
                Some(f) =>
                  match LucentFinishReason::from_json(f) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing finish_reason")
              }
              Ok(Finish(fr))
            }
            "usage" => {
              let usage = match m.get("usage") {
                Some(j) =>
                  match LucentUsage::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing usage")
              }
              Ok(Usage(usage))
            }
            "error" => {
              let err = match m.get("error") {
                Some(j) =>
                  match LucentError::from_json(j) {
                    Ok(v) => v
                    Err(e) => return Err(e)
                  }
                None => return Err("missing error")
              }
              Ok(Error(err))
            }
            "done" => Ok(Done)
            _ => Err("unknown stream event")
          }
        Some(_) => Err("expected string for event")
        None => Err("missing event in LucentStreamEvent")
      }
    _ => Err("expected object for LucentStreamEvent")
  }
}

///|
/// === ProviderCapability ===
pub fn ProviderCapability::from_json(
  jv : Json,
) -> Result[ProviderCapability, String] {
  match jv {
    Object(m) => {
      let model_pattern = match get_str(m, "model_pattern") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let provider = match get_str(m, "provider") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let capabilities = match m.get("capabilities") {
        Some(j) =>
          match LucentCapabilities::from_json(j) {
            Ok(v) => v
            Err(e) => return Err(e)
          }
        None => return Err("missing capabilities")
      }
      let extra_params : Map[String, Json]? = match m.get("extra_params") {
        Some(Object(map)) => Some(map)
        _ => None
      }
      Ok({ model_pattern, provider, capabilities, extra_params })
    }
    _ => Err("expected object for ProviderCapability")
  }
}

///|
/// === LucentDiscovery ===
pub fn LucentDiscovery::from_json(jv : Json) -> Result[LucentDiscovery, String] {
  match jv {
    Object(m) => {
      let schema_version = match get_str(m, "schema_version") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let protocol_version = match get_str(m, "protocol_version") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let schema_url = match get_str(m, "schema_url") {
        Ok(s) => s
        Err(e) => return Err(e)
      }
      let providers = match get_arr_provider(m, "providers") {
        Ok(v) => v
        Err(e) => return Err(e)
      }
      Ok({ schema_version, protocol_version, schema_url, providers })
    }
    _ => Err("expected object for LucentDiscovery")
  }
}