llm.rb 15.2.2 → 15.4.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -3
  3. data/README.md +180 -64
  4. data/bin/llm.rb +9 -2
  5. data/data/alibaba.json +45 -0
  6. data/data/anthropic.json +67 -0
  7. data/data/bedrock.json +1466 -397
  8. data/data/deepinfra.json +148 -16
  9. data/data/deepseek.json +3 -0
  10. data/data/mistral.json +42 -0
  11. data/data/openai.json +186 -0
  12. data/data/openrouter.json +1538 -378
  13. data/data/xai.json +53 -20
  14. data/data/zai.json +90 -4
  15. data/docs/deepdive/advanced/compaction.md +1 -2
  16. data/docs/deepdive/advanced/context.md +214 -1
  17. data/docs/deepdive/advanced/guard.md +9 -57
  18. data/docs/deepdive/features/builtin_tools.md +14 -16
  19. data/docs/deepdive/features/console.md +5 -0
  20. data/docs/deepdive/features/database.md +85 -10
  21. data/docs/deepdive/fundamentals/agents.md +7 -8
  22. data/docs/deepdive/fundamentals/providers.md +45 -5
  23. data/docs/deepdive/fundamentals/schema.md +73 -0
  24. data/docs/deepdive/fundamentals/tools.md +80 -27
  25. data/docs/deepdive/media/audio.md +8 -19
  26. data/docs/deepdive/media/images.md +8 -10
  27. data/docs/deepdive/media/ocr.md +1 -3
  28. data/docs/deepdive/reference/cost.md +48 -0
  29. data/docs/deepdive/reference/tracer.md +76 -0
  30. data/docs/deepdive.md +1 -1
  31. data/lib/llm/active_record/message.rb +113 -0
  32. data/lib/llm/active_record.rb +1 -0
  33. data/lib/llm/agent.rb +44 -19
  34. data/lib/llm/console/buffer.rb +9 -1
  35. data/lib/llm/console.rb +6 -1
  36. data/lib/llm/context/deserializer.rb +10 -3
  37. data/lib/llm/context.rb +62 -26
  38. data/lib/llm/guard.rb +2 -8
  39. data/lib/llm/message.rb +18 -7
  40. data/lib/llm/provider.rb +74 -16
  41. data/lib/llm/providers/alibaba.rb +15 -0
  42. data/lib/llm/providers/anthropic/error_handler.rb +5 -2
  43. data/lib/llm/providers/anthropic/files.rb +12 -12
  44. data/lib/llm/providers/anthropic/models.rb +2 -2
  45. data/lib/llm/providers/anthropic.rb +5 -3
  46. data/lib/llm/providers/bedrock/error_handler.rb +3 -2
  47. data/lib/llm/providers/bedrock/models.rb +5 -3
  48. data/lib/llm/providers/bedrock.rb +5 -3
  49. data/lib/llm/providers/deepinfra/audio.rb +4 -4
  50. data/lib/llm/providers/deepinfra/images.rb +4 -4
  51. data/lib/llm/providers/google/error_handler.rb +5 -2
  52. data/lib/llm/providers/google/files.rb +10 -10
  53. data/lib/llm/providers/google/images.rb +2 -2
  54. data/lib/llm/providers/google/models.rb +2 -2
  55. data/lib/llm/providers/google.rb +7 -7
  56. data/lib/llm/providers/mistral.rb +3 -1
  57. data/lib/llm/providers/ollama/error_handler.rb +5 -2
  58. data/lib/llm/providers/ollama/models.rb +2 -2
  59. data/lib/llm/providers/ollama.rb +7 -5
  60. data/lib/llm/providers/openai/audio.rb +6 -6
  61. data/lib/llm/providers/openai/error_handler.rb +5 -2
  62. data/lib/llm/providers/openai/files.rb +10 -10
  63. data/lib/llm/providers/openai/images.rb +4 -4
  64. data/lib/llm/providers/openai/models.rb +2 -2
  65. data/lib/llm/providers/openai/moderations.rb +2 -2
  66. data/lib/llm/providers/openai/request_adapter.rb +1 -1
  67. data/lib/llm/providers/openai/responses.rb +8 -8
  68. data/lib/llm/providers/openai/vector_stores.rb +22 -22
  69. data/lib/llm/providers/openai.rb +10 -8
  70. data/lib/llm/providers/xai/images.rb +4 -4
  71. data/lib/llm/schema.rb +24 -0
  72. data/lib/llm/tracer/telemetry.rb +4 -4
  73. data/lib/llm/tracer.rb +11 -3
  74. data/lib/llm/transport/execution.rb +8 -4
  75. data/lib/llm/utils.rb +13 -0
  76. data/lib/llm/version.rb +1 -1
  77. data/llm.gemspec +2 -2
  78. metadata +5 -5
  79. data/lib/llm/guard/loop.rb +0 -89
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: 15.2.2
4
+ version: 15.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Gleeson
@@ -429,6 +429,7 @@ files:
429
429
  - lib/llm/active_record.rb
430
430
  - lib/llm/active_record/acts_as_agent.rb
431
431
  - lib/llm/active_record/acts_as_llm.rb
432
+ - lib/llm/active_record/message.rb
432
433
  - lib/llm/agent.rb
433
434
  - lib/llm/buffer.rb
434
435
  - lib/llm/compactor.rb
@@ -492,7 +493,6 @@ files:
492
493
  - lib/llm/function/thread/task.rb
493
494
  - lib/llm/function/tracing.rb
494
495
  - lib/llm/guard.rb
495
- - lib/llm/guard/loop.rb
496
496
  - lib/llm/guard/null.rb
497
497
  - lib/llm/json_adapter.rb
498
498
  - lib/llm/mcp.rb
@@ -695,8 +695,8 @@ metadata:
695
695
  source_code_uri: https://github.com/r-uby-dev/llm
696
696
  documentation_uri: https://r.uby.dev
697
697
  changelog_uri: https://github.com/r-uby-dev/llm/blob/main/CHANGELOG.md
698
- post_install_message: "\nGot a question about llm.rb? \nThe https://r.uby.dev website
699
- can help.\n\n"
698
+ post_install_message: "\nWant to see what I'm building with llm.rb? \nCheckout the
699
+ https://r.uby.dev website.\n\n"
700
700
  rdoc_options: []
701
701
  require_paths:
702
702
  - lib
@@ -711,7 +711,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
711
711
  - !ruby/object:Gem::Version
712
712
  version: '0'
713
713
  requirements: []
714
- rubygems_version: 4.0.20
714
+ rubygems_version: 4.0.16
715
715
  specification_version: 4
716
716
  summary: Ruby's capable AI runtime
717
717
  test_files: []
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class LLM::Guard
4
- ##
5
- # {LLM::Guard::Loop LLM::Guard::Loop} is the built-in loop-detection
6
- # guard. It detects when a context is repeating the same tool-call
7
- # pattern instead of making progress, and returns an in-band
8
- # {LLM::Function::Return LLM::Function::Return} so {LLM::Context
9
- # LLM::Context} can block the pending tool work.
10
- class Loop < self
11
- ##
12
- # The default number of repeated tool-call patterns required before
13
- # the guard intervenes.
14
- # @return [Integer]
15
- DEFAULT_THRESHOLD = 3
16
-
17
- ##
18
- # Checks the current context for repeated tool-call patterns.
19
- #
20
- # This method inspects assistant tool calls only. It reduces each call
21
- # to a `[tool_name, arguments]` signature and checks whether the tail
22
- # of the sequence is repeating.
23
- #
24
- # @param [LLM::Function] function
25
- # The pending function being decided about. Only its id and name are
26
- # used to build the return.
27
- # @param [Integer] threshold
28
- # How many repeated tool-call patterns must appear at the tail of the
29
- # sequence before the guard returns a warning.
30
- # @param [Hash] opts
31
- # Additional per-call options (ignored).
32
- # @return [LLM::Function::Return, nil]
33
- # Returns a tool return that blocks this function's pending call, or
34
- # nil when execution should continue.
35
- def call(function:, threshold: DEFAULT_THRESHOLD, **opts)
36
- repetitions = detect(messages.to_a, threshold)
37
- return unless repetitions
38
- function.return(error: true, type: "guard_error", message: warning(repetitions))
39
- end
40
-
41
- private
42
-
43
- def detect(messages, threshold)
44
- signatures = extract_signatures(messages)
45
- return if signatures.size < threshold
46
- check_repeating_pattern(signatures, threshold)
47
- end
48
-
49
- def warning(repetitions)
50
- <<~MSG
51
- SYSTEM NOTICE: Repeated tool-call pattern detected - the same pattern has repeated #{repetitions} times.
52
- You are stuck in a loop and not making progress. Stop and try a fundamentally different approach:
53
- - Re-read the relevant context before retrying
54
- - Try a different tool or strategy
55
- - Break the problem into smaller steps
56
- - If a tool keeps failing, investigate why before retrying
57
- MSG
58
- end
59
-
60
- def extract_signatures(messages)
61
- messages
62
- .select(&:assistant?)
63
- .flat_map { |message| message.functions.map { [_1.name.to_s, _1.arguments.to_h] } }
64
- end
65
-
66
- def check_repeating_pattern(sequence, threshold)
67
- max_pattern_len = sequence.size / threshold
68
- (1..max_pattern_len).each do |pattern_len|
69
- count = count_tail_repetitions(sequence, pattern_len)
70
- return count if count >= threshold
71
- end
72
- nil
73
- end
74
-
75
- def count_tail_repetitions(sequence, length)
76
- return 0 if sequence.size < length
77
- pattern = sequence.last(length)
78
- count = 1
79
- pos = sequence.size - length
80
- while pos >= length
81
- candidate = sequence[(pos - length)...pos]
82
- break unless candidate == pattern
83
- count += 1
84
- pos -= length
85
- end
86
- count
87
- end
88
- end
89
- end