geminize 1.1.0 → 1.2.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.
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Extends ContentResponse with function calling response handling
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
36
+
37
+ @function_call
38
+ end
39
+
40
+ # Check if the response contains a function call
41
+ # @return [Boolean] True if the response contains a function call
42
+ def has_function_call?
43
+ !function_call.nil?
44
+ end
45
+
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)
50
+
51
+ @json_response = nil
52
+
53
+ if has_text?
54
+ begin
55
+ @json_response = JSON.parse(text)
56
+ 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
67
+ end
68
+ end
69
+
70
+ @json_response
71
+ end
72
+
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?
77
+ end
78
+
79
+ # Enhanced parse_response method to handle function calls
80
+ alias_method :original_parse_response, :parse_response
81
+
82
+ private
83
+
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
91
+
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
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Represents a function declaration for function calling in Gemini API
6
+ class FunctionDeclaration
7
+ # @return [String] Name of the function
8
+ attr_reader :name
9
+
10
+ # @return [String] Description of what the function does
11
+ attr_reader :description
12
+
13
+ # @return [Hash] JSON schema for function parameters
14
+ attr_reader :parameters
15
+
16
+ # Initialize a new function declaration
17
+ # @param name [String] The name of the function
18
+ # @param description [String] A description of what the function does
19
+ # @param parameters [Hash] JSON schema for function parameters
20
+ # @raise [Geminize::ValidationError] If the function declaration is invalid
21
+ def initialize(name, description, parameters)
22
+ @name = name
23
+ @description = description
24
+ @parameters = parameters
25
+ validate!
26
+ end
27
+
28
+ # Validate the function declaration
29
+ # @raise [Geminize::ValidationError] If the function declaration is invalid
30
+ # @return [Boolean] true if validation passes
31
+ def validate!
32
+ validate_name!
33
+ validate_description!
34
+ validate_parameters!
35
+ true
36
+ end
37
+
38
+ # Convert the function declaration to a hash for API requests
39
+ # @return [Hash] The function declaration as a hash
40
+ def to_hash
41
+ {
42
+ name: @name,
43
+ description: @description,
44
+ parameters: @parameters
45
+ }
46
+ end
47
+
48
+ # Alias for to_hash
49
+ # @return [Hash] The function declaration as a hash
50
+ def to_h
51
+ to_hash
52
+ end
53
+
54
+ private
55
+
56
+ # Validate the function name
57
+ # @raise [Geminize::ValidationError] If the name is invalid
58
+ def validate_name!
59
+ unless @name.is_a?(String)
60
+ raise Geminize::ValidationError.new(
61
+ "Function name must be a string, got #{@name.class}",
62
+ "INVALID_ARGUMENT"
63
+ )
64
+ end
65
+
66
+ if @name.empty?
67
+ raise Geminize::ValidationError.new(
68
+ "Function name cannot be empty",
69
+ "INVALID_ARGUMENT"
70
+ )
71
+ end
72
+ end
73
+
74
+ # Validate the function description
75
+ # @raise [Geminize::ValidationError] If the description is invalid
76
+ def validate_description!
77
+ unless @description.is_a?(String)
78
+ raise Geminize::ValidationError.new(
79
+ "Function description must be a string, got #{@description.class}",
80
+ "INVALID_ARGUMENT"
81
+ )
82
+ end
83
+
84
+ if @description.empty?
85
+ raise Geminize::ValidationError.new(
86
+ "Function description cannot be empty",
87
+ "INVALID_ARGUMENT"
88
+ )
89
+ end
90
+ end
91
+
92
+ # Validate the function parameters
93
+ # @raise [Geminize::ValidationError] If the parameters are invalid
94
+ def validate_parameters!
95
+ unless @parameters.is_a?(Hash)
96
+ raise Geminize::ValidationError.new(
97
+ "Function parameters must be a hash, got #{@parameters.class}",
98
+ "INVALID_ARGUMENT"
99
+ )
100
+ end
101
+
102
+ # Validate that the parameters follow JSON Schema format
103
+ unless @parameters.key?(:type) || @parameters.key?("type")
104
+ raise Geminize::ValidationError.new(
105
+ "Function parameters must include a 'type' field",
106
+ "INVALID_ARGUMENT"
107
+ )
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Represents a function response from Gemini API
6
+ class FunctionResponse
7
+ # @return [String] The name of the function that was called
8
+ attr_reader :name
9
+
10
+ # @return [Hash, Array, String, Numeric, Boolean, nil] The response from the function
11
+ attr_reader :response
12
+
13
+ # Initialize a new function response
14
+ # @param name [String] The name of the function that was called
15
+ # @param response [Hash, Array, String, Numeric, Boolean, nil] The response from the function
16
+ def initialize(name, response)
17
+ @name = name
18
+ @response = response
19
+ validate!
20
+ end
21
+
22
+ # Validate the function response
23
+ # @raise [Geminize::ValidationError] If the function response is invalid
24
+ # @return [Boolean] true if validation passes
25
+ def validate!
26
+ if @name.nil? || @name.empty?
27
+ raise Geminize::ValidationError.new(
28
+ "Function name cannot be empty",
29
+ "INVALID_ARGUMENT"
30
+ )
31
+ end
32
+
33
+ true
34
+ end
35
+
36
+ # Create a FunctionResponse from a hash
37
+ # @param hash [Hash] The hash representation of a function response
38
+ # @return [Geminize::Models::FunctionResponse] The function response
39
+ # @raise [Geminize::ValidationError] If the hash is invalid
40
+ def self.from_hash(hash)
41
+ unless hash.is_a?(Hash)
42
+ raise Geminize::ValidationError.new(
43
+ "Expected a Hash, got #{hash.class}",
44
+ "INVALID_ARGUMENT"
45
+ )
46
+ end
47
+
48
+ name = hash["name"] || hash[:name]
49
+ response = hash["response"] || hash[:response]
50
+
51
+ new(name, response)
52
+ end
53
+
54
+ # Convert the function response to a hash
55
+ # @return [Hash] The function response as a hash
56
+ def to_hash
57
+ {
58
+ name: @name,
59
+ response: @response
60
+ }
61
+ end
62
+
63
+ # Alias for to_hash
64
+ # @return [Hash] The function response as a hash
65
+ def to_h
66
+ to_hash
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Represents a safety setting for content filtering in Gemini API
6
+ class SafetySetting
7
+ # Valid harm categories for safety settings
8
+ HARM_CATEGORIES = [
9
+ "HARM_CATEGORY_HARASSMENT",
10
+ "HARM_CATEGORY_HATE_SPEECH",
11
+ "HARM_CATEGORY_SEXUALLY_EXPLICIT",
12
+ "HARM_CATEGORY_DANGEROUS_CONTENT"
13
+ ].freeze
14
+
15
+ # Valid threshold levels for safety settings
16
+ THRESHOLD_LEVELS = [
17
+ "BLOCK_NONE",
18
+ "BLOCK_LOW_AND_ABOVE",
19
+ "BLOCK_MEDIUM_AND_ABOVE",
20
+ "BLOCK_ONLY_HIGH"
21
+ ].freeze
22
+
23
+ # @return [String] The harm category this setting applies to
24
+ attr_reader :category
25
+
26
+ # @return [String] The threshold level for filtering
27
+ attr_reader :threshold
28
+
29
+ # Initialize a new safety setting
30
+ # @param category [String] The harm category this setting applies to
31
+ # @param threshold [String] The threshold level for filtering
32
+ # @raise [Geminize::ValidationError] If the safety setting is invalid
33
+ def initialize(category, threshold)
34
+ @category = category
35
+ @threshold = threshold
36
+ validate!
37
+ end
38
+
39
+ # Validate the safety setting
40
+ # @raise [Geminize::ValidationError] If the safety setting is invalid
41
+ # @return [Boolean] true if validation passes
42
+ def validate!
43
+ validate_category!
44
+ validate_threshold!
45
+ true
46
+ end
47
+
48
+ # Convert the safety setting to a hash for API requests
49
+ # @return [Hash] The safety setting as a hash
50
+ def to_hash
51
+ {
52
+ category: @category,
53
+ threshold: @threshold
54
+ }
55
+ end
56
+
57
+ # Alias for to_hash
58
+ # @return [Hash] The safety setting as a hash
59
+ def to_h
60
+ to_hash
61
+ end
62
+
63
+ private
64
+
65
+ # Validate the harm category
66
+ # @raise [Geminize::ValidationError] If the category is invalid
67
+ def validate_category!
68
+ unless @category.is_a?(String)
69
+ raise Geminize::ValidationError.new(
70
+ "Category must be a string, got #{@category.class}",
71
+ "INVALID_ARGUMENT"
72
+ )
73
+ end
74
+
75
+ unless HARM_CATEGORIES.include?(@category)
76
+ raise Geminize::ValidationError.new(
77
+ "Invalid harm category: #{@category}. Must be one of: #{HARM_CATEGORIES.join(", ")}",
78
+ "INVALID_ARGUMENT"
79
+ )
80
+ end
81
+ end
82
+
83
+ # Validate the threshold level
84
+ # @raise [Geminize::ValidationError] If the threshold is invalid
85
+ def validate_threshold!
86
+ unless @threshold.is_a?(String)
87
+ raise Geminize::ValidationError.new(
88
+ "Threshold must be a string, got #{@threshold.class}",
89
+ "INVALID_ARGUMENT"
90
+ )
91
+ end
92
+
93
+ unless THRESHOLD_LEVELS.include?(@threshold)
94
+ raise Geminize::ValidationError.new(
95
+ "Invalid threshold level: #{@threshold}. Must be one of: #{THRESHOLD_LEVELS.join(", ")}",
96
+ "INVALID_ARGUMENT"
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Represents a tool for function calling in Gemini API
6
+ class Tool
7
+ # @return [Geminize::Models::FunctionDeclaration] The function declaration for this tool
8
+ attr_reader :function_declaration
9
+
10
+ # Initialize a new tool
11
+ # @param function_declaration [Geminize::Models::FunctionDeclaration] The function declaration
12
+ # @raise [Geminize::ValidationError] If the tool is invalid
13
+ def initialize(function_declaration)
14
+ @function_declaration = function_declaration
15
+ validate!
16
+ end
17
+
18
+ # Validate the tool
19
+ # @raise [Geminize::ValidationError] If the tool is invalid
20
+ # @return [Boolean] true if validation passes
21
+ def validate!
22
+ unless @function_declaration.is_a?(Geminize::Models::FunctionDeclaration)
23
+ raise Geminize::ValidationError.new(
24
+ "Function declaration must be a FunctionDeclaration, got #{@function_declaration.class}",
25
+ "INVALID_ARGUMENT"
26
+ )
27
+ end
28
+
29
+ true
30
+ end
31
+
32
+ # Convert the tool to a hash for API requests
33
+ # @return [Hash] The tool as a hash
34
+ def to_hash
35
+ {
36
+ functionDeclarations: @function_declaration.to_hash
37
+ }
38
+ end
39
+
40
+ # Alias for to_hash
41
+ # @return [Hash] The tool as a hash
42
+ def to_h
43
+ to_hash
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ module Models
5
+ # Represents configuration for tool execution in Gemini API
6
+ class ToolConfig
7
+ # Valid execution modes for tools
8
+ EXECUTION_MODES = ["AUTO", "MANUAL", "NONE"].freeze
9
+
10
+ # @return [String] The execution mode for tools
11
+ attr_reader :execution_mode
12
+
13
+ # Initialize a new tool configuration
14
+ # @param execution_mode [String] The execution mode for tools
15
+ # @raise [Geminize::ValidationError] If the tool configuration is invalid
16
+ def initialize(execution_mode = "AUTO")
17
+ @execution_mode = execution_mode
18
+ validate!
19
+ end
20
+
21
+ # Validate the tool configuration
22
+ # @raise [Geminize::ValidationError] If the tool configuration is invalid
23
+ # @return [Boolean] true if validation passes
24
+ def validate!
25
+ unless EXECUTION_MODES.include?(@execution_mode)
26
+ raise Geminize::ValidationError.new(
27
+ "Invalid execution mode: #{@execution_mode}. Must be one of: #{EXECUTION_MODES.join(", ")}",
28
+ "INVALID_ARGUMENT"
29
+ )
30
+ end
31
+
32
+ true
33
+ end
34
+
35
+ # Convert the tool configuration to a hash for API requests
36
+ # @return [Hash] The tool configuration as a hash
37
+ def to_hash
38
+ {
39
+ function_calling_config: {
40
+ mode: @execution_mode
41
+ }
42
+ }
43
+ end
44
+
45
+ # Alias for to_hash
46
+ # @return [Hash] The tool configuration as a hash
47
+ def to_h
48
+ to_hash
49
+ end
50
+ end
51
+ end
52
+ end