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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -0
  3. data/.rspec_status +161 -0
  4. data/CHANGELOG.md +38 -0
  5. data/Gemfile +20 -0
  6. data/Gemfile.lock +321 -0
  7. data/LICENSE +21 -0
  8. data/README.md +421 -0
  9. data/Rakefile +10 -0
  10. data/langsmithrb_rails-0.1.0.gem +0 -0
  11. data/langsmithrb_rails-0.1.1.gem +0 -0
  12. data/langsmithrb_rails.gemspec +45 -0
  13. data/lib/generators/langsmithrb_rails/buffer/buffer_generator.rb +94 -0
  14. data/lib/generators/langsmithrb_rails/buffer/templates/create_langsmith_run_buffers.rb +29 -0
  15. data/lib/generators/langsmithrb_rails/buffer/templates/flush_buffer_job.rb +40 -0
  16. data/lib/generators/langsmithrb_rails/buffer/templates/langsmith.rake +71 -0
  17. data/lib/generators/langsmithrb_rails/buffer/templates/langsmith_run_buffer.rb +70 -0
  18. data/lib/generators/langsmithrb_rails/buffer/templates/migration.rb +28 -0
  19. data/lib/generators/langsmithrb_rails/ci/ci_generator.rb +37 -0
  20. data/lib/generators/langsmithrb_rails/ci/templates/langsmith-evals.yml +85 -0
  21. data/lib/generators/langsmithrb_rails/ci/templates/langsmith_export_summary.rb +81 -0
  22. data/lib/generators/langsmithrb_rails/demo/demo_generator.rb +81 -0
  23. data/lib/generators/langsmithrb_rails/demo/templates/chat_controller.js +88 -0
  24. data/lib/generators/langsmithrb_rails/demo/templates/chat_controller.rb +58 -0
  25. data/lib/generators/langsmithrb_rails/demo/templates/chat_message.rb +24 -0
  26. data/lib/generators/langsmithrb_rails/demo/templates/create_chat_messages.rb +19 -0
  27. data/lib/generators/langsmithrb_rails/demo/templates/index.html.erb +180 -0
  28. data/lib/generators/langsmithrb_rails/demo/templates/llm_service.rb +165 -0
  29. data/lib/generators/langsmithrb_rails/evals/evals_generator.rb +52 -0
  30. data/lib/generators/langsmithrb_rails/evals/templates/checks/correctness.rb +71 -0
  31. data/lib/generators/langsmithrb_rails/evals/templates/checks/llm_graded.rb +137 -0
  32. data/lib/generators/langsmithrb_rails/evals/templates/datasets/sample.yml +60 -0
  33. data/lib/generators/langsmithrb_rails/evals/templates/langsmith_evals.rake +255 -0
  34. data/lib/generators/langsmithrb_rails/evals/templates/targets/http.rb +120 -0
  35. data/lib/generators/langsmithrb_rails/evals/templates/targets/ruby.rb +136 -0
  36. data/lib/generators/langsmithrb_rails/install/install_generator.rb +35 -0
  37. data/lib/generators/langsmithrb_rails/install/templates/config.yml +45 -0
  38. data/lib/generators/langsmithrb_rails/install/templates/initializer.rb +34 -0
  39. data/lib/generators/langsmithrb_rails/privacy/privacy_generator.rb +39 -0
  40. data/lib/generators/langsmithrb_rails/privacy/templates/custom_redactor.rb +132 -0
  41. data/lib/generators/langsmithrb_rails/privacy/templates/privacy.yml +88 -0
  42. data/lib/generators/langsmithrb_rails/privacy/templates/privacy_initializer.rb +41 -0
  43. data/lib/generators/langsmithrb_rails/tracing/templates/langsmith_traced.rb +146 -0
  44. data/lib/generators/langsmithrb_rails/tracing/templates/langsmith_traced_job.rb +151 -0
  45. data/lib/generators/langsmithrb_rails/tracing/templates/request_tracing.rb +117 -0
  46. data/lib/generators/langsmithrb_rails/tracing/tracing_generator.rb +78 -0
  47. data/lib/langsmithrb_rails/client.rb +292 -0
  48. data/lib/langsmithrb_rails/config.rb +169 -0
  49. data/lib/langsmithrb_rails/evaluation/evaluator.rb +178 -0
  50. data/lib/langsmithrb_rails/evaluation/llm_evaluator.rb +154 -0
  51. data/lib/langsmithrb_rails/evaluation/string_evaluator.rb +158 -0
  52. data/lib/langsmithrb_rails/evaluation.rb +76 -0
  53. data/lib/langsmithrb_rails/generators/langsmithrb_rails/langsmith_generator.rb +61 -0
  54. data/lib/langsmithrb_rails/generators/langsmithrb_rails/templates/langsmith_initializer.rb +22 -0
  55. data/lib/langsmithrb_rails/langsmith.rb +35 -0
  56. data/lib/langsmithrb_rails/otel/exporter.rb +120 -0
  57. data/lib/langsmithrb_rails/otel.rb +135 -0
  58. data/lib/langsmithrb_rails/railtie.rb +33 -0
  59. data/lib/langsmithrb_rails/redactor.rb +76 -0
  60. data/lib/langsmithrb_rails/run_trees.rb +157 -0
  61. data/lib/langsmithrb_rails/version.rb +5 -0
  62. data/lib/langsmithrb_rails/wrappers/anthropic.rb +146 -0
  63. data/lib/langsmithrb_rails/wrappers/base.rb +81 -0
  64. data/lib/langsmithrb_rails/wrappers/llm.rb +151 -0
  65. data/lib/langsmithrb_rails/wrappers/openai.rb +193 -0
  66. data/lib/langsmithrb_rails/wrappers.rb +41 -0
  67. data/lib/langsmithrb_rails.rb +151 -0
  68. data/pkg/langsmithrb_rails-0.3.0.gem +0 -0
  69. 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