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 +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +120 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +48 -0
- data/LICENSE +22 -0
- data/README.md +224 -0
- data/Rakefile +8 -0
- data/examples/LLM_TRACING.md +439 -0
- data/examples/complex_agent.rb +472 -0
- data/examples/llm_tracing.rb +304 -0
- data/examples/openai_integration.rb +751 -0
- data/langsmith.gemspec +38 -0
- data/lib/langsmith/batch_processor.rb +237 -0
- data/lib/langsmith/client.rb +181 -0
- data/lib/langsmith/configuration.rb +96 -0
- data/lib/langsmith/context.rb +73 -0
- data/lib/langsmith/errors.rb +13 -0
- data/lib/langsmith/railtie.rb +86 -0
- data/lib/langsmith/run.rb +320 -0
- data/lib/langsmith/run_tree.rb +154 -0
- data/lib/langsmith/traceable.rb +120 -0
- data/lib/langsmith/version.rb +5 -0
- data/lib/langsmith.rb +144 -0
- metadata +134 -0
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
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
|
+
|