llm.rb 11.3.1 → 12.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +354 -2
  3. data/LICENSE +93 -17
  4. data/README.md +226 -616
  5. data/data/anthropic.json +322 -426
  6. data/data/bedrock.json +2634 -1144
  7. data/data/deepinfra.json +1513 -0
  8. data/data/deepseek.json +57 -28
  9. data/data/google.json +411 -771
  10. data/data/openai.json +1104 -771
  11. data/data/xai.json +141 -292
  12. data/data/zai.json +263 -141
  13. data/lib/llm/active_record/acts_as_agent.rb +3 -41
  14. data/lib/llm/active_record/acts_as_llm.rb +18 -0
  15. data/lib/llm/active_record.rb +3 -3
  16. data/lib/llm/agent.rb +25 -1
  17. data/lib/llm/context.rb +17 -5
  18. data/lib/llm/contract/completion.rb +2 -2
  19. data/lib/llm/json_adapter.rb +29 -3
  20. data/lib/llm/provider.rb +3 -3
  21. data/lib/llm/providers/deepinfra/audio.rb +66 -0
  22. data/lib/llm/providers/deepinfra/images.rb +90 -0
  23. data/lib/llm/providers/deepinfra/response_adapter.rb +36 -0
  24. data/lib/llm/providers/deepinfra.rb +100 -0
  25. data/lib/llm/providers/deepseek/images.rb +109 -0
  26. data/lib/llm/providers/deepseek/request_adapter.rb +32 -0
  27. data/lib/llm/providers/deepseek/response_adapter/image.rb +9 -0
  28. data/lib/llm/providers/deepseek/response_adapter.rb +29 -0
  29. data/lib/llm/providers/deepseek.rb +4 -2
  30. data/lib/llm/providers/google/request_adapter.rb +22 -5
  31. data/lib/llm/providers/google.rb +4 -4
  32. data/lib/llm/providers/llamacpp.rb +5 -5
  33. data/lib/llm/providers/openai/audio.rb +6 -2
  34. data/lib/llm/providers/openai/images.rb +9 -50
  35. data/lib/llm/providers/openai/request_adapter/respond.rb +38 -4
  36. data/lib/llm/providers/openai/response_adapter/audio.rb +5 -1
  37. data/lib/llm/providers/openai/response_adapter/completion.rb +1 -1
  38. data/lib/llm/providers/openai/response_adapter/image.rb +0 -4
  39. data/lib/llm/providers/openai/responses.rb +1 -0
  40. data/lib/llm/providers/openai/stream_parser.rb +5 -6
  41. data/lib/llm/providers/openai.rb +2 -2
  42. data/lib/llm/providers/xai/images.rb +49 -26
  43. data/lib/llm/providers/xai.rb +2 -2
  44. data/lib/llm/repl/input.rb +64 -0
  45. data/lib/llm/repl/status.rb +30 -0
  46. data/lib/llm/repl/stream.rb +46 -0
  47. data/lib/llm/repl/transcript.rb +61 -0
  48. data/lib/llm/repl/window.rb +107 -0
  49. data/lib/llm/repl.rb +78 -0
  50. data/lib/llm/response.rb +10 -0
  51. data/lib/llm/schema/leaf.rb +7 -1
  52. data/lib/llm/schema/renderer.rb +121 -0
  53. data/lib/llm/schema.rb +30 -0
  54. data/lib/llm/sequel/agent.rb +2 -43
  55. data/lib/llm/sequel/plugin.rb +25 -7
  56. data/lib/llm/tools/chdir.rb +23 -0
  57. data/lib/llm/tools/git.rb +41 -0
  58. data/lib/llm/tools/mkdir.rb +32 -0
  59. data/lib/llm/tools/pwd.rb +20 -0
  60. data/lib/llm/tools/read_file.rb +40 -0
  61. data/lib/llm/tools/rg.rb +46 -0
  62. data/lib/llm/tools/shell.rb +48 -0
  63. data/lib/llm/tools/swap_text.rb +25 -0
  64. data/lib/llm/tools/write_file.rb +24 -0
  65. data/lib/llm/tools.rb +5 -0
  66. data/lib/llm/tracer/telemetry.rb +4 -6
  67. data/lib/llm/tracer.rb +9 -21
  68. data/lib/llm/transport/execution.rb +16 -1
  69. data/lib/llm/transport/net_http_adapter.rb +1 -1
  70. data/lib/llm/uridata.rb +16 -0
  71. data/lib/llm/version.rb +1 -1
  72. data/lib/llm.rb +9 -0
  73. data/llm.gemspec +5 -18
  74. data/resources/deepdive.md +829 -263
  75. metadata +31 -18
  76. data/lib/llm/tracer/langsmith.rb +0 -144
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llm.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.3.1
4
+ version: 12.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Robert (0x1eef)
7
+ - bsdrobert
8
8
  - Antar Azri
9
9
  - Rodrigo Serrano
10
10
  bindir: bin
@@ -277,20 +277,8 @@ dependencies:
277
277
  - - "~>"
278
278
  - !ruby/object:Gem::Version
279
279
  version: '1.18'
280
- description: |
281
- llm.rb is Ruby's capable AI runtime.
282
-
283
- It runs on Ruby's standard library by default. loads optional pieces
284
- only when needed, and offers a single runtime for providers, agents,
285
- tools, skills, MCP, A2A (Agent2Agent), RAG (vector stores & embeddings),
286
- streaming, files, and persisted state. As a bonus, llm.rb is also available
287
- for mruby.
288
-
289
- It supports OpenAI, OpenAI-compatible endpoints, Anthropic, Google
290
- Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp. It
291
- also includes built-in ActiveRecord and Sequel support, plus concurrent
292
- tool execution through threads, tasks (via async gem), fibers, ractors,
293
- and fork (via xchan.rb gem).
280
+ description: llm.rb is not a library, framework or toolkit but an advanced runtime
281
+ for building highly capable AI applications on CRuby.
294
282
  email:
295
283
  - robert@r.uby.dev
296
284
  executables: []
@@ -302,6 +290,7 @@ files:
302
290
  - README.md
303
291
  - data/anthropic.json
304
292
  - data/bedrock.json
293
+ - data/deepinfra.json
305
294
  - data/deepseek.json
306
295
  - data/google.json
307
296
  - data/openai.json
@@ -401,9 +390,16 @@ files:
401
390
  - lib/llm/providers/bedrock/signature.rb
402
391
  - lib/llm/providers/bedrock/stream_decoder.rb
403
392
  - lib/llm/providers/bedrock/stream_parser.rb
393
+ - lib/llm/providers/deepinfra.rb
394
+ - lib/llm/providers/deepinfra/audio.rb
395
+ - lib/llm/providers/deepinfra/images.rb
396
+ - lib/llm/providers/deepinfra/response_adapter.rb
404
397
  - lib/llm/providers/deepseek.rb
398
+ - lib/llm/providers/deepseek/images.rb
405
399
  - lib/llm/providers/deepseek/request_adapter.rb
406
400
  - lib/llm/providers/deepseek/request_adapter/completion.rb
401
+ - lib/llm/providers/deepseek/response_adapter.rb
402
+ - lib/llm/providers/deepseek/response_adapter/image.rb
407
403
  - lib/llm/providers/google.rb
408
404
  - lib/llm/providers/google/audio.rb
409
405
  - lib/llm/providers/google/error_handler.rb
@@ -463,6 +459,12 @@ files:
463
459
  - lib/llm/providers/xai/images.rb
464
460
  - lib/llm/providers/zai.rb
465
461
  - lib/llm/registry.rb
462
+ - lib/llm/repl.rb
463
+ - lib/llm/repl/input.rb
464
+ - lib/llm/repl/status.rb
465
+ - lib/llm/repl/stream.rb
466
+ - lib/llm/repl/transcript.rb
467
+ - lib/llm/repl/window.rb
466
468
  - lib/llm/response.rb
467
469
  - lib/llm/schema.rb
468
470
  - lib/llm/schema/all_of.rb
@@ -477,6 +479,7 @@ files:
477
479
  - lib/llm/schema/object.rb
478
480
  - lib/llm/schema/one_of.rb
479
481
  - lib/llm/schema/parser.rb
482
+ - lib/llm/schema/renderer.rb
480
483
  - lib/llm/schema/string.rb
481
484
  - lib/llm/schema/version.rb
482
485
  - lib/llm/sequel/agent.rb
@@ -488,8 +491,17 @@ files:
488
491
  - lib/llm/stream/queue.rb
489
492
  - lib/llm/tool.rb
490
493
  - lib/llm/tool/param.rb
494
+ - lib/llm/tools.rb
495
+ - lib/llm/tools/chdir.rb
496
+ - lib/llm/tools/git.rb
497
+ - lib/llm/tools/mkdir.rb
498
+ - lib/llm/tools/pwd.rb
499
+ - lib/llm/tools/read_file.rb
500
+ - lib/llm/tools/rg.rb
501
+ - lib/llm/tools/shell.rb
502
+ - lib/llm/tools/swap_text.rb
503
+ - lib/llm/tools/write_file.rb
491
504
  - lib/llm/tracer.rb
492
- - lib/llm/tracer/langsmith.rb
493
505
  - lib/llm/tracer/logger.rb
494
506
  - lib/llm/tracer/null.rb
495
507
  - lib/llm/tracer/telemetry.rb
@@ -505,6 +517,7 @@ files:
505
517
  - lib/llm/transport/response/http.rb
506
518
  - lib/llm/transport/stream_decoder.rb
507
519
  - lib/llm/transport/utils.rb
520
+ - lib/llm/uridata.rb
508
521
  - lib/llm/usage.rb
509
522
  - lib/llm/utils.rb
510
523
  - lib/llm/version.rb
@@ -514,7 +527,7 @@ files:
514
527
  - resources/deepdive.md
515
528
  homepage: https://r.uby.dev/llm/
516
529
  licenses:
517
- - 0BSD
530
+ - BUSL-1.1
518
531
  metadata:
519
532
  homepage_uri: https://r.uby.dev/llm/
520
533
  source_code_uri: https://github.com/r-uby-dev/llm.rb
@@ -1,144 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module LLM
4
- ##
5
- # LangSmith-specific tracer built on top of Telemetry. Supports extra
6
- # inputs/outputs and metadata on traces and spans via {#merge_extra} and
7
- # {#start_trace}(metadata:).
8
- #
9
- # @example Constructor metadata and tags
10
- # llm.tracer = LLM::Tracer::Langsmith.new(
11
- # llm,
12
- # session_id: "123e4567-e89b-12d3-a456-426614174000",
13
- # metadata: {env: "dev"},
14
- # tags: ["changelog"]
15
- # )
16
- #
17
- # @example Per-request extra metadata and inputs (e.g. from chatbot)
18
- # tracer.merge_extra(
19
- # metadata: { turn_id: turn.id, component: "chatbot_message_stream" },
20
- # inputs: { "gen_ai.input.messages" => messages_json }
21
- # )
22
- # bot.chat(prompt)
23
- #
24
- # @example Trace-level metadata via start_trace
25
- # tracer.start_trace(trace_group_id: turn.id, name: "chatbot.turn", metadata: { turn_id: turn.id })
26
- class Tracer::Langsmith < Tracer::Telemetry
27
- THREAD_EXTRA_KEY = :llm_langsmith_extra
28
-
29
- UUID = /\A
30
- [0-9a-f]{8}-
31
- [0-9a-f]{4}-
32
- [1-5][0-9a-f]{3}-
33
- [89ab][0-9a-f]{3}-
34
- [0-9a-f]{12}
35
- \z/ix
36
-
37
- def initialize(provider, options = {})
38
- super
39
- setup_langsmith!(options)
40
- end
41
-
42
- def start_trace(trace_group_id: nil, name: "llm", attributes: {}, metadata: nil)
43
- merge_extra(metadata: metadata) if metadata && !metadata.empty?
44
- super
45
- end
46
-
47
- def stop_trace
48
- clear_thread_extra!
49
- super
50
- end
51
-
52
- def merge_extra(metadata: nil, inputs: nil, outputs: nil)
53
- store = thread_extra
54
- store[:metadata].merge!(metadata) if metadata && !metadata.empty?
55
- store[:inputs].merge!(inputs) if inputs && !inputs.empty?
56
- store[:outputs].merge!(outputs) if outputs && !outputs.empty?
57
- self
58
- end
59
-
60
- def current_extra
61
- store = thread_extra
62
- {
63
- metadata: store[:metadata].dup,
64
- inputs: store[:inputs].dup,
65
- outputs: store[:outputs].dup
66
- }
67
- end
68
-
69
- def consume_extra_inputs
70
- thread_extra[:inputs].tap { thread_extra[:inputs] = {} }
71
- end
72
-
73
- def consume_extra_outputs
74
- thread_extra[:outputs].tap { thread_extra[:outputs] = {} }
75
- end
76
-
77
- private
78
-
79
- def trace_attributes(span_kind:)
80
- attributes = {}
81
- unless @langsmith_session_id.to_s.empty?
82
- attributes["langsmith.trace.session_id"] = @langsmith_session_id
83
- end
84
- merged_metadata = @langsmith_metadata.merge(thread_extra[:metadata])
85
- merged_metadata.each do |key, value|
86
- next if value.nil?
87
-
88
- attr_key = key.to_s.start_with?("langsmith.metadata.") ? key.to_s : "langsmith.metadata.#{key}"
89
- attributes[attr_key] = serialize_langsmith_value(value)
90
- end
91
- unless @langsmith_tags.empty?
92
- attributes["langsmith.span.tags"] = @langsmith_tags.map(&:to_s).join(",")
93
- end
94
- attributes["langsmith.span.kind"] = span_kind
95
- attributes
96
- end
97
-
98
- def thread_extra
99
- Thread.current[THREAD_EXTRA_KEY] ||= {
100
- metadata: {},
101
- inputs: {},
102
- outputs: {}
103
- }
104
- end
105
-
106
- def clear_thread_extra!
107
- Thread.current[THREAD_EXTRA_KEY] = nil
108
- end
109
-
110
- def setup_langsmith!(options)
111
- options ||= {}
112
- @langsmith_metadata = options[:metadata] || {}
113
- @langsmith_session_id = normalize_langsmith_session_id(
114
- options[:session_id],
115
- metadata: @langsmith_metadata
116
- )
117
- @langsmith_tags = options[:tags] || []
118
- end
119
-
120
- def serialize_langsmith_value(value)
121
- case value
122
- when String, Numeric, TrueClass, FalseClass
123
- value
124
- else
125
- LLM.json.dump(value)
126
- end
127
- end
128
-
129
- def normalize_langsmith_session_id(session_id, metadata:)
130
- raw = session_id&.to_s
131
- return nil if raw.to_s.empty?
132
- return raw if uuid?(raw)
133
-
134
- # Keep arbitrary identifiers in metadata instead of forcing
135
- # them into langsmith.trace.session_id, which expects a UUID.
136
- metadata[:session_id] ||= raw
137
- nil
138
- end
139
-
140
- def uuid?(value)
141
- value.match?(UUID)
142
- end
143
- end
144
- end