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.
@@ -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
@@ -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.new
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.1.1
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-12 00:00:00.000000000 Z
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