langsmithrb_rails 0.1.0 → 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 +3 -0
- data/.rspec_status +161 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +321 -0
- data/LICENSE +21 -0
- data/README.md +421 -0
- data/Rakefile +10 -0
- data/langsmithrb_rails-0.1.0.gem +0 -0
- data/langsmithrb_rails-0.1.1.gem +0 -0
- data/langsmithrb_rails.gemspec +45 -0
- data/lib/generators/langsmithrb_rails/buffer/buffer_generator.rb +94 -0
- data/lib/generators/langsmithrb_rails/buffer/templates/create_langsmith_run_buffers.rb +29 -0
- data/lib/generators/langsmithrb_rails/buffer/templates/flush_buffer_job.rb +40 -0
- data/lib/generators/langsmithrb_rails/buffer/templates/langsmith.rake +71 -0
- data/lib/generators/langsmithrb_rails/buffer/templates/langsmith_run_buffer.rb +70 -0
- data/lib/generators/langsmithrb_rails/buffer/templates/migration.rb +28 -0
- data/lib/generators/langsmithrb_rails/ci/ci_generator.rb +37 -0
- data/lib/generators/langsmithrb_rails/ci/templates/langsmith-evals.yml +85 -0
- data/lib/generators/langsmithrb_rails/ci/templates/langsmith_export_summary.rb +81 -0
- data/lib/generators/langsmithrb_rails/demo/demo_generator.rb +81 -0
- data/lib/generators/langsmithrb_rails/demo/templates/chat_controller.js +88 -0
- data/lib/generators/langsmithrb_rails/demo/templates/chat_controller.rb +58 -0
- data/lib/generators/langsmithrb_rails/demo/templates/chat_message.rb +24 -0
- data/lib/generators/langsmithrb_rails/demo/templates/create_chat_messages.rb +19 -0
- data/lib/generators/langsmithrb_rails/demo/templates/index.html.erb +180 -0
- data/lib/generators/langsmithrb_rails/demo/templates/llm_service.rb +165 -0
- data/lib/generators/langsmithrb_rails/evals/evals_generator.rb +52 -0
- data/lib/generators/langsmithrb_rails/evals/templates/checks/correctness.rb +71 -0
- data/lib/generators/langsmithrb_rails/evals/templates/checks/llm_graded.rb +137 -0
- data/lib/generators/langsmithrb_rails/evals/templates/datasets/sample.yml +60 -0
- data/lib/generators/langsmithrb_rails/evals/templates/langsmith_evals.rake +255 -0
- data/lib/generators/langsmithrb_rails/evals/templates/targets/http.rb +120 -0
- data/lib/generators/langsmithrb_rails/evals/templates/targets/ruby.rb +136 -0
- data/lib/generators/langsmithrb_rails/install/install_generator.rb +35 -0
- data/lib/generators/langsmithrb_rails/install/templates/config.yml +45 -0
- data/lib/generators/langsmithrb_rails/install/templates/initializer.rb +34 -0
- data/lib/generators/langsmithrb_rails/privacy/privacy_generator.rb +39 -0
- data/lib/generators/langsmithrb_rails/privacy/templates/custom_redactor.rb +132 -0
- data/lib/generators/langsmithrb_rails/privacy/templates/privacy.yml +88 -0
- data/lib/generators/langsmithrb_rails/privacy/templates/privacy_initializer.rb +41 -0
- data/lib/generators/langsmithrb_rails/tracing/templates/langsmith_traced.rb +146 -0
- data/lib/generators/langsmithrb_rails/tracing/templates/langsmith_traced_job.rb +151 -0
- data/lib/generators/langsmithrb_rails/tracing/templates/request_tracing.rb +117 -0
- data/lib/generators/langsmithrb_rails/tracing/tracing_generator.rb +78 -0
- data/lib/langsmithrb_rails/client.rb +292 -0
- data/lib/langsmithrb_rails/config.rb +169 -0
- 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/generators/langsmithrb_rails/langsmith_generator.rb +61 -0
- data/lib/langsmithrb_rails/generators/langsmithrb_rails/templates/langsmith_initializer.rb +22 -0
- data/lib/langsmithrb_rails/langsmith.rb +35 -0
- data/lib/langsmithrb_rails/otel/exporter.rb +120 -0
- data/lib/langsmithrb_rails/otel.rb +135 -0
- data/lib/langsmithrb_rails/railtie.rb +33 -0
- data/lib/langsmithrb_rails/redactor.rb +76 -0
- data/lib/langsmithrb_rails/run_trees.rb +157 -0
- data/lib/langsmithrb_rails/version.rb +5 -0
- 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 +151 -0
- data/pkg/langsmithrb_rails-0.3.0.gem +0 -0
- metadata +74 -7
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LangsmithrbRails
|
4
|
+
module Wrappers
|
5
|
+
# Base module for LLM provider wrappers
|
6
|
+
module Base
|
7
|
+
# Wrap a provider client with LangSmith tracing
|
8
|
+
# @param client [Object] The provider client to wrap
|
9
|
+
# @param project_name [String] Optional project name for traces
|
10
|
+
# @param tags [Array<String>] Optional tags for traces
|
11
|
+
# @return [Object] The wrapped client
|
12
|
+
def self.wrap(client, project_name: nil, tags: [])
|
13
|
+
# To be implemented by specific provider wrappers
|
14
|
+
raise NotImplementedError, "This method should be implemented by provider-specific wrappers"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a run for a provider operation
|
18
|
+
# @param name [String] Name of the operation
|
19
|
+
# @param inputs [Hash] Input data
|
20
|
+
# @param run_type [String] Type of run (e.g., "llm", "chain")
|
21
|
+
# @param project_name [String] Optional project name
|
22
|
+
# @param tags [Array<String>] Optional tags
|
23
|
+
# @return [Hash] The created run data
|
24
|
+
def self.create_run(name, inputs, run_type: "llm", project_name: nil, tags: [])
|
25
|
+
client = LangsmithrbRails::Client.new
|
26
|
+
|
27
|
+
run_data = {
|
28
|
+
name: name,
|
29
|
+
inputs: inputs,
|
30
|
+
run_type: run_type,
|
31
|
+
start_time: Time.now.utc.iso8601,
|
32
|
+
execution_order: 1,
|
33
|
+
serialized: { name: name },
|
34
|
+
session_name: project_name,
|
35
|
+
tags: tags
|
36
|
+
}
|
37
|
+
|
38
|
+
response = client.create_run(run_data)
|
39
|
+
|
40
|
+
if response[:status] >= 200 && response[:status] < 300
|
41
|
+
response[:body]
|
42
|
+
else
|
43
|
+
LangsmithrbRails.logger.error("Failed to create run: #{response[:error] || response[:body]}")
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Update a run with outputs and end time
|
49
|
+
# @param run_id [String] ID of the run to update
|
50
|
+
# @param outputs [Hash] Output data
|
51
|
+
# @param error [String] Optional error message
|
52
|
+
# @return [Hash] The updated run data
|
53
|
+
def self.update_run(run_id, outputs, error: nil)
|
54
|
+
return unless run_id
|
55
|
+
|
56
|
+
client = LangsmithrbRails::Client.new
|
57
|
+
|
58
|
+
run_data = {
|
59
|
+
outputs: outputs,
|
60
|
+
end_time: Time.now.utc.iso8601
|
61
|
+
}
|
62
|
+
|
63
|
+
if error
|
64
|
+
run_data[:error] = error
|
65
|
+
run_data[:status] = "error"
|
66
|
+
else
|
67
|
+
run_data[:status] = "success"
|
68
|
+
end
|
69
|
+
|
70
|
+
response = client.update_run(run_id, run_data)
|
71
|
+
|
72
|
+
if response[:status] >= 200 && response[:status] < 300
|
73
|
+
response[:body]
|
74
|
+
else
|
75
|
+
LangsmithrbRails.logger.error("Failed to update run: #{response[:error] || response[:body]}")
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module LangsmithrbRails
|
6
|
+
module Wrappers
|
7
|
+
# Wrapper for generic LLM providers
|
8
|
+
module LLM
|
9
|
+
# Wrap an LLM with LangSmith tracing
|
10
|
+
# @param llm [Object] The LLM 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 LLM
|
14
|
+
def self.wrap(llm, project_name: nil, tags: [])
|
15
|
+
# Create a wrapper class
|
16
|
+
wrapper_class = Class.new do
|
17
|
+
attr_reader :llm, :project_name, :tags
|
18
|
+
|
19
|
+
def initialize(llm, project_name, tags)
|
20
|
+
@llm = llm
|
21
|
+
@project_name = project_name
|
22
|
+
@tags = tags
|
23
|
+
end
|
24
|
+
|
25
|
+
# Wrap the call method
|
26
|
+
def call(prompt, **params)
|
27
|
+
# Prepare inputs for tracing
|
28
|
+
inputs = {
|
29
|
+
prompt: prompt
|
30
|
+
}.merge(params).compact
|
31
|
+
|
32
|
+
# Create a run
|
33
|
+
run_id = nil
|
34
|
+
begin
|
35
|
+
run = LangsmithrbRails::Wrappers::Base.create_run(
|
36
|
+
"llm.call",
|
37
|
+
inputs,
|
38
|
+
run_type: "llm",
|
39
|
+
project_name: project_name,
|
40
|
+
tags: tags
|
41
|
+
)
|
42
|
+
run_id = run&.dig("id")
|
43
|
+
rescue => e
|
44
|
+
LangsmithrbRails.logger.error("Failed to create LangSmith run: #{e.message}")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Call the original method
|
48
|
+
begin
|
49
|
+
result = if llm.respond_to?(:call)
|
50
|
+
llm.call(prompt, **params)
|
51
|
+
elsif llm.is_a?(Proc)
|
52
|
+
llm.call(prompt, **params)
|
53
|
+
else
|
54
|
+
raise ArgumentError, "LLM must respond to 'call' or be a Proc"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Update the run with the result
|
58
|
+
if run_id
|
59
|
+
outputs = { response: result }
|
60
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, outputs)
|
61
|
+
end
|
62
|
+
|
63
|
+
result
|
64
|
+
rescue => e
|
65
|
+
# Update the run with the error
|
66
|
+
if run_id
|
67
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, {}, error: e.message)
|
68
|
+
end
|
69
|
+
raise e
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Forward other methods to the original LLM
|
74
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
75
|
+
if llm.respond_to?(method_name)
|
76
|
+
llm.send(method_name, *args, **kwargs, &block)
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def respond_to_missing?(method_name, include_private = false)
|
83
|
+
llm.respond_to?(method_name, include_private) || super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create and return an instance of the wrapper class
|
88
|
+
wrapper_class.new(llm, project_name, tags)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Create a traceable decorator for any method
|
92
|
+
# @param object [Object] The object with the method to trace
|
93
|
+
# @param method_name [Symbol] The name of the method to trace
|
94
|
+
# @param run_name [String] Name for the run
|
95
|
+
# @param project_name [String] Optional project name for traces
|
96
|
+
# @param tags [Array<String>] Optional tags for traces
|
97
|
+
# @return [Object] The object with the traced method
|
98
|
+
def self.traceable(object, method_name, run_name: nil, project_name: nil, tags: [])
|
99
|
+
run_name ||= "#{object.class.name}.#{method_name}"
|
100
|
+
|
101
|
+
# Store the original method
|
102
|
+
original_method = object.method(method_name)
|
103
|
+
|
104
|
+
# Define a new method that wraps the original
|
105
|
+
object.define_singleton_method(method_name) do |*args, **kwargs, &block|
|
106
|
+
# Prepare inputs for tracing
|
107
|
+
inputs = {
|
108
|
+
args: args,
|
109
|
+
kwargs: kwargs
|
110
|
+
}.compact
|
111
|
+
|
112
|
+
# Create a run
|
113
|
+
run_id = nil
|
114
|
+
begin
|
115
|
+
run = LangsmithrbRails::Wrappers::Base.create_run(
|
116
|
+
run_name,
|
117
|
+
inputs,
|
118
|
+
run_type: "chain",
|
119
|
+
project_name: project_name,
|
120
|
+
tags: tags
|
121
|
+
)
|
122
|
+
run_id = run&.dig("id")
|
123
|
+
rescue => e
|
124
|
+
LangsmithrbRails.logger.error("Failed to create LangSmith run: #{e.message}")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Call the original method
|
128
|
+
begin
|
129
|
+
result = original_method.call(*args, **kwargs, &block)
|
130
|
+
|
131
|
+
# Update the run with the result
|
132
|
+
if run_id
|
133
|
+
outputs = { result: result }
|
134
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, outputs)
|
135
|
+
end
|
136
|
+
|
137
|
+
result
|
138
|
+
rescue => e
|
139
|
+
# Update the run with the error
|
140
|
+
if run_id
|
141
|
+
LangsmithrbRails::Wrappers::Base.update_run(run_id, {}, error: e.message)
|
142
|
+
end
|
143
|
+
raise e
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
object
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -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
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "langsmithrb"
|
4
|
+
require "rails"
|
5
|
+
require "json"
|
6
|
+
require "securerandom"
|
7
|
+
require "logger"
|
8
|
+
|
9
|
+
require_relative "langsmithrb_rails/version"
|
10
|
+
require_relative "langsmithrb_rails/config"
|
11
|
+
require_relative "langsmithrb_rails/client"
|
12
|
+
require_relative "langsmithrb_rails/redactor"
|
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"
|
18
|
+
require_relative "langsmithrb_rails/railtie" if defined?(Rails)
|
19
|
+
|
20
|
+
# LangsmithRails provides integration with LangSmith for Rails applications
|
21
|
+
module LangsmithrbRails
|
22
|
+
class << self
|
23
|
+
# Configure LangsmithrbRails
|
24
|
+
# @yield [config] Configuration block
|
25
|
+
# @yieldparam config [LangsmithrbRails::Config] Configuration object
|
26
|
+
# @return [LangsmithrbRails::Config] Configuration object
|
27
|
+
def configure
|
28
|
+
yield config if block_given?
|
29
|
+
config
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get the current configuration
|
33
|
+
# @return [LangsmithrbRails::Config] Configuration object
|
34
|
+
def 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
|
+
)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
Binary file
|