openlayer 0.13.1 → 0.14.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 697bde0fdcf61182dd6235f06ab2ab9439b245251284f66100a53c44a6bfc146
|
|
4
|
+
data.tar.gz: ea5d247e85a34a14143e82e2aa49e39e44c322d7329cbba01967afb15fb7fc5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a22c1acd44798f116733781f5648b8cf74697811c09a8ed76ac73a217b803467dd3213a7efe19a1b57c13b3ff93dddd7ca0b4eb5496ea05228f559a29c830dc5
|
|
7
|
+
data.tar.gz: c2b731b11958e0adc38bd37b30045494a832225daf1d8c79a310fb78b8cb72e49a7765d45700c3fcf01e982a5a830faacc7bf8127b0e84806d4a79dccbfc4f8f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.1 (2026-07-06)
|
|
4
|
+
|
|
5
|
+
Full Changelog: [v0.14.0...v0.14.1](https://github.com/openlayer-ai/openlayer-ruby/compare/v0.14.0...v0.14.1)
|
|
6
|
+
|
|
7
|
+
## 0.14.0 (2026-07-06)
|
|
8
|
+
|
|
9
|
+
Full Changelog: [v0.13.1...v0.14.0](https://github.com/openlayer-ai/openlayer-ruby/compare/v0.13.1...v0.14.0)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **closes OPEN-11569:** support additional columns in ConversationalSearchService tracer ([e5cd8df](https://github.com/openlayer-ai/openlayer-ruby/commit/e5cd8df9335795f7296b394ea007caea3b090475))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **client:** send content-type header for requests with an omitted optional body ([1778b7d](https://github.com/openlayer-ai/openlayer-ruby/commit/1778b7d71ddf2c244be71648141b84666f81addd))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Chores
|
|
22
|
+
|
|
23
|
+
* **internal:** bound formatter parallelism to CPU count ([f8781d0](https://github.com/openlayer-ai/openlayer-ruby/commit/f8781d074e80fc95497513b400c601e246998729))
|
|
24
|
+
|
|
3
25
|
## 0.13.1 (2026-05-14)
|
|
4
26
|
|
|
5
27
|
Full Changelog: [v0.13.0...v0.13.1](https://github.com/openlayer-ai/openlayer-ruby/compare/v0.13.0...v0.13.1)
|
data/README.md
CHANGED
|
@@ -21,15 +21,25 @@ module Openlayer
|
|
|
21
21
|
# Openlayer::Integrations::GoogleConversationalSearchTracer.trace_client(
|
|
22
22
|
# google_client,
|
|
23
23
|
# openlayer_client: openlayer,
|
|
24
|
-
# inference_pipeline_id: 'your-pipeline-id'
|
|
24
|
+
# inference_pipeline_id: 'your-pipeline-id',
|
|
25
|
+
# additional_columns: { environment: 'production' }
|
|
25
26
|
# )
|
|
26
27
|
#
|
|
27
|
-
# # Now all answer_query calls are automatically traced
|
|
28
|
+
# # Now all answer_query calls are automatically traced! Pass
|
|
29
|
+
# # additional_columns on an individual call to attach data (like your
|
|
30
|
+
# # own trace ID) to just that row; it takes precedence over the
|
|
31
|
+
# # static defaults above on a key conflict.
|
|
28
32
|
# response = google_client.answer_query(
|
|
29
33
|
# serving_config: "projects/.../servingConfigs/default",
|
|
30
|
-
# query: { text: "What is the meaning of life?" }
|
|
34
|
+
# query: { text: "What is the meaning of life?" },
|
|
35
|
+
# additional_columns: { trace_id: "abc-123" }
|
|
31
36
|
# )
|
|
32
37
|
class GoogleConversationalSearchTracer
|
|
38
|
+
# Row keys computed by this tracer. Any key in a caller-supplied
|
|
39
|
+
# additional_columns hash matching one of these is dropped, so custom
|
|
40
|
+
# data can never overwrite core trace fields.
|
|
41
|
+
RESERVED_ROW_KEYS = [:query, :answer, :latency_ms, :timestamp, :metadata, :steps, :context, :session_id, :user_id].freeze
|
|
42
|
+
|
|
33
43
|
# Enable tracing on a Google ConversationalSearchService client
|
|
34
44
|
#
|
|
35
45
|
# @param client [Google::Cloud::DiscoveryEngine::V1::ConversationalSearchService::Client]
|
|
@@ -42,8 +52,12 @@ module Openlayer
|
|
|
42
52
|
# Optional session ID to use for all traces. Takes precedence over auto-extracted sessions.
|
|
43
53
|
# @param user_id [String, nil]
|
|
44
54
|
# Optional user ID to use for all traces.
|
|
55
|
+
# @param additional_columns [Hash, nil]
|
|
56
|
+
# Optional static column values merged into every trace sent through this client (e.g. `{ environment: 'production' }`).
|
|
57
|
+
# A value passed to an individual answer_query call takes precedence over these on a key conflict. Keys colliding
|
|
58
|
+
# with a reserved row column (query, answer, latency_ms, timestamp, metadata, steps, context, session_id, user_id) are dropped.
|
|
45
59
|
# @return [void]
|
|
46
|
-
def self.trace_client(client, openlayer_client:, inference_pipeline_id:, session_id: nil, user_id: nil)
|
|
60
|
+
def self.trace_client(client, openlayer_client:, inference_pipeline_id:, session_id: nil, user_id: nil, additional_columns: {})
|
|
47
61
|
# Store original method reference
|
|
48
62
|
original_answer_query = client.method(:answer_query)
|
|
49
63
|
|
|
@@ -52,6 +66,10 @@ module Openlayer
|
|
|
52
66
|
# Capture start time
|
|
53
67
|
start_time = Time.now
|
|
54
68
|
|
|
69
|
+
# Extract per-call additional columns before forwarding to the
|
|
70
|
+
# real client; Google's client never sees this key
|
|
71
|
+
call_additional_columns = kwargs.delete(:additional_columns)
|
|
72
|
+
|
|
55
73
|
# Execute the original method
|
|
56
74
|
response = original_answer_query.call(*args, **kwargs, &block)
|
|
57
75
|
|
|
@@ -69,7 +87,9 @@ module Openlayer
|
|
|
69
87
|
openlayer_client: openlayer_client,
|
|
70
88
|
inference_pipeline_id: inference_pipeline_id,
|
|
71
89
|
session_id: session_id,
|
|
72
|
-
user_id: user_id
|
|
90
|
+
user_id: user_id,
|
|
91
|
+
additional_columns: additional_columns,
|
|
92
|
+
call_additional_columns: call_additional_columns
|
|
73
93
|
)
|
|
74
94
|
rescue StandardError => e
|
|
75
95
|
# Never break the user's application due to tracing errors
|
|
@@ -95,8 +115,10 @@ module Openlayer
|
|
|
95
115
|
# @param inference_pipeline_id [String] Pipeline ID
|
|
96
116
|
# @param session_id [String, nil] Optional session ID (takes precedence over auto-extracted)
|
|
97
117
|
# @param user_id [String, nil] Optional user ID
|
|
118
|
+
# @param additional_columns [Hash, nil] Optional static column values (see {.trace_client})
|
|
119
|
+
# @param call_additional_columns [Hash, nil] Optional per-call column values; takes precedence over additional_columns
|
|
98
120
|
# @return [void]
|
|
99
|
-
def self.send_trace(args:, kwargs:, response:, start_time:, end_time:, openlayer_client:, inference_pipeline_id:, session_id: nil, user_id: nil)
|
|
121
|
+
def self.send_trace(args:, kwargs:, response:, start_time:, end_time:, openlayer_client:, inference_pipeline_id:, session_id: nil, user_id: nil, additional_columns: {}, call_additional_columns: {})
|
|
100
122
|
# Calculate latency
|
|
101
123
|
latency_ms = ((end_time - start_time) * 1000).round(2)
|
|
102
124
|
|
|
@@ -199,6 +221,12 @@ module Openlayer
|
|
|
199
221
|
trace_data[:config][:userIdColumnName] = "user_id"
|
|
200
222
|
end
|
|
201
223
|
|
|
224
|
+
# Merge additional columns (per-call values take precedence over
|
|
225
|
+
# static defaults; keys colliding with reserved row columns are
|
|
226
|
+
# dropped so custom data can never corrupt core trace fields)
|
|
227
|
+
extra_columns = resolve_additional_columns(additional_columns, call_additional_columns)
|
|
228
|
+
trace_data[:rows][0].merge!(extra_columns) unless extra_columns.empty?
|
|
229
|
+
|
|
202
230
|
# Send to Openlayer
|
|
203
231
|
openlayer_client
|
|
204
232
|
.inference_pipelines
|
|
@@ -317,6 +345,9 @@ module Openlayer
|
|
|
317
345
|
|
|
318
346
|
# Extract references from answer
|
|
319
347
|
#
|
|
348
|
+
# Each reference's `content` is a oneof: exactly one of chunk_info,
|
|
349
|
+
# structured_document_info, or unstructured_document_info is present.
|
|
350
|
+
#
|
|
320
351
|
# @param answer [Object] Answer object from response
|
|
321
352
|
# @return [Array<Hash>, nil] Array of reference hashes or nil
|
|
322
353
|
def self.extract_references(answer)
|
|
@@ -326,25 +357,85 @@ module Openlayer
|
|
|
326
357
|
return nil if references.nil? || !references.respond_to?(:each_with_index)
|
|
327
358
|
|
|
328
359
|
references.each_with_index.map do |reference, index|
|
|
329
|
-
|
|
330
|
-
next nil if chunk_info.nil?
|
|
331
|
-
|
|
332
|
-
doc_metadata = safe_extract(chunk_info, :document_metadata)
|
|
333
|
-
|
|
334
|
-
{
|
|
335
|
-
reference_id: index.to_s,
|
|
336
|
-
content: safe_extract(chunk_info, :content),
|
|
337
|
-
relevance_score: safe_extract(chunk_info, :relevance_score)&.to_f,
|
|
338
|
-
document_id: doc_metadata ? safe_extract(doc_metadata, :document) : nil,
|
|
339
|
-
uri: doc_metadata ? safe_extract(doc_metadata, :uri) : nil,
|
|
340
|
-
title: doc_metadata ? safe_extract(doc_metadata, :title) : nil
|
|
341
|
-
}.compact
|
|
360
|
+
extract_reference(reference, index)
|
|
342
361
|
end.compact
|
|
343
362
|
rescue StandardError => e
|
|
344
363
|
warn_if_debug("[Openlayer] Failed to extract references: #{e.message}")
|
|
345
364
|
nil
|
|
346
365
|
end
|
|
347
366
|
|
|
367
|
+
# Extract a single reference hash from whichever `content` oneof
|
|
368
|
+
# variant is present on it
|
|
369
|
+
#
|
|
370
|
+
# @param reference [Object] Reference object
|
|
371
|
+
# @param index [Integer] Reference's index in the references array
|
|
372
|
+
# @return [Hash, nil] Reference hash, or nil if no known variant is set
|
|
373
|
+
def self.extract_reference(reference, index)
|
|
374
|
+
if (chunk_info = safe_extract(reference, :chunk_info))
|
|
375
|
+
extract_chunk_info_reference(chunk_info, index)
|
|
376
|
+
elsif (structured_document_info = safe_extract(reference, :structured_document_info))
|
|
377
|
+
extract_structured_document_info_reference(structured_document_info, index)
|
|
378
|
+
elsif (unstructured_document_info = safe_extract(reference, :unstructured_document_info))
|
|
379
|
+
extract_unstructured_document_info_reference(unstructured_document_info, index)
|
|
380
|
+
end
|
|
381
|
+
rescue StandardError => e
|
|
382
|
+
warn_if_debug("[Openlayer] Failed to extract reference: #{e.message}")
|
|
383
|
+
nil
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# Build a reference hash from a ChunkInfo variant
|
|
387
|
+
#
|
|
388
|
+
# @param chunk_info [Object] ChunkInfo object
|
|
389
|
+
# @param index [Integer] Reference's index in the references array
|
|
390
|
+
# @return [Hash] Reference hash
|
|
391
|
+
def self.extract_chunk_info_reference(chunk_info, index)
|
|
392
|
+
doc_metadata = safe_extract(chunk_info, :document_metadata)
|
|
393
|
+
|
|
394
|
+
{
|
|
395
|
+
reference_id: index.to_s,
|
|
396
|
+
content: safe_extract(chunk_info, :content),
|
|
397
|
+
relevance_score: safe_extract(chunk_info, :relevance_score)&.to_f,
|
|
398
|
+
document_id: doc_metadata ? safe_extract(doc_metadata, :document) : nil,
|
|
399
|
+
uri: doc_metadata ? safe_extract(doc_metadata, :uri) : nil,
|
|
400
|
+
title: doc_metadata ? safe_extract(doc_metadata, :title) : nil
|
|
401
|
+
}.compact
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# Build a reference hash from a StructuredDocumentInfo variant
|
|
405
|
+
#
|
|
406
|
+
# @param structured_document_info [Object] StructuredDocumentInfo object
|
|
407
|
+
# @param index [Integer] Reference's index in the references array
|
|
408
|
+
# @return [Hash] Reference hash
|
|
409
|
+
def self.extract_structured_document_info_reference(structured_document_info, index)
|
|
410
|
+
{
|
|
411
|
+
reference_id: index.to_s,
|
|
412
|
+
document_id: safe_extract(structured_document_info, :document),
|
|
413
|
+
uri: safe_extract(structured_document_info, :uri),
|
|
414
|
+
title: safe_extract(structured_document_info, :title),
|
|
415
|
+
struct_data: safe_extract(structured_document_info, :struct_data)
|
|
416
|
+
}.compact
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Build a reference hash from an UnstructuredDocumentInfo variant
|
|
420
|
+
#
|
|
421
|
+
# @param unstructured_document_info [Object] UnstructuredDocumentInfo object
|
|
422
|
+
# @param index [Integer] Reference's index in the references array
|
|
423
|
+
# @return [Hash] Reference hash
|
|
424
|
+
def self.extract_unstructured_document_info_reference(unstructured_document_info, index)
|
|
425
|
+
chunk_contents = safe_extract(unstructured_document_info, :chunk_contents)
|
|
426
|
+
content = if chunk_contents.respond_to?(:map)
|
|
427
|
+
chunk_contents.map { |chunk| safe_extract(chunk, :content) }.compact.join("\n")
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
{
|
|
431
|
+
reference_id: index.to_s,
|
|
432
|
+
content: (content unless content.nil? || content.empty?),
|
|
433
|
+
document_id: safe_extract(unstructured_document_info, :document),
|
|
434
|
+
uri: safe_extract(unstructured_document_info, :uri),
|
|
435
|
+
title: safe_extract(unstructured_document_info, :title)
|
|
436
|
+
}.compact
|
|
437
|
+
end
|
|
438
|
+
|
|
348
439
|
# Extract related questions from answer
|
|
349
440
|
#
|
|
350
441
|
# @param answer [Object] Answer object from response
|
|
@@ -594,6 +685,45 @@ module Openlayer
|
|
|
594
685
|
nil
|
|
595
686
|
end
|
|
596
687
|
|
|
688
|
+
# Merge static and per-call additional columns into a single Hash of
|
|
689
|
+
# extra row columns. Call-level values take precedence over static
|
|
690
|
+
# ones on key conflict, and any key colliding with a reserved row
|
|
691
|
+
# column is dropped.
|
|
692
|
+
#
|
|
693
|
+
# @param static_columns [Object] Value passed to trace_client (expected Hash)
|
|
694
|
+
# @param call_columns [Object] Value passed to an individual answer_query call (expected Hash)
|
|
695
|
+
# @return [Hash] Extra columns safe to merge onto a trace row
|
|
696
|
+
def self.resolve_additional_columns(static_columns, call_columns)
|
|
697
|
+
merged = normalize_additional_columns(static_columns).merge(normalize_additional_columns(call_columns))
|
|
698
|
+
|
|
699
|
+
merged.each_with_object({}) do |(key, value), result|
|
|
700
|
+
if RESERVED_ROW_KEYS.include?(key)
|
|
701
|
+
warn_if_debug("[Openlayer] additional_columns key :#{key} collides with a reserved column and was ignored")
|
|
702
|
+
else
|
|
703
|
+
result[key] = value
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
# Normalize an additional_columns value into a Hash with Symbol keys.
|
|
709
|
+
# Non-Hash input (or a key that can't be a Symbol) is dropped rather
|
|
710
|
+
# than raising, so a caller mistake can never break tracing.
|
|
711
|
+
#
|
|
712
|
+
# @param columns [Object] Expected to be a Hash of column name => value
|
|
713
|
+
# @return [Hash]
|
|
714
|
+
def self.normalize_additional_columns(columns)
|
|
715
|
+
return {} unless columns.is_a?(Hash)
|
|
716
|
+
|
|
717
|
+
columns.each_with_object({}) do |(key, value), result|
|
|
718
|
+
next unless key.respond_to?(:to_sym)
|
|
719
|
+
|
|
720
|
+
result[key.to_sym] = value
|
|
721
|
+
end
|
|
722
|
+
rescue StandardError => e
|
|
723
|
+
warn_if_debug("[Openlayer] Failed to normalize additional columns: #{e.message}")
|
|
724
|
+
{}
|
|
725
|
+
end
|
|
726
|
+
|
|
597
727
|
# Safely extract a field from an object
|
|
598
728
|
#
|
|
599
729
|
# @param obj [Object] Object to extract from
|
|
@@ -650,6 +780,10 @@ module Openlayer
|
|
|
650
780
|
:extract_citations,
|
|
651
781
|
:extract_citation_sources,
|
|
652
782
|
:extract_references,
|
|
783
|
+
:extract_reference,
|
|
784
|
+
:extract_chunk_info_reference,
|
|
785
|
+
:extract_structured_document_info_reference,
|
|
786
|
+
:extract_unstructured_document_info_reference,
|
|
653
787
|
:extract_related_questions,
|
|
654
788
|
:extract_steps,
|
|
655
789
|
:extract_step_data,
|
|
@@ -659,6 +793,8 @@ module Openlayer
|
|
|
659
793
|
:extract_session,
|
|
660
794
|
:extract_user_pseudo_id,
|
|
661
795
|
:extract_query_understanding_info,
|
|
796
|
+
:resolve_additional_columns,
|
|
797
|
+
:normalize_additional_columns,
|
|
662
798
|
:safe_extract,
|
|
663
799
|
:safe_count,
|
|
664
800
|
:extract_timestamp
|
|
@@ -306,7 +306,10 @@ module Openlayer
|
|
|
306
306
|
Openlayer::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
|
|
307
307
|
end
|
|
308
308
|
|
|
309
|
-
|
|
309
|
+
# Generated methods always pass `req[:body]` for operations that define a
|
|
310
|
+
# request body, so only elide the content-type header when the operation
|
|
311
|
+
# has no body at all, not when an optional body param was omitted.
|
|
312
|
+
headers.delete("content-type") if body.nil? && !req.key?(:body)
|
|
310
313
|
|
|
311
314
|
url = Openlayer::Internal::Util.join_parsed_uri(
|
|
312
315
|
@base_url_components,
|
data/lib/openlayer/version.rb
CHANGED
|
@@ -10,7 +10,8 @@ module Openlayer
|
|
|
10
10
|
openlayer_client: Openlayer::Client,
|
|
11
11
|
inference_pipeline_id: String,
|
|
12
12
|
session_id: T.nilable(String),
|
|
13
|
-
user_id: T.nilable(String)
|
|
13
|
+
user_id: T.nilable(String),
|
|
14
|
+
additional_columns: T::Hash[Symbol, T.untyped]
|
|
14
15
|
).void
|
|
15
16
|
end
|
|
16
17
|
def self.trace_client(
|
|
@@ -18,7 +19,8 @@ module Openlayer
|
|
|
18
19
|
openlayer_client:,
|
|
19
20
|
inference_pipeline_id:,
|
|
20
21
|
session_id: nil,
|
|
21
|
-
user_id: nil
|
|
22
|
+
user_id: nil,
|
|
23
|
+
additional_columns: {}
|
|
22
24
|
)
|
|
23
25
|
end
|
|
24
26
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openlayer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Openlayer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cgi
|