exa-ai 0.3.1 → 0.4.1

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +58 -17
  3. data/exe/exa-ai +111 -6
  4. data/exe/exa-ai-enrichment-cancel +107 -0
  5. data/exe/exa-ai-enrichment-create +235 -0
  6. data/exe/exa-ai-enrichment-delete +121 -0
  7. data/exe/exa-ai-enrichment-get +103 -0
  8. data/exe/exa-ai-enrichment-list +98 -0
  9. data/exe/exa-ai-enrichment-update +170 -0
  10. data/exe/exa-ai-find-similar +240 -0
  11. data/exe/exa-ai-webset-cancel +96 -0
  12. data/exe/exa-ai-webset-create +192 -0
  13. data/exe/exa-ai-webset-delete +110 -0
  14. data/exe/exa-ai-webset-get +92 -0
  15. data/exe/exa-ai-webset-item-delete +111 -0
  16. data/exe/exa-ai-webset-item-get +104 -0
  17. data/exe/exa-ai-webset-item-list +93 -0
  18. data/exe/exa-ai-webset-list +90 -0
  19. data/exe/exa-ai-webset-search-cancel +103 -0
  20. data/exe/exa-ai-webset-search-create +233 -0
  21. data/exe/exa-ai-webset-search-get +104 -0
  22. data/exe/exa-ai-webset-update +139 -0
  23. data/lib/exa/cli/formatters/enrichment_formatter.rb +69 -0
  24. data/lib/exa/cli/formatters/webset_formatter.rb +68 -0
  25. data/lib/exa/cli/formatters/webset_item_formatter.rb +69 -0
  26. data/lib/exa/client.rb +171 -0
  27. data/lib/exa/resources/webset.rb +74 -0
  28. data/lib/exa/resources/webset_collection.rb +33 -0
  29. data/lib/exa/resources/webset_enrichment.rb +71 -0
  30. data/lib/exa/resources/webset_enrichment_collection.rb +28 -0
  31. data/lib/exa/resources/webset_search.rb +112 -0
  32. data/lib/exa/services/parameter_converter.rb +1 -0
  33. data/lib/exa/services/websets/cancel.rb +36 -0
  34. data/lib/exa/services/websets/cancel_enrichment.rb +35 -0
  35. data/lib/exa/services/websets/cancel_search.rb +44 -0
  36. data/lib/exa/services/websets/create.rb +45 -0
  37. data/lib/exa/services/websets/create_enrichment.rb +35 -0
  38. data/lib/exa/services/websets/create_search.rb +48 -0
  39. data/lib/exa/services/websets/create_search_validator.rb +128 -0
  40. data/lib/exa/services/websets/create_validator.rb +189 -0
  41. data/lib/exa/services/websets/delete.rb +36 -0
  42. data/lib/exa/services/websets/delete_enrichment.rb +35 -0
  43. data/lib/exa/services/websets/delete_item.rb +20 -0
  44. data/lib/exa/services/websets/get_item.rb +20 -0
  45. data/lib/exa/services/websets/get_search.rb +43 -0
  46. data/lib/exa/services/websets/list.rb +25 -0
  47. data/lib/exa/services/websets/list_items.rb +20 -0
  48. data/lib/exa/services/websets/retrieve.rb +47 -0
  49. data/lib/exa/services/websets/retrieve_enrichment.rb +35 -0
  50. data/lib/exa/services/websets/update.rb +37 -0
  51. data/lib/exa/services/websets/update_enrichment.rb +36 -0
  52. data/lib/exa/services/websets_parameter_converter.rb +45 -0
  53. data/lib/exa/version.rb +1 -1
  54. data/lib/exa.rb +25 -0
  55. metadata +64 -3
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "create_search_validator"
4
+
5
+ module Exa
6
+ module Services
7
+ module Websets
8
+ class CreateSearch
9
+ def initialize(connection, webset_id:, **params)
10
+ @connection = connection
11
+ @webset_id = webset_id
12
+ @params = params
13
+ end
14
+
15
+ def call
16
+ CreateSearchValidator.validate!(@params)
17
+
18
+ response = @connection.post(
19
+ "/websets/v0/websets/#{@webset_id}/searches",
20
+ @params
21
+ )
22
+ body = response.body
23
+
24
+ Resources::WebsetSearch.new(
25
+ id: body["id"],
26
+ object: body["object"],
27
+ status: body["status"],
28
+ webset_id: body["websetId"],
29
+ query: body["query"],
30
+ entity: body["entity"],
31
+ criteria: body["criteria"],
32
+ count: body["count"],
33
+ behavior: body["behavior"],
34
+ exclude: body["exclude"],
35
+ scope: body["scope"],
36
+ progress: body["progress"],
37
+ recall: body["recall"],
38
+ metadata: body["metadata"],
39
+ canceled_at: body["canceledAt"],
40
+ canceled_reason: body["canceledReason"],
41
+ created_at: body["createdAt"],
42
+ updated_at: body["updatedAt"]
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ # Validates parameters for webset search creation
7
+ class CreateSearchValidator
8
+ VALID_ENTITY_TYPES = %w[company person article research_paper custom].freeze
9
+ VALID_BEHAVIORS = %w[override append].freeze
10
+ VALID_SOURCE_TYPES = %w[import webset].freeze
11
+
12
+ class << self
13
+ def validate!(params)
14
+ validate_query!(params[:query]) if params[:query]
15
+ validate_count!(params[:count]) if params[:count]
16
+ validate_entity!(params[:entity]) if params[:entity]
17
+ validate_criteria!(params[:criteria]) if params[:criteria]
18
+ validate_scope!(params[:scope]) if params[:scope]
19
+ validate_exclude!(params[:exclude]) if params[:exclude]
20
+ validate_behavior!(params[:behavior]) if params[:behavior]
21
+ validate_metadata!(params[:metadata]) if params[:metadata]
22
+ end
23
+
24
+ private
25
+
26
+ def validate_query!(query)
27
+ raise ArgumentError, "query must be a String" unless query.is_a?(String)
28
+ raise ArgumentError, "query cannot be empty" if query.strip.empty?
29
+ raise ArgumentError, "query cannot exceed 5000 characters" if query.length > 5000
30
+ end
31
+
32
+ def validate_count!(count)
33
+ raise ArgumentError, "count must be a positive Integer" unless count.is_a?(Integer) && count > 0
34
+ end
35
+
36
+ def validate_entity!(entity)
37
+ raise ArgumentError, "entity must be a Hash" unless entity.is_a?(Hash)
38
+ raise ArgumentError, "entity[:type] is required" unless entity[:type]
39
+
40
+ type = entity[:type]
41
+ unless VALID_ENTITY_TYPES.include?(type)
42
+ raise ArgumentError, "entity[:type] must be one of: #{VALID_ENTITY_TYPES.join(', ')}"
43
+ end
44
+
45
+ if type == "custom"
46
+ raise ArgumentError, "entity[:description] is required for custom entity type" unless entity[:description]
47
+ validate_string_length!(entity[:description], "entity[:description]", min: 2, max: 200)
48
+ end
49
+ end
50
+
51
+ def validate_criteria!(criteria)
52
+ raise ArgumentError, "criteria must be an Array" unless criteria.is_a?(Array)
53
+ raise ArgumentError, "criteria must have at least 1 item" if criteria.empty?
54
+ raise ArgumentError, "criteria cannot have more than 5 items" if criteria.length > 5
55
+
56
+ criteria.each_with_index do |criterion, index|
57
+ raise ArgumentError, "criteria[#{index}] must be a Hash" unless criterion.is_a?(Hash)
58
+ raise ArgumentError, "criteria[#{index}][:description] is required" unless criterion[:description]
59
+ validate_string_length!(criterion[:description], "criteria[#{index}][:description]", min: 1, max: 1000)
60
+ end
61
+ end
62
+
63
+ def validate_scope!(scope)
64
+ raise ArgumentError, "scope must be an Array" unless scope.is_a?(Array)
65
+
66
+ scope.each_with_index do |item, index|
67
+ validate_source_reference!(item, "scope[#{index}]")
68
+
69
+ next unless item[:relationship]
70
+
71
+ rel = item[:relationship]
72
+ raise ArgumentError, "scope[#{index}][:relationship] must be a Hash" unless rel.is_a?(Hash)
73
+ raise ArgumentError, "scope[#{index}][:relationship][:definition] is required" unless rel[:definition]
74
+ raise ArgumentError, "scope[#{index}][:relationship][:limit] is required" unless rel[:limit]
75
+
76
+ limit = rel[:limit]
77
+ unless limit.is_a?(Integer) && limit >= 1 && limit <= 10
78
+ raise ArgumentError, "scope[#{index}][:relationship][:limit] must be an Integer between 1 and 10"
79
+ end
80
+ end
81
+ end
82
+
83
+ def validate_exclude!(exclude)
84
+ raise ArgumentError, "exclude must be an Array" unless exclude.is_a?(Array)
85
+
86
+ exclude.each_with_index do |item, index|
87
+ validate_source_reference!(item, "exclude[#{index}]")
88
+ end
89
+ end
90
+
91
+ def validate_behavior!(behavior)
92
+ unless VALID_BEHAVIORS.include?(behavior)
93
+ raise ArgumentError, "behavior must be one of: #{VALID_BEHAVIORS.join(', ')}"
94
+ end
95
+ end
96
+
97
+ def validate_metadata!(metadata)
98
+ raise ArgumentError, "metadata must be a Hash" unless metadata.is_a?(Hash)
99
+
100
+ metadata.each do |key, value|
101
+ raise ArgumentError, "metadata values must be Strings" unless value.is_a?(String)
102
+ raise ArgumentError, "metadata value for '#{key}' cannot exceed 1000 characters" if value.length > 1000
103
+ end
104
+ end
105
+
106
+ def validate_source_reference!(item, context)
107
+ raise ArgumentError, "#{context} must be a Hash" unless item.is_a?(Hash)
108
+ raise ArgumentError, "#{context}[:source] is required" unless item[:source]
109
+ raise ArgumentError, "#{context}[:id] is required" unless item[:id]
110
+
111
+ source = item[:source]
112
+ unless VALID_SOURCE_TYPES.include?(source)
113
+ raise ArgumentError, "#{context}[:source] must be one of: #{VALID_SOURCE_TYPES.join(', ')}"
114
+ end
115
+
116
+ raise ArgumentError, "#{context}[:id] must be a non-empty String" unless item[:id].is_a?(String) && !item[:id].empty?
117
+ end
118
+
119
+ def validate_string_length!(value, name, min: nil, max: nil)
120
+ raise ArgumentError, "#{name} must be a String" unless value.is_a?(String)
121
+ raise ArgumentError, "#{name} must be at least #{min} characters" if min && value.length < min
122
+ raise ArgumentError, "#{name} cannot exceed #{max} characters" if max && value.length > max
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ # Validates parameters for webset creation
7
+ class CreateValidator
8
+ VALID_ENTITY_TYPES = %w[company person article research_paper custom].freeze
9
+ VALID_ENRICHMENT_FORMATS = %w[text date number options url].freeze
10
+ VALID_SOURCE_TYPES = %w[import webset].freeze
11
+
12
+ class << self
13
+ def validate!(params)
14
+ validate_has_search_or_import!(params)
15
+ validate_search!(params[:search]) if params[:search]
16
+ validate_import!(params[:import]) if params[:import]
17
+ validate_enrichments!(params[:enrichments]) if params[:enrichments]
18
+ validate_exclude!(params[:exclude]) if params[:exclude]
19
+ validate_external_id!(params[:externalId]) if params[:externalId]
20
+ validate_metadata!(params[:metadata]) if params[:metadata]
21
+ end
22
+
23
+ private
24
+
25
+ def validate_has_search_or_import!(params)
26
+ return if params[:search] || params[:import]
27
+
28
+ raise ArgumentError, "At least one of :search or :import is required"
29
+ end
30
+
31
+ def validate_search!(search)
32
+ raise ArgumentError, "search must be a Hash" unless search.is_a?(Hash)
33
+ raise ArgumentError, "search[:query] is required" unless search[:query]
34
+ raise ArgumentError, "search[:query] must be a String" unless search[:query].is_a?(String)
35
+ raise ArgumentError, "search[:query] cannot be empty" if search[:query].strip.empty?
36
+ raise ArgumentError, "search[:query] cannot exceed 5000 characters" if search[:query].length > 5000
37
+
38
+ validate_count!(search[:count]) if search[:count]
39
+ validate_entity!(search[:entity]) if search[:entity]
40
+ validate_criteria!(search[:criteria]) if search[:criteria]
41
+ validate_scope!(search[:scope]) if search[:scope]
42
+ validate_exclude_list!(search[:exclude]) if search[:exclude]
43
+ end
44
+
45
+ def validate_count!(count)
46
+ raise ArgumentError, "count must be a positive Integer" unless count.is_a?(Integer) && count > 0
47
+ end
48
+
49
+ def validate_entity!(entity)
50
+ raise ArgumentError, "entity must be a Hash" unless entity.is_a?(Hash)
51
+ raise ArgumentError, "entity[:type] is required" unless entity[:type]
52
+
53
+ type = entity[:type]
54
+ unless VALID_ENTITY_TYPES.include?(type)
55
+ raise ArgumentError, "entity[:type] must be one of: #{VALID_ENTITY_TYPES.join(', ')}"
56
+ end
57
+
58
+ if type == "custom"
59
+ raise ArgumentError, "entity[:description] is required for custom entity type" unless entity[:description]
60
+ validate_string_length!(entity[:description], "entity[:description]", min: 2, max: 200)
61
+ end
62
+ end
63
+
64
+ def validate_criteria!(criteria)
65
+ raise ArgumentError, "criteria must be an Array" unless criteria.is_a?(Array)
66
+ raise ArgumentError, "criteria must have at least 1 item" if criteria.empty?
67
+ raise ArgumentError, "criteria cannot have more than 5 items" if criteria.length > 5
68
+
69
+ criteria.each_with_index do |criterion, index|
70
+ raise ArgumentError, "criteria[#{index}] must be a Hash" unless criterion.is_a?(Hash)
71
+ raise ArgumentError, "criteria[#{index}][:description] is required" unless criterion[:description]
72
+ validate_string_length!(criterion[:description], "criteria[#{index}][:description]", min: 1, max: 1000)
73
+ end
74
+ end
75
+
76
+ def validate_scope!(scope)
77
+ raise ArgumentError, "scope must be an Array" unless scope.is_a?(Array)
78
+
79
+ scope.each_with_index do |item, index|
80
+ validate_source_reference!(item, "scope[#{index}]")
81
+
82
+ next unless item[:relationship]
83
+
84
+ rel = item[:relationship]
85
+ raise ArgumentError, "scope[#{index}][:relationship] must be a Hash" unless rel.is_a?(Hash)
86
+ raise ArgumentError, "scope[#{index}][:relationship][:definition] is required" unless rel[:definition]
87
+ raise ArgumentError, "scope[#{index}][:relationship][:limit] is required" unless rel[:limit]
88
+
89
+ limit = rel[:limit]
90
+ unless limit.is_a?(Integer) && limit >= 1 && limit <= 10
91
+ raise ArgumentError, "scope[#{index}][:relationship][:limit] must be an Integer between 1 and 10"
92
+ end
93
+ end
94
+ end
95
+
96
+ def validate_exclude_list!(exclude)
97
+ raise ArgumentError, "exclude must be an Array" unless exclude.is_a?(Array)
98
+
99
+ exclude.each_with_index do |item, index|
100
+ validate_source_reference!(item, "exclude[#{index}]")
101
+ end
102
+ end
103
+
104
+ def validate_import!(import)
105
+ raise ArgumentError, "import must be an Array" unless import.is_a?(Array)
106
+
107
+ import.each_with_index do |item, index|
108
+ validate_source_reference!(item, "import[#{index}]")
109
+ end
110
+ end
111
+
112
+ def validate_enrichments!(enrichments)
113
+ raise ArgumentError, "enrichments must be an Array" unless enrichments.is_a?(Array)
114
+
115
+ enrichments.each_with_index do |enrichment, index|
116
+ raise ArgumentError, "enrichments[#{index}] must be a Hash" unless enrichment.is_a?(Hash)
117
+ raise ArgumentError, "enrichments[#{index}][:description] is required" unless enrichment[:description]
118
+ validate_string_length!(enrichment[:description], "enrichments[#{index}][:description]", min: 1, max: 5000)
119
+
120
+ if enrichment[:format]
121
+ format = enrichment[:format]
122
+ unless VALID_ENRICHMENT_FORMATS.include?(format)
123
+ raise ArgumentError, "enrichments[#{index}][:format] must be one of: #{VALID_ENRICHMENT_FORMATS.join(', ')}"
124
+ end
125
+
126
+ if format == "options"
127
+ validate_enrichment_options!(enrichment[:options], index)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def validate_enrichment_options!(options, enrichment_index)
134
+ unless options.is_a?(Array)
135
+ raise ArgumentError, "enrichments[#{enrichment_index}][:options] is required when format is 'options'"
136
+ end
137
+
138
+ if options.empty? || options.length > 150
139
+ raise ArgumentError, "enrichments[#{enrichment_index}][:options] must have between 1 and 150 items"
140
+ end
141
+
142
+ options.each_with_index do |option, option_index|
143
+ unless option.is_a?(Hash) && option[:label]
144
+ raise ArgumentError, "enrichments[#{enrichment_index}][:options][#{option_index}][:label] is required"
145
+ end
146
+ end
147
+ end
148
+
149
+ def validate_exclude!(exclude)
150
+ validate_exclude_list!(exclude)
151
+ end
152
+
153
+ def validate_external_id!(external_id)
154
+ raise ArgumentError, "externalId must be a String" unless external_id.is_a?(String)
155
+ raise ArgumentError, "externalId cannot exceed 300 characters" if external_id.length > 300
156
+ end
157
+
158
+ def validate_metadata!(metadata)
159
+ raise ArgumentError, "metadata must be a Hash" unless metadata.is_a?(Hash)
160
+
161
+ metadata.each do |key, value|
162
+ raise ArgumentError, "metadata values must be Strings" unless value.is_a?(String)
163
+ raise ArgumentError, "metadata value for '#{key}' cannot exceed 1000 characters" if value.length > 1000
164
+ end
165
+ end
166
+
167
+ def validate_source_reference!(item, context)
168
+ raise ArgumentError, "#{context} must be a Hash" unless item.is_a?(Hash)
169
+ raise ArgumentError, "#{context}[:source] is required" unless item[:source]
170
+ raise ArgumentError, "#{context}[:id] is required" unless item[:id]
171
+
172
+ source = item[:source]
173
+ unless VALID_SOURCE_TYPES.include?(source)
174
+ raise ArgumentError, "#{context}[:source] must be one of: #{VALID_SOURCE_TYPES.join(', ')}"
175
+ end
176
+
177
+ raise ArgumentError, "#{context}[:id] must be a non-empty String" unless item[:id].is_a?(String) && !item[:id].empty?
178
+ end
179
+
180
+ def validate_string_length!(value, name, min: nil, max: nil)
181
+ raise ArgumentError, "#{name} must be a String" unless value.is_a?(String)
182
+ raise ArgumentError, "#{name} must be at least #{min} characters" if min && value.length < min
183
+ raise ArgumentError, "#{name} cannot exceed #{max} characters" if max && value.length > max
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class Delete
7
+ def initialize(connection, id:)
8
+ @connection = connection
9
+ @id = id
10
+ end
11
+
12
+ def call
13
+ response = @connection.delete("/websets/v0/websets/#{@id}")
14
+ body = response.body
15
+
16
+ Resources::Webset.new(
17
+ id: body["id"],
18
+ object: body["object"],
19
+ status: body["status"],
20
+ external_id: body["externalId"],
21
+ title: body["title"],
22
+ searches: body["searches"],
23
+ imports: body["imports"],
24
+ enrichments: body["enrichments"],
25
+ monitors: body["monitors"],
26
+ excludes: body["excludes"],
27
+ metadata: body["metadata"],
28
+ created_at: body["createdAt"],
29
+ updated_at: body["updatedAt"],
30
+ items: body["items"]
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class DeleteEnrichment
7
+ def initialize(connection, webset_id:, id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ @id = id
11
+ end
12
+
13
+ def call
14
+ response = @connection.delete("/websets/v0/websets/#{@webset_id}/enrichments/#{@id}")
15
+ body = response.body
16
+
17
+ Resources::WebsetEnrichment.new(
18
+ id: body["id"],
19
+ object: body["object"],
20
+ status: body["status"],
21
+ webset_id: body["websetId"],
22
+ title: body["title"],
23
+ description: body["description"],
24
+ format: body["format"],
25
+ options: body["options"],
26
+ instructions: body["instructions"],
27
+ metadata: body["metadata"],
28
+ created_at: body["createdAt"],
29
+ updated_at: body["updatedAt"]
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class DeleteItem
7
+ def initialize(connection, webset_id:, id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ @id = id
11
+ end
12
+
13
+ def call
14
+ @connection.delete("/websets/v0/websets/#{@webset_id}/items/#{@id}")
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class GetItem
7
+ def initialize(connection, webset_id:, id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ @id = id
11
+ end
12
+
13
+ def call
14
+ response = @connection.get("/websets/v0/websets/#{@webset_id}/items/#{@id}")
15
+ response.body
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class GetSearch
7
+ def initialize(connection, webset_id:, id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ @id = id
11
+ end
12
+
13
+ def call
14
+ response = @connection.get(
15
+ "/websets/v0/websets/#{@webset_id}/searches/#{@id}"
16
+ )
17
+ body = response.body
18
+
19
+ Resources::WebsetSearch.new(
20
+ id: body["id"],
21
+ object: body["object"],
22
+ status: body["status"],
23
+ webset_id: body["websetId"],
24
+ query: body["query"],
25
+ entity: body["entity"],
26
+ criteria: body["criteria"],
27
+ count: body["count"],
28
+ behavior: body["behavior"],
29
+ exclude: body["exclude"],
30
+ scope: body["scope"],
31
+ progress: body["progress"],
32
+ recall: body["recall"],
33
+ metadata: body["metadata"],
34
+ canceled_at: body["canceledAt"],
35
+ canceled_reason: body["canceledReason"],
36
+ created_at: body["createdAt"],
37
+ updated_at: body["updatedAt"]
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class List
7
+ def initialize(connection, **params)
8
+ @connection = connection
9
+ @params = params
10
+ end
11
+
12
+ def call
13
+ response = @connection.get("/websets/v0/websets", @params)
14
+ body = response.body
15
+
16
+ Resources::WebsetCollection.new(
17
+ data: body["data"],
18
+ has_more: body["hasMore"],
19
+ next_cursor: body["nextCursor"]
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class ListItems
7
+ def initialize(connection, webset_id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ end
11
+
12
+ def call
13
+ response = @connection.get("/websets/v0/websets/#{@webset_id}/items")
14
+ body = response.body
15
+ body["data"] || []
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class Retrieve
7
+ def initialize(connection, id:, **params)
8
+ @connection = connection
9
+ @id = id
10
+ @params = normalize_params(params)
11
+ end
12
+
13
+ def call
14
+ response = @connection.get("/websets/v0/websets/#{@id}", @params)
15
+ body = response.body
16
+
17
+ Resources::Webset.new(
18
+ id: body["id"],
19
+ object: body["object"],
20
+ status: body["status"],
21
+ external_id: body["externalId"],
22
+ title: body["title"],
23
+ searches: body["searches"],
24
+ imports: body["imports"],
25
+ enrichments: body["enrichments"],
26
+ monitors: body["monitors"],
27
+ excludes: body["excludes"],
28
+ metadata: body["metadata"],
29
+ created_at: body["createdAt"],
30
+ updated_at: body["updatedAt"],
31
+ items: body["items"]
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ def normalize_params(params)
38
+ # Convert expand array to comma-separated string for API compatibility
39
+ if params[:expand].is_a?(Array)
40
+ params[:expand] = params[:expand].join(",")
41
+ end
42
+ params
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Exa
4
+ module Services
5
+ module Websets
6
+ class RetrieveEnrichment
7
+ def initialize(connection, webset_id:, id:)
8
+ @connection = connection
9
+ @webset_id = webset_id
10
+ @id = id
11
+ end
12
+
13
+ def call
14
+ response = @connection.get("/websets/v0/websets/#{@webset_id}/enrichments/#{@id}")
15
+ body = response.body
16
+
17
+ Resources::WebsetEnrichment.new(
18
+ id: body["id"],
19
+ object: body["object"],
20
+ status: body["status"],
21
+ webset_id: body["websetId"],
22
+ title: body["title"],
23
+ description: body["description"],
24
+ format: body["format"],
25
+ options: body["options"],
26
+ instructions: body["instructions"],
27
+ metadata: body["metadata"],
28
+ created_at: body["createdAt"],
29
+ updated_at: body["updatedAt"]
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end