nitro_intelligence 3.0.2 → 3.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 +4 -4
- data/Rakefile +3 -1
- data/docs/README.md +11 -1
- data/lib/nitro_intelligence/client/observers/langfuse_observer.rb +24 -5
- data/lib/nitro_intelligence/langfuse_extension.rb +12 -5
- data/lib/nitro_intelligence/langfuse_tracer_provider.rb +22 -1
- data/lib/nitro_intelligence/reporter.rb +2 -1
- data/lib/nitro_intelligence/version.rb +1 -1
- 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: 2151383fb66e0292ac5fbf73f16697e32b9c0f7d64263500babdb505275925cc
|
|
4
|
+
data.tar.gz: 91b5f4bbbb3e87f56e883b710f8af8a6c1900e3c1e733d3eba14258bbc8ebf05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d058b2fed129d9c85210558afe41b947a44090a5532aca084a6eff04fa2913f4e0d284f0848818ce98195673833e6d675141ee06464bafcf588cfdf29bfea9f8
|
|
7
|
+
data.tar.gz: 10b6c3213894be249388a5bdbc7cc5ef963b904581d00de2d02ac3175aeb9abc9348e959a2f54c8a27b20cb19b1ecdd4c09fdb974325d331367d25df98be73b7
|
data/Rakefile
CHANGED
data/docs/README.md
CHANGED
|
@@ -323,7 +323,7 @@ client.chat(
|
|
|
323
323
|
|
|
324
324
|
### Custom Trace IDs
|
|
325
325
|
|
|
326
|
-
To generate a deterministic trace ID in the observability platform, you can pass `trace_seed` as a parameter. This is useful when you want a stable identifier that is derived from a specific domain value (e.g., document ID). The same `trace_seed` will produce the same trace ID, making it easier to correlate multiple events.
|
|
326
|
+
To generate a deterministic trace ID in the observability platform, you can pass `trace_seed` as a parameter. This is useful when you want a stable identifier that is derived from a specific domain value (e.g., document ID). The same `trace_seed` will produce the same trace ID, making it easier to correlate multiple events. Each seeded call creates a root observation in that trace, including when another observation is active. With Langfuse v4 event reads, trace input and output come from the most recently ingested root observation. Calls without a seed keep their active parent observation.
|
|
327
327
|
|
|
328
328
|
```ruby
|
|
329
329
|
document_id = "document-123"
|
|
@@ -424,6 +424,16 @@ trace_id = NitroIntelligence::Trace.create_id(seed: document_id)
|
|
|
424
424
|
reporter.score(name: "precision", value: 0.5, trace_id:)
|
|
425
425
|
```
|
|
426
426
|
|
|
427
|
+
#### Using different data types for scoring
|
|
428
|
+
|
|
429
|
+
By default, scores submitted on a trace are numeric, but you can use the `data_type` keyword to change it to `:boolean` or `:categorical`. For example:
|
|
430
|
+
|
|
431
|
+
```ruby
|
|
432
|
+
reporter.score(name: "precision", value: "Partially Correct", trace_id:, data_type: :categorical)
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Numeric data is helpful for scores that happen on a sliding scale, like success rate or percent similarity. Boolean data is helpful to convey something that either passes or fails. Categorical data is helpful for scores that have pre-defined categories, like "Correct", "Partially Correct", and "Incorrect".
|
|
436
|
+
|
|
427
437
|
### Prompts
|
|
428
438
|
|
|
429
439
|
#### Message Shape
|
|
@@ -10,6 +10,10 @@ module NitroIntelligence
|
|
|
10
10
|
|
|
11
11
|
MAX_STATUS_MESSAGE_LENGTH = 2000
|
|
12
12
|
|
|
13
|
+
# Attributes a handler may report off a response, each written only when it supplied
|
|
14
|
+
# one. See #record_result for why absence is left as absence.
|
|
15
|
+
RECORDED_ATTRIBUTES = %i[model model_parameters usage_details cost_details input output].freeze
|
|
16
|
+
|
|
13
17
|
attr_reader :project_client
|
|
14
18
|
|
|
15
19
|
def initialize(project_client:)
|
|
@@ -63,15 +67,30 @@ module NitroIntelligence
|
|
|
63
67
|
|
|
64
68
|
handle_truncation(trace_attributes[:input], trace_attributes[:output], trace_attributes[:model])
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
RECORDED_ATTRIBUTES.each do |name|
|
|
71
|
+
value = trace_attributes[name]
|
|
72
|
+
generation.public_send(:"#{name}=", value) if value
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
record_status(generation, trace_attributes)
|
|
71
76
|
|
|
72
77
|
generation.update_trace(input: trace_attributes[:input], output: trace_attributes[:output])
|
|
73
78
|
end
|
|
74
79
|
|
|
80
|
+
# A response the endpoint answered but did not finish -- a generation cut off at its
|
|
81
|
+
# token ceiling, say -- is neither a success worth nothing being said about nor a
|
|
82
|
+
# failure the request raised on. Recording it at WARNING leaves it findable without
|
|
83
|
+
# putting it among the errors.
|
|
84
|
+
def record_status(generation, trace_attributes)
|
|
85
|
+
status_message = trace_attributes[:status_message]
|
|
86
|
+
return if status_message.blank?
|
|
87
|
+
|
|
88
|
+
generation.update(
|
|
89
|
+
level: trace_attributes[:level] || "WARNING",
|
|
90
|
+
status_message: status_message.truncate(MAX_STATUS_MESSAGE_LENGTH)
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
|
|
75
94
|
# Recorded before the request is made so that a request which raises still
|
|
76
95
|
# shows what was sent. Handlers whose input is not safe to record twice
|
|
77
96
|
# (image generation sends base64 payloads that are replaced with media
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
# The content of this file should eventually make its way upstream. Setting a custom trace ID is
|
|
5
5
|
# already awaiting approval in https://github.com/simplepractice/langfuse-rb/pull/69.
|
|
6
6
|
#
|
|
7
|
+
# Explicit trace IDs create real roots here while the upstream fix awaits approval:
|
|
8
|
+
# https://github.com/simplepractice/langfuse-rb/pull/128.
|
|
9
|
+
#
|
|
7
10
|
|
|
8
11
|
require "nitro_intelligence/langfuse_tracer_provider"
|
|
9
12
|
|
|
@@ -50,11 +53,15 @@ module NitroIntelligence
|
|
|
50
53
|
Langfuse.send(:validate_observation_type!, as_type, type_str) unless skip_validation
|
|
51
54
|
|
|
52
55
|
otel_tracer = @tracer_provider.tracer
|
|
53
|
-
otel_span =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
otel_span = if trace_id
|
|
57
|
+
@tracer_provider.start_root_span(name, trace_id: parent_span_context.trace_id, start_time:)
|
|
58
|
+
else
|
|
59
|
+
Langfuse.send(:create_otel_span,
|
|
60
|
+
name:,
|
|
61
|
+
start_time:,
|
|
62
|
+
parent_span_context:,
|
|
63
|
+
otel_tracer:)
|
|
64
|
+
end
|
|
58
65
|
|
|
59
66
|
# Serialize attributes
|
|
60
67
|
# Only set attributes if span is still recording (should always be true here, but guard for safety)
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
module NitroIntelligence
|
|
8
8
|
class LangfuseTracerProvider
|
|
9
9
|
def initialize(config)
|
|
10
|
+
@trace_id_key = OpenTelemetry::Context.create_key("nitro-intelligence-trace-id")
|
|
10
11
|
@tracer_provider = create_tracer_provider(config)
|
|
11
12
|
end
|
|
12
13
|
|
|
@@ -18,6 +19,25 @@ module NitroIntelligence
|
|
|
18
19
|
@tracer_provider.shutdown(timeout:)
|
|
19
20
|
end
|
|
20
21
|
|
|
22
|
+
def start_root_span(name, trace_id:, start_time: nil)
|
|
23
|
+
# A seed identifies the trace, not a missing parent. Preserve propagated
|
|
24
|
+
# attributes while clearing the ambient span for this explicit root.
|
|
25
|
+
context = OpenTelemetry::Trace.context_with_span(OpenTelemetry::Trace::Span::INVALID)
|
|
26
|
+
context = context.set_value(@trace_id_key, trace_id)
|
|
27
|
+
# generate_trace_id has no context argument, so expose the seed through Context.current.
|
|
28
|
+
OpenTelemetry::Context.with_current(context) do
|
|
29
|
+
tracer.start_span(name, with_parent: context, start_timestamp: start_time)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_trace_id
|
|
34
|
+
OpenTelemetry::Context.value(@trace_id_key) || OpenTelemetry::Trace.generate_trace_id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def generate_span_id
|
|
38
|
+
OpenTelemetry::Trace.generate_span_id
|
|
39
|
+
end
|
|
40
|
+
|
|
21
41
|
private
|
|
22
42
|
|
|
23
43
|
def create_tracer_provider(config)
|
|
@@ -36,7 +56,7 @@ module NitroIntelligence
|
|
|
36
56
|
max_export_batch_size: config.batch_size
|
|
37
57
|
)
|
|
38
58
|
|
|
39
|
-
tracer_provider = OpenTelemetry::SDK::Trace::TracerProvider.new
|
|
59
|
+
tracer_provider = OpenTelemetry::SDK::Trace::TracerProvider.new(id_generator: self)
|
|
40
60
|
tracer_provider.add_span_processor(processor)
|
|
41
61
|
span_processor = Langfuse::SpanProcessor.new(config:)
|
|
42
62
|
tracer_provider.add_span_processor(span_processor)
|
|
@@ -53,6 +73,7 @@ module NitroIntelligence
|
|
|
53
73
|
encoded = Base64.strict_encode64(credentials)
|
|
54
74
|
{
|
|
55
75
|
"Authorization" => "Basic #{encoded}",
|
|
76
|
+
"x-langfuse-ingestion-version" => "4",
|
|
56
77
|
}
|
|
57
78
|
end
|
|
58
79
|
end
|
|
@@ -35,12 +35,13 @@ module NitroIntelligence
|
|
|
35
35
|
response
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def score(trace_id:, name:, value:, id: "#{trace_id}-#{name}")
|
|
38
|
+
def score(trace_id:, name:, value:, id: "#{trace_id}-#{name}", data_type: :numeric)
|
|
39
39
|
@project_client.observability_client.create_score(
|
|
40
40
|
id:,
|
|
41
41
|
trace_id:,
|
|
42
42
|
name:,
|
|
43
43
|
value:,
|
|
44
|
+
data_type:,
|
|
44
45
|
environment: NitroIntelligence.environment
|
|
45
46
|
)
|
|
46
47
|
end
|