exaonruby 1.0.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.txt +21 -0
- data/README.md +614 -0
- data/exaonruby.gemspec +37 -0
- data/exe/exa +7 -0
- data/lib/exa/cli.rb +458 -0
- data/lib/exa/client.rb +210 -0
- data/lib/exa/configuration.rb +81 -0
- data/lib/exa/endpoints/answer.rb +109 -0
- data/lib/exa/endpoints/contents.rb +141 -0
- data/lib/exa/endpoints/events.rb +71 -0
- data/lib/exa/endpoints/find_similar.rb +154 -0
- data/lib/exa/endpoints/imports.rb +145 -0
- data/lib/exa/endpoints/monitors.rb +193 -0
- data/lib/exa/endpoints/research.rb +158 -0
- data/lib/exa/endpoints/search.rb +195 -0
- data/lib/exa/endpoints/webhooks.rb +161 -0
- data/lib/exa/endpoints/webset_enrichments.rb +162 -0
- data/lib/exa/endpoints/webset_items.rb +90 -0
- data/lib/exa/endpoints/webset_searches.rb +137 -0
- data/lib/exa/endpoints/websets.rb +214 -0
- data/lib/exa/errors.rb +180 -0
- data/lib/exa/resources/answer_response.rb +101 -0
- data/lib/exa/resources/base.rb +56 -0
- data/lib/exa/resources/contents_response.rb +123 -0
- data/lib/exa/resources/event.rb +84 -0
- data/lib/exa/resources/import.rb +137 -0
- data/lib/exa/resources/monitor.rb +205 -0
- data/lib/exa/resources/paginated_response.rb +87 -0
- data/lib/exa/resources/research_task.rb +165 -0
- data/lib/exa/resources/search_response.rb +111 -0
- data/lib/exa/resources/search_result.rb +95 -0
- data/lib/exa/resources/webhook.rb +152 -0
- data/lib/exa/resources/webset.rb +491 -0
- data/lib/exa/resources/webset_item.rb +256 -0
- data/lib/exa/utils/parameter_converter.rb +159 -0
- data/lib/exa/utils/webhook_handler.rb +239 -0
- data/lib/exa/version.rb +7 -0
- data/lib/exa.rb +130 -0
- metadata +146 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Resources
|
|
7
|
+
class Event < Base
|
|
8
|
+
# @return [String] Unique identifier
|
|
9
|
+
def id
|
|
10
|
+
get(:id)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String] Object type
|
|
14
|
+
def object
|
|
15
|
+
get(:object)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] Event type (e.g., "webset.item.created")
|
|
19
|
+
def type
|
|
20
|
+
get(:type)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Hash] Event payload data
|
|
24
|
+
def data
|
|
25
|
+
get(:data, {})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [String, nil] Related Webset ID
|
|
29
|
+
def webset_id
|
|
30
|
+
dig(:data, :webset_id) || dig(:data, :websetId)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [String, nil] Related Item ID
|
|
34
|
+
def item_id
|
|
35
|
+
dig(:data, :item_id) || dig(:data, :itemId)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Time, nil] Event timestamp
|
|
39
|
+
def created_at
|
|
40
|
+
parse_time(get(:created_at))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Boolean] Whether this is a webset event
|
|
44
|
+
def webset_event?
|
|
45
|
+
type&.start_with?("webset.")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Boolean] Whether this is an item event
|
|
49
|
+
def item_event?
|
|
50
|
+
type&.include?(".item.")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [Boolean] Whether this is a monitor event
|
|
54
|
+
def monitor_event?
|
|
55
|
+
type&.start_with?("monitor.")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Boolean] Whether this is an import event
|
|
59
|
+
def import_event?
|
|
60
|
+
type&.start_with?("import.")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def parse_time(value)
|
|
66
|
+
return nil unless value
|
|
67
|
+
|
|
68
|
+
Time.parse(value)
|
|
69
|
+
rescue ArgumentError
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def inspectable_attributes
|
|
74
|
+
{ id: id, type: type }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class EventListResponse < PaginatedResponse
|
|
79
|
+
def data
|
|
80
|
+
@data ||= (get(:data, []) || []).map { |item| Event.new(item) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Resources
|
|
7
|
+
class Import < Base
|
|
8
|
+
# @return [String] Unique identifier
|
|
9
|
+
def id
|
|
10
|
+
get(:id)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String] Object type
|
|
14
|
+
def object
|
|
15
|
+
get(:object)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] Status: pending, processing, completed, failed
|
|
19
|
+
def status
|
|
20
|
+
get(:status)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] Format: csv, webset
|
|
24
|
+
def format
|
|
25
|
+
get(:format)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Hash, nil] Entity configuration
|
|
29
|
+
def entity
|
|
30
|
+
get(:entity)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [String, nil] Entity type
|
|
34
|
+
def entity_type
|
|
35
|
+
dig(:entity, :type)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [String, nil] Title
|
|
39
|
+
def title
|
|
40
|
+
get(:title)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Integer, nil] Number of entities
|
|
44
|
+
def count
|
|
45
|
+
get(:count)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Hash] Custom metadata
|
|
49
|
+
def metadata
|
|
50
|
+
get(:metadata, {})
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [String, nil] URL to upload file to
|
|
54
|
+
def upload_url
|
|
55
|
+
get(:upload_url)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Time, nil] Upload URL expiration
|
|
59
|
+
def upload_valid_until
|
|
60
|
+
parse_time(get(:upload_valid_until))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Boolean] Whether upload URL is still valid
|
|
64
|
+
def upload_valid?
|
|
65
|
+
valid_until = upload_valid_until
|
|
66
|
+
return false unless valid_until
|
|
67
|
+
|
|
68
|
+
Time.now < valid_until
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [String, nil] Failure reason
|
|
72
|
+
def failed_reason
|
|
73
|
+
get(:failed_reason)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Time, nil] Failure timestamp
|
|
77
|
+
def failed_at
|
|
78
|
+
parse_time(get(:failed_at))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [String, nil] Failure message
|
|
82
|
+
def failed_message
|
|
83
|
+
get(:failed_message)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Time, nil] Creation timestamp
|
|
87
|
+
def created_at
|
|
88
|
+
parse_time(get(:created_at))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Time, nil] Last update timestamp
|
|
92
|
+
def updated_at
|
|
93
|
+
parse_time(get(:updated_at))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [Boolean] Whether import is pending
|
|
97
|
+
def pending?
|
|
98
|
+
status == "pending"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# @return [Boolean] Whether import is processing
|
|
102
|
+
def processing?
|
|
103
|
+
status == "processing"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Boolean] Whether import completed
|
|
107
|
+
def completed?
|
|
108
|
+
status == "completed"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @return [Boolean] Whether import failed
|
|
112
|
+
def failed?
|
|
113
|
+
status == "failed"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def parse_time(value)
|
|
119
|
+
return nil unless value
|
|
120
|
+
|
|
121
|
+
Time.parse(value)
|
|
122
|
+
rescue ArgumentError
|
|
123
|
+
nil
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def inspectable_attributes
|
|
127
|
+
{ id: id, status: status, title: title }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class ImportListResponse < PaginatedResponse
|
|
132
|
+
def data
|
|
133
|
+
@data ||= (get(:data, []) || []).map { |item| Import.new(item) }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Resources
|
|
7
|
+
class Monitor < Base
|
|
8
|
+
# @return [String] Unique identifier
|
|
9
|
+
def id
|
|
10
|
+
get(:id)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String] Object type
|
|
14
|
+
def object
|
|
15
|
+
get(:object)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] Status: enabled, disabled
|
|
19
|
+
def status
|
|
20
|
+
get(:status)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] Parent Webset ID
|
|
24
|
+
def webset_id
|
|
25
|
+
get(:webset_id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Hash, nil] Cadence configuration
|
|
29
|
+
def cadence
|
|
30
|
+
get(:cadence)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [String, nil] Cron expression
|
|
34
|
+
def cron
|
|
35
|
+
dig(:cadence, :cron)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [String, nil] Timezone
|
|
39
|
+
def timezone
|
|
40
|
+
dig(:cadence, :timezone)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Hash, nil] Behavior configuration
|
|
44
|
+
def behavior
|
|
45
|
+
get(:behavior)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [String, nil] Behavior type: search, refresh
|
|
49
|
+
def behavior_type
|
|
50
|
+
dig(:behavior, :type)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [Hash, nil] Behavior config
|
|
54
|
+
def behavior_config
|
|
55
|
+
dig(:behavior, :config)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [MonitorRun, nil] Last run information
|
|
59
|
+
def last_run
|
|
60
|
+
run_data = get(:last_run)
|
|
61
|
+
return nil unless run_data
|
|
62
|
+
|
|
63
|
+
MonitorRun.new(run_data)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [Time, nil] Next scheduled run
|
|
67
|
+
def next_run_at
|
|
68
|
+
parse_time(get(:next_run_at))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [Hash] Custom metadata
|
|
72
|
+
def metadata
|
|
73
|
+
get(:metadata, {})
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Time, nil] Creation timestamp
|
|
77
|
+
def created_at
|
|
78
|
+
parse_time(get(:created_at))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Time, nil] Last update timestamp
|
|
82
|
+
def updated_at
|
|
83
|
+
parse_time(get(:updated_at))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Boolean] Whether monitor is enabled
|
|
87
|
+
def enabled?
|
|
88
|
+
status == "enabled"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Boolean] Whether monitor is disabled
|
|
92
|
+
def disabled?
|
|
93
|
+
status == "disabled"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def parse_time(value)
|
|
99
|
+
return nil unless value
|
|
100
|
+
|
|
101
|
+
Time.parse(value)
|
|
102
|
+
rescue ArgumentError
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def inspectable_attributes
|
|
107
|
+
{ id: id, status: status, cron: cron }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class MonitorRun < Base
|
|
112
|
+
# @return [String] Unique identifier
|
|
113
|
+
def id
|
|
114
|
+
get(:id)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @return [String] Object type
|
|
118
|
+
def object
|
|
119
|
+
get(:object)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @return [String] Status: created, running, completed, failed, canceled
|
|
123
|
+
def status
|
|
124
|
+
get(:status)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @return [String] Parent monitor ID
|
|
128
|
+
def monitor_id
|
|
129
|
+
get(:monitor_id)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @return [String] Run type: search, refresh
|
|
133
|
+
def type
|
|
134
|
+
get(:type)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @return [Time, nil] Completion timestamp
|
|
138
|
+
def completed_at
|
|
139
|
+
parse_time(get(:completed_at))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @return [Time, nil] Failure timestamp
|
|
143
|
+
def failed_at
|
|
144
|
+
parse_time(get(:failed_at))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# @return [String, nil] Failure reason
|
|
148
|
+
def failed_reason
|
|
149
|
+
get(:failed_reason)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @return [Time, nil] Cancellation timestamp
|
|
153
|
+
def canceled_at
|
|
154
|
+
parse_time(get(:canceled_at))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @return [Time, nil] Creation timestamp
|
|
158
|
+
def created_at
|
|
159
|
+
parse_time(get(:created_at))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @return [Time, nil] Last update timestamp
|
|
163
|
+
def updated_at
|
|
164
|
+
parse_time(get(:updated_at))
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [Boolean] Whether run completed successfully
|
|
168
|
+
def completed?
|
|
169
|
+
status == "completed"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# @return [Boolean] Whether run failed
|
|
173
|
+
def failed?
|
|
174
|
+
status == "failed"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @return [Boolean] Whether run was canceled
|
|
178
|
+
def canceled?
|
|
179
|
+
status == "canceled"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
def parse_time(value)
|
|
185
|
+
return nil unless value
|
|
186
|
+
|
|
187
|
+
Time.parse(value)
|
|
188
|
+
rescue ArgumentError
|
|
189
|
+
nil
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
class MonitorListResponse < PaginatedResponse
|
|
194
|
+
def data
|
|
195
|
+
@data ||= (get(:data, []) || []).map { |item| Monitor.new(item) }
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class MonitorRunListResponse < PaginatedResponse
|
|
200
|
+
def data
|
|
201
|
+
@data ||= (get(:data, []) || []).map { |item| MonitorRun.new(item) }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Resources
|
|
7
|
+
class PaginatedResponse < Base
|
|
8
|
+
# @return [Array] Data items
|
|
9
|
+
def data
|
|
10
|
+
get(:data, [])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Boolean] Whether more items exist
|
|
14
|
+
def has_more
|
|
15
|
+
get(:has_more, false)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias has_more? has_more
|
|
19
|
+
|
|
20
|
+
# @return [String, nil] Cursor for next page
|
|
21
|
+
def next_cursor
|
|
22
|
+
get(:next_cursor)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [Boolean] Whether there are more pages
|
|
26
|
+
def more_pages?
|
|
27
|
+
has_more && !next_cursor.nil?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [Integer] Number of items in current page
|
|
31
|
+
def count
|
|
32
|
+
data.length
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Boolean] Whether data is empty
|
|
36
|
+
def empty?
|
|
37
|
+
data.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Iterates over each item in data
|
|
41
|
+
# @yield [Object] Each item
|
|
42
|
+
# @return [Enumerator, self]
|
|
43
|
+
def each(&block)
|
|
44
|
+
return data.each unless block
|
|
45
|
+
|
|
46
|
+
data.each(&block)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param index [Integer] Index of item
|
|
51
|
+
# @return [Object, nil] Item at index
|
|
52
|
+
def [](index)
|
|
53
|
+
data[index]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [Object, nil] First item
|
|
57
|
+
def first
|
|
58
|
+
data.first
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [Object, nil] Last item
|
|
62
|
+
def last
|
|
63
|
+
data.last
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def inspectable_attributes
|
|
69
|
+
{ count: count, has_more: has_more }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class WebsetsListResponse < PaginatedResponse
|
|
74
|
+
# @return [Array<Webset>] List of websets
|
|
75
|
+
def data
|
|
76
|
+
@data ||= (get(:data, []) || []).map { |item| Webset.new(item) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class WebsetItemsListResponse < PaginatedResponse
|
|
81
|
+
# @return [Array<WebsetItem>] List of webset items
|
|
82
|
+
def data
|
|
83
|
+
@data ||= (get(:data, []) || []).map { |item| WebsetItem.new(item) }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# typed: strict
|
|
4
|
+
|
|
5
|
+
module Exa
|
|
6
|
+
module Resources
|
|
7
|
+
class ResearchTask < Base
|
|
8
|
+
# @return [String] Unique identifier for the research task
|
|
9
|
+
def research_id
|
|
10
|
+
get(:research_id)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String] The model used for this research
|
|
14
|
+
def model
|
|
15
|
+
get(:model)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] The original research instructions
|
|
19
|
+
def instructions
|
|
20
|
+
get(:instructions)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] Status: pending, running, completed, canceled, failed
|
|
24
|
+
def status
|
|
25
|
+
get(:status)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Integer, nil] Unix timestamp when research was created (ms)
|
|
29
|
+
def created_at
|
|
30
|
+
get(:created_at)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Time, nil] Creation time as Ruby Time object
|
|
34
|
+
def created_at_time
|
|
35
|
+
ts = created_at
|
|
36
|
+
return nil unless ts
|
|
37
|
+
|
|
38
|
+
Time.at(ts / 1000.0)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Integer, nil] Unix timestamp when research completed (ms)
|
|
42
|
+
def completed_at
|
|
43
|
+
get(:completed_at)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Time, nil] Completion time as Ruby Time object
|
|
47
|
+
def completed_at_time
|
|
48
|
+
ts = completed_at
|
|
49
|
+
return nil unless ts
|
|
50
|
+
|
|
51
|
+
Time.at(ts / 1000.0)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [Hash, nil] JSON Schema used to validate output
|
|
55
|
+
def output_schema
|
|
56
|
+
get(:output_schema)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [String, Hash, nil] Research output (markdown or structured JSON)
|
|
60
|
+
def output
|
|
61
|
+
get(:output)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @return [Array<Hash>] Events/progress updates
|
|
65
|
+
def events
|
|
66
|
+
get(:events, [])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Integer, nil] Number of operations completed
|
|
70
|
+
def operations_completed
|
|
71
|
+
dig(:operations, :completed)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [Integer, nil] Total number of operations
|
|
75
|
+
def operations_total
|
|
76
|
+
dig(:operations, :total)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Float, nil] Progress percentage (0.0 to 1.0)
|
|
80
|
+
def progress
|
|
81
|
+
completed = operations_completed
|
|
82
|
+
total = operations_total
|
|
83
|
+
return nil unless completed && total && total > 0
|
|
84
|
+
|
|
85
|
+
completed.to_f / total
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Integer, nil] Reasoning tokens used
|
|
89
|
+
def reasoning_tokens
|
|
90
|
+
dig(:usage, :reasoning_tokens)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @return [Integer, nil] Agent search operations
|
|
94
|
+
def agent_search_operations
|
|
95
|
+
dig(:usage, :agent_search_operations)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @return [Integer, nil] Agent page reads
|
|
99
|
+
def agent_page_reads
|
|
100
|
+
dig(:usage, :agent_page_reads)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @return [String, nil] Error message if failed
|
|
104
|
+
def error_message
|
|
105
|
+
get(:error)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @return [CostInfo, nil] Cost information
|
|
109
|
+
def cost
|
|
110
|
+
cost_data = get(:cost_dollars)
|
|
111
|
+
return nil unless cost_data
|
|
112
|
+
|
|
113
|
+
CostInfo.new(cost_data)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [Boolean] Whether research is pending
|
|
117
|
+
def pending?
|
|
118
|
+
status == "pending"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @return [Boolean] Whether research is running
|
|
122
|
+
def running?
|
|
123
|
+
status == "running"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @return [Boolean] Whether research is completed
|
|
127
|
+
def completed?
|
|
128
|
+
status == "completed"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @return [Boolean] Whether research was canceled
|
|
132
|
+
def canceled?
|
|
133
|
+
status == "canceled"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @return [Boolean] Whether research failed
|
|
137
|
+
def failed?
|
|
138
|
+
status == "failed"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @return [Boolean] Whether research is still in progress
|
|
142
|
+
def in_progress?
|
|
143
|
+
pending? || running?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @return [Boolean] Whether research is finished (success or failure)
|
|
147
|
+
def finished?
|
|
148
|
+
completed? || canceled? || failed?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def inspectable_attributes
|
|
154
|
+
{ research_id: research_id, status: status, model: model }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class ResearchListResponse < PaginatedResponse
|
|
159
|
+
# @return [Array<ResearchTask>] List of research tasks
|
|
160
|
+
def data
|
|
161
|
+
@data ||= (get(:data, []) || []).map { |item| ResearchTask.new(item) }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|