geminize 1.2.0 → 1.3.0

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: 900c19e9cf075e32779e3ee2f4f4530fdced2c027e8bc393d05250c1b20e5dac
4
- data.tar.gz: 7d1a9a2f365eff24d2625492916d578f661fd881263c1cd4101352d7b16780ce
3
+ metadata.gz: 251a4ad929a96e4773c21c159849ec415c8ee32269e3a1cda100effeda96d1ed
4
+ data.tar.gz: 74b958a848f75a0cf700c3751a2fb596c5a22a4a65ea9b927ead0149fef255e7
5
5
  SHA512:
6
- metadata.gz: b1e3a5235a330b9bba4690a2452992f331bf4b76294109e2d4a8beb3fbc357c3f06be8b9728d607aff8653367458827bf17b166a1c89c6d243062272e747b154
7
- data.tar.gz: a9c73fc3264fccaa26ba6b525d89807a9a99052232c12387281844d1cca821609c795fe199ed222c601962e8f144fc73854b73eccc75994fc3ab6ac70e4f7b2d
6
+ metadata.gz: a1ec3f597e8f30eedf8fe12304497896ba98e7e35137d751a64f0d3430a4723db6d63997b8852e0794e44029d014408d070613a117ae0089290d0c8e852cb2bd
7
+ data.tar.gz: cc0de3e6fa6949894ee531092ba02c0f07a4d828540fd1bf4f0b3d8c037f7e761212e4e4cc02dc35fedd2eea0ac8446c17c1f1ff601d64b5b5bbfa847b6702a4
@@ -12,6 +12,7 @@
12
12
  - Function calling capabilities
13
13
  - JSON mode for structured responses
14
14
  - Safety settings for content moderation
15
+ - Code execution capabilities
15
16
 
16
17
  ### Key Implementation Details
17
18
 
@@ -32,6 +33,15 @@
32
33
  - Function result processing
33
34
  - String-based function call detection
34
35
 
36
+ #### Code Execution
37
+
38
+ - Python code generation and execution support
39
+ - Code execution result handling
40
+ - Support for code libraries like matplotlib, numpy, etc.
41
+ - Tool-based code execution integration
42
+ - Executable code response parsing
43
+ - Code output result parsing
44
+
35
45
  #### JSON Mode
36
46
 
37
47
  - Structured data response handling
@@ -91,10 +101,10 @@
91
101
  - Error handling refinements
92
102
  - Models API integration
93
103
  - Function calling support
104
+ - Code execution capabilities
94
105
 
95
106
  ### Upcoming Features
96
107
 
97
- - Code execution support
98
108
  - Additional content types (audio, PDF)
99
109
  - Caching support
100
110
  - Advanced parameter tuning
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Current Status
4
4
 
5
- Geminize is a functional Ruby gem providing a complete interface to the Google Gemini API. The current implementation includes all core features with a focus on stability, documentation, and ease of use. The gem now also includes function calling, JSON mode, and safety settings features.
5
+ Geminize is a functional Ruby gem providing a complete interface to the Google Gemini API. The current implementation includes all core features with a focus on stability, documentation, and ease of use. The gem now also includes function calling, JSON mode, safety settings, and code execution features.
6
6
 
7
7
  ## Development Progress
8
8
 
@@ -19,6 +19,7 @@ Geminize is a functional Ruby gem providing a complete interface to the Google G
19
19
  - **Function Calling**: ✅ Complete
20
20
  - **JSON Mode**: ✅ Complete
21
21
  - **Safety Settings**: ✅ Complete
22
+ - **Code Execution**: ✅ Complete
22
23
 
23
24
  ### Documentation
24
25
 
@@ -79,7 +80,7 @@ Geminize is a functional Ruby gem providing a complete interface to the Google G
79
80
 
80
81
  ### v1.3.0 (Planned)
81
82
 
82
- - Code execution support
83
+ - Added code execution support
83
84
  - Additional content types (audio, PDF)
84
85
  - Caching mechanisms
85
86
  - Improved conversation persistence
@@ -29,10 +29,12 @@
29
29
  - [x] Add safety configuration to requests
30
30
  - [x] Implement module-level safety methods
31
31
  - [x] Add tests for safety settings
32
- - [ ] **Code Execution Support**
33
- - [ ] Create code execution model classes
34
- - [ ] Implement code execution tools in requests
35
- - [ ] Update response handling for code execution
32
+ - [x] **Code Execution Support**
33
+ - [x] Create code execution model classes
34
+ - [x] Implement code execution tools in requests
35
+ - [x] Update response handling for code execution
36
+ - [x] Add module-level code execution methods
37
+ - [x] Create example script for code execution
36
38
  - [ ] **Additional Content Types**
37
39
  - [ ] Audio content support
38
40
  - [ ] Document/PDF content support
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.3.0] - 2024-05-02
2
+
3
+ ### Added
4
+
5
+ - Code execution capabilities for generating and running Python code
6
+ - Added `generate_with_code_execution` method to create and execute Python code
7
+ - Implemented `ExecutableCode` model for representing generated code
8
+ - Added `CodeExecutionResult` model for capturing execution output and status
9
+ - Added support for visualizations and data analysis with matplotlib and other libraries
10
+ - Extended tool system to support code execution tools
11
+ - Updated content request/response handling for code execution
12
+ - Added comprehensive test suite with VCR tests for code execution
13
+ - Added examples demonstrating code execution functionality
14
+
1
15
  ## [1.2.0] - 2025-05-02
2
16
 
3
17
  ### Added
data/README.md CHANGED
@@ -454,6 +454,48 @@ Available threshold levels (from most to least restrictive):
454
454
  - `BLOCK_ONLY_HIGH`
455
455
  - `BLOCK_NONE`
456
456
 
457
+ ## Code Execution
458
+
459
+ Generate and run Python code to solve problems or analyze data:
460
+
461
+ ```ruby
462
+ require 'geminize'
463
+ # Assumes API key is configured via .env
464
+
465
+ # Ask Gemini to solve a problem with code
466
+ response = Geminize.generate_with_code_execution(
467
+ "Calculate the sum of the first 10 prime numbers",
468
+ "gemini-2.0-flash", # Use a model that supports code execution
469
+ { temperature: 0.2 }
470
+ )
471
+
472
+ # Display the response text
473
+ puts "Gemini's explanation:"
474
+ puts response.text
475
+
476
+ # Access the generated code
477
+ if response.has_executable_code?
478
+ puts "\nGenerated Python code:"
479
+ puts response.executable_code.code
480
+ end
481
+
482
+ # Access the code execution result
483
+ if response.has_code_execution_result?
484
+ puts "\nExecution result:"
485
+ puts "Outcome: #{response.code_execution_result.outcome}"
486
+ puts "Output: #{response.code_execution_result.output}"
487
+ end
488
+ ```
489
+
490
+ Code execution is perfect for:
491
+
492
+ - Solving mathematical problems
493
+ - Data analysis and visualization
494
+ - Algorithm implementation
495
+ - Demonstrating programming concepts
496
+
497
+ The model generates Python code, executes it in a secure environment, and returns both the code and its execution results.
498
+
457
499
  ## Streaming Responses
458
500
 
459
501
  Get real-time, token-by-token responses:
@@ -496,6 +538,8 @@ Check out these example applications to see Geminize in action:
496
538
  - [Multimodal Example](examples/multimodal.rb)
497
539
  - [System Instructions Example](examples/system_instructions.rb)
498
540
  - [Models API Example](examples/models_api.rb)
541
+ - [Function Calling Example](examples/function_calling.rb)
542
+ - [Code Execution Example](examples/code_execution.rb)
499
543
 
500
544
  ## Working with Models
501
545
 
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "geminize"
6
+
7
+ # Configure the API key
8
+ Geminize.configure do |config|
9
+ config.api_key = ENV["GEMINI_API_KEY"] # Make sure to set your API key in the environment
10
+ config.default_model = "gemini-2.0-flash" # Use a model that supports code execution
11
+ end
12
+
13
+ puts "==========================================="
14
+ puts "= GEMINIZE CODE EXECUTION EXAMPLES ="
15
+ puts "==========================================="
16
+
17
+ puts "\n=== BASIC CODE EXECUTION EXAMPLE ==="
18
+ puts "Asking Gemini to calculate the sum of the first 50 prime numbers..."
19
+
20
+ begin
21
+ # Generate a response with code execution enabled
22
+ response = Geminize.generate_with_code_execution(
23
+ "What is the sum of the first 50 prime numbers? Generate and run code for the calculation, and make sure you get all 50.",
24
+ nil,
25
+ {temperature: 0.2}
26
+ )
27
+
28
+ # Display the generated text
29
+ puts "\nGemini's response:"
30
+ puts response.text
31
+
32
+ # Display the executable code if present
33
+ if response.has_executable_code?
34
+ puts "\nGenerated code:"
35
+ puts "```#{response.executable_code.language.downcase}"
36
+ puts response.executable_code.code
37
+ puts "```"
38
+ end
39
+
40
+ # Display the code execution result if present
41
+ if response.has_code_execution_result?
42
+ puts "\nCode execution result:"
43
+ puts "Outcome: #{response.code_execution_result.outcome}"
44
+ puts "Output: #{response.code_execution_result.output}"
45
+ end
46
+ rescue => e
47
+ puts "Error during code execution: #{e.message}"
48
+ end
49
+
50
+ puts "\n\n=== PRACTICAL CODE EXECUTION EXAMPLE ==="
51
+ puts "Asking Gemini to analyze some text data..."
52
+
53
+ begin
54
+ # Generate a response with code execution for data analysis
55
+ response = Geminize.generate_with_code_execution(
56
+ "I have a list of temperatures: 32, 25, 30, 22, 28, 27, 35, 31, 29, 26. " \
57
+ "Please write code to calculate the mean, median, and standard deviation. " \
58
+ "Also, create a simple histogram visualization using matplotlib.",
59
+ nil,
60
+ {temperature: 0.2}
61
+ )
62
+
63
+ # Display the generated text
64
+ puts "\nGemini's response:"
65
+ puts response.text
66
+
67
+ # Display the executable code if present
68
+ if response.has_executable_code?
69
+ puts "\nGenerated code:"
70
+ puts "```#{response.executable_code.language.downcase}"
71
+ puts response.executable_code.code
72
+ puts "```"
73
+ end
74
+
75
+ # Display the code execution result if present
76
+ if response.has_code_execution_result?
77
+ puts "\nCode execution result:"
78
+ puts "Outcome: #{response.code_execution_result.outcome}"
79
+ puts "Output: #{response.code_execution_result.output}"
80
+ end
81
+ rescue => e
82
+ puts "Error during code execution: #{e.message}"
83
+ end
84
+
85
+ puts "\n\n=== ITERATIVE PROBLEM SOLVING EXAMPLE ==="
86
+ puts "Asking Gemini to solve a complex problem using code execution..."
87
+
88
+ begin
89
+ # Generate a response for a more complex problem
90
+ response = Geminize.generate_with_code_execution(
91
+ "Write a Python function to find the nth Fibonacci number where n is a positive integer. " \
92
+ "Then use this function to calculate the 50th Fibonacci number. " \
93
+ "Implement it efficiently using memoization to avoid redundant calculations.",
94
+ nil,
95
+ {temperature: 0.2}
96
+ )
97
+
98
+ # Display the generated text
99
+ puts "\nGemini's response:"
100
+ puts response.text
101
+
102
+ # Display the executable code if present
103
+ if response.has_executable_code?
104
+ puts "\nGenerated code:"
105
+ puts "```#{response.executable_code.language.downcase}"
106
+ puts response.executable_code.code
107
+ puts "```"
108
+ end
109
+
110
+ # Display the code execution result if present
111
+ if response.has_code_execution_result?
112
+ puts "\nCode execution result:"
113
+ puts "Outcome: #{response.code_execution_result.outcome}"
114
+ puts "Output: #{response.code_execution_result.output}"
115
+ end
116
+ rescue => e
117
+ puts "Error during code execution: #{e.message}"
118
+ puts "\nNOTE: If you hit a quota limit, try:"
119
+ puts "1. Using a paid API key with higher quotas"
120
+ puts "2. Reducing the number of examples you run"
121
+ puts "3. Adding delays between API calls"
122
+ end
123
+
124
+ puts "\n==========================================="
125
+ puts "= END OF EXAMPLES ="
126
+ puts "==========================================="
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ module CodeExecution
6
+ # Represents the result of executed code
7
+ class CodeExecutionResult
8
+ # Valid outcome values for code execution
9
+ VALID_OUTCOMES = ["OUTCOME_OK", "OUTCOME_ERROR"].freeze
10
+
11
+ # @return [String] The outcome of the code execution (e.g., "OUTCOME_OK", "OUTCOME_ERROR")
12
+ attr_reader :outcome
13
+
14
+ # @return [String] The output from the code execution
15
+ attr_reader :output
16
+
17
+ # Initialize a new code execution result
18
+ # @param outcome [String] The outcome of the code execution
19
+ # @param output [String] The output from the code execution
20
+ # @raise [Geminize::ValidationError] If the code execution result is invalid
21
+ def initialize(outcome, output)
22
+ @outcome = outcome
23
+ @output = output
24
+ validate!
25
+ end
26
+
27
+ # Validate the code execution result
28
+ # @raise [Geminize::ValidationError] If the code execution result is invalid
29
+ # @return [Boolean] true if validation passes
30
+ def validate!
31
+ unless @outcome.is_a?(String)
32
+ raise Geminize::ValidationError.new(
33
+ "Outcome must be a string, got #{@outcome.class}",
34
+ "INVALID_ARGUMENT"
35
+ )
36
+ end
37
+
38
+ unless VALID_OUTCOMES.include?(@outcome)
39
+ raise Geminize::ValidationError.new(
40
+ "Invalid outcome: #{@outcome}. Must be one of: #{VALID_OUTCOMES.join(", ")}",
41
+ "INVALID_ARGUMENT"
42
+ )
43
+ end
44
+
45
+ unless @output.is_a?(String)
46
+ raise Geminize::ValidationError.new(
47
+ "Output must be a string, got #{@output.class}",
48
+ "INVALID_ARGUMENT"
49
+ )
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ # Convert the code execution result to a hash
56
+ # @return [Hash] The code execution result as a hash
57
+ def to_hash
58
+ {
59
+ outcome: @outcome,
60
+ output: @output
61
+ }
62
+ end
63
+
64
+ # Alias for to_hash
65
+ # @return [Hash] The code execution result as a hash
66
+ def to_h
67
+ to_hash
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ module CodeExecution
6
+ # Represents executable code generated by the model
7
+ class ExecutableCode
8
+ # Valid language options for executable code
9
+ VALID_LANGUAGES = ["PYTHON"].freeze
10
+
11
+ # @return [String] The language of the code (e.g., "PYTHON")
12
+ attr_reader :language
13
+
14
+ # @return [String] The code content
15
+ attr_reader :code
16
+
17
+ # Initialize a new executable code object
18
+ # @param language [String] The programming language of the code (e.g., "PYTHON")
19
+ # @param code [String] The code content
20
+ # @raise [Geminize::ValidationError] If the executable code is invalid
21
+ def initialize(language, code)
22
+ @language = language
23
+ @code = code
24
+ validate!
25
+ end
26
+
27
+ # Validate the executable code
28
+ # @raise [Geminize::ValidationError] If the executable code is invalid
29
+ # @return [Boolean] true if validation passes
30
+ def validate!
31
+ unless @language.is_a?(String)
32
+ raise Geminize::ValidationError.new(
33
+ "Language must be a string, got #{@language.class}",
34
+ "INVALID_ARGUMENT"
35
+ )
36
+ end
37
+
38
+ unless VALID_LANGUAGES.include?(@language)
39
+ raise Geminize::ValidationError.new(
40
+ "Invalid language: #{@language}. Must be one of: #{VALID_LANGUAGES.join(", ")}",
41
+ "INVALID_ARGUMENT"
42
+ )
43
+ end
44
+
45
+ unless @code.is_a?(String)
46
+ raise Geminize::ValidationError.new(
47
+ "Code must be a string, got #{@code.class}",
48
+ "INVALID_ARGUMENT"
49
+ )
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ # Convert the executable code to a hash
56
+ # @return [Hash] The executable code as a hash
57
+ def to_hash
58
+ {
59
+ language: @language,
60
+ code: @code
61
+ }
62
+ end
63
+
64
+ # Alias for to_hash
65
+ # @return [Hash] The executable code as a hash
66
+ def to_h
67
+ to_hash
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -31,6 +31,14 @@ module Geminize
31
31
  self
32
32
  end
33
33
 
34
+ # Enable code execution for the request
35
+ # @return [self] The request object for chaining
36
+ def enable_code_execution
37
+ @tools ||= []
38
+ @tools << Tool.new(nil, true)
39
+ self
40
+ end
41
+
34
42
  # Set the tool config for function execution
35
43
  # @param execution_mode [String] The execution mode for functions ("AUTO", "MANUAL", or "NONE")
36
44
  # @return [self] The request object for chaining
@@ -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
@@ -224,5 +224,60 @@ module Geminize
224
224
  generator.generate(content_request)
225
225
  end
226
226
  end
227
+
228
+ # Generate text with code execution capabilities
229
+ # @param prompt [String] The input prompt
230
+ # @param model_name [String, nil] The model to use, defaults to the configured default model
231
+ # @param params [Hash] Additional parameters for generation
232
+ # @option params [Float] :temperature Controls randomness (0.0-1.0)
233
+ # @option params [Integer] :max_tokens Maximum tokens to generate
234
+ # @option params [Float] :top_p Top-p value for nucleus sampling (0.0-1.0)
235
+ # @option params [Integer] :top_k Top-k value for sampling
236
+ # @option params [Array<String>] :stop_sequences Stop sequences to end generation
237
+ # @option params [String] :system_instruction System instruction to guide model behavior
238
+ # @param with_retries [Boolean] Whether to retry the generation if it fails
239
+ # @param max_retries [Integer] Maximum number of retries
240
+ # @param retry_delay [Float] Delay between retries in seconds
241
+ # @param client_options [Hash] Options for the HTTP client
242
+ # @return [Geminize::Models::ContentResponse] The generated response
243
+ # @raise [Geminize::Error] If the generation fails
244
+ # @example Generate text with code execution
245
+ # Geminize.generate_with_code_execution(
246
+ # "What is the sum of the first 50 prime numbers?",
247
+ # nil,
248
+ # { temperature: 0.2 }
249
+ # )
250
+ def generate_with_code_execution(prompt, model_name = nil, params = {}, with_retries: true, max_retries: 3, retry_delay: 1.0, client_options: nil)
251
+ validate_configuration!
252
+
253
+ # Initialize the generator
254
+ client = client_options ? Client.new(client_options) : Client.new
255
+ generator = TextGeneration.new(client)
256
+
257
+ # Set up params with defaults
258
+ generation_params = params.dup
259
+ with_retries = generation_params.delete(:with_retries) != false if generation_params.key?(:with_retries)
260
+
261
+ # Enhance the system instruction to ensure code execution is effective
262
+ generation_params[:system_instruction] ||= ""
263
+ generation_params[:system_instruction] = "You are a helpful assistant with the ability to generate and execute Python code. When appropriate, use code to solve problems or complete tasks. " + generation_params[:system_instruction]
264
+
265
+ # Create the request
266
+ content_request = Models::ContentRequest.new(
267
+ prompt,
268
+ model_name || configuration.default_model,
269
+ generation_params
270
+ )
271
+
272
+ # Enable code execution
273
+ content_request.enable_code_execution
274
+
275
+ # Generate the response
276
+ if with_retries
277
+ generator.generate_with_retries(content_request, max_retries, retry_delay)
278
+ else
279
+ generator.generate(content_request)
280
+ end
281
+ end
227
282
  end
228
283
  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.0"
5
5
  end
data/lib/geminize.rb CHANGED
@@ -33,6 +33,8 @@ require_relative "geminize/models/tool"
33
33
  require_relative "geminize/models/tool_config"
34
34
  require_relative "geminize/models/function_response"
35
35
  require_relative "geminize/models/safety_setting"
36
+ require_relative "geminize/models/code_execution/executable_code"
37
+ require_relative "geminize/models/code_execution/code_execution_result"
36
38
  require_relative "geminize/request_builder"
37
39
  require_relative "geminize/vector_utils"
38
40
  require_relative "geminize/text_generation"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geminize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nhat Long Nguyen
@@ -182,6 +182,7 @@ files:
182
182
  - README.md
183
183
  - Rakefile
184
184
  - examples/README.md
185
+ - examples/code_execution.rb
185
186
  - examples/configuration.rb
186
187
  - examples/embeddings.rb
187
188
  - examples/function_calling.rb
@@ -204,6 +205,8 @@ files:
204
205
  - lib/geminize/model_info.rb
205
206
  - lib/geminize/models/chat_request.rb
206
207
  - lib/geminize/models/chat_response.rb
208
+ - lib/geminize/models/code_execution/code_execution_result.rb
209
+ - lib/geminize/models/code_execution/executable_code.rb
207
210
  - lib/geminize/models/content_request.rb
208
211
  - lib/geminize/models/content_request_extensions.rb
209
212
  - lib/geminize/models/content_request_safety.rb