Class: SolidLoop::Dialects::Gemini

Inherits:
Object
  • Object
show all
Defined in:
app/services/solid_loop/dialects/gemini.rb

Instance Method Summary collapse

Instance Method Details

#apply_reasoning_strategies!(messages, strategies) ⇒ Object



28
29
30
31
32
33
34
35
36
37
# File 'app/services/solid_loop/dialects/gemini.rb', line 28

def apply_reasoning_strategies!(messages, strategies)
  return unless messages.is_a?(Array)

  # Gemini filter: only keep gemini-specific and universal XML strategies
  filtered = strategies.select { |s| [:gemini_signed, :xml, :xml!].include?(s) }

  messages.each do |msg|
    SolidLoop::Dialects::ReasoningPacker.pack(msg, filtered)
  end
end

#auth_headers(_api_token) ⇒ Object



10
11
12
# File 'app/services/solid_loop/dialects/gemini.rb', line 10

def auth_headers(_api_token)
  {}  # Gemini uses ?key= query param, not a header
end

#completion_url(base_url) ⇒ Object



6
7
8
# File 'app/services/solid_loop/dialects/gemini.rb', line 6

def completion_url(base_url)
  base_url.to_s.gsub(/\/+$/, "") + "/v1beta/models"
end

#extract_message_data(data) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'app/services/solid_loop/dialects/gemini.rb', line 39

def extract_message_data(data)
  candidate = data.dig("candidates", 0) || {}
  content_obj = candidate.dig("content") || {}
  parts = content_obj.dig("parts") || []

  text_part = parts.find { |p| p["text"].present? }
  thought_part = parts.find { |p| p["thought"].present? }
  tool_calls_part = parts.select { |p| p["functionCall"].present? }

  {
    content: text_part&.dig("text"),
    reasoning: thought_part&.dig("thought"),
    tool_calls: map_tool_calls_from_gemini(tool_calls_part),
    metadata: { thought_signature: candidate.dig("thoughtSignature") }.compact
  }
end

#normalize_response(data, fallback_text: "", fallback_messages_text: "") ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/services/solid_loop/dialects/gemini.rb', line 56

def normalize_response(data, fallback_text: "", fallback_messages_text: "")
  res = extract_message_data(data)

  usage_data =
    if data["usageMetadata"].is_a?(Hash)
      SolidLoop::LlmUsageParser::Gemini.call(data["usageMetadata"])
    else
      SolidLoop::LlmUsageParser.estimate(fallback_text, fallback_messages_text)
    end

  {
    content: res[:content],
    reasoning: res[:reasoning],
    tool_calls: res[:tool_calls],
    usage: usage_data,
    metadata: res[:metadata].merge({ "model" => data["modelVersion"].presence }.compact)
  }
end

#render_payload(payload) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'app/services/solid_loop/dialects/gemini.rb', line 14

def render_payload(payload)
  gemini_payload = {
    contents: payload[:messages].map { |m| map_message(m) },
    generationConfig: {
      temperature: payload[:temperature],
      maxOutputTokens: payload[:max_tokens]
    }.compact
  }
  if payload[:tools]&.any?
    gemini_payload[:tools] = [{ function_declarations: payload[:tools].map { |t| map_tool(t) } }]
  end
  gemini_payload
end