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.
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resources/agent_run"
4
+
5
+ module Exa
6
+ module Services
7
+ class AgentRunDelete
8
+ def initialize(connection, run_id:)
9
+ @connection = connection
10
+ @run_id = run_id
11
+ end
12
+
13
+ def call
14
+ response = @connection.delete("/agent/runs/#{@run_id}")
15
+ body = response.body
16
+
17
+ # ponytail: 204-vs-200 unconfirmed; nil-guarded. Confirm against live API.
18
+ if body.nil? || body.empty?
19
+ return Resources::AgentRun.new(id: @run_id, object: "agent_run", status: "deleted")
20
+ end
21
+
22
+ Resources::AgentRun.new(
23
+ id: body["id"],
24
+ object: body["object"],
25
+ status: body["status"],
26
+ stop_reason: body["stopReason"],
27
+ created_at: body["createdAt"],
28
+ completed_at: body["completedAt"],
29
+ request: body["request"],
30
+ output: body["output"],
31
+ usage: body["usage"],
32
+ cost_dollars: body["costDollars"]
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resources/agent_run_event_list"
4
+
5
+ module Exa
6
+ module Services
7
+ class AgentRunEvents
8
+ def initialize(connection, run_id:, **params)
9
+ @connection = connection
10
+ @run_id = run_id
11
+ @params = params
12
+ end
13
+
14
+ def call
15
+ response = @connection.get("/agent/runs/#{@run_id}/events", @params)
16
+ body = response.body
17
+
18
+ # Confirmed live: GET /agent/runs/{id}/events returns a paginated list
19
+ # {object: "list", data: [...], hasMore, nextCursor}.
20
+ Resources::AgentRunEventList.new(
21
+ data: body["data"] || [],
22
+ has_more: body["hasMore"],
23
+ next_cursor: body["nextCursor"]
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resources/agent_run"
4
+
5
+ module Exa
6
+ module Services
7
+ class AgentRunGet
8
+ def initialize(connection, run_id:, **params)
9
+ @connection = connection
10
+ @run_id = run_id
11
+ @params = params
12
+ end
13
+
14
+ def call
15
+ response = @connection.get("/agent/runs/#{@run_id}", @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
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resources/agent_run"
4
+ require_relative "../resources/agent_run_list"
5
+
6
+ module Exa
7
+ module Services
8
+ class AgentRunList
9
+ def initialize(connection, **params)
10
+ @connection = connection
11
+ @params = params
12
+ end
13
+
14
+ def call
15
+ response = @connection.get("/agent/runs", @params)
16
+ body = response.body
17
+
18
+ data = body["data"].map do |run_data|
19
+ Resources::AgentRun.new(
20
+ id: run_data["id"],
21
+ object: run_data["object"],
22
+ status: run_data["status"],
23
+ stop_reason: run_data["stopReason"],
24
+ created_at: run_data["createdAt"],
25
+ completed_at: run_data["completedAt"],
26
+ request: run_data["request"],
27
+ output: run_data["output"],
28
+ usage: run_data["usage"],
29
+ cost_dollars: run_data["costDollars"]
30
+ )
31
+ end
32
+
33
+ Resources::AgentRunList.new(
34
+ data: data,
35
+ has_more: body["hasMore"],
36
+ next_cursor: body["nextCursor"]
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,110 @@
1
+ require "json"
2
+ require_relative "parameter_converter"
3
+ require_relative "../resources/agent_run"
4
+
5
+ # ponytail: copied AnswerStream's SSE parser rather than extracting a shared module — agent frames add event:/id: lines and the two may diverge. Extract only if a third SSE consumer appears.
6
+
7
+ module Exa
8
+ module Services
9
+ class AgentRunStream
10
+ TERMINAL_EVENTS = %w[agent_run.completed agent_run.failed agent_run.cancelled].freeze
11
+
12
+ def initialize(connection, **params)
13
+ @connection = connection
14
+ @params = params
15
+ end
16
+
17
+ # Streams SSE frames to the block as (event_type, data). Returns the final
18
+ # AgentRun built from the terminal event's payload (completed/failed/
19
+ # cancelled), or nil if the stream ended before a terminal event arrived.
20
+ def call(&block)
21
+ raise ArgumentError, "block required for streaming" unless block_given?
22
+
23
+ @buffer = ""
24
+ @final_payload = nil
25
+
26
+ # Capture the terminal payload as it streams, then forward to the caller.
27
+ interceptor = proc do |event_type, data|
28
+ @final_payload = data if TERMINAL_EVENTS.include?(event_type)
29
+ block.call(event_type, data)
30
+ end
31
+
32
+ body = ParameterConverter.convert(@params)
33
+
34
+ @connection.post("/agent/runs", body) do |req|
35
+ req.headers["Accept"] = "text/event-stream"
36
+ req.options.on_data = proc do |chunk|
37
+ @buffer += chunk
38
+ process_sse_buffer(&interceptor)
39
+ end
40
+ end
41
+
42
+ process_remaining_buffer(&interceptor) if @buffer.length.positive?
43
+
44
+ @final_payload && Resources::AgentRun.from_response(@final_payload)
45
+ end
46
+
47
+ private
48
+
49
+ def process_sse_buffer
50
+ return if @buffer.empty?
51
+
52
+ parts = @buffer.split("\n\n")
53
+
54
+ if @buffer.end_with?("\n\n")
55
+ complete_parts = parts
56
+ @buffer = ""
57
+ else
58
+ complete_parts = parts[0...-1]
59
+ @buffer = parts.last || ""
60
+ end
61
+
62
+ complete_parts.each do |frame|
63
+ next if frame.empty?
64
+
65
+ event_type = nil
66
+ data_json = nil
67
+
68
+ frame.split("\n").each do |line|
69
+ if line.start_with?("event: ")
70
+ event_type = line.sub(/^event: /, "").strip
71
+ elsif line.start_with?("data: ")
72
+ data_json = line.sub(/^data: /, "").strip
73
+ end
74
+ end
75
+
76
+ next if data_json.nil?
77
+
78
+ begin
79
+ data = JSON.parse(data_json)
80
+ yield(event_type, data)
81
+ rescue JSON::ParserError
82
+ # Skip lines that aren't valid JSON
83
+ end
84
+ end
85
+ end
86
+
87
+ def process_remaining_buffer
88
+ event_type = nil
89
+ data_json = nil
90
+
91
+ @buffer.split("\n").each do |line|
92
+ if line.start_with?("event: ")
93
+ event_type = line.sub(/^event: /, "").strip
94
+ elsif line.start_with?("data: ")
95
+ data_json = line.sub(/^data: /, "").strip
96
+ end
97
+ end
98
+
99
+ return if data_json.nil?
100
+
101
+ begin
102
+ data = JSON.parse(data_json)
103
+ yield(event_type, data)
104
+ rescue JSON::ParserError
105
+ # Skip lines that aren't valid JSON
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -75,6 +75,8 @@ module Exa
75
75
  when :include_domains then :includeDomains
76
76
  when :exclude_domains then :excludeDomains
77
77
  when :system_prompt then :systemPrompt
78
+ when :previous_run_id then :previousRunId
79
+ when :data_sources then :dataSources
78
80
  else
79
81
  key
80
82
  end
data/lib/exa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Exa
4
- VERSION = "0.11.2"
4
+ VERSION = "0.12.1"
5
5
  end
data/lib/exa.rb CHANGED
@@ -12,6 +12,9 @@ require_relative "exa/resources/find_similar_result"
12
12
  require_relative "exa/resources/contents_result"
13
13
  require_relative "exa/resources/research_task"
14
14
  require_relative "exa/resources/research_list"
15
+ require_relative "exa/resources/agent_run"
16
+ require_relative "exa/resources/agent_run_list"
17
+ require_relative "exa/resources/agent_run_event_list"
15
18
  require_relative "exa/resources/answer"
16
19
  require_relative "exa/resources/context_result"
17
20
  require_relative "exa/resources/webset_collection"
@@ -32,6 +35,13 @@ require_relative "exa/services/get_contents"
32
35
  require_relative "exa/services/research_get"
33
36
  require_relative "exa/services/research_start"
34
37
  require_relative "exa/services/research_list"
38
+ require_relative "exa/services/agent_run_create"
39
+ require_relative "exa/services/agent_run_stream"
40
+ require_relative "exa/services/agent_run_get"
41
+ require_relative "exa/services/agent_run_list"
42
+ require_relative "exa/services/agent_run_cancel"
43
+ require_relative "exa/services/agent_run_delete"
44
+ require_relative "exa/services/agent_run_events"
35
45
  require_relative "exa/services/answer"
36
46
  require_relative "exa/services/answer_stream"
37
47
  require_relative "exa/services/context"
@@ -74,6 +84,7 @@ require_relative "exa/cli/formatters/webset_search_formatter"
74
84
  require_relative "exa/cli/formatters/context_formatter"
75
85
  require_relative "exa/cli/formatters/contents_formatter"
76
86
  require_relative "exa/cli/formatters/research_formatter"
87
+ require_relative "exa/cli/formatters/agent_run_formatter"
77
88
  require_relative "exa/cli/formatters/answer_formatter"
78
89
  require_relative "exa/cli/formatters/webset_formatter"
79
90
  require_relative "exa/cli/formatters/webset_item_formatter"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exa-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.2
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Jackson
@@ -146,6 +146,12 @@ files:
146
146
  - LICENSE
147
147
  - README.md
148
148
  - exe/exa-ai
149
+ - exe/exa-ai-agent-run-cancel
150
+ - exe/exa-ai-agent-run-create
151
+ - exe/exa-ai-agent-run-delete
152
+ - exe/exa-ai-agent-run-events
153
+ - exe/exa-ai-agent-run-get
154
+ - exe/exa-ai-agent-run-list
149
155
  - exe/exa-ai-answer
150
156
  - exe/exa-ai-context
151
157
  - exe/exa-ai-enrichment-cancel
@@ -188,6 +194,7 @@ files:
188
194
  - lib/exa.rb
189
195
  - lib/exa/cli/base.rb
190
196
  - lib/exa/cli/error_handler.rb
197
+ - lib/exa/cli/formatters/agent_run_formatter.rb
191
198
  - lib/exa/cli/formatters/answer_formatter.rb
192
199
  - lib/exa/cli/formatters/contents_formatter.rb
193
200
  - lib/exa/cli/formatters/context_formatter.rb
@@ -207,6 +214,9 @@ files:
207
214
  - lib/exa/constants/websets.rb
208
215
  - lib/exa/error.rb
209
216
  - lib/exa/middleware/raise_error.rb
217
+ - lib/exa/resources/agent_run.rb
218
+ - lib/exa/resources/agent_run_event_list.rb
219
+ - lib/exa/resources/agent_run_list.rb
210
220
  - lib/exa/resources/answer.rb
211
221
  - lib/exa/resources/contents_result.rb
212
222
  - lib/exa/resources/context_result.rb
@@ -226,6 +236,13 @@ files:
226
236
  - lib/exa/resources/webset_enrichment_collection.rb
227
237
  - lib/exa/resources/webset_item_collection.rb
228
238
  - lib/exa/resources/webset_search.rb
239
+ - lib/exa/services/agent_run_cancel.rb
240
+ - lib/exa/services/agent_run_create.rb
241
+ - lib/exa/services/agent_run_delete.rb
242
+ - lib/exa/services/agent_run_events.rb
243
+ - lib/exa/services/agent_run_get.rb
244
+ - lib/exa/services/agent_run_list.rb
245
+ - lib/exa/services/agent_run_stream.rb
229
246
  - lib/exa/services/answer.rb
230
247
  - lib/exa/services/answer_stream.rb
231
248
  - lib/exa/services/context.rb