girb-gemini 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f31f46cab9413d0ab42e04ab660d422d2c2eab6aa3c24c73bd0cb92302ddee7b
4
- data.tar.gz: 96d2cf77aec44f0d0e3e7ed58974be763f5f0fd84b5ffeeadf02b1fe501825ae
3
+ metadata.gz: d9692895112892bc745f025b77c01ae731530a0b4a09a7f83ab3105555f6b9a2
4
+ data.tar.gz: 66d577249cfb2074b3cbf08bd964c5e3f057e1d913e87d7d2cab06eee9093bde
5
5
  SHA512:
6
- metadata.gz: e732d81d5c50e8e9d816c4980c0dd6ae88e050f08903bbae28a05b55a86a31c2a829893adfa8c2ddbb9a54be75b561aade611bf8ec81365de0b7483583055385
7
- data.tar.gz: f9e9a2ce202940ed7085e4c3e3ee82cf73345233e29afb45006b4adcf2e1520d9b2c09a67fae97776c5fbd87316b5963da5b96da3e3820a5acc7e16fdbcabf82
6
+ metadata.gz: 18df72d065bca04af22802ef2407d05143489eecafd636ce7c7aa3195ea8931e2e2b11c18128a2aef5bdeba7a48c254e1c9c08f7527e5302a1dd8aa0b90bfde2
7
+ data.tar.gz: '06828a39f38e9a78276cdcc2ccc8f539f06c6747c9d17d79d0790626c1b97d12152b62ef8ba7d450c8eaa1aa7c61ed04c9ed117c4f5d3c1b8fcc05ba37bb1e62'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.1] - 2026-02-12
4
+
5
+ ### Fixed
6
+
7
+ - Detect abnormal finish reasons (e.g., `MALFORMED_FUNCTION_CALL`) from Gemini API
8
+ - Set `response.error` with the finish reason and `response.text` with the LLM's failed output
9
+ - Enables girb to feed the error back to the LLM for self-correction
10
+
3
11
  ## [0.3.0] - 2026-02-12
4
12
 
5
13
  ### Added
@@ -108,14 +108,28 @@ module Girb
108
108
  def parse_response(response)
109
109
  return Response.new(error: "No response") unless response
110
110
 
111
- text = response.text
112
- function_calls = parse_function_calls(response)
113
-
114
111
  if response.respond_to?(:error) && response.error
115
- Response.new(error: response.error, raw_response: response)
116
- else
117
- Response.new(text: text, function_calls: function_calls, raw_response: response)
112
+ return Response.new(error: response.error, raw_response: response)
118
113
  end
114
+
115
+ # Check for abnormal finish reasons (e.g., MALFORMED_FUNCTION_CALL)
116
+ finish_reason, finish_message = extract_finish_info(response)
117
+ if finish_reason && finish_reason != "STOP" && finish_reason != "MAX_TOKENS"
118
+ # Try to recover function calls from malformed text
119
+ recovered = recover_function_calls(finish_message)
120
+ if recovered.any?
121
+ return Response.new(text: "", function_calls: recovered, raw_response: response)
122
+ end
123
+ return Response.new(
124
+ error: "#{finish_reason}: #{finish_message}",
125
+ text: finish_message || "",
126
+ raw_response: response
127
+ )
128
+ end
129
+
130
+ text = response.text
131
+ function_calls = parse_function_calls(response)
132
+ Response.new(text: text, function_calls: function_calls, raw_response: response)
119
133
  end
120
134
 
121
135
  def parse_function_calls(response)
@@ -136,6 +150,33 @@ module Girb
136
150
  end
137
151
  end
138
152
 
153
+ # Try to parse function calls from MALFORMED_FUNCTION_CALL text
154
+ # e.g., "print(default_api.evaluate_code(code='''...'''))"
155
+ def recover_function_calls(text)
156
+ return [] unless text
157
+
158
+ calls = []
159
+ # Match: tool_name(arg='''...''') or tool_name(arg="""...""") or tool_name(arg='...') or tool_name(arg="...")
160
+ text.scan(/\.(\w+)\((\w+)=('{3}|"{3}|'|")(.*?)\3\)/m) do |name, arg, _quote, value|
161
+ calls << { name: name, args: { arg => value } }
162
+ end
163
+ calls
164
+ rescue
165
+ []
166
+ end
167
+
168
+ def extract_finish_info(response)
169
+ return nil unless response.respond_to?(:candidates)
170
+
171
+ candidates = response.candidates
172
+ return nil unless candidates.is_a?(Array) && candidates.first
173
+
174
+ candidate = candidates.first
175
+ [candidate["finishReason"], candidate["finishMessage"]]
176
+ rescue
177
+ nil
178
+ end
179
+
139
180
  def extract_thought_signature(response)
140
181
  return nil unless response.respond_to?(:first_thought_signature)
141
182
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GirbGemini
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: girb-gemini
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rira100000000