langsmith-sdk 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dd9018b27dbb87f1518a96570bedf1bf7b09702b18816f072f9764075cb6adf8
4
+ data.tar.gz: d69c6d0194c193fbb8567beb42d98042fa2e36fe24e855a0429753d7440efc63
5
+ SHA512:
6
+ metadata.gz: 156b32b2b1c09ef127183b8899dda9ba58bd837895b0baeb36074bc507b1ebed4a959847a9ca71b7dfaac93bcd8d0f35afe7a5aa9677ff8e9959c0a1132d4e56
7
+ data.tar.gz: '08a7208efb8a94a5d8a798fa4a3d490575342ca807629a0ba43e577ad7fb9c0f15c0c0476cc65067c1540f127332f9a79ebe07b2047a3c922f721f6bb56f1f17'
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
4
+
data/.rubocop.yml ADDED
@@ -0,0 +1,120 @@
1
+ # RuboCop configuration for langsmith-sdk-ruby
2
+ # Run: bundle exec rubocop
3
+
4
+ plugins:
5
+ - rubocop-rspec
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 3.1
9
+ NewCops: enable
10
+ SuggestExtensions: false
11
+ Exclude:
12
+ - 'vendor/**/*'
13
+ - 'examples/**/*'
14
+ - '*.gemspec'
15
+
16
+ # Layout
17
+ Layout/LineLength:
18
+ Max: 120
19
+ AllowedPatterns:
20
+ - '^\s*#' # Allow long comments
21
+
22
+ Layout/EmptyLinesAroundClassBody:
23
+ Enabled: false
24
+
25
+ Layout/EmptyLinesAroundModuleBody:
26
+ Enabled: false
27
+
28
+ # Metrics
29
+ Metrics/MethodLength:
30
+ Max: 25
31
+ Exclude:
32
+ - 'spec/**/*'
33
+
34
+ Metrics/BlockLength:
35
+ Exclude:
36
+ - 'spec/**/*'
37
+ - 'Rakefile'
38
+
39
+ Metrics/ClassLength:
40
+ Max: 200
41
+
42
+ Metrics/AbcSize:
43
+ Max: 32
44
+
45
+ Metrics/CyclomaticComplexity:
46
+ Max: 12
47
+
48
+ Metrics/PerceivedComplexity:
49
+ Max: 12
50
+
51
+ # Data classes and API methods often need many parameters
52
+ # Run.new needs all trace attributes, trace() accepts many options
53
+ Metrics/ParameterLists:
54
+ Max: 8
55
+ CountKeywordArgs: false # Keyword args are self-documenting, less problematic
56
+
57
+ # Style
58
+ Style/Documentation:
59
+ Enabled: false
60
+
61
+ Style/FrozenStringLiteralComment:
62
+ Enabled: true
63
+
64
+ Style/StringLiterals:
65
+ EnforcedStyle: double_quotes
66
+
67
+ Style/StringLiteralsInInterpolation:
68
+ EnforcedStyle: double_quotes
69
+
70
+ Style/TrailingCommaInHashLiteral:
71
+ Enabled: false
72
+
73
+ Style/TrailingCommaInArrayLiteral:
74
+ Enabled: false
75
+
76
+ Style/TrailingCommaInArguments:
77
+ Enabled: false
78
+
79
+ # Naming
80
+ Naming/MethodParameterName:
81
+ AllowedNames:
82
+ - id
83
+ - e
84
+
85
+ # Renamed from PredicateName in newer rubocop
86
+ Naming/PredicatePrefix:
87
+ Enabled: false
88
+
89
+ # Allow set_ prefix for explicit setter methods (set_inputs, set_outputs, set_token_usage)
90
+ # These are intentionally named to be explicit about mutation vs simple assignment
91
+ Naming/AccessorMethodName:
92
+ Enabled: false
93
+
94
+ # RSpec
95
+ RSpec/MultipleExpectations:
96
+ Max: 6
97
+
98
+ RSpec/ExampleLength:
99
+ Max: 15
100
+
101
+ RSpec/NestedGroups:
102
+ Max: 4
103
+
104
+ RSpec/DescribeClass:
105
+ Exclude:
106
+ - 'spec/langsmith_spec.rb'
107
+
108
+ RSpec/MessageSpies:
109
+ EnforcedStyle: have_received
110
+
111
+ RSpec/VerifiedDoubles:
112
+ Enabled: true
113
+
114
+ # Numbered let names like run1, run2 are clear in batch processing tests
115
+ RSpec/IndexedLet:
116
+ Enabled: false
117
+
118
+ # receive_messages is often less readable than separate stubs
119
+ RSpec/ReceiveMessages:
120
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.1] - 2025-12-21
11
+
12
+ ### Added
13
+
14
+ - Per-trace `project` parameter to override the default project at runtime
15
+ - Child traces automatically inherit project from parent (enforced)
16
+
17
+ ## [0.1.0] - 2025-12-21
18
+
19
+ ### Added
20
+
21
+ - Initial release of the LangSmith Ruby SDK
22
+ - Block-based tracing with `Langsmith.trace`
23
+ - Method decoration with `Langsmith::Traceable` module
24
+ - Automatic parent-child trace linking for nested traces
25
+ - Thread-safe batch processing with background worker
26
+ - Thread-local context for proper isolation in concurrent environments
27
+ - Multi-tenant support with per-trace `tenant_id` override
28
+ - Token usage tracking with `set_token_usage`
29
+ - Model metadata with `set_model`
30
+ - Streaming metrics with `set_streaming_metrics`
31
+ - Event tracking with `add_event`
32
+ - Configurable via environment variables or programmatic configuration
33
+ - Automatic retry with exponential backoff for failed API requests
34
+ - Graceful shutdown with `at_exit` hook
35
+
36
+ ### Run Types
37
+
38
+ - `chain` - A sequence of operations
39
+ - `llm` - LLM API calls
40
+ - `tool` - Tool/function executions
41
+ - `retriever` - Document retrieval operations
42
+ - `prompt` - Prompt template rendering
43
+ - `parser` - Output parsing operations
44
+
45
+ [Unreleased]: https://github.com/felipekb/langsmith-ruby-sdk/compare/v0.1.1...HEAD
46
+ [0.1.1]: https://github.com/felipekb/langsmith-ruby-sdk/compare/v0.1.0...v0.1.1
47
+ [0.1.0]: https://github.com/felipekb/langsmith-ruby-sdk/releases/tag/v0.1.0
48
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Felipe Cabezudo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # LangSmith Ruby SDK
2
+
3
+ A Ruby SDK for [LangSmith](https://smith.langchain.com/) tracing and observability.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'langsmith-sdk'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install langsmith-sdk
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ Set up your LangSmith credentials via environment variables:
28
+
29
+ ```bash
30
+ export LANGSMITH_API_KEY=ls_...
31
+ export LANGSMITH_TRACING=true
32
+ export LANGSMITH_PROJECT=my-project # optional, defaults to "default"
33
+ export LANGSMITH_TENANT_ID=tenant-123 # optional, for multi-tenant scenarios
34
+ ```
35
+
36
+ Or configure programmatically:
37
+
38
+ ```ruby
39
+ Langsmith.configure do |config|
40
+ config.api_key = "ls_..."
41
+ config.tracing_enabled = true
42
+ config.project = "my-project"
43
+ config.tenant_id = "tenant-123" # optional, for multi-tenant scenarios
44
+ end
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### Block-based Tracing
50
+
51
+ ```ruby
52
+ require "langsmith"
53
+
54
+ result = Langsmith.trace("my_operation", run_type: "chain") do |run|
55
+ run.add_metadata(user_id: "123")
56
+
57
+ # Your code here
58
+ response = call_llm(prompt)
59
+
60
+ response
61
+ end
62
+ ```
63
+
64
+ ### Nested Traces
65
+
66
+ Traces automatically nest when called within other traces:
67
+
68
+ ```ruby
69
+ Langsmith.trace("parent_chain", run_type: "chain") do
70
+ # This will be a child of parent_chain
71
+ Langsmith.trace("child_llm_call", run_type: "llm") do
72
+ call_openai(prompt)
73
+ end
74
+
75
+ # Another child
76
+ Langsmith.trace("child_tool", run_type: "tool") do
77
+ search_database(query)
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### Method Decoration with Traceable
83
+
84
+ ```ruby
85
+ class MyService
86
+ include Langsmith::Traceable
87
+
88
+ traceable run_type: "chain"
89
+ def process(input)
90
+ # This method is automatically traced
91
+ transform(input)
92
+ end
93
+
94
+ traceable run_type: "llm", name: "openai_call"
95
+ def call_llm(prompt)
96
+ # Traced with custom name
97
+ client.chat(prompt)
98
+ end
99
+ end
100
+ ```
101
+
102
+ ## Run Types
103
+
104
+ Supported run types:
105
+ - `"chain"` - A sequence of operations
106
+ - `"llm"` - LLM API calls
107
+ - `"tool"` - Tool/function executions
108
+ - `"retriever"` - Document retrieval operations
109
+ - `"prompt"` - Prompt template rendering
110
+ - `"parser"` - Output parsing operations
111
+
112
+ ## Per-Trace Project Override
113
+
114
+ You can override the project for specific traces at runtime:
115
+
116
+ ```ruby
117
+ # Override project for a specific trace (and its children)
118
+ Langsmith.trace("operation", project: "my-special-project") do
119
+ # This trace goes to "my-special-project"
120
+
121
+ # Nested traces inherit project from parent automatically
122
+ Langsmith.trace("child") do
123
+ # Also goes to "my-special-project"
124
+ end
125
+ end
126
+ ```
127
+
128
+
129
+ ## Multi-Tenant Support
130
+
131
+ For multi-tenant scenarios, you can set a global tenant ID or override it per-trace:
132
+
133
+ ### Global Configuration
134
+
135
+ ```ruby
136
+ Langsmith.configure do |config|
137
+ config.tenant_id = "tenant-123"
138
+ end
139
+
140
+ # All traces will use tenant-123
141
+ Langsmith.trace("operation") do
142
+ # ...
143
+ end
144
+ ```
145
+
146
+ ### Per-Trace Override
147
+
148
+ ```ruby
149
+ # Override tenant for a specific trace (and its children)
150
+ Langsmith.trace("operation", tenant_id: "tenant-456") do
151
+ # This trace goes to tenant-456
152
+
153
+ # Nested traces inherit tenant_id from parent
154
+ Langsmith.trace("child") do
155
+ # Also goes to tenant-456
156
+ end
157
+ end
158
+ ```
159
+
160
+ ### With Traceable Module
161
+
162
+ ```ruby
163
+ class MultiTenantService
164
+ include Langsmith::Traceable
165
+
166
+ traceable run_type: "chain", tenant_id: "tenant-123"
167
+ def process_for_tenant_123(data)
168
+ # Always traced to tenant-123
169
+ end
170
+
171
+ traceable run_type: "chain", tenant_id: "tenant-456"
172
+ def process_for_tenant_456(data)
173
+ # Always traced to tenant-456
174
+ end
175
+ end
176
+ ```
177
+
178
+ The SDK automatically batches traces by tenant ID, so traces for different tenants are sent in separate API requests with the appropriate `X-Tenant-Id` header.
179
+
180
+ ## Token Usage Tracking
181
+
182
+ Track token usage for LLM calls:
183
+
184
+ ```ruby
185
+ Langsmith.trace("openai_call", run_type: "llm") do |run|
186
+ response = openai_client.chat(parameters: { model: "gpt-4", messages: messages })
187
+
188
+ # Set model info (displayed in LangSmith UI)
189
+ run.set_model(model: "gpt-4", provider: "openai")
190
+
191
+ # Set token usage from API response
192
+ run.set_token_usage(
193
+ input_tokens: response["usage"]["prompt_tokens"],
194
+ output_tokens: response["usage"]["completion_tokens"],
195
+ total_tokens: response["usage"]["total_tokens"]
196
+ )
197
+
198
+ # Add additional metadata
199
+ run.add_metadata(finish_reason: response.dig("choices", 0, "finish_reason"))
200
+
201
+ response.dig("choices", 0, "message", "content")
202
+ end
203
+ ```
204
+
205
+ ## Examples
206
+
207
+ See [`examples/LLM_TRACING.md`](examples/LLM_TRACING.md) for comprehensive examples including:
208
+
209
+ - Basic LLM calls with token usage
210
+ - Streaming responses
211
+ - Multi-step chains (RAG)
212
+ - OpenAI and Anthropic integrations
213
+ - Error handling and retries
214
+ - Multi-tenant tracing
215
+ - Per-trace project overrides
216
+
217
+ ## Development
218
+
219
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
220
+
221
+ ## License
222
+
223
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
224
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec