verica-observability 0.1.7 → 0.1.8
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/README.md +35 -8
- data/lib/verica/config.rb +2 -1
- data/lib/verica/openai_wrapper.rb +2 -0
- data/lib/verica/ruby_openai_wrapper.rb +2 -0
- data/lib/verica/version.rb +1 -1
- data/lib/verica.rb +36 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b2286c95c84642a8961ca9521858ce47f8cdff3d764e7269a278acd6a78284c
|
|
4
|
+
data.tar.gz: bb471935256948e92255edf0b4c92ef97f245544890fe4de09b9862b92d2c092
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2765905d27ac2b2d5b94f59d79ebda8fab9b42aeb9e677cd97eeff9e1fb9021567b68b249d58a65ccc423f76997fcf6274320b205b220f18d60c8c9b73acdfb
|
|
7
|
+
data.tar.gz: d1d66aa9cf309ac254c4bcfde0dadbc4ecfdbad75aa85da8f98b88ccb143f3058fea5d2586946168bdfacc7f33fc61867cb400da690b18a069fad22011851f95
|
data/README.md
CHANGED
|
@@ -81,6 +81,32 @@ end
|
|
|
81
81
|
body with it and keep the return unchanged. Each thread carries its own override,
|
|
82
82
|
so concurrent requests never leak conversation ids into one another.
|
|
83
83
|
|
|
84
|
+
**Resend history exactly as sent.** Turns are stored as deltas: at ingest,
|
|
85
|
+
Verica matches the history you resend against what previous turns already
|
|
86
|
+
stored, and that match is exact (byte-identical text). If your app mutates
|
|
87
|
+
prior messages between requests (for example, appending "Respond in JSON" to
|
|
88
|
+
the last user message and stripping it from earlier turns when rebuilding the
|
|
89
|
+
history), no prefix ever matches and every turn falls back to storing, and
|
|
90
|
+
showing, the full conversation again. Keep injected instructions in the system
|
|
91
|
+
prompt, or resend them exactly as originally sent.
|
|
92
|
+
|
|
93
|
+
## Tags
|
|
94
|
+
|
|
95
|
+
Tags land on each trace (`traces.tags`): filter the workbench by them, and bind
|
|
96
|
+
criteria to them so evaluation preselects the right criteria per tag.
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
Verica.init(token: ENV.fetch("VERICA_TOKEN"), tags: ["mtn-campus", "prod"])
|
|
100
|
+
|
|
101
|
+
Verica.with_tags(["mentor-chat"]) do
|
|
102
|
+
client.chat(parameters: { ... })
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Per-request tags UNION with the globals (dedup, order preserved); nested blocks
|
|
107
|
+
accumulate; the scope is thread-local and restored even if the block raises.
|
|
108
|
+
Values are coerced with `to_s`; the server caps at 20 tags x 120 chars.
|
|
109
|
+
|
|
84
110
|
## Streaming (`wrap_openai_compatible`)
|
|
85
111
|
|
|
86
112
|
Streaming calls (a `stream:` proc in `parameters`) are fully captured: the
|
|
@@ -127,14 +153,15 @@ span batch is exported.
|
|
|
127
153
|
|
|
128
154
|
## Options
|
|
129
155
|
|
|
130
|
-
| Option / env var | Default | Notes
|
|
131
|
-
| --------------------------------------------- | ---------- |
|
|
132
|
-
| `token:` / `VERICA_TOKEN` | (required) | ingest-scoped API token
|
|
133
|
-
| `capture_content:` / `VERICA_CAPTURE_CONTENT` | `true` | send prompt/response content
|
|
134
|
-
| `conversation_id:` | (none) | stamps `gen_ai.conversation.id`
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
156
|
+
| Option / env var | Default | Notes |
|
|
157
|
+
| --------------------------------------------- | ---------- | -------------------------------------------- |
|
|
158
|
+
| `token:` / `VERICA_TOKEN` | (required) | ingest-scoped API token |
|
|
159
|
+
| `capture_content:` / `VERICA_CAPTURE_CONTENT` | `true` | send prompt/response content |
|
|
160
|
+
| `conversation_id:` | (none) | stamps `gen_ai.conversation.id` |
|
|
161
|
+
| `tags:` | (none) | global tags; per-request: `Verica.with_tags` |
|
|
162
|
+
| `service_name:` / `OTEL_SERVICE_NAME` | `app` | resource service.name |
|
|
163
|
+
| `stream_usage:` / `VERICA_STREAM_USAGE` | `false` | inject `stream_options` for tokens |
|
|
164
|
+
| `debug:` / `VERICA_DEBUG` | `false` | log export errors |
|
|
138
165
|
|
|
139
166
|
Fail-open by design: if Verica is unreachable or the token is invalid, spans are
|
|
140
167
|
dropped and your app is never affected. Export errors are silent unless `debug`
|
data/lib/verica/config.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Verica
|
|
4
4
|
# Config resolution: options > env vars > defaults. Pure.
|
|
5
|
-
Config = Struct.new(:token, :endpoint, :capture_content, :conversation_id, :service_name, :debug,
|
|
5
|
+
Config = Struct.new(:token, :endpoint, :capture_content, :conversation_id, :tags, :service_name, :debug,
|
|
6
6
|
:stream_usage, keyword_init: true) do
|
|
7
7
|
# Verica's hosted ingest endpoint. Override via the `endpoint:` option or the
|
|
8
8
|
# VERICA_ENDPOINT env var for self-host or local dev. Only `token` is required.
|
|
@@ -21,6 +21,7 @@ module Verica
|
|
|
21
21
|
endpoint: raw_endpoint.sub(%r{/+\z}, ''),
|
|
22
22
|
capture_content: capture,
|
|
23
23
|
conversation_id: options[:conversation_id],
|
|
24
|
+
tags: Array(options[:tags]).map(&:to_s).reject(&:empty?).uniq,
|
|
24
25
|
service_name: options[:service_name] || env['OTEL_SERVICE_NAME'] || 'app',
|
|
25
26
|
debug: options[:debug].nil? ? %w[1 true].include?(env['VERICA_DEBUG']) : options[:debug],
|
|
26
27
|
# Opt-in: append `stream_options: { include_usage: true }` to streaming
|
|
@@ -78,6 +78,8 @@ module Verica
|
|
|
78
78
|
span.set_attribute('gen_ai.request.model', params[:model].to_s) if params[:model]
|
|
79
79
|
conversation_id = Verica.conversation_id
|
|
80
80
|
span.set_attribute('gen_ai.conversation.id', conversation_id) if conversation_id
|
|
81
|
+
tags = Verica.current_tags
|
|
82
|
+
span.set_attribute('verica.tags', JSON.generate(tags)) unless tags.empty?
|
|
81
83
|
|
|
82
84
|
model = response.respond_to?(:model) ? response.model : nil
|
|
83
85
|
span.set_attribute('gen_ai.response.model', model) if model
|
|
@@ -136,6 +136,8 @@ module Verica
|
|
|
136
136
|
span.set_attribute('gen_ai.request.model', model.to_s) if model
|
|
137
137
|
conversation_id = Verica.conversation_id
|
|
138
138
|
span.set_attribute('gen_ai.conversation.id', conversation_id) if conversation_id
|
|
139
|
+
tags = Verica.current_tags
|
|
140
|
+
span.set_attribute('verica.tags', JSON.generate(tags)) unless tags.empty?
|
|
139
141
|
span.set_attribute('gen_ai.input.messages', JSON.generate(messages)) if cfg&.capture_content && messages
|
|
140
142
|
|
|
141
143
|
# Streaming: annotate the response side from the drained accumulator.
|
data/lib/verica/version.rb
CHANGED
data/lib/verica.rb
CHANGED
|
@@ -13,6 +13,10 @@ module Verica
|
|
|
13
13
|
# String (including '') means an override IS active for this fiber/thread.
|
|
14
14
|
CONVERSATION_KEY = :verica_conversation_override
|
|
15
15
|
|
|
16
|
+
# Fiber-local key holding the per-request tag scope (Array<String>); nil means
|
|
17
|
+
# no scope is active. Scopes UNION with the global config tags.
|
|
18
|
+
TAGS_KEY = :verica_tags_scope
|
|
19
|
+
|
|
16
20
|
class << self
|
|
17
21
|
attr_reader :config
|
|
18
22
|
|
|
@@ -43,6 +47,27 @@ module Verica
|
|
|
43
47
|
value.empty? ? nil : value
|
|
44
48
|
end
|
|
45
49
|
|
|
50
|
+
# Adds tags to every span emitted inside the block, UNIONED with the global
|
|
51
|
+
# `tags:` from `init` (global first, dedup, `to_s` coercion). Thread-safe
|
|
52
|
+
# (fiber-local), nestable (nested scopes accumulate), restored on the way
|
|
53
|
+
# out even if the block raises. Returns the block's value.
|
|
54
|
+
#
|
|
55
|
+
# Verica.with_tags(["mentor-chat"]) do
|
|
56
|
+
# client.chat(parameters: { ... })
|
|
57
|
+
# end
|
|
58
|
+
def with_tags(tags)
|
|
59
|
+
previous = Thread.current[TAGS_KEY]
|
|
60
|
+
Thread.current[TAGS_KEY] = merge_tags(previous || [], tags)
|
|
61
|
+
yield
|
|
62
|
+
ensure
|
|
63
|
+
Thread.current[TAGS_KEY] = previous
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Global config tags ∪ the active thread-local scope, deduped in order.
|
|
67
|
+
def current_tags
|
|
68
|
+
merge_tags(@config&.tags || [], Thread.current[TAGS_KEY] || [])
|
|
69
|
+
end
|
|
70
|
+
|
|
46
71
|
def init(**options)
|
|
47
72
|
if @initialized
|
|
48
73
|
warn '[verica] init called twice; ignoring the second call.'
|
|
@@ -113,5 +138,16 @@ module Verica
|
|
|
113
138
|
@initialized = false
|
|
114
139
|
@config = nil
|
|
115
140
|
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def merge_tags(base, extra)
|
|
145
|
+
out = base.dup
|
|
146
|
+
Array(extra).each do |t|
|
|
147
|
+
s = t.to_s
|
|
148
|
+
out << s unless s.empty? || out.include?(s)
|
|
149
|
+
end
|
|
150
|
+
out
|
|
151
|
+
end
|
|
116
152
|
end
|
|
117
153
|
end
|