geminize 0.1.1 → 1.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/mcp.json +3 -0
  3. data/.cursor/rules/isolation_rules/Core/command-execution.mdc +235 -0
  4. data/.cursor/rules/isolation_rules/Core/complexity-decision-tree.mdc +187 -0
  5. data/.cursor/rules/isolation_rules/Core/creative-phase-enforcement.mdc +145 -0
  6. data/.cursor/rules/isolation_rules/Core/creative-phase-metrics.mdc +195 -0
  7. data/.cursor/rules/isolation_rules/Core/file-verification.mdc +198 -0
  8. data/.cursor/rules/isolation_rules/Core/platform-awareness.mdc +71 -0
  9. data/.cursor/rules/isolation_rules/Level3/planning-comprehensive.mdc +159 -0
  10. data/.cursor/rules/isolation_rules/Level3/task-tracking-intermediate.mdc +135 -0
  11. data/.cursor/rules/isolation_rules/Phases/CreativePhase/creative-phase-architecture.mdc +187 -0
  12. data/.cursor/rules/isolation_rules/main.mdc +123 -0
  13. data/.cursor/rules/isolation_rules/visual-maps/archive-mode-map.mdc +277 -0
  14. data/.cursor/rules/isolation_rules/visual-maps/creative-mode-map.mdc +224 -0
  15. data/.cursor/rules/isolation_rules/visual-maps/implement-mode-map.mdc +321 -0
  16. data/.cursor/rules/isolation_rules/visual-maps/plan-mode-map.mdc +269 -0
  17. data/.cursor/rules/isolation_rules/visual-maps/qa-mode-map.mdc +495 -0
  18. data/.cursor/rules/isolation_rules/visual-maps/reflect-mode-map.mdc +234 -0
  19. data/.cursor/rules/isolation_rules/visual-maps/van-mode-map.mdc +902 -0
  20. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-complexity-determination.mdc +60 -0
  21. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-file-verification.mdc +49 -0
  22. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-mode-map.mdc +49 -0
  23. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-platform-detection.mdc +50 -0
  24. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-checks/build-test.mdc +117 -0
  25. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-checks/config-check.mdc +103 -0
  26. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-checks/dependency-check.mdc +147 -0
  27. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-checks/environment-check.mdc +104 -0
  28. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-checks/file-verification.mdc +1 -0
  29. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-main.mdc +142 -0
  30. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-utils/common-fixes.mdc +92 -0
  31. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-utils/mode-transitions.mdc +101 -0
  32. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-utils/reports.mdc +149 -0
  33. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-utils/rule-calling-guide.mdc +66 -0
  34. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-utils/rule-calling-help.mdc +19 -0
  35. data/.cursor/rules/isolation_rules/visual-maps/van_mode_split/van-qa-validation.md.old +363 -0
  36. data/.env.example +7 -0
  37. data/.memory_bank/activeContext.md +78 -0
  38. data/.memory_bank/progress.md +80 -0
  39. data/.memory_bank/projectbrief.md +29 -0
  40. data/.memory_bank/systemPatterns.md +90 -0
  41. data/.memory_bank/tasks.md +98 -0
  42. data/.memory_bank/techContext.md +73 -0
  43. data/.tool-versions +1 -0
  44. data/CHANGELOG.md +27 -1
  45. data/README.md +34 -98
  46. data/examples/README.md +7 -28
  47. data/examples/embeddings.rb +18 -20
  48. data/examples/models_api.rb +125 -0
  49. data/examples/multimodal.rb +9 -9
  50. data/lib/geminize/configuration.rb +4 -4
  51. data/lib/geminize/model_info.rb +87 -8
  52. data/lib/geminize/models/content_request.rb +1 -1
  53. data/lib/geminize/models/model.rb +101 -109
  54. data/lib/geminize/models/model_list.rb +70 -28
  55. data/lib/geminize/request_builder.rb +29 -0
  56. data/lib/geminize/version.rb +1 -1
  57. data/lib/geminize.rb +71 -17
  58. metadata +46 -20
  59. data/examples/rails_chat/README.md +0 -69
  60. data/examples/rails_chat/app/controllers/chat_controller.rb +0 -26
  61. data/examples/rails_chat/app/views/chat/index.html.erb +0 -112
  62. data/examples/rails_chat/config/routes.rb +0 -8
  63. data/examples/rails_initializer.rb +0 -46
  64. data/geminize-0.1.0.gem +0 -0
  65. data/lib/geminize/rails/app/controllers/concerns/geminize/controller.rb +0 -105
  66. data/lib/geminize/rails/app/helpers/geminize_helper.rb +0 -125
  67. data/lib/geminize/rails/controller_additions.rb +0 -41
  68. data/lib/geminize/rails/engine.rb +0 -29
  69. data/lib/geminize/rails/helper_additions.rb +0 -37
  70. data/lib/geminize/rails.rb +0 -50
  71. data/lib/geminize/railtie.rb +0 -33
  72. data/lib/generators/geminize/install_generator.rb +0 -22
  73. data/lib/generators/geminize/templates/README +0 -31
  74. data/lib/generators/geminize/templates/initializer.rb +0 -38
@@ -20,7 +20,7 @@ begin
20
20
  # Generate content with an image from a file
21
21
  response = Geminize.generate_text_multimodal(
22
22
  "Describe this image in detail:",
23
- [{source_type: "file", data: "./examples/ruby.png"}] # Updated path
23
+ [{source_type: "file", data: File.join(File.dirname(__FILE__), "ruby.png")}] # Use relative path from script
24
24
  )
25
25
 
26
26
  puts "Response:"
@@ -28,7 +28,7 @@ begin
28
28
  puts "\nFinish reason: #{response.finish_reason}"
29
29
  rescue => e
30
30
  puts "Error: #{e.message}"
31
- puts "Make sure the path './examples/ruby.png' is correct relative to the project root."
31
+ puts "Make sure the path './ruby.png' is correct relative to the project root."
32
32
  end
33
33
 
34
34
  puts "\n============================================================"
@@ -60,8 +60,8 @@ begin
60
60
  response = Geminize.generate_text_multimodal(
61
61
  "Compare these two images and describe the differences (they might be the same):",
62
62
  [
63
- {source_type: "file", data: "./examples/ruby.png"}, # Updated path
64
- {source_type: "file", data: "./examples/ruby.png"} # Updated path
63
+ {source_type: "file", data: File.join(File.dirname(__FILE__), "ruby.png")}, # Use relative path from script
64
+ {source_type: "file", data: File.join(File.dirname(__FILE__), "ruby.png")} # Use relative path from script
65
65
  ],
66
66
  "gemini-1.5-pro-latest", # Explicitly specify model
67
67
  max_tokens: 500
@@ -71,7 +71,7 @@ begin
71
71
  puts response.text
72
72
  rescue => e
73
73
  puts "Error: #{e.message}"
74
- puts "Make sure the path './examples/ruby.png' is correct."
74
+ puts "Make sure the path './ruby.png' is correct."
75
75
  end
76
76
 
77
77
  puts "\n============================================================"
@@ -80,7 +80,7 @@ puts "============================================================"
80
80
 
81
81
  begin
82
82
  # Read image directly into bytes
83
- image_bytes = File.binread("./examples/ruby.png") # Updated path
83
+ image_bytes = File.binread(File.join(File.dirname(__FILE__), "ruby.png")) # Use relative path from script
84
84
 
85
85
  # Generate content with raw image bytes
86
86
  response = Geminize.generate_text_multimodal(
@@ -92,7 +92,7 @@ begin
92
92
  puts response.text
93
93
  rescue => e
94
94
  puts "Error: #{e.message}"
95
- puts "Make sure the path './examples/ruby.png' is correct."
95
+ puts "Make sure the path './ruby.png' is correct."
96
96
  end
97
97
 
98
98
  puts "\n============================================================"
@@ -112,7 +112,7 @@ begin
112
112
  )
113
113
 
114
114
  # Add multiple images using different methods
115
- request.add_image_from_file("./examples/ruby.png") # Updated path
115
+ request.add_image_from_file(File.join(File.dirname(__FILE__), "ruby.png")) # Use relative path from script
116
116
  request.add_image_from_url("https://miro.medium.com/v2/resize:fit:720/format:webp/1*zkA1cWgJDlMUxI5TRcIHdQ.jpeg") # Updated URL
117
117
 
118
118
  # Generate the response
@@ -122,7 +122,7 @@ begin
122
122
  puts response.text
123
123
 
124
124
  # Check if usage data is available before accessing it
125
- if response.usage && response.usage.total_tokens
125
+ if response.usage&.total_tokens
126
126
  puts "\nUsed #{response.usage.total_tokens} tokens total"
127
127
  else
128
128
  puts "\nUsage data not available in the response."
@@ -17,7 +17,7 @@ module Geminize
17
17
  DEFAULT_MODEL = "gemini-2.0-flash"
18
18
 
19
19
  # Default embedding model
20
- DEFAULT_EMBEDDING_MODEL = "embedding-001"
20
+ DEFAULT_EMBEDDING_MODEL = "gemini-embedding-exp-03-07"
21
21
 
22
22
  # Default timeout values (in seconds)
23
23
  DEFAULT_TIMEOUT = 30
@@ -69,9 +69,9 @@ module Geminize
69
69
  # @return [void]
70
70
  def reset!
71
71
  @api_key = ENV["GEMINI_API_KEY"]
72
- @api_version = DEFAULT_API_VERSION
73
- @default_model = DEFAULT_MODEL
74
- @default_embedding_model = DEFAULT_EMBEDDING_MODEL
72
+ @api_version = ENV["GEMINI_API_VERSION"] || DEFAULT_API_VERSION
73
+ @default_model = ENV["GEMINI_MODEL"] || DEFAULT_MODEL
74
+ @default_embedding_model = ENV["GEMINI_EMBEDDING_MODEL"] || DEFAULT_EMBEDDING_MODEL
75
75
  @timeout = DEFAULT_TIMEOUT
76
76
  @open_timeout = DEFAULT_OPEN_TIMEOUT
77
77
  @streaming_timeout = DEFAULT_STREAMING_TIMEOUT
@@ -9,6 +9,9 @@ module Geminize
9
9
  # @return [Geminize::Client] The HTTP client
10
10
  attr_reader :client
11
11
 
12
+ # Default page size for listing models
13
+ DEFAULT_PAGE_SIZE = 50
14
+
12
15
  # Initialize a new ModelInfo instance
13
16
  # @param client [Geminize::Client, nil] The HTTP client to use
14
17
  # @param options [Hash] Additional options for the client
@@ -20,19 +23,26 @@ module Geminize
20
23
  end
21
24
 
22
25
  # List available models from the Gemini API
26
+ # @param page_size [Integer, nil] Number of models to return per page (max 1000)
27
+ # @param page_token [String, nil] Token for retrieving a specific page
23
28
  # @param force_refresh [Boolean] Force a refresh from the API instead of using cache
24
29
  # @return [Geminize::Models::ModelList] List of available models
25
30
  # @raise [Geminize::GeminizeError] If the request fails
26
- def list_models(force_refresh: false)
27
- cache_key = "models_list"
31
+ def list_models(page_size: nil, page_token: nil, force_refresh: false)
32
+ cache_key = "models_list_#{page_size}_#{page_token}"
28
33
 
29
34
  # Check if we have a valid cached result
30
35
  if !force_refresh && @cache[cache_key] && @cache_expiry[cache_key] > Time.now
31
36
  return @cache[cache_key]
32
37
  end
33
38
 
39
+ # Prepare query parameters
40
+ params = {}
41
+ params[:pageSize] = page_size if page_size
42
+ params[:pageToken] = page_token
43
+
34
44
  # Make the API request
35
- response = client.get("models")
45
+ response = client.get("models", params)
36
46
 
37
47
  # Create a ModelList from the response
38
48
  model_list = Models::ModelList.from_api_data(response)
@@ -44,13 +54,62 @@ module Geminize
44
54
  model_list
45
55
  end
46
56
 
57
+ # Get all available models, handling pagination automatically
58
+ # @param force_refresh [Boolean] Force a refresh from the API instead of using cache
59
+ # @return [Geminize::Models::ModelList] Complete list of all available models
60
+ # @raise [Geminize::GeminizeError] If any request fails
61
+ def list_all_models(force_refresh: false)
62
+ cache_key = "all_models_list"
63
+
64
+ # Check if we have a valid cached result
65
+ if !force_refresh && @cache[cache_key] && @cache_expiry[cache_key] > Time.now
66
+ return @cache[cache_key]
67
+ end
68
+
69
+ all_models = []
70
+ page_token = nil
71
+
72
+ # Fetch first page
73
+ model_list = list_models(
74
+ page_size: DEFAULT_PAGE_SIZE,
75
+ page_token: page_token,
76
+ force_refresh: force_refresh
77
+ )
78
+ all_models.concat(model_list.models)
79
+
80
+ # Fetch additional pages if available
81
+ while model_list.has_more_pages?
82
+ page_token = model_list.next_page_token
83
+ model_list = list_models(
84
+ page_size: DEFAULT_PAGE_SIZE,
85
+ page_token: page_token,
86
+ force_refresh: force_refresh
87
+ )
88
+ all_models.concat(model_list.models)
89
+ end
90
+
91
+ # Create a consolidated model list
92
+ result = Models::ModelList.new(all_models)
93
+
94
+ # Cache the result
95
+ @cache[cache_key] = result
96
+ @cache_expiry[cache_key] = Time.now + @cache_ttl
97
+
98
+ result
99
+ end
100
+
47
101
  # Get information about a specific model
48
- # @param model_id [String] The model ID to retrieve
102
+ # @param model_name [String] The model name to retrieve (models/{model})
49
103
  # @param force_refresh [Boolean] Force a refresh from the API instead of using cache
50
104
  # @return [Geminize::Models::Model] The model information
51
105
  # @raise [Geminize::GeminizeError] If the request fails or model is not found
52
- def get_model(model_id, force_refresh: false)
53
- cache_key = "model_#{model_id}"
106
+ def get_model(model_name, force_refresh: false)
107
+ # Handle both formats: "models/gemini-1.5-pro" or just "gemini-1.5-pro"
108
+ unless model_name.start_with?("models/")
109
+ model_name = "models/#{model_name}"
110
+ end
111
+
112
+ cache_key = "model_#{model_name}"
54
113
 
55
114
  # Check if we have a valid cached result
56
115
  if !force_refresh && @cache[cache_key] && @cache_expiry[cache_key] > Time.now
@@ -59,7 +118,7 @@ module Geminize
59
118
 
60
119
  # Make the API request
61
120
  begin
62
- response = client.get("models/#{model_id}")
121
+ response = client.get(model_name)
63
122
 
64
123
  # Create a Model from the response
65
124
  model = Models::Model.from_api_data(response)
@@ -71,10 +130,30 @@ module Geminize
71
130
  model
72
131
  rescue Geminize::NotFoundError => e
73
132
  # Re-raise with a more descriptive message
74
- raise Geminize::NotFoundError.new("Model '#{model_id}' not found", e.code, e.http_status)
133
+ raise Geminize::NotFoundError.new("Model '#{model_name}' not found", e.code, e.http_status)
75
134
  end
76
135
  end
77
136
 
137
+ # Get models that support a specific generation method
138
+ # @param method [String] The generation method (e.g., "generateContent", "embedContent")
139
+ # @param force_refresh [Boolean] Force a refresh from the API instead of using cache
140
+ # @return [Geminize::Models::ModelList] List of models that support the method
141
+ # @raise [Geminize::GeminizeError] If the request fails
142
+ def get_models_by_method(method, force_refresh: false)
143
+ all_models = list_all_models(force_refresh: force_refresh)
144
+ all_models.filter_by_method(method)
145
+ end
146
+
147
+ # Get models by base model ID
148
+ # @param base_model_id [String] The base model ID to filter by
149
+ # @param force_refresh [Boolean] Force a refresh from the API instead of using cache
150
+ # @return [Geminize::Models::ModelList] List of models with the specified base model ID
151
+ # @raise [Geminize::GeminizeError] If the request fails
152
+ def get_models_by_base_id(base_model_id, force_refresh: false)
153
+ all_models = list_all_models(force_refresh: force_refresh)
154
+ all_models.filter_by_base_model_id(base_model_id)
155
+ end
156
+
78
157
  # Clear all cached model information
79
158
  # @return [void]
80
159
  def clear_cache
@@ -214,7 +214,7 @@ module Geminize
214
214
  # Map content_parts to the structure the API expects
215
215
  api_parts = @content_parts.map do |part|
216
216
  if part[:type] == "text"
217
- { text: part[:text] }
217
+ {text: part[:text]}
218
218
  elsif part[:type] == "image"
219
219
  {
220
220
  inlineData: {
@@ -4,69 +4,131 @@ module Geminize
4
4
  module Models
5
5
  # Represents an AI model from the Gemini API.
6
6
  class Model
7
- # @return [String] The unique identifier for the model
8
- attr_reader :id
9
-
10
- # @return [String] The display name of the model
7
+ # @return [String] The resource name of the model
11
8
  attr_reader :name
12
9
 
10
+ # @return [String] The base model ID
11
+ attr_reader :base_model_id
12
+
13
13
  # @return [String] The model version
14
14
  attr_reader :version
15
15
 
16
+ # @return [String] The display name of the model
17
+ attr_reader :display_name
18
+
16
19
  # @return [String] The model description
17
20
  attr_reader :description
18
21
 
19
- # @return [Array<String>] List of supported capabilities (e.g., 'text', 'vision', 'embedding')
20
- attr_reader :capabilities
22
+ # @return [Integer] Maximum number of input tokens allowed
23
+ attr_reader :input_token_limit
24
+
25
+ # @return [Integer] Maximum number of output tokens available
26
+ attr_reader :output_token_limit
21
27
 
22
- # @return [Hash] Model limitations and constraints
23
- attr_reader :limitations
28
+ # @return [Array<String>] Supported generation methods
29
+ attr_reader :supported_generation_methods
24
30
 
25
- # @return [Array<String>] Recommended use cases for this model
26
- attr_reader :use_cases
31
+ # @return [Float] Default temperature
32
+ attr_reader :temperature
33
+
34
+ # @return [Float] Maximum allowed temperature
35
+ attr_reader :max_temperature
36
+
37
+ # @return [Float] Default top_p value for nucleus sampling
38
+ attr_reader :top_p
39
+
40
+ # @return [Integer] Default top_k value for sampling
41
+ attr_reader :top_k
27
42
 
28
43
  # @return [Hash] Raw model data from the API
29
44
  attr_reader :raw_data
30
45
 
31
46
  # Create a new Model instance
32
47
  # @param attributes [Hash] Model attributes
33
- # @option attributes [String] :id The model ID
34
- # @option attributes [String] :name The model name
48
+ # @option attributes [String] :name The resource name of the model
49
+ # @option attributes [String] :base_model_id The base model ID
35
50
  # @option attributes [String] :version The model version
51
+ # @option attributes [String] :display_name The display name of the model
36
52
  # @option attributes [String] :description The model description
37
- # @option attributes [Array<String>] :capabilities List of capabilities
38
- # @option attributes [Hash] :limitations Model limitations
39
- # @option attributes [Array<String>] :use_cases Recommended use cases
53
+ # @option attributes [Integer] :input_token_limit Maximum input tokens
54
+ # @option attributes [Integer] :output_token_limit Maximum output tokens
55
+ # @option attributes [Array<String>] :supported_generation_methods Supported methods
56
+ # @option attributes [Float] :temperature Default temperature
57
+ # @option attributes [Float] :max_temperature Maximum temperature
58
+ # @option attributes [Float] :top_p Default top_p value
59
+ # @option attributes [Integer] :top_k Default top_k value
40
60
  # @option attributes [Hash] :raw_data Raw model data from API
41
61
  def initialize(attributes = {})
42
- @id = attributes[:id]
43
62
  @name = attributes[:name]
63
+ @base_model_id = attributes[:base_model_id]
44
64
  @version = attributes[:version]
65
+ @display_name = attributes[:display_name]
45
66
  @description = attributes[:description]
46
- @capabilities = attributes[:capabilities] || []
47
- @limitations = attributes[:limitations] || {}
48
- @use_cases = attributes[:use_cases] || []
67
+ @input_token_limit = attributes[:input_token_limit]
68
+ @output_token_limit = attributes[:output_token_limit]
69
+ @supported_generation_methods = attributes[:supported_generation_methods] || []
70
+ @temperature = attributes[:temperature]
71
+ @max_temperature = attributes[:max_temperature]
72
+ @top_p = attributes[:top_p]
73
+ @top_k = attributes[:top_k]
49
74
  @raw_data = attributes[:raw_data] || {}
50
75
  end
51
76
 
52
- # Check if model supports a specific capability
53
- # @param capability [String] Capability to check for
54
- # @return [Boolean] True if the model supports the capability
55
- def supports?(capability)
56
- capabilities.include?(capability.to_s.downcase)
77
+ # Shorthand accessor for the model ID (last part of the name path)
78
+ # @return [String] The model ID
79
+ def id
80
+ return nil unless @name
81
+ @name.split("/").last
82
+ end
83
+
84
+ # Check if model supports a specific generation method
85
+ # @param method [String] Generation method to check for
86
+ # @return [Boolean] True if the model supports the method
87
+ def supports_method?(method)
88
+ supported_generation_methods.include?(method.to_s)
89
+ end
90
+
91
+ # Check if model supports content generation
92
+ # @return [Boolean] True if the model supports content generation
93
+ def supports_content_generation?
94
+ supports_method?("generateContent")
95
+ end
96
+
97
+ # Check if model supports message generation (chat)
98
+ # @return [Boolean] True if the model supports message generation
99
+ def supports_message_generation?
100
+ supports_method?("generateMessage")
101
+ end
102
+
103
+ # Check if model supports embedding generation
104
+ # @return [Boolean] True if the model supports embedding generation
105
+ def supports_embedding?
106
+ supports_method?("embedContent")
107
+ end
108
+
109
+ # Check if model supports streaming content generation
110
+ # @return [Boolean] True if the model supports streaming content generation
111
+ def supports_streaming?
112
+ supports_method?("streamGenerateContent")
57
113
  end
58
114
 
59
115
  # Convert model to a hash representation
60
116
  # @return [Hash] Hash representation of the model
61
117
  def to_h
62
118
  {
63
- id: id,
64
119
  name: name,
120
+ id: id,
121
+ base_model_id: base_model_id,
65
122
  version: version,
123
+ display_name: display_name,
66
124
  description: description,
67
- capabilities: capabilities,
68
- limitations: limitations,
69
- use_cases: use_cases
125
+ input_token_limit: input_token_limit,
126
+ output_token_limit: output_token_limit,
127
+ supported_generation_methods: supported_generation_methods,
128
+ temperature: temperature,
129
+ max_temperature: max_temperature,
130
+ top_p: top_p,
131
+ top_k: top_k
70
132
  }
71
133
  end
72
134
 
@@ -80,92 +142,22 @@ module Geminize
80
142
  # @param data [Hash] Raw API response data
81
143
  # @return [Model] New Model instance
82
144
  def self.from_api_data(data)
83
- # Extract capabilities from model data
84
- capabilities = extract_capabilities(data)
85
-
86
- # Extract limitations from model data
87
- limitations = extract_limitations(data)
88
-
89
- # Extract use cases from model data
90
- use_cases = extract_use_cases(data)
91
-
92
145
  new(
93
- id: data["name"]&.split("/")&.last,
94
- name: data["displayName"],
95
- version: extract_version(data),
146
+ name: data["name"],
147
+ base_model_id: data["baseModelId"],
148
+ version: data["version"],
149
+ display_name: data["displayName"],
96
150
  description: data["description"],
97
- capabilities: capabilities,
98
- limitations: limitations,
99
- use_cases: use_cases,
151
+ input_token_limit: data["inputTokenLimit"],
152
+ output_token_limit: data["outputTokenLimit"],
153
+ supported_generation_methods: data["supportedGenerationMethods"] || [],
154
+ temperature: data["temperature"],
155
+ max_temperature: data["maxTemperature"],
156
+ top_p: data["topP"],
157
+ top_k: data["topK"],
100
158
  raw_data: data
101
159
  )
102
160
  end
103
-
104
- private_class_method def self.extract_version(data)
105
- # Extract version from model name or other fields
106
- # Example: if name is "gemini-1.0-pro", extract "1.0"
107
- if data["displayName"]
108
- match = data["displayName"].match(/[-_](\d+\.\d+)[-_]/)
109
- return match[1] if match
110
-
111
- # Try another pattern (e.g., "Gemini 1.5 Pro")
112
- match = data["displayName"].match(/\s(\d+\.\d+)\s/)
113
- return match[1] if match
114
- end
115
- nil
116
- end
117
-
118
- private_class_method def self.extract_capabilities(data)
119
- capabilities = []
120
-
121
- # Example capability extraction, adjust based on actual API response format
122
- capabilities << "text" if data.dig("supportedGenerationMethods")&.include?("generateText")
123
- capabilities << "chat" if data.dig("supportedGenerationMethods")&.include?("generateMessage")
124
- capabilities << "vision" if data.dig("supportedGenerationMethods")&.include?("generateContent") &&
125
- data.dig("inputSetting", "supportMultiModal")
126
- capabilities << "embedding" if data.dig("supportedGenerationMethods")&.include?("embedContent")
127
-
128
- capabilities
129
- end
130
-
131
- private_class_method def self.extract_limitations(data)
132
- limitations = {}
133
-
134
- # Extract token limits
135
- if data.dig("inputTokenLimit")
136
- limitations[:input_token_limit] = data["inputTokenLimit"]
137
- end
138
-
139
- if data.dig("outputTokenLimit")
140
- limitations[:output_token_limit] = data["outputTokenLimit"]
141
- end
142
-
143
- # Extract any other limitations from the API data
144
- limitations
145
- end
146
-
147
- private_class_method def self.extract_use_cases(data)
148
- # Extract use cases from the description or other fields
149
- # This is a simple implementation - adjust based on actual API data
150
- use_cases = []
151
-
152
- if data["description"]
153
- if data["description"].include?("chat")
154
- use_cases << "conversational_ai"
155
- end
156
-
157
- if data["description"].include?("vision") || data["description"].include?("image")
158
- use_cases << "image_understanding"
159
- end
160
-
161
- if data["description"].include?("embedding")
162
- use_cases << "semantic_search"
163
- use_cases << "clustering"
164
- end
165
- end
166
-
167
- use_cases
168
- end
169
161
  end
170
162
  end
171
163
  end
@@ -12,13 +12,18 @@ module Geminize
12
12
  # @return [Array<Model>] The list of models
13
13
  attr_reader :models
14
14
 
15
+ # @return [String, nil] Token for fetching the next page of results
16
+ attr_reader :next_page_token
17
+
15
18
  # Delegate array methods to the underlying models array
16
19
  def_delegators :@models, :[], :size, :length, :empty?, :first, :last
17
20
 
18
21
  # Create a new ModelList
19
22
  # @param models [Array<Model>] Initial list of models
20
- def initialize(models = [])
23
+ # @param next_page_token [String, nil] Token for fetching the next page
24
+ def initialize(models = [], next_page_token = nil)
21
25
  @models = models
26
+ @next_page_token = next_page_token
22
27
  end
23
28
 
24
29
  # Implement Enumerable's required each method
@@ -35,43 +40,50 @@ module Geminize
35
40
  self
36
41
  end
37
42
 
38
- # Find a model by its ID
43
+ # Find a model by its resource name
44
+ # @param name [String] The model name to search for
45
+ # @return [Model, nil] The found model or nil
46
+ def find_by_name(name)
47
+ @models.find { |model| model.name == name }
48
+ end
49
+
50
+ # Find a model by its ID (last part of the resource name)
39
51
  # @param id [String] The model ID to search for
40
52
  # @return [Model, nil] The found model or nil
41
53
  def find_by_id(id)
42
54
  @models.find { |model| model.id == id }
43
55
  end
44
56
 
45
- # Find all models that support a specific capability
46
- # @param capability [String] The capability to filter by
57
+ # Find all models that support a specific generation method
58
+ # @param method [String] The generation method to filter by
47
59
  # @return [ModelList] A new ModelList containing only matching models
48
- def filter_by_capability(capability)
49
- filtered = @models.select { |model| model.supports?(capability) }
50
- ModelList.new(filtered)
60
+ def filter_by_method(method)
61
+ filtered = @models.select { |model| model.supports_method?(method) }
62
+ ModelList.new(filtered, nil)
51
63
  end
52
64
 
53
- # Find all models that support vision capabilities
54
- # @return [ModelList] A new ModelList containing only vision-capable models
55
- def vision_models
56
- filter_by_capability("vision")
65
+ # Find all models that support content generation
66
+ # @return [ModelList] A new ModelList containing only content generation capable models
67
+ def content_generation_models
68
+ filter_by_method("generateContent")
57
69
  end
58
70
 
59
- # Find all models that support embedding capabilities
60
- # @return [ModelList] A new ModelList containing only embedding-capable models
61
- def embedding_models
62
- filter_by_capability("embedding")
71
+ # Find all models that support streaming content generation
72
+ # @return [ModelList] A new ModelList containing only streaming capable models
73
+ def streaming_models
74
+ filter_by_method("streamGenerateContent")
63
75
  end
64
76
 
65
- # Find all models that support text generation
66
- # @return [ModelList] A new ModelList containing only text generation models
67
- def text_models
68
- filter_by_capability("text")
77
+ # Find all models that support embeddings
78
+ # @return [ModelList] A new ModelList containing only embedding-capable models
79
+ def embedding_models
80
+ filter_by_method("embedContent")
69
81
  end
70
82
 
71
83
  # Find all models that support chat/conversation
72
84
  # @return [ModelList] A new ModelList containing only chat-capable models
73
85
  def chat_models
74
- filter_by_capability("chat")
86
+ filter_by_method("generateMessage")
75
87
  end
76
88
 
77
89
  # Filter models by version
@@ -79,16 +91,40 @@ module Geminize
79
91
  # @return [ModelList] A new ModelList containing only matching models
80
92
  def filter_by_version(version)
81
93
  filtered = @models.select { |model| model.version == version }
82
- ModelList.new(filtered)
94
+ ModelList.new(filtered, nil)
83
95
  end
84
96
 
85
- # Filter models by name pattern
86
- # @param pattern [String, Regexp] The pattern to match model names against
97
+ # Filter models by display name pattern
98
+ # @param pattern [String, Regexp] The pattern to match model display names against
87
99
  # @return [ModelList] A new ModelList containing only matching models
88
- def filter_by_name(pattern)
100
+ def filter_by_display_name(pattern)
89
101
  pattern = Regexp.new(pattern.to_s, Regexp::IGNORECASE) if pattern.is_a?(String)
90
- filtered = @models.select { |model| model.name&.match?(pattern) }
91
- ModelList.new(filtered)
102
+ filtered = @models.select { |model| model.display_name&.match?(pattern) }
103
+ ModelList.new(filtered, nil)
104
+ end
105
+
106
+ # Filter models by base model ID
107
+ # @param base_model_id [String] The base model ID to filter by
108
+ # @return [ModelList] A new ModelList containing only matching models
109
+ def filter_by_base_model_id(base_model_id)
110
+ filtered = @models.select { |model| model.base_model_id == base_model_id }
111
+ ModelList.new(filtered, nil)
112
+ end
113
+
114
+ # Find models with a minimum input token limit
115
+ # @param min_limit [Integer] The minimum input token limit
116
+ # @return [ModelList] A new ModelList containing only matching models
117
+ def filter_by_min_input_tokens(min_limit)
118
+ filtered = @models.select { |model| model.input_token_limit && model.input_token_limit >= min_limit }
119
+ ModelList.new(filtered, nil)
120
+ end
121
+
122
+ # Find models with a minimum output token limit
123
+ # @param min_limit [Integer] The minimum output token limit
124
+ # @return [ModelList] A new ModelList containing only matching models
125
+ def filter_by_min_output_tokens(min_limit)
126
+ filtered = @models.select { |model| model.output_token_limit && model.output_token_limit >= min_limit }
127
+ ModelList.new(filtered, nil)
92
128
  end
93
129
 
94
130
  # Create a ModelList from API response data
@@ -96,16 +132,22 @@ module Geminize
96
132
  # @return [ModelList] New ModelList instance
97
133
  def self.from_api_data(data)
98
134
  models = []
135
+ next_page_token = data["nextPageToken"]
99
136
 
100
137
  # Process model data from API response
101
- # The exact structure will depend on the Gemini API response format
102
138
  if data.key?("models")
103
139
  models = data["models"].map do |model_data|
104
140
  Model.from_api_data(model_data)
105
141
  end
106
142
  end
107
143
 
108
- new(models)
144
+ new(models, next_page_token)
145
+ end
146
+
147
+ # Check if there are more pages of results available
148
+ # @return [Boolean] True if there are more pages
149
+ def has_more_pages?
150
+ !next_page_token.nil? && !next_page_token.empty?
109
151
  end
110
152
 
111
153
  # Convert to array of hashes representation