hermes-client 0.0.0 → 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 +4 -4
- data/.yardopts +11 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.md +21 -0
- data/README.md +105 -7
- data/lib/hermes-client.rb +3 -8
- data/lib/hermes_agent/client/configuration.rb +98 -0
- data/lib/hermes_agent/client/conversation.rb +134 -0
- data/lib/hermes_agent/client/entities/capabilities.rb +289 -0
- data/lib/hermes_agent/client/entities/chat_completion.rb +370 -0
- data/lib/hermes_agent/client/entities/health.rb +140 -0
- data/lib/hermes_agent/client/entities/job.rb +394 -0
- data/lib/hermes_agent/client/entities/model.rb +68 -0
- data/lib/hermes_agent/client/entities/response.rb +429 -0
- data/lib/hermes_agent/client/entities/run.rb +427 -0
- data/lib/hermes_agent/client/entities/session_headers.rb +78 -0
- data/lib/hermes_agent/client/entity.rb +89 -0
- data/lib/hermes_agent/client/errors.rb +228 -0
- data/lib/hermes_agent/client/resources/capabilities.rb +34 -0
- data/lib/hermes_agent/client/resources/chat.rb +139 -0
- data/lib/hermes_agent/client/resources/health.rb +49 -0
- data/lib/hermes_agent/client/resources/jobs.rb +213 -0
- data/lib/hermes_agent/client/resources/models.rb +38 -0
- data/lib/hermes_agent/client/resources/responses.rb +204 -0
- data/lib/hermes_agent/client/resources/runs.rb +156 -0
- data/lib/hermes_agent/client/stream.rb +166 -0
- data/lib/hermes_agent/client/transport.rb +281 -0
- data/lib/hermes_agent/client/util.rb +56 -0
- data/lib/hermes_agent/client/version.rb +11 -0
- data/lib/hermes_agent/client.rb +137 -0
- metadata +72 -11
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hermes_agent/client/entity"
|
|
4
|
+
|
|
5
|
+
module HermesAgent
|
|
6
|
+
class Client
|
|
7
|
+
module Entities
|
|
8
|
+
##
|
|
9
|
+
# The repeat policy of a {Job} ({Job#repeat}): how many times the job runs
|
|
10
|
+
# before the server deletes it, and how many runs it has completed so far.
|
|
11
|
+
#
|
|
12
|
+
class JobRepeat < Entity
|
|
13
|
+
##
|
|
14
|
+
# The maximum number of runs, or `nil` for an unbounded (uncapped)
|
|
15
|
+
# recurring job. Once a capped job reaches this many runs the server
|
|
16
|
+
# deletes it.
|
|
17
|
+
# @return [Integer, nil]
|
|
18
|
+
#
|
|
19
|
+
def times
|
|
20
|
+
self["times"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# The number of runs completed so far (incremented per executed run).
|
|
25
|
+
# @return [Integer, nil]
|
|
26
|
+
#
|
|
27
|
+
def completed
|
|
28
|
+
self["completed"]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# The schedule of a {Job} ({Job#schedule}): a tagged union discriminated by
|
|
34
|
+
# {#kind}. The create/update `schedule` string is parsed server-side into
|
|
35
|
+
# one of three kinds, each carrying its own payload:
|
|
36
|
+
#
|
|
37
|
+
# - `"once"` — a one-shot run at {#run_at} (an ISO-8601 string).
|
|
38
|
+
# - `"interval"` — a recurring run every {#minutes} minutes.
|
|
39
|
+
# - `"cron"` — a recurring run on the cron expression {#expr}.
|
|
40
|
+
#
|
|
41
|
+
# Use the {#once?} / {#interval?} / {#cron?} predicates to discriminate.
|
|
42
|
+
# The payload readers ({#run_at} / {#minutes} / {#expr}) return `nil` when
|
|
43
|
+
# the schedule is not of their kind. {#display} is a human-readable form of
|
|
44
|
+
# the schedule for any kind. Field readers are best-effort; {#to_h} remains
|
|
45
|
+
# the source of truth.
|
|
46
|
+
#
|
|
47
|
+
class JobSchedule < Entity
|
|
48
|
+
##
|
|
49
|
+
# The schedule kind: `"once"`, `"interval"`, or `"cron"`.
|
|
50
|
+
# @return [String, nil]
|
|
51
|
+
#
|
|
52
|
+
def kind
|
|
53
|
+
self["kind"]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Whether this is a one-shot (`"once"`) schedule.
|
|
58
|
+
# @return [boolean]
|
|
59
|
+
#
|
|
60
|
+
def once?
|
|
61
|
+
kind == "once"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Whether this is a recurring interval (`"interval"`) schedule.
|
|
66
|
+
# @return [boolean]
|
|
67
|
+
#
|
|
68
|
+
def interval?
|
|
69
|
+
kind == "interval"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Whether this is a recurring cron (`"cron"`) schedule.
|
|
74
|
+
# @return [boolean]
|
|
75
|
+
#
|
|
76
|
+
def cron?
|
|
77
|
+
kind == "cron"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# A human-readable rendering of the schedule (e.g. `"every 120m"`,
|
|
82
|
+
# `"0 9 * * *"`, or `"once at 2027-02-03 14:00"`).
|
|
83
|
+
# @return [String, nil]
|
|
84
|
+
#
|
|
85
|
+
def display
|
|
86
|
+
self["display"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# The scheduled run time of a `"once"` schedule, as an ISO-8601 string.
|
|
91
|
+
# Returns `nil` when the schedule is not of kind `"once"`.
|
|
92
|
+
# @return [String, nil]
|
|
93
|
+
#
|
|
94
|
+
def run_at
|
|
95
|
+
self["run_at"]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# The interval in minutes of an `"interval"` schedule. Returns `nil` when
|
|
100
|
+
# the schedule is not of kind `"interval"`.
|
|
101
|
+
# @return [Integer, nil]
|
|
102
|
+
#
|
|
103
|
+
def minutes
|
|
104
|
+
self["minutes"]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# The cron expression of a `"cron"` schedule. Returns `nil` when the
|
|
109
|
+
# schedule is not of kind `"cron"`.
|
|
110
|
+
# @return [String, nil]
|
|
111
|
+
#
|
|
112
|
+
def expr
|
|
113
|
+
self["expr"]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
##
|
|
118
|
+
# A scheduled background job from the Jobs API (`/api/jobs`): a cron-like
|
|
119
|
+
# recurring task, a one-shot deferred task, or a watchdog script. The same
|
|
120
|
+
# object is returned by create, get, list, update, pause, resume, and
|
|
121
|
+
# trigger.
|
|
122
|
+
#
|
|
123
|
+
# Note the timestamp fields ({#created_at} / {#next_run_at} /
|
|
124
|
+
# {#last_run_at} / {#paused_at}) are **ISO-8601 strings with an offset**,
|
|
125
|
+
# not the epoch floats the runs and responses APIs use; they are exposed
|
|
126
|
+
# verbatim with no Time parsing. The override slots ({#model} /
|
|
127
|
+
# {#provider} / {#base_url} / {#workdir} / {#profile} / {#context_from}) are
|
|
128
|
+
# **read-only** here — the HTTP API ignores them on create/update, so they
|
|
129
|
+
# are surfaced as readers but are not create/update parameters.
|
|
130
|
+
#
|
|
131
|
+
# Field readers are best-effort; {#to_h} remains the source of truth.
|
|
132
|
+
#
|
|
133
|
+
class Job < Entity
|
|
134
|
+
##
|
|
135
|
+
# The job id: 12 lowercase hex characters (no `run_`-style prefix).
|
|
136
|
+
# @return [String, nil]
|
|
137
|
+
#
|
|
138
|
+
def id
|
|
139
|
+
self["id"]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
##
|
|
143
|
+
# The human-friendly job name (required on create).
|
|
144
|
+
# @return [String, nil]
|
|
145
|
+
#
|
|
146
|
+
def name
|
|
147
|
+
self["name"]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
##
|
|
151
|
+
# The task instruction given to the agent each run. `nil` for a
|
|
152
|
+
# `no_agent` script job, which needs none.
|
|
153
|
+
# @return [String, nil]
|
|
154
|
+
#
|
|
155
|
+
def prompt
|
|
156
|
+
self["prompt"]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
##
|
|
160
|
+
# The attached skill names. The singular {#skill} is a separate scalar
|
|
161
|
+
# slot.
|
|
162
|
+
# @return [Array<String>, nil]
|
|
163
|
+
#
|
|
164
|
+
def skills
|
|
165
|
+
self["skills"]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
##
|
|
169
|
+
# The single attached skill (a scalar slot separate from {#skills});
|
|
170
|
+
# `nil` when unused.
|
|
171
|
+
# @return [String, nil]
|
|
172
|
+
#
|
|
173
|
+
def skill
|
|
174
|
+
self["skill"]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
##
|
|
178
|
+
# The per-job model override. **Read-only via this API** (ignored on
|
|
179
|
+
# create/update); `nil` means the profile's configured model is used.
|
|
180
|
+
# @return [String, nil]
|
|
181
|
+
#
|
|
182
|
+
def model
|
|
183
|
+
self["model"]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
##
|
|
187
|
+
# The per-job provider override. **Read-only via this API**; `nil` =
|
|
188
|
+
# the profile's configured provider.
|
|
189
|
+
# @return [String, nil]
|
|
190
|
+
#
|
|
191
|
+
def provider
|
|
192
|
+
self["provider"]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# The per-job base URL override. **Read-only via this API**; `nil` =
|
|
197
|
+
# the profile's configured base URL.
|
|
198
|
+
# @return [String, nil]
|
|
199
|
+
#
|
|
200
|
+
def base_url
|
|
201
|
+
self["base_url"]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
##
|
|
205
|
+
# The path of a script (under `~/.hermes/scripts/`) run each execution;
|
|
206
|
+
# by default its stdout is injected into the agent prompt.
|
|
207
|
+
# @return [String, nil]
|
|
208
|
+
#
|
|
209
|
+
def script
|
|
210
|
+
self["script"]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
##
|
|
214
|
+
# Whether the LLM is skipped entirely: the {#script} runs and its stdout
|
|
215
|
+
# is delivered verbatim (the watchdog pattern). Defaults to `false` (the
|
|
216
|
+
# agent runs) when the field is absent.
|
|
217
|
+
# @return [boolean]
|
|
218
|
+
#
|
|
219
|
+
def no_agent?
|
|
220
|
+
!!self["no_agent"]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
##
|
|
224
|
+
# The source to pull run context from. **Read-only via this API**.
|
|
225
|
+
# @return [String, nil]
|
|
226
|
+
#
|
|
227
|
+
def context_from
|
|
228
|
+
self["context_from"]
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# The schedule, wrapped in a {JobSchedule}. Returns `nil` when absent.
|
|
233
|
+
# @return [JobSchedule, nil]
|
|
234
|
+
#
|
|
235
|
+
def schedule
|
|
236
|
+
raw = self["schedule"]
|
|
237
|
+
raw.is_a?(::Hash) ? JobSchedule.new(raw) : nil
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
##
|
|
241
|
+
# A human-readable rendering of the schedule (mirrors
|
|
242
|
+
# {JobSchedule#display}).
|
|
243
|
+
# @return [String, nil]
|
|
244
|
+
#
|
|
245
|
+
def schedule_display
|
|
246
|
+
self["schedule_display"]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
# The repeat policy, wrapped in a {JobRepeat}. Returns `nil` when absent.
|
|
251
|
+
# @return [JobRepeat, nil]
|
|
252
|
+
#
|
|
253
|
+
def repeat
|
|
254
|
+
raw = self["repeat"]
|
|
255
|
+
raw.is_a?(::Hash) ? JobRepeat.new(raw) : nil
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
##
|
|
259
|
+
# Whether the job is enabled (`false` while paused).
|
|
260
|
+
# @return [boolean, nil]
|
|
261
|
+
#
|
|
262
|
+
def enabled?
|
|
263
|
+
self["enabled"]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
##
|
|
267
|
+
# The lifecycle state, e.g. `"scheduled"` or `"paused"`. (There is no
|
|
268
|
+
# terminal state — an exhausted one-shot or capped job is deleted by the
|
|
269
|
+
# server.)
|
|
270
|
+
# @return [String, nil]
|
|
271
|
+
#
|
|
272
|
+
def state
|
|
273
|
+
self["state"]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
##
|
|
277
|
+
# When the job was paused, as an ISO-8601 string; `nil` when not paused.
|
|
278
|
+
# @return [String, nil]
|
|
279
|
+
#
|
|
280
|
+
def paused_at
|
|
281
|
+
self["paused_at"]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
##
|
|
285
|
+
# The optional reason the job was paused; `nil` for a manual pause.
|
|
286
|
+
# @return [String, nil]
|
|
287
|
+
#
|
|
288
|
+
def paused_reason
|
|
289
|
+
self["paused_reason"]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
##
|
|
293
|
+
# When the job was created, as an ISO-8601 string with offset.
|
|
294
|
+
# @return [String, nil]
|
|
295
|
+
#
|
|
296
|
+
def created_at
|
|
297
|
+
self["created_at"]
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
##
|
|
301
|
+
# When the job is next scheduled to run, as an ISO-8601 string with
|
|
302
|
+
# offset.
|
|
303
|
+
# @return [String, nil]
|
|
304
|
+
#
|
|
305
|
+
def next_run_at
|
|
306
|
+
self["next_run_at"]
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
# When the job last ran, as an ISO-8601 string with offset; `nil` until
|
|
311
|
+
# the first execution.
|
|
312
|
+
# @return [String, nil]
|
|
313
|
+
#
|
|
314
|
+
def last_run_at
|
|
315
|
+
self["last_run_at"]
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
##
|
|
319
|
+
# The outcome of the most recent run: `"ok"` on success or `"error"`
|
|
320
|
+
# when the run failed; `nil` before the first run.
|
|
321
|
+
# @return [String, nil]
|
|
322
|
+
#
|
|
323
|
+
def last_status
|
|
324
|
+
self["last_status"]
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
# The error detail from the last execution when {#last_status} is
|
|
329
|
+
# `"error"`, or `nil` on success. The message is prefixed with the
|
|
330
|
+
# failing exception class, e.g. `"RuntimeError: Gemini HTTP 400 …"`.
|
|
331
|
+
# @return [String, nil]
|
|
332
|
+
#
|
|
333
|
+
def last_error
|
|
334
|
+
self["last_error"]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
##
|
|
338
|
+
# The error detail from the last delivery attempt, or `nil` on success.
|
|
339
|
+
# @return [String, nil]
|
|
340
|
+
#
|
|
341
|
+
def last_delivery_error
|
|
342
|
+
self["last_delivery_error"]
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
##
|
|
346
|
+
# The delivery target, e.g. `"local"`, `"origin"`, `"telegram"`, or
|
|
347
|
+
# `"platform:chat_id"`. Defaults to `"local"`.
|
|
348
|
+
# @return [String, nil]
|
|
349
|
+
#
|
|
350
|
+
def deliver
|
|
351
|
+
self["deliver"]
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
##
|
|
355
|
+
# The originating channel info (for `deliver: "origin"`), passed through
|
|
356
|
+
# raw; `nil` for locally created jobs. Its populated shape was never
|
|
357
|
+
# observed, so it is not wrapped — use {#to_h} / {#[]} if a shape appears.
|
|
358
|
+
# @return [Object, nil]
|
|
359
|
+
#
|
|
360
|
+
def origin
|
|
361
|
+
self["origin"]
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
##
|
|
365
|
+
# The toolset allowlist, passed through raw; `nil` = the default set.
|
|
366
|
+
# Expected to be a plain array of names if populated (never observed), so
|
|
367
|
+
# it is not wrapped.
|
|
368
|
+
# @return [Object, nil]
|
|
369
|
+
#
|
|
370
|
+
def enabled_toolsets
|
|
371
|
+
self["enabled_toolsets"]
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
##
|
|
375
|
+
# The absolute working directory for the run. **Read-only via this API**;
|
|
376
|
+
# `nil` = no project context.
|
|
377
|
+
# @return [String, nil]
|
|
378
|
+
#
|
|
379
|
+
def workdir
|
|
380
|
+
self["workdir"]
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
##
|
|
384
|
+
# The Hermes profile the job runs under. **Read-only via this API**;
|
|
385
|
+
# `nil` = the scheduler's existing profile.
|
|
386
|
+
# @return [String, nil]
|
|
387
|
+
#
|
|
388
|
+
def profile
|
|
389
|
+
self["profile"]
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hermes_agent/client/entity"
|
|
4
|
+
|
|
5
|
+
module HermesAgent
|
|
6
|
+
class Client
|
|
7
|
+
module Entities
|
|
8
|
+
##
|
|
9
|
+
# A model advertised by the server (one entry of the `GET /v1/models`
|
|
10
|
+
# list). Field readers are best-effort; {#to_h} remains the source of
|
|
11
|
+
# truth.
|
|
12
|
+
#
|
|
13
|
+
# The `permission` field is intentionally not exposed as a reader: it was
|
|
14
|
+
# observed empty, its element type is unknown (and likely a nested
|
|
15
|
+
# object), so callers should reach it via {#[]} / {#to_h} for now.
|
|
16
|
+
#
|
|
17
|
+
class Model < Entity
|
|
18
|
+
##
|
|
19
|
+
# The model identifier, e.g. `"hermes-test"`.
|
|
20
|
+
# @return [String, nil]
|
|
21
|
+
#
|
|
22
|
+
def id
|
|
23
|
+
self["id"]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# The object type, `"model"`.
|
|
28
|
+
# @return [String, nil]
|
|
29
|
+
#
|
|
30
|
+
def object
|
|
31
|
+
self["object"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# When the model was created, as a Unix timestamp (seconds).
|
|
36
|
+
# @return [Integer, nil]
|
|
37
|
+
#
|
|
38
|
+
def created
|
|
39
|
+
self["created"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# The organization that owns the model, e.g. `"hermes"`.
|
|
44
|
+
# @return [String, nil]
|
|
45
|
+
#
|
|
46
|
+
def owned_by
|
|
47
|
+
self["owned_by"]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# The root model id this model derives from.
|
|
52
|
+
# @return [String, nil]
|
|
53
|
+
#
|
|
54
|
+
def root
|
|
55
|
+
self["root"]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# The parent model id, or `nil` when there is no parent.
|
|
60
|
+
# @return [String, nil]
|
|
61
|
+
#
|
|
62
|
+
def parent
|
|
63
|
+
self["parent"]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|