langsmithrb 0.1.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 +7 -0
- data/README.md +626 -0
- data/Rakefile +8 -0
- data/lib/langsmith/client.rb +1023 -0
- data/lib/langsmith/dataset.rb +177 -0
- data/lib/langsmith/evaluation.rb +101 -0
- data/lib/langsmith/feedback.rb +43 -0
- data/lib/langsmith/project.rb +40 -0
- data/lib/langsmith/run.rb +114 -0
- data/lib/langsmith/trace.rb +96 -0
- data/lib/langsmith/version.rb +6 -0
- data/lib/langsmith.rb +86 -0
- data/lib/langsmithrb.rb +4 -0
- metadata +211 -0
@@ -0,0 +1,1023 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Langsmith
|
7
|
+
class Client
|
8
|
+
attr_reader :api_key, :api_url
|
9
|
+
|
10
|
+
# Initialize a new LangSmith client
|
11
|
+
#
|
12
|
+
# @param api_key [String] LangSmith API key
|
13
|
+
# @param api_url [String] LangSmith API URL
|
14
|
+
def initialize(api_key: nil, api_url: nil)
|
15
|
+
@api_key = api_key || ENV["LANGSMITH_API_KEY"] || Langsmith.api_key
|
16
|
+
@api_url = api_url || ENV["LANGSMITH_API_URL"] || Langsmith.api_url || Langsmith::DEFAULT_API_URL
|
17
|
+
|
18
|
+
raise Langsmith::Errors::AuthenticationError, "API key is required" unless @api_key
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create a new run
|
22
|
+
#
|
23
|
+
# @param name [String] Name of the run
|
24
|
+
# @param run_type [String] Type of run (e.g., llm, chain, tool)
|
25
|
+
# @param project_name [String] Name of the project
|
26
|
+
# @param inputs [Hash] Input values for the run
|
27
|
+
# @param extra [Hash] Additional metadata for the run
|
28
|
+
# @return [Langsmith::Run] The created run
|
29
|
+
def create_run(name:, run_type:, project_name: nil, inputs: {}, extra: {})
|
30
|
+
data = {
|
31
|
+
name: name,
|
32
|
+
run_type: run_type,
|
33
|
+
inputs: inputs,
|
34
|
+
extra: extra
|
35
|
+
}
|
36
|
+
data[:project_name] = project_name if project_name
|
37
|
+
|
38
|
+
response = post("/runs", data)
|
39
|
+
Langsmith::Run.new(self, response)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Update a run
|
43
|
+
#
|
44
|
+
# @param run_id [String] ID of the run to update
|
45
|
+
# @param outputs [Hash] Output values from the run
|
46
|
+
# @param end_time [Time] End time of the run
|
47
|
+
# @param error [String] Error message if the run failed
|
48
|
+
# @return [Langsmith::Run] The updated run
|
49
|
+
def update_run(run_id:, outputs: nil, end_time: nil, error: nil)
|
50
|
+
data = {}
|
51
|
+
data[:outputs] = outputs if outputs
|
52
|
+
data[:end_time] = end_time.iso8601 if end_time
|
53
|
+
data[:error] = error if error
|
54
|
+
|
55
|
+
response = patch("/runs/#{run_id}", data)
|
56
|
+
Langsmith::Run.new(self, response)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get a run by ID
|
60
|
+
#
|
61
|
+
# @param run_id [String] ID of the run to retrieve
|
62
|
+
# @return [Langsmith::Run] The requested run
|
63
|
+
def get_run(run_id:)
|
64
|
+
response = get("/runs/#{run_id}")
|
65
|
+
Langsmith::Run.new(self, response)
|
66
|
+
end
|
67
|
+
|
68
|
+
# List runs with optional filters
|
69
|
+
#
|
70
|
+
# @param project_name [String] Filter by project name
|
71
|
+
# @param run_type [String] Filter by run type
|
72
|
+
# @param limit [Integer] Maximum number of runs to return
|
73
|
+
# @return [Array<Langsmith::Run>] List of runs
|
74
|
+
def list_runs(project_name: nil, run_type: nil, limit: 100)
|
75
|
+
params = {}
|
76
|
+
params[:project_name] = project_name if project_name
|
77
|
+
params[:run_type] = run_type if run_type
|
78
|
+
params[:limit] = limit
|
79
|
+
|
80
|
+
response = get("/runs", params)
|
81
|
+
response.map { |run_data| Langsmith::Run.new(self, run_data) }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create a new project
|
85
|
+
#
|
86
|
+
# @param name [String] Name of the project
|
87
|
+
# @param description [String] Description of the project
|
88
|
+
# @return [Langsmith::Project] The created project
|
89
|
+
def create_project(name:, description: nil)
|
90
|
+
data = { name: name }
|
91
|
+
data[:description] = description if description
|
92
|
+
|
93
|
+
response = post("/projects", data)
|
94
|
+
Langsmith::Project.new(self, response)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get a project by name
|
98
|
+
#
|
99
|
+
# @param name [String] Name of the project to retrieve
|
100
|
+
# @return [Langsmith::Project] The requested project
|
101
|
+
def get_project(name:)
|
102
|
+
response = get("/projects/#{name}")
|
103
|
+
Langsmith::Project.new(self, response)
|
104
|
+
end
|
105
|
+
|
106
|
+
# List projects
|
107
|
+
#
|
108
|
+
# @param limit [Integer] Maximum number of projects to return
|
109
|
+
# @return [Array<Langsmith::Project>] List of projects
|
110
|
+
def list_projects(limit: 100)
|
111
|
+
params = { limit: limit }
|
112
|
+
response = get("/projects", params)
|
113
|
+
response.map { |project_data| Langsmith::Project.new(self, project_data) }
|
114
|
+
end
|
115
|
+
|
116
|
+
# Create a new dataset
|
117
|
+
#
|
118
|
+
# @param name [String] Name of the dataset
|
119
|
+
# @param description [String] Description of the dataset
|
120
|
+
# @return [Langsmith::Dataset] The created dataset
|
121
|
+
def create_dataset(name:, description: nil)
|
122
|
+
data = { name: name }
|
123
|
+
data[:description] = description if description
|
124
|
+
|
125
|
+
response = post("/datasets", data)
|
126
|
+
Langsmith::Dataset.new(self, response)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get a dataset by name
|
130
|
+
#
|
131
|
+
# @param name [String] Name of the dataset to retrieve
|
132
|
+
# @return [Langsmith::Dataset] The requested dataset
|
133
|
+
def get_dataset(name:)
|
134
|
+
response = get("/datasets/#{name}")
|
135
|
+
Langsmith::Dataset.new(self, response)
|
136
|
+
end
|
137
|
+
|
138
|
+
# List datasets
|
139
|
+
#
|
140
|
+
# @param limit [Integer] Maximum number of datasets to return
|
141
|
+
# @param offset [Integer] Number of datasets to skip
|
142
|
+
# @param name [String, nil] Filter by dataset name (optional)
|
143
|
+
# @param name_contains [String, nil] Filter by dataset name containing string (optional)
|
144
|
+
# @return [Array<Langsmith::Dataset>] List of datasets
|
145
|
+
def list_datasets(limit: 100, offset: 0, name: nil, name_contains: nil)
|
146
|
+
params = {
|
147
|
+
limit: limit,
|
148
|
+
offset: offset
|
149
|
+
}
|
150
|
+
params[:name] = name if name
|
151
|
+
params[:name_contains] = name_contains if name_contains
|
152
|
+
|
153
|
+
response = get("/datasets", params)
|
154
|
+
response.map { |dataset_data| Langsmith::Dataset.new(self, dataset_data) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Get a dataset by ID
|
158
|
+
#
|
159
|
+
# @param id [String] ID of the dataset to retrieve
|
160
|
+
# @return [Langsmith::Dataset] The requested dataset
|
161
|
+
def get_dataset_by_id(id:)
|
162
|
+
response = get("/datasets/#{id}")
|
163
|
+
Langsmith::Dataset.new(self, response)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Update a dataset
|
167
|
+
#
|
168
|
+
# @param id [String] ID of the dataset to update
|
169
|
+
# @param name [String, nil] New name for the dataset (optional)
|
170
|
+
# @param description [String, nil] New description for the dataset (optional)
|
171
|
+
# @return [Langsmith::Dataset] The updated dataset
|
172
|
+
def update_dataset(id:, name: nil, description: nil)
|
173
|
+
data = {}
|
174
|
+
data[:name] = name if name
|
175
|
+
data[:description] = description if description
|
176
|
+
|
177
|
+
response = patch("/datasets/#{id}", data)
|
178
|
+
Langsmith::Dataset.new(self, response)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Delete a dataset
|
182
|
+
#
|
183
|
+
# @param id [String] ID of the dataset to delete
|
184
|
+
# @return [Boolean] True if successful
|
185
|
+
def delete_dataset(id:)
|
186
|
+
delete("/datasets/#{id}")
|
187
|
+
true
|
188
|
+
end
|
189
|
+
|
190
|
+
# Get an example by ID
|
191
|
+
#
|
192
|
+
# @param id [String] ID of the example to retrieve
|
193
|
+
# @return [Langsmith::Example] The requested example
|
194
|
+
def get_example(id:)
|
195
|
+
response = get("/examples/#{id}")
|
196
|
+
Langsmith::Example.new(self, response)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Update an example
|
200
|
+
#
|
201
|
+
# @param id [String] ID of the example to update
|
202
|
+
# @param inputs [Hash, nil] New input values (optional)
|
203
|
+
# @param outputs [Hash, nil] New output values (optional)
|
204
|
+
# @param metadata [Hash, nil] New metadata (optional)
|
205
|
+
# @return [Langsmith::Example] The updated example
|
206
|
+
def update_example(id:, inputs: nil, outputs: nil, metadata: nil)
|
207
|
+
data = {}
|
208
|
+
data[:inputs] = inputs if inputs
|
209
|
+
data[:outputs] = outputs if outputs
|
210
|
+
data[:metadata] = metadata if metadata
|
211
|
+
|
212
|
+
response = patch("/examples/#{id}", data)
|
213
|
+
Langsmith::Example.new(self, response)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Delete an example
|
217
|
+
#
|
218
|
+
# @param id [String] ID of the example to delete
|
219
|
+
# @return [Boolean] True if successful
|
220
|
+
def delete_example(id:)
|
221
|
+
delete("/examples/#{id}")
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
# Create multiple examples in batch
|
226
|
+
#
|
227
|
+
# @param dataset_id [String] ID of the dataset to add examples to
|
228
|
+
# @param examples [Array<Hash>] Array of example data, each containing :inputs and optionally :outputs and :metadata
|
229
|
+
# @return [Array<Langsmith::Example>] The created examples
|
230
|
+
def create_examples_batch(dataset_id:, examples:)
|
231
|
+
data = examples.map do |example|
|
232
|
+
example_data = {
|
233
|
+
dataset_id: dataset_id,
|
234
|
+
inputs: example[:inputs]
|
235
|
+
}
|
236
|
+
example_data[:outputs] = example[:outputs] if example[:outputs]
|
237
|
+
example_data[:metadata] = example[:metadata] if example[:metadata]
|
238
|
+
example_data
|
239
|
+
end
|
240
|
+
|
241
|
+
response = post("/examples/batch", { examples: data })
|
242
|
+
response.map { |example_data| Langsmith::Example.new(self, example_data) }
|
243
|
+
end
|
244
|
+
|
245
|
+
# Get an evaluation by ID
|
246
|
+
#
|
247
|
+
# @param id [String] ID of the evaluation to retrieve
|
248
|
+
# @return [Langsmith::Evaluation] The requested evaluation
|
249
|
+
def get_evaluation(id:)
|
250
|
+
response = get("/evaluations/#{id}")
|
251
|
+
Langsmith::Evaluation.new(self, response)
|
252
|
+
end
|
253
|
+
|
254
|
+
# List evaluations with optional filters
|
255
|
+
#
|
256
|
+
# @param dataset_id [String, nil] Filter by dataset ID (optional)
|
257
|
+
# @param evaluator_name [String, nil] Filter by evaluator name (optional)
|
258
|
+
# @param run_id [String, nil] Filter by run ID (optional)
|
259
|
+
# @param status [String, nil] Filter by status (optional)
|
260
|
+
# @param limit [Integer] Maximum number of evaluations to return
|
261
|
+
# @param offset [Integer] Number of evaluations to skip
|
262
|
+
# @return [Array<Langsmith::Evaluation>] List of evaluations
|
263
|
+
def list_evaluations(dataset_id: nil, evaluator_name: nil, run_id: nil, status: nil, limit: 100, offset: 0)
|
264
|
+
params = {
|
265
|
+
limit: limit,
|
266
|
+
offset: offset
|
267
|
+
}
|
268
|
+
params[:dataset_id] = dataset_id if dataset_id
|
269
|
+
params[:evaluator_name] = evaluator_name if evaluator_name
|
270
|
+
params[:run_id] = run_id if run_id
|
271
|
+
params[:status] = status if status
|
272
|
+
|
273
|
+
response = get("/evaluations", params)
|
274
|
+
response.map { |eval_data| Langsmith::Evaluation.new(self, eval_data) }
|
275
|
+
end
|
276
|
+
|
277
|
+
# Update an evaluation's metadata
|
278
|
+
#
|
279
|
+
# @param id [String] ID of the evaluation to update
|
280
|
+
# @param metadata [Hash] New metadata for the evaluation
|
281
|
+
# @return [Langsmith::Evaluation] The updated evaluation
|
282
|
+
def update_evaluation_metadata(id:, metadata:)
|
283
|
+
data = { metadata: metadata }
|
284
|
+
response = patch("/evaluations/#{id}", data)
|
285
|
+
Langsmith::Evaluation.new(self, response)
|
286
|
+
end
|
287
|
+
|
288
|
+
# Cancel an evaluation
|
289
|
+
#
|
290
|
+
# @param id [String] ID of the evaluation to cancel
|
291
|
+
# @return [Langsmith::Evaluation] The canceled evaluation
|
292
|
+
def cancel_evaluation(id:)
|
293
|
+
response = post("/evaluations/#{id}/cancel")
|
294
|
+
Langsmith::Evaluation.new(self, response)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Delete an evaluation
|
298
|
+
#
|
299
|
+
# @param id [String] ID of the evaluation to delete
|
300
|
+
# @return [Boolean] True if successful
|
301
|
+
def delete_evaluation(id:)
|
302
|
+
delete("/evaluations/#{id}")
|
303
|
+
true
|
304
|
+
end
|
305
|
+
|
306
|
+
# Create feedback for a run
|
307
|
+
#
|
308
|
+
# @param run_id [String] ID of the run to provide feedback for
|
309
|
+
# @param key [String] Feedback key (e.g., "correctness", "helpfulness")
|
310
|
+
# @param score [Float] Feedback score (typically 0.0 to 1.0)
|
311
|
+
# @param comment [String] Optional comment with the feedback
|
312
|
+
# @return [Langsmith::Feedback] The created feedback
|
313
|
+
def create_feedback(run_id:, key:, score:, comment: nil)
|
314
|
+
data = {
|
315
|
+
run_id: run_id,
|
316
|
+
key: key,
|
317
|
+
score: score
|
318
|
+
}
|
319
|
+
data[:comment] = comment if comment
|
320
|
+
|
321
|
+
response = post("/feedback", data)
|
322
|
+
Langsmith::Feedback.new(self, response)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Get feedback for a run
|
326
|
+
#
|
327
|
+
# @param run_id [String] ID of the run to get feedback for
|
328
|
+
# @return [Array<Langsmith::Feedback>] List of feedback for the run
|
329
|
+
def get_feedback(run_id:)
|
330
|
+
params = { run_id: run_id }
|
331
|
+
response = get("/feedback", params)
|
332
|
+
response.map { |feedback_data| Langsmith::Feedback.new(self, feedback_data) }
|
333
|
+
end
|
334
|
+
|
335
|
+
# Create a new tracer session
|
336
|
+
#
|
337
|
+
# @param name [String] Name of the tracer session
|
338
|
+
# @param metadata [Hash] Additional metadata for the tracer session
|
339
|
+
# @return [Hash] The created tracer session data
|
340
|
+
def create_tracer_session(name:, metadata: {})
|
341
|
+
data = {
|
342
|
+
name: name,
|
343
|
+
metadata: metadata
|
344
|
+
}
|
345
|
+
post("/tracer-sessions", data)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Get a tracer session by ID
|
349
|
+
#
|
350
|
+
# @param id [String] ID of the tracer session to retrieve
|
351
|
+
# @return [Hash] The requested tracer session data
|
352
|
+
def get_tracer_session(id:)
|
353
|
+
get("/tracer-sessions/#{id}")
|
354
|
+
end
|
355
|
+
|
356
|
+
# List tracer sessions with optional filters
|
357
|
+
#
|
358
|
+
# @param limit [Integer] Maximum number of tracer sessions to return
|
359
|
+
# @param offset [Integer] Number of tracer sessions to skip
|
360
|
+
# @return [Array<Hash>] List of tracer sessions
|
361
|
+
def list_tracer_sessions(limit: 100, offset: 0)
|
362
|
+
params = {
|
363
|
+
limit: limit,
|
364
|
+
offset: offset
|
365
|
+
}
|
366
|
+
get("/tracer-sessions", params)
|
367
|
+
end
|
368
|
+
|
369
|
+
# Get current organization information
|
370
|
+
#
|
371
|
+
# @return [Hash] Organization information
|
372
|
+
def get_current_organization
|
373
|
+
get("/orgs/current")
|
374
|
+
end
|
375
|
+
|
376
|
+
# List organizations the user belongs to
|
377
|
+
#
|
378
|
+
# @return [Array<Hash>] List of organizations
|
379
|
+
def list_organizations
|
380
|
+
get("/orgs")
|
381
|
+
end
|
382
|
+
|
383
|
+
# Get organization by ID
|
384
|
+
#
|
385
|
+
# @param id [String] ID of the organization to retrieve
|
386
|
+
# @return [Hash] The requested organization data
|
387
|
+
def get_organization(id:)
|
388
|
+
get("/orgs/#{id}")
|
389
|
+
end
|
390
|
+
|
391
|
+
# List API keys
|
392
|
+
#
|
393
|
+
# @return [Array<Hash>] List of API keys
|
394
|
+
def list_api_keys
|
395
|
+
get("/api-key")
|
396
|
+
end
|
397
|
+
|
398
|
+
# Create a new API key
|
399
|
+
#
|
400
|
+
# @param name [String] Name of the API key
|
401
|
+
# @param expires_at [Time, nil] Expiration time for the API key (optional)
|
402
|
+
# @return [Hash] The created API key data
|
403
|
+
def create_api_key(name:, expires_at: nil)
|
404
|
+
data = { name: name }
|
405
|
+
data[:expires_at] = expires_at.iso8601 if expires_at
|
406
|
+
post("/api-key", data)
|
407
|
+
end
|
408
|
+
|
409
|
+
# Delete an API key
|
410
|
+
#
|
411
|
+
# @param id [String] ID of the API key to delete
|
412
|
+
# @return [Boolean] True if successful
|
413
|
+
def delete_api_key(id:)
|
414
|
+
delete("/api-key/#{id}")
|
415
|
+
true
|
416
|
+
end
|
417
|
+
|
418
|
+
# Get tenant information
|
419
|
+
#
|
420
|
+
# @param id [String] ID of the tenant to retrieve
|
421
|
+
# @return [Hash] The requested tenant data
|
422
|
+
def get_tenant(id:)
|
423
|
+
get("/tenant/#{id}")
|
424
|
+
end
|
425
|
+
|
426
|
+
# List tenants
|
427
|
+
#
|
428
|
+
# @return [Array<Hash>] List of tenants
|
429
|
+
def list_tenants
|
430
|
+
get("/tenant")
|
431
|
+
end
|
432
|
+
|
433
|
+
# Create a new tenant
|
434
|
+
#
|
435
|
+
# @param name [String] Name of the tenant
|
436
|
+
# @param config [Hash] Configuration for the tenant
|
437
|
+
# @return [Hash] The created tenant data
|
438
|
+
def create_tenant(name:, config: {})
|
439
|
+
data = {
|
440
|
+
name: name,
|
441
|
+
config: config
|
442
|
+
}
|
443
|
+
post("/tenant", data)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Update a tenant
|
447
|
+
#
|
448
|
+
# @param id [String] ID of the tenant to update
|
449
|
+
# @param name [String, nil] New name for the tenant (optional)
|
450
|
+
# @param config [Hash, nil] New configuration for the tenant (optional)
|
451
|
+
# @return [Hash] The updated tenant data
|
452
|
+
def update_tenant(id:, name: nil, config: nil)
|
453
|
+
data = {}
|
454
|
+
data[:name] = name if name
|
455
|
+
data[:config] = config if config
|
456
|
+
patch("/tenant/#{id}", data)
|
457
|
+
end
|
458
|
+
|
459
|
+
# Create a new tag
|
460
|
+
#
|
461
|
+
# @param name [String] Name of the tag
|
462
|
+
# @return [Hash] The created tag data
|
463
|
+
def create_tag(name:)
|
464
|
+
data = { name: name }
|
465
|
+
post("/tags", data)
|
466
|
+
end
|
467
|
+
|
468
|
+
# Get a tag by ID
|
469
|
+
#
|
470
|
+
# @param id [String] ID of the tag to retrieve
|
471
|
+
# @return [Hash] The requested tag data
|
472
|
+
def get_tag(id:)
|
473
|
+
get("/tags/#{id}")
|
474
|
+
end
|
475
|
+
|
476
|
+
# List tags
|
477
|
+
#
|
478
|
+
# @param limit [Integer] Maximum number of tags to return
|
479
|
+
# @param offset [Integer] Number of tags to skip
|
480
|
+
# @return [Array<Hash>] List of tags
|
481
|
+
def list_tags(limit: 100, offset: 0)
|
482
|
+
params = {
|
483
|
+
limit: limit,
|
484
|
+
offset: offset
|
485
|
+
}
|
486
|
+
get("/tags", params)
|
487
|
+
end
|
488
|
+
|
489
|
+
# Delete a tag
|
490
|
+
#
|
491
|
+
# @param id [String] ID of the tag to delete
|
492
|
+
# @return [Boolean] True if successful
|
493
|
+
def delete_tag(id:)
|
494
|
+
delete("/tags/#{id}")
|
495
|
+
true
|
496
|
+
end
|
497
|
+
|
498
|
+
# Create a new prompt
|
499
|
+
#
|
500
|
+
# @param name [String] Name of the prompt
|
501
|
+
# @param prompt_template [String] The prompt template
|
502
|
+
# @param metadata [Hash] Additional metadata for the prompt
|
503
|
+
# @return [Hash] The created prompt data
|
504
|
+
def create_prompt(name:, prompt_template:, metadata: {})
|
505
|
+
data = {
|
506
|
+
name: name,
|
507
|
+
prompt_template: prompt_template,
|
508
|
+
metadata: metadata
|
509
|
+
}
|
510
|
+
post("/prompts", data)
|
511
|
+
end
|
512
|
+
|
513
|
+
# Get a prompt by ID
|
514
|
+
#
|
515
|
+
# @param id [String] ID of the prompt to retrieve
|
516
|
+
# @return [Hash] The requested prompt data
|
517
|
+
def get_prompt(id:)
|
518
|
+
get("/prompts/#{id}")
|
519
|
+
end
|
520
|
+
|
521
|
+
# List prompts
|
522
|
+
#
|
523
|
+
# @param limit [Integer] Maximum number of prompts to return
|
524
|
+
# @param offset [Integer] Number of prompts to skip
|
525
|
+
# @return [Array<Hash>] List of prompts
|
526
|
+
def list_prompts(limit: 100, offset: 0)
|
527
|
+
params = {
|
528
|
+
limit: limit,
|
529
|
+
offset: offset
|
530
|
+
}
|
531
|
+
get("/prompts", params)
|
532
|
+
end
|
533
|
+
|
534
|
+
# Update a prompt
|
535
|
+
#
|
536
|
+
# @param id [String] ID of the prompt to update
|
537
|
+
# @param name [String, nil] New name for the prompt (optional)
|
538
|
+
# @param prompt_template [String, nil] New prompt template (optional)
|
539
|
+
# @param metadata [Hash, nil] New metadata for the prompt (optional)
|
540
|
+
# @return [Hash] The updated prompt data
|
541
|
+
def update_prompt(id:, name: nil, prompt_template: nil, metadata: nil)
|
542
|
+
data = {}
|
543
|
+
data[:name] = name if name
|
544
|
+
data[:prompt_template] = prompt_template if prompt_template
|
545
|
+
data[:metadata] = metadata if metadata
|
546
|
+
patch("/prompts/#{id}", data)
|
547
|
+
end
|
548
|
+
|
549
|
+
# Delete a prompt
|
550
|
+
#
|
551
|
+
# @param id [String] ID of the prompt to delete
|
552
|
+
# @return [Boolean] True if successful
|
553
|
+
def delete_prompt(id:)
|
554
|
+
delete("/prompts/#{id}")
|
555
|
+
true
|
556
|
+
end
|
557
|
+
|
558
|
+
# Create a new annotation queue
|
559
|
+
#
|
560
|
+
# @param name [String] Name of the annotation queue
|
561
|
+
# @param description [String] Description of the annotation queue
|
562
|
+
# @param metadata [Hash] Additional metadata for the annotation queue
|
563
|
+
# @return [Hash] The created annotation queue data
|
564
|
+
def create_annotation_queue(name:, description: nil, metadata: {})
|
565
|
+
data = {
|
566
|
+
name: name,
|
567
|
+
metadata: metadata
|
568
|
+
}
|
569
|
+
data[:description] = description if description
|
570
|
+
post("/annotation-queues", data)
|
571
|
+
end
|
572
|
+
|
573
|
+
# Get an annotation queue by ID
|
574
|
+
#
|
575
|
+
# @param id [String] ID of the annotation queue to retrieve
|
576
|
+
# @return [Hash] The requested annotation queue data
|
577
|
+
def get_annotation_queue(id:)
|
578
|
+
get("/annotation-queues/#{id}")
|
579
|
+
end
|
580
|
+
|
581
|
+
# List annotation queues
|
582
|
+
#
|
583
|
+
# @param limit [Integer] Maximum number of annotation queues to return
|
584
|
+
# @param offset [Integer] Number of annotation queues to skip
|
585
|
+
# @return [Array<Hash>] List of annotation queues
|
586
|
+
def list_annotation_queues(limit: 100, offset: 0)
|
587
|
+
params = {
|
588
|
+
limit: limit,
|
589
|
+
offset: offset
|
590
|
+
}
|
591
|
+
get("/annotation-queues", params)
|
592
|
+
end
|
593
|
+
|
594
|
+
# Update an annotation queue
|
595
|
+
#
|
596
|
+
# @param id [String] ID of the annotation queue to update
|
597
|
+
# @param name [String, nil] New name for the annotation queue (optional)
|
598
|
+
# @param description [String, nil] New description for the annotation queue (optional)
|
599
|
+
# @param metadata [Hash, nil] New metadata for the annotation queue (optional)
|
600
|
+
# @return [Hash] The updated annotation queue data
|
601
|
+
def update_annotation_queue(id:, name: nil, description: nil, metadata: nil)
|
602
|
+
data = {}
|
603
|
+
data[:name] = name if name
|
604
|
+
data[:description] = description if description
|
605
|
+
data[:metadata] = metadata if metadata
|
606
|
+
patch("/annotation-queues/#{id}", data)
|
607
|
+
end
|
608
|
+
|
609
|
+
# Delete an annotation queue
|
610
|
+
#
|
611
|
+
# @param id [String] ID of the annotation queue to delete
|
612
|
+
# @return [Boolean] True if successful
|
613
|
+
def delete_annotation_queue(id:)
|
614
|
+
delete("/annotation-queues/#{id}")
|
615
|
+
true
|
616
|
+
end
|
617
|
+
|
618
|
+
# Create a new feedback config
|
619
|
+
#
|
620
|
+
# @param name [String] Name of the feedback config
|
621
|
+
# @param type [String] Type of feedback config
|
622
|
+
# @param metadata [Hash] Additional metadata for the feedback config
|
623
|
+
# @return [Hash] The created feedback config data
|
624
|
+
def create_feedback_config(name:, type:, metadata: {})
|
625
|
+
data = {
|
626
|
+
name: name,
|
627
|
+
type: type,
|
628
|
+
metadata: metadata
|
629
|
+
}
|
630
|
+
post("/feedback-configs", data)
|
631
|
+
end
|
632
|
+
|
633
|
+
# Get a feedback config by ID
|
634
|
+
#
|
635
|
+
# @param id [String] ID of the feedback config to retrieve
|
636
|
+
# @return [Hash] The requested feedback config data
|
637
|
+
def get_feedback_config(id:)
|
638
|
+
get("/feedback-configs/#{id}")
|
639
|
+
end
|
640
|
+
|
641
|
+
# List feedback configs
|
642
|
+
#
|
643
|
+
# @param limit [Integer] Maximum number of feedback configs to return
|
644
|
+
# @param offset [Integer] Number of feedback configs to skip
|
645
|
+
# @return [Array<Hash>] List of feedback configs
|
646
|
+
def list_feedback_configs(limit: 100, offset: 0)
|
647
|
+
params = {
|
648
|
+
limit: limit,
|
649
|
+
offset: offset
|
650
|
+
}
|
651
|
+
get("/feedback-configs", params)
|
652
|
+
end
|
653
|
+
|
654
|
+
# Update a feedback config
|
655
|
+
#
|
656
|
+
# @param id [String] ID of the feedback config to update
|
657
|
+
# @param name [String, nil] New name for the feedback config (optional)
|
658
|
+
# @param type [String, nil] New type for the feedback config (optional)
|
659
|
+
# @param metadata [Hash, nil] New metadata for the feedback config (optional)
|
660
|
+
# @return [Hash] The updated feedback config data
|
661
|
+
def update_feedback_config(id:, name: nil, type: nil, metadata: nil)
|
662
|
+
data = {}
|
663
|
+
data[:name] = name if name
|
664
|
+
data[:type] = type if type
|
665
|
+
data[:metadata] = metadata if metadata
|
666
|
+
patch("/feedback-configs/#{id}", data)
|
667
|
+
end
|
668
|
+
|
669
|
+
# Delete a feedback config
|
670
|
+
#
|
671
|
+
# @param id [String] ID of the feedback config to delete
|
672
|
+
# @return [Boolean] True if successful
|
673
|
+
def delete_feedback_config(id:)
|
674
|
+
delete("/feedback-configs/#{id}")
|
675
|
+
true
|
676
|
+
end
|
677
|
+
|
678
|
+
# Get usage limits
|
679
|
+
#
|
680
|
+
# @return [Hash] Usage limits information
|
681
|
+
def get_usage_limits
|
682
|
+
get("/usage-limits")
|
683
|
+
end
|
684
|
+
|
685
|
+
# Get API info
|
686
|
+
#
|
687
|
+
# @return [Hash] API information
|
688
|
+
def get_api_info
|
689
|
+
get("/info")
|
690
|
+
end
|
691
|
+
|
692
|
+
# Get analytics for a project
|
693
|
+
#
|
694
|
+
# @param project_id [String] ID of the project to get analytics for
|
695
|
+
# @param start_time [Time, nil] Start time for analytics (optional)
|
696
|
+
# @param end_time [Time, nil] End time for analytics (optional)
|
697
|
+
# @param group_by [String, nil] Field to group analytics by (optional)
|
698
|
+
# @param metrics [Array<String>, nil] Metrics to include (optional)
|
699
|
+
# @param filters [Hash, nil] Filters to apply (optional)
|
700
|
+
# @return [Hash] Analytics data
|
701
|
+
def get_project_analytics(project_id:, start_time: nil, end_time: nil, group_by: nil, metrics: nil, filters: nil)
|
702
|
+
params = {}
|
703
|
+
params[:start_time] = start_time.iso8601 if start_time
|
704
|
+
params[:end_time] = end_time.iso8601 if end_time
|
705
|
+
params[:group_by] = group_by if group_by
|
706
|
+
params[:metrics] = metrics if metrics
|
707
|
+
params[:filters] = filters if filters
|
708
|
+
|
709
|
+
get("/projects/#{project_id}/analytics", params)
|
710
|
+
end
|
711
|
+
|
712
|
+
# Get analytics for a dataset
|
713
|
+
#
|
714
|
+
# @param dataset_id [String] ID of the dataset to get analytics for
|
715
|
+
# @param start_time [Time, nil] Start time for analytics (optional)
|
716
|
+
# @param end_time [Time, nil] End time for analytics (optional)
|
717
|
+
# @param group_by [String, nil] Field to group analytics by (optional)
|
718
|
+
# @param metrics [Array<String>, nil] Metrics to include (optional)
|
719
|
+
# @param filters [Hash, nil] Filters to apply (optional)
|
720
|
+
# @return [Hash] Analytics data
|
721
|
+
def get_dataset_analytics(dataset_id:, start_time: nil, end_time: nil, group_by: nil, metrics: nil, filters: nil)
|
722
|
+
params = {}
|
723
|
+
params[:start_time] = start_time.iso8601 if start_time
|
724
|
+
params[:end_time] = end_time.iso8601 if end_time
|
725
|
+
params[:group_by] = group_by if group_by
|
726
|
+
params[:metrics] = metrics if metrics
|
727
|
+
params[:filters] = filters if filters
|
728
|
+
|
729
|
+
get("/datasets/#{dataset_id}/analytics", params)
|
730
|
+
end
|
731
|
+
|
732
|
+
# Create a comment on a run
|
733
|
+
#
|
734
|
+
# @param run_id [String] ID of the run to comment on
|
735
|
+
# @param comment [String] Comment text
|
736
|
+
# @param metadata [Hash, nil] Additional metadata for the comment (optional)
|
737
|
+
# @return [Hash] The created comment data
|
738
|
+
def create_run_comment(run_id:, comment:, metadata: nil)
|
739
|
+
data = {
|
740
|
+
comment: comment
|
741
|
+
}
|
742
|
+
data[:metadata] = metadata if metadata
|
743
|
+
|
744
|
+
post("/runs/#{run_id}/comments", data)
|
745
|
+
end
|
746
|
+
|
747
|
+
# List comments on a run
|
748
|
+
#
|
749
|
+
# @param run_id [String] ID of the run to list comments for
|
750
|
+
# @param limit [Integer] Maximum number of comments to return
|
751
|
+
# @param offset [Integer] Number of comments to skip
|
752
|
+
# @return [Array<Hash>] List of comments
|
753
|
+
def list_run_comments(run_id:, limit: 100, offset: 0)
|
754
|
+
params = {
|
755
|
+
limit: limit,
|
756
|
+
offset: offset
|
757
|
+
}
|
758
|
+
|
759
|
+
get("/runs/#{run_id}/comments", params)
|
760
|
+
end
|
761
|
+
|
762
|
+
# Delete a comment
|
763
|
+
#
|
764
|
+
# @param comment_id [String] ID of the comment to delete
|
765
|
+
# @return [Boolean] True if successful
|
766
|
+
def delete_comment(comment_id:)
|
767
|
+
delete("/comments/#{comment_id}")
|
768
|
+
true
|
769
|
+
end
|
770
|
+
|
771
|
+
# Create an event
|
772
|
+
#
|
773
|
+
# @param name [String] Event name
|
774
|
+
# @param data [Hash] Event data
|
775
|
+
# @param metadata [Hash, nil] Additional metadata for the event (optional)
|
776
|
+
# @return [Hash] The created event data
|
777
|
+
def create_event(name:, data:, metadata: nil)
|
778
|
+
event_data = {
|
779
|
+
name: name,
|
780
|
+
data: data
|
781
|
+
}
|
782
|
+
event_data[:metadata] = metadata if metadata
|
783
|
+
|
784
|
+
post("/events", event_data)
|
785
|
+
end
|
786
|
+
|
787
|
+
# List events
|
788
|
+
#
|
789
|
+
# @param name [String, nil] Filter by event name (optional)
|
790
|
+
# @param start_time [Time, nil] Start time for events (optional)
|
791
|
+
# @param end_time [Time, nil] End time for events (optional)
|
792
|
+
# @param limit [Integer] Maximum number of events to return
|
793
|
+
# @param offset [Integer] Number of events to skip
|
794
|
+
# @return [Array<Hash>] List of events
|
795
|
+
def list_events(name: nil, start_time: nil, end_time: nil, limit: 100, offset: 0)
|
796
|
+
params = {
|
797
|
+
limit: limit,
|
798
|
+
offset: offset
|
799
|
+
}
|
800
|
+
params[:name] = name if name
|
801
|
+
params[:start_time] = start_time.iso8601 if start_time
|
802
|
+
params[:end_time] = end_time.iso8601 if end_time
|
803
|
+
|
804
|
+
get("/events", params)
|
805
|
+
end
|
806
|
+
|
807
|
+
# Get settings
|
808
|
+
#
|
809
|
+
# @return [Hash] Settings data
|
810
|
+
def get_settings
|
811
|
+
get("/settings")
|
812
|
+
end
|
813
|
+
|
814
|
+
# Update settings
|
815
|
+
#
|
816
|
+
# @param settings [Hash] Settings to update
|
817
|
+
# @return [Hash] Updated settings data
|
818
|
+
def update_settings(settings:)
|
819
|
+
patch("/settings", settings)
|
820
|
+
end
|
821
|
+
|
822
|
+
# Bulk tag runs
|
823
|
+
#
|
824
|
+
# @param run_ids [Array<String>] List of run IDs to tag
|
825
|
+
# @param tag_ids [Array<String>] List of tag IDs to apply
|
826
|
+
# @return [Hash] Result of the bulk operation
|
827
|
+
def bulk_tag_runs(run_ids:, tag_ids:)
|
828
|
+
data = {
|
829
|
+
run_ids: run_ids,
|
830
|
+
tag_ids: tag_ids
|
831
|
+
}
|
832
|
+
|
833
|
+
post("/runs/bulk/tag", data)
|
834
|
+
end
|
835
|
+
|
836
|
+
# Bulk untag runs
|
837
|
+
#
|
838
|
+
# @param run_ids [Array<String>] List of run IDs to untag
|
839
|
+
# @param tag_ids [Array<String>] List of tag IDs to remove
|
840
|
+
# @return [Hash] Result of the bulk operation
|
841
|
+
def bulk_untag_runs(run_ids:, tag_ids:)
|
842
|
+
data = {
|
843
|
+
run_ids: run_ids,
|
844
|
+
tag_ids: tag_ids
|
845
|
+
}
|
846
|
+
|
847
|
+
post("/runs/bulk/untag", data)
|
848
|
+
end
|
849
|
+
|
850
|
+
# Bulk delete runs
|
851
|
+
#
|
852
|
+
# @param run_ids [Array<String>] List of run IDs to delete
|
853
|
+
# @return [Hash] Result of the bulk operation
|
854
|
+
def bulk_delete_runs(run_ids:)
|
855
|
+
data = {
|
856
|
+
run_ids: run_ids
|
857
|
+
}
|
858
|
+
|
859
|
+
post("/runs/bulk/delete", data)
|
860
|
+
end
|
861
|
+
|
862
|
+
# Create a webhook
|
863
|
+
#
|
864
|
+
# @param url [String] Webhook URL
|
865
|
+
# @param event_types [Array<String>] List of event types to subscribe to
|
866
|
+
# @param metadata [Hash, nil] Additional metadata for the webhook (optional)
|
867
|
+
# @return [Hash] The created webhook data
|
868
|
+
def create_webhook(url:, event_types:, metadata: nil)
|
869
|
+
data = {
|
870
|
+
url: url,
|
871
|
+
event_types: event_types
|
872
|
+
}
|
873
|
+
data[:metadata] = metadata if metadata
|
874
|
+
|
875
|
+
post("/webhooks", data)
|
876
|
+
end
|
877
|
+
|
878
|
+
# Get a webhook by ID
|
879
|
+
#
|
880
|
+
# @param id [String] ID of the webhook to retrieve
|
881
|
+
# @return [Hash] The requested webhook data
|
882
|
+
def get_webhook(id:)
|
883
|
+
get("/webhooks/#{id}")
|
884
|
+
end
|
885
|
+
|
886
|
+
# List webhooks
|
887
|
+
#
|
888
|
+
# @param limit [Integer] Maximum number of webhooks to return
|
889
|
+
# @param offset [Integer] Number of webhooks to skip
|
890
|
+
# @return [Array<Hash>] List of webhooks
|
891
|
+
def list_webhooks(limit: 100, offset: 0)
|
892
|
+
params = {
|
893
|
+
limit: limit,
|
894
|
+
offset: offset
|
895
|
+
}
|
896
|
+
|
897
|
+
get("/webhooks", params)
|
898
|
+
end
|
899
|
+
|
900
|
+
# Update a webhook
|
901
|
+
#
|
902
|
+
# @param id [String] ID of the webhook to update
|
903
|
+
# @param url [String, nil] New webhook URL (optional)
|
904
|
+
# @param event_types [Array<String>, nil] New list of event types (optional)
|
905
|
+
# @param metadata [Hash, nil] New metadata for the webhook (optional)
|
906
|
+
# @param active [Boolean, nil] Whether the webhook is active (optional)
|
907
|
+
# @return [Hash] The updated webhook data
|
908
|
+
def update_webhook(id:, url: nil, event_types: nil, metadata: nil, active: nil)
|
909
|
+
data = {}
|
910
|
+
data[:url] = url if url
|
911
|
+
data[:event_types] = event_types if event_types
|
912
|
+
data[:metadata] = metadata if metadata
|
913
|
+
data[:active] = active unless active.nil?
|
914
|
+
|
915
|
+
patch("/webhooks/#{id}", data)
|
916
|
+
end
|
917
|
+
|
918
|
+
# Delete a webhook
|
919
|
+
#
|
920
|
+
# @param id [String] ID of the webhook to delete
|
921
|
+
# @return [Boolean] True if successful
|
922
|
+
def delete_webhook(id:)
|
923
|
+
delete("/webhooks/#{id}")
|
924
|
+
true
|
925
|
+
end
|
926
|
+
|
927
|
+
# List team members
|
928
|
+
#
|
929
|
+
# @param organization_id [String, nil] Organization ID (optional, defaults to current organization)
|
930
|
+
# @param limit [Integer] Maximum number of members to return
|
931
|
+
# @param offset [Integer] Number of members to skip
|
932
|
+
# @return [Array<Hash>] List of team members
|
933
|
+
def list_team_members(organization_id: nil, limit: 100, offset: 0)
|
934
|
+
params = {
|
935
|
+
limit: limit,
|
936
|
+
offset: offset
|
937
|
+
}
|
938
|
+
params[:organization_id] = organization_id if organization_id
|
939
|
+
|
940
|
+
get("/team/members", params)
|
941
|
+
end
|
942
|
+
|
943
|
+
# Invite a team member
|
944
|
+
#
|
945
|
+
# @param email [String] Email of the person to invite
|
946
|
+
# @param role [String] Role to assign to the new member
|
947
|
+
# @param organization_id [String, nil] Organization ID (optional, defaults to current organization)
|
948
|
+
# @return [Hash] The created invitation data
|
949
|
+
def invite_team_member(email:, role:, organization_id: nil)
|
950
|
+
data = {
|
951
|
+
email: email,
|
952
|
+
role: role
|
953
|
+
}
|
954
|
+
data[:organization_id] = organization_id if organization_id
|
955
|
+
|
956
|
+
post("/team/invite", data)
|
957
|
+
end
|
958
|
+
|
959
|
+
# Remove a team member
|
960
|
+
#
|
961
|
+
# @param user_id [String] ID of the user to remove
|
962
|
+
# @param organization_id [String, nil] Organization ID (optional, defaults to current organization)
|
963
|
+
# @return [Boolean] True if successful
|
964
|
+
def remove_team_member(user_id:, organization_id: nil)
|
965
|
+
data = {
|
966
|
+
user_id: user_id
|
967
|
+
}
|
968
|
+
data[:organization_id] = organization_id if organization_id
|
969
|
+
|
970
|
+
post("/team/remove", data)
|
971
|
+
true
|
972
|
+
end
|
973
|
+
|
974
|
+
# HTTP request methods
|
975
|
+
|
976
|
+
def get(path, params = {})
|
977
|
+
response = connection.get(path, params)
|
978
|
+
handle_response(response)
|
979
|
+
end
|
980
|
+
|
981
|
+
def post(path, data = {})
|
982
|
+
response = connection.post(path) do |req|
|
983
|
+
req.body = JSON.generate(data)
|
984
|
+
end
|
985
|
+
handle_response(response)
|
986
|
+
end
|
987
|
+
|
988
|
+
def patch(path, data = {})
|
989
|
+
response = connection.patch(path) do |req|
|
990
|
+
req.body = JSON.generate(data)
|
991
|
+
end
|
992
|
+
handle_response(response)
|
993
|
+
end
|
994
|
+
|
995
|
+
def delete(path)
|
996
|
+
response = connection.delete(path)
|
997
|
+
handle_response(response)
|
998
|
+
end
|
999
|
+
|
1000
|
+
private
|
1001
|
+
|
1002
|
+
def connection
|
1003
|
+
@connection ||= Faraday.new(url: @api_url) do |conn|
|
1004
|
+
conn.headers["Authorization"] = "Bearer #{@api_key}"
|
1005
|
+
conn.headers["Content-Type"] = "application/json"
|
1006
|
+
conn.adapter Faraday.default_adapter
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def handle_response(response)
|
1011
|
+
case response.status
|
1012
|
+
when 200..299
|
1013
|
+
JSON.parse(response.body)
|
1014
|
+
when 401
|
1015
|
+
raise Langsmith::Errors::AuthenticationError, "Authentication failed: #{response.body}"
|
1016
|
+
when 404
|
1017
|
+
raise Langsmith::Errors::ResourceNotFoundError, "Resource not found: #{response.body}"
|
1018
|
+
else
|
1019
|
+
raise Langsmith::Errors::APIError, "API error (#{response.status}): #{response.body}"
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
end
|