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,233 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Recursively convert hash keys from strings to symbols
7
+ def deep_symbolize_keys(obj)
8
+ case obj
9
+ when Hash
10
+ obj.each_with_object({}) do |(key, value), result|
11
+ result[key.to_sym] = deep_symbolize_keys(value)
12
+ end
13
+ when Array
14
+ obj.map { |item| deep_symbolize_keys(item) }
15
+ else
16
+ obj
17
+ end
18
+ end
19
+
20
+ # Parse JSON string or load from file (supports @file.json syntax)
21
+ def parse_json_or_file(value)
22
+ json_data = if value.start_with?("@")
23
+ file_path = value[1..]
24
+ JSON.parse(File.read(file_path))
25
+ else
26
+ JSON.parse(value)
27
+ end
28
+ deep_symbolize_keys(json_data)
29
+ rescue JSON::ParserError => e
30
+ $stderr.puts "Error: Invalid JSON: #{e.message}"
31
+ exit 1
32
+ rescue Errno::ENOENT => e
33
+ $stderr.puts "Error: File not found: #{e.message}"
34
+ exit 1
35
+ end
36
+
37
+ # Parse command-line arguments
38
+ def parse_args(argv)
39
+ args = {
40
+ output_format: "json",
41
+ api_key: nil
42
+ }
43
+
44
+ # First positional argument is webset_id
45
+ webset_id_found = false
46
+
47
+ i = 0
48
+ while i < argv.length
49
+ arg = argv[i]
50
+ case arg
51
+ when "--query"
52
+ args[:query] = argv[i + 1]
53
+ i += 2
54
+ when "--count"
55
+ args[:count] = argv[i + 1].to_i
56
+ i += 2
57
+ when "--entity"
58
+ args[:entity] = argv[i + 1]
59
+ i += 2
60
+ when "--entity-description"
61
+ args[:entity_description] = argv[i + 1]
62
+ i += 2
63
+ when "--criteria"
64
+ args[:criteria] = parse_json_or_file(argv[i + 1])
65
+ i += 2
66
+ when "--exclude"
67
+ args[:exclude] = parse_json_or_file(argv[i + 1])
68
+ i += 2
69
+ when "--scope"
70
+ args[:scope] = parse_json_or_file(argv[i + 1])
71
+ i += 2
72
+ when "--recall"
73
+ args[:recall] = true
74
+ i += 1
75
+ when "--behavior"
76
+ behavior = argv[i + 1]
77
+ unless ["override", "append"].include?(behavior)
78
+ $stderr.puts "Error: Behavior must be 'override' or 'append'"
79
+ exit 1
80
+ end
81
+ args[:behavior] = behavior
82
+ i += 2
83
+ when "--metadata"
84
+ args[:metadata] = parse_json_or_file(argv[i + 1])
85
+ i += 2
86
+ when "--api-key"
87
+ args[:api_key] = argv[i + 1]
88
+ i += 2
89
+ when "--output-format"
90
+ args[:output_format] = argv[i + 1]
91
+ i += 2
92
+ when "--help", "-h"
93
+ puts <<~HELP
94
+ Usage: exa-ai webset-search-create <webset_id> [OPTIONS]
95
+
96
+ Create a new search within a webset
97
+
98
+ Arguments:
99
+ webset_id ID of the webset (required)
100
+
101
+ Options:
102
+ --query QUERY Search query (required)
103
+ --count N Number of results to find
104
+ --entity TYPE Entity type: person, company, article, research_paper, custom
105
+ --entity-description TXT Description for custom entity type (required with --entity custom)
106
+ --criteria JSON Search criteria array (supports @file.json)
107
+ --exclude JSON Items to exclude from results (supports @file.json)
108
+ --scope JSON Limit search to specific sources (supports @file.json)
109
+ --recall Estimate total available results
110
+ --behavior TYPE "override" or "append" (default: override)
111
+ --metadata JSON Custom metadata (supports @file.json)
112
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
113
+ --output-format FMT Output format: json, pretty, or text (default: json)
114
+ --help, -h Show this help message
115
+
116
+ Examples:
117
+ # Basic search
118
+ exa-ai webset-search-create ws_123 --query "AI startups"
119
+
120
+ # Search with entity type
121
+ exa-ai webset-search-create ws_123 --query "tech CEOs" --entity person
122
+ exa-ai webset-search-create ws_123 --query "Silicon Valley firms" --entity company
123
+
124
+ # Search with custom entity type
125
+ exa-ai webset-search-create ws_123 --query "Ford Mustang" \\
126
+ --entity custom --entity-description "vintage cars"
127
+
128
+ # Other options
129
+ exa-ai webset-search-create ws_123 --query "machine learning" --count 50
130
+ exa-ai webset-search-create ws_123 --query "research" --behavior append --recall
131
+ HELP
132
+ exit 0
133
+ else
134
+ # First positional argument is webset_id
135
+ unless webset_id_found
136
+ args[:webset_id] = arg
137
+ webset_id_found = true
138
+ else
139
+ $stderr.puts "Unknown option: #{arg}"
140
+ exit 1
141
+ end
142
+ i += 1
143
+ end
144
+ end
145
+
146
+ args
147
+ end
148
+
149
+ # Main execution
150
+ begin
151
+ args = parse_args(ARGV)
152
+
153
+ # Validate required parameters
154
+ unless args[:webset_id]
155
+ $stderr.puts "Error: webset_id argument is required"
156
+ $stderr.puts "Run 'exa-ai webset-search-create --help' for usage information"
157
+ exit 1
158
+ end
159
+
160
+ unless args[:query]
161
+ $stderr.puts "Error: --query is required"
162
+ $stderr.puts "Run 'exa-ai webset-search-create --help' for usage information"
163
+ exit 1
164
+ end
165
+
166
+ # Resolve API key
167
+ api_key = Exa::CLI::Base.resolve_api_key(args[:api_key])
168
+
169
+ # Resolve output format
170
+ output_format = Exa::CLI::Base.resolve_output_format(args[:output_format])
171
+
172
+ # Build client
173
+ client = Exa::CLI::Base.build_client(api_key)
174
+
175
+ # Build entity parameter from --entity and --entity-description
176
+ entity = nil
177
+ if args[:entity]
178
+ # Build entity hash from string type
179
+ entity = { type: args[:entity] }
180
+ if args[:entity] == "custom"
181
+ unless args[:entity_description]
182
+ $stderr.puts "Error: --entity-description is required when --entity is 'custom'"
183
+ exit 1
184
+ end
185
+ entity[:description] = args[:entity_description]
186
+ elsif args[:entity_description]
187
+ $stderr.puts "Warning: --entity-description is only used with --entity custom (ignoring)"
188
+ end
189
+ end
190
+
191
+ # Prepare search parameters
192
+ search_params = { query: args[:query] }
193
+ search_params[:count] = args[:count] if args[:count]
194
+ search_params[:entity] = entity if entity
195
+ search_params[:criteria] = args[:criteria] if args[:criteria]
196
+ search_params[:exclude] = args[:exclude] if args[:exclude]
197
+ search_params[:scope] = args[:scope] if args[:scope]
198
+ search_params[:recall] = args[:recall] if args[:recall]
199
+ search_params[:behavior] = args[:behavior] if args[:behavior]
200
+ search_params[:metadata] = args[:metadata] if args[:metadata]
201
+
202
+ # Create search
203
+ search = client.create_webset_search(webset_id: args[:webset_id], **search_params)
204
+
205
+ # Format and output result
206
+ output = Exa::CLI::Formatters::SearchFormatter.format(search, output_format)
207
+ puts output
208
+
209
+ rescue Exa::ConfigurationError => e
210
+ $stderr.puts "Configuration error: #{e.message}"
211
+ exit 1
212
+ rescue Exa::Unauthorized => e
213
+ $stderr.puts "Authentication error: #{e.message}"
214
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
215
+ exit 1
216
+ rescue Exa::NotFound => e
217
+ $stderr.puts "Webset not found: #{e.message}"
218
+ exit 1
219
+ rescue Exa::ClientError => e
220
+ $stderr.puts "Client error: #{e.message}"
221
+ exit 1
222
+ rescue Exa::ServerError => e
223
+ $stderr.puts "Server error: #{e.message}"
224
+ $stderr.puts "The Exa API may be experiencing issues. Please try again later."
225
+ exit 1
226
+ rescue Exa::Error => e
227
+ $stderr.puts "Error: #{e.message}"
228
+ exit 1
229
+ rescue StandardError => e
230
+ $stderr.puts "Unexpected error: #{e.message}"
231
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
232
+ exit 1
233
+ end
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "exa-ai"
5
+
6
+ # Parse arguments
7
+ webset_id = nil
8
+ search_id = nil
9
+ api_key = nil
10
+ output_format = "json"
11
+
12
+ args = ARGV.dup
13
+ while args.any?
14
+ arg = args.shift
15
+ case arg
16
+ when "--api-key"
17
+ api_key = args.shift
18
+ when "--output-format"
19
+ output_format = args.shift
20
+ when "--help", "-h"
21
+ puts <<~HELP
22
+ Usage: exa-ai webset-search-get <webset_id> <search_id> [OPTIONS]
23
+
24
+ Get details of a webset search
25
+
26
+ Arguments:
27
+ webset_id ID of the webset (required)
28
+ search_id ID of the search (required)
29
+
30
+ Options:
31
+ --api-key KEY Exa API key (or set EXA_API_KEY env var)
32
+ --output-format FMT Output format: json, pretty, or text (default: json)
33
+ --help, -h Show this help message
34
+
35
+ Examples:
36
+ exa-ai webset-search-get ws_123 search_456
37
+ exa-ai webset-search-get ws_123 search_456 --output-format pretty
38
+ exa-ai webset-search-get ws_123 search_456 --output-format text
39
+ HELP
40
+ exit 0
41
+ else
42
+ # First positional argument is webset_id, second is search_id
43
+ if webset_id.nil?
44
+ webset_id = arg
45
+ elsif search_id.nil?
46
+ search_id = arg
47
+ else
48
+ $stderr.puts "Unknown option: #{arg}"
49
+ exit 1
50
+ end
51
+ end
52
+ end
53
+
54
+ # Validate
55
+ if webset_id.nil?
56
+ $stderr.puts "Error: webset_id argument is required"
57
+ $stderr.puts "Usage: exa-ai webset-search-get <webset_id> <search_id> [OPTIONS]"
58
+ $stderr.puts "Try 'exa-ai webset-search-get --help' for more information"
59
+ exit 1
60
+ end
61
+
62
+ if search_id.nil?
63
+ $stderr.puts "Error: search_id argument is required"
64
+ $stderr.puts "Usage: exa-ai webset-search-get <webset_id> <search_id> [OPTIONS]"
65
+ $stderr.puts "Try 'exa-ai webset-search-get --help' for more information"
66
+ exit 1
67
+ end
68
+
69
+ begin
70
+ # Resolve API key and format
71
+ api_key = Exa::CLI::Base.resolve_api_key(api_key)
72
+ output_format = Exa::CLI::Base.resolve_output_format(output_format)
73
+
74
+ # Build client
75
+ client = Exa::CLI::Base.build_client(api_key)
76
+
77
+ # Get search
78
+ search = client.get_webset_search(webset_id: webset_id, id: search_id)
79
+
80
+ # Format and output
81
+ output = Exa::CLI::Formatters::SearchFormatter.format(search, output_format)
82
+ puts output
83
+
84
+ rescue Exa::NotFound => e
85
+ $stderr.puts "Search not found: #{e.message}"
86
+ exit 1
87
+ rescue Exa::Unauthorized => e
88
+ $stderr.puts "Authentication error: #{e.message}"
89
+ $stderr.puts "Check your API key (set EXA_API_KEY or use --api-key)"
90
+ exit 1
91
+ rescue Exa::ClientError => e
92
+ $stderr.puts "Client error: #{e.message}"
93
+ exit 1
94
+ rescue Exa::ServerError => e
95
+ $stderr.puts "Server error: #{e.message}"
96
+ exit 1
97
+ rescue Exa::Error => e
98
+ $stderr.puts "Error: #{e.message}"
99
+ exit 1
100
+ rescue StandardError => e
101
+ $stderr.puts "Unexpected error: #{e.message}"
102
+ $stderr.puts e.backtrace.first(5) if ENV["DEBUG"]
103
+ exit 1
104
+ end
@@ -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
@@ -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