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 +4 -4
- data/.memory_bank/activeContext.md +11 -1
- data/.memory_bank/progress.md +3 -2
- data/.memory_bank/tasks.md +6 -4
- data/CHANGELOG.md +14 -0
- data/README.md +44 -0
- data/examples/code_execution.rb +126 -0
- data/lib/geminize/models/code_execution/code_execution_result.rb +72 -0
- data/lib/geminize/models/code_execution/executable_code.rb +72 -0
- data/lib/geminize/models/content_request_extensions.rb +8 -0
- data/lib/geminize/models/content_response_extensions.rb +99 -90
- data/lib/geminize/models/tool.rb +26 -8
- data/lib/geminize/module_extensions.rb +55 -0
- data/lib/geminize/version.rb +1 -1
- data/lib/geminize.rb +2 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 251a4ad929a96e4773c21c159849ec415c8ee32269e3a1cda100effeda96d1ed
|
4
|
+
data.tar.gz: 74b958a848f75a0cf700c3751a2fb596c5a22a4a65ea9b927ead0149fef255e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/.memory_bank/progress.md
CHANGED
@@ -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,
|
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
|
-
-
|
83
|
+
- Added code execution support
|
83
84
|
- Additional content types (audio, PDF)
|
84
85
|
- Caching mechanisms
|
85
86
|
- Improved conversation persistence
|
data/.memory_bank/tasks.md
CHANGED
@@ -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
|
-
- [
|
33
|
-
- [
|
34
|
-
- [
|
35
|
-
- [
|
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
|
5
|
+
# Extends ContentResponse with function calling capabilities
|
6
6
|
class ContentResponse
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
#
|
41
|
-
# @return [Boolean]
|
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
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
69
|
+
@function_call = FunctionResponse.new(function_name, function_args)
|
70
|
+
end
|
71
|
+
end
|
52
72
|
|
53
|
-
|
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
|
-
#
|
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
|
-
|
71
|
-
|
86
|
+
# Next check if it's returned in structured format
|
87
|
+
candidates = @response_data.dig("candidates") || []
|
88
|
+
return if candidates.empty?
|
72
89
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
#
|
80
|
-
|
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
|
-
|
104
|
+
content = candidates[0].dig("content") || {}
|
105
|
+
parts = content.dig("parts") || []
|
83
106
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
data/lib/geminize/models/tool.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/geminize/version.rb
CHANGED
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.
|
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
|