exa-ai 0.11.2 → 0.12.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.
- checksums.yaml +4 -4
- data/README.md +112 -0
- data/exe/exa-ai +28 -1
- data/exe/exa-ai-agent-run-cancel +90 -0
- data/exe/exa-ai-agent-run-create +278 -0
- data/exe/exa-ai-agent-run-delete +96 -0
- data/exe/exa-ai-agent-run-events +105 -0
- data/exe/exa-ai-agent-run-get +90 -0
- data/exe/exa-ai-agent-run-list +94 -0
- data/lib/exa/cli/formatters/agent_run_formatter.rb +149 -0
- data/lib/exa/client.rb +77 -0
- data/lib/exa/resources/agent_run.rb +54 -0
- data/lib/exa/resources/agent_run_event_list.rb +21 -0
- data/lib/exa/resources/agent_run_list.rb +18 -0
- data/lib/exa/services/agent_run_cancel.rb +32 -0
- data/lib/exa/services/agent_run_create.rb +33 -0
- data/lib/exa/services/agent_run_delete.rb +37 -0
- data/lib/exa/services/agent_run_events.rb +28 -0
- data/lib/exa/services/agent_run_get.rb +33 -0
- data/lib/exa/services/agent_run_list.rb +41 -0
- data/lib/exa/services/agent_run_stream.rb +110 -0
- data/lib/exa/services/parameter_converter.rb +2 -0
- data/lib/exa/version.rb +1 -1
- data/lib/exa.rb +11 -0
- metadata +18 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
|
|
6
|
+
# Parse command-line arguments
|
|
7
|
+
api_key = nil
|
|
8
|
+
run_id = nil
|
|
9
|
+
output_format = "json"
|
|
10
|
+
|
|
11
|
+
args = ARGV.dup
|
|
12
|
+
while args.any?
|
|
13
|
+
arg = args.shift
|
|
14
|
+
case arg
|
|
15
|
+
when "--api-key"
|
|
16
|
+
api_key = args.shift
|
|
17
|
+
when "--output-format"
|
|
18
|
+
output_format = args.shift
|
|
19
|
+
when "--help", "-h"
|
|
20
|
+
puts <<~HELP
|
|
21
|
+
Usage: exa-ai agent-run-events <run_id> [options]
|
|
22
|
+
|
|
23
|
+
Retrieve events for an agent run.
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
run_id ID of the agent run
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--api-key KEY Exa API key (or use EXA_API_KEY env var)
|
|
30
|
+
--output-format FORMAT Output format: json, pretty, or text (default: json)
|
|
31
|
+
--help, -h Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
exa-ai agent-run-events run_123
|
|
35
|
+
exa-ai agent-run-events run_123 --output-format pretty
|
|
36
|
+
HELP
|
|
37
|
+
exit 0
|
|
38
|
+
else
|
|
39
|
+
# First positional argument is the run_id
|
|
40
|
+
if run_id.nil?
|
|
41
|
+
run_id = arg
|
|
42
|
+
else
|
|
43
|
+
warn "Unknown option: #{arg}"
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Validate required arguments
|
|
50
|
+
if run_id.nil?
|
|
51
|
+
warn "Error: Run ID argument required"
|
|
52
|
+
warn "Usage: exa-ai agent-run-events <run_id> [options]"
|
|
53
|
+
warn "Try 'exa-ai agent-run-events --help' for more information"
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
# Resolve API key
|
|
59
|
+
api_key = Exa::CLI::Base.resolve_api_key(api_key)
|
|
60
|
+
|
|
61
|
+
# Build client
|
|
62
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
63
|
+
|
|
64
|
+
# Call API
|
|
65
|
+
events = client.agent_run_events(run_id)
|
|
66
|
+
|
|
67
|
+
# Format output
|
|
68
|
+
items = events.respond_to?(:data) ? events.data : events
|
|
69
|
+
case output_format
|
|
70
|
+
when "json"
|
|
71
|
+
puts JSON.pretty_generate(events.respond_to?(:to_h) ? events.to_h : events)
|
|
72
|
+
when "pretty"
|
|
73
|
+
puts "Events for Agent Run: #{run_id}"
|
|
74
|
+
puts ""
|
|
75
|
+
items.each_with_index do |event, i|
|
|
76
|
+
puts "#{i + 1}. #{event.respond_to?(:to_h) ? event.to_h.inspect : event}"
|
|
77
|
+
end
|
|
78
|
+
when "text"
|
|
79
|
+
items.each do |event|
|
|
80
|
+
puts event.respond_to?(:to_h) ? event.to_h.inspect : event.to_s
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
puts JSON.pretty_generate(events.respond_to?(:to_h) ? events.to_h : events)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
rescue Exa::NotFound => e
|
|
87
|
+
warn "Agent run not found: #{e.message}"
|
|
88
|
+
exit 1
|
|
89
|
+
rescue Exa::Unauthorized => e
|
|
90
|
+
warn "Authentication failed: #{e.message}"
|
|
91
|
+
warn "Please provide a valid API key via --api-key or EXA_API_KEY environment variable"
|
|
92
|
+
exit 1
|
|
93
|
+
rescue Exa::ClientError => e
|
|
94
|
+
warn "Client error: #{e.message}"
|
|
95
|
+
exit 1
|
|
96
|
+
rescue Exa::ServerError => e
|
|
97
|
+
warn "Server error: #{e.message}"
|
|
98
|
+
exit 1
|
|
99
|
+
rescue Exa::Error => e
|
|
100
|
+
warn "Error: #{e.message}"
|
|
101
|
+
exit 1
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
warn "Unexpected error: #{e.message}"
|
|
104
|
+
exit 1
|
|
105
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
|
|
6
|
+
# Parse command-line arguments
|
|
7
|
+
api_key = nil
|
|
8
|
+
run_id = nil
|
|
9
|
+
output_format = "json"
|
|
10
|
+
|
|
11
|
+
args = ARGV.dup
|
|
12
|
+
while args.any?
|
|
13
|
+
arg = args.shift
|
|
14
|
+
case arg
|
|
15
|
+
when "--api-key"
|
|
16
|
+
api_key = args.shift
|
|
17
|
+
when "--output-format"
|
|
18
|
+
output_format = args.shift
|
|
19
|
+
when "--help", "-h"
|
|
20
|
+
puts <<~HELP
|
|
21
|
+
Usage: exa-ai agent-run-get <run_id> [options]
|
|
22
|
+
|
|
23
|
+
Get the status and results of an agent run.
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
run_id ID of the agent run to retrieve
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--api-key KEY Exa API key (or use EXA_API_KEY env var)
|
|
30
|
+
--output-format FORMAT Output format: json, pretty, or text (default: json)
|
|
31
|
+
--help, -h Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
exa-ai agent-run-get run_123
|
|
35
|
+
exa-ai agent-run-get run_123 --output-format pretty
|
|
36
|
+
HELP
|
|
37
|
+
exit 0
|
|
38
|
+
else
|
|
39
|
+
# First positional argument is the run_id
|
|
40
|
+
if run_id.nil?
|
|
41
|
+
run_id = arg
|
|
42
|
+
else
|
|
43
|
+
warn "Unknown option: #{arg}"
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Validate required arguments
|
|
50
|
+
if run_id.nil?
|
|
51
|
+
warn "Error: Run ID argument required"
|
|
52
|
+
warn "Usage: exa-ai agent-run-get <run_id> [options]"
|
|
53
|
+
warn "Try 'exa-ai agent-run-get --help' for more information"
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
# Resolve API key
|
|
59
|
+
api_key = Exa::CLI::Base.resolve_api_key(api_key)
|
|
60
|
+
|
|
61
|
+
# Build client
|
|
62
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
63
|
+
|
|
64
|
+
# Call API
|
|
65
|
+
result = client.agent_run_get(run_id)
|
|
66
|
+
|
|
67
|
+
# Format output
|
|
68
|
+
formatted = Exa::CLI::Formatters::AgentRunFormatter.format_run(result, output_format)
|
|
69
|
+
puts formatted
|
|
70
|
+
|
|
71
|
+
rescue Exa::NotFound => e
|
|
72
|
+
warn "Agent run not found: #{e.message}"
|
|
73
|
+
exit 1
|
|
74
|
+
rescue Exa::Unauthorized => e
|
|
75
|
+
warn "Authentication failed: #{e.message}"
|
|
76
|
+
warn "Please provide a valid API key via --api-key or EXA_API_KEY environment variable"
|
|
77
|
+
exit 1
|
|
78
|
+
rescue Exa::ClientError => e
|
|
79
|
+
warn "Client error: #{e.message}"
|
|
80
|
+
exit 1
|
|
81
|
+
rescue Exa::ServerError => e
|
|
82
|
+
warn "Server error: #{e.message}"
|
|
83
|
+
exit 1
|
|
84
|
+
rescue Exa::Error => e
|
|
85
|
+
warn "Error: #{e.message}"
|
|
86
|
+
exit 1
|
|
87
|
+
rescue StandardError => e
|
|
88
|
+
warn "Unexpected error: #{e.message}"
|
|
89
|
+
exit 1
|
|
90
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "exa-ai"
|
|
5
|
+
|
|
6
|
+
# Parse command-line arguments
|
|
7
|
+
api_key = nil
|
|
8
|
+
cursor = nil
|
|
9
|
+
limit = 10
|
|
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 "--cursor"
|
|
19
|
+
cursor = args.shift
|
|
20
|
+
when "--limit"
|
|
21
|
+
limit = args.shift.to_i
|
|
22
|
+
when "--output-format"
|
|
23
|
+
output_format = args.shift
|
|
24
|
+
when "--help", "-h"
|
|
25
|
+
puts <<~HELP
|
|
26
|
+
Usage: exa-ai agent-run-list [options]
|
|
27
|
+
|
|
28
|
+
List agent runs with cursor-based pagination.
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--api-key KEY Exa API key (or use EXA_API_KEY env var)
|
|
32
|
+
--cursor CURSOR Pagination cursor for next page
|
|
33
|
+
--limit LIMIT Number of results per page (default: 10)
|
|
34
|
+
--output-format FORMAT Output format: json, pretty, or text (default: json)
|
|
35
|
+
--help, -h Show this help message
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
exa-ai agent-run-list
|
|
39
|
+
exa-ai agent-run-list --limit 20
|
|
40
|
+
exa-ai agent-run-list --cursor next_page_cursor
|
|
41
|
+
exa-ai agent-run-list --output-format pretty
|
|
42
|
+
HELP
|
|
43
|
+
exit 0
|
|
44
|
+
else
|
|
45
|
+
warn "Unknown option: #{arg}"
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
# Resolve API key
|
|
52
|
+
api_key = Exa::CLI::Base.resolve_api_key(api_key)
|
|
53
|
+
|
|
54
|
+
# Build client
|
|
55
|
+
client = Exa::CLI::Base.build_client(api_key)
|
|
56
|
+
|
|
57
|
+
# Build parameters
|
|
58
|
+
params = { limit: limit }
|
|
59
|
+
params[:cursor] = cursor if cursor
|
|
60
|
+
|
|
61
|
+
# Call API
|
|
62
|
+
result = client.agent_run_list(**params)
|
|
63
|
+
|
|
64
|
+
# Format output
|
|
65
|
+
formatted = Exa::CLI::Formatters::AgentRunFormatter.format_list(result, output_format)
|
|
66
|
+
puts formatted
|
|
67
|
+
|
|
68
|
+
# Show pagination info if there are more results
|
|
69
|
+
if result.has_more && result.next_cursor
|
|
70
|
+
if output_format == "pretty"
|
|
71
|
+
puts "\n" + "=" * 80
|
|
72
|
+
puts "More results available. Use --cursor #{result.next_cursor} to get next page."
|
|
73
|
+
else
|
|
74
|
+
warn "More results available. Use --cursor #{result.next_cursor} to get next page."
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
rescue Exa::Unauthorized => e
|
|
79
|
+
warn "Authentication failed: #{e.message}"
|
|
80
|
+
warn "Please provide a valid API key via --api-key or EXA_API_KEY environment variable"
|
|
81
|
+
exit 1
|
|
82
|
+
rescue Exa::ClientError => e
|
|
83
|
+
warn "Client error: #{e.message}"
|
|
84
|
+
exit 1
|
|
85
|
+
rescue Exa::ServerError => e
|
|
86
|
+
warn "Server error: #{e.message}"
|
|
87
|
+
exit 1
|
|
88
|
+
rescue Exa::Error => e
|
|
89
|
+
warn "Error: #{e.message}"
|
|
90
|
+
exit 1
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
warn "Unexpected error: #{e.message}"
|
|
93
|
+
exit 1
|
|
94
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Exa
|
|
2
|
+
module CLI
|
|
3
|
+
module Formatters
|
|
4
|
+
class AgentRunFormatter
|
|
5
|
+
def self.format_run(run, format)
|
|
6
|
+
case format
|
|
7
|
+
when "json"
|
|
8
|
+
JSON.pretty_generate(run.to_h)
|
|
9
|
+
when "pretty"
|
|
10
|
+
format_run_pretty(run)
|
|
11
|
+
when "text"
|
|
12
|
+
format_run_text(run)
|
|
13
|
+
when "toon"
|
|
14
|
+
Exa::CLI::Base.encode_as_toon(run.to_h)
|
|
15
|
+
else
|
|
16
|
+
JSON.pretty_generate(run.to_h)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.format_list(list, format)
|
|
21
|
+
case format
|
|
22
|
+
when "json"
|
|
23
|
+
JSON.pretty_generate(list.to_h)
|
|
24
|
+
when "pretty"
|
|
25
|
+
format_list_pretty(list)
|
|
26
|
+
when "text"
|
|
27
|
+
format_list_text(list)
|
|
28
|
+
when "toon"
|
|
29
|
+
Exa::CLI::Base.encode_as_toon(list.to_h)
|
|
30
|
+
else
|
|
31
|
+
JSON.pretty_generate(list.to_h)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def self.format_run_pretty(run)
|
|
38
|
+
output = []
|
|
39
|
+
output << "Agent Run: #{run.id}"
|
|
40
|
+
output << "Status: #{run.status.upcase}"
|
|
41
|
+
output << "Created: #{run.created_at}"
|
|
42
|
+
output << ""
|
|
43
|
+
|
|
44
|
+
case run.status
|
|
45
|
+
when "queued"
|
|
46
|
+
output << "Run is queued..."
|
|
47
|
+
when "running"
|
|
48
|
+
output << "Run is running... ⚙️"
|
|
49
|
+
when "completed"
|
|
50
|
+
output << "Output:"
|
|
51
|
+
output << "--------"
|
|
52
|
+
if run.output.is_a?(Hash)
|
|
53
|
+
output << (run.output[:text] || run.output["text"] || run.output.inspect)
|
|
54
|
+
else
|
|
55
|
+
output << run.output.to_s
|
|
56
|
+
end
|
|
57
|
+
if run.output.is_a?(Hash) && (structured = run.output["structured"] || run.output[:structured])
|
|
58
|
+
output << ""
|
|
59
|
+
output << "Structured:"
|
|
60
|
+
output << JSON.pretty_generate(structured)
|
|
61
|
+
end
|
|
62
|
+
sources = grounding_sources(run)
|
|
63
|
+
unless sources.empty?
|
|
64
|
+
output << ""
|
|
65
|
+
output << "Grounding:"
|
|
66
|
+
sources.each { |s| output << " - #{s}" }
|
|
67
|
+
end
|
|
68
|
+
output << ""
|
|
69
|
+
if run.cost_dollars
|
|
70
|
+
total = run.cost_dollars.is_a?(Hash) ? (run.cost_dollars["total"] || run.cost_dollars[:total]) : run.cost_dollars
|
|
71
|
+
output << "Cost: $#{total}"
|
|
72
|
+
end
|
|
73
|
+
output << "Completed: #{run.completed_at}" if run.completed_at
|
|
74
|
+
when "failed"
|
|
75
|
+
output << "Run failed"
|
|
76
|
+
output << "Stop reason: #{run.stop_reason}" if run.stop_reason
|
|
77
|
+
when "cancelled"
|
|
78
|
+
output << "Run was cancelled"
|
|
79
|
+
output << "Completed: #{run.completed_at}" if run.completed_at
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
output.join("\n")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.format_list_pretty(list)
|
|
86
|
+
output = []
|
|
87
|
+
output << "Agent Runs (#{list.data.length}):"
|
|
88
|
+
output << ""
|
|
89
|
+
|
|
90
|
+
if list.data.empty?
|
|
91
|
+
output << "No runs found."
|
|
92
|
+
else
|
|
93
|
+
output << "%-40s %-15s %s" % ["Run ID", "Status", "Created"]
|
|
94
|
+
output << "-" * 70
|
|
95
|
+
|
|
96
|
+
list.data.each do |run|
|
|
97
|
+
run_id = run.id.to_s[0..38]
|
|
98
|
+
status = run.status.upcase[0..14]
|
|
99
|
+
created = run.created_at.to_s[0..19]
|
|
100
|
+
output << "%-40s %-15s %s" % [run_id, status, created]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
output << ""
|
|
105
|
+
if list.has_more
|
|
106
|
+
output << "More results available. Use --cursor #{list.next_cursor} for next page."
|
|
107
|
+
else
|
|
108
|
+
output << "End of results."
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
output.join("\n")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.format_run_text(run)
|
|
115
|
+
output = []
|
|
116
|
+
output << "#{run.id} #{run.status.upcase} #{run.created_at}"
|
|
117
|
+
if run.status == "completed" && run.output
|
|
118
|
+
text = run.output.is_a?(Hash) ? (run.output[:text] || run.output["text"]) : run.output.to_s
|
|
119
|
+
output << text.to_s
|
|
120
|
+
if run.output.is_a?(Hash) && (structured = run.output["structured"] || run.output[:structured])
|
|
121
|
+
output << JSON.generate(structured)
|
|
122
|
+
end
|
|
123
|
+
elsif run.status == "failed"
|
|
124
|
+
output << "Stop reason: #{run.stop_reason}"
|
|
125
|
+
end
|
|
126
|
+
output.join("\n")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Flattens an output.grounding array into displayable source strings,
|
|
130
|
+
# tolerating both shapes seen from the API: flat {url,title} items and
|
|
131
|
+
# nested {citations:[{url,title}]} items.
|
|
132
|
+
def self.grounding_sources(run)
|
|
133
|
+
grounding = run.output.is_a?(Hash) ? (run.output["grounding"] || run.output[:grounding]) : nil
|
|
134
|
+
Array(grounding).flat_map do |item|
|
|
135
|
+
citations = item.is_a?(Hash) && item["citations"] ? item["citations"] : [item]
|
|
136
|
+
citations.map { |c| c.is_a?(Hash) ? (c["title"] || c["url"]) : c }
|
|
137
|
+
end.compact
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.format_list_text(list)
|
|
141
|
+
output = list.data.map do |run|
|
|
142
|
+
"#{run.id} #{run.status.upcase} #{run.created_at}"
|
|
143
|
+
end
|
|
144
|
+
output.join("\n")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
data/lib/exa/client.rb
CHANGED
|
@@ -112,6 +112,83 @@ module Exa
|
|
|
112
112
|
Services::ResearchGet.new(connection, research_id: research_id, **params).call
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
# Create a new agent run
|
|
116
|
+
#
|
|
117
|
+
# @param query [String] The query or task for the agent to execute (required)
|
|
118
|
+
# @param params [Hash] Additional run parameters
|
|
119
|
+
# @option params [Hash] :input Input data for the agent run. Multi-word keys inside
|
|
120
|
+
# +input+, +data_sources+ items, and +metadata+ must be supplied in the exact shape
|
|
121
|
+
# the API expects — the parameter converter does not recurse into nested values
|
|
122
|
+
# (same contract as +output_schema+).
|
|
123
|
+
# @option params [Array<Hash>] :data_sources Data sources for the agent run
|
|
124
|
+
# @option params [Hash] :metadata Custom metadata
|
|
125
|
+
# @return [Resources::AgentRun] The newly created agent run
|
|
126
|
+
def agent_run_create(query:, **params)
|
|
127
|
+
Services::AgentRunCreate.new(connection, query: query, **params).call
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Stream an agent run, yielding chunks as they arrive
|
|
131
|
+
#
|
|
132
|
+
# @param query [String] The query or task for the agent to execute (required)
|
|
133
|
+
# @param params [Hash] Additional run parameters
|
|
134
|
+
# @option params [Hash] :input Input data for the agent run. Multi-word keys inside
|
|
135
|
+
# +input+, +data_sources+ items, and +metadata+ must be supplied in the exact shape
|
|
136
|
+
# the API expects — the parameter converter does not recurse into nested values
|
|
137
|
+
# (same contract as +output_schema+).
|
|
138
|
+
# @option params [Array<Hash>] :data_sources Data sources for the agent run
|
|
139
|
+
# @option params [Hash] :metadata Custom metadata
|
|
140
|
+
# @yield [chunk] Yields each streamed event chunk as it arrives
|
|
141
|
+
# @yieldparam chunk [Hash] Partial agent run event data
|
|
142
|
+
# @return [void]
|
|
143
|
+
def agent_run_stream(query:, **params, &block)
|
|
144
|
+
Services::AgentRunStream.new(connection, query: query, **params).call(&block)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Get the status and results of an agent run
|
|
148
|
+
#
|
|
149
|
+
# @param run_id [String] Agent run ID
|
|
150
|
+
# @return [Resources::AgentRun] Agent run with current status and results
|
|
151
|
+
def agent_run_get(run_id)
|
|
152
|
+
Services::AgentRunGet.new(connection, run_id: run_id).call
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# List all agent runs
|
|
156
|
+
#
|
|
157
|
+
# @param params [Hash] Listing parameters
|
|
158
|
+
# @option params [Integer] :limit Maximum number of runs to return
|
|
159
|
+
# @option params [String] :cursor Cursor for pagination
|
|
160
|
+
# @return [Resources::AgentRunList] List of agent runs
|
|
161
|
+
def agent_run_list(**params)
|
|
162
|
+
Services::AgentRunList.new(connection, **params).call
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Cancel an in-progress agent run
|
|
166
|
+
#
|
|
167
|
+
# @param run_id [String] Agent run ID
|
|
168
|
+
# @return [Resources::AgentRun] The cancelled agent run
|
|
169
|
+
def agent_run_cancel(run_id)
|
|
170
|
+
Services::AgentRunCancel.new(connection, run_id: run_id).call
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Delete an agent run
|
|
174
|
+
#
|
|
175
|
+
# @param run_id [String] Agent run ID
|
|
176
|
+
# @return [Resources::AgentRun] The deleted agent run
|
|
177
|
+
def agent_run_delete(run_id)
|
|
178
|
+
Services::AgentRunDelete.new(connection, run_id: run_id).call
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# List events for an agent run
|
|
182
|
+
#
|
|
183
|
+
# @param run_id [String] Agent run ID
|
|
184
|
+
# @param params [Hash] Listing parameters
|
|
185
|
+
# @option params [Integer] :limit Maximum number of events to return
|
|
186
|
+
# @option params [String] :cursor Cursor for pagination
|
|
187
|
+
# @return [Array<Hash>] List of agent run events
|
|
188
|
+
def agent_run_events(run_id, **params)
|
|
189
|
+
Services::AgentRunEvents.new(connection, run_id: run_id, **params).call
|
|
190
|
+
end
|
|
191
|
+
|
|
115
192
|
# Search code repositories
|
|
116
193
|
#
|
|
117
194
|
# @param query [String] Code search query
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Exa
|
|
2
|
+
module Resources
|
|
3
|
+
class AgentRun < Struct.new(
|
|
4
|
+
:id, :object, :status, :stop_reason, :created_at, :completed_at,
|
|
5
|
+
:request, :output, :usage, :cost_dollars, keyword_init: true
|
|
6
|
+
)
|
|
7
|
+
def initialize(id:, object:, status:, stop_reason: nil, created_at: nil, completed_at: nil, request: nil, output: nil, usage: nil, cost_dollars: nil)
|
|
8
|
+
super
|
|
9
|
+
freeze
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Build from a raw API response body (camelCase keys), e.g. a GET payload
|
|
13
|
+
# or the data of a terminal agent_run.* stream event.
|
|
14
|
+
def self.from_response(body)
|
|
15
|
+
new(
|
|
16
|
+
id: body["id"],
|
|
17
|
+
object: body["object"] || "agent_run",
|
|
18
|
+
status: body["status"],
|
|
19
|
+
stop_reason: body["stopReason"],
|
|
20
|
+
created_at: body["createdAt"],
|
|
21
|
+
completed_at: body["completedAt"],
|
|
22
|
+
request: body["request"],
|
|
23
|
+
output: body["output"],
|
|
24
|
+
usage: body["usage"],
|
|
25
|
+
cost_dollars: body["costDollars"]
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def queued? = status == 'queued'
|
|
30
|
+
def running? = status == 'running'
|
|
31
|
+
def completed? = status == 'completed'
|
|
32
|
+
def failed? = status == 'failed'
|
|
33
|
+
def cancelled? = status == 'cancelled'
|
|
34
|
+
|
|
35
|
+
def finished? = completed? || failed? || cancelled?
|
|
36
|
+
|
|
37
|
+
def to_h
|
|
38
|
+
result = {
|
|
39
|
+
id: id,
|
|
40
|
+
object: object,
|
|
41
|
+
status: status
|
|
42
|
+
}
|
|
43
|
+
result[:stop_reason] = stop_reason if stop_reason
|
|
44
|
+
result[:created_at] = created_at if created_at
|
|
45
|
+
result[:completed_at] = completed_at if completed_at
|
|
46
|
+
result[:request] = request if request
|
|
47
|
+
result[:output] = output if output
|
|
48
|
+
result[:usage] = usage if usage
|
|
49
|
+
result[:cost_dollars] = cost_dollars if cost_dollars
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Exa
|
|
2
|
+
module Resources
|
|
3
|
+
# Paginated collection of agent run events.
|
|
4
|
+
# Mirrors AgentRunList's data/has_more/next_cursor shape; event items are
|
|
5
|
+
# kept as plain hashes (the API returns {id, event, data, createdAt} per event).
|
|
6
|
+
class AgentRunEventList < Struct.new(:data, :has_more, :next_cursor, keyword_init: true)
|
|
7
|
+
def initialize(data:, has_more: false, next_cursor: nil)
|
|
8
|
+
super
|
|
9
|
+
freeze
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_h
|
|
13
|
+
{
|
|
14
|
+
data: data.map { |item| item.respond_to?(:to_h) ? item.to_h : item },
|
|
15
|
+
has_more: has_more,
|
|
16
|
+
next_cursor: next_cursor
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Exa
|
|
2
|
+
module Resources
|
|
3
|
+
class AgentRunList < Struct.new(:data, :has_more, :next_cursor, keyword_init: true)
|
|
4
|
+
def initialize(data:, has_more:, next_cursor: nil)
|
|
5
|
+
super
|
|
6
|
+
freeze
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_h
|
|
10
|
+
{
|
|
11
|
+
data: data.map { |item| item.respond_to?(:to_h) ? item.to_h : item },
|
|
12
|
+
has_more: has_more,
|
|
13
|
+
next_cursor: next_cursor
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resources/agent_run"
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Services
|
|
7
|
+
class AgentRunCancel
|
|
8
|
+
def initialize(connection, run_id:)
|
|
9
|
+
@connection = connection
|
|
10
|
+
@run_id = run_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
response = @connection.post("/agent/runs/#{@run_id}/cancel", {})
|
|
15
|
+
body = response.body
|
|
16
|
+
|
|
17
|
+
Resources::AgentRun.new(
|
|
18
|
+
id: body["id"],
|
|
19
|
+
object: body["object"],
|
|
20
|
+
status: body["status"],
|
|
21
|
+
stop_reason: body["stopReason"],
|
|
22
|
+
created_at: body["createdAt"],
|
|
23
|
+
completed_at: body["completedAt"],
|
|
24
|
+
request: body["request"],
|
|
25
|
+
output: body["output"],
|
|
26
|
+
usage: body["usage"],
|
|
27
|
+
cost_dollars: body["costDollars"]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "parameter_converter"
|
|
4
|
+
require_relative "../resources/agent_run"
|
|
5
|
+
|
|
6
|
+
module Exa
|
|
7
|
+
module Services
|
|
8
|
+
class AgentRunCreate
|
|
9
|
+
def initialize(connection, query:, **params)
|
|
10
|
+
@connection = connection
|
|
11
|
+
@params = params.merge(query: query)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
response = @connection.post("/agent/runs", ParameterConverter.convert(@params))
|
|
16
|
+
body = response.body
|
|
17
|
+
|
|
18
|
+
Resources::AgentRun.new(
|
|
19
|
+
id: body["id"],
|
|
20
|
+
object: body["object"],
|
|
21
|
+
status: body["status"],
|
|
22
|
+
stop_reason: body["stopReason"],
|
|
23
|
+
created_at: body["createdAt"],
|
|
24
|
+
completed_at: body["completedAt"],
|
|
25
|
+
request: body["request"],
|
|
26
|
+
output: body["output"],
|
|
27
|
+
usage: body["usage"],
|
|
28
|
+
cost_dollars: body["costDollars"]
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|