Module: LlmConductor::Eval::JsonParser

Defined in:
lib/llm_conductor/eval/json_parser.rb

Overview

Minimal, conservative JSON-from-LLM-text parser.

Replaces the app-level LlmJsonCleaner the Rails prototype relied on. The guiding principle (from docs/llm_eval_framework.md) is: NEVER “repair” already-valid JSON — heavy cleaning corrupts numeric scores and the like. We only strip markdown fences, drop any preamble before the first brace, trim to the outermost balanced object/array, then parse once.

Class Method Summary collapse

Class Method Details

.balance(str) ⇒ Object

Given a string that starts with ‘{’ or ‘[’, return the substring up to and including its matching close. String contents (and escapes) are skipped so braces inside string literals don’t throw off the depth.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/llm_conductor/eval/json_parser.rb', line 47

def balance(str)
  open = str[0]
  close = open == '{' ? '}' : ']'
  depth = 0
  in_string = false
  escape = false

  str.each_char.with_index do |char, index|
    if in_string
      if escape then escape = false
      elsif char == '\\' then escape = true
      elsif char == '"' then in_string = false
      end
      next
    end

    case char
    when '"' then in_string = true
    when open then depth += 1
    when close
      depth -= 1
      return str[0..index] if depth.zero?
    end
  end

  str
end

.parse(text) ⇒ Object

Parse text into a Hash or Array, or return nil on any failure.



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/llm_conductor/eval/json_parser.rb', line 18

def parse(text)
  prepared = prepare_text(text)
  return nil if prepared.empty?

  obj = begin
    JSON.parse(prepared)
  rescue JSON::ParserError
    nil
  end
  obj.is_a?(Hash) || obj.is_a?(Array) ? obj : nil
end

.prepare_text(text) ⇒ Object

Strip “‘json fences, drop preamble before the first [ or {, and trim to the matching closing brace/bracket. Returns ” when there is no JSON-looking content at all.



33
34
35
36
37
38
39
40
41
42
# File 'lib/llm_conductor/eval/json_parser.rb', line 33

def prepare_text(text)
  str = text.to_s.strip
            .gsub(/\A```(?:json)?\s*/i, '')
            .gsub(/```\s*\z/, '')
            .strip
  start = str.index(/[\[{]/)
  return '' if start.nil?

  balance(str[start..])
end