llm.rb 12.3.1 → 12.5.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +395 -0
  3. data/README.md +103 -16
  4. data/data/anthropic.json +249 -249
  5. data/data/bedrock.json +2038 -1879
  6. data/data/deepinfra.json +591 -591
  7. data/data/deepseek.json +31 -31
  8. data/data/google.json +332 -329
  9. data/data/mistral.json +381 -381
  10. data/data/openai.json +1132 -1132
  11. data/data/xai.json +138 -138
  12. data/data/zai.json +165 -165
  13. data/lib/llm/agent.rb +12 -7
  14. data/lib/llm/buffer.rb +15 -0
  15. data/lib/llm/context/deserializer.rb +2 -5
  16. data/lib/llm/context.rb +4 -5
  17. data/lib/llm/function.rb +2 -35
  18. data/lib/llm/message.rb +12 -2
  19. data/lib/llm/provider.rb +11 -1
  20. data/lib/llm/providers/anthropic/stream_parser.rb +3 -12
  21. data/lib/llm/providers/anthropic.rb +11 -0
  22. data/lib/llm/providers/bedrock/stream_parser.rb +4 -13
  23. data/lib/llm/providers/google/stream_parser.rb +3 -12
  24. data/lib/llm/providers/google.rb +7 -0
  25. data/lib/llm/providers/mistral.rb +11 -0
  26. data/lib/llm/providers/ollama/stream_parser.rb +4 -4
  27. data/lib/llm/providers/ollama.rb +11 -0
  28. data/lib/llm/providers/openai/responses/stream_parser.rb +4 -14
  29. data/lib/llm/providers/openai/responses.rb +10 -0
  30. data/lib/llm/providers/openai/stream_parser.rb +5 -15
  31. data/lib/llm/providers/openai.rb +11 -0
  32. data/lib/llm/repl/command.rb +201 -0
  33. data/lib/llm/repl/commands/exit.rb +23 -0
  34. data/lib/llm/repl/commands/help.rb +24 -0
  35. data/lib/llm/repl/input.rb +118 -35
  36. data/lib/llm/repl/stream.rb +43 -8
  37. data/lib/llm/repl/transcript.rb +6 -0
  38. data/lib/llm/repl/window.rb +28 -6
  39. data/lib/llm/repl.rb +125 -27
  40. data/lib/llm/stream.rb +2 -7
  41. data/lib/llm/tools/ls.rb +30 -0
  42. data/lib/llm/tools/which.rb +38 -0
  43. data/lib/llm/version.rb +1 -1
  44. data/resources/deepdive.md +67 -1
  45. metadata +6 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1be1cd2af0cc8a817e510b04d1d74efd08cedf714b3faff4bdb7a58345fe14f
4
- data.tar.gz: 3768fdf7f131d30c04d0ba83514bec31eb8d64de661980aa811a4e804a4df1e5
3
+ metadata.gz: 665281b1c81541ff2f9621138cee510e473dfbda2c7dcff7d1a324e5fe558c89
4
+ data.tar.gz: c3ee596e41885038dc831ef6459da330d1984fea0125a9dbc5aa7d0ff8e21061
5
5
  SHA512:
6
- metadata.gz: 667c546559bb199c7381fbea97171b5e82e874098c40a763004db85667038aa430fa98592c4d42b1426e4536e288e70c018278b9e7af9fcf9b6d972eabd65cbe
7
- data.tar.gz: 0163f85848ce7292905396458755d9ccb23298c6f7c6f06cf0532fb3bdb3517e1b188a022937eb540b823d82d7bc8d869549bc17c8d79767c5efe027bf5ae9be
6
+ metadata.gz: cecf7fa0a2b1c6aff010c537b6741c74e537104eb9a67e3c2cd2840d06963040834c87263f71f6a1f815a4be831efa7740b2c03eb3f5fde9bbb274be4ee69c5c
7
+ data.tar.gz: 8b695e7c1656bf427bb095a257e954437b5b2dfc46e4d22349eb91dab9981eac1c5fc6b46bca89ecfec14986d386a0fba6303ef4dcf39a82345d3a136e944092
data/CHANGELOG.md CHANGED
@@ -15,8 +15,403 @@
15
15
 
16
16
  ## What's next
17
17
 
18
+ Changes since `v12.5.0`.
19
+
20
+ ## v12.5.0
21
+
22
+ Changes since `v12.4.0`.
23
+
24
+ This release extends the REPL command system with typed parameters, a
25
+ built-in `/help` command, command aliases (`/quit`), and cancellation
26
+ via the 'Esc' key.
27
+
28
+ The default HTTP timeout is increased to 15 minutes (900s) to better
29
+ accommodate reasoning models and large structured outputs.
30
+ `LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self` for
31
+ method chaining, and `LLM::Buffer#pop` is added for tail-end message
32
+ removal.
33
+
34
+ Tool resolution gains a fallback to the global `LLM::Function` registry
35
+ before raising `LLM::NoSuchToolError`, and `pending_functions` aliases
36
+ are added on both contexts and agents for a consistent interface.
37
+
38
+ Several REPL bugs are fixed including parameter state leakage across
39
+ turns and invalid tool-call error routing.
40
+
41
+ Model metadata is refreshed across all providers with new Anthropic,
42
+ OpenAI, Google, DeepInfra, DeepSeek, and xAI model entries.
43
+
44
+ ### Add
45
+
46
+ #### Buffer & function internals
47
+
48
+ * **buffer: add `LLM::Buffer#pop`** <br>
49
+ Add `LLM::Buffer#pop` for removing the last message from the tail
50
+ of the buffer, complementing the existing `#<<` and array-style
51
+ message management.
52
+
53
+ * **function: add registry fallback for tool resolution** <br>
54
+ When resolving tool calls from a message, if the tool is not found
55
+ in the available tools list, it now also looks up the global
56
+ `LLM::Function` registry via `LLM::Function.find_by_name` before
57
+ creating a placeholder function. This improves tool resolution for
58
+ tools that are registered globally but not passed directly through
59
+ the request tool set.
60
+
61
+ #### Consistent `pending_functions` aliases
62
+
63
+ * **context: alias `LLM::Context#functions` as `LLM::Context#pending_functions`** <br>
64
+ Add `LLM::Context#pending_functions` as an alias for `LLM::Context#functions`,
65
+ so callers that prefer the more descriptive `pending_functions` name can use
66
+ it instead of `functions` when checking for unresolved tool work.
67
+
68
+ * **agent: alias `LLM::Agent#functions` as `LLM::Agent#pending_functions`** <br>
69
+ Add `LLM::Agent#pending_functions` as an alias for `LLM::Agent#functions`,
70
+ matching the same alias on `LLM::Context`, so callers have a consistent
71
+ `pending_functions` interface across both contexts and agents.
72
+
73
+ #### REPL command system
74
+
75
+ * **repl: extend command system with parameter support** <br>
76
+ Commands can now declare typed parameters using the `parameter`
77
+ DSL, modelled after `LLM::Tool` and `LLM::Schema` conventions.
78
+ Parameters can be marked as required with `required %i[...]`,
79
+ and values are type-checked before being passed to `call`.
80
+ Argument parsing is handled by the repl: arguments are split
81
+ from the input string and assigned to parameters by position.
82
+
83
+ ```ruby
84
+ class Greeter < LLM::Command
85
+ name "greet"
86
+ description "Greets the given name"
87
+ parameter :name, String, "The person's name"
88
+ required %i[name]
89
+
90
+ def call(name:)
91
+ write("Welcome #{name}!\n")
92
+ end
93
+ end
94
+ ```
95
+
96
+ * **repl: add `help` command** <br>
97
+ Add `LLM::Repl::Help` as a new built-in command, registered
98
+ automatically via the command registry. Typing `/help` shows
99
+ the `help` command's own name, description, and parameters,
100
+ while `/help <name>` shows details for a specific command,
101
+ including its parameters and whether each is required or
102
+ optional. Unknown command names produce an error message.
103
+
104
+ ```ruby
105
+ class Help < Command
106
+ name "help"
107
+ description "show help for a given command"
108
+ parameter :name, String, "The name of a command"
109
+
110
+ def call(name: nil)
111
+ if name.nil?
112
+ write("\n#{self.class.help}\n\n")
113
+ elsif command = LLM::Command.find_by(name:)
114
+ write("\n#{command.help}\n\n")
115
+ else
116
+ write "\nNo help for #{name} was found" \
117
+ "\nThat command doesn't exist.\n\n"
118
+ end
119
+ end
120
+ end
121
+ ```
122
+
123
+ * **repl: add support for command aliases** <br>
124
+ Commands can now be aliased by creating a subclass of another
125
+ command (with `LLM::Command` as an indirect ancestor). The
126
+ first alias introduced is `/quit` as an alias of `/exit`.
127
+
128
+ ```ruby
129
+ class Quit < Command::Exit
130
+ name "quit"
131
+ end
132
+ ```
133
+
134
+ * **repl: add `Command::Parameter#optional?`** <br>
135
+ Parameters now expose an `#optional?` method that returns `true`
136
+ when a parameter has not been marked as required, making it
137
+ possible to query parameter optionality programmatically.
138
+
139
+ * **repl: add `LLM::Repl::Command#write`** <br>
140
+ Commands can now write output to the transcript via the `write`
141
+ method. Commands also receive a reference to the active repl
142
+ through their `#initialize` method, making it possible to
143
+ interact with the repl window from within a command.
144
+
145
+ * **repl: display command errors in the curses UI** <br>
146
+ Commands invoked with too few arguments now display an error
147
+ message — `command(<name>): too few arguments` — directly in
148
+ the curses transcript area, giving immediate feedback instead
149
+ of silently failing.
150
+
151
+ * **repl: add `LLM::Command` convenience constant** <br>
152
+ Add `LLM::Command = LLM::Repl::Command` as a shorter alias,
153
+ available once `"llm/repl"` is required.
154
+
155
+ #### Misc
156
+
157
+ * **repl: implement cancellation with the 'Esc' key** <br>
158
+ The curses-based REPL now supports cancelling an active model
159
+ request by pressing the 'Esc' key. When a request is in progress,
160
+ the status line shows `thinking • Esc to cancel`, and pressing
161
+ Esc calls `LLM::Agent#cancel!` to interrupt the request. The
162
+ transcript displays `request cancelled!` to confirm the
163
+ cancellation.
164
+
165
+ ### Change
166
+
167
+ #### Misc
168
+
169
+ * **provider: increase default timeout to 900s** <br>
170
+ The default HTTP timeout for all providers has been increased from
171
+ 180 to 900 seconds (15 minutes) to better accommodate long-running
172
+ requests such as reasoning models and large structured outputs.
173
+
174
+ * **agent: `deserialize` and `restore` return `self`** <br>
175
+ `LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self`
176
+ (the agent instance) instead of forwarding the context's return
177
+ value, enabling method chaining after restoring agent state.
178
+
179
+ * **context: discard all messages from a cancelled turn** <br>
180
+ When `LLM::Context#cancel!` is called, all messages added during
181
+ that turn are now discarded via `Buffer#slice!`, preventing edge
182
+ cases where dangling tool calls between turns caused repeated
183
+ cancellation loops. The `#repair!` method now handles tool call
184
+ cancellations on the next turn instead of mutating the conversation
185
+ buffer directly at cancellation time.
186
+
187
+ * **stream: drop the `error` argument from `on_tool_call`** <br>
188
+ The `on_tool_call` callback no longer accepts an `error` argument.
189
+ Previously, stream parsers passed both a tool and an optional error,
190
+ requiring boilerplate like `if error; queue << error; end` in every
191
+ callback. Error handling is now pushed directly onto the stream queue
192
+ inside each provider's stream parser, so `on_tool_call(tool)` is the
193
+ only signature. The REPL stream and base `LLM::Stream` class have
194
+ been updated accordingly.
195
+
196
+ #### REPL internals
197
+
198
+ * **repl: pass the repl instance to command constructors** <br>
199
+ `LLM::Repl::Command` subclasses now receive the active repl
200
+ instance via `initialize(repl)`, enabling commands to write
201
+ to the transcript and interact with the repl window.
202
+
203
+ * **repl: `Command#write` prefixes messages with the command name** <br>
204
+ The `#write` method now prefixes output with `command(<name>): `
205
+ so command messages are consistent with the `user:` and `agent:`
206
+ labels in the transcript. The prefix can be customised with the
207
+ `who:` keyword argument, or set to `who: nil` to disable it
208
+ entirely.
209
+
210
+ ### Fix
211
+
212
+ #### Misc
213
+
214
+ * **function: avoid silent skip of tools not found in available tools** <br>
215
+ When a model calls a tool that is not present in the available tools
216
+ list, instead of silently skipping the tool call (via `next`), a
217
+ `LLM::NoSuchToolError` is now raised so the model receives feedback
218
+ about the invalid tool call and can correct course.
219
+ <br><br>
220
+ An additional fallback to the global `LLM::Function` registry is
221
+ tried before raising, so globally registered tools are still
222
+ resolved even when not in the per-request tool set.
223
+
224
+ #### REPL bugs
225
+
226
+ * **repl: don't persist parameter state between turns** <br>
227
+ Parameter state (such as `Parameter#value`) was leaking across
228
+ turns because the same parameter objects were being mutated
229
+ in place. A duplicate set of parameters is now created for each
230
+ turn, keeping the original parameter definitions intact and
231
+ preventing stale state from carrying over.
232
+
233
+ * **repl: reply with error when given an invalid tool** <br>
234
+ When the model tries to call a tool that does not exist, the
235
+ error is now pushed onto the stream queue so the model can
236
+ see the error and correct course, instead of silently dropping
237
+ the invalid tool call and leaving it to `Context#repair` to
238
+ remove it from history.
239
+
240
+ * **repl: fix save of initial runtime state** <br>
241
+ Fix a bug in `LLM::Repl#configure` where a non-existent path
242
+ argument was treated as no path at all, preventing the initial
243
+ runtime state from being saved after the first turn. The correct
244
+ behavior is to create the file so it can be written to after
245
+ the first turn completes.
246
+
247
+ ### Refresh
248
+
249
+ * **Refresh model metadata across all providers** <br>
250
+ Update model listings, pricing, capabilities, reasoning options,
251
+ modality support, context limits, and release dates across all
252
+ provider registries (Anthropic, AWS Bedrock, DeepInfra, DeepSeek,
253
+ Google, Mistral, OpenAI, xAI, and ZAI). Notable changes include
254
+ Anthropic claude-opus-4-8 and claude-sonnet-4-6 additions with
255
+ effort-based reasoning, OpenAI gpt-5.6-sol/terra/luna and
256
+ gpt-5-codex additions, Google gemini-3-pro-preview and
257
+ gemini-3-flash-preview additions, DeepInfra Qwen3.5 and DeepSeek
258
+ V4 model additions, and updated xAI Grok model entries.
259
+
260
+ ## v12.4.0
261
+
18
262
  Changes since `v12.3.1`.
19
263
 
264
+ This release brings major improvements to the curses-based REPL
265
+ (`LLM::Agent#repl`). The REPL now supports saving and restoring runtime
266
+ state across sessions, automatic paste-mode detection for fast bulk input,
267
+ a command system foundation with the `/exit` command, and several new
268
+ keybindings (Ctrl+F, Ctrl+K, Ctrl+Y). Tool calls are rendered with a
269
+ compact function-call syntax in the status bar.
270
+
271
+ Two new built-in tools — `LLM::Tool::Ls` and `LLM::Tool::Which` — are
272
+ available as opt-in additions for file listing and executable lookup.
273
+
274
+ Model metadata has been refreshed across providers, the REPL loop
275
+ internals have been refactored to use `catch`/`throw` for cleaner command
276
+ routing, and several bugs have been fixed including a tracer restoration
277
+ issue in the agent ensure clause and a missing cursor in the REPL input
278
+ area.
279
+
280
+ ### Add
281
+
282
+ * **repl: allow runtime state to be saved and restored** <br>
283
+ `LLM::Agent#repl` now accepts a `path:` option that serializes
284
+ runtime state to the filesystem. When the path already exists,
285
+ runtime state is restored when the read-eval-print loop starts.
286
+ Otherwise the path is written after the first turn, making it
287
+ possible to resume a session across process restarts.
288
+
289
+ * **repl: scroll to the bottom on submit** <br>
290
+ The curses-based REPL now scrolls the transcript to the bottom when
291
+ the user submits their input, so the latest response is visible
292
+ without needing to scroll down manually.
293
+
294
+ * **repl: add Ctrl+F to move the cursor forward** <br>
295
+ The curses-based REPL input now supports Ctrl+F to move the cursor
296
+ forward by one column, matching common terminal editing conventions
297
+ found in shells like `/bin/sh`.
298
+
299
+ * **repl: add Ctrl+K to erase from cursor to end of line** <br>
300
+ The curses-based REPL input now supports Ctrl+K to erase all text
301
+ from the cursor position to the end of the input buffer, matching
302
+ common terminal editing conventions found in shells like `/bin/sh`.
303
+
304
+ * **repl: add Ctrl+Y to paste previously killed text** <br>
305
+ The curses-based REPL input now supports Ctrl+Y to insert the most
306
+ recently killed text (via Ctrl+K) at the current cursor position,
307
+ matching the yank/paste convention found in shells like `/bin/sh`.
308
+ The killed text is stored in an internal copy buffer so it can be
309
+ pasted multiple times or at different cursor positions.
310
+
311
+ * **repl: add command system foundation** <br>
312
+ Add `LLM::Repl::Command` as a new base class for REPL commands,
313
+ along with the first built-in command `LLM::Repl::Command::Exit`
314
+ which exits the read-eval-print loop via `throw(:exit)`.
315
+ Commands are identified by a name and can be looked up through
316
+ `Command.find_by`. This is the foundation for the `/` command
317
+ syntax used in the REPL input line.
318
+
319
+ * **repl: connect the command system to user input** <br>
320
+ The curses-based REPL now routes user input through the command
321
+ system. Any input string beginning with `"/"` is matched against
322
+ the command registry via `Command.find_by`, and the corresponding
323
+ command is executed instead of being forwarded to the model.
324
+ This makes built-in commands like `/exit` functional from the
325
+ input line. Command arguments are not yet supported.
326
+
327
+ * **repl: add `LLM::Repl::Command.registry`** <br>
328
+ Add `LLM::Repl::Command.registry` for auto-registering command
329
+ subclasses. The `inherited` hook captures each new subclass and
330
+ stores it in the registry, making it possible to enumerate all
331
+ available commands at runtime. Built-in commands like Exit are
332
+ automatically registered when the command file is loaded.
333
+
334
+ * **repl: detect and handle paste mode in the input line** <br>
335
+ The curses-based REPL input now detects paste operations by tracking
336
+ the rate at which characters arrive. A paste rate of ≤50ms is
337
+ assumed to be a burst of characters that could only be explained by
338
+ a paste — no human types that fast. Multiline pastes are supported
339
+ through internal refactoring of the input handling logic.
340
+
341
+ * **repl: optimize paste mode rendering** <br>
342
+ Track the paste state with an internal `@paste` variable and switch
343
+ to a faster input path during paste operations. While in paste mode,
344
+ the input buffer is drained via `Curses.getch`, bypassing the more
345
+ expensive char-by-char render path used for ordinary interactive
346
+ input. This makes pasting large amounts of text noticeably faster.
347
+
348
+ * **Add `LLM::Tool::Ls`** <br>
349
+ Add a built-in tool for listing files and directories, with optional
350
+ glob pattern filtering to narrow results. <br>
351
+ It must be required explicitly with `require "llm/tools/ls"`.
352
+
353
+ * **Add `LLM::Tool::Which`** <br>
354
+ Add a built-in tool for locating an executable on the system PATH.
355
+ This lets an agent check whether a command is available before
356
+ attempting to run it, avoiding failed subprocess calls. <br>
357
+ It must be required explicitly with `require "llm/tools/which"`.
358
+
359
+ * **repl: render tool calls in a function-call syntax** <br>
360
+ The curses-based REPL status bar now renders tool calls with a
361
+ compact function-call syntax — `tool(key: value)` instead of
362
+ `tool: name`. Strings are quoted and truncated, arrays show their
363
+ first two elements, and hashes collapse to `{…}`, making it easier
364
+ to see what arguments the model is passing. The `tool done` status
365
+ message has been removed since the tool call itself conveys
366
+ completion information.
367
+
368
+ ### Change
369
+
370
+ * **Refresh model metadata** <br>
371
+ Update model listings, pricing, and capabilities across providers.
372
+ Fix GPT-5.6 model family names in the OpenAI registry (`gpt` to
373
+ `gpt-sol`, `gpt-nano` to `gpt-luna`, `gpt-mini` to `gpt-terra`).
374
+ Add OpenAI models (`gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`)
375
+ to the AWS Bedrock registry. Update DeepInfra pricing for
376
+ `DeepSeek-V3` and `Sky-T1-32B-Preview`. Fix Google model knowledge
377
+ cutoff dates.
378
+
379
+ * **repl: control the loop with catch & throw** <br>
380
+ The curses-based REPL input loop now uses `catch(:exit)` and
381
+ `throw(:exit)` instead of returning the `:exit` symbol and
382
+ breaking out of the loop. This enables the `/command` syntax
383
+ without requiring an `:exit` return value to be propagated
384
+ through a potentially deeply nested call path.
385
+
386
+ * **repl: replace Ctrl+D with shell-like delete-at-cursor** <br>
387
+ The curses-based REPL input now treats Ctrl+D as a delete action
388
+ that removes the character at the current cursor position, matching
389
+ the shell/Emacs convention where Ctrl+D deletes the character under
390
+ the cursor instead of signalling end-of-file. The previous Ctrl+D
391
+ behaviour (exiting the REPL) is superseded by the `/exit` command.
392
+
393
+ * **repl: switch to 'Thinking' mode after tool return** <br>
394
+ The curses-based REPL status line now switches to "Thinking" mode
395
+ after a tool returns, so the user can see the agent is processing
396
+ the tool result rather than showing a stale tool-call status.
397
+
398
+ ### Fix
399
+
400
+ * **agent: fix a subtle typo in the ensure clause** <br>
401
+ Fix a subtle typo in `LLM::Agent` where the deprecated `trace` local
402
+ variable was given preference over `tracer` (the preferred local name)
403
+ in an `ensure` clause. The `trace` local was supported for backward
404
+ compatibility but the ensure clause still referenced `trace` instead of
405
+ `tracer`, which meant the previous tracer was never restored when the
406
+ REPL session ended.
407
+
408
+ * **repl: restore the cursor in the input area** <br>
409
+ Remove the `Curses.curs_set(0)` call from the REPL redraw method,
410
+ which was inadvertently hiding the cursor and making it impossible
411
+ to see the current position in the input area. The input field is
412
+ now always drawn at its full height so the cursor position is
413
+ correctly maintained after each redraw.
414
+
20
415
  ## v12.3.1
21
416
 
22
417
  Changes since `v12.3.0`.
data/README.md CHANGED
@@ -129,28 +129,56 @@ This feature requires that the [curses](https://github.com/ruby/curses)
129
129
  and [kramdown](https://github.com/gettalong/kramdown) libraries are
130
130
  installed and available to require.
131
131
 
132
+ The TUI displays a status line with a context-usage bar and cost
133
+ counter, a scrollable transcript with markdown rendering, and a
134
+ multi-line input area. The UI stays responsive while the model
135
+ is generating a response.
136
+
137
+ ##### REPL: Agent
138
+
139
+ A REPL session is started by calling `repl` on any agent
140
+ instance. The session inherits the agent's model, tools,
141
+ skills, and instructions.
142
+
132
143
  ```ruby
144
+ require "llm"
145
+
133
146
  llm = LLM.deepseek(key: ENV["KEY"])
134
147
  agent = LLM::Agent.new(llm)
135
148
  agent.repl
136
149
  ```
137
150
 
138
- The read-eval-print loop accepts both `tools` and `skills`
139
- options that lets you attach additional tools or skills
140
- for the duration of the session. This is in addition to
141
- any tools or skills that might already be associated with
142
- an agent.
151
+ ##### REPL: State
152
+
153
+ The `path:` option accepts a file path where runtime state
154
+ is read from and written to. This lets you resume a
155
+ conversation across REPL sessions.
143
156
 
144
157
  ```ruby
158
+ require "llm"
159
+
145
160
  llm = LLM.deepseek(key: ENV["KEY"])
146
161
  agent = LLM::Agent.new(llm)
147
- agent.repl(tools: [Debugger], skills: [__dir__])
162
+ agent.repl(path: "session.json")
148
163
  ```
149
164
 
150
- A number of optional tools are distributed as part
151
- of llm.rb. They power the agents that can be found in
152
- the [agents/](agents/) directory, so they're optimized
153
- for developer tasks.
165
+ ##### REPL: Tools
166
+
167
+ The `tools` option lets you attach additional tools
168
+ for the duration of the session. This is in addition to
169
+ any tools that might already be associated with an agent.
170
+
171
+ A number of optional tools are distributed as part of
172
+ llm.rb. They power the agents that can be found in the
173
+ [agents/](agents/) directory.
174
+
175
+ ```ruby
176
+ require "llm"
177
+
178
+ llm = LLM.deepseek(key: ENV["KEY"])
179
+ agent = LLM::Agent.new(llm)
180
+ agent.repl(tools: [Debugger])
181
+ ```
154
182
 
155
183
  The following example starts a read-eval-print loop
156
184
  with all of the builtin tools available.
@@ -164,18 +192,77 @@ agent = LLM::Agent.new(llm)
164
192
  agent.repl(tools: LLM::Tool.subclasses)
165
193
  ```
166
194
 
167
- By default the tracer is disabled for the duration of
168
- the session. This can be configured through the
169
- `tracer` option. Setting it to `true` will configure
170
- the REPL to use the tracer associated with an instance
171
- of [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
195
+ ##### REPL: Skills
196
+
197
+ The `skills` option lets you load extra skill directories
198
+ without attaching them to an agent permanently.
172
199
 
173
200
  ```ruby
201
+ require "llm"
202
+
174
203
  llm = LLM.deepseek(key: ENV["KEY"])
175
- agent = LLM::Agent.new(llm, tracer: LLM.logger(llm, path: "agent.log"))
204
+ agent = LLM::Agent.new(llm)
205
+ agent.repl(skills: [__dir__])
206
+ ```
207
+
208
+ ##### REPL: Tracer
209
+
210
+ By default the tracer is disabled for the duration of the
211
+ session. Setting `tracer: true` configures the REPL to use
212
+ the tracer associated with an instance of
213
+ [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
214
+
215
+ ```ruby
216
+ require "llm"
217
+
218
+ llm = LLM.deepseek(key: ENV["KEY"])
219
+ tracer = LLM.logger(llm, path: "agent.log")
220
+ agent = LLM::Agent.new(llm, tracer:)
176
221
  agent.repl(tracer: true, tools: [Debugger])
177
222
  ```
178
223
 
224
+ ##### REPL: Commands
225
+
226
+ Commands are recognized by a `/` prefix and are backed by the
227
+ [`LLM::Repl::Command`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Command.html)
228
+ class, which can be subclassed to add custom commands. Once you
229
+ create a subclass, it is automatically added to the repl. A command
230
+ can have zero or more parameters, and all parameters are presumed
231
+ to be a String (at least for now).
232
+
233
+ ```ruby
234
+ require "llm"
235
+ require "llm/repl"
236
+
237
+ class Greeter < LLM::Command
238
+ name "greet"
239
+ description "Greets the given name"
240
+ parameter :name, String, "The person's name"
241
+ required %i[name]
242
+
243
+ def call(name:)
244
+ write("Welcome #{name}!\n")
245
+ end
246
+ end
247
+ ```
248
+
249
+ ##### REPL: Input
250
+
251
+ The input area supports several keyboard shortcuts:
252
+
253
+ | Key | Action |
254
+ |---|---|
255
+ | `Enter` | Submit the current prompt |
256
+ | `Ctrl+A` | Jump to the start of the line |
257
+ | `Ctrl+E` | Jump to the end of the line |
258
+ | `Ctrl+F` | Move the cursor forward |
259
+ | `Ctrl+K` | Erase from cursor to the end of the line |
260
+ | `Ctrl+Y` | Paste previously killed text |
261
+ | `Ctrl+D` | Delete the character at the cursor |
262
+ | `Left / Right` | Move the cursor |
263
+ | `Up / Down` | Scroll the transcript |
264
+ | `/exit` | Leave the REPL |
265
+
179
266
  #### LLM::MCP
180
267
 
181
268
  The Model Context Protocol (MCP) has first-class support