exa-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +562 -0
- data/exe/exa-ai +95 -0
- data/exe/exa-ai-answer +131 -0
- data/exe/exa-ai-context +104 -0
- data/exe/exa-ai-get-contents +114 -0
- data/exe/exa-ai-research-get +110 -0
- data/exe/exa-ai-research-list +95 -0
- data/exe/exa-ai-research-start +175 -0
- data/exe/exa-ai-search +134 -0
- data/lib/exa/cli/base.rb +51 -0
- data/lib/exa/cli/error_handler.rb +98 -0
- data/lib/exa/cli/formatters/answer_formatter.rb +63 -0
- data/lib/exa/cli/formatters/contents_formatter.rb +50 -0
- data/lib/exa/cli/formatters/context_formatter.rb +58 -0
- data/lib/exa/cli/formatters/research_formatter.rb +120 -0
- data/lib/exa/cli/formatters/search_formatter.rb +44 -0
- data/lib/exa/cli/polling.rb +46 -0
- data/lib/exa/client.rb +132 -0
- data/lib/exa/connection.rb +32 -0
- data/lib/exa/error.rb +31 -0
- data/lib/exa/middleware/raise_error.rb +55 -0
- data/lib/exa/resources/answer.rb +20 -0
- data/lib/exa/resources/contents_result.rb +29 -0
- data/lib/exa/resources/context_result.rb +37 -0
- data/lib/exa/resources/find_similar_result.rb +28 -0
- data/lib/exa/resources/research_list.rb +18 -0
- data/lib/exa/resources/research_task.rb +39 -0
- data/lib/exa/resources/search_result.rb +30 -0
- data/lib/exa/services/answer.rb +23 -0
- data/lib/exa/services/context.rb +27 -0
- data/lib/exa/services/find_similar.rb +26 -0
- data/lib/exa/services/get_contents.rb +25 -0
- data/lib/exa/services/research_get.rb +30 -0
- data/lib/exa/services/research_list.rb +37 -0
- data/lib/exa/services/research_start.rb +26 -0
- data/lib/exa/services/search.rb +26 -0
- data/lib/exa/version.rb +5 -0
- data/lib/exa.rb +54 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ca5a6bcb0b981d51fcc93e1dc9f1e7037d1d6346e082826e1b1c003d586cce45
|
4
|
+
data.tar.gz: e598e0c91c2815a5abea958adb9f5112def140b78f13ba29872d07009c4056cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b648ebe500a28dbb8d31aa936fd0d30a09ff94c372a3f3caec463d648993ae26473fc9724aea0157395b8e78ac9a25fc3f2752c8d6fd1cc68745bb8758b0ca7
|
7
|
+
data.tar.gz: 512d79543838b4b82ba263586c9cd029f8e19f350ff9c744b85dcb5b8f4e62236b442b70d07316721bc844f6ca9ac79bac6c1b42d57d5f0ef1214c5be407818e
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Benjamin Jackson
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,562 @@
|
|
1
|
+
# exa-ruby
|
2
|
+
|
3
|
+
Ruby client for the Exa.ai API with comprehensive command-line interface.
|
4
|
+
|
5
|
+
**Status**: Phase 9 CLI implementation complete. All commands working with full option support.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add to your Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'exa-ai'
|
13
|
+
```
|
14
|
+
|
15
|
+
Then run:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install directly:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
gem install exa-ai
|
25
|
+
```
|
26
|
+
|
27
|
+
## Configuration
|
28
|
+
|
29
|
+
### Setting Your API Key
|
30
|
+
|
31
|
+
Get your API key from [dashboard.exa.ai](https://dashboard.exa.ai).
|
32
|
+
|
33
|
+
**Option 1: Environment Variable (recommended)**
|
34
|
+
|
35
|
+
```bash
|
36
|
+
export EXA_API_KEY="your-api-key-here"
|
37
|
+
```
|
38
|
+
|
39
|
+
**Option 2: Ruby Code**
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'exa'
|
43
|
+
|
44
|
+
Exa.configure do |config|
|
45
|
+
config.api_key = "your-api-key-here"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Or pass directly to client
|
49
|
+
client = Exa::Client.new(api_key: "your-api-key-here")
|
50
|
+
```
|
51
|
+
|
52
|
+
**Option 3: CLI Flag**
|
53
|
+
|
54
|
+
```bash
|
55
|
+
exa-ai search "query" --api-key YOUR_API_KEY
|
56
|
+
```
|
57
|
+
|
58
|
+
## Ruby API Usage
|
59
|
+
|
60
|
+
### Quick Start
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
require 'exa'
|
64
|
+
|
65
|
+
Exa.configure do |config|
|
66
|
+
config.api_key = ENV['EXA_API_KEY']
|
67
|
+
end
|
68
|
+
|
69
|
+
client = Exa::Client.new
|
70
|
+
|
71
|
+
# Search the web
|
72
|
+
results = client.search("ruby programming")
|
73
|
+
puts results.results.first["title"]
|
74
|
+
|
75
|
+
# Get an answer to a question
|
76
|
+
answer = client.answer("What are the latest trends in AI?")
|
77
|
+
puts answer.answer
|
78
|
+
puts answer.citations
|
79
|
+
|
80
|
+
# Get code context
|
81
|
+
code = client.context("React hooks")
|
82
|
+
puts code.response
|
83
|
+
|
84
|
+
# Retrieve page contents
|
85
|
+
contents = client.get_contents(["https://example.com"])
|
86
|
+
puts contents.results.first["text"]
|
87
|
+
```
|
88
|
+
|
89
|
+
### Search
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
client = Exa::Client.new(api_key: "your-key")
|
93
|
+
|
94
|
+
# Basic search
|
95
|
+
results = client.search("machine learning")
|
96
|
+
|
97
|
+
# With options
|
98
|
+
results = client.search("AI",
|
99
|
+
num_results: 10,
|
100
|
+
type: "neural",
|
101
|
+
include_domains: ["arxiv.org", "github.com"]
|
102
|
+
)
|
103
|
+
|
104
|
+
# Access results
|
105
|
+
results.results.each do |item|
|
106
|
+
puts item["title"]
|
107
|
+
puts item["url"]
|
108
|
+
puts item["score"]
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
### Answer
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
client = Exa::Client.new(api_key: "your-key")
|
116
|
+
|
117
|
+
# Get an answer to a question
|
118
|
+
answer = client.answer("What are the best practices for API design?")
|
119
|
+
|
120
|
+
puts answer.answer # The generated answer
|
121
|
+
puts answer.citations # Array of source citations
|
122
|
+
puts answer.cost_dollars # API cost
|
123
|
+
|
124
|
+
# With text content from sources
|
125
|
+
answer = client.answer("Latest AI breakthroughs", text: true)
|
126
|
+
puts answer.answer
|
127
|
+
answer.citations.each do |citation|
|
128
|
+
puts "#{citation["title"]} (#{citation['url']})"
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
### Context (Code Search)
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
code = client.context("authentication in Rails")
|
136
|
+
|
137
|
+
puts code.response # The code context
|
138
|
+
puts code.results_count # Number of results
|
139
|
+
puts code.cost_dollars # API cost
|
140
|
+
```
|
141
|
+
|
142
|
+
### Get Contents
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
contents = client.get_contents([
|
146
|
+
"https://example.com/page1",
|
147
|
+
"https://example.com/page2"
|
148
|
+
])
|
149
|
+
|
150
|
+
contents.results.each do |content|
|
151
|
+
puts content["url"]
|
152
|
+
puts content["title"]
|
153
|
+
puts content["text"]
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
### Research (Async Tasks)
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
# Start a research task
|
161
|
+
task = client.research_start(
|
162
|
+
instructions: "Analyze recent AI breakthroughs",
|
163
|
+
model: "gpt-4"
|
164
|
+
)
|
165
|
+
puts task.research_id # Save for later polling
|
166
|
+
|
167
|
+
# Check status
|
168
|
+
status = client.research_get(task.research_id)
|
169
|
+
if status.completed?
|
170
|
+
puts status.output
|
171
|
+
end
|
172
|
+
|
173
|
+
# List all tasks
|
174
|
+
list = client.research_list(limit: 20)
|
175
|
+
list.data.each do |task|
|
176
|
+
puts "#{task.research_id}: #{task.status}"
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
## CLI Usage
|
181
|
+
|
182
|
+
### Main Commands
|
183
|
+
|
184
|
+
```bash
|
185
|
+
exa-ai <command> [options]
|
186
|
+
|
187
|
+
Commands:
|
188
|
+
search Search the web
|
189
|
+
answer Generate answers to questions
|
190
|
+
context Get code context from repositories
|
191
|
+
get-contents Retrieve page contents
|
192
|
+
research-start Start a research task
|
193
|
+
research-get Get research task status
|
194
|
+
research-list List research tasks
|
195
|
+
```
|
196
|
+
|
197
|
+
### Search Command
|
198
|
+
|
199
|
+
Search the web using Exa's neural search:
|
200
|
+
|
201
|
+
```bash
|
202
|
+
# Basic search
|
203
|
+
exa-ai search "ruby programming"
|
204
|
+
|
205
|
+
# With options
|
206
|
+
exa-ai search "machine learning" --num-results 10 --type keyword
|
207
|
+
|
208
|
+
# Filter by domains
|
209
|
+
exa-ai search "tutorials" \
|
210
|
+
--include-domains "github.com,dev.to" \
|
211
|
+
--exclude-domains "outdated-site.com"
|
212
|
+
|
213
|
+
# Pretty output
|
214
|
+
exa-ai search "AI" --output-format pretty
|
215
|
+
```
|
216
|
+
|
217
|
+
**Options:**
|
218
|
+
- `QUERY` - Search query (required)
|
219
|
+
- `--num-results N` - Number of results (default: 10)
|
220
|
+
- `--type TYPE` - Search type: keyword, neural, or auto (default: auto)
|
221
|
+
- `--include-domains DOMAINS` - Comma-separated domains to include
|
222
|
+
- `--exclude-domains DOMAINS` - Comma-separated domains to exclude
|
223
|
+
- `--use-autoprompt` - Use Exa's autoprompt feature
|
224
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
225
|
+
- `--api-key KEY` - API key (or set EXA_API_KEY env var)
|
226
|
+
|
227
|
+
### Answer Command
|
228
|
+
|
229
|
+
Generate comprehensive answers to questions using Exa's answer generation feature:
|
230
|
+
|
231
|
+
```bash
|
232
|
+
# Basic question
|
233
|
+
exa-ai answer "What is the capital of France?"
|
234
|
+
|
235
|
+
# Get answer with source citations
|
236
|
+
exa-ai answer "Latest developments in quantum computing"
|
237
|
+
|
238
|
+
# Include full text from sources
|
239
|
+
exa-ai answer "Ruby on Rails best practices" --text
|
240
|
+
|
241
|
+
# Pretty formatted output
|
242
|
+
exa-ai answer "How do I learn machine learning?" --output-format pretty
|
243
|
+
```
|
244
|
+
|
245
|
+
**Options:**
|
246
|
+
- `QUERY` - Question to answer (required)
|
247
|
+
- `--text` - Include full text content from source pages
|
248
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
249
|
+
- `--api-key KEY` - API key (or set EXA_API_KEY env var)
|
250
|
+
|
251
|
+
**Response fields:**
|
252
|
+
- `answer` - The generated answer to your question
|
253
|
+
- `citations` - Array of source citations with URLs
|
254
|
+
- `cost_dollars` - Cost of the API request
|
255
|
+
|
256
|
+
### Context Command (Code Search)
|
257
|
+
|
258
|
+
Find code snippets and context from open-source repositories:
|
259
|
+
|
260
|
+
```bash
|
261
|
+
# Basic query
|
262
|
+
exa-ai context "authentication with JWT"
|
263
|
+
|
264
|
+
# With custom token allocation
|
265
|
+
exa-ai context "React hooks" --tokens-num 5000
|
266
|
+
|
267
|
+
# Text output
|
268
|
+
exa-ai context "async/await patterns" --output-format text
|
269
|
+
```
|
270
|
+
|
271
|
+
**Options:**
|
272
|
+
- `QUERY` - Search query (required)
|
273
|
+
- `--tokens-num NUM` - Token allocation, integer or "dynamic" (default: dynamic)
|
274
|
+
- `--output-format FORMAT` - json or text (default: json)
|
275
|
+
- `--api-key KEY` - API key
|
276
|
+
|
277
|
+
### Get-Contents Command
|
278
|
+
|
279
|
+
Retrieve the full text content from web pages:
|
280
|
+
|
281
|
+
```bash
|
282
|
+
# Single page
|
283
|
+
exa-ai get-contents "https://example.com/article"
|
284
|
+
|
285
|
+
# Multiple pages (comma-separated)
|
286
|
+
exa-ai get-contents "https://site1.com,https://site2.com"
|
287
|
+
|
288
|
+
# With options
|
289
|
+
exa-ai get-contents "id1,id2,id3" \
|
290
|
+
--text \
|
291
|
+
--highlights \
|
292
|
+
--output-format pretty
|
293
|
+
```
|
294
|
+
|
295
|
+
**Options:**
|
296
|
+
- `IDS` - Page IDs or URLs (required, comma-separated)
|
297
|
+
- `--text` - Include full text content
|
298
|
+
- `--highlights` - Include highlighted sections
|
299
|
+
- `--summary` - Include summary
|
300
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
301
|
+
- `--api-key KEY` - API key
|
302
|
+
|
303
|
+
### Research Commands
|
304
|
+
|
305
|
+
Start and manage long-running research tasks:
|
306
|
+
|
307
|
+
#### research-start
|
308
|
+
|
309
|
+
```bash
|
310
|
+
# Start a task
|
311
|
+
exa-ai research-start --instructions "Find Ruby performance tips"
|
312
|
+
|
313
|
+
# Start and wait for completion
|
314
|
+
exa-ai research-start \
|
315
|
+
--instructions "Analyze AI safety papers" \
|
316
|
+
--model gpt-4 \
|
317
|
+
--wait
|
318
|
+
|
319
|
+
# With output schema
|
320
|
+
exa-ai research-start \
|
321
|
+
--instructions "Extract key metrics" \
|
322
|
+
--output-schema '{"format":"json","fields":["metric","value"]}'
|
323
|
+
```
|
324
|
+
|
325
|
+
**Options:**
|
326
|
+
- `--instructions TEXT` - Research instructions (required)
|
327
|
+
- `--model MODEL` - Model to use (e.g., gpt-4)
|
328
|
+
- `--output-schema SCHEMA` - JSON schema for structured output
|
329
|
+
- `--wait` - Wait for task to complete (with polling)
|
330
|
+
- `--events` - Show event log during polling
|
331
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
332
|
+
- `--api-key KEY` - API key
|
333
|
+
|
334
|
+
#### research-get
|
335
|
+
|
336
|
+
```bash
|
337
|
+
# Check task status
|
338
|
+
exa-ai research-get abc-123
|
339
|
+
|
340
|
+
# With events
|
341
|
+
exa-ai research-get abc-123 --events
|
342
|
+
|
343
|
+
# Pretty output
|
344
|
+
exa-ai research-get abc-123 --output-format pretty
|
345
|
+
```
|
346
|
+
|
347
|
+
**Options:**
|
348
|
+
- `RESEARCH_ID` - Task ID (required)
|
349
|
+
- `--events` - Include event log
|
350
|
+
- `--stream` - Stream results (premium feature)
|
351
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
352
|
+
- `--api-key KEY` - API key
|
353
|
+
|
354
|
+
#### research-list
|
355
|
+
|
356
|
+
```bash
|
357
|
+
# List all tasks
|
358
|
+
exa-ai research-list
|
359
|
+
|
360
|
+
# With pagination
|
361
|
+
exa-ai research-list --limit 20
|
362
|
+
|
363
|
+
# Next page
|
364
|
+
exa-ai research-list --cursor "next_page_cursor"
|
365
|
+
|
366
|
+
# Pretty table format
|
367
|
+
exa-ai research-list --output-format pretty
|
368
|
+
```
|
369
|
+
|
370
|
+
**Options:**
|
371
|
+
- `--cursor CURSOR` - Pagination cursor
|
372
|
+
- `--limit N` - Results per page (default: 10)
|
373
|
+
- `--output-format FORMAT` - json or pretty (default: json)
|
374
|
+
- `--api-key KEY` - API key
|
375
|
+
|
376
|
+
### Global Options
|
377
|
+
|
378
|
+
All commands support:
|
379
|
+
- `--api-key KEY` - Override API key
|
380
|
+
- `--output-format FORMAT` - json, pretty, or text (varies by command)
|
381
|
+
- `--help, -h` - Show command help
|
382
|
+
- `exa-ai --version` - Show version
|
383
|
+
- `exa-ai --help` - Show available commands
|
384
|
+
|
385
|
+
### Output Formats
|
386
|
+
|
387
|
+
**JSON (default)**
|
388
|
+
```bash
|
389
|
+
exa-ai search "ruby" --output-format json
|
390
|
+
# Returns formatted JSON object
|
391
|
+
```
|
392
|
+
|
393
|
+
**Pretty**
|
394
|
+
```bash
|
395
|
+
exa-ai search "ruby" --output-format pretty
|
396
|
+
# Returns human-readable format with titles, URLs, scores
|
397
|
+
```
|
398
|
+
|
399
|
+
**Text**
|
400
|
+
```bash
|
401
|
+
exa-ai context "React" --output-format text
|
402
|
+
# Returns plain text output
|
403
|
+
```
|
404
|
+
|
405
|
+
## Error Handling
|
406
|
+
|
407
|
+
The CLI provides helpful error messages:
|
408
|
+
|
409
|
+
```bash
|
410
|
+
# Missing API key
|
411
|
+
$ exa search "test"
|
412
|
+
❌ Configuration Error
|
413
|
+
|
414
|
+
Missing API key. Set EXA_API_KEY or use --api-key
|
415
|
+
|
416
|
+
Solutions:
|
417
|
+
1. Set the EXA_API_KEY environment variable:
|
418
|
+
export EXA_API_KEY='your-api-key'
|
419
|
+
...
|
420
|
+
|
421
|
+
# Invalid credentials
|
422
|
+
$ exa search "test" --api-key bad-key
|
423
|
+
❌ Authentication Error
|
424
|
+
|
425
|
+
Your API key is invalid or has expired.
|
426
|
+
...
|
427
|
+
|
428
|
+
# Rate limited
|
429
|
+
$ exa search "test" # After many requests
|
430
|
+
❌ Request Error
|
431
|
+
|
432
|
+
You've exceeded the rate limit. Please wait before trying again.
|
433
|
+
```
|
434
|
+
|
435
|
+
### Ruby API Error Handling
|
436
|
+
|
437
|
+
```ruby
|
438
|
+
client = Exa::Client.new(api_key: "test")
|
439
|
+
|
440
|
+
begin
|
441
|
+
results = client.search("test")
|
442
|
+
rescue Exa::Unauthorized => e
|
443
|
+
puts "Invalid API key: #{e.message}"
|
444
|
+
rescue Exa::TooManyRequests => e
|
445
|
+
puts "Rate limited, please wait"
|
446
|
+
rescue Exa::ServerError => e
|
447
|
+
puts "API error: #{e.message}"
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
## Examples
|
452
|
+
|
453
|
+
### CLI Examples
|
454
|
+
|
455
|
+
```bash
|
456
|
+
# Find Ruby tutorials
|
457
|
+
exa search "Ruby best practices" --num-results 5
|
458
|
+
|
459
|
+
# Get an answer to a question
|
460
|
+
exa answer "What is machine learning?"
|
461
|
+
|
462
|
+
# Get code examples for async/await
|
463
|
+
exa context "async/await error handling"
|
464
|
+
|
465
|
+
# Research AI trends
|
466
|
+
exa research-start --instructions "What are latest AI trends?" --wait
|
467
|
+
|
468
|
+
# Retrieve and analyze multiple pages
|
469
|
+
exa get-contents "url1,url2,url3" --text --output-format pretty
|
470
|
+
```
|
471
|
+
|
472
|
+
### Ruby API Examples
|
473
|
+
|
474
|
+
```ruby
|
475
|
+
require 'exa'
|
476
|
+
|
477
|
+
Exa.configure do |config|
|
478
|
+
config.api_key = ENV['EXA_API_KEY']
|
479
|
+
end
|
480
|
+
|
481
|
+
client = Exa::Client.new
|
482
|
+
|
483
|
+
# Search with filtering
|
484
|
+
results = client.search("kubernetes tutorial",
|
485
|
+
num_results: 20,
|
486
|
+
type: "neural",
|
487
|
+
include_domains: ["kubernetes.io", "github.com"],
|
488
|
+
use_autoprompt: true
|
489
|
+
)
|
490
|
+
|
491
|
+
results.results.each do |item|
|
492
|
+
puts "#{item['title']} (#{item['url']})"
|
493
|
+
end
|
494
|
+
|
495
|
+
# Get code context
|
496
|
+
code_result = client.context("Docker best practices", tokens_num: 5000)
|
497
|
+
puts code_result.response
|
498
|
+
|
499
|
+
# Start async research
|
500
|
+
task = client.research_start(
|
501
|
+
instructions: "Summarize recent ML papers",
|
502
|
+
model: "gpt-4"
|
503
|
+
)
|
504
|
+
puts "Task started: #{task.research_id}"
|
505
|
+
|
506
|
+
# Poll for results
|
507
|
+
loop do
|
508
|
+
status = client.research_get(task.research_id)
|
509
|
+
break if status.completed? || status.failed?
|
510
|
+
sleep 5
|
511
|
+
end
|
512
|
+
|
513
|
+
if status.completed?
|
514
|
+
puts status.output
|
515
|
+
end
|
516
|
+
```
|
517
|
+
|
518
|
+
## Development
|
519
|
+
|
520
|
+
### Running Tests
|
521
|
+
|
522
|
+
```bash
|
523
|
+
# Run all tests
|
524
|
+
bundle exec rake test
|
525
|
+
|
526
|
+
# Run specific test file
|
527
|
+
bundle exec ruby test/cli/search_test.rb
|
528
|
+
|
529
|
+
# Run with verbose output
|
530
|
+
bundle exec rake test TESTOPTS="-v"
|
531
|
+
```
|
532
|
+
|
533
|
+
### Building the Gem
|
534
|
+
|
535
|
+
```bash
|
536
|
+
bundle exec rake build
|
537
|
+
bundle exec rake install
|
538
|
+
```
|
539
|
+
|
540
|
+
## Documentation
|
541
|
+
|
542
|
+
- [Exa API Documentation](https://docs.exa.ai)
|
543
|
+
- [API Reference](https://docs.exa.ai/reference)
|
544
|
+
- [Status Page](https://status.exa.ai)
|
545
|
+
|
546
|
+
## Support
|
547
|
+
|
548
|
+
- **Documentation**: https://docs.exa.ai
|
549
|
+
- **API Key**: https://dashboard.exa.ai
|
550
|
+
- **Status**: https://status.exa.ai
|
551
|
+
|
552
|
+
## License
|
553
|
+
|
554
|
+
MIT License - See LICENSE file for details
|
555
|
+
|
556
|
+
## Changelog
|
557
|
+
|
558
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|
559
|
+
|
560
|
+
---
|
561
|
+
|
562
|
+
**Built with Exa.ai** - Search and discovery API for the web
|
data/exe/exa-ai
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "exa"
|
6
|
+
require "exa/cli/base"
|
7
|
+
|
8
|
+
# Global CLI interface for Exa API
|
9
|
+
|
10
|
+
module ExaCLI
|
11
|
+
AVAILABLE_COMMANDS = {
|
12
|
+
"search" => "Search the web",
|
13
|
+
"answer" => "Generate an answer to a question",
|
14
|
+
"context" => "Get code context from repositories",
|
15
|
+
"get-contents" => "Retrieve page contents",
|
16
|
+
"research-start" => "Start a research task",
|
17
|
+
"research-get" => "Get research task status",
|
18
|
+
"research-list" => "List research tasks"
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def self.run
|
22
|
+
case ARGV[0]
|
23
|
+
when "--version"
|
24
|
+
puts Exa::VERSION
|
25
|
+
exit 0
|
26
|
+
when "--help", "help", "-h", nil
|
27
|
+
print_help
|
28
|
+
exit 0
|
29
|
+
when "--api-key"
|
30
|
+
puts "Use --api-key flag with specific commands"
|
31
|
+
exit 1
|
32
|
+
when "search"
|
33
|
+
exec File.expand_path("../exa-ai-search", __FILE__), *ARGV[1..]
|
34
|
+
when "answer"
|
35
|
+
exec File.expand_path("../exa-ai-answer", __FILE__), *ARGV[1..]
|
36
|
+
when "context"
|
37
|
+
exec File.expand_path("../exa-ai-context", __FILE__), *ARGV[1..]
|
38
|
+
when "get-contents"
|
39
|
+
exec File.expand_path("../exa-ai-get-contents", __FILE__), *ARGV[1..]
|
40
|
+
when "research-start"
|
41
|
+
exec File.expand_path("../exa-ai-research-start", __FILE__), *ARGV[1..]
|
42
|
+
when "research-get"
|
43
|
+
exec File.expand_path("../exa-ai-research-get", __FILE__), *ARGV[1..]
|
44
|
+
when "research-list"
|
45
|
+
exec File.expand_path("../exa-ai-research-list", __FILE__), *ARGV[1..]
|
46
|
+
else
|
47
|
+
print_error_for_command(ARGV[0])
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.print_help
|
53
|
+
puts "Exa CLI v#{Exa::VERSION}"
|
54
|
+
puts ""
|
55
|
+
puts "Usage: exa-ai <command> [options]"
|
56
|
+
puts ""
|
57
|
+
puts "Commands:"
|
58
|
+
AVAILABLE_COMMANDS.each do |cmd, desc|
|
59
|
+
puts " #{cmd.ljust(20)} #{desc}"
|
60
|
+
end
|
61
|
+
puts ""
|
62
|
+
puts "Global Options:"
|
63
|
+
puts " --help, -h Show this help message"
|
64
|
+
puts " --version Show version number"
|
65
|
+
puts ""
|
66
|
+
puts "Examples:"
|
67
|
+
puts " exa-ai search 'ruby programming'"
|
68
|
+
puts " exa-ai context 'React hooks' --tokens-num 5000"
|
69
|
+
puts " exa-ai research-start --instructions 'Find AI papers'"
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.print_error_for_command(cmd)
|
73
|
+
return print_help if cmd.nil?
|
74
|
+
|
75
|
+
puts "Error: Unknown command '#{cmd}'"
|
76
|
+
puts ""
|
77
|
+
|
78
|
+
# Try to suggest similar commands
|
79
|
+
suggestion = suggest_command(cmd)
|
80
|
+
puts "Did you mean: #{suggestion}?" if suggestion
|
81
|
+
|
82
|
+
puts ""
|
83
|
+
puts "Run 'exa-ai --help' for usage information."
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.suggest_command(input)
|
87
|
+
# Simple suggestion based on string similarity
|
88
|
+
# If first char matches and length is similar, suggest it
|
89
|
+
input_start = input[0]
|
90
|
+
candidates = AVAILABLE_COMMANDS.keys.select { |c| c[0] == input_start }
|
91
|
+
candidates.first
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
ExaCLI.run
|