geminize 1.2.0 → 1.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.
@@ -2,117 +2,126 @@
2
2
 
3
3
  module Geminize
4
4
  module Models
5
- # Extends ContentResponse with function calling response handling
5
+ # Extends ContentResponse with function calling capabilities
6
6
  class ContentResponse
7
- # Get the function call information from the response
8
- # @return [Geminize::Models::FunctionResponse, nil] Function call information or nil if not present
9
- def function_call
10
- return @function_call if defined?(@function_call)
11
-
12
- @function_call = nil
13
- candidates = @raw_response["candidates"]
14
-
15
- if candidates && !candidates.empty?
16
- content = candidates.first["content"]
17
- if content && content["parts"] && !content["parts"].empty?
18
- function_call_part = content["parts"].find { |part| part["functionCall"] || part["function_call"] }
19
-
20
- if function_call_part
21
- # Handle both "functionCall" and "function_call" formats (API may vary)
22
- function_data = function_call_part["functionCall"] || function_call_part["function_call"]
23
-
24
- if function_data
25
- # Extract name and args - handle different API response formats
26
- name = function_data["name"]
27
- args = function_data["args"] || function_data["arguments"]
28
-
29
- if name
30
- @function_call = FunctionResponse.new(name, args || {})
31
- end
32
- end
33
- end
34
- end
35
- end
7
+ # @return [Geminize::Models::FunctionResponse, nil] The function call in the response, if any
8
+ attr_reader :function_call
9
+
10
+ # @return [String, nil] The raw JSON response content, if this is a JSON response
11
+ attr_reader :json_response
12
+
13
+ # @return [Geminize::Models::CodeExecution::ExecutableCode, nil] The executable code in the response, if any
14
+ attr_reader :executable_code
36
15
 
37
- @function_call
16
+ # @return [Geminize::Models::CodeExecution::CodeExecutionResult, nil] The code execution result in the response, if any
17
+ attr_reader :code_execution_result
18
+
19
+ # Store the response data for extensions
20
+ alias_method :original_initialize, :initialize
21
+ def initialize(response_data)
22
+ @response_data = response_data
23
+ original_initialize(response_data)
24
+ parse_function_call
25
+ parse_json_response
26
+ parse_code_execution
38
27
  end
39
28
 
40
- # Check if the response contains a function call
41
- # @return [Boolean] True if the response contains a function call
29
+ # Determine if the response contains a function call
30
+ # @return [Boolean] true if the response contains a function call
42
31
  def has_function_call?
43
- !function_call.nil?
32
+ !@function_call.nil?
33
+ end
34
+
35
+ # Determine if the response contains a JSON response
36
+ # @return [Boolean] true if the response contains a JSON response
37
+ def has_json_response?
38
+ !@json_response.nil?
39
+ end
40
+
41
+ # Determine if the response contains executable code
42
+ # @return [Boolean] true if the response contains executable code
43
+ def has_executable_code?
44
+ !@executable_code.nil?
45
+ end
46
+
47
+ # Determine if the response contains a code execution result
48
+ # @return [Boolean] true if the response contains a code execution result
49
+ def has_code_execution_result?
50
+ !@code_execution_result.nil?
44
51
  end
45
52
 
46
- # Get structured JSON response if available
47
- # @return [Hash, nil] Parsed JSON response or nil if not a JSON response
48
- def json_response
49
- return @json_response if defined?(@json_response)
53
+ private
54
+
55
+ # Parse the function call from the response
56
+ def parse_function_call
57
+ candidates = @response_data.dig("candidates") || []
58
+ return if candidates.empty?
59
+
60
+ content = candidates[0].dig("content") || {}
61
+ parts = content.dig("parts") || []
62
+ function_call_part = parts.find { |part| part.dig("functionCall") }
63
+
64
+ if function_call_part
65
+ function_call_data = function_call_part["functionCall"]
66
+ function_name = function_call_data["name"]
67
+ function_args = function_call_data["args"] || {}
50
68
 
51
- @json_response = nil
69
+ @function_call = FunctionResponse.new(function_name, function_args)
70
+ end
71
+ end
52
72
 
53
- if has_text?
73
+ # Parse JSON response if available
74
+ def parse_json_response
75
+ # First try to check if it's explicitly returned as JSON
76
+ if @response_data.dig("candidates", 0, "content", "parts", 0, "text")
77
+ text = @response_data.dig("candidates", 0, "content", "parts", 0, "text")
54
78
  begin
55
79
  @json_response = JSON.parse(text)
80
+ return
56
81
  rescue JSON::ParserError
57
- # Try to parse any JSON-like content from the text
58
- json_match = text.match(/```json\s*(.*?)\s*```/m) || text.match(/\{.*\}/m) || text.match(/\[.*\]/m)
59
- if json_match
60
- begin
61
- @json_response = JSON.parse(json_match[1] || json_match[0])
62
- rescue JSON::ParserError
63
- # Still not valid JSON
64
- @json_response = nil
65
- end
66
- end
82
+ # Not valid JSON, continue checking other methods
67
83
  end
68
84
  end
69
85
 
70
- @json_response
71
- end
86
+ # Next check if it's returned in structured format
87
+ candidates = @response_data.dig("candidates") || []
88
+ return if candidates.empty?
72
89
 
73
- # Check if the response contains valid JSON
74
- # @return [Boolean] True if the response contains valid JSON
75
- def has_json_response?
76
- !json_response.nil?
90
+ content = candidates[0].dig("content") || {}
91
+ parts = content.dig("parts") || []
92
+ json_part = parts.find { |part| part.key?("structuredValue") }
93
+
94
+ if json_part && json_part["structuredValue"]
95
+ @json_response = json_part["structuredValue"]
96
+ end
77
97
  end
78
98
 
79
- # Enhanced parse_response method to handle function calls
80
- alias_method :original_parse_response, :parse_response
99
+ # Parse code execution data from the response
100
+ def parse_code_execution
101
+ candidates = @response_data.dig("candidates") || []
102
+ return if candidates.empty?
81
103
 
82
- private
104
+ content = candidates[0].dig("content") || {}
105
+ parts = content.dig("parts") || []
83
106
 
84
- # Parse the response data and extract relevant information
85
- def parse_response
86
- original_parse_response
87
- # Clear any cached function call before parsing again
88
- remove_instance_variable(:@function_call) if defined?(@function_call)
89
- parse_function_call
90
- end
107
+ # Find executable code
108
+ executable_code_part = parts.find { |part| part.dig("executableCode") }
109
+ if executable_code_part && executable_code_part["executableCode"]
110
+ code_data = executable_code_part["executableCode"]
111
+ language = code_data["language"] || "PYTHON"
112
+ code = code_data["code"] || ""
91
113
 
92
- # Parse function call information from the response
93
- def parse_function_call
94
- candidates = @raw_response["candidates"]
95
-
96
- if candidates && !candidates.empty?
97
- content = candidates.first["content"]
98
- if content && content["parts"] && !content["parts"].empty?
99
- function_call_part = content["parts"].find { |part| part["functionCall"] || part["function_call"] }
100
-
101
- if function_call_part
102
- # Handle both "functionCall" and "function_call" formats (API may vary)
103
- function_data = function_call_part["functionCall"] || function_call_part["function_call"]
104
-
105
- if function_data
106
- # Extract name and args - handle different API response formats
107
- name = function_data["name"]
108
- args = function_data["args"] || function_data["arguments"]
109
-
110
- if name
111
- @function_call = FunctionResponse.new(name, args || {})
112
- end
113
- end
114
- end
115
- end
114
+ @executable_code = Geminize::Models::CodeExecution::ExecutableCode.new(language, code)
115
+ end
116
+
117
+ # Find code execution result
118
+ result_part = parts.find { |part| part.dig("codeExecutionResult") }
119
+ if result_part && result_part["codeExecutionResult"]
120
+ result_data = result_part["codeExecutionResult"]
121
+ outcome = result_data["outcome"] || "OUTCOME_OK"
122
+ output = result_data["output"] || ""
123
+
124
+ @code_execution_result = Geminize::Models::CodeExecution::CodeExecutionResult.new(outcome, output)
116
125
  end
117
126
  end
118
127
  end
@@ -2,16 +2,21 @@
2
2
 
3
3
  module Geminize
4
4
  module Models
5
- # Represents a tool for function calling in Gemini API
5
+ # Represents a tool for function calling or code execution in Gemini API
6
6
  class Tool
7
- # @return [Geminize::Models::FunctionDeclaration] The function declaration for this tool
7
+ # @return [Geminize::Models::FunctionDeclaration, nil] The function declaration for this tool
8
8
  attr_reader :function_declaration
9
9
 
10
+ # @return [Boolean] Whether this tool is a code execution tool
11
+ attr_reader :code_execution
12
+
10
13
  # Initialize a new tool
11
- # @param function_declaration [Geminize::Models::FunctionDeclaration] The function declaration
14
+ # @param function_declaration [Geminize::Models::FunctionDeclaration, nil] The function declaration
15
+ # @param code_execution [Boolean] Whether this is a code execution tool
12
16
  # @raise [Geminize::ValidationError] If the tool is invalid
13
- def initialize(function_declaration)
17
+ def initialize(function_declaration = nil, code_execution = false)
14
18
  @function_declaration = function_declaration
19
+ @code_execution = code_execution
15
20
  validate!
16
21
  end
17
22
 
@@ -19,7 +24,14 @@ module Geminize
19
24
  # @raise [Geminize::ValidationError] If the tool is invalid
20
25
  # @return [Boolean] true if validation passes
21
26
  def validate!
22
- unless @function_declaration.is_a?(Geminize::Models::FunctionDeclaration)
27
+ if !@code_execution && @function_declaration.nil?
28
+ raise Geminize::ValidationError.new(
29
+ "Either function_declaration or code_execution must be provided",
30
+ "INVALID_ARGUMENT"
31
+ )
32
+ end
33
+
34
+ if @function_declaration && !@function_declaration.is_a?(Geminize::Models::FunctionDeclaration)
23
35
  raise Geminize::ValidationError.new(
24
36
  "Function declaration must be a FunctionDeclaration, got #{@function_declaration.class}",
25
37
  "INVALID_ARGUMENT"
@@ -32,9 +44,15 @@ module Geminize
32
44
  # Convert the tool to a hash for API requests
33
45
  # @return [Hash] The tool as a hash
34
46
  def to_hash
35
- {
36
- functionDeclarations: @function_declaration.to_hash
37
- }
47
+ if @code_execution
48
+ {
49
+ code_execution: {}
50
+ }
51
+ else
52
+ {
53
+ functionDeclarations: @function_declaration.to_hash
54
+ }
55
+ end
38
56
  end
39
57
 
40
58
  # Alias for to_hash
@@ -68,7 +68,7 @@ module Geminize
68
68
  # @param model_name [String] The model name or ID to get info for
69
69
  # @return [String] The complete API endpoint path for getting model info
70
70
  def build_get_model_endpoint(model_name)
71
- # Handle both formats: "models/gemini-1.5-pro" or just "gemini-1.5-pro"
71
+ # Handle both formats: "models/gemini-2.0-flash" or just "gemini-2.0-flash"
72
72
  unless model_name.start_with?("models/")
73
73
  model_name = "models/#{model_name}"
74
74
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Geminize
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.1"
5
5
  end