exa-ai 0.3.0 → 0.4.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 +4 -4
- data/README.md +94 -591
- data/exe/exa-ai +112 -9
- data/exe/exa-ai-answer +1 -5
- data/exe/exa-ai-context +1 -4
- data/exe/exa-ai-enrichment-cancel +107 -0
- data/exe/exa-ai-enrichment-create +235 -0
- data/exe/exa-ai-enrichment-delete +121 -0
- data/exe/exa-ai-enrichment-get +103 -0
- data/exe/exa-ai-enrichment-list +98 -0
- data/exe/exa-ai-enrichment-update +170 -0
- data/exe/exa-ai-find-similar +240 -0
- data/exe/exa-ai-get-contents +1 -4
- data/exe/exa-ai-research-get +1 -2
- data/exe/exa-ai-research-list +1 -2
- data/exe/exa-ai-research-start +1 -3
- data/exe/exa-ai-search +1 -3
- data/exe/exa-ai-webset-cancel +96 -0
- data/exe/exa-ai-webset-create +192 -0
- data/exe/exa-ai-webset-delete +110 -0
- data/exe/exa-ai-webset-get +92 -0
- data/exe/exa-ai-webset-item-delete +111 -0
- data/exe/exa-ai-webset-item-get +104 -0
- data/exe/exa-ai-webset-item-list +93 -0
- data/exe/exa-ai-webset-list +90 -0
- data/exe/exa-ai-webset-search-cancel +103 -0
- data/exe/exa-ai-webset-search-create +233 -0
- data/exe/exa-ai-webset-search-get +104 -0
- data/exe/exa-ai-webset-update +139 -0
- data/lib/exa/cli/base.rb +3 -3
- data/lib/exa/cli/formatters/enrichment_formatter.rb +69 -0
- data/lib/exa/cli/formatters/webset_formatter.rb +68 -0
- data/lib/exa/cli/formatters/webset_item_formatter.rb +69 -0
- data/lib/exa/client.rb +172 -0
- data/lib/exa/connection.rb +8 -1
- data/lib/exa/resources/webset.rb +74 -0
- data/lib/exa/resources/webset_collection.rb +33 -0
- data/lib/exa/resources/webset_enrichment.rb +71 -0
- data/lib/exa/resources/webset_enrichment_collection.rb +28 -0
- data/lib/exa/resources/webset_search.rb +112 -0
- data/lib/exa/services/parameter_converter.rb +1 -0
- data/lib/exa/services/websets/cancel.rb +36 -0
- data/lib/exa/services/websets/cancel_enrichment.rb +35 -0
- data/lib/exa/services/websets/cancel_search.rb +44 -0
- data/lib/exa/services/websets/create.rb +45 -0
- data/lib/exa/services/websets/create_enrichment.rb +35 -0
- data/lib/exa/services/websets/create_search.rb +48 -0
- data/lib/exa/services/websets/create_search_validator.rb +128 -0
- data/lib/exa/services/websets/create_validator.rb +189 -0
- data/lib/exa/services/websets/delete.rb +36 -0
- data/lib/exa/services/websets/delete_enrichment.rb +35 -0
- data/lib/exa/services/websets/delete_item.rb +20 -0
- data/lib/exa/services/websets/get_item.rb +20 -0
- data/lib/exa/services/websets/get_search.rb +43 -0
- data/lib/exa/services/websets/list.rb +25 -0
- data/lib/exa/services/websets/list_items.rb +20 -0
- data/lib/exa/services/websets/retrieve.rb +47 -0
- data/lib/exa/services/websets/retrieve_enrichment.rb +35 -0
- data/lib/exa/services/websets/update.rb +37 -0
- data/lib/exa/services/websets/update_enrichment.rb +36 -0
- data/lib/exa/services/websets_parameter_converter.rb +45 -0
- data/lib/exa/version.rb +1 -1
- data/lib/exa-ai.rb +5 -0
- data/lib/exa.rb +26 -0
- metadata +65 -3
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
# Parse arguments
|
|
8
|
+
webset_id = nil
|
|
9
|
+
metadata = nil
|
|
10
|
+
api_key = nil
|
|
11
|
+
output_format = "json"
|
|
12
|
+
|
|
13
|
+
# Recursively convert hash keys from strings to symbols
|
|
14
|
+
def deep_symbolize_keys(obj)
|
|
15
|
+
case obj
|
|
16
|
+
when Hash
|
|
17
|
+
obj.each_with_object({}) do |(key, value), result|
|
|
18
|
+
result[key.to_sym] = deep_symbolize_keys(value)
|
|
19
|
+
end
|
|
20
|
+
when Array
|
|
21
|
+
obj.map { |item| deep_symbolize_keys(item) }
|
|
22
|
+
else
|
|
23
|
+
obj
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Helper to parse JSON string or load from file
|
|
28
|
+
def parse_json_or_file(value)
|
|
29
|
+
json_data = if value.start_with?("@")
|
|
30
|
+
file_path = value[1..]
|
|
31
|
+
JSON.parse(File.read(file_path))
|
|
32
|
+
else
|
|
33
|
+
JSON.parse(value)
|
|
34
|
+
end
|
|
35
|
+
deep_symbolize_keys(json_data)
|
|
36
|
+
rescue JSON::ParserError => e
|
|
37
|
+
$stderr.puts "Error: Invalid JSON: #{e.message}"
|
|
38
|
+
exit 1
|
|
39
|
+
rescue Errno::ENOENT => e
|
|
40
|
+
$stderr.puts "Error: File not found: #{e.message}"
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
args = ARGV.dup
|
|
45
|
+
while args.any?
|
|
46
|
+
arg = args.shift
|
|
47
|
+
case arg
|
|
48
|
+
when "--metadata"
|
|
49
|
+
metadata = parse_json_or_file(args.shift)
|
|
50
|
+
when "--api-key"
|
|
51
|
+
api_key = args.shift
|
|
52
|
+
when "--output-format"
|
|
53
|
+
output_format = args.shift
|
|
54
|
+
when "--help", "-h"
|
|
55
|
+
puts <<~HELP
|
|
56
|
+
Usage: exa-ai webset-update <webset_id> [OPTIONS]
|
|
57
|
+
|
|
58
|
+
Update a webset's metadata
|
|
59
|
+
|
|
60
|
+
Arguments:
|
|
61
|
+
webset_id ID of the webset to update (required)
|
|
62
|
+
|
|
63
|
+
Options:
|
|
64
|
+
--metadata JSON Custom metadata to update (supports @file.json)
|
|
65
|
+
--api-key KEY Exa API key (or set EXA_API_KEY env var)
|
|
66
|
+
--output-format FMT Output format: json, pretty, or text (default: json)
|
|
67
|
+
--help, -h Show this help message
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
exa-ai webset-update ws_abc123 --metadata '{"project":"Q1-2025"}'
|
|
71
|
+
exa-ai webset-update ws_abc123 --metadata @metadata.json
|
|
72
|
+
exa-ai webset-update ws_abc123 --metadata '{"tags":["important"]}' --output-format pretty
|
|
73
|
+
HELP
|
|
74
|
+
exit 0
|
|
75
|
+
else
|
|
76
|
+
# First positional argument is webset_id
|
|
77
|
+
if webset_id.nil?
|
|
78
|
+
webset_id = arg
|
|
79
|
+
else
|
|
80
|
+
$stderr.puts "Unknown option: #{arg}"
|
|
81
|
+
exit 1
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Validate required arguments
|
|
87
|
+
if webset_id.nil?
|
|
88
|
+
$stderr.puts "Error: webset_id argument is required"
|
|
89
|
+
$stderr.puts "Usage: exa-ai webset-update <webset_id> [OPTIONS]"
|
|
90
|
+
$stderr.puts "Try 'exa-ai webset-update --help' for more information"
|
|
91
|
+
exit 1
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if metadata.nil?
|
|
95
|
+
$stderr.puts "Error: --metadata is required"
|
|
96
|
+
$stderr.puts "Try 'exa-ai webset-update --help' for more information"
|
|
97
|
+
exit 1
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
begin
|
|
101
|
+
# Resolve API key and format
|
|
102
|
+
api_key = Exa::CLI::Base.resolve_api_key(api_key)
|
|
103
|
+
output_format = Exa::CLI::Base.resolve_output_format(output_format)
|
|
104
|
+
|
|
105
|
+
# Build client
|
|
106
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
107
|
+
|
|
108
|
+
# Update webset
|
|
109
|
+
webset = client.update_webset(webset_id, metadata: metadata)
|
|
110
|
+
|
|
111
|
+
# Format and output
|
|
112
|
+
output = Exa::CLI::Formatters::WebsetFormatter.format(webset, output_format)
|
|
113
|
+
puts output
|
|
114
|
+
|
|
115
|
+
rescue Exa::NotFound => e
|
|
116
|
+
$stderr.puts "Webset not found: #{e.message}"
|
|
117
|
+
exit 1
|
|
118
|
+
rescue Exa::ConfigurationError => e
|
|
119
|
+
$stderr.puts "Configuration error: #{e.message}"
|
|
120
|
+
exit 1
|
|
121
|
+
rescue Exa::Unauthorized => e
|
|
122
|
+
$stderr.puts "Authentication error: #{e.message}"
|
|
123
|
+
$stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
|
|
124
|
+
exit 1
|
|
125
|
+
rescue Exa::ClientError => e
|
|
126
|
+
$stderr.puts "Client error: #{e.message}"
|
|
127
|
+
exit 1
|
|
128
|
+
rescue Exa::ServerError => e
|
|
129
|
+
$stderr.puts "Server error: #{e.message}"
|
|
130
|
+
$stderr.puts "The Exa API may be experiencing issues. Please try again later."
|
|
131
|
+
exit 1
|
|
132
|
+
rescue Exa::Error => e
|
|
133
|
+
$stderr.puts "Error: #{e.message}"
|
|
134
|
+
exit 1
|
|
135
|
+
rescue StandardError => e
|
|
136
|
+
$stderr.puts "Unexpected error: #{e.message}"
|
|
137
|
+
$stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
|
|
138
|
+
exit 1
|
|
139
|
+
end
|
data/lib/exa/cli/base.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Exa
|
|
|
11
11
|
env_key = ENV["EXA_API_KEY"]
|
|
12
12
|
return env_key if env_key && !env_key.empty?
|
|
13
13
|
|
|
14
|
-
raise ConfigurationError,
|
|
14
|
+
raise Exa::ConfigurationError,
|
|
15
15
|
"Missing API key. Set EXA_API_KEY environment variable or use --api-key flag"
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -24,13 +24,13 @@ module Exa
|
|
|
24
24
|
|
|
25
25
|
return format if valid_formats.include?(format)
|
|
26
26
|
|
|
27
|
-
raise ConfigurationError,
|
|
27
|
+
raise Exa::ConfigurationError,
|
|
28
28
|
"Invalid output format: #{format}. Valid formats: #{valid_formats.join(', ')}"
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Build a client instance with the given API key
|
|
32
32
|
def self.build_client(api_key, **options)
|
|
33
|
-
Client.new(api_key: api_key, **options)
|
|
33
|
+
Exa::Client.new(api_key: api_key, **options)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Format output data based on format type
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exa
|
|
4
|
+
module CLI
|
|
5
|
+
module Formatters
|
|
6
|
+
class EnrichmentFormatter
|
|
7
|
+
def self.format(enrichment, output_format)
|
|
8
|
+
case output_format
|
|
9
|
+
when "json"
|
|
10
|
+
JSON.generate(enrichment.to_h)
|
|
11
|
+
when "pretty"
|
|
12
|
+
JSON.pretty_generate(enrichment.to_h)
|
|
13
|
+
when "text"
|
|
14
|
+
format_as_text(enrichment)
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.format_collection(collection, output_format)
|
|
21
|
+
case output_format
|
|
22
|
+
when "json"
|
|
23
|
+
JSON.generate(collection.to_h)
|
|
24
|
+
when "pretty"
|
|
25
|
+
JSON.pretty_generate(collection.to_h)
|
|
26
|
+
when "text"
|
|
27
|
+
format_collection_as_text(collection)
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.format_as_text(enrichment)
|
|
34
|
+
lines = []
|
|
35
|
+
lines << "Enrichment: #{enrichment.id}"
|
|
36
|
+
lines << "Webset: #{enrichment.webset_id}" if enrichment.webset_id
|
|
37
|
+
lines << "Status: #{enrichment.status}"
|
|
38
|
+
lines << "Title: #{enrichment.title}" if enrichment.title
|
|
39
|
+
lines << "Description: #{enrichment.description}" if enrichment.description
|
|
40
|
+
lines << "Format: #{enrichment.format}" if enrichment.format
|
|
41
|
+
|
|
42
|
+
if enrichment.options && !enrichment.options.empty?
|
|
43
|
+
lines << "\nOptions:"
|
|
44
|
+
enrichment.options.each do |option|
|
|
45
|
+
lines << " - #{option['label']}" if option['label']
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
lines << "Created: #{enrichment.created_at}" if enrichment.created_at
|
|
50
|
+
lines << "Updated: #{enrichment.updated_at}" if enrichment.updated_at
|
|
51
|
+
|
|
52
|
+
lines.join("\n")
|
|
53
|
+
end
|
|
54
|
+
private_class_method :format_as_text
|
|
55
|
+
|
|
56
|
+
def self.format_collection_as_text(collection)
|
|
57
|
+
lines = ["Enrichments (#{collection.data.length} items):"]
|
|
58
|
+
collection.data.each do |enr|
|
|
59
|
+
lines << "\n #{enr['id']}"
|
|
60
|
+
lines << " Status: #{enr['status']}"
|
|
61
|
+
lines << " Title: #{enr['title']}" if enr['title']
|
|
62
|
+
end
|
|
63
|
+
lines.join("\n")
|
|
64
|
+
end
|
|
65
|
+
private_class_method :format_collection_as_text
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exa
|
|
4
|
+
module CLI
|
|
5
|
+
module Formatters
|
|
6
|
+
class WebsetFormatter
|
|
7
|
+
def self.format(webset, output_format)
|
|
8
|
+
case output_format
|
|
9
|
+
when "json"
|
|
10
|
+
JSON.generate(webset.to_h)
|
|
11
|
+
when "pretty"
|
|
12
|
+
JSON.pretty_generate(webset.to_h)
|
|
13
|
+
when "text"
|
|
14
|
+
format_as_text(webset)
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.format_collection(collection, output_format)
|
|
21
|
+
case output_format
|
|
22
|
+
when "json"
|
|
23
|
+
JSON.generate(collection.to_h)
|
|
24
|
+
when "pretty"
|
|
25
|
+
JSON.pretty_generate(collection.to_h)
|
|
26
|
+
when "text"
|
|
27
|
+
format_collection_as_text(collection)
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.format_as_text(webset)
|
|
34
|
+
lines = []
|
|
35
|
+
lines << "Webset: #{webset.id}"
|
|
36
|
+
lines << "Status: #{webset.status}"
|
|
37
|
+
lines << "Created: #{webset.created_at}" if webset.created_at
|
|
38
|
+
lines << "Updated: #{webset.updated_at}" if webset.updated_at
|
|
39
|
+
|
|
40
|
+
if webset.searches && !webset.searches.empty?
|
|
41
|
+
lines << "\nSearches:"
|
|
42
|
+
webset.searches.each do |search|
|
|
43
|
+
lines << " - #{search['query']}" if search['query']
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if webset.enrichments && !webset.enrichments.empty?
|
|
48
|
+
lines << "\nEnrichments: #{webset.enrichments.length}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
lines.join("\n")
|
|
52
|
+
end
|
|
53
|
+
private_class_method :format_as_text
|
|
54
|
+
|
|
55
|
+
def self.format_collection_as_text(collection)
|
|
56
|
+
lines = ["Websets (#{collection.data.length} items):"]
|
|
57
|
+
collection.data.each do |ws|
|
|
58
|
+
lines << "\n #{ws['id']}"
|
|
59
|
+
lines << " Status: #{ws['status']}"
|
|
60
|
+
lines << " Created: #{ws['createdAt']}" if ws['createdAt']
|
|
61
|
+
end
|
|
62
|
+
lines.join("\n")
|
|
63
|
+
end
|
|
64
|
+
private_class_method :format_collection_as_text
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exa
|
|
4
|
+
module CLI
|
|
5
|
+
module Formatters
|
|
6
|
+
class WebsetItemFormatter
|
|
7
|
+
def self.format(item, output_format)
|
|
8
|
+
case output_format
|
|
9
|
+
when "json"
|
|
10
|
+
JSON.generate(item)
|
|
11
|
+
when "pretty"
|
|
12
|
+
JSON.pretty_generate(item)
|
|
13
|
+
when "text"
|
|
14
|
+
format_as_text(item)
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.format_collection(items, output_format)
|
|
21
|
+
case output_format
|
|
22
|
+
when "json"
|
|
23
|
+
JSON.generate(items)
|
|
24
|
+
when "pretty"
|
|
25
|
+
JSON.pretty_generate(items)
|
|
26
|
+
when "text"
|
|
27
|
+
format_collection_as_text(items)
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Unknown output format: #{output_format}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.format_as_text(item)
|
|
34
|
+
lines = []
|
|
35
|
+
lines << "Item: #{item['id']}"
|
|
36
|
+
lines << "URL: #{item['url']}" if item['url']
|
|
37
|
+
lines << "Title: #{item['title']}" if item['title']
|
|
38
|
+
lines << "Status: #{item['status']}" if item['status']
|
|
39
|
+
lines << "Created: #{item['createdAt']}" if item['createdAt']
|
|
40
|
+
lines << "Updated: #{item['updatedAt']}" if item['updatedAt']
|
|
41
|
+
|
|
42
|
+
if item['entity']
|
|
43
|
+
lines << "\nEntity:"
|
|
44
|
+
lines << " Type: #{item['entity']['type']}" if item['entity']['type']
|
|
45
|
+
lines << " Name: #{item['entity']['name']}" if item['entity']['name']
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
lines.join("\n")
|
|
49
|
+
end
|
|
50
|
+
private_class_method :format_as_text
|
|
51
|
+
|
|
52
|
+
def self.format_collection_as_text(items)
|
|
53
|
+
lines = ["Items (#{items.length} total):"]
|
|
54
|
+
items.each_with_index do |item, idx|
|
|
55
|
+
lines << "\n#{idx + 1}. #{item['id']}"
|
|
56
|
+
lines << " URL: #{item['url']}" if item['url']
|
|
57
|
+
lines << " Title: #{item['title']}" if item['title']
|
|
58
|
+
lines << " Status: #{item['status']}" if item['status']
|
|
59
|
+
if item['entity'] && item['entity']['name']
|
|
60
|
+
lines << " Entity: #{item['entity']['name']}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
lines.join("\n")
|
|
64
|
+
end
|
|
65
|
+
private_class_method :format_collection_as_text
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/exa/client.rb
CHANGED
|
@@ -148,6 +148,177 @@ module Exa
|
|
|
148
148
|
search(query, type: "keyword", includeDomains: ["linkedin.com/in"], **params)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
# List all websets
|
|
152
|
+
#
|
|
153
|
+
# @param params [Hash] Pagination parameters
|
|
154
|
+
# @option params [String] :cursor Cursor for pagination
|
|
155
|
+
# @option params [Integer] :limit Maximum number of websets to return
|
|
156
|
+
# @return [Resources::WebsetCollection] Paginated list of websets
|
|
157
|
+
def list_websets(**params)
|
|
158
|
+
Services::Websets::List.new(connection, **params).call
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Get a specific webset by ID
|
|
162
|
+
#
|
|
163
|
+
# @param id [String] Webset ID
|
|
164
|
+
# @param params [Hash] Optional parameters
|
|
165
|
+
# @option params [Array<String>] :expand Resources to expand in response (e.g., ['items'])
|
|
166
|
+
# @return [Resources::Webset] The requested webset
|
|
167
|
+
def get_webset(id, **params)
|
|
168
|
+
Services::Websets::Retrieve.new(connection, id: id, **params).call
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Delete a webset
|
|
172
|
+
#
|
|
173
|
+
# @param id [String] Webset ID
|
|
174
|
+
# @return [Resources::Webset] The deleted webset
|
|
175
|
+
def delete_webset(id)
|
|
176
|
+
Services::Websets::Delete.new(connection, id: id).call
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Cancel in-progress operations on a webset
|
|
180
|
+
#
|
|
181
|
+
# @param id [String] Webset ID
|
|
182
|
+
# @return [Resources::Webset] The webset with cancelled operations
|
|
183
|
+
def cancel_webset(id)
|
|
184
|
+
Services::Websets::Cancel.new(connection, id: id).call
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Update a webset's metadata
|
|
188
|
+
#
|
|
189
|
+
# @param id [String] Webset ID
|
|
190
|
+
# @param params [Hash] Update parameters
|
|
191
|
+
# @option params [Hash] :metadata Metadata to update
|
|
192
|
+
# @return [Resources::Webset] The updated webset
|
|
193
|
+
def update_webset(id, **params)
|
|
194
|
+
Services::Websets::Update.new(connection, id: id, **params).call
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Create a new webset
|
|
198
|
+
#
|
|
199
|
+
# @param params [Hash] Creation parameters
|
|
200
|
+
# @option params [Hash] :search Search configuration
|
|
201
|
+
# @return [Resources::Webset] The newly created webset
|
|
202
|
+
def create_webset(**params)
|
|
203
|
+
Services::Websets::Create.new(connection, **params).call
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Create a new enrichment for a webset
|
|
207
|
+
#
|
|
208
|
+
# @param webset_id [String] Webset ID
|
|
209
|
+
# @param params [Hash] Enrichment parameters
|
|
210
|
+
# @option params [String] :description Description of data to extract
|
|
211
|
+
# @option params [String] :format Format type (text, url, options, etc.)
|
|
212
|
+
# @option params [Array<Hash>] :options Options for enrichment
|
|
213
|
+
# @option params [Hash] :metadata Custom metadata
|
|
214
|
+
# @return [Resources::WebsetEnrichment] The newly created enrichment
|
|
215
|
+
def create_enrichment(webset_id:, **params)
|
|
216
|
+
Services::Websets::CreateEnrichment.new(connection, webset_id: webset_id, **params).call
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Get a specific enrichment by ID
|
|
220
|
+
#
|
|
221
|
+
# @param webset_id [String] Webset ID
|
|
222
|
+
# @param id [String] Enrichment ID
|
|
223
|
+
# @return [Resources::WebsetEnrichment] The requested enrichment
|
|
224
|
+
def get_enrichment(webset_id:, id:)
|
|
225
|
+
Services::Websets::RetrieveEnrichment.new(connection, webset_id: webset_id, id: id).call
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Update an enrichment
|
|
229
|
+
#
|
|
230
|
+
# @param webset_id [String] Webset ID
|
|
231
|
+
# @param id [String] Enrichment ID
|
|
232
|
+
# @param params [Hash] Update parameters
|
|
233
|
+
# @option params [String] :description Updated description
|
|
234
|
+
# @option params [String] :format Updated format
|
|
235
|
+
# @option params [Array<Hash>] :options Updated options
|
|
236
|
+
# @option params [Hash] :metadata Updated metadata
|
|
237
|
+
# @return [Resources::WebsetEnrichment] The updated enrichment
|
|
238
|
+
def update_enrichment(webset_id:, id:, **params)
|
|
239
|
+
Services::Websets::UpdateEnrichment.new(connection, webset_id: webset_id, id: id, **params).call
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Delete an enrichment
|
|
243
|
+
#
|
|
244
|
+
# @param webset_id [String] Webset ID
|
|
245
|
+
# @param id [String] Enrichment ID
|
|
246
|
+
# @return [Resources::WebsetEnrichment] The deleted enrichment
|
|
247
|
+
def delete_enrichment(webset_id:, id:)
|
|
248
|
+
Services::Websets::DeleteEnrichment.new(connection, webset_id: webset_id, id: id).call
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Cancel a running enrichment
|
|
252
|
+
#
|
|
253
|
+
# @param webset_id [String] Webset ID
|
|
254
|
+
# @param id [String] Enrichment ID
|
|
255
|
+
# @return [Resources::WebsetEnrichment] The cancelled enrichment
|
|
256
|
+
def cancel_enrichment(webset_id:, id:)
|
|
257
|
+
Services::Websets::CancelEnrichment.new(connection, webset_id: webset_id, id: id).call
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Create a new search within a webset
|
|
261
|
+
#
|
|
262
|
+
# @param webset_id [String] Webset ID
|
|
263
|
+
# @param params [Hash] Search parameters
|
|
264
|
+
# @option params [String] :query The search query (required)
|
|
265
|
+
# @option params [Integer] :count Number of results to find
|
|
266
|
+
# @option params [Hash] :entity Entity type specification
|
|
267
|
+
# @option params [Array<Hash>] :criteria Search criteria
|
|
268
|
+
# @option params [Array<Hash>] :exclude Items to exclude from results
|
|
269
|
+
# @option params [Array<Hash>] :scope Limit search to specific sources
|
|
270
|
+
# @option params [Boolean] :recall Whether to estimate total available results
|
|
271
|
+
# @option params [String] :behavior "override" or "append" (default: "override")
|
|
272
|
+
# @option params [Hash] :metadata Custom metadata
|
|
273
|
+
# @return [Resources::WebsetSearch] The newly created search
|
|
274
|
+
def create_webset_search(webset_id:, **params)
|
|
275
|
+
Services::Websets::CreateSearch.new(connection, webset_id: webset_id, **params).call
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Get a webset search by ID
|
|
279
|
+
#
|
|
280
|
+
# @param webset_id [String] Webset ID
|
|
281
|
+
# @param id [String] Search ID
|
|
282
|
+
# @return [Resources::WebsetSearch] The requested search
|
|
283
|
+
def get_webset_search(webset_id:, id:)
|
|
284
|
+
Services::Websets::GetSearch.new(connection, webset_id: webset_id, id: id).call
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Cancel a webset search
|
|
288
|
+
#
|
|
289
|
+
# @param webset_id [String] Webset ID
|
|
290
|
+
# @param id [String] Search ID
|
|
291
|
+
# @return [Resources::WebsetSearch] The cancelled search
|
|
292
|
+
def cancel_webset_search(webset_id:, id:)
|
|
293
|
+
Services::Websets::CancelSearch.new(connection, webset_id: webset_id, id: id).call
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Get a webset item by ID
|
|
297
|
+
#
|
|
298
|
+
# @param webset_id [String] Webset ID
|
|
299
|
+
# @param id [String] Item ID
|
|
300
|
+
# @return [Hash] The requested item
|
|
301
|
+
def get_item(webset_id:, id:)
|
|
302
|
+
Services::Websets::GetItem.new(connection, webset_id: webset_id, id: id).call
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Delete a webset item by ID
|
|
306
|
+
#
|
|
307
|
+
# @param webset_id [String] Webset ID
|
|
308
|
+
# @param id [String] Item ID
|
|
309
|
+
# @return [Boolean] True if deletion was successful
|
|
310
|
+
def delete_item(webset_id:, id:)
|
|
311
|
+
Services::Websets::DeleteItem.new(connection, webset_id: webset_id, id: id).call
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# List all items in a webset
|
|
315
|
+
#
|
|
316
|
+
# @param webset_id [String] Webset ID
|
|
317
|
+
# @return [Array<Hash>] Array of items
|
|
318
|
+
def list_items(webset_id:)
|
|
319
|
+
Services::Websets::ListItems.new(connection, webset_id: webset_id).call
|
|
320
|
+
end
|
|
321
|
+
|
|
151
322
|
private
|
|
152
323
|
|
|
153
324
|
def connection
|
|
@@ -161,6 +332,7 @@ module Exa
|
|
|
161
332
|
options = {}
|
|
162
333
|
options[:base_url] = @options[:base_url] if @options[:base_url]
|
|
163
334
|
options[:timeout] = @options[:timeout] if @options[:timeout]
|
|
335
|
+
options[:debug] = true if ENV["EXA_DEBUG"]
|
|
164
336
|
options
|
|
165
337
|
end
|
|
166
338
|
|
data/lib/exa/connection.rb
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
|
+
require "logger"
|
|
4
5
|
|
|
5
6
|
module Exa
|
|
6
7
|
class Connection
|
|
8
|
+
DEFAULT_BASE_URL = "https://api.exa.ai"
|
|
7
9
|
def self.build(api_key:, **options, &block)
|
|
8
10
|
Faraday.new(url: options[:base_url] || DEFAULT_BASE_URL) do |conn|
|
|
9
11
|
# Authentication
|
|
10
|
-
conn.
|
|
12
|
+
conn.headers["x-api-key"] = api_key
|
|
11
13
|
|
|
12
14
|
# Request/Response JSON encoding
|
|
13
15
|
conn.request :json
|
|
14
16
|
|
|
17
|
+
# Debug logging (when enabled via option)
|
|
18
|
+
if options[:debug]
|
|
19
|
+
conn.response :logger, Logger.new($stdout), headers: true, bodies: true
|
|
20
|
+
end
|
|
21
|
+
|
|
15
22
|
# Custom error handling (registered before JSON so it runs after in response chain)
|
|
16
23
|
conn.response :raise_error
|
|
17
24
|
conn.response :json, content_type: /\bjson$/
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exa
|
|
4
|
+
module Resources
|
|
5
|
+
# Represents a webset from the Exa API
|
|
6
|
+
#
|
|
7
|
+
# A webset is a collection of web entities (companies, people, etc.)
|
|
8
|
+
# discovered through searches, imports, and enrichments.
|
|
9
|
+
class Webset < Struct.new(
|
|
10
|
+
:id,
|
|
11
|
+
:object,
|
|
12
|
+
:status,
|
|
13
|
+
:external_id,
|
|
14
|
+
:title,
|
|
15
|
+
:searches,
|
|
16
|
+
:imports,
|
|
17
|
+
:enrichments,
|
|
18
|
+
:monitors,
|
|
19
|
+
:excludes,
|
|
20
|
+
:metadata,
|
|
21
|
+
:created_at,
|
|
22
|
+
:updated_at,
|
|
23
|
+
:items,
|
|
24
|
+
keyword_init: true
|
|
25
|
+
)
|
|
26
|
+
def initialize(
|
|
27
|
+
id:,
|
|
28
|
+
object:,
|
|
29
|
+
status:,
|
|
30
|
+
external_id: nil,
|
|
31
|
+
title: nil,
|
|
32
|
+
searches: nil,
|
|
33
|
+
imports: nil,
|
|
34
|
+
enrichments: nil,
|
|
35
|
+
monitors: nil,
|
|
36
|
+
excludes: nil,
|
|
37
|
+
metadata: nil,
|
|
38
|
+
created_at: nil,
|
|
39
|
+
updated_at: nil,
|
|
40
|
+
items: nil
|
|
41
|
+
)
|
|
42
|
+
super
|
|
43
|
+
freeze
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def idle?
|
|
47
|
+
status == "idle"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def processing?
|
|
51
|
+
status == "processing"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def to_h
|
|
55
|
+
{
|
|
56
|
+
id: id,
|
|
57
|
+
object: object,
|
|
58
|
+
status: status,
|
|
59
|
+
external_id: external_id,
|
|
60
|
+
title: title,
|
|
61
|
+
searches: searches,
|
|
62
|
+
imports: imports,
|
|
63
|
+
enrichments: enrichments,
|
|
64
|
+
monitors: monitors,
|
|
65
|
+
excludes: excludes,
|
|
66
|
+
metadata: metadata,
|
|
67
|
+
created_at: created_at,
|
|
68
|
+
updated_at: updated_at,
|
|
69
|
+
items: items
|
|
70
|
+
}.compact
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Exa
|
|
4
|
+
module Resources
|
|
5
|
+
# Represents a paginated list of websets from the Exa API
|
|
6
|
+
#
|
|
7
|
+
# This class wraps the JSON response from the GET /websets/v0/websets endpoint
|
|
8
|
+
# and provides pagination support.
|
|
9
|
+
class WebsetCollection < Struct.new(
|
|
10
|
+
:data,
|
|
11
|
+
:has_more,
|
|
12
|
+
:next_cursor,
|
|
13
|
+
keyword_init: true
|
|
14
|
+
)
|
|
15
|
+
def initialize(data:, has_more: false, next_cursor: nil)
|
|
16
|
+
super
|
|
17
|
+
freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def empty?
|
|
21
|
+
data.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
{
|
|
26
|
+
data: data,
|
|
27
|
+
has_more: has_more,
|
|
28
|
+
next_cursor: next_cursor
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|