payloop 0.0.3 → 0.0.4

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: 79647d261089034fcabaf8bc8f02468c22a4dafac253f0e3b387bf169f0fe410
4
- data.tar.gz: 5d7796139816c6f880d0acfa0e364065480835def4e1f98fa9fd30004a1cab6b
3
+ metadata.gz: 0d134effe2b909cc928b4dfc80149d3c9dcde238be7630bb37dba541fa33be0a
4
+ data.tar.gz: ddf58b4672381d25ab90b349c6bf29391e4fe099f9e304ba20f5169d3ee6bf1c
5
5
  SHA512:
6
- metadata.gz: e87d2509afeee593b39cd9ab7a519f2fde382aaa939fecaa370cf4817aa38009a5c68a3550444052dbf0867a6162c6a3cd5771cfb039a4f812b280a9e3bb29f6
7
- data.tar.gz: d7ac74462b724bce7178e6588d41bb6e91e4bf5ec98ff067bd41164cf17b4fd570892eacdb633b797fc3aab1857503cd4cf88998ad95d675be647980fd0f85d0
6
+ metadata.gz: 3b943601582c65f45f66209c6c7aa37c0041f3f961130b3fce5109f09369359d05cc2c7264ca85ffea99b067c95b7c1419d165bf11d847fb994f43a29697c4df
7
+ data.tar.gz: 2d4ff576bccc13c526dc80ae8ab9b2dfb72a9480f3f6c88ed261a723a217271867a3f6f88e2083f8bb5a65813362b5603e8396bd5a6c944885cbf1c4010fe963
data/CHANGELOG.md CHANGED
@@ -29,4 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29
29
  ## [0.0.3] - 2025-10-27
30
30
 
31
31
  ### Fixed
32
- - Corrected Payloop request payload structure for Google GenAI
32
+ - Correct Payloop request payload structure for Google GenAI
33
+
34
+ ## [0.0.4] - 2025-10-28
35
+
36
+ ### Fixed
37
+ - Correct Payloop request payload structure for OpenAI streaming
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Payloop
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
@@ -84,11 +84,62 @@ module Payloop
84
84
  collector.submit_async(payload)
85
85
  end
86
86
 
87
+ def payloop_merge_streaming_chunk(accumulated, chunk)
88
+ chunk.each do |key, value|
89
+ if accumulated.key?(key)
90
+ case accumulated[key]
91
+ when Hash
92
+ payloop_merge_streaming_chunk(accumulated[key], value) if value.is_a?(Hash)
93
+ when Array
94
+ # Concatenate arrays (matches Python SDK behavior: data[key].extend(chunk_value))
95
+ accumulated[key].concat(value) if value.is_a?(Array)
96
+ else
97
+ accumulated[key] = value
98
+ end
99
+ else
100
+ accumulated[key] = value
101
+ end
102
+ end
103
+ accumulated
104
+ end
105
+
106
+ def payloop_normalize_openai_chunk(chunk)
107
+ # Normalize OpenAI streaming chunks to match Python SDK format
108
+ # Ensure delta objects have all keys present with null values
109
+ return chunk unless chunk.is_a?(Hash)
110
+
111
+ if chunk["choices"].is_a?(Array)
112
+ chunk["choices"].each do |choice|
113
+ next unless choice.is_a?(Hash) && choice.key?("delta")
114
+
115
+ delta = choice["delta"]
116
+ if delta.is_a?(Hash)
117
+ # Add missing keys with nil values to match Python SDK
118
+ delta["role"] ||= nil unless delta.key?("role")
119
+ delta["content"] ||= nil unless delta.key?("content")
120
+ delta["refusal"] ||= nil unless delta.key?("refusal")
121
+ delta["tool_calls"] ||= nil unless delta.key?("tool_calls")
122
+ delta["function_call"] ||= nil unless delta.key?("function_call")
123
+ end
124
+ end
125
+ end
126
+
127
+ chunk
128
+ end
129
+
87
130
  private
88
131
 
89
132
  def extract_query(_method, _args, kwargs)
90
133
  # Deep copy to avoid mutation issues
91
- deep_copy(kwargs)
134
+ query = deep_copy(kwargs)
135
+
136
+ # Normalize stream parameter: convert Proc to boolean true
137
+ # This ensures streaming requests are properly serialized to JSON
138
+ if query.is_a?(Hash) && query[:stream].is_a?(Proc)
139
+ query[:stream] = true
140
+ end
141
+
142
+ query
92
143
  rescue StandardError
93
144
  {}
94
145
  end
@@ -45,15 +45,38 @@ module Payloop
45
45
  define_method(:chat) do |parameters: {}|
46
46
  start_time = Time.now
47
47
 
48
+ # Detect streaming and wrap callback to accumulate chunks
49
+ streaming = parameters[:stream].is_a?(Proc)
50
+ accumulated_response = {} if streaming
51
+
52
+ if streaming
53
+ # Configure streaming to include usage (matches Python SDK behavior)
54
+ parameters[:stream_options] ||= {}
55
+ parameters[:stream_options][:include_usage] = true
56
+
57
+ user_callback = parameters[:stream]
58
+ parameters[:stream] = proc do |chunk, bytesize|
59
+ # Normalize chunk to match Python SDK format (add missing delta keys with nil)
60
+ normalized_chunk = payloop_normalize_openai_chunk(chunk)
61
+ # Accumulate chunk (merge into accumulated_response)
62
+ payloop_merge_streaming_chunk(accumulated_response, normalized_chunk)
63
+ # Call user's original callback with original chunk (don't modify user's data)
64
+ user_callback.call(chunk, bytesize)
65
+ end
66
+ end
67
+
48
68
  # Call original method
49
69
  response = original_chat(parameters: parameters)
50
70
 
71
+ # Use accumulated response for streaming, otherwise use returned response
72
+ final_response = streaming ? accumulated_response : response
73
+
51
74
  # Submit analytics
52
75
  payloop_submit_analytics(
53
76
  method: :chat,
54
77
  args: [],
55
78
  kwargs: parameters,
56
- response: response,
79
+ response: final_response,
57
80
  start_time: start_time,
58
81
  end_time: Time.now,
59
82
  title: OPENAI_CLIENT_TITLE
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: payloop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Payloop
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-27 00:00:00.000000000 Z
11
+ date: 2025-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby