langsmithrb_rails 0.1.1 → 0.3.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/.rspec_status +158 -79
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +1 -1
- data/README.md +153 -0
- data/langsmithrb_rails-0.1.1.gem +0 -0
- data/lib/langsmithrb_rails/client.rb +217 -2
- data/lib/langsmithrb_rails/config.rb +143 -46
- data/lib/langsmithrb_rails/evaluation/evaluator.rb +178 -0
- data/lib/langsmithrb_rails/evaluation/llm_evaluator.rb +154 -0
- data/lib/langsmithrb_rails/evaluation/string_evaluator.rb +158 -0
- data/lib/langsmithrb_rails/evaluation.rb +76 -0
- data/lib/langsmithrb_rails/otel/exporter.rb +120 -0
- data/lib/langsmithrb_rails/otel.rb +135 -0
- data/lib/langsmithrb_rails/run_trees.rb +157 -0
- data/lib/langsmithrb_rails/version.rb +1 -1
- data/lib/langsmithrb_rails/wrappers/anthropic.rb +146 -0
- data/lib/langsmithrb_rails/wrappers/base.rb +81 -0
- data/lib/langsmithrb_rails/wrappers/llm.rb +151 -0
- data/lib/langsmithrb_rails/wrappers/openai.rb +193 -0
- data/lib/langsmithrb_rails/wrappers.rb +41 -0
- data/lib/langsmithrb_rails.rb +121 -1
- data/pkg/langsmithrb_rails-0.3.0.gem +0 -0
- metadata +16 -2
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module LangsmithrbRails
|
6
|
+
module Wrappers
|
7
|
+
# Wrapper for OpenAI client
|
8
|
+
module OpenAI
|
9
|
+
# Wrap an OpenAI client with LangSmith tracing
|
10
|
+
# @param client [Object] The OpenAI client to wrap
|
11
|
+
# @param project_name [String] Optional project name for traces
|
12
|
+
# @param tags [Array<String>] Optional tags for traces
|
13
|
+
# @return [Object] The wrapped client
|
14
|
+
def self.wrap(client, project_name: nil, tags: [])
|
15
|
+
# Create a wrapper class that inherits from the client's class
|
16
|
+
wrapper_class = Class.new(client.class) do
|
17
|
+
attr_reader :original_client, :project_name, :tags
|
18
|
+
|
19
|
+
def initialize(original_client, project_name, tags)
|
20
|
+
@original_client = original_client
|
21
|
+
@project_name = project_name
|
22
|
+
@tags = tags
|
23
|
+
end
|
24
|
+
|
25
|
+
# Wrap chat completions
|
26
|
+
def chat(messages:, model: nil, temperature: nil, max_tokens: nil, **params)
|
27
|
+
# Prepare inputs for tracing
|
28
|
+
inputs = {
|
29
|
+
messages: messages,
|
30
|
+
model: model,
|
31
|
+
temperature: temperature,
|
32
|
+
max_tokens: max_tokens
|
33
|
+
}.merge(params).compact
|
34
|
+
|
35
|
+
# Create a run
|
36
|
+
run_id = nil
|
37
|
+
begin
|
38
|
+
run = LangsmithrbRails::Wrappers::Base.create_run(
|
39
|
+
"openai.chat",
|
40
|
+
inputs,
|
41
|
+
run_type: "llm",
|
42
|
+
project_name: project_name,
|
43
|
+
tags: tags
|
44
|
+
)
|
45
|
+
run_id = run&.dig("id")
|
46
|
+
rescue => e
|
47
|
+
LangsmithrbRails.logger.error("Failed to create LangSmith run: #{e.message}")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Call the original method
|
51
|
+
begin
|
52
|
+
result = original_client.chat(
|
53
|
+
messages: messages,
|
54
|
+
model: model,
|
55
|
+
temperature: temperature,
|
56
|
+
max_tokens: max_tokens,
|
57
|
+
**params
|
58
|
+
)
|
59
|
+
|
60
|
+
# Update the run with the result
|
61
|
+
if run_id
|
62
|
+
outputs = { response: result }
|
63
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, outputs)
|
64
|
+
end
|
65
|
+
|
66
|
+
result
|
67
|
+
rescue => e
|
68
|
+
# Update the run with the error
|
69
|
+
if run_id
|
70
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, {}, error: e.message)
|
71
|
+
end
|
72
|
+
raise e
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Wrap completions
|
77
|
+
def completions(prompt:, model: nil, temperature: nil, max_tokens: nil, **params)
|
78
|
+
# Prepare inputs for tracing
|
79
|
+
inputs = {
|
80
|
+
prompt: prompt,
|
81
|
+
model: model,
|
82
|
+
temperature: temperature,
|
83
|
+
max_tokens: max_tokens
|
84
|
+
}.merge(params).compact
|
85
|
+
|
86
|
+
# Create a run
|
87
|
+
run_id = nil
|
88
|
+
begin
|
89
|
+
run = LangsmithrbRails::Wrappers::Base.create_run(
|
90
|
+
"openai.completions",
|
91
|
+
inputs,
|
92
|
+
run_type: "llm",
|
93
|
+
project_name: project_name,
|
94
|
+
tags: tags
|
95
|
+
)
|
96
|
+
run_id = run&.dig("id")
|
97
|
+
rescue => e
|
98
|
+
LangsmithrbRails.logger.error("Failed to create LangSmith run: #{e.message}")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Call the original method
|
102
|
+
begin
|
103
|
+
result = original_client.completions(
|
104
|
+
prompt: prompt,
|
105
|
+
model: model,
|
106
|
+
temperature: temperature,
|
107
|
+
max_tokens: max_tokens,
|
108
|
+
**params
|
109
|
+
)
|
110
|
+
|
111
|
+
# Update the run with the result
|
112
|
+
if run_id
|
113
|
+
outputs = { response: result }
|
114
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, outputs)
|
115
|
+
end
|
116
|
+
|
117
|
+
result
|
118
|
+
rescue => e
|
119
|
+
# Update the run with the error
|
120
|
+
if run_id
|
121
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, {}, error: e.message)
|
122
|
+
end
|
123
|
+
raise e
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Wrap embeddings
|
128
|
+
def embeddings(input:, model: nil, **params)
|
129
|
+
# Prepare inputs for tracing
|
130
|
+
inputs = {
|
131
|
+
input: input,
|
132
|
+
model: model
|
133
|
+
}.merge(params).compact
|
134
|
+
|
135
|
+
# Create a run
|
136
|
+
run_id = nil
|
137
|
+
begin
|
138
|
+
run = LangsmithrbRails::Wrappers::Base.create_run(
|
139
|
+
"openai.embeddings",
|
140
|
+
inputs,
|
141
|
+
run_type: "embedding",
|
142
|
+
project_name: project_name,
|
143
|
+
tags: tags
|
144
|
+
)
|
145
|
+
run_id = run&.dig("id")
|
146
|
+
rescue => e
|
147
|
+
LangsmithrbRails.logger.error("Failed to create LangSmith run: #{e.message}")
|
148
|
+
end
|
149
|
+
|
150
|
+
# Call the original method
|
151
|
+
begin
|
152
|
+
result = original_client.embeddings(
|
153
|
+
input: input,
|
154
|
+
model: model,
|
155
|
+
**params
|
156
|
+
)
|
157
|
+
|
158
|
+
# Update the run with the result
|
159
|
+
if run_id
|
160
|
+
outputs = { response: result }
|
161
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, outputs)
|
162
|
+
end
|
163
|
+
|
164
|
+
result
|
165
|
+
rescue => e
|
166
|
+
# Update the run with the error
|
167
|
+
if run_id
|
168
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, {}, error: e.message)
|
169
|
+
end
|
170
|
+
raise e
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Forward other methods to the original client
|
175
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
176
|
+
if original_client.respond_to?(method_name)
|
177
|
+
original_client.send(method_name, *args, **kwargs, &block)
|
178
|
+
else
|
179
|
+
super
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def respond_to_missing?(method_name, include_private = false)
|
184
|
+
original_client.respond_to?(method_name, include_private) || super
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Create and return an instance of the wrapper class
|
189
|
+
wrapper_class.new(client, project_name, tags)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "wrappers/base"
|
4
|
+
require_relative "wrappers/openai"
|
5
|
+
require_relative "wrappers/anthropic"
|
6
|
+
require_relative "wrappers/llm"
|
7
|
+
|
8
|
+
module LangsmithrbRails
|
9
|
+
# Wrappers for LLM providers
|
10
|
+
module Wrappers
|
11
|
+
# Wrap an LLM provider client with LangSmith tracing
|
12
|
+
# @param client [Object] The provider client to wrap
|
13
|
+
# @param provider [Symbol] The provider type (:openai, :anthropic, :llm)
|
14
|
+
# @param project_name [String] Optional project name for traces
|
15
|
+
# @param tags [Array<String>] Optional tags for traces
|
16
|
+
# @return [Object] The wrapped client
|
17
|
+
def self.wrap(client, provider:, project_name: nil, tags: [])
|
18
|
+
case provider.to_sym
|
19
|
+
when :openai
|
20
|
+
OpenAI.wrap(client, project_name: project_name, tags: tags)
|
21
|
+
when :anthropic
|
22
|
+
Anthropic.wrap(client, project_name: project_name, tags: tags)
|
23
|
+
when :llm
|
24
|
+
LLM.wrap(client, project_name: project_name, tags: tags)
|
25
|
+
else
|
26
|
+
raise ArgumentError, "Unsupported provider: #{provider}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create a traceable decorator for any method
|
31
|
+
# @param object [Object] The object with the method to trace
|
32
|
+
# @param method_name [Symbol] The name of the method to trace
|
33
|
+
# @param run_name [String] Name for the run
|
34
|
+
# @param project_name [String] Optional project name for traces
|
35
|
+
# @param tags [Array<String>] Optional tags for traces
|
36
|
+
# @return [Object] The object with the traced method
|
37
|
+
def self.traceable(object, method_name, run_name: nil, project_name: nil, tags: [])
|
38
|
+
LLM.traceable(object, method_name, run_name: run_name, project_name: project_name, tags: tags)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/langsmithrb_rails.rb
CHANGED
@@ -2,12 +2,19 @@
|
|
2
2
|
|
3
3
|
require "langsmithrb"
|
4
4
|
require "rails"
|
5
|
+
require "json"
|
6
|
+
require "securerandom"
|
7
|
+
require "logger"
|
5
8
|
|
6
9
|
require_relative "langsmithrb_rails/version"
|
7
10
|
require_relative "langsmithrb_rails/config"
|
8
11
|
require_relative "langsmithrb_rails/client"
|
9
12
|
require_relative "langsmithrb_rails/redactor"
|
10
13
|
require_relative "langsmithrb_rails/langsmith"
|
14
|
+
require_relative "langsmithrb_rails/wrappers"
|
15
|
+
require_relative "langsmithrb_rails/run_trees"
|
16
|
+
require_relative "langsmithrb_rails/otel"
|
17
|
+
require_relative "langsmithrb_rails/evaluation"
|
11
18
|
require_relative "langsmithrb_rails/railtie" if defined?(Rails)
|
12
19
|
|
13
20
|
# LangsmithRails provides integration with LangSmith for Rails applications
|
@@ -25,7 +32,120 @@ module LangsmithrbRails
|
|
25
32
|
# Get the current configuration
|
26
33
|
# @return [LangsmithrbRails::Config] Configuration object
|
27
34
|
def config
|
28
|
-
@config ||= Config.
|
35
|
+
@config ||= Config.instance
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the logger
|
39
|
+
# @return [Logger] Logger instance
|
40
|
+
def logger
|
41
|
+
config.logger
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wrap an LLM provider client with LangSmith tracing
|
45
|
+
# @param client [Object] The provider client to wrap
|
46
|
+
# @param provider [Symbol] The provider type (:openai, :anthropic, :llm)
|
47
|
+
# @param project_name [String] Optional project name for traces
|
48
|
+
# @param tags [Array<String>] Optional tags for traces
|
49
|
+
# @return [Object] The wrapped client
|
50
|
+
def wrap(client, provider:, project_name: nil, tags: [])
|
51
|
+
Wrappers.wrap(client, provider: provider, project_name: project_name, tags: tags)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Create a traceable decorator for any method
|
55
|
+
# @param object [Object] The object with the method to trace
|
56
|
+
# @param method_name [Symbol] The name of the method to trace
|
57
|
+
# @param run_name [String] Name for the run
|
58
|
+
# @param project_name [String] Optional project name for traces
|
59
|
+
# @param tags [Array<String>] Optional tags for traces
|
60
|
+
# @return [Object] The object with the traced method
|
61
|
+
def traceable(object, method_name, run_name: nil, project_name: nil, tags: [])
|
62
|
+
Wrappers.traceable(object, method_name, run_name: run_name, project_name: project_name, tags: tags)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create a run context for hierarchical tracing
|
66
|
+
# @param name [String] Name of the run
|
67
|
+
# @param run_type [String] Type of run (e.g., "llm", "chain")
|
68
|
+
# @param inputs [Hash] Input data
|
69
|
+
# @param parent_run_id [String] Optional parent run ID
|
70
|
+
# @param project_name [String] Optional project name
|
71
|
+
# @param tags [Array<String>] Optional tags
|
72
|
+
# @yield [RunTree] The run tree
|
73
|
+
# @return [Object] The result of the block
|
74
|
+
def run(name:, run_type:, inputs:, parent_run_id: nil, project_name: nil, tags: [], &block)
|
75
|
+
RunContext.run(
|
76
|
+
name: name,
|
77
|
+
run_type: run_type,
|
78
|
+
inputs: inputs,
|
79
|
+
project_name: project_name,
|
80
|
+
tags: tags,
|
81
|
+
&block
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create an evaluator
|
86
|
+
# @param type [Symbol] Type of evaluator (:string, :llm)
|
87
|
+
# @param options [Hash] Options for the evaluator
|
88
|
+
# @return [Evaluation::Evaluator] The evaluator instance
|
89
|
+
def evaluator(type, **options)
|
90
|
+
Evaluation.create(type, **options)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Alias for evaluator for better naming consistency
|
94
|
+
# @param type [Symbol] Type of evaluator (:string, :llm)
|
95
|
+
# @param options [Hash] Options for the evaluator
|
96
|
+
# @return [Evaluation::Evaluator] The evaluator instance
|
97
|
+
def create_evaluator(type, **options)
|
98
|
+
Evaluation.create(type, **options)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get a client instance
|
102
|
+
# @return [LangsmithrbRails::Client] Client instance
|
103
|
+
def client
|
104
|
+
@client ||= Client.new(api_key: config.api_key, api_url: config.api_url)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Initialize OpenTelemetry with LangSmith exporter
|
108
|
+
# @param api_key [String] LangSmith API key
|
109
|
+
# @param api_url [String] LangSmith API URL
|
110
|
+
# @param service_name [String] Service name for OpenTelemetry
|
111
|
+
# @param service_version [String] Service version for OpenTelemetry
|
112
|
+
# @return [Boolean] Whether OpenTelemetry was initialized
|
113
|
+
def init_otel(api_key: nil, api_url: nil, service_name: nil, service_version: nil)
|
114
|
+
OTEL.init(
|
115
|
+
api_key: api_key || config.api_key,
|
116
|
+
api_url: api_url || config.api_url,
|
117
|
+
service_name: service_name || config.otel_service_name,
|
118
|
+
service_version: service_version
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Create a trace span
|
123
|
+
# @param name [String] Name of the span
|
124
|
+
# @param attributes [Hash] Span attributes
|
125
|
+
# @param kind [Symbol] Span kind
|
126
|
+
# @yield Block to execute within the span
|
127
|
+
# @return [Object] Result of the block
|
128
|
+
def trace(name, attributes: {}, kind: :internal, &block)
|
129
|
+
OTEL.trace(name, attributes: attributes, kind: kind, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Create a trace span for LLM operations
|
133
|
+
# @param name [String] Name of the span
|
134
|
+
# @param inputs [Hash] LLM inputs
|
135
|
+
# @param run_type [String] Type of run
|
136
|
+
# @param project_name [String] Project name
|
137
|
+
# @param tags [Array<String>] Tags
|
138
|
+
# @yield Block to execute within the span
|
139
|
+
# @return [Object] Result of the block
|
140
|
+
def trace_llm(name, inputs: {}, run_type: "llm", project_name: nil, tags: [], &block)
|
141
|
+
OTEL.trace_llm(
|
142
|
+
name,
|
143
|
+
inputs: inputs,
|
144
|
+
run_type: run_type,
|
145
|
+
project_name: project_name || config.project_name,
|
146
|
+
tags: tags,
|
147
|
+
&block
|
148
|
+
)
|
29
149
|
end
|
30
150
|
end
|
31
151
|
end
|
Binary file
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langsmithrb_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Protocol Grid
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-08-
|
10
|
+
date: 2025-08-16 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: langsmithrb
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- README.md
|
167
167
|
- Rakefile
|
168
168
|
- langsmithrb_rails-0.1.0.gem
|
169
|
+
- langsmithrb_rails-0.1.1.gem
|
169
170
|
- langsmithrb_rails.gemspec
|
170
171
|
- lib/generators/langsmithrb_rails/buffer/buffer_generator.rb
|
171
172
|
- lib/generators/langsmithrb_rails/buffer/templates/create_langsmith_run_buffers.rb
|
@@ -204,12 +205,25 @@ files:
|
|
204
205
|
- lib/langsmithrb_rails.rb
|
205
206
|
- lib/langsmithrb_rails/client.rb
|
206
207
|
- lib/langsmithrb_rails/config.rb
|
208
|
+
- lib/langsmithrb_rails/evaluation.rb
|
209
|
+
- lib/langsmithrb_rails/evaluation/evaluator.rb
|
210
|
+
- lib/langsmithrb_rails/evaluation/llm_evaluator.rb
|
211
|
+
- lib/langsmithrb_rails/evaluation/string_evaluator.rb
|
207
212
|
- lib/langsmithrb_rails/generators/langsmithrb_rails/langsmith_generator.rb
|
208
213
|
- lib/langsmithrb_rails/generators/langsmithrb_rails/templates/langsmith_initializer.rb
|
209
214
|
- lib/langsmithrb_rails/langsmith.rb
|
215
|
+
- lib/langsmithrb_rails/otel.rb
|
216
|
+
- lib/langsmithrb_rails/otel/exporter.rb
|
210
217
|
- lib/langsmithrb_rails/railtie.rb
|
211
218
|
- lib/langsmithrb_rails/redactor.rb
|
219
|
+
- lib/langsmithrb_rails/run_trees.rb
|
212
220
|
- lib/langsmithrb_rails/version.rb
|
221
|
+
- lib/langsmithrb_rails/wrappers.rb
|
222
|
+
- lib/langsmithrb_rails/wrappers/anthropic.rb
|
223
|
+
- lib/langsmithrb_rails/wrappers/base.rb
|
224
|
+
- lib/langsmithrb_rails/wrappers/llm.rb
|
225
|
+
- lib/langsmithrb_rails/wrappers/openai.rb
|
226
|
+
- pkg/langsmithrb_rails-0.3.0.gem
|
213
227
|
homepage: https://github.com/cdaviis/langsmithrb_rails
|
214
228
|
licenses:
|
215
229
|
- MIT
|