llm.rb 15.3.0 → 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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -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 +28 -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 +6 -1
  37. data/lib/llm/context.rb +8 -4
  38. data/lib/llm/guard.rb +2 -8
  39. data/lib/llm/provider.rb +16 -0
  40. data/lib/llm/providers/alibaba.rb +15 -0
  41. data/lib/llm/providers/anthropic/error_handler.rb +5 -2
  42. data/lib/llm/providers/anthropic/files.rb +12 -12
  43. data/lib/llm/providers/anthropic/models.rb +2 -2
  44. data/lib/llm/providers/anthropic.rb +2 -2
  45. data/lib/llm/providers/bedrock/error_handler.rb +3 -2
  46. data/lib/llm/providers/bedrock/models.rb +5 -3
  47. data/lib/llm/providers/bedrock.rb +2 -2
  48. data/lib/llm/providers/deepinfra/audio.rb +4 -4
  49. data/lib/llm/providers/deepinfra/images.rb +4 -4
  50. data/lib/llm/providers/google/error_handler.rb +5 -2
  51. data/lib/llm/providers/google/files.rb +10 -10
  52. data/lib/llm/providers/google/images.rb +2 -2
  53. data/lib/llm/providers/google/models.rb +2 -2
  54. data/lib/llm/providers/google.rb +4 -4
  55. data/lib/llm/providers/ollama/error_handler.rb +5 -2
  56. data/lib/llm/providers/ollama/models.rb +2 -2
  57. data/lib/llm/providers/ollama.rb +4 -4
  58. data/lib/llm/providers/openai/audio.rb +6 -6
  59. data/lib/llm/providers/openai/error_handler.rb +5 -2
  60. data/lib/llm/providers/openai/files.rb +10 -10
  61. data/lib/llm/providers/openai/images.rb +4 -4
  62. data/lib/llm/providers/openai/models.rb +2 -2
  63. data/lib/llm/providers/openai/moderations.rb +2 -2
  64. data/lib/llm/providers/openai/responses.rb +6 -6
  65. data/lib/llm/providers/openai/vector_stores.rb +22 -22
  66. data/lib/llm/providers/openai.rb +4 -4
  67. data/lib/llm/providers/xai/images.rb +4 -4
  68. data/lib/llm/tracer/telemetry.rb +4 -4
  69. data/lib/llm/tracer.rb +11 -3
  70. data/lib/llm/transport/execution.rb +8 -4
  71. data/lib/llm/version.rb +1 -1
  72. data/llm.gemspec +2 -2
  73. metadata +5 -5
  74. data/lib/llm/guard/loop.rb +0 -89
@@ -23,9 +23,9 @@ class LLM::DeepInfra
23
23
  path = path("/v1/inference/#{model}", base_path: false)
24
24
  req = LLM::Transport::Request.post(path, headers)
25
25
  req.body = JSON.dump(params.merge(text: input))
26
- res, span, tracer = execute(request: req, operation: "request")
26
+ res, span, tracer, request_id = execute(request: req, operation: "request")
27
27
  res = ResponseAdapter.adapt LLM::Response.new(res), type: :audio
28
- tracer.on_request_finish(operation: "request", model:, res:, span:)
28
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
29
29
  res
30
30
  end
31
31
 
@@ -45,9 +45,9 @@ class LLM::DeepInfra
45
45
  req = LLM::Transport::Request.post(path, headers)
46
46
  req["content-type"] = multi.content_type
47
47
  transport.set_body_stream(req, multi.body)
48
- res, span, tracer = execute(request: req, operation: "request")
48
+ res, span, tracer, request_id = execute(request: req, operation: "request")
49
49
  res = LLM::Response.new(res)
50
- tracer.on_request_finish(operation: "request", model:, res:, span:)
50
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
51
51
  res
52
52
  end
53
53
 
@@ -44,9 +44,9 @@ class LLM::DeepInfra
44
44
  req = LLM::Transport::Request.post(path("/images/generations"), headers)
45
45
  params = {prompt:, model:, size:, n:, response_format:, quality:, style:}.compact
46
46
  req.body = LLM.json.dump(params)
47
- res, span, tracer = execute(request: req, operation: "request")
47
+ res, span, tracer, request_id = execute(request: req, operation: "request")
48
48
  res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
49
- tracer.on_request_finish(operation: "request", model:, res:, span:)
49
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
50
50
  res
51
51
  end
52
52
 
@@ -75,9 +75,9 @@ class LLM::DeepInfra
75
75
  req = LLM::Transport::Request.post(path("/images/edits"), headers)
76
76
  req["content-type"] = multi.content_type
77
77
  transport.set_body_stream(req, multi.body)
78
- res, span, tracer = execute(request: req, operation: "request")
78
+ res, span, tracer, request_id = execute(request: req, operation: "request")
79
79
  res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
80
- tracer.on_request_finish(operation: "request", model:, res:, span:)
80
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
81
81
  res
82
82
  end
83
83
 
@@ -21,10 +21,13 @@ class LLM::Google
21
21
  # The span
22
22
  # @param [LLM::Transport::Response, Net::HTTPResponse] res
23
23
  # The response from the server
24
+ # @param [String] request_id
25
+ # The id of the request that failed
24
26
  # @return [LLM::Google::ErrorHandler]
25
- def initialize(tracer, span, res)
27
+ def initialize(tracer, span, res, request_id)
26
28
  @tracer = tracer
27
29
  @span = span
30
+ @request_id = request_id
28
31
  @res = LLM::Transport::Response.from(res)
29
32
  end
30
33
 
@@ -33,7 +36,7 @@ class LLM::Google
33
36
  # Raises a subclass of {LLM::Error LLM::Error}
34
37
  def raise_error!
35
38
  ex = error
36
- @tracer.on_request_error(ex:, span:)
39
+ @tracer.on_request_error(ex:, span:, request_id: @request_id)
37
40
  ensure
38
41
  raise(ex) if ex
39
42
  end
@@ -46,9 +46,9 @@ class LLM::Google
46
46
  def all(**params)
47
47
  query = URI.encode_www_form(params.merge!(key: key))
48
48
  req = LLM::Transport::Request.get("/v1beta/files?#{query}", headers)
49
- res, span, tracer = execute(request: req, operation: "request")
49
+ res, span, tracer, request_id = execute(request: req, operation: "request")
50
50
  res = ResponseAdapter.adapt(res, type: :files)
51
- tracer.on_request_finish(operation: "request", res:, span:)
51
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
52
52
  res
53
53
  end
54
54
 
@@ -70,9 +70,9 @@ class LLM::Google
70
70
  req["X-Goog-Upload-Command"] = "upload, finalize"
71
71
  file.with_io do |io|
72
72
  transport.set_body_stream(req, io)
73
- res, span, tracer = execute(request: req, operation: "request")
73
+ res, span, tracer, request_id = execute(request: req, operation: "request")
74
74
  res = ResponseAdapter.adapt(res, type: :file)
75
- tracer.on_request_finish(operation: "request", res:, span:)
75
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
76
76
  res
77
77
  end
78
78
  end
@@ -92,9 +92,9 @@ class LLM::Google
92
92
  file_id = file.respond_to?(:name) ? file.name : file.to_s
93
93
  query = URI.encode_www_form(params.merge!(key: key))
94
94
  req = LLM::Transport::Request.get("/v1beta/#{file_id}?#{query}", headers)
95
- res, span, tracer = execute(request: req, operation: "request")
95
+ res, span, tracer, request_id = execute(request: req, operation: "request")
96
96
  res = ResponseAdapter.adapt(res, type: :file)
97
- tracer.on_request_finish(operation: "request", res:, span:)
97
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
98
98
  res
99
99
  end
100
100
 
@@ -112,9 +112,9 @@ class LLM::Google
112
112
  file_id = file.respond_to?(:name) ? file.name : file.to_s
113
113
  query = URI.encode_www_form(params.merge!(key: key))
114
114
  req = LLM::Transport::Request.delete("/v1beta/#{file_id}?#{query}", headers)
115
- res, span, tracer = execute(request: req, operation: "request")
115
+ res, span, tracer, request_id = execute(request: req, operation: "request")
116
116
  res = LLM::Response.new(res)
117
- tracer.on_request_finish(operation: "request", res:, span:)
117
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
118
118
  res
119
119
  end
120
120
 
@@ -134,9 +134,9 @@ class LLM::Google
134
134
  req["X-Goog-Upload-Header-Content-Length"] = file.bytesize
135
135
  req["X-Goog-Upload-Header-Content-Type"] = file.mime_type
136
136
  req.body = LLM.json.dump({file: {display_name: File.basename(file.path)}})
137
- res, span, tracer = execute(request: req, operation: "request")
137
+ res, span, tracer, request_id = execute(request: req, operation: "request")
138
138
  trace_res = LLM::Response.new(res)
139
- tracer.on_request_finish(operation: "request", res: trace_res, span:)
139
+ tracer.on_request_finish(operation: "request", res: trace_res, span:, request_id:)
140
140
  res["x-goog-upload-url"]
141
141
  end
142
142
 
@@ -51,9 +51,9 @@ class LLM::Google
51
51
  instances: [{prompt:}]
52
52
  })
53
53
  req.body = body
54
- res, span, tracer = execute(request: req, operation: "request")
54
+ res, span, tracer, request_id = execute(request: req, operation: "request")
55
55
  res = ResponseAdapter.adapt(res, type: :image)
56
- tracer.on_request_finish(operation: "request", model:, res:, span:)
56
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
57
57
  res
58
58
  end
59
59
 
@@ -40,9 +40,9 @@ class LLM::Google
40
40
  def all(**params)
41
41
  query = URI.encode_www_form(params.merge!(key: key))
42
42
  req = LLM::Transport::Request.get("/v1beta/models?#{query}", headers)
43
- res, span, tracer = execute(request: req, operation: "request")
43
+ res, span, tracer, request_id = execute(request: req, operation: "request")
44
44
  res = ResponseAdapter.adapt(res, type: :models)
45
- tracer.on_request_finish(operation: "request", res:, span:)
45
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
46
46
  res
47
47
  end
48
48
 
@@ -58,9 +58,9 @@ module LLM
58
58
  path = ["/v1beta/models/#{model}", "embedContent?key=#{@key}"].join(":")
59
59
  req = LLM::Transport::Request.post(path, headers)
60
60
  req.body = LLM.json.dump({content: {parts: [{text: input}]}})
61
- res, span, tracer = execute(request: req, operation: "embeddings", model:)
61
+ res, span, tracer, request_id = execute(request: req, operation: "embeddings", model:)
62
62
  res = ResponseAdapter.adapt(res, type: :embedding)
63
- tracer.on_request_finish(operation: "embeddings", model:, res:, span:)
63
+ tracer.on_request_finish(operation: "embeddings", model:, res:, span:, request_id:)
64
64
  res
65
65
  end
66
66
 
@@ -77,10 +77,10 @@ module LLM
77
77
  def complete(prompt, params = {})
78
78
  params, stream, tools, role, model = normalize_complete_params(params)
79
79
  req = build_complete_request(prompt, params, role, model, stream)
80
- res, span, tracer = execute(request: req, stream: stream, operation: "chat", model:)
80
+ res, span, tracer, request_id = execute(request: req, stream: stream, operation: "chat", model:)
81
81
  res = ResponseAdapter.adapt(res, type: :completion)
82
82
  .extend(Module.new { define_method(:__tools__) { tools } })
83
- tracer.on_request_finish(operation: "chat", model:, res:, span:)
83
+ tracer.on_request_finish(operation: "chat", model:, res:, span:, request_id:)
84
84
  res
85
85
  end
86
86
 
@@ -21,10 +21,13 @@ class LLM::Ollama
21
21
  # The span
22
22
  # @param [LLM::Transport::Response, Net::HTTPResponse] res
23
23
  # The response from the server
24
+ # @param [String] request_id
25
+ # The id of the request that failed
24
26
  # @return [LLM::Ollama::ErrorHandler]
25
- def initialize(tracer, span, res)
27
+ def initialize(tracer, span, res, request_id)
26
28
  @tracer = tracer
27
29
  @span = span
30
+ @request_id = request_id
28
31
  @res = LLM::Transport::Response.from(res)
29
32
  end
30
33
 
@@ -33,7 +36,7 @@ class LLM::Ollama
33
36
  # Raises a subclass of {LLM::Error LLM::Error}
34
37
  def raise_error!
35
38
  ex = error
36
- @tracer.on_request_error(ex:, span:)
39
+ @tracer.on_request_error(ex:, span:, request_id: @request_id)
37
40
  ensure
38
41
  raise(ex) if ex
39
42
  end
@@ -41,9 +41,9 @@ class LLM::Ollama
41
41
  def all(**params)
42
42
  query = URI.encode_www_form(params)
43
43
  req = LLM::Transport::Request.get("/api/tags?#{query}", headers)
44
- res, span, tracer = execute(request: req, operation: "request")
44
+ res, span, tracer, request_id = execute(request: req, operation: "request")
45
45
  res = ResponseAdapter.adapt(res, type: :models)
46
- tracer.on_request_finish(operation: "request", res:, span:)
46
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
47
47
  res
48
48
  end
49
49
 
@@ -50,9 +50,9 @@ module LLM
50
50
  params = {model:}.merge!(params)
51
51
  req = LLM::Transport::Request.post("/v1/embeddings", headers)
52
52
  req.body = LLM.json.dump({input:}.merge!(params))
53
- res, span, tracer = execute(request: req, operation: "embeddings", model:)
53
+ res, span, tracer, request_id = execute(request: req, operation: "embeddings", model:)
54
54
  res = ResponseAdapter.adapt(res, type: :embedding)
55
- tracer.on_request_finish(operation: "embeddings", model:, res:, span:)
55
+ tracer.on_request_finish(operation: "embeddings", model:, res:, span:, request_id:)
56
56
  res
57
57
  end
58
58
 
@@ -69,10 +69,10 @@ module LLM
69
69
  def complete(prompt, params = {})
70
70
  params, stream, tools, role = normalize_complete_params(params)
71
71
  req = build_complete_request(prompt, params, role)
72
- res, span, tracer = execute(request: req, stream: stream, operation: "chat", model: params[:model])
72
+ res, span, tracer, request_id = execute(request: req, stream: stream, operation: "chat", model: params[:model])
73
73
  res = ResponseAdapter.adapt(res, type: :completion)
74
74
  .extend(Module.new { define_method(:__tools__) { tools } })
75
- tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:)
75
+ tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:, request_id:)
76
76
  res
77
77
  end
78
78
 
@@ -35,13 +35,13 @@ class LLM::OpenAI
35
35
  req = LLM::Transport::Request.post(path("/audio/speech"), headers)
36
36
  req.body = LLM.json.dump({input:, voice:, model:, response_format:}.merge!(params))
37
37
  io = StringIO.new("".b)
38
- res, span, tracer = execute(request: req, operation: "request") { _1.read_body { |chunk| io << chunk } }
38
+ res, span, tracer, request_id = execute(request: req, operation: "request") { _1.read_body { |chunk| io << chunk } }
39
39
  content_type = res["content-type"].to_s.split(";").first
40
40
  content_type = content_type.empty? ? LLM::Mime[".#{response_format}"] : content_type
41
41
  data = "data:#{content_type};base64,#{[io.string].pack("m0")}"
42
42
  res.body = LLM::Object.from(audio: data)
43
43
  res = ResponseAdapter.adapt(LLM::Response.new(res), type: :audio)
44
- tracer.on_request_finish(operation: "request", model:, res:, span:)
44
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
45
45
  res
46
46
  end
47
47
 
@@ -62,9 +62,9 @@ class LLM::OpenAI
62
62
  req = LLM::Transport::Request.post(path("/audio/transcriptions"), headers)
63
63
  req["content-type"] = multi.content_type
64
64
  transport.set_body_stream(req, multi.body)
65
- res, span, tracer = execute(request: req, operation: "request")
65
+ res, span, tracer, request_id = execute(request: req, operation: "request")
66
66
  res = LLM::Response.new(res)
67
- tracer.on_request_finish(operation: "request", model:, res:, span:)
67
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
68
68
  res
69
69
  end
70
70
 
@@ -86,9 +86,9 @@ class LLM::OpenAI
86
86
  req = LLM::Transport::Request.post(path("/audio/translations"), headers)
87
87
  req["content-type"] = multi.content_type
88
88
  transport.set_body_stream(req, multi.body)
89
- res, span, tracer = execute(request: req, operation: "request")
89
+ res, span, tracer, request_id = execute(request: req, operation: "request")
90
90
  res = LLM::Response.new(res)
91
- tracer.on_request_finish(operation: "request", model:, res:, span:)
91
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
92
92
  res
93
93
  end
94
94
 
@@ -21,10 +21,13 @@ class LLM::OpenAI
21
21
  # The span
22
22
  # @param [LLM::Transport::Response, Net::HTTPResponse] res
23
23
  # The response from the server
24
+ # @param [String] request_id
25
+ # The id of the request that failed
24
26
  # @return [LLM::OpenAI::ErrorHandler]
25
- def initialize(tracer, span, res)
27
+ def initialize(tracer, span, res, request_id)
26
28
  @tracer = tracer
27
29
  @span = span
30
+ @request_id = request_id
28
31
  @res = LLM::Transport::Response.from(res)
29
32
  end
30
33
 
@@ -33,7 +36,7 @@ class LLM::OpenAI
33
36
  # Raises a subclass of {LLM::Error LLM::Error}
34
37
  def raise_error!
35
38
  ex = error
36
- @tracer.on_request_error(ex:, span:)
39
+ @tracer.on_request_error(ex:, span:, request_id: @request_id)
37
40
  ensure
38
41
  raise(ex) if ex
39
42
  end
@@ -41,9 +41,9 @@ class LLM::OpenAI
41
41
  def all(**params)
42
42
  query = URI.encode_www_form(params)
43
43
  req = LLM::Transport::Request.get(path("/files?#{query}"), headers)
44
- res, span, tracer = execute(request: req, operation: "request")
44
+ res, span, tracer, request_id = execute(request: req, operation: "request")
45
45
  res = ResponseAdapter.adapt(res, type: :enumerable)
46
- tracer.on_request_finish(operation: "request", res:, span:)
46
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
47
47
  res
48
48
  end
49
49
 
@@ -63,9 +63,9 @@ class LLM::OpenAI
63
63
  req = LLM::Transport::Request.post(path("/files"), headers)
64
64
  req["content-type"] = multi.content_type
65
65
  transport.set_body_stream(req, multi.body)
66
- res, span, tracer = execute(request: req, operation: "request")
66
+ res, span, tracer, request_id = execute(request: req, operation: "request")
67
67
  res = ResponseAdapter.adapt(res, type: :file)
68
- tracer.on_request_finish(operation: "request", res:, span:)
68
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
69
69
  res
70
70
  end
71
71
 
@@ -84,9 +84,9 @@ class LLM::OpenAI
84
84
  file_id = file.respond_to?(:id) ? file.id : file
85
85
  query = URI.encode_www_form(params)
86
86
  req = LLM::Transport::Request.get(path("/files/#{file_id}?#{query}"), headers)
87
- res, span, tracer = execute(request: req, operation: "request")
87
+ res, span, tracer, request_id = execute(request: req, operation: "request")
88
88
  res = ResponseAdapter.adapt(res, type: :file)
89
- tracer.on_request_finish(operation: "request", res:, span:)
89
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
90
90
  res
91
91
  end
92
92
 
@@ -107,9 +107,9 @@ class LLM::OpenAI
107
107
  file_id = file.respond_to?(:id) ? file.id : file
108
108
  req = LLM::Transport::Request.get(path("/files/#{file_id}/content?#{query}"), headers)
109
109
  io = StringIO.new("".b)
110
- res, span, tracer = execute(request: req, operation: "request") { |res| res.read_body { |chunk| io << chunk } }
110
+ res, span, tracer, request_id = execute(request: req, operation: "request") { |res| res.read_body { |chunk| io << chunk } }
111
111
  res = LLM::Response.new(res).tap { _1.define_singleton_method(:file) { io } }
112
- tracer.on_request_finish(operation: "request", res:, span:)
112
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
113
113
  res
114
114
  end
115
115
 
@@ -126,9 +126,9 @@ class LLM::OpenAI
126
126
  def delete(file:)
127
127
  file_id = file.respond_to?(:id) ? file.id : file
128
128
  req = LLM::Transport::Request.delete(path("/files/#{file_id}"), headers)
129
- res, span, tracer = execute(request: req, operation: "request")
129
+ res, span, tracer, request_id = execute(request: req, operation: "request")
130
130
  res = LLM::Response.new(res)
131
- tracer.on_request_finish(operation: "request", res:, span:)
131
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
132
132
  res
133
133
  end
134
134
 
@@ -38,9 +38,9 @@ class LLM::OpenAI
38
38
  def create(prompt:, model: "gpt-image-1-mini", output_format: "png", **params)
39
39
  req = LLM::Transport::Request.post(path("/images/generations"), headers)
40
40
  req.body = LLM.json.dump({prompt:, n: 1, model:, output_format:}.merge!(params))
41
- res, span, tracer = execute(request: req, operation: "request")
41
+ res, span, tracer, request_id = execute(request: req, operation: "request")
42
42
  res = ResponseAdapter.adapt(res, type: :image)
43
- tracer.on_request_finish(operation: "request", model:, res:, span:)
43
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
44
44
  res
45
45
  end
46
46
 
@@ -64,9 +64,9 @@ class LLM::OpenAI
64
64
  req = LLM::Transport::Request.post(path("/images/edits"), headers)
65
65
  req["content-type"] = multi.content_type
66
66
  transport.set_body_stream(req, multi.body)
67
- res, span, tracer = execute(request: req, operation: "request")
67
+ res, span, tracer, request_id = execute(request: req, operation: "request")
68
68
  res = ResponseAdapter.adapt(res, type: :image)
69
- tracer.on_request_finish(operation: "request", model:, res:, span:)
69
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
70
70
  res
71
71
  end
72
72
 
@@ -40,9 +40,9 @@ class LLM::OpenAI
40
40
  def all(**params)
41
41
  query = URI.encode_www_form(params)
42
42
  req = LLM::Transport::Request.get(path("/models?#{query}"), headers)
43
- res, span, tracer = execute(request: req, operation: "request")
43
+ res, span, tracer, request_id = execute(request: req, operation: "request")
44
44
  res = ResponseAdapter.adapt(res, type: :models)
45
- tracer.on_request_finish(operation: "request", res:, span:)
45
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
46
46
  res
47
47
  end
48
48
 
@@ -50,9 +50,9 @@ class LLM::OpenAI
50
50
  req = LLM::Transport::Request.post(path("/moderations"), headers)
51
51
  input = RequestAdapter::Moderation.new(input).adapt
52
52
  req.body = LLM.json.dump({input:, model:}.merge!(params))
53
- res, span, tracer = execute(request: req, operation: "request")
53
+ res, span, tracer, request_id = execute(request: req, operation: "request")
54
54
  res = ResponseAdapter.adapt(res, type: :moderations)
55
- tracer.on_request_finish(operation: "request", model:, res:, span:)
55
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
56
56
  res
57
57
  end
58
58
 
@@ -54,10 +54,10 @@ class LLM::OpenAI
54
54
  params.delete(:input)
55
55
  body = LLM.json.dump({input: [adapt(messages, mode: :response)].flatten}.merge!(params))
56
56
  transport.set_body_stream(req, StringIO.new(body))
57
- res, span, tracer = execute(request: req, stream:, stream_parser:, operation: "chat", model: params[:model])
57
+ res, span, tracer, request_id = execute(request: req, stream:, stream_parser:, operation: "chat", model: params[:model])
58
58
  res = ResponseAdapter.adapt(res, type: :responds)
59
59
  .extend(Module.new { define_method(:__tools__) { tools } })
60
- tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:)
60
+ tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:, request_id:)
61
61
  res
62
62
  end
63
63
 
@@ -71,9 +71,9 @@ class LLM::OpenAI
71
71
  response_id = response.respond_to?(:id) ? response.id : response
72
72
  query = URI.encode_www_form(params)
73
73
  req = LLM::Transport::Request.get(path("/responses/#{response_id}?#{query}"), headers)
74
- res, span, tracer = execute(request: req, operation: "request")
74
+ res, span, tracer, request_id = execute(request: req, operation: "request")
75
75
  res = ResponseAdapter.adapt(res, type: :responds)
76
- tracer.on_request_finish(operation: "request", res:, span:)
76
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
77
77
  res
78
78
  end
79
79
 
@@ -86,9 +86,9 @@ class LLM::OpenAI
86
86
  def delete(response)
87
87
  response_id = response.respond_to?(:id) ? response.id : response
88
88
  req = LLM::Transport::Request.delete(path("/responses/#{response_id}"), headers)
89
- res, span, tracer = execute(request: req, operation: "request")
89
+ res, span, tracer, request_id = execute(request: req, operation: "request")
90
90
  res = LLM::Response.new(res)
91
- tracer.on_request_finish(operation: "request", res:, span:)
91
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
92
92
  res
93
93
  end
94
94
 
@@ -32,9 +32,9 @@ class LLM::OpenAI
32
32
  def all(**params)
33
33
  query = URI.encode_www_form(params)
34
34
  req = LLM::Transport::Request.get(path("/vector_stores?#{query}"), headers)
35
- res, span, tracer = execute(request: req, operation: "request")
35
+ res, span, tracer, request_id = execute(request: req, operation: "request")
36
36
  res = ResponseAdapter.adapt(res, type: :enumerable)
37
- tracer.on_request_finish(operation: "request", res:, span:)
37
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
38
38
  res
39
39
  end
40
40
 
@@ -49,9 +49,9 @@ class LLM::OpenAI
49
49
  def create(name:, file_ids: nil, **params)
50
50
  req = LLM::Transport::Request.post(path("/vector_stores"), headers)
51
51
  req.body = LLM.json.dump(params.merge({name:, file_ids:}).compact)
52
- res, span, tracer = execute(request: req, operation: "request")
52
+ res, span, tracer, request_id = execute(request: req, operation: "request")
53
53
  res = LLM::Response.new(res)
54
- tracer.on_request_finish(operation: "request", res:, span:)
54
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
55
55
  res
56
56
  end
57
57
 
@@ -73,9 +73,9 @@ class LLM::OpenAI
73
73
  def get(vector:)
74
74
  vector_id = vector.respond_to?(:id) ? vector.id : vector
75
75
  req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}"), headers)
76
- res, span, tracer = execute(request: req, operation: "request")
76
+ res, span, tracer, request_id = execute(request: req, operation: "request")
77
77
  res = LLM::Response.new(res)
78
- tracer.on_request_finish(operation: "request", res:, span:)
78
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
79
79
  res
80
80
  end
81
81
 
@@ -91,9 +91,9 @@ class LLM::OpenAI
91
91
  vector_id = vector.respond_to?(:id) ? vector.id : vector
92
92
  req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}"), headers)
93
93
  req.body = LLM.json.dump(params.merge({name:}).compact)
94
- res, span, tracer = execute(request: req, operation: "request")
94
+ res, span, tracer, request_id = execute(request: req, operation: "request")
95
95
  res = LLM::Response.new(res)
96
- tracer.on_request_finish(operation: "request", res:, span:)
96
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
97
97
  res
98
98
  end
99
99
 
@@ -106,9 +106,9 @@ class LLM::OpenAI
106
106
  def delete(vector:)
107
107
  vector_id = vector.respond_to?(:id) ? vector.id : vector
108
108
  req = LLM::Transport::Request.delete(path("/vector_stores/#{vector_id}"), headers)
109
- res, span, tracer = execute(request: req, operation: "request")
109
+ res, span, tracer, request_id = execute(request: req, operation: "request")
110
110
  res = LLM::Response.new(res)
111
- tracer.on_request_finish(operation: "request", res:, span:)
111
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
112
112
  res
113
113
  end
114
114
 
@@ -124,9 +124,9 @@ class LLM::OpenAI
124
124
  vector_id = vector.respond_to?(:id) ? vector.id : vector
125
125
  req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/search"), headers)
126
126
  req.body = LLM.json.dump(params.merge({query:}).compact)
127
- res, span, tracer = execute(request: req, operation: "retrieval")
127
+ res, span, tracer, request_id = execute(request: req, operation: "retrieval")
128
128
  res = ResponseAdapter.adapt(res, type: :enumerable)
129
- tracer.on_request_finish(operation: "retrieval", res:, span:)
129
+ tracer.on_request_finish(operation: "retrieval", res:, span:, request_id:)
130
130
  res
131
131
  end
132
132
 
@@ -141,9 +141,9 @@ class LLM::OpenAI
141
141
  vector_id = vector.respond_to?(:id) ? vector.id : vector
142
142
  query = URI.encode_www_form(params)
143
143
  req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}/files?#{query}"), headers)
144
- res, span, tracer = execute(request: req, operation: "request")
144
+ res, span, tracer, request_id = execute(request: req, operation: "request")
145
145
  res = ResponseAdapter.adapt(res, type: :enumerable)
146
- tracer.on_request_finish(operation: "request", res:, span:)
146
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
147
147
  res
148
148
  end
149
149
 
@@ -161,9 +161,9 @@ class LLM::OpenAI
161
161
  file_id = file.respond_to?(:id) ? file.id : file
162
162
  req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/files"), headers)
163
163
  req.body = LLM.json.dump(params.merge({file_id:, attributes:}).compact)
164
- res, span, tracer = execute(request: req, operation: "request")
164
+ res, span, tracer, request_id = execute(request: req, operation: "request")
165
165
  res = LLM::Response.new(res)
166
- tracer.on_request_finish(operation: "request", res:, span:)
166
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
167
167
  res
168
168
  end
169
169
  alias_method :create_file, :add_file
@@ -192,9 +192,9 @@ class LLM::OpenAI
192
192
  file_id = file.respond_to?(:id) ? file.id : file
193
193
  req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/files/#{file_id}"), headers)
194
194
  req.body = LLM.json.dump(params.merge({attributes:}).compact)
195
- res, span, tracer = execute(request: req, operation: "request")
195
+ res, span, tracer, request_id = execute(request: req, operation: "request")
196
196
  res = LLM::Response.new(res)
197
- tracer.on_request_finish(operation: "request", res:, span:)
197
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
198
198
  res
199
199
  end
200
200
 
@@ -210,9 +210,9 @@ class LLM::OpenAI
210
210
  file_id = file.respond_to?(:id) ? file.id : file
211
211
  query = URI.encode_www_form(params)
212
212
  req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}/files/#{file_id}?#{query}"), headers)
213
- res, span, tracer = execute(request: req, operation: "request")
213
+ res, span, tracer, request_id = execute(request: req, operation: "request")
214
214
  res = LLM::Response.new(res)
215
- tracer.on_request_finish(operation: "request", res:, span:)
215
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
216
216
  res
217
217
  end
218
218
 
@@ -227,9 +227,9 @@ class LLM::OpenAI
227
227
  vector_id = vector.respond_to?(:id) ? vector.id : vector
228
228
  file_id = file.respond_to?(:id) ? file.id : file
229
229
  req = LLM::Transport::Request.delete(path("/vector_stores/#{vector_id}/files/#{file_id}"), headers)
230
- res, span, tracer = execute(request: req, operation: "request")
230
+ res, span, tracer, request_id = execute(request: req, operation: "request")
231
231
  res = LLM::Response.new(res)
232
- tracer.on_request_finish(operation: "request", res:, span:)
232
+ tracer.on_request_finish(operation: "request", res:, span:, request_id:)
233
233
  res
234
234
  end
235
235
 
@@ -54,9 +54,9 @@ module LLM
54
54
  def embed(input, model: "text-embedding-3-small", **params)
55
55
  req = LLM::Transport::Request.post(path("/embeddings"), headers)
56
56
  req.body = LLM.json.dump({input:, model:}.merge!(params))
57
- res, span, tracer = execute(request: req, operation: "embeddings", model:)
57
+ res, span, tracer, request_id = execute(request: req, operation: "embeddings", model:)
58
58
  res = ResponseAdapter.adapt(res, type: :embedding)
59
- tracer.on_request_finish(operation: "embeddings", model:, res:, span:)
59
+ tracer.on_request_finish(operation: "embeddings", model:, res:, span:, request_id:)
60
60
  res
61
61
  end
62
62
 
@@ -74,10 +74,10 @@ module LLM
74
74
  params, stream, tools, role = normalize_complete_params(params)
75
75
  req, messages = build_complete_request(prompt, params, role)
76
76
  tracer.set_request_metadata(user_input: extract_user_input(messages, fallback: prompt))
77
- res, span, tracer = execute(request: req, stream: stream, operation: "chat", model: params[:model])
77
+ res, span, tracer, request_id = execute(request: req, stream: stream, operation: "chat", model: params[:model])
78
78
  res = ResponseAdapter.adapt(res, type: :completion)
79
79
  .extend(Module.new { define_method(:__tools__) { tools } })
80
- tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:)
80
+ tracer.on_request_finish(operation: "chat", model: params[:model], res:, span:, request_id:)
81
81
  res
82
82
  end
83
83
 
@@ -34,9 +34,9 @@ class LLM::XAI
34
34
  def create(prompt:, model: "grok-imagine-image-quality", **params)
35
35
  req = LLM::Transport::Request.post(path("/images/generations"), headers)
36
36
  req.body = LLM.json.dump({prompt:, n: 1, model:, response_format: "b64_json"}.merge!(params))
37
- res, span, tracer = execute(request: req, operation: "request")
37
+ res, span, tracer, request_id = execute(request: req, operation: "request")
38
38
  res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
39
- tracer.on_request_finish(operation: "request", model:, res:, span:)
39
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
40
40
  res
41
41
  end
42
42
 
@@ -61,9 +61,9 @@ class LLM::XAI
61
61
  image: image_url(image),
62
62
  response_format: "b64_json"
63
63
  }.merge!(params))
64
- res, span, tracer = execute(request: req, operation: "request")
64
+ res, span, tracer, request_id = execute(request: req, operation: "request")
65
65
  res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
66
- tracer.on_request_finish(operation: "request", model:, res:, span:)
66
+ tracer.on_request_finish(operation: "request", model:, res:, span:, request_id:)
67
67
  res
68
68
  end
69
69