llm.rb 15.4.1 → 15.5.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 +4 -4
- data/CHANGELOG.md +53 -0
- data/README.md +3 -3
- data/docs/deepdive/reference/tracer.md +10 -0
- data/lib/llm/function.rb +6 -0
- data/lib/llm/provider.rb +19 -4
- data/lib/llm/schema/deferred.rb +68 -0
- data/lib/llm/schema/leaf.rb +3 -0
- data/lib/llm/schema.rb +13 -0
- data/lib/llm/tool/param.rb +4 -2
- data/lib/llm/tools/exec.rb +10 -2
- data/lib/llm/tracer/registry.rb +86 -0
- data/lib/llm/tracer.rb +56 -0
- data/lib/llm/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df1a9b7fbf194f568b95f3f1b2d0314bb703027bc89e4432109d118f238ea2ba
|
|
4
|
+
data.tar.gz: 679938a2f6538f9c1999d31870462498393e1b3a7c7203630c5e8706c893e9ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 49151bad60a22a06b119e08062e58c6d35727d59d10e07d9921166a23b5817083ea11fe942a090bc0e64b68a06340b9cf175a87ecfa81775dffcc7d88a8abb0c
|
|
7
|
+
data.tar.gz: 92ddc3cd73f7af70f25e735fd8ac63dcbca6ed48fd78ba3510be0171ea180d006f399ff60e215bdf1b9159e52b721916c59fcb7278c322fb0ffe789db5900602
|
data/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,59 @@
|
|
|
17
17
|
|
|
18
18
|
*No unreleased changes yet. Check back after the next release.*
|
|
19
19
|
|
|
20
|
+
## v15.5.0
|
|
21
|
+
|
|
22
|
+
Changes since `v15.4.1`.
|
|
23
|
+
|
|
24
|
+
This release makes the exec-backed tools report how long a command ran, calls a
|
|
25
|
+
tracer's `on_exit` hook once when the last scope ends, and resolves a `Proc`
|
|
26
|
+
parameter type when a schema is serialized. It also fixes `LLM::Schema.to_s`
|
|
27
|
+
for a schema with a deferred type.
|
|
28
|
+
|
|
29
|
+
### Tools
|
|
30
|
+
|
|
31
|
+
* **tools: report how long a command ran** <br>
|
|
32
|
+
[`LLM::Tool::Exec#call`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Exec.html#call-instance_method)
|
|
33
|
+
now includes a `duration` field in its result, a string such as `"0.4 seconds"`
|
|
34
|
+
that reports how long the command took. The tools that route through `exec`
|
|
35
|
+
(`git`, `rg`, `mkdir`, `ruby`, and `bundle`) return it as well. A command that
|
|
36
|
+
was not found still returns the error hash, which has no `duration`.
|
|
37
|
+
|
|
38
|
+
### Tracers
|
|
39
|
+
|
|
40
|
+
* **tracer: call `on_exit` once, when the last scope ends** <br>
|
|
41
|
+
[`LLM::Tracer#on_exit`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#on_exit-instance_method)
|
|
42
|
+
is a new hook, called once when the last scope that is open for a tracer
|
|
43
|
+
ends. A tracer that holds a connection, a file, or a span releases it here.
|
|
44
|
+
The count of open scopes belongs to the tracer rather than to the thread that
|
|
45
|
+
opened one, because a tool runs on a thread of its own and scopes the turn's
|
|
46
|
+
tracer while it does. Before this, a tool's scope looked like the outermost
|
|
47
|
+
one: a tracer that released its resource in `on_exit` lost it in the middle
|
|
48
|
+
of the turn, and the rest of the trace was written without it.
|
|
49
|
+
|
|
50
|
+
### Schema
|
|
51
|
+
|
|
52
|
+
* **schema: resolve a parameter type when the schema is serialized** <br>
|
|
53
|
+
[`LLM::Schema`](https://r.uby.dev/api-docs/llm.rb/LLM/Schema.html) and
|
|
54
|
+
[`LLM::Tool`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html) accept a `Proc`
|
|
55
|
+
as the type of a parameter or a property, and the proc is called when the
|
|
56
|
+
schema is serialized rather than when the class that declares it is defined.
|
|
57
|
+
A type that is only known at runtime no longer has to be known at load time:
|
|
58
|
+
`parameter :kind, proc { Enum[*Article.kinds] }, "The article kind"` asks the
|
|
59
|
+
model for the values a store holds right now, and the proc runs again on the
|
|
60
|
+
next request, so it sees the values that are current then. Whatever the proc
|
|
61
|
+
returns is resolved in turn, so it may be a leaf, a class, or an array of
|
|
62
|
+
either, and the description, `required`, `default`, and `enum` given with the
|
|
63
|
+
parameter are applied to it.
|
|
64
|
+
|
|
65
|
+
* **schema: keep `to_s` working for a deferred type** <br>
|
|
66
|
+
Fix a bug where a schema with a proc-typed property raised from
|
|
67
|
+
[`LLM::Schema.to_s`](https://r.uby.dev/api-docs/llm.rb/LLM/Schema.html#to_s-class_method)
|
|
68
|
+
(and so from `inspect` and `p`) because the placeholder resolved its leaf
|
|
69
|
+
through a private method that shadowed the one `LLM::Schema::Leaf#default`
|
|
70
|
+
uses. Serialization, and therefore the request sent to a provider, was
|
|
71
|
+
unaffected.
|
|
72
|
+
|
|
20
73
|
## v15.4.1
|
|
21
74
|
|
|
22
75
|
Changes since `v15.4.0`.
|
data/README.md
CHANGED
|
@@ -1092,9 +1092,9 @@ web</a>.
|
|
|
1092
1092
|
|
|
1093
1093
|
The llm.rb project was started more than three
|
|
1094
1094
|
years ago by
|
|
1095
|
-
[@
|
|
1096
|
-
[@antaz](https://github.com/
|
|
1097
|
-
maintainer is [@
|
|
1095
|
+
[@altruby](https://github.com/altruby) and
|
|
1096
|
+
[@antaz](https://github.com/altruby). The primary
|
|
1097
|
+
maintainer is [@altruby](https://github.com/altruby).
|
|
1098
1098
|
Over those three years multiple other contributors have
|
|
1099
1099
|
contributed to llm.rb as well, and new contributors are
|
|
1100
1100
|
always welcome.
|
|
@@ -156,6 +156,16 @@ Three more hooks cover a local tool call. `on_tool_start` fires before
|
|
|
156
156
|
the tool runs and returns the span that `on_tool_finish` and
|
|
157
157
|
`on_tool_error` receive.
|
|
158
158
|
|
|
159
|
+
A tracer's own lifetime is bracketed as well. `on_exit` fires once,
|
|
160
|
+
when the last scope that is open for that tracer ends. That scope can
|
|
161
|
+
belong to a different thread than the one that opened the first: a tool
|
|
162
|
+
runs on a thread of its own and scopes the turn's tracer while it does.
|
|
163
|
+
`on_exit` is called after the scoped lookup has been restored, so a
|
|
164
|
+
tracer that asks its provider for the current tracer from inside it
|
|
165
|
+
sees the tracer the next request will see. A tracer can be scoped again
|
|
166
|
+
afterwards, so it has to remain usable after `on_exit`, and `on_exit`
|
|
167
|
+
may be called more than once over its life.
|
|
168
|
+
|
|
159
169
|
A turn is additionally bracketed with `start_trace` and `stop_trace`.
|
|
160
170
|
The runtime calls them around every agent turn with a `trace_group_id`,
|
|
161
171
|
and a tracer that supports it (such as
|
data/lib/llm/function.rb
CHANGED
|
@@ -199,6 +199,12 @@ class LLM::Function
|
|
|
199
199
|
|
|
200
200
|
##
|
|
201
201
|
# Set (or get) the function parameters
|
|
202
|
+
#
|
|
203
|
+
# @note
|
|
204
|
+
# A parameter type that is given as a proc is resolved when the
|
|
205
|
+
# parameters are rendered rather than here, so a tool can say
|
|
206
|
+
# `parameter :fruit, proc { Enum[Fruit.keys] }` and ask the model
|
|
207
|
+
# for what is available at the time of the call.
|
|
202
208
|
# @yieldparam [LLM::Schema] schema The schema object
|
|
203
209
|
# @return [LLM::Schema::Leaf, nil]
|
|
204
210
|
def params
|
data/lib/llm/provider.rb
CHANGED
|
@@ -396,9 +396,18 @@ class LLM::Provider
|
|
|
396
396
|
# Set the provider's default tracer
|
|
397
397
|
# This tracer is shared by the provider instance and becomes the fallback
|
|
398
398
|
# whenever no scoped override is active.
|
|
399
|
+
#
|
|
400
|
+
# A tracer assigned this way is not scoped, so nothing declares when it
|
|
401
|
+
# is finished and {LLM::Tracer#on_exit} never fires for it. Release what
|
|
402
|
+
# it holds when the provider is done with, or scope it with
|
|
403
|
+
# {#with_tracer} instead.
|
|
404
|
+
#
|
|
399
405
|
# @example
|
|
400
406
|
# llm = LLM.openai(key: ENV["KEY"])
|
|
401
407
|
# llm.tracer = LLM::Tracer.logger(llm, path: "/path/to/log.txt")
|
|
408
|
+
# llm.with_tracer(llm.tracer) do
|
|
409
|
+
# # ...
|
|
410
|
+
# end
|
|
402
411
|
# @param [LLM::Tracer] tracer
|
|
403
412
|
# A tracer
|
|
404
413
|
# @return [void]
|
|
@@ -418,19 +427,25 @@ class LLM::Provider
|
|
|
418
427
|
# @yield
|
|
419
428
|
# @return [Object]
|
|
420
429
|
def with_tracer(tracer)
|
|
430
|
+
scoped = tracer || LLM::Tracer::Null.new(self)
|
|
421
431
|
wm = weakmaps.tracer
|
|
422
432
|
had_override = wm.key?(self)
|
|
423
433
|
previous = wm[self]
|
|
424
|
-
wm[self] =
|
|
434
|
+
wm[self] = scoped
|
|
435
|
+
entered = LLM::Tracer.enter(scoped)
|
|
425
436
|
yield
|
|
426
437
|
ensure
|
|
427
438
|
if had_override
|
|
428
439
|
wm[self] = previous
|
|
429
|
-
elsif wm.respond_to?(:delete)
|
|
430
|
-
wm.delete(self)
|
|
431
440
|
else
|
|
432
|
-
wm[self] = nil
|
|
441
|
+
wm.respond_to?(:delete) ? wm.delete(self) : wm[self] = nil
|
|
433
442
|
end
|
|
443
|
+
##
|
|
444
|
+
# The scope this tracer was opened for is over, and the tracer is
|
|
445
|
+
# told so when the last one that is open for it ends - on whichever
|
|
446
|
+
# thread that happens, because a tool opens a scope of its own for
|
|
447
|
+
# the turn's tracer.
|
|
448
|
+
LLM::Tracer.exit(scoped) if entered
|
|
434
449
|
end
|
|
435
450
|
|
|
436
451
|
##
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LLM::Schema
|
|
4
|
+
##
|
|
5
|
+
# {LLM::Schema::Deferred LLM::Schema::Deferred} is a placeholder for a
|
|
6
|
+
# leaf that is resolved when the schema is rendered.
|
|
7
|
+
#
|
|
8
|
+
# A parameter type that can only be known at runtime - an enum of the
|
|
9
|
+
# keys a store holds right now, for example - is given as a proc. The
|
|
10
|
+
# proc is called when the schema is rendered rather than when the class
|
|
11
|
+
# that declares the parameter is defined, so declaring a tool does not
|
|
12
|
+
# touch whatever the type depends on:
|
|
13
|
+
#
|
|
14
|
+
# parameter :fruit, proc { Enum[*Fruit.kinds]}, "A fruit", required: true
|
|
15
|
+
#
|
|
16
|
+
# Whatever the proc returns is resolved in turn, so it can return a
|
|
17
|
+
# leaf, a class, or an array of either.
|
|
18
|
+
#
|
|
19
|
+
# @api private
|
|
20
|
+
class Deferred < Leaf
|
|
21
|
+
##
|
|
22
|
+
# @param [Proc] block
|
|
23
|
+
# A block that returns the leaf to resolve to
|
|
24
|
+
# @return [LLM::Schema::Deferred]
|
|
25
|
+
def initialize(&block)
|
|
26
|
+
super()
|
|
27
|
+
@block = block
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# @return [Hash]
|
|
32
|
+
def to_h
|
|
33
|
+
resolved_leaf.to_h
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# @param [Hash] options
|
|
38
|
+
# @return [String]
|
|
39
|
+
def to_json(options = {})
|
|
40
|
+
resolved_leaf.to_h.to_json(options)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# @return [String]
|
|
45
|
+
def to_s
|
|
46
|
+
resolved_leaf.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Returns the leaf the block resolves to, with the settings that
|
|
53
|
+
# were given to the placeholder applied to it.
|
|
54
|
+
#
|
|
55
|
+
# The settings live on the placeholder, because they are given
|
|
56
|
+
# before the leaf it stands for exists.
|
|
57
|
+
# @return [LLM::Schema::Leaf]
|
|
58
|
+
def resolved_leaf
|
|
59
|
+
leaf = @block.call
|
|
60
|
+
leaf.description(@description) if @description
|
|
61
|
+
leaf.default(@default) unless @default.nil?
|
|
62
|
+
leaf.enum(*@enum) if @enum
|
|
63
|
+
leaf.const(@const) unless @const.nil?
|
|
64
|
+
leaf.required if required?
|
|
65
|
+
leaf
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/llm/schema/leaf.rb
CHANGED
data/lib/llm/schema.rb
CHANGED
|
@@ -36,6 +36,7 @@ class LLM::Schema
|
|
|
36
36
|
require_relative "schema/parser"
|
|
37
37
|
require_relative "schema/renderer"
|
|
38
38
|
require_relative "schema/leaf"
|
|
39
|
+
require_relative "schema/deferred"
|
|
39
40
|
require_relative "schema/object"
|
|
40
41
|
require_relative "schema/array"
|
|
41
42
|
require_relative "schema/all_of"
|
|
@@ -56,9 +57,21 @@ class LLM::Schema
|
|
|
56
57
|
module Utils
|
|
57
58
|
extend self
|
|
58
59
|
|
|
60
|
+
##
|
|
61
|
+
# Resolves a parameter type into a leaf of the schema.
|
|
62
|
+
#
|
|
63
|
+
# A proc is called for its type, and whatever it returns is resolved
|
|
64
|
+
# in turn, so a type that can only be known at runtime - an enum of
|
|
65
|
+
# the keys a store holds right now - can be declared without being
|
|
66
|
+
# evaluated when the class that declares it is defined.
|
|
67
|
+
# @param [LLM::Schema] schema
|
|
68
|
+
# @param [Object] type
|
|
69
|
+
# @return [LLM::Schema::Leaf]
|
|
59
70
|
def resolve(schema, type)
|
|
60
71
|
if LLM::Schema::Leaf === type
|
|
61
72
|
type
|
|
73
|
+
elsif ::Proc === type
|
|
74
|
+
Deferred.new { resolve(schema, type.call) }
|
|
62
75
|
elsif ::Array === type
|
|
63
76
|
resolve_array(schema, type)
|
|
64
77
|
elsif Class === type && type.respond_to?(:object)
|
data/lib/llm/tool/param.rb
CHANGED
|
@@ -22,8 +22,10 @@ class LLM::Tool
|
|
|
22
22
|
##
|
|
23
23
|
# @param name [Symbol]
|
|
24
24
|
# The name of a parameter
|
|
25
|
-
# @param type [LLM::Schema::Leaf, Class]
|
|
26
|
-
# The parameter type (eg String)
|
|
25
|
+
# @param type [LLM::Schema::Leaf, Class, Proc]
|
|
26
|
+
# The parameter type (eg String). A proc is called for the type
|
|
27
|
+
# when the parameters are read, so a type that can only be known
|
|
28
|
+
# at runtime does not have to be known when the class is defined.
|
|
27
29
|
# @param description [String]
|
|
28
30
|
# The description of a property
|
|
29
31
|
# @param options [Hash]
|
data/lib/llm/tools/exec.rb
CHANGED
|
@@ -54,7 +54,8 @@ class LLM::Tool
|
|
|
54
54
|
# the max number of bytes to emit
|
|
55
55
|
# @return [Hash]
|
|
56
56
|
def call(arguments: [], timeout: 60, max_bytes: self.class.max_bytes)
|
|
57
|
-
|
|
57
|
+
startat = now
|
|
58
|
+
name = arguments[0]
|
|
58
59
|
command = spawn(name:, arguments: arguments[1..], env:, max_bytes:)
|
|
59
60
|
wait(command:, timeout:)
|
|
60
61
|
if command.not_found?
|
|
@@ -62,7 +63,8 @@ class LLM::Tool
|
|
|
62
63
|
else
|
|
63
64
|
{ok: command.success?,
|
|
64
65
|
stdout: truncate(command.stdout, max_bytes:),
|
|
65
|
-
stderr: truncate(command.stderr, max_bytes:)
|
|
66
|
+
stderr: truncate(command.stderr, max_bytes:),
|
|
67
|
+
duration: "#{(now - startat).round(1)} seconds"}
|
|
66
68
|
end
|
|
67
69
|
rescue LLM::Interrupt
|
|
68
70
|
command.kill! if command&.running?
|
|
@@ -72,5 +74,11 @@ class LLM::Tool
|
|
|
72
74
|
private
|
|
73
75
|
|
|
74
76
|
attr_reader :env
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# @return [Float]
|
|
80
|
+
def now
|
|
81
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
82
|
+
end
|
|
75
83
|
end
|
|
76
84
|
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LLM::Tracer
|
|
4
|
+
##
|
|
5
|
+
# {LLM::Tracer::Registry LLM::Tracer::Registry} counts the scopes that
|
|
6
|
+
# are open for each tracer.
|
|
7
|
+
#
|
|
8
|
+
# The count belongs to the tracer, not to the thread that opened a
|
|
9
|
+
# scope. A tool runs on a thread of its own and opens a scope there for
|
|
10
|
+
# the turn's tracer, and a count kept per thread would let that scope
|
|
11
|
+
# look like the outermost one: it would end the tracer while the turn
|
|
12
|
+
# is still running, and every write after it would fail.
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
class Registry
|
|
16
|
+
##
|
|
17
|
+
# @return [LLM::Tracer::Registry]
|
|
18
|
+
def initialize
|
|
19
|
+
@counts = ObjectSpace::WeakMap.new
|
|
20
|
+
@mutex = Mutex.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Records that a scope was opened for a tracer.
|
|
25
|
+
# @param [LLM::Tracer] tracer
|
|
26
|
+
# @return [LLM::Tracer::Registry]
|
|
27
|
+
# Returns self, so a caller can tell the scope was recorded
|
|
28
|
+
def enter(tracer)
|
|
29
|
+
@mutex.synchronize do
|
|
30
|
+
@counts[tracer] = count(tracer) + 1
|
|
31
|
+
end
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Records that a scope was closed, and tells the tracer it is
|
|
37
|
+
# finished when it was the last one that was open.
|
|
38
|
+
#
|
|
39
|
+
# The count is brought up to date under the lock, and the tracer is
|
|
40
|
+
# told after the lock is released: a tracer that calls back into the
|
|
41
|
+
# provider from `on_exit` would otherwise deadlock against itself.
|
|
42
|
+
#
|
|
43
|
+
# An `exit` without a matching `enter` is ignored: no scope is open,
|
|
44
|
+
# so there is nothing to end, and the tracer is not told it is
|
|
45
|
+
# finished when no scope was holding it.
|
|
46
|
+
# @param [LLM::Tracer] tracer
|
|
47
|
+
# @return [void]
|
|
48
|
+
def exit(tracer)
|
|
49
|
+
last = @mutex.synchronize do
|
|
50
|
+
open = count(tracer)
|
|
51
|
+
if open.zero?
|
|
52
|
+
false
|
|
53
|
+
elsif open == 1
|
|
54
|
+
forget(tracer)
|
|
55
|
+
true
|
|
56
|
+
else
|
|
57
|
+
@counts[tracer] = open - 1
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
tracer.on_exit if last
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Drops a tracer that has no open scopes left.
|
|
68
|
+
# @param [LLM::Tracer] tracer
|
|
69
|
+
# @return [Boolean]
|
|
70
|
+
def forget(tracer)
|
|
71
|
+
if @counts.respond_to?(:delete)
|
|
72
|
+
@counts.delete(tracer)
|
|
73
|
+
else
|
|
74
|
+
@counts[tracer] = 0
|
|
75
|
+
end
|
|
76
|
+
true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# @param [LLM::Tracer] tracer
|
|
81
|
+
# @return [Integer]
|
|
82
|
+
def count(tracer)
|
|
83
|
+
@counts[tracer] || 0
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/llm/tracer.rb
CHANGED
|
@@ -13,6 +13,39 @@ module LLM
|
|
|
13
13
|
require_relative "tracer/telemetry"
|
|
14
14
|
require_relative "tracer/null"
|
|
15
15
|
require_relative "tracer/pretty_logger"
|
|
16
|
+
require_relative "tracer/registry"
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Returns the registry that counts the open scopes for each tracer.
|
|
20
|
+
# @api private
|
|
21
|
+
# @return [LLM::Tracer::Registry]
|
|
22
|
+
def self.registry
|
|
23
|
+
LLM.lock(:registry) do
|
|
24
|
+
@registry ||= Registry.new
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Records that a scope was opened for a tracer.
|
|
30
|
+
# @api private
|
|
31
|
+
# @see LLM::Tracer::Registry#enter
|
|
32
|
+
# @param [LLM::Tracer] tracer
|
|
33
|
+
# @return [LLM::Tracer::Registry]
|
|
34
|
+
# Returns the registry, so a caller can tell the scope was recorded
|
|
35
|
+
def self.enter(tracer)
|
|
36
|
+
registry.enter(tracer)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Records that a scope was closed for a tracer, and tells the tracer
|
|
41
|
+
# it is finished when it was the last one that was open.
|
|
42
|
+
# @api private
|
|
43
|
+
# @see LLM::Tracer::Registry#exit
|
|
44
|
+
# @param [LLM::Tracer] tracer
|
|
45
|
+
# @return [void]
|
|
46
|
+
def self.exit(tracer)
|
|
47
|
+
registry.exit(tracer)
|
|
48
|
+
end
|
|
16
49
|
|
|
17
50
|
##
|
|
18
51
|
# Builds a {LLM::Tracer::PrettyLogger} for a provider.
|
|
@@ -61,6 +94,29 @@ module LLM
|
|
|
61
94
|
@options = {}
|
|
62
95
|
end
|
|
63
96
|
|
|
97
|
+
##
|
|
98
|
+
# Called when the tracer is no longer in use.
|
|
99
|
+
#
|
|
100
|
+
# A tracer that holds something - a file, a span, a connection pool -
|
|
101
|
+
# releases it here. The default does nothing, because most tracers hold
|
|
102
|
+
# nothing that needs releasing.
|
|
103
|
+
#
|
|
104
|
+
# It is called once, when the last scope that is open for this tracer
|
|
105
|
+
# ends, and that scope can have been opened by another thread: a tool
|
|
106
|
+
# runs on a thread of its own and scopes the turn's tracer while it
|
|
107
|
+
# does. It is called after the scoped lookup is restored, so a tracer
|
|
108
|
+
# that reads {LLM::Provider#tracer} inside it sees the tracer that the
|
|
109
|
+
# next request will see.
|
|
110
|
+
#
|
|
111
|
+
# A tracer can be scoped again afterwards, on a later turn, so it has
|
|
112
|
+
# to stay usable after this call, and it has to tolerate the call
|
|
113
|
+
# happening more than once over its life.
|
|
114
|
+
# @see LLM::Tracer::Registry
|
|
115
|
+
# @see LLM::Provider#with_tracer
|
|
116
|
+
# @return [void]
|
|
117
|
+
def on_exit
|
|
118
|
+
end
|
|
119
|
+
|
|
64
120
|
##
|
|
65
121
|
# Called before an LLM provider request is executed.
|
|
66
122
|
# @param [String] operation
|
data/lib/llm/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 15.
|
|
4
|
+
version: 15.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Gleeson
|
|
@@ -624,6 +624,7 @@ files:
|
|
|
624
624
|
- lib/llm/schema/any_of.rb
|
|
625
625
|
- lib/llm/schema/array.rb
|
|
626
626
|
- lib/llm/schema/boolean.rb
|
|
627
|
+
- lib/llm/schema/deferred.rb
|
|
627
628
|
- lib/llm/schema/enum.rb
|
|
628
629
|
- lib/llm/schema/integer.rb
|
|
629
630
|
- lib/llm/schema/leaf.rb
|
|
@@ -665,6 +666,7 @@ files:
|
|
|
665
666
|
- lib/llm/tracer/logger.rb
|
|
666
667
|
- lib/llm/tracer/null.rb
|
|
667
668
|
- lib/llm/tracer/pretty_logger.rb
|
|
669
|
+
- lib/llm/tracer/registry.rb
|
|
668
670
|
- lib/llm/tracer/telemetry.rb
|
|
669
671
|
- lib/llm/transformer.rb
|
|
670
672
|
- lib/llm/transformer/null.rb
|