llm.rb 13.0.0 → 14.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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +505 -14
  3. data/README.md +484 -50
  4. data/bin/llm.rb +148 -0
  5. data/data/anthropic.json +206 -263
  6. data/data/bedrock.json +2138 -1860
  7. data/data/deepinfra.json +1003 -624
  8. data/data/deepseek.json +38 -34
  9. data/data/google.json +1079 -371
  10. data/data/mistral.json +448 -368
  11. data/data/moonshot.json +384 -0
  12. data/data/openai.json +974 -1343
  13. data/data/xai.json +154 -126
  14. data/data/zai.json +191 -191
  15. data/lib/llm/agent.rb +123 -20
  16. data/lib/llm/context.rb +71 -88
  17. data/lib/llm/cost.rb +23 -17
  18. data/lib/llm/error.rb +0 -8
  19. data/lib/llm/function/array.rb +3 -3
  20. data/lib/llm/function/async/task.rb +2 -0
  21. data/lib/llm/function/fiber/task.rb +2 -0
  22. data/lib/llm/function/fork/task.rb +2 -0
  23. data/lib/llm/function/ractor/task.rb +2 -0
  24. data/lib/llm/function/sequential/group.rb +4 -1
  25. data/lib/llm/function/sequential/task.rb +1 -1
  26. data/lib/llm/function/task.rb +4 -0
  27. data/lib/llm/function/thread/task.rb +2 -0
  28. data/lib/llm/function.rb +33 -6
  29. data/lib/llm/guard/loop.rb +89 -0
  30. data/lib/llm/guard/null.rb +19 -0
  31. data/lib/llm/guard.rb +61 -0
  32. data/lib/llm/provider.rb +36 -0
  33. data/lib/llm/providers/anthropic/stream_parser.rb +1 -0
  34. data/lib/llm/providers/anthropic.rb +2 -9
  35. data/lib/llm/providers/bedrock/request_adapter.rb +1 -1
  36. data/lib/llm/providers/bedrock/stream_parser.rb +1 -0
  37. data/lib/llm/providers/bedrock.rb +1 -8
  38. data/lib/llm/providers/google/stream_parser.rb +1 -0
  39. data/lib/llm/providers/google.rb +1 -8
  40. data/lib/llm/providers/mistral.rb +1 -1
  41. data/lib/llm/providers/moonshot.rb +76 -0
  42. data/lib/llm/providers/ollama.rb +2 -9
  43. data/lib/llm/providers/openai/responses/stream_parser.rb +1 -0
  44. data/lib/llm/providers/openai/responses.rb +7 -9
  45. data/lib/llm/providers/openai/stream_parser.rb +1 -0
  46. data/lib/llm/providers/openai.rb +4 -11
  47. data/lib/llm/repl/bar.rb +4 -3
  48. data/lib/llm/repl/{transcript.rb → buffer.rb} +69 -29
  49. data/lib/llm/repl/color.rb +78 -0
  50. data/lib/llm/repl/command.rb +12 -5
  51. data/lib/llm/repl/commands/compact.rb +2 -2
  52. data/lib/llm/repl/commands/help.rb +3 -5
  53. data/lib/llm/repl/input/char.rb +46 -0
  54. data/lib/llm/repl/input/row.rb +39 -0
  55. data/lib/llm/repl/input.rb +251 -66
  56. data/lib/llm/repl/markdown/table.rb +11 -3
  57. data/lib/llm/repl/markdown.rb +34 -8
  58. data/lib/llm/repl/node.rb +37 -0
  59. data/lib/llm/repl/status.rb +42 -7
  60. data/lib/llm/repl/stream.rb +18 -6
  61. data/lib/llm/repl/walker.rb +3 -2
  62. data/lib/llm/repl/window.rb +54 -35
  63. data/lib/llm/repl.rb +74 -32
  64. data/lib/llm/skill.rb +20 -4
  65. data/lib/llm/stream.rb +8 -7
  66. data/lib/llm/tool.rb +29 -0
  67. data/lib/llm/tools/{swap_text.rb → edit-file.rb} +3 -3
  68. data/lib/llm/tools/git.rb +3 -0
  69. data/lib/llm/tools/mkdir.rb +3 -0
  70. data/lib/llm/tools/rg.rb +3 -0
  71. data/lib/llm/tools/ruby.rb +46 -0
  72. data/lib/llm/tools/shell.rb +3 -0
  73. data/lib/llm/tracer/pretty_logger.rb +127 -0
  74. data/lib/llm/tracer.rb +1 -0
  75. data/lib/llm/transformer/null.rb +21 -0
  76. data/lib/llm/transformer.rb +55 -0
  77. data/lib/llm/version.rb +1 -1
  78. data/lib/llm.rb +12 -2
  79. data/llm.gemspec +9 -2
  80. data/resources/deepdive/advanced/cancellation.md +74 -0
  81. data/resources/deepdive/advanced/compaction.md +83 -0
  82. data/resources/deepdive/advanced/context.md +267 -0
  83. data/resources/deepdive/advanced/guard.md +371 -0
  84. data/resources/deepdive/advanced/tracer.md +180 -0
  85. data/resources/deepdive/advanced/transformer.md +67 -0
  86. data/resources/deepdive/advanced/transports.md +45 -0
  87. data/resources/deepdive/everything_else/audio.md +122 -0
  88. data/resources/deepdive/everything_else/cost.md +99 -0
  89. data/resources/deepdive/everything_else/images.md +89 -0
  90. data/resources/deepdive/everything_else/object.md +108 -0
  91. data/resources/deepdive/everything_else/ocr.md +48 -0
  92. data/resources/deepdive/fundamentals/agents.md +202 -0
  93. data/resources/deepdive/fundamentals/builtin_tools.md +191 -0
  94. data/resources/deepdive/fundamentals/concurrency.md +104 -0
  95. data/resources/deepdive/fundamentals/database.md +449 -0
  96. data/resources/deepdive/fundamentals/embeddings.md +157 -0
  97. data/resources/deepdive/fundamentals/repl.md +87 -0
  98. data/resources/deepdive/fundamentals/schema.md +61 -0
  99. data/resources/deepdive/fundamentals/skills.md +106 -0
  100. data/resources/deepdive/fundamentals/stream.md +110 -0
  101. data/resources/deepdive/fundamentals/tools.md +265 -0
  102. data/resources/deepdive/protocols/a2a.md +106 -0
  103. data/resources/deepdive/protocols/mcp.md +111 -0
  104. data/resources/deepdive.md +58 -1792
  105. metadata +51 -7
  106. data/lib/llm/loop_guard.rb +0 -107
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llm.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 13.0.0
4
+ version: 14.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - robert
@@ -280,23 +280,26 @@ dependencies:
280
280
  description: |
281
281
  llm.rb is an advanced runtime for building capable AI applications
282
282
  on CRuby. By default it has zero runtime dependencies although certain
283
- functionality – such as ActiveRecord support – require
283
+ functionality (such as ActiveRecord support) require
284
284
  optional dependencies that are opt-in.
285
285
  email:
286
286
  - robert@r.uby.dev
287
- executables: []
287
+ executables:
288
+ - llm.rb
288
289
  extensions: []
289
290
  extra_rdoc_files: []
290
291
  files:
291
292
  - CHANGELOG.md
292
293
  - LICENSE
293
294
  - README.md
295
+ - bin/llm.rb
294
296
  - data/anthropic.json
295
297
  - data/bedrock.json
296
298
  - data/deepinfra.json
297
299
  - data/deepseek.json
298
300
  - data/google.json
299
301
  - data/mistral.json
302
+ - data/moonshot.json
300
303
  - data/openai.json
301
304
  - data/xai.json
302
305
  - data/zai.json
@@ -355,8 +358,10 @@ files:
355
358
  - lib/llm/function/thread/group.rb
356
359
  - lib/llm/function/thread/task.rb
357
360
  - lib/llm/function/tracing.rb
361
+ - lib/llm/guard.rb
362
+ - lib/llm/guard/loop.rb
363
+ - lib/llm/guard/null.rb
358
364
  - lib/llm/json_adapter.rb
359
- - lib/llm/loop_guard.rb
360
365
  - lib/llm/mcp.rb
361
366
  - lib/llm/mcp/command.rb
362
367
  - lib/llm/mcp/error.rb
@@ -433,6 +438,7 @@ files:
433
438
  - lib/llm/providers/mistral.rb
434
439
  - lib/llm/providers/mistral/request_adapter.rb
435
440
  - lib/llm/providers/mistral/request_adapter/completion.rb
441
+ - lib/llm/providers/moonshot.rb
436
442
  - lib/llm/providers/ollama.rb
437
443
  - lib/llm/providers/ollama/error_handler.rb
438
444
  - lib/llm/providers/ollama/models.rb
@@ -475,16 +481,20 @@ files:
475
481
  - lib/llm/registry.rb
476
482
  - lib/llm/repl.rb
477
483
  - lib/llm/repl/bar.rb
484
+ - lib/llm/repl/buffer.rb
485
+ - lib/llm/repl/color.rb
478
486
  - lib/llm/repl/command.rb
479
487
  - lib/llm/repl/commands/compact.rb
480
488
  - lib/llm/repl/commands/exit.rb
481
489
  - lib/llm/repl/commands/help.rb
482
490
  - lib/llm/repl/input.rb
491
+ - lib/llm/repl/input/char.rb
492
+ - lib/llm/repl/input/row.rb
483
493
  - lib/llm/repl/markdown.rb
484
494
  - lib/llm/repl/markdown/table.rb
495
+ - lib/llm/repl/node.rb
485
496
  - lib/llm/repl/status.rb
486
497
  - lib/llm/repl/stream.rb
487
- - lib/llm/repl/transcript.rb
488
498
  - lib/llm/repl/walker.rb
489
499
  - lib/llm/repl/window.rb
490
500
  - lib/llm/response.rb
@@ -517,21 +527,25 @@ files:
517
527
  - lib/llm/tool/param.rb
518
528
  - lib/llm/tools.rb
519
529
  - lib/llm/tools/chdir.rb
530
+ - lib/llm/tools/edit-file.rb
520
531
  - lib/llm/tools/git.rb
521
532
  - lib/llm/tools/ls.rb
522
533
  - lib/llm/tools/mkdir.rb
523
534
  - lib/llm/tools/pwd.rb
524
535
  - lib/llm/tools/read_file.rb
525
536
  - lib/llm/tools/rg.rb
537
+ - lib/llm/tools/ruby.rb
526
538
  - lib/llm/tools/shell.rb
527
- - lib/llm/tools/swap_text.rb
528
539
  - lib/llm/tools/utils.rb
529
540
  - lib/llm/tools/which.rb
530
541
  - lib/llm/tools/write_file.rb
531
542
  - lib/llm/tracer.rb
532
543
  - lib/llm/tracer/logger.rb
533
544
  - lib/llm/tracer/null.rb
545
+ - lib/llm/tracer/pretty_logger.rb
534
546
  - lib/llm/tracer/telemetry.rb
547
+ - lib/llm/transformer.rb
548
+ - lib/llm/transformer/null.rb
535
549
  - lib/llm/transport.rb
536
550
  - lib/llm/transport/curb.rb
537
551
  - lib/llm/transport/execution.rb
@@ -552,6 +566,30 @@ files:
552
566
  - lib/sequel/plugins/llm.rb
553
567
  - llm.gemspec
554
568
  - resources/deepdive.md
569
+ - resources/deepdive/advanced/cancellation.md
570
+ - resources/deepdive/advanced/compaction.md
571
+ - resources/deepdive/advanced/context.md
572
+ - resources/deepdive/advanced/guard.md
573
+ - resources/deepdive/advanced/tracer.md
574
+ - resources/deepdive/advanced/transformer.md
575
+ - resources/deepdive/advanced/transports.md
576
+ - resources/deepdive/everything_else/audio.md
577
+ - resources/deepdive/everything_else/cost.md
578
+ - resources/deepdive/everything_else/images.md
579
+ - resources/deepdive/everything_else/object.md
580
+ - resources/deepdive/everything_else/ocr.md
581
+ - resources/deepdive/fundamentals/agents.md
582
+ - resources/deepdive/fundamentals/builtin_tools.md
583
+ - resources/deepdive/fundamentals/concurrency.md
584
+ - resources/deepdive/fundamentals/database.md
585
+ - resources/deepdive/fundamentals/embeddings.md
586
+ - resources/deepdive/fundamentals/repl.md
587
+ - resources/deepdive/fundamentals/schema.md
588
+ - resources/deepdive/fundamentals/skills.md
589
+ - resources/deepdive/fundamentals/stream.md
590
+ - resources/deepdive/fundamentals/tools.md
591
+ - resources/deepdive/protocols/a2a.md
592
+ - resources/deepdive/protocols/mcp.md
555
593
  homepage: https://r.uby.dev/llm/
556
594
  licenses:
557
595
  - MIT
@@ -560,6 +598,11 @@ metadata:
560
598
  source_code_uri: https://github.com/r-uby-dev/llm.rb
561
599
  documentation_uri: https://r.uby.dev/llm/
562
600
  changelog_uri: https://r.uby.dev/api-docs/llm.rb/file.CHANGELOG.html
601
+ post_install_message: |2+
602
+
603
+ Learn more about llm.rb by reading the deepdive:
604
+ https://r.uby.dev/llm/deepdive
605
+
563
606
  rdoc_options: []
564
607
  require_paths:
565
608
  - lib
@@ -574,7 +617,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
574
617
  - !ruby/object:Gem::Version
575
618
  version: '0'
576
619
  requirements: []
577
- rubygems_version: 4.0.6
620
+ rubygems_version: 4.0.16
578
621
  specification_version: 4
579
622
  summary: Ruby's capable AI runtime
580
623
  test_files: []
624
+ ...
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ##
4
- # {LLM::LoopGuard LLM::LoopGuard} is the built-in implementation of
5
- # llm.rb's `guard` capability.
6
- #
7
- # A guard is a context-level supervisor for agentic execution. It can inspect
8
- # the current runtime state and return a warning string when pending tool work
9
- # should be blocked before the loop keeps going.
10
- #
11
- # {LLM::LoopGuard LLM::LoopGuard} detects when a context is repeating the same
12
- # tool-call pattern instead of making progress.
13
- #
14
- # The public interface is intentionally small:
15
- # - `call(ctx)` returns `nil` when no intervention is needed
16
- # - `call(ctx)` returns a warning string when pending tool execution should be blocked
17
- #
18
- # {LLM::Context LLM::Context} can use that warning to return in-band
19
- # {LLM::GuardError LLM::GuardError} tool errors, and
20
- # {LLM::Agent LLM::Agent} enables this guard by default through its wrapped
21
- # context.
22
- #
23
- class LLM::LoopGuard
24
- ##
25
- # The default number of repeated tool-call patterns required before
26
- # the guard intervenes.
27
- # @return [Integer]
28
- DEFAULT_THRESHOLD = 3
29
-
30
- ##
31
- # Returns the repetition threshold.
32
- # @return [Integer]
33
- attr_reader :threshold
34
-
35
- ##
36
- # @param [Hash] config
37
- # @option config [Integer] :threshold
38
- # How many repeated tool-call patterns must appear at the tail of the
39
- # sequence before the guard returns a warning.
40
- def initialize(config = {})
41
- @threshold = config.fetch(:threshold, DEFAULT_THRESHOLD)
42
- end
43
-
44
- ##
45
- # Checks the current context for repeated tool-call patterns.
46
- #
47
- # This method inspects assistant tool calls only. It reduces each call to a
48
- # `[tool_name, arguments]` signature and checks whether the tail of the
49
- # sequence is repeating.
50
- #
51
- # @param [LLM::Context] ctx
52
- # @return [String, nil]
53
- # Returns a warning string when pending tool execution should be blocked,
54
- # or `nil` when execution should continue.
55
- def call(ctx)
56
- repetitions = detect(ctx.messages.to_a)
57
- repetitions ? warning(repetitions) : nil
58
- end
59
-
60
- private
61
-
62
- def detect(messages)
63
- signatures = extract_signatures(messages)
64
- return if signatures.size < threshold
65
- check_repeating_pattern(signatures)
66
- end
67
-
68
- def warning(repetitions)
69
- <<~MSG
70
- SYSTEM NOTICE: Repeated tool-call pattern detected - the same pattern has repeated #{repetitions} times.
71
- You are stuck in a loop and not making progress. Stop and try a fundamentally different approach:
72
- - Re-read the relevant context before retrying
73
- - Try a different tool or strategy
74
- - Break the problem into smaller steps
75
- - If a tool keeps failing, investigate why before retrying
76
- MSG
77
- end
78
-
79
- def extract_signatures(messages)
80
- messages
81
- .select { _1.respond_to?(:functions) && _1.assistant? }
82
- .flat_map { |message| message.functions.map { [_1.name.to_s, _1.arguments.to_s] } }
83
- end
84
-
85
- def check_repeating_pattern(sequence)
86
- max_pattern_len = sequence.size / threshold
87
- (1..max_pattern_len).each do |pattern_len|
88
- count = count_tail_repetitions(sequence, pattern_len)
89
- return count if count >= threshold
90
- end
91
- nil
92
- end
93
-
94
- def count_tail_repetitions(sequence, length)
95
- return 0 if sequence.size < length
96
- pattern = sequence.last(length)
97
- count = 1
98
- pos = sequence.size - length
99
- while pos >= length
100
- candidate = sequence[(pos - length)...pos]
101
- break unless candidate == pattern
102
- count += 1
103
- pos -= length
104
- end
105
- count
106
- end
107
- end