exaonruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +614 -0
- data/exaonruby.gemspec +37 -0
- data/exe/exa +7 -0
- data/lib/exa/cli.rb +458 -0
- data/lib/exa/client.rb +210 -0
- data/lib/exa/configuration.rb +81 -0
- data/lib/exa/endpoints/answer.rb +109 -0
- data/lib/exa/endpoints/contents.rb +141 -0
- data/lib/exa/endpoints/events.rb +71 -0
- data/lib/exa/endpoints/find_similar.rb +154 -0
- data/lib/exa/endpoints/imports.rb +145 -0
- data/lib/exa/endpoints/monitors.rb +193 -0
- data/lib/exa/endpoints/research.rb +158 -0
- data/lib/exa/endpoints/search.rb +195 -0
- data/lib/exa/endpoints/webhooks.rb +161 -0
- data/lib/exa/endpoints/webset_enrichments.rb +162 -0
- data/lib/exa/endpoints/webset_items.rb +90 -0
- data/lib/exa/endpoints/webset_searches.rb +137 -0
- data/lib/exa/endpoints/websets.rb +214 -0
- data/lib/exa/errors.rb +180 -0
- data/lib/exa/resources/answer_response.rb +101 -0
- data/lib/exa/resources/base.rb +56 -0
- data/lib/exa/resources/contents_response.rb +123 -0
- data/lib/exa/resources/event.rb +84 -0
- data/lib/exa/resources/import.rb +137 -0
- data/lib/exa/resources/monitor.rb +205 -0
- data/lib/exa/resources/paginated_response.rb +87 -0
- data/lib/exa/resources/research_task.rb +165 -0
- data/lib/exa/resources/search_response.rb +111 -0
- data/lib/exa/resources/search_result.rb +95 -0
- data/lib/exa/resources/webhook.rb +152 -0
- data/lib/exa/resources/webset.rb +491 -0
- data/lib/exa/resources/webset_item.rb +256 -0
- data/lib/exa/utils/parameter_converter.rb +159 -0
- data/lib/exa/utils/webhook_handler.rb +239 -0
- data/lib/exa/version.rb +7 -0
- data/lib/exa.rb +130 -0
- metadata +146 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Endpoints
|
|
7
|
+
module Webhooks
|
|
8
|
+
VALID_EVENTS = %w[
|
|
9
|
+
webset.created webset.deleted webset.paused webset.idle
|
|
10
|
+
webset.search.created webset.search.canceled webset.search.completed webset.search.updated
|
|
11
|
+
import.created import.completed
|
|
12
|
+
webset.item.created webset.item.enriched
|
|
13
|
+
monitor.created monitor.updated monitor.deleted
|
|
14
|
+
monitor.run.created monitor.run.completed
|
|
15
|
+
webset.export.created webset.export.completed
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
# Creates a new Webhook for event notifications
|
|
19
|
+
#
|
|
20
|
+
# Webhooks notify you when events happen in your Websets via HTTP POST.
|
|
21
|
+
# The response includes a secret key for signature verification.
|
|
22
|
+
#
|
|
23
|
+
# @param url [String] URL to send webhook notifications to
|
|
24
|
+
# @param events [Array<String>] Event types to subscribe to
|
|
25
|
+
# @param metadata [Hash, nil] Custom metadata
|
|
26
|
+
#
|
|
27
|
+
# @return [Exa::Resources::Webhook] Created Webhook with secret
|
|
28
|
+
#
|
|
29
|
+
# @example Create a webhook for item events
|
|
30
|
+
# webhook = client.create_webhook(
|
|
31
|
+
# url: "https://example.com/webhooks/exa",
|
|
32
|
+
# events: ["webset.item.created", "webset.item.enriched"]
|
|
33
|
+
# )
|
|
34
|
+
# puts "Secret (save this!): #{webhook.secret}"
|
|
35
|
+
def create_webhook(url:, events:, metadata: nil)
|
|
36
|
+
validate_webhook_params!(url, events)
|
|
37
|
+
|
|
38
|
+
params = {
|
|
39
|
+
url: url,
|
|
40
|
+
events: events
|
|
41
|
+
}
|
|
42
|
+
params[:metadata] = metadata if metadata
|
|
43
|
+
|
|
44
|
+
response = websets_post("/webhooks", params)
|
|
45
|
+
|
|
46
|
+
Resources::Webhook.new(
|
|
47
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Gets a Webhook by ID
|
|
52
|
+
#
|
|
53
|
+
# @param webhook_id [String] Webhook ID
|
|
54
|
+
# @return [Exa::Resources::Webhook] The Webhook
|
|
55
|
+
def get_webhook(webhook_id)
|
|
56
|
+
raise InvalidRequestError, "webhook_id must be a non-empty string" if !webhook_id.is_a?(String) || webhook_id.empty?
|
|
57
|
+
|
|
58
|
+
response = websets_get("/webhooks/#{webhook_id}")
|
|
59
|
+
|
|
60
|
+
Resources::Webhook.new(
|
|
61
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Lists all Webhooks
|
|
66
|
+
#
|
|
67
|
+
# @param cursor [String, nil] Cursor for pagination
|
|
68
|
+
# @param limit [Integer, nil] Number of results per page
|
|
69
|
+
#
|
|
70
|
+
# @return [Exa::Resources::WebhookListResponse] Paginated list of Webhooks
|
|
71
|
+
def list_webhooks(cursor: nil, limit: nil)
|
|
72
|
+
params = {}
|
|
73
|
+
params[:cursor] = cursor if cursor
|
|
74
|
+
params[:limit] = limit if limit
|
|
75
|
+
|
|
76
|
+
response = websets_get("/webhooks", params)
|
|
77
|
+
|
|
78
|
+
Resources::WebhookListResponse.new(
|
|
79
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Updates a Webhook
|
|
84
|
+
#
|
|
85
|
+
# @param webhook_id [String] Webhook ID
|
|
86
|
+
# @param url [String, nil] Updated URL
|
|
87
|
+
# @param events [Array<String>, nil] Updated event subscriptions
|
|
88
|
+
# @param status [String, nil] "active" or "inactive"
|
|
89
|
+
# @param metadata [Hash, nil] Updated metadata
|
|
90
|
+
#
|
|
91
|
+
# @return [Exa::Resources::Webhook] Updated Webhook
|
|
92
|
+
def update_webhook(webhook_id, url: nil, events: nil, status: nil, metadata: nil)
|
|
93
|
+
raise InvalidRequestError, "webhook_id must be a non-empty string" if !webhook_id.is_a?(String) || webhook_id.empty?
|
|
94
|
+
|
|
95
|
+
if events
|
|
96
|
+
validate_events!(events)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
params = {}
|
|
100
|
+
params[:url] = url if url
|
|
101
|
+
params[:events] = events if events
|
|
102
|
+
params[:status] = status if status
|
|
103
|
+
params[:metadata] = metadata if metadata
|
|
104
|
+
|
|
105
|
+
response = websets_patch("/webhooks/#{webhook_id}", params)
|
|
106
|
+
|
|
107
|
+
Resources::Webhook.new(
|
|
108
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Deletes a Webhook
|
|
113
|
+
#
|
|
114
|
+
# @param webhook_id [String] Webhook ID
|
|
115
|
+
# @return [Hash] Deletion confirmation
|
|
116
|
+
def delete_webhook(webhook_id)
|
|
117
|
+
raise InvalidRequestError, "webhook_id must be a non-empty string" if !webhook_id.is_a?(String) || webhook_id.empty?
|
|
118
|
+
|
|
119
|
+
websets_delete("/webhooks/#{webhook_id}")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Lists delivery attempts for a Webhook
|
|
123
|
+
#
|
|
124
|
+
# @param webhook_id [String] Webhook ID
|
|
125
|
+
# @param cursor [String, nil] Cursor for pagination
|
|
126
|
+
# @param limit [Integer, nil] Number of results per page
|
|
127
|
+
#
|
|
128
|
+
# @return [Exa::Resources::WebhookAttemptListResponse] Paginated list of attempts
|
|
129
|
+
def list_webhook_attempts(webhook_id, cursor: nil, limit: nil)
|
|
130
|
+
raise InvalidRequestError, "webhook_id must be a non-empty string" if !webhook_id.is_a?(String) || webhook_id.empty?
|
|
131
|
+
|
|
132
|
+
params = {}
|
|
133
|
+
params[:cursor] = cursor if cursor
|
|
134
|
+
params[:limit] = limit if limit
|
|
135
|
+
|
|
136
|
+
response = websets_get("/webhooks/#{webhook_id}/attempts", params)
|
|
137
|
+
|
|
138
|
+
Resources::WebhookAttemptListResponse.new(
|
|
139
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def validate_webhook_params!(url, events)
|
|
146
|
+
raise InvalidRequestError, "url must be a non-empty string" if !url.is_a?(String) || url.empty?
|
|
147
|
+
raise InvalidRequestError, "events must be a non-empty array" if !events.is_a?(Array) || events.empty?
|
|
148
|
+
|
|
149
|
+
validate_events!(events)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def validate_events!(events)
|
|
153
|
+
events.each do |event|
|
|
154
|
+
unless VALID_EVENTS.include?(event)
|
|
155
|
+
raise InvalidRequestError, "Invalid event: #{event}. Valid events: #{VALID_EVENTS.join(", ")}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Endpoints
|
|
7
|
+
module WebsetEnrichments
|
|
8
|
+
VALID_FORMATS = %w[text enum number boolean date].freeze
|
|
9
|
+
|
|
10
|
+
# Creates a new Enrichment for a Webset
|
|
11
|
+
#
|
|
12
|
+
# Enrichments automatically search for and extract specific information
|
|
13
|
+
# from each Item added to the Webset.
|
|
14
|
+
#
|
|
15
|
+
# @param webset_id [String] Webset ID or external ID
|
|
16
|
+
# @param description [String] Description of what to extract
|
|
17
|
+
# @param format [String] Output format: text, enum, number, boolean, date
|
|
18
|
+
# @param options [Array<Hash>] Options for enum format [{ label: "..." }]
|
|
19
|
+
# @param instructions [String] Additional instructions for extraction
|
|
20
|
+
# @param metadata [Hash] Custom metadata
|
|
21
|
+
#
|
|
22
|
+
# @return [Exa::Resources::WebsetEnrichment] Created Enrichment
|
|
23
|
+
#
|
|
24
|
+
# @raise [Exa::NotFoundError] if Webset not found
|
|
25
|
+
#
|
|
26
|
+
# @example Create a text enrichment
|
|
27
|
+
# enrichment = client.create_webset_enrichment(
|
|
28
|
+
# "webset_abc123",
|
|
29
|
+
# description: "Company's primary product or service",
|
|
30
|
+
# format: "text"
|
|
31
|
+
# )
|
|
32
|
+
#
|
|
33
|
+
# @example Create an enum enrichment
|
|
34
|
+
# enrichment = client.create_webset_enrichment(
|
|
35
|
+
# "webset_abc123",
|
|
36
|
+
# description: "Company's funding stage",
|
|
37
|
+
# format: "enum",
|
|
38
|
+
# options: [
|
|
39
|
+
# { label: "Pre-seed" },
|
|
40
|
+
# { label: "Seed" },
|
|
41
|
+
# { label: "Series A" },
|
|
42
|
+
# { label: "Series B+" }
|
|
43
|
+
# ]
|
|
44
|
+
# )
|
|
45
|
+
#
|
|
46
|
+
# @example Create a number enrichment
|
|
47
|
+
# enrichment = client.create_webset_enrichment(
|
|
48
|
+
# "webset_abc123",
|
|
49
|
+
# description: "Number of employees",
|
|
50
|
+
# format: "number"
|
|
51
|
+
# )
|
|
52
|
+
def create_webset_enrichment(webset_id, **enrichment_options)
|
|
53
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
54
|
+
raise InvalidRequestError, "description is required" unless enrichment_options[:description]
|
|
55
|
+
|
|
56
|
+
validate_enrichment_options!(enrichment_options)
|
|
57
|
+
|
|
58
|
+
params = build_enrichment_params(enrichment_options)
|
|
59
|
+
response = websets_post("/websets/#{webset_id}/enrichments", params)
|
|
60
|
+
|
|
61
|
+
Resources::WebsetEnrichment.new(
|
|
62
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Lists all Enrichments for a Webset
|
|
67
|
+
#
|
|
68
|
+
# @param webset_id [String] Webset ID or external ID
|
|
69
|
+
#
|
|
70
|
+
# @return [Array<Exa::Resources::WebsetEnrichment>] List of Enrichments
|
|
71
|
+
#
|
|
72
|
+
# @raise [Exa::NotFoundError] if Webset not found
|
|
73
|
+
#
|
|
74
|
+
# @example List Enrichments
|
|
75
|
+
# enrichments = client.list_webset_enrichments("webset_abc123")
|
|
76
|
+
# enrichments.each { |e| puts e.description }
|
|
77
|
+
def list_webset_enrichments(webset_id)
|
|
78
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
79
|
+
|
|
80
|
+
response = websets_get("/websets/#{webset_id}/enrichments")
|
|
81
|
+
|
|
82
|
+
enrichments_data = response.is_a?(Hash) && response[:data] ? response[:data] : response
|
|
83
|
+
enrichments_data = [enrichments_data] unless enrichments_data.is_a?(Array)
|
|
84
|
+
|
|
85
|
+
enrichments_data.map do |data|
|
|
86
|
+
Resources::WebsetEnrichment.new(
|
|
87
|
+
Utils::ParameterConverter.from_api_response(data)
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Gets an Enrichment by ID
|
|
93
|
+
#
|
|
94
|
+
# @param webset_id [String] Webset ID or external ID
|
|
95
|
+
# @param enrichment_id [String] Enrichment ID
|
|
96
|
+
#
|
|
97
|
+
# @return [Exa::Resources::WebsetEnrichment] The Enrichment
|
|
98
|
+
#
|
|
99
|
+
# @raise [Exa::NotFoundError] if Webset or Enrichment not found
|
|
100
|
+
#
|
|
101
|
+
# @example Get an Enrichment
|
|
102
|
+
# enrichment = client.get_webset_enrichment("webset_abc123", "enrichment_xyz789")
|
|
103
|
+
def get_webset_enrichment(webset_id, enrichment_id)
|
|
104
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
105
|
+
raise InvalidRequestError, "enrichment_id must be a non-empty string" if !enrichment_id.is_a?(String) || enrichment_id.empty?
|
|
106
|
+
|
|
107
|
+
response = websets_get("/websets/#{webset_id}/enrichments/#{enrichment_id}")
|
|
108
|
+
|
|
109
|
+
Resources::WebsetEnrichment.new(
|
|
110
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Deletes an Enrichment from a Webset
|
|
115
|
+
#
|
|
116
|
+
# @param webset_id [String] Webset ID or external ID
|
|
117
|
+
# @param enrichment_id [String] Enrichment ID
|
|
118
|
+
#
|
|
119
|
+
# @return [Hash] Deletion confirmation
|
|
120
|
+
#
|
|
121
|
+
# @raise [Exa::NotFoundError] if Webset or Enrichment not found
|
|
122
|
+
#
|
|
123
|
+
# @example Delete an Enrichment
|
|
124
|
+
# client.delete_webset_enrichment("webset_abc123", "enrichment_xyz789")
|
|
125
|
+
def delete_webset_enrichment(webset_id, enrichment_id)
|
|
126
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
127
|
+
raise InvalidRequestError, "enrichment_id must be a non-empty string" if !enrichment_id.is_a?(String) || enrichment_id.empty?
|
|
128
|
+
|
|
129
|
+
websets_delete("/websets/#{webset_id}/enrichments/#{enrichment_id}")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# @param options [Hash] Options to validate
|
|
135
|
+
def validate_enrichment_options!(options)
|
|
136
|
+
if options[:format]
|
|
137
|
+
format = options[:format].to_s
|
|
138
|
+
unless VALID_FORMATS.include?(format)
|
|
139
|
+
raise InvalidRequestError, "Invalid format: #{format}. Valid formats: #{VALID_FORMATS.join(", ")}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
if format == "enum" && (!options[:options] || options[:options].empty?)
|
|
143
|
+
raise InvalidRequestError, "options are required when format is 'enum'"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# @param options [Hash] Enrichment options
|
|
149
|
+
# @return [Hash] API-formatted parameters
|
|
150
|
+
def build_enrichment_params(options)
|
|
151
|
+
params = { description: options[:description] }
|
|
152
|
+
|
|
153
|
+
params[:format] = options[:format] if options[:format]
|
|
154
|
+
params[:options] = options[:options] if options[:options]
|
|
155
|
+
params[:instructions] = options[:instructions] if options[:instructions]
|
|
156
|
+
params[:metadata] = options[:metadata] if options[:metadata]
|
|
157
|
+
|
|
158
|
+
params
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Endpoints
|
|
7
|
+
module WebsetItems
|
|
8
|
+
# Lists all Items in a Webset with pagination
|
|
9
|
+
#
|
|
10
|
+
# @param webset_id [String] Webset ID or external ID
|
|
11
|
+
# @param cursor [String, nil] Cursor for pagination
|
|
12
|
+
# @param limit [Integer, nil] Number of results per page (1-100)
|
|
13
|
+
# @param source_id [String, nil] Filter by source ID
|
|
14
|
+
#
|
|
15
|
+
# @return [Exa::Resources::WebsetItemsListResponse] Paginated list of Items
|
|
16
|
+
#
|
|
17
|
+
# @raise [Exa::NotFoundError] if Webset not found
|
|
18
|
+
#
|
|
19
|
+
# @example List all Items in a Webset
|
|
20
|
+
# response = client.list_webset_items("webset_abc123")
|
|
21
|
+
# response.data.each { |item| puts item.url }
|
|
22
|
+
#
|
|
23
|
+
# @example Paginate through Items
|
|
24
|
+
# response = client.list_webset_items("webset_abc123", limit: 50)
|
|
25
|
+
# all_items = response.data.dup
|
|
26
|
+
# while response.has_more?
|
|
27
|
+
# response = client.list_webset_items("webset_abc123", cursor: response.next_cursor)
|
|
28
|
+
# all_items.concat(response.data)
|
|
29
|
+
# end
|
|
30
|
+
def list_webset_items(webset_id, cursor: nil, limit: nil, source_id: nil)
|
|
31
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
32
|
+
|
|
33
|
+
if limit && (limit < 1 || limit > 100)
|
|
34
|
+
raise InvalidRequestError, "limit must be between 1 and 100"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
params = {}
|
|
38
|
+
params[:cursor] = cursor if cursor
|
|
39
|
+
params[:limit] = limit if limit
|
|
40
|
+
params[:sourceId] = source_id if source_id
|
|
41
|
+
|
|
42
|
+
response = websets_get("/websets/#{webset_id}/items", params)
|
|
43
|
+
|
|
44
|
+
Resources::WebsetItemsListResponse.new(
|
|
45
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Gets a specific Item from a Webset
|
|
50
|
+
#
|
|
51
|
+
# @param webset_id [String] Webset ID or external ID
|
|
52
|
+
# @param item_id [String] Item ID
|
|
53
|
+
#
|
|
54
|
+
# @return [Exa::Resources::WebsetItem] The Item
|
|
55
|
+
#
|
|
56
|
+
# @raise [Exa::NotFoundError] if Webset or Item not found
|
|
57
|
+
#
|
|
58
|
+
# @example Get an Item
|
|
59
|
+
# item = client.get_webset_item("webset_abc123", "item_xyz789")
|
|
60
|
+
def get_webset_item(webset_id, item_id)
|
|
61
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
62
|
+
raise InvalidRequestError, "item_id must be a non-empty string" if !item_id.is_a?(String) || item_id.empty?
|
|
63
|
+
|
|
64
|
+
response = websets_get("/websets/#{webset_id}/items/#{item_id}")
|
|
65
|
+
|
|
66
|
+
Resources::WebsetItem.new(
|
|
67
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Deletes an Item from a Webset
|
|
72
|
+
#
|
|
73
|
+
# @param webset_id [String] Webset ID or external ID
|
|
74
|
+
# @param item_id [String] Item ID
|
|
75
|
+
#
|
|
76
|
+
# @return [Hash] Deletion confirmation
|
|
77
|
+
#
|
|
78
|
+
# @raise [Exa::NotFoundError] if Webset or Item not found
|
|
79
|
+
#
|
|
80
|
+
# @example Delete an Item
|
|
81
|
+
# client.delete_webset_item("webset_abc123", "item_xyz789")
|
|
82
|
+
def delete_webset_item(webset_id, item_id)
|
|
83
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
84
|
+
raise InvalidRequestError, "item_id must be a non-empty string" if !item_id.is_a?(String) || item_id.empty?
|
|
85
|
+
|
|
86
|
+
websets_delete("/websets/#{webset_id}/items/#{item_id}")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Endpoints
|
|
7
|
+
module WebsetSearches
|
|
8
|
+
VALID_ENTITY_TYPES = %w[company person research_paper].freeze
|
|
9
|
+
VALID_BEHAVIORS = %w[override append].freeze
|
|
10
|
+
|
|
11
|
+
# Creates a new Search for a Webset
|
|
12
|
+
#
|
|
13
|
+
# @param webset_id [String] Webset ID or external ID
|
|
14
|
+
# @param query [String] Search query
|
|
15
|
+
# @param count [Integer] Target number of results
|
|
16
|
+
# @param entity [Hash] Entity type configuration { type: "company"|"person"|"research_paper" }
|
|
17
|
+
# @param criteria [Array<Hash>] Criteria for filtering [{ description: "..." }]
|
|
18
|
+
# @param recall [Boolean] Enable recall estimation
|
|
19
|
+
# @param exclude [Array<Hash>] Exclusion rules
|
|
20
|
+
# @param scope [Array<Hash>] Scope configuration
|
|
21
|
+
# @param behavior [String] Behavior: "override" or "append"
|
|
22
|
+
# @param metadata [Hash] Custom metadata
|
|
23
|
+
#
|
|
24
|
+
# @return [Exa::Resources::WebsetSearch] Created Search
|
|
25
|
+
#
|
|
26
|
+
# @raise [Exa::NotFoundError] if Webset not found
|
|
27
|
+
#
|
|
28
|
+
# @example Create a Search
|
|
29
|
+
# search = client.create_webset_search(
|
|
30
|
+
# "webset_abc123",
|
|
31
|
+
# query: "AI startups in healthcare",
|
|
32
|
+
# count: 100,
|
|
33
|
+
# entity: { type: "company" },
|
|
34
|
+
# criteria: [{ description: "Must be in healthcare industry" }]
|
|
35
|
+
# )
|
|
36
|
+
def create_webset_search(webset_id, **options)
|
|
37
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
38
|
+
raise InvalidRequestError, "query is required" unless options[:query]
|
|
39
|
+
|
|
40
|
+
validate_webset_search_options!(options)
|
|
41
|
+
|
|
42
|
+
params = build_webset_search_params(options)
|
|
43
|
+
response = websets_post("/websets/#{webset_id}/searches", params)
|
|
44
|
+
|
|
45
|
+
Resources::WebsetSearch.new(
|
|
46
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Gets a Search by ID
|
|
51
|
+
#
|
|
52
|
+
# @param webset_id [String] Webset ID or external ID
|
|
53
|
+
# @param search_id [String] Search ID
|
|
54
|
+
#
|
|
55
|
+
# @return [Exa::Resources::WebsetSearch] The Search
|
|
56
|
+
#
|
|
57
|
+
# @raise [Exa::NotFoundError] if Webset or Search not found
|
|
58
|
+
#
|
|
59
|
+
# @example Get a Search with progress
|
|
60
|
+
# search = client.get_webset_search("webset_abc123", "search_xyz789")
|
|
61
|
+
# puts "Progress: #{search.completion_percentage}%"
|
|
62
|
+
def get_webset_search(webset_id, search_id)
|
|
63
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
64
|
+
raise InvalidRequestError, "search_id must be a non-empty string" if !search_id.is_a?(String) || search_id.empty?
|
|
65
|
+
|
|
66
|
+
response = websets_get("/websets/#{webset_id}/searches/#{search_id}")
|
|
67
|
+
|
|
68
|
+
Resources::WebsetSearch.new(
|
|
69
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Cancels an in-progress Search
|
|
74
|
+
#
|
|
75
|
+
# @param webset_id [String] Webset ID or external ID
|
|
76
|
+
# @param search_id [String] Search ID
|
|
77
|
+
#
|
|
78
|
+
# @return [Exa::Resources::WebsetSearch] The canceled Search
|
|
79
|
+
#
|
|
80
|
+
# @raise [Exa::NotFoundError] if Webset or Search not found
|
|
81
|
+
#
|
|
82
|
+
# @example Cancel a Search
|
|
83
|
+
# search = client.cancel_webset_search("webset_abc123", "search_xyz789")
|
|
84
|
+
# puts "Canceled at: #{search.canceled_at}"
|
|
85
|
+
def cancel_webset_search(webset_id, search_id)
|
|
86
|
+
raise InvalidRequestError, "webset_id must be a non-empty string" if !webset_id.is_a?(String) || webset_id.empty?
|
|
87
|
+
raise InvalidRequestError, "search_id must be a non-empty string" if !search_id.is_a?(String) || search_id.empty?
|
|
88
|
+
|
|
89
|
+
response = websets_post("/websets/#{webset_id}/searches/#{search_id}/cancel", {})
|
|
90
|
+
|
|
91
|
+
Resources::WebsetSearch.new(
|
|
92
|
+
Utils::ParameterConverter.from_api_response(response)
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# @param options [Hash] Options to validate
|
|
99
|
+
def validate_webset_search_options!(options)
|
|
100
|
+
if options[:entity] && options[:entity][:type]
|
|
101
|
+
entity_type = options[:entity][:type].to_s
|
|
102
|
+
unless VALID_ENTITY_TYPES.include?(entity_type)
|
|
103
|
+
raise InvalidRequestError, "Invalid entity type: #{entity_type}. Valid types: #{VALID_ENTITY_TYPES.join(", ")}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if options[:behavior]
|
|
108
|
+
behavior = options[:behavior].to_s
|
|
109
|
+
unless VALID_BEHAVIORS.include?(behavior)
|
|
110
|
+
raise InvalidRequestError, "Invalid behavior: #{behavior}. Valid behaviors: #{VALID_BEHAVIORS.join(", ")}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if options[:count] && (options[:count] < 1 || options[:count] > 10_000)
|
|
115
|
+
raise InvalidRequestError, "count must be between 1 and 10000"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @param options [Hash] Search options
|
|
120
|
+
# @return [Hash] API-formatted parameters
|
|
121
|
+
def build_webset_search_params(options)
|
|
122
|
+
params = { query: options[:query] }
|
|
123
|
+
|
|
124
|
+
params[:count] = options[:count] if options[:count]
|
|
125
|
+
params[:entity] = options[:entity] if options[:entity]
|
|
126
|
+
params[:criteria] = options[:criteria] if options[:criteria]
|
|
127
|
+
params[:recall] = options[:recall] if options.key?(:recall)
|
|
128
|
+
params[:exclude] = options[:exclude] if options[:exclude]
|
|
129
|
+
params[:scope] = options[:scope] if options[:scope]
|
|
130
|
+
params[:behavior] = options[:behavior] if options[:behavior]
|
|
131
|
+
params[:metadata] = options[:metadata] if options[:metadata]
|
|
132
|
+
|
|
133
|
+
params
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|