ruby_llm-responses_api 0.5.0 → 0.5.2

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: 5eafd14a08ce95dc9637f022c3dcd0b88dc979314efd534ec3a3d5dbb2a6e396
4
- data.tar.gz: 6b84beeec2204e791727bb969aac9b9e490e574204c78ab87ec6b69692f1ad7d
3
+ metadata.gz: 0c13536c81c541310d1017204b4d312fa6cca67043cf9600f3e7ef497819a87c
4
+ data.tar.gz: 17b16d29633466bf1d9907ad871afae85029641f4768c849bcc75157e85475f6
5
5
  SHA512:
6
- metadata.gz: 5216a047aa783ed7b221e91f0173fd5785ae003b3006a9dcbcdb07effef569e73cac28fab1129f9e157e15f03a5219d18fdcfcc84c17406f3913fe8cdc1ed761
7
- data.tar.gz: 3aa365e82445b4deb9f3b2dcda4ec47f096728f05c3f7a89eb9076be41ba81e873e2f513b4057391b84d6f1e1b8fd630ade743a46b0c2a37f9c2187eb43efbd6
6
+ metadata.gz: b82fa66f5c8b4a5410a6560c6a3e06530f211e9a0d2738e7170d4f198dc826869cb38a0185fd411ea531a1b2c8029f5a0f983f744e86b8c6d3dc28acf6b6395a
7
+ data.tar.gz: 1bb3d6cc3c1ee74f94e66225acbab358d7022e3f1feefa94e123d418b265d12ec11ef8053fcbee619d134f62681f4a5fb0bb1a62bc5de5565ce4f77f9b7b3cb8
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.2] - 2026-03-18
9
+
10
+ ### Fixed
11
+
12
+ - Fix streamed tool call accumulation: argument deltas no longer overwrite the tool name (PR #3 by @shllg)
13
+ - Fix gem packaging: correct file permissions from 600 to 644 (issue #2 by @myxoh)
14
+ - Move streaming unit tests to dedicated spec file
15
+
16
+ ## [0.5.1] - 2026-03-03
17
+
18
+ ### Added
19
+
20
+ - `tool_choice` support (`auto`, `required`, `none`, or specific function name)
21
+ - `parallel_tool_calls` support (`:many` / `:one`) via RubyLLM's `tool_prefs`
22
+
23
+ ### Fixed
24
+
25
+ - Compatibility with RubyLLM v1.13.0 (`tool_prefs:` parameter in `complete` and `render_payload`)
26
+ - Handle new pre-normalized schema format from `Chat.with_schema`
27
+
8
28
  ## [0.5.0] - 2026-02-25
9
29
 
10
30
  ### Added
@@ -12,10 +12,11 @@ module RubyLLM
12
12
 
13
13
  module_function
14
14
 
15
- def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) # rubocop:disable Metrics/ParameterLists,Lint/UnusedMethodArgument
16
- # Extract system messages for instructions
17
- system_messages = messages.select { |m| m.role == :system }
18
- non_system_messages = messages.reject { |m| m.role == :system }
15
+ # rubocop:disable Metrics/ParameterLists
16
+ def render_payload(messages, tools:, temperature:, model:, stream: false,
17
+ schema: nil, thinking: nil, tool_prefs: nil) # rubocop:disable Lint/UnusedMethodArgument
18
+ tool_prefs ||= {}
19
+ system_messages, non_system_messages = messages.partition { |m| m.role == :system }
19
20
 
20
21
  instructions = system_messages.map { |m| extract_text_content(m.content) }.join("\n\n")
21
22
 
@@ -27,27 +28,54 @@ module RubyLLM
27
28
 
28
29
  payload[:instructions] = instructions unless instructions.empty?
29
30
  payload[:temperature] = temperature unless temperature.nil?
31
+ apply_tools(payload, tools, tool_prefs)
32
+ payload[:text] = build_schema_format(schema) if schema
30
33
 
31
- payload[:tools] = tools.map { |_, tool| tool_for(tool) } if tools.any?
32
-
33
- if schema
34
- payload[:text] = {
35
- format: {
36
- type: 'json_schema',
37
- name: 'response',
38
- schema: schema,
39
- strict: schema[:strict] != false
40
- }
41
- }
42
- end
43
-
44
- # Auto-chain conversations: find the last response_id from assistant messages
45
- # This enables automatic stateful conversations without manual tracking
46
34
  last_response_id = extract_last_response_id(messages)
47
35
  payload[:previous_response_id] = last_response_id if last_response_id
48
36
 
49
37
  payload
50
38
  end
39
+ # rubocop:enable Metrics/ParameterLists
40
+
41
+ # Apply tools and tool preferences to the payload.
42
+ def apply_tools(payload, tools, tool_prefs)
43
+ return unless tools.any?
44
+
45
+ payload[:tools] = tools.map { |_, tool| Tools.tool_for(tool) }
46
+ payload[:tool_choice] = build_tool_choice(tool_prefs[:choice]) unless tool_prefs[:choice].nil?
47
+ payload[:parallel_tool_calls] = tool_prefs[:calls] == :many unless tool_prefs[:calls].nil?
48
+ end
49
+
50
+ # Convert a RubyLLM tool choice symbol to the Responses API format.
51
+ # Responses API accepts "auto", "required", "none", or
52
+ # { type: "function", name: "fn_name" } for a specific function.
53
+ def build_tool_choice(choice)
54
+ case choice
55
+ when :auto, :none, :required
56
+ choice.to_s
57
+ else
58
+ { type: 'function', name: choice.to_s }
59
+ end
60
+ end
61
+
62
+ # Build the Responses API text format block from a schema.
63
+ # Schema arrives pre-normalized as { name:, schema:, strict: } from
64
+ # RubyLLM::Chat.with_schema (v1.13+), or as a raw hash (legacy).
65
+ def build_schema_format(schema)
66
+ schema_name = schema[:name] || 'response'
67
+ schema_def = schema[:schema] || schema
68
+ strict = schema.key?(:strict) ? schema[:strict] : true
69
+
70
+ {
71
+ format: {
72
+ type: 'json_schema',
73
+ name: schema_name,
74
+ schema: schema_def,
75
+ strict: strict
76
+ }
77
+ }
78
+ end
51
79
 
52
80
  def extract_last_response_id(messages)
53
81
  messages
@@ -92,9 +92,13 @@ module RubyLLM
92
92
  call_id = data['call_id'] || data['item_id']
93
93
  return nil unless call_id
94
94
 
95
+ # Argument delta events don't carry a tool name — only the initial
96
+ # output_item.added event does. Omit `id` on nameless deltas so
97
+ # StreamAccumulator appends arguments to the latest tool call
98
+ # instead of creating a new entry that overwrites the named one.
95
99
  {
96
100
  call_id => ToolCall.new(
97
- id: call_id,
101
+ id: data['name'] ? call_id : nil,
98
102
  name: data['name'],
99
103
  arguments: data['delta'] || ''
100
104
  )
@@ -17,7 +17,9 @@ module RubyLLM
17
17
  end
18
18
 
19
19
  # Override to support WebSocket transport via with_params(transport: :websocket)
20
- def complete(messages, tools:, temperature:, model:, params: {}, headers: {}, schema: nil, thinking: nil, &block) # rubocop:disable Metrics/ParameterLists
20
+ # rubocop:disable Metrics/ParameterLists
21
+ def complete(messages, tools:, temperature:, model:, params: {}, headers: {},
22
+ schema: nil, thinking: nil, tool_prefs: nil, &block)
21
23
  if params[:transport]&.to_sym == :websocket
22
24
  ws_complete(messages, tools: tools, temperature: temperature, model: model,
23
25
  params: params.except(:transport), schema: schema,
@@ -26,6 +28,7 @@ module RubyLLM
26
28
  super
27
29
  end
28
30
  end
31
+ # rubocop:enable Metrics/ParameterLists
29
32
 
30
33
  def headers
31
34
  {
@@ -39,7 +39,7 @@ RubyLLM::Providers::OpenAIResponses::ModelRegistry.register_all!
39
39
  module RubyLLM
40
40
  # ResponsesAPI namespace for direct access to helpers and version
41
41
  module ResponsesAPI
42
- VERSION = '0.5.0'
42
+ VERSION = '0.5.2'
43
43
 
44
44
  # Shorthand access to built-in tool helpers
45
45
  BuiltInTools = Providers::OpenAIResponses::BuiltInTools
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-responses_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hasinski