strongmind-platform-sdk 3.29.0 → 3.30.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +5 -11
- data/lib/platform_sdk/observability/langfuse/bedrock_claude_adapter.rb +244 -0
- data/lib/platform_sdk/observability/langfuse/openai_adapter.rb +87 -0
- data/lib/platform_sdk/observability/langfuse.rb +2 -0
- data/lib/platform_sdk/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47af8235ee05e3c5a4e31ac2f6ad5a4936dcfd004ce0a0eae2737cf19ca59440
|
|
4
|
+
data.tar.gz: e6814730ac3a3187e89554d7b7dd3d99b629f3833f41a72de4f332f59a769a72
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e47283eb744412ddd608074dd12c38433db14273eb55ba3df3c5f60737b02e687b2ecce1ad70463d01709be5c6914178512c9d416f15c403444e6bf241d0e4e6
|
|
7
|
+
data.tar.gz: 24aeba0a4181cdf7cfda05ad764c54d0348016232029e0db2fe7b4c1a0362c43c75618c14eeb3bce79b47cda9977f8f296808d499681bf327f8e2ebb2c3909a8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [3.30.0] - 2026-05-14
|
|
4
|
+
|
|
5
|
+
- Add `PlatformSdk::Observability::Langfuse::OpenAIAdapter` — wraps direct `ruby-openai` chat calls and fires `llm_call.platform_sdk` notifications with model, input messages, output content, and token usage extracted from OpenAI's response shape. Apps that hit OpenAI's API outside of RubyLLM now get the same Langfuse generation observations.
|
|
6
|
+
- Add `PlatformSdk::Observability::Langfuse::BedrockClaudeAdapter` — wraps direct AWS Bedrock `invoke_model` calls for Anthropic Claude (Messages API on Bedrock). Supports both non-streaming (`with_observability`) and event-streaming (`with_streaming_observability { |collector| ... }`) modes; the streaming collector accumulates `content_block_delta` text and pulls final token counts from `message_stop`'s `amazon-bedrock-invocationMetrics`.
|
|
7
|
+
- Both adapters expose `with_observability` block helpers (success/failure fires + re-raises) and a bare `fire(...)` method for after-the-fact emission. Neither adapter requires its underlying gem (`ruby-openai`, `aws-sdk-bedrockruntime`) at load time — the SDK loads cleanly without them.
|
|
8
|
+
|
|
3
9
|
## [3.29.0] - 2026-05-12
|
|
4
10
|
|
|
5
11
|
- Add `PlatformSdk::Observability::Langfuse::NotificationSubscriber` — subscribes to the `llm_call.platform_sdk` `ActiveSupport::Notifications` event and forwards to `Recorder.record_generation`. Apps can instrument LLM calls without coupling to a host concern; adding a second observability backend later is one more `subscribe` call.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
strongmind-platform-sdk (3.
|
|
4
|
+
strongmind-platform-sdk (3.30.0)
|
|
5
5
|
asset_sync
|
|
6
6
|
aws-sdk-cloudwatch
|
|
7
7
|
aws-sdk-secretsmanager (~> 1.66)
|
|
@@ -154,7 +154,7 @@ GEM
|
|
|
154
154
|
ffi (1.17.0)
|
|
155
155
|
ffi (1.17.0-x86_64-darwin)
|
|
156
156
|
ffi (1.17.0-x86_64-linux-gnu)
|
|
157
|
-
fog-aws (3.33.
|
|
157
|
+
fog-aws (3.33.1)
|
|
158
158
|
base64 (>= 0.2, < 0.4)
|
|
159
159
|
fog-core (~> 2.6)
|
|
160
160
|
fog-json (~> 1.1)
|
|
@@ -164,7 +164,7 @@ GEM
|
|
|
164
164
|
excon (~> 1.0)
|
|
165
165
|
formatador (>= 0.2, < 2.0)
|
|
166
166
|
mime-types
|
|
167
|
-
fog-json (1.
|
|
167
|
+
fog-json (1.2.0)
|
|
168
168
|
fog-core
|
|
169
169
|
multi_json (~> 1.10)
|
|
170
170
|
fog-xml (0.1.5)
|
|
@@ -177,12 +177,6 @@ GEM
|
|
|
177
177
|
google-protobuf (4.33.6)
|
|
178
178
|
bigdecimal
|
|
179
179
|
rake (>= 13)
|
|
180
|
-
google-protobuf (4.33.6-x86_64-darwin)
|
|
181
|
-
bigdecimal
|
|
182
|
-
rake (>= 13)
|
|
183
|
-
google-protobuf (4.33.6-x86_64-linux-gnu)
|
|
184
|
-
bigdecimal
|
|
185
|
-
rake (>= 13)
|
|
186
180
|
googleapis-common-protos-types (1.22.0)
|
|
187
181
|
google-protobuf (~> 4.26)
|
|
188
182
|
hashdiff (1.1.0)
|
|
@@ -213,7 +207,7 @@ GEM
|
|
|
213
207
|
mime-types (3.7.0)
|
|
214
208
|
logger
|
|
215
209
|
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
216
|
-
mime-types-data (3.2026.
|
|
210
|
+
mime-types-data (3.2026.0317)
|
|
217
211
|
mini_mime (1.1.5)
|
|
218
212
|
mini_portile2 (2.8.7)
|
|
219
213
|
minitest (5.24.1)
|
|
@@ -221,7 +215,7 @@ GEM
|
|
|
221
215
|
mutex_m (0.2.0)
|
|
222
216
|
net-http (0.4.1)
|
|
223
217
|
uri
|
|
224
|
-
net-imap (0.6.
|
|
218
|
+
net-imap (0.6.3)
|
|
225
219
|
date
|
|
226
220
|
net-protocol
|
|
227
221
|
net-pop (0.1.2)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/notifications'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module PlatformSdk
|
|
7
|
+
module Observability
|
|
8
|
+
module Langfuse
|
|
9
|
+
# Fires `llm_call.platform_sdk` notifications for Anthropic Claude calls
|
|
10
|
+
# made directly through the AWS Bedrock SDK (rather than via RubyLLM).
|
|
11
|
+
# Two entry points cover the two Bedrock invocation modes:
|
|
12
|
+
#
|
|
13
|
+
# # Non-streaming (`bedrock_client.invoke_model(payload)`)
|
|
14
|
+
# PlatformSdk::Observability::Langfuse::BedrockClaudeAdapter.with_observability(
|
|
15
|
+
# payload: { model_id: 'us.anthropic.claude-sonnet-4-6',
|
|
16
|
+
# body: JSON.dump(anthropic_messages_payload) },
|
|
17
|
+
# context: 'generate_outline'
|
|
18
|
+
# ) { bedrock_client.invoke_model(payload) }
|
|
19
|
+
#
|
|
20
|
+
# # Streaming (`bedrock_client.invoke_model_with_response_stream`)
|
|
21
|
+
# PlatformSdk::Observability::Langfuse::BedrockClaudeAdapter.with_streaming_observability(
|
|
22
|
+
# payload: { model_id: ..., body: JSON.dump(...) },
|
|
23
|
+
# context: 'chat_stream'
|
|
24
|
+
# ) do |collector|
|
|
25
|
+
# bedrock_client.invoke_model_with_response_stream(
|
|
26
|
+
# payload.merge(event_stream_handler: proc do |stream|
|
|
27
|
+
# stream.on_chunk_event do |event|
|
|
28
|
+
# collector.observe(event)
|
|
29
|
+
# # ...the caller's own chunk handling
|
|
30
|
+
# end
|
|
31
|
+
# end)
|
|
32
|
+
# )
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# The streaming collector accumulates text from `content_block_delta`
|
|
36
|
+
# events and pulls the final `input_tokens`/`output_tokens` from the
|
|
37
|
+
# `message_stop` event's `amazon-bedrock-invocationMetrics`. The
|
|
38
|
+
# notification fires once after the block returns.
|
|
39
|
+
#
|
|
40
|
+
# No hard dependency on the AWS SDK — this adapter only reads the
|
|
41
|
+
# documented JSON shape of Anthropic's Messages API on Bedrock.
|
|
42
|
+
module BedrockClaudeAdapter
|
|
43
|
+
class << self
|
|
44
|
+
def with_observability(payload:, context:)
|
|
45
|
+
response = yield
|
|
46
|
+
fire(payload:, response:, context:)
|
|
47
|
+
response
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
fire(payload:, response: nil, context:, error: e)
|
|
50
|
+
raise
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def with_streaming_observability(payload:, context:)
|
|
54
|
+
collector = StreamCollector.new
|
|
55
|
+
result = yield collector
|
|
56
|
+
fire_from_collector(payload:, collector:, context:)
|
|
57
|
+
result
|
|
58
|
+
rescue StandardError => e
|
|
59
|
+
fire_from_collector(payload:, collector:, context:, error: e)
|
|
60
|
+
raise
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# After-the-fact emission for non-streaming calls. Caller can use
|
|
64
|
+
# this if they want to invoke Bedrock outside of the block helper
|
|
65
|
+
# and emit observability separately.
|
|
66
|
+
def fire(payload:, response:, context:, error: nil)
|
|
67
|
+
attrs = decode_response(response)
|
|
68
|
+
notify(payload:, output: attrs[:output], usage: attrs[:usage], context:, error:)
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
OpenTelemetry.handle_error(
|
|
71
|
+
message: "BedrockClaudeAdapter.fire failed: #{e.class}: #{e.message[0, 200]}"
|
|
72
|
+
)
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def fire_from_collector(payload:, collector:, context:, error: nil)
|
|
79
|
+
usage = collector.usage
|
|
80
|
+
notify(
|
|
81
|
+
payload:,
|
|
82
|
+
output: collector.text.empty? ? nil : collector.text,
|
|
83
|
+
usage: usage.values.any? ? usage : {},
|
|
84
|
+
context:,
|
|
85
|
+
error:
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def notify(payload:, output:, usage:, context:, error:)
|
|
90
|
+
built = build_payload(payload:, output:, usage:, context:, error:)
|
|
91
|
+
return unless built
|
|
92
|
+
|
|
93
|
+
ActiveSupport::Notifications.instrument(LLM_CALL_EVENT, built)
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
OpenTelemetry.handle_error(
|
|
96
|
+
message: "BedrockClaudeAdapter.fire failed: #{e.class}: #{e.message[0, 200]}"
|
|
97
|
+
)
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_payload(payload:, output:, usage:, context:, error:)
|
|
102
|
+
return nil unless Langfuse.enabled?
|
|
103
|
+
|
|
104
|
+
# Tolerate non-Hash payloads (some callers pass `to_json`'d strings
|
|
105
|
+
# in tests) — extract only when the shape is what we expect.
|
|
106
|
+
payload = payload.is_a?(Hash) ? payload : {}
|
|
107
|
+
body = decode_body(payload[:body] || payload['body'])
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
name: context || 'llm_call',
|
|
111
|
+
model: payload[:model_id] || payload['model_id'] || body[:model] || body['model'],
|
|
112
|
+
input: body[:messages] || body['messages'],
|
|
113
|
+
output:,
|
|
114
|
+
usage: usage || {},
|
|
115
|
+
provider: 'aws.bedrock',
|
|
116
|
+
error:
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def decode_body(body)
|
|
121
|
+
return body if body.is_a?(Hash)
|
|
122
|
+
return {} if body.nil?
|
|
123
|
+
|
|
124
|
+
JSON.parse(body.to_s)
|
|
125
|
+
rescue JSON::ParserError
|
|
126
|
+
{}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Pulls `output` (concatenated text from content blocks) and `usage`
|
|
130
|
+
# (`input_tokens`/`output_tokens`) from a non-streaming Anthropic
|
|
131
|
+
# Messages-on-Bedrock response. Tolerant of both `response.body`
|
|
132
|
+
# (AWS SDK Seahorse) and plain Hash shapes for ease of testing.
|
|
133
|
+
def decode_response(response)
|
|
134
|
+
return { output: nil, usage: {} } if response.nil?
|
|
135
|
+
|
|
136
|
+
body = response.respond_to?(:body) ? response.body : response
|
|
137
|
+
# Rewind after read so the caller can re-read response.body
|
|
138
|
+
# downstream — Bedrock's Seahorse response wraps a StringIO that
|
|
139
|
+
# would otherwise be consumed by our instrumentation.
|
|
140
|
+
if body.respond_to?(:read)
|
|
141
|
+
raw = body.read
|
|
142
|
+
body.rewind if body.respond_to?(:rewind)
|
|
143
|
+
body = raw
|
|
144
|
+
end
|
|
145
|
+
parsed = body.is_a?(Hash) ? body : safe_parse_json(body.to_s)
|
|
146
|
+
|
|
147
|
+
{ output: extract_text(parsed), usage: extract_usage(parsed) }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def safe_parse_json(str)
|
|
151
|
+
JSON.parse(str)
|
|
152
|
+
rescue JSON::ParserError
|
|
153
|
+
{}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def extract_text(parsed)
|
|
157
|
+
content = parsed['content'] || parsed[:content]
|
|
158
|
+
return nil unless content.is_a?(Array)
|
|
159
|
+
|
|
160
|
+
content.filter_map { |block| block['text'] || block[:text] }.join
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def extract_usage(parsed)
|
|
164
|
+
usage = parsed['usage'] || parsed[:usage] || {}
|
|
165
|
+
{
|
|
166
|
+
input_tokens: usage['input_tokens'] || usage[:input_tokens],
|
|
167
|
+
output_tokens: usage['output_tokens'] || usage[:output_tokens]
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Accumulates streaming-response state so the adapter can emit a
|
|
173
|
+
# single observation after the stream ends.
|
|
174
|
+
#
|
|
175
|
+
# Bedrock's invoke_model_with_response_stream yields events whose
|
|
176
|
+
# `bytes` is JSON. For Anthropic on Bedrock the relevant event types
|
|
177
|
+
# are:
|
|
178
|
+
#
|
|
179
|
+
# * `content_block_delta` — incremental text in `delta.text`
|
|
180
|
+
# * `message_delta` — top-level `usage.output_tokens` (final count)
|
|
181
|
+
# * `message_stop` — Bedrock adds `amazon-bedrock-invocationMetrics`
|
|
182
|
+
# here with `inputTokenCount` and `outputTokenCount`
|
|
183
|
+
#
|
|
184
|
+
# Both message_delta and message_stop are emitted on success; we
|
|
185
|
+
# prefer message_stop's metrics when both are present.
|
|
186
|
+
class StreamCollector
|
|
187
|
+
attr_reader :text
|
|
188
|
+
|
|
189
|
+
def initialize
|
|
190
|
+
@text = +''
|
|
191
|
+
@input_tokens = nil
|
|
192
|
+
@output_tokens = nil
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def usage
|
|
196
|
+
{ input_tokens: @input_tokens, output_tokens: @output_tokens }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def observe(event)
|
|
200
|
+
parsed = parse_event(event)
|
|
201
|
+
return if parsed.nil?
|
|
202
|
+
|
|
203
|
+
case parsed['type']
|
|
204
|
+
when 'message_start'
|
|
205
|
+
# Anthropic emits initial input_tokens here. Used as a
|
|
206
|
+
# fallback when Bedrock's message_stop invocationMetrics
|
|
207
|
+
# are absent (future models, guardrail truncations, etc.).
|
|
208
|
+
start_usage = parsed.dig('message', 'usage') || {}
|
|
209
|
+
@input_tokens ||= start_usage['input_tokens']
|
|
210
|
+
when 'content_block_delta'
|
|
211
|
+
delta_text = parsed.dig('delta', 'text')
|
|
212
|
+
@text << delta_text if delta_text
|
|
213
|
+
when 'message_delta'
|
|
214
|
+
# message_delta usage is cumulative across the (possibly many)
|
|
215
|
+
# message_delta events Anthropic emits, so the latest value
|
|
216
|
+
# wins. See https://docs.anthropic.com/en/api/messages-streaming
|
|
217
|
+
usage = parsed['usage'] || {}
|
|
218
|
+
@output_tokens = usage['output_tokens'] if usage['output_tokens']
|
|
219
|
+
when 'message_stop'
|
|
220
|
+
metrics = parsed['amazon-bedrock-invocationMetrics'] || {}
|
|
221
|
+
@input_tokens = metrics['inputTokenCount'] if metrics['inputTokenCount']
|
|
222
|
+
@output_tokens = metrics['outputTokenCount'] if metrics['outputTokenCount']
|
|
223
|
+
end
|
|
224
|
+
rescue StandardError => e
|
|
225
|
+
OpenTelemetry.handle_error(
|
|
226
|
+
message: "BedrockClaudeAdapter::StreamCollector#observe failed: #{e.class}: #{e.message[0, 200]}"
|
|
227
|
+
)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
private
|
|
231
|
+
|
|
232
|
+
def parse_event(event)
|
|
233
|
+
bytes = event.respond_to?(:bytes) ? event.bytes : event
|
|
234
|
+
return nil if bytes.nil?
|
|
235
|
+
|
|
236
|
+
JSON.parse(bytes.to_s)
|
|
237
|
+
rescue JSON::ParserError
|
|
238
|
+
nil
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/notifications'
|
|
4
|
+
|
|
5
|
+
module PlatformSdk
|
|
6
|
+
module Observability
|
|
7
|
+
module Langfuse
|
|
8
|
+
# Fires `llm_call.platform_sdk` ActiveSupport::Notifications events with
|
|
9
|
+
# a payload extracted from a `ruby-openai` chat call. Apps that hit
|
|
10
|
+
# OpenAI's chat completions API directly (rather than via RubyLLM) call
|
|
11
|
+
# `with_observability` to get cost, tokens, model, input, and output
|
|
12
|
+
# captured in Langfuse:
|
|
13
|
+
#
|
|
14
|
+
# PlatformSdk::Observability::Langfuse::OpenAIAdapter.with_observability(
|
|
15
|
+
# parameters: { model: 'gpt-4o', messages: [...], ... },
|
|
16
|
+
# context: 'amend_json'
|
|
17
|
+
# ) do
|
|
18
|
+
# OpenAI::Client.new.chat(parameters: parameters)
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# The block runs the actual LLM call and its return value is forwarded
|
|
22
|
+
# back to the caller. On success a generation observation is recorded
|
|
23
|
+
# with model/usage from the response. On raise a failure observation
|
|
24
|
+
# is recorded and the exception is re-raised unchanged.
|
|
25
|
+
#
|
|
26
|
+
# No hard dependency on the `ruby-openai` gem — this adapter only
|
|
27
|
+
# touches the plain-Hash response shape OpenAI's API returns.
|
|
28
|
+
module OpenAIAdapter
|
|
29
|
+
class << self
|
|
30
|
+
# Wrap an `OpenAI::Client#chat` call. Forwards the block's return
|
|
31
|
+
# value. On success: fires success notification with model/input/
|
|
32
|
+
# output/usage extracted from `parameters` and the returned response.
|
|
33
|
+
# On raise: fires failure notification with `error:` set, re-raises.
|
|
34
|
+
def with_observability(parameters:, context:)
|
|
35
|
+
response = yield
|
|
36
|
+
fire(parameters:, response:, context:)
|
|
37
|
+
response
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
fire(parameters:, response: nil, context:, error: e)
|
|
40
|
+
raise
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Fire a single `llm_call.platform_sdk` notification. Useful when
|
|
44
|
+
# the caller has already invoked OpenAI and just wants to record
|
|
45
|
+
# the observation after the fact.
|
|
46
|
+
def fire(parameters:, response:, context:, error: nil)
|
|
47
|
+
payload = build_payload(parameters:, response:, context:, error:)
|
|
48
|
+
return unless payload
|
|
49
|
+
|
|
50
|
+
ActiveSupport::Notifications.instrument(LLM_CALL_EVENT, payload)
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
OpenTelemetry.handle_error(
|
|
53
|
+
message: "OpenAIAdapter.fire failed: #{e.class}: #{e.message[0, 200]}"
|
|
54
|
+
)
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def build_payload(parameters:, response:, context:, error:)
|
|
61
|
+
return nil unless Langfuse.enabled?
|
|
62
|
+
|
|
63
|
+
parameters ||= {}
|
|
64
|
+
choice = response.is_a?(Hash) ? response.dig('choices', 0, 'message') : nil
|
|
65
|
+
usage = response.is_a?(Hash) ? response['usage'] : nil
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
name: context || 'llm_call',
|
|
69
|
+
model: parameters[:model] || parameters['model'],
|
|
70
|
+
input: parameters[:messages] || parameters['messages'],
|
|
71
|
+
output: choice && choice['content'],
|
|
72
|
+
# ruby-openai responses use prompt_tokens / completion_tokens; the
|
|
73
|
+
# input_tokens / output_tokens fallbacks cover OpenAI-compatible
|
|
74
|
+
# proxies and any future rename of those fields.
|
|
75
|
+
usage: {
|
|
76
|
+
input_tokens: usage && (usage['prompt_tokens'] || usage['input_tokens']),
|
|
77
|
+
output_tokens: usage && (usage['completion_tokens'] || usage['output_tokens'])
|
|
78
|
+
},
|
|
79
|
+
provider: 'openai',
|
|
80
|
+
error:
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -24,6 +24,8 @@ require 'platform_sdk/observability/langfuse/traceable'
|
|
|
24
24
|
require 'platform_sdk/observability/langfuse/sidekiq_lifecycle'
|
|
25
25
|
require 'platform_sdk/observability/langfuse/notification_subscriber'
|
|
26
26
|
require 'platform_sdk/observability/langfuse/ruby_llm_adapter'
|
|
27
|
+
require 'platform_sdk/observability/langfuse/openai_adapter'
|
|
28
|
+
require 'platform_sdk/observability/langfuse/bedrock_claude_adapter'
|
|
27
29
|
|
|
28
30
|
module PlatformSdk
|
|
29
31
|
module Observability
|
data/lib/platform_sdk/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strongmind-platform-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.30.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Platform Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -316,10 +316,12 @@ files:
|
|
|
316
316
|
- lib/platform_sdk/logging/pii_formatter.rb
|
|
317
317
|
- lib/platform_sdk/observability.rb
|
|
318
318
|
- lib/platform_sdk/observability/langfuse.rb
|
|
319
|
+
- lib/platform_sdk/observability/langfuse/bedrock_claude_adapter.rb
|
|
319
320
|
- lib/platform_sdk/observability/langfuse/coercions.rb
|
|
320
321
|
- lib/platform_sdk/observability/langfuse/configuration.rb
|
|
321
322
|
- lib/platform_sdk/observability/langfuse/notification_subscriber.rb
|
|
322
323
|
- lib/platform_sdk/observability/langfuse/null_span_exporter.rb
|
|
324
|
+
- lib/platform_sdk/observability/langfuse/openai_adapter.rb
|
|
323
325
|
- lib/platform_sdk/observability/langfuse/recorder.rb
|
|
324
326
|
- lib/platform_sdk/observability/langfuse/ruby_llm_adapter.rb
|
|
325
327
|
- lib/platform_sdk/observability/langfuse/sidekiq_lifecycle.rb
|