geminize 0.1.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.standard.yml +3 -0
  4. data/.yardopts +14 -0
  5. data/CHANGELOG.md +24 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/CONTRIBUTING.md +109 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +423 -0
  10. data/Rakefile +10 -0
  11. data/examples/README.md +75 -0
  12. data/examples/configuration.rb +58 -0
  13. data/examples/embeddings.rb +195 -0
  14. data/examples/multimodal.rb +126 -0
  15. data/examples/rails_chat/README.md +69 -0
  16. data/examples/rails_chat/app/controllers/chat_controller.rb +26 -0
  17. data/examples/rails_chat/app/views/chat/index.html.erb +112 -0
  18. data/examples/rails_chat/config/routes.rb +8 -0
  19. data/examples/rails_initializer.rb +46 -0
  20. data/examples/system_instructions.rb +101 -0
  21. data/lib/geminize/chat.rb +98 -0
  22. data/lib/geminize/client.rb +318 -0
  23. data/lib/geminize/configuration.rb +98 -0
  24. data/lib/geminize/conversation_repository.rb +161 -0
  25. data/lib/geminize/conversation_service.rb +126 -0
  26. data/lib/geminize/embeddings.rb +145 -0
  27. data/lib/geminize/error_mapper.rb +96 -0
  28. data/lib/geminize/error_parser.rb +120 -0
  29. data/lib/geminize/errors.rb +185 -0
  30. data/lib/geminize/middleware/error_handler.rb +72 -0
  31. data/lib/geminize/model_info.rb +91 -0
  32. data/lib/geminize/models/chat_request.rb +186 -0
  33. data/lib/geminize/models/chat_response.rb +118 -0
  34. data/lib/geminize/models/content_request.rb +530 -0
  35. data/lib/geminize/models/content_response.rb +99 -0
  36. data/lib/geminize/models/conversation.rb +156 -0
  37. data/lib/geminize/models/embedding_request.rb +222 -0
  38. data/lib/geminize/models/embedding_response.rb +1064 -0
  39. data/lib/geminize/models/memory.rb +88 -0
  40. data/lib/geminize/models/message.rb +140 -0
  41. data/lib/geminize/models/model.rb +171 -0
  42. data/lib/geminize/models/model_list.rb +124 -0
  43. data/lib/geminize/models/stream_response.rb +99 -0
  44. data/lib/geminize/rails/app/controllers/concerns/geminize/controller.rb +105 -0
  45. data/lib/geminize/rails/app/helpers/geminize_helper.rb +125 -0
  46. data/lib/geminize/rails/controller_additions.rb +41 -0
  47. data/lib/geminize/rails/engine.rb +29 -0
  48. data/lib/geminize/rails/helper_additions.rb +37 -0
  49. data/lib/geminize/rails.rb +50 -0
  50. data/lib/geminize/railtie.rb +33 -0
  51. data/lib/geminize/request_builder.rb +57 -0
  52. data/lib/geminize/text_generation.rb +285 -0
  53. data/lib/geminize/validators.rb +150 -0
  54. data/lib/geminize/vector_utils.rb +164 -0
  55. data/lib/geminize/version.rb +5 -0
  56. data/lib/geminize.rb +527 -0
  57. data/lib/generators/geminize/install_generator.rb +22 -0
  58. data/lib/generators/geminize/templates/README +31 -0
  59. data/lib/generators/geminize/templates/initializer.rb +38 -0
  60. data/sig/geminize.rbs +4 -0
  61. metadata +218 -0
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ # High-level service for managing conversations
5
+ class ConversationService
6
+ # @return [Geminize::ConversationRepository] The repository for storing conversations
7
+ attr_reader :repository
8
+
9
+ # @return [Geminize::Client] The client instance
10
+ attr_reader :client
11
+
12
+ # Initialize a new conversation service
13
+ # @param repository [Geminize::ConversationRepository, nil] The repository to use
14
+ # @param client [Geminize::Client, nil] The client to use
15
+ # @param options [Hash] Additional options
16
+ def initialize(repository = nil, client = nil, options = {})
17
+ @repository = repository || Geminize.conversation_repository
18
+ @client = client || Client.new(options)
19
+ @options = options
20
+ end
21
+
22
+ # Create a new conversation
23
+ # @param title [String, nil] Optional title for the conversation
24
+ # @param system_instruction [String, nil] Optional system instruction to guide model behavior
25
+ # @return [Models::Conversation] The new conversation
26
+ def create_conversation(title = nil, system_instruction = nil)
27
+ # Create a conversation with the appropriate arguments
28
+ conversation = if system_instruction
29
+ Models::Conversation.new(nil, title, nil, nil, system_instruction)
30
+ else
31
+ Models::Conversation.new(nil, title)
32
+ end
33
+ @repository.save(conversation)
34
+ conversation
35
+ end
36
+
37
+ # Get a conversation by ID
38
+ # @param id [String] The ID of the conversation to get
39
+ # @return [Models::Conversation, nil] The conversation or nil if not found
40
+ # @raise [Geminize::GeminizeError] If the conversation cannot be loaded
41
+ def get_conversation(id)
42
+ @repository.load(id)
43
+ end
44
+
45
+ # Send a message in a conversation and get a response
46
+ # @param conversation_id [String] The ID of the conversation
47
+ # @param message [String] The message to send
48
+ # @param model_name [String, nil] The model to use
49
+ # @param params [Hash] Additional generation parameters
50
+ # @return [Hash] The response and updated conversation
51
+ # @raise [Geminize::GeminizeError] If the request fails
52
+ def send_message(conversation_id, message, model_name = nil, params = {})
53
+ # Load the conversation
54
+ conversation = get_conversation(conversation_id)
55
+ raise Geminize::GeminizeError.new("Conversation not found: #{conversation_id}", nil, nil) unless conversation
56
+
57
+ # Create a chat instance with the conversation
58
+ chat = Chat.new(conversation, @client, @options)
59
+
60
+ # Send the message
61
+ response = chat.send_message(message, model_name, params)
62
+
63
+ # Save the updated conversation
64
+ @repository.save(conversation)
65
+
66
+ {
67
+ response: response,
68
+ conversation: conversation
69
+ }
70
+ end
71
+
72
+ # List all available conversations
73
+ # @return [Array<Hash>] An array of conversation metadata
74
+ def list_conversations
75
+ @repository.list
76
+ end
77
+
78
+ # Delete a conversation
79
+ # @param id [String] The ID of the conversation to delete
80
+ # @return [Boolean] True if the deletion was successful
81
+ def delete_conversation(id)
82
+ @repository.delete(id)
83
+ end
84
+
85
+ # Update a conversation title
86
+ # @param id [String] The ID of the conversation to update
87
+ # @param title [String] The new title
88
+ # @return [Models::Conversation] The updated conversation
89
+ # @raise [Geminize::GeminizeError] If the conversation cannot be loaded or saved
90
+ def update_conversation_title(id, title)
91
+ conversation = get_conversation(id)
92
+ raise Geminize::GeminizeError.new("Conversation not found: #{id}", nil, nil) unless conversation
93
+
94
+ conversation.title = title
95
+ @repository.save(conversation)
96
+ conversation
97
+ end
98
+
99
+ # Clear a conversation's message history
100
+ # @param id [String] The ID of the conversation to clear
101
+ # @return [Models::Conversation] The updated conversation
102
+ # @raise [Geminize::GeminizeError] If the conversation cannot be loaded or saved
103
+ def clear_conversation(id)
104
+ conversation = get_conversation(id)
105
+ raise Geminize::GeminizeError.new("Conversation not found: #{id}", nil, nil) unless conversation
106
+
107
+ conversation.clear
108
+ @repository.save(conversation)
109
+ conversation
110
+ end
111
+
112
+ # Update a conversation's system instruction
113
+ # @param id [String] The ID of the conversation to update
114
+ # @param system_instruction [String] The new system instruction
115
+ # @return [Models::Conversation] The updated conversation
116
+ # @raise [Geminize::GeminizeError] If the conversation cannot be loaded or saved
117
+ def update_conversation_system_instruction(id, system_instruction)
118
+ conversation = get_conversation(id)
119
+ raise Geminize::GeminizeError.new("Conversation not found: #{id}", nil, nil) unless conversation
120
+
121
+ conversation.system_instruction = system_instruction
122
+ @repository.save(conversation)
123
+ conversation
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "models/embedding_request"
4
+ require_relative "models/embedding_response"
5
+
6
+ module Geminize
7
+ # Class for embedding generation functionality
8
+ class Embeddings
9
+ # Default maximum batch size
10
+ DEFAULT_MAX_BATCH_SIZE = 100
11
+
12
+ # @return [Geminize::Client] The client instance
13
+ attr_reader :client
14
+
15
+ # Initialize a new embeddings instance
16
+ # @param client [Geminize::Client, nil] The client to use (optional)
17
+ # @param options [Hash] Additional options
18
+ def initialize(client = nil, options = {})
19
+ @client = client || Client.new(options)
20
+ @options = options
21
+ end
22
+
23
+ # Generate embeddings based on an embedding request
24
+ # @param embedding_request [Geminize::Models::EmbeddingRequest] The embedding request
25
+ # @return [Geminize::Models::EmbeddingResponse] The embedding response
26
+ # @raise [Geminize::GeminizeError] If the request fails
27
+ def generate(embedding_request)
28
+ endpoint = build_embedding_endpoint(embedding_request.model_name, embedding_request.batch?)
29
+ payload = embedding_request.to_hash
30
+
31
+ response_data = @client.post(endpoint, payload)
32
+ Models::EmbeddingResponse.from_hash(response_data)
33
+ end
34
+
35
+ # Generate embeddings from text with optional parameters
36
+ # @param text [String, Array<String>] The input text or array of texts
37
+ # @param model_name [String, nil] The model to use (optional)
38
+ # @param params [Hash] Additional generation parameters
39
+ # @option params [Integer] :dimensions Desired dimensionality of the embeddings
40
+ # @option params [String] :task_type The embedding task type
41
+ # @option params [Integer] :batch_size Maximum number of texts to process in one batch
42
+ # @return [Geminize::Models::EmbeddingResponse] The embedding response
43
+ # @raise [Geminize::GeminizeError] If the request fails
44
+ def generate_embedding(text, model_name = nil, params = {})
45
+ model = model_name || Geminize.configuration.default_embedding_model
46
+
47
+ # Check if we need to handle batching
48
+ if text.is_a?(Array) && text.length > 1
49
+ batch_size = params.delete(:batch_size) || DEFAULT_MAX_BATCH_SIZE
50
+ if text.length > batch_size
51
+ return batch_process_embeddings(text, model, params, batch_size)
52
+ end
53
+ end
54
+
55
+ # Regular processing for a single text or a small batch
56
+ embedding_request = Models::EmbeddingRequest.new(
57
+ model_name: model,
58
+ text: text,
59
+ task_type: params[:task_type] || Geminize::Models::EmbeddingRequest::RETRIEVAL_DOCUMENT,
60
+ params: params
61
+ )
62
+
63
+ generate(embedding_request)
64
+ end
65
+
66
+ # Process multiple texts in batches
67
+ # @param texts [Array<String>] List of texts to process
68
+ # @param model_name [String, nil] Model name
69
+ # @param params [Hash] Additional parameters
70
+ # @param batch_size [Integer] Maximum batch size
71
+ # @return [Geminize::Models::EmbeddingResponse] Combined embedding response
72
+ def batch_process_embeddings(texts, model_name, params, batch_size)
73
+ batches = texts.each_slice(batch_size).to_a
74
+ responses = []
75
+
76
+ batches.each do |batch|
77
+ request = Models::EmbeddingRequest.new(
78
+ model_name: model_name,
79
+ text: batch,
80
+ task_type: params[:task_type] || Geminize::Models::EmbeddingRequest::RETRIEVAL_DOCUMENT,
81
+ params: params
82
+ )
83
+ responses << generate(request)
84
+ end
85
+
86
+ # Combine all responses into a single response object
87
+ combine_responses(responses)
88
+ end
89
+
90
+ private
91
+
92
+ # Combine multiple embedding responses into a single response
93
+ # @param responses [Array<Geminize::Models::EmbeddingResponse>] List of responses
94
+ # @return [Geminize::Models::EmbeddingResponse] Combined response
95
+ def combine_responses(responses)
96
+ return responses.first if responses.length == 1
97
+
98
+ # Create a synthesized response hash
99
+ combined_hash = {
100
+ "embeddings" => [],
101
+ "usageMetadata" => {
102
+ "promptTokenCount" => 0,
103
+ "totalTokenCount" => 0
104
+ }
105
+ }
106
+
107
+ # Merge all embeddings and usage data
108
+ responses.each do |response|
109
+ # Add embeddings
110
+ if response.raw_response["embeddings"]
111
+ combined_hash["embeddings"].concat(response.raw_response["embeddings"])
112
+ end
113
+
114
+ # Aggregate usage data
115
+ if response.usage
116
+ combined_hash["usageMetadata"]["promptTokenCount"] += response.prompt_tokens || 0
117
+ combined_hash["usageMetadata"]["totalTokenCount"] += response.total_tokens || 0
118
+ end
119
+ end
120
+
121
+ # Create a new response object
122
+ Models::EmbeddingResponse.from_hash(combined_hash)
123
+ end
124
+
125
+ # Build the endpoint for the embeddings API based on the model name
126
+ # @param model_name [String] The name of the model to use for embeddings
127
+ # @param batch [Boolean] Whether this is a batch request
128
+ # @return [String] The endpoint URL
129
+ def build_embedding_endpoint(model_name, batch = false)
130
+ # Clean model name - remove 'models/' prefix if present
131
+ clean_model_name = model_name.start_with?("models/") ? model_name[7..] : model_name
132
+
133
+ if batch
134
+ # For text-embedding models, use a different format
135
+ if clean_model_name.start_with?("text-embedding-")
136
+ "#{clean_model_name}:batchEmbedContents"
137
+ else
138
+ "models/#{clean_model_name}:batchEmbedContents"
139
+ end
140
+ else
141
+ "models/#{clean_model_name}:embedContent"
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ # Maps API error responses to appropriate exception classes
5
+ class ErrorMapper
6
+ # Maps an error response to the appropriate exception
7
+ # @param error_info [Hash] Hash containing error information (http_status, code, message)
8
+ # @return [Geminize::GeminizeError] An instance of the appropriate exception class
9
+ def self.map(error_info)
10
+ new(error_info).map
11
+ end
12
+
13
+ # @return [Hash] The error information hash
14
+ attr_reader :error_info
15
+
16
+ # Initialize a new mapper
17
+ # @param error_info [Hash] Hash containing error information (http_status, code, message)
18
+ def initialize(error_info)
19
+ @error_info = error_info
20
+ end
21
+
22
+ # Map the error to an appropriate exception
23
+ # @return [Geminize::GeminizeError] An instance of the appropriate exception class
24
+ def map
25
+ error_class = determine_error_class
26
+ error_class.new(
27
+ error_info[:message],
28
+ error_info[:code],
29
+ error_info[:http_status]
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ # Determine the appropriate error class based on status code and error type
36
+ # @return [Class] The exception class to instantiate
37
+ def determine_error_class
38
+ # First check for specific error codes from the API
39
+ api_error_class = map_api_error_code
40
+ return api_error_class if api_error_class
41
+
42
+ # Then fall back to HTTP status code mapping
43
+ http_status_class = map_http_status
44
+ return http_status_class if http_status_class
45
+
46
+ # Default to generic error
47
+ GeminizeError
48
+ end
49
+
50
+ # Map error based on API-specific error codes
51
+ # @return [Class, nil] The error class or nil if no mapping is found
52
+ def map_api_error_code
53
+ return nil unless error_info[:code]
54
+
55
+ code = error_info[:code].to_s.downcase
56
+ message = error_info[:message].to_s.downcase
57
+
58
+ if code.include?("permission") || code.include?("unauthorized") || code.include?("unauthenticated")
59
+ AuthenticationError
60
+ elsif code.include?("quota") || code.include?("rate") || code.include?("limit")
61
+ RateLimitError
62
+ elsif code.include?("not_found") || code.include?("notfound")
63
+ ResourceNotFoundError
64
+ elsif code.include?("invalid") && (code.include?("model") || message.include?("model"))
65
+ InvalidModelError
66
+ elsif code.include?("invalid") || code.include?("validation")
67
+ ValidationError
68
+ elsif code.include?("blocked") || message.include?("blocked") || message.include?("safety")
69
+ ContentBlockedError
70
+ elsif code.include?("server") || code.include?("internal")
71
+ ServerError
72
+ elsif code.include?("config")
73
+ ConfigurationError
74
+ end
75
+ end
76
+
77
+ # Map error based on HTTP status code
78
+ # @return [Class] The error class
79
+ def map_http_status
80
+ status = error_info[:http_status]
81
+
82
+ case status
83
+ when 400
84
+ BadRequestError
85
+ when 401, 403
86
+ AuthenticationError
87
+ when 404
88
+ ResourceNotFoundError
89
+ when 429
90
+ RateLimitError
91
+ when 500..599
92
+ ServerError
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Geminize
6
+ # Utility class for parsing error responses from the Gemini API
7
+ class ErrorParser
8
+ # Parse an error response and extract relevant information
9
+ # @param response [Faraday::Response] The response object
10
+ # @return [Hash] Hash containing extracted error information
11
+ def self.parse(response)
12
+ new(response).parse
13
+ end
14
+
15
+ # @return [Faraday::Response] The response object
16
+ attr_reader :response
17
+
18
+ # Initialize a new parser
19
+ # @param response [Faraday::Response] The response object
20
+ def initialize(response)
21
+ @response = response
22
+ end
23
+
24
+ # Parse the response and extract error information
25
+ # @return [Hash] Hash containing error code, message, and status
26
+ def parse
27
+ {
28
+ http_status: response.status,
29
+ code: extract_error_code,
30
+ message: extract_error_message
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ # Extract the error code from the response
37
+ # @return [String, nil] The error code or nil if not present
38
+ def extract_error_code
39
+ return nil unless parsed_body && parsed_body["error"]
40
+
41
+ if parsed_body["error"].is_a?(Hash)
42
+ code = parsed_body["error"]["code"] || parsed_body["error"]["status"]
43
+ code&.to_s # Use safe navigation operator
44
+ elsif parsed_body["error"].is_a?(String)
45
+ # Extract error code from string if possible
46
+ parsed_body["error"].scan(/code[:\s]+([A-Z_]+)/i).flatten.first
47
+ end
48
+ end
49
+
50
+ # Extract the error message from the response
51
+ # @return [String] The error message
52
+ def extract_error_message
53
+ return default_error_message unless parsed_body
54
+
55
+ if parsed_body["error"].is_a?(Hash)
56
+ if parsed_body["error"]["message"]
57
+ parsed_body["error"]["message"]
58
+ else
59
+ detailed_message = extract_detailed_error_message
60
+ detailed_message || default_error_message
61
+ end
62
+ elsif parsed_body["error"].is_a?(String)
63
+ parsed_body["error"]
64
+ else
65
+ default_error_message
66
+ end
67
+ end
68
+
69
+ # Extract a detailed error message from nested error structures
70
+ # @return [String, nil] Detailed error message if present
71
+ def extract_detailed_error_message
72
+ return nil unless parsed_body["error"].is_a?(Hash)
73
+
74
+ details = parsed_body["error"]["details"]
75
+ return nil unless details.is_a?(Array) && !details.empty?
76
+
77
+ # Try to extract messages from details
78
+ messages = details.map do |detail|
79
+ if detail.is_a?(Hash) && detail["@type"] && detail["@type"].include?("type.googleapis.com")
80
+ detail["detail"] || detail["description"] || detail["message"]
81
+ end
82
+ end.compact
83
+
84
+ messages.join(". ") unless messages.empty?
85
+ end
86
+
87
+ # Parses the JSON response body
88
+ # @return [Hash, nil] The parsed JSON or nil if parsing fails
89
+ def parsed_body
90
+ @parsed_body ||= begin
91
+ return nil if response.body.to_s.empty?
92
+
93
+ JSON.parse(response.body)
94
+ rescue JSON::ParserError
95
+ nil
96
+ end
97
+ end
98
+
99
+ # Generate a default error message based on HTTP status
100
+ # @return [String] A default error message
101
+ def default_error_message
102
+ case response.status
103
+ when 400
104
+ "Bad Request: The server could not process the request"
105
+ when 401
106
+ "Unauthorized: Authentication is required or has failed"
107
+ when 403
108
+ "Forbidden: You don't have permission to access this resource"
109
+ when 404
110
+ "Not Found: The requested resource could not be found"
111
+ when 429
112
+ "Too Many Requests: Rate limit exceeded"
113
+ when 500..599
114
+ "Server Error: The server encountered an error (#{response.status})"
115
+ else
116
+ "Error: An unexpected error occurred (HTTP #{response.status})"
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geminize
4
+ # Base error class for all Geminize errors
5
+ class GeminizeError < StandardError
6
+ # @return [String] The error message
7
+ attr_reader :message
8
+
9
+ # @return [String, nil] The error code from the API response
10
+ attr_reader :code
11
+
12
+ # @return [Integer, nil] The HTTP status code
13
+ attr_reader :http_status
14
+
15
+ # Initialize a new error
16
+ # @param message [String] The error message
17
+ # @param code [String, nil] The error code from the API response
18
+ # @param http_status [Integer, nil] The HTTP status code
19
+ def initialize(message = nil, code = nil, http_status = nil)
20
+ @message = message || "An error occurred with the Geminize API"
21
+ @code = code
22
+ @http_status = http_status
23
+ super(@message)
24
+ end
25
+ end
26
+
27
+ # Error raised when there's an authentication issue
28
+ class AuthenticationError < GeminizeError
29
+ def initialize(message = nil, code = nil, http_status = 401)
30
+ super(
31
+ message || "Authentication failed. Please check your API key.",
32
+ code,
33
+ http_status
34
+ )
35
+ end
36
+ end
37
+
38
+ # Error raised when rate limits are exceeded
39
+ class RateLimitError < GeminizeError
40
+ def initialize(message = nil, code = nil, http_status = 429)
41
+ super(
42
+ message || "Rate limit exceeded. Please retry after some time.",
43
+ code,
44
+ http_status
45
+ )
46
+ end
47
+ end
48
+
49
+ # Error raised for client-side errors (4xx)
50
+ class BadRequestError < GeminizeError
51
+ def initialize(message = nil, code = nil, http_status = 400)
52
+ super(
53
+ message || "Invalid request. Please check your parameters.",
54
+ code,
55
+ http_status
56
+ )
57
+ end
58
+ end
59
+
60
+ # Error raised for server-side errors (5xx)
61
+ class ServerError < GeminizeError
62
+ def initialize(message = nil, code = nil, http_status = 500)
63
+ super(
64
+ message || "The Gemini API encountered a server error.",
65
+ code,
66
+ http_status
67
+ )
68
+ end
69
+ end
70
+
71
+ # Error raised for network/request issues
72
+ class RequestError < GeminizeError
73
+ def initialize(message = nil, code = nil, http_status = nil)
74
+ super(
75
+ message || "There was an error making the request to the Gemini API.",
76
+ code,
77
+ http_status
78
+ )
79
+ end
80
+ end
81
+
82
+ # Error raised when a resource is not found
83
+ class ResourceNotFoundError < BadRequestError
84
+ def initialize(message = nil, code = nil, http_status = 404)
85
+ super(
86
+ message || "The requested resource was not found.",
87
+ code,
88
+ http_status
89
+ )
90
+ end
91
+ end
92
+
93
+ # Error raised when a requested model is invalid or not available
94
+ class InvalidModelError < BadRequestError
95
+ def initialize(message = nil, code = nil, http_status = 400)
96
+ super(
97
+ message || "The specified model is invalid or not available.",
98
+ code,
99
+ http_status
100
+ )
101
+ end
102
+ end
103
+
104
+ # Error raised for validation errors
105
+ class ValidationError < BadRequestError
106
+ def initialize(message = nil, code = nil, http_status = 400)
107
+ super(
108
+ message || "Validation failed. Please check your input parameters.",
109
+ code,
110
+ http_status
111
+ )
112
+ end
113
+ end
114
+
115
+ # Error raised when the content is blocked by the safety filters
116
+ class ContentBlockedError < BadRequestError
117
+ def initialize(message = nil, code = nil, http_status = 400)
118
+ super(
119
+ message || "Content blocked by safety filters.",
120
+ code,
121
+ http_status
122
+ )
123
+ end
124
+ end
125
+
126
+ # Error raised when there are configuration issues
127
+ class ConfigurationError < GeminizeError
128
+ def initialize(message = nil, code = nil, http_status = nil)
129
+ super(
130
+ message || "Configuration error. Please check your Geminize configuration.",
131
+ code,
132
+ http_status
133
+ )
134
+ end
135
+ end
136
+
137
+ # Error specific to streaming operations
138
+ class StreamingError < GeminizeError
139
+ def initialize(message = nil, code = nil, http_status = nil)
140
+ super(
141
+ message || "Streaming operation failed",
142
+ code || "STREAMING_ERROR",
143
+ http_status
144
+ )
145
+ end
146
+ end
147
+
148
+ # Error during connection interrupted during streaming
149
+ class StreamingInterruptedError < StreamingError
150
+ def initialize(message = nil, code = nil, http_status = nil)
151
+ super(
152
+ message || "Streaming connection was interrupted",
153
+ code || "STREAMING_INTERRUPTED",
154
+ http_status
155
+ )
156
+ end
157
+ end
158
+
159
+ # Error when the stream format is invalid
160
+ class InvalidStreamFormatError < StreamingError
161
+ def initialize(message = nil, code = nil, http_status = 400)
162
+ super(
163
+ message || "Invalid stream format or response structure",
164
+ code || "INVALID_STREAM_FORMAT",
165
+ http_status
166
+ )
167
+ end
168
+ end
169
+
170
+ # Error for timeout during streaming
171
+ class StreamingTimeoutError < StreamingError
172
+ def initialize(message = nil, code = nil, http_status = 408)
173
+ super(
174
+ message || "Streaming operation timed out",
175
+ code || "STREAMING_TIMEOUT",
176
+ http_status
177
+ )
178
+ end
179
+ end
180
+
181
+ # Error raised when a requested resource is not found (alias for ResourceNotFoundError)
182
+ class NotFoundError < ResourceNotFoundError
183
+ # Uses the same implementation as ResourceNotFoundError
184
+ end
185
+ end