girb-ruby_llm 0.1.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cfe19c19205f70c38e3f95b1fca105e6c3c5d5c3398871e1a43cf5310e7bfb7a
4
- data.tar.gz: 52dcce3593879b4bd66864f8928e5b7eb0aa1f4adc186c62bc665bfc217c1fd5
3
+ metadata.gz: 9c9b33177aa9d0dc0bb6b1f3dcaf44ebaa74bf3028f46458115dc9359b759b80
4
+ data.tar.gz: b10c9fe5007191e1c4f2a3897aa2b8bea2681bdcc9c52386b041b296aa53eb39
5
5
  SHA512:
6
- metadata.gz: e030b3dd7df74d07d15b6bfef72e61c7f5c1d9c6d8aa093eed9440855960c8e0773bb3046461a8d05e496e07d8891d6366a4e17bac299cb74d4d8da3bfb5fc2b
7
- data.tar.gz: eb256345ace0da90621bd10069a4b0ed3dc94bc72b768d5da79cfd3cf4f558250fae6d9aa0aa3782aa66c632c86783931cf025739dab8dd6e1826b6010dca17f
6
+ metadata.gz: 1726d79382bc171b089c7c761f567997b2ac43b7b14791529cbbeb3ba9a0d03b4d6e81b06723dec280f9d13631e4c1ed3dd9ceaca7a2067e472d9496624118b1
7
+ data.tar.gz: 86888dd0aee1bd750a331f6231c346100f029131018c830661242b65909053de391af7267de7fc550759ee80558d312f68870ff6aa081da038274fddc94d15ee
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-02-07
4
+
5
+ ### Fixed
6
+
7
+ - Fix Gemini 3 thought_signature error on function calls
8
+ - Preserve provider-specific metadata (e.g. thought_signature) through tool call lifecycle
9
+
10
+ ## [0.2.0] - 2026-02-05
11
+
12
+ ### Added
13
+
14
+ - Debug gem (rdbg) support for AI-assisted debugging
15
+
3
16
  ## [0.1.2] - 2026-02-03
4
17
 
5
18
  ### Fixed
data/README.md CHANGED
@@ -117,7 +117,7 @@ require 'girb-ruby_llm'
117
117
 
118
118
  # Set OPENAI_API_KEY environment variable
119
119
  Girb.configure do |c|
120
- c.provider = Girb::Providers::RubyLlm.new(model: 'gpt-4o')
120
+ c.provider = Girb::Providers::RubyLlm.new(model: 'gpt-5.2-2025-12-11')
121
121
  end
122
122
  ```
123
123
 
@@ -129,7 +129,7 @@ require 'girb-ruby_llm'
129
129
 
130
130
  # Set ANTHROPIC_API_KEY environment variable
131
131
  Girb.configure do |c|
132
- c.provider = Girb::Providers::RubyLlm.new(model: 'claude-sonnet-4-20250514')
132
+ c.provider = Girb::Providers::RubyLlm.new(model: 'claude-opus-4-5')
133
133
  end
134
134
  ```
135
135
 
data/README_ja.md CHANGED
@@ -115,7 +115,7 @@ require 'girb-ruby_llm'
115
115
 
116
116
  # OPENAI_API_KEY 環境変数を設定
117
117
  Girb.configure do |c|
118
- c.provider = Girb::Providers::RubyLlm.new(model: 'gpt-4o')
118
+ c.provider = Girb::Providers::RubyLlm.new(model: 'gpt-5.2-2025-12-11')
119
119
  end
120
120
  ```
121
121
 
@@ -127,7 +127,7 @@ require 'girb-ruby_llm'
127
127
 
128
128
  # ANTHROPIC_API_KEY 環境変数を設定
129
129
  Girb.configure do |c|
130
- c.provider = Girb::Providers::RubyLlm.new(model: 'claude-sonnet-4-20250514')
130
+ c.provider = Girb::Providers::RubyLlm.new(model: 'claude-opus-4-5')
131
131
  end
132
132
  ```
133
133
 
@@ -6,23 +6,11 @@ require "girb/providers/base"
6
6
  module Girb
7
7
  module Providers
8
8
  class RubyLlm < Base
9
- # Thread-local storage for current binding
10
- def self.current_binding=(binding)
11
- Thread.current[:girb_ruby_llm_binding] = binding
12
- end
13
-
14
- def self.current_binding
15
- Thread.current[:girb_ruby_llm_binding]
16
- end
17
-
18
9
  def initialize(model: nil)
19
10
  @model = model
20
11
  end
21
12
 
22
13
  def chat(messages:, system_prompt:, tools:, binding: nil)
23
- # Store binding for tool execution
24
- self.class.current_binding = binding
25
-
26
14
  # Use specified model or RubyLLM's default
27
15
  chat_options = @model ? { model: @model } : {}
28
16
  ruby_llm_chat = ::RubyLLM.chat(**chat_options)
@@ -30,31 +18,41 @@ module Girb
30
18
  # Set system prompt
31
19
  ruby_llm_chat.with_instructions(system_prompt) if system_prompt && !system_prompt.empty?
32
20
 
33
- # Add tools
21
+ # Add tool schemas (for API payload generation only, not auto-executed)
34
22
  tool_instances = build_tools(tools)
35
23
  tool_instances.each { |tool| ruby_llm_chat.with_tool(tool) }
36
24
 
37
- # Add messages except the last user message
38
- add_messages_to_chat(ruby_llm_chat, messages[0..-2])
39
-
40
- # Get the last user message
41
- last_message = messages.last
42
- last_content = extract_content(last_message)
25
+ # Add all messages to the chat
26
+ add_messages_to_chat(ruby_llm_chat, messages)
43
27
 
44
- # Send the request - RubyLLM will auto-execute tools
45
- response = ruby_llm_chat.ask(last_content)
28
+ # Get raw response without auto-executing tools
29
+ response = raw_complete(ruby_llm_chat)
46
30
 
47
31
  parse_response(response)
48
32
  rescue Faraday::BadRequestError => e
49
33
  Response.new(error: "API Error: #{e.message}")
50
34
  rescue StandardError => e
51
35
  Response.new(error: "Error: #{e.message}")
52
- ensure
53
- self.class.current_binding = nil
54
36
  end
55
37
 
56
38
  private
57
39
 
40
+ # Call provider.complete() directly, bypassing RubyLLM's handle_tool_calls.
41
+ # This returns the raw response so the caller can handle tool execution.
42
+ def raw_complete(chat)
43
+ provider = chat.instance_variable_get(:@provider)
44
+ provider.complete(
45
+ chat.messages,
46
+ tools: chat.tools,
47
+ temperature: chat.instance_variable_get(:@temperature),
48
+ model: chat.model,
49
+ params: chat.params,
50
+ headers: chat.headers,
51
+ schema: chat.schema,
52
+ thinking: chat.instance_variable_get(:@thinking)
53
+ )
54
+ end
55
+
58
56
  def add_messages_to_chat(chat, messages)
59
57
  messages.each do |msg|
60
58
  case msg[:role]
@@ -63,16 +61,16 @@ module Girb
63
61
  when :assistant
64
62
  chat.add_message(role: :assistant, content: msg[:content])
65
63
  when :tool_call
66
- # Add as assistant message with tool_calls
64
+ id = msg[:id] || "call_#{SecureRandom.hex(12)}"
65
+ tool_call_opts = { id: id, name: msg[:name], arguments: msg[:args] }
66
+ if msg.dig(:metadata, :thought_signature)
67
+ tool_call_opts[:thought_signature] = msg[:metadata][:thought_signature]
68
+ end
67
69
  chat.add_message(
68
70
  role: :assistant,
69
71
  content: nil,
70
72
  tool_calls: {
71
- msg[:name] => ::RubyLLM::ToolCall.new(
72
- id: msg[:id] || "call_#{SecureRandom.hex(12)}",
73
- name: msg[:name],
74
- arguments: msg[:args]
75
- )
73
+ id => ::RubyLLM::ToolCall.new(**tool_call_opts)
76
74
  }
77
75
  )
78
76
  when :tool_result
@@ -85,15 +83,6 @@ module Girb
85
83
  end
86
84
  end
87
85
 
88
- def extract_content(message)
89
- case message[:role]
90
- when :user, :assistant
91
- message[:content]
92
- else
93
- message[:content].to_s
94
- end
95
- end
96
-
97
86
  def build_tools(tools)
98
87
  return [] if tools.nil? || tools.empty?
99
88
 
@@ -107,11 +96,10 @@ module Girb
107
96
  tool_description = tool_def[:description]
108
97
  tool_parameters = tool_def[:parameters] || {}
109
98
 
110
- # Create a dynamic tool class
99
+ # Create a dynamic tool class for schema generation only
111
100
  tool_class = Class.new(::RubyLLM::Tool) do
112
101
  description tool_description
113
102
 
114
- # Define parameters
115
103
  properties = tool_parameters[:properties] || {}
116
104
  required_params = tool_parameters[:required] || []
117
105
 
@@ -122,41 +110,11 @@ module Girb
122
110
  required: required_params.include?(prop_name.to_s) || required_params.include?(prop_name)
123
111
  end
124
112
 
125
- # Store tool_name for execute method and override name for RubyLLM
126
- define_method(:girb_tool_name) { tool_name }
127
113
  define_method(:name) { tool_name }
128
114
 
129
- # Execute method - actually execute the girb tool
130
- define_method(:execute) do |**args|
131
- tool_name_for_log = girb_tool_name
132
-
133
- if Girb.configuration&.debug
134
- args_str = args.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
135
- puts "[girb] Tool: #{tool_name_for_log}(#{args_str})"
136
- end
137
-
138
- binding = Girb::Providers::RubyLlm.current_binding
139
- girb_tool_class = Girb::Tools.find_tool(tool_name_for_log)
140
-
141
- unless girb_tool_class
142
- return { error: "Unknown tool: #{tool_name_for_log}" }
143
- end
144
-
145
- girb_tool = girb_tool_class.new
146
-
147
- result = if binding
148
- girb_tool.execute(binding, **args)
149
- else
150
- { error: "No binding available for tool execution" }
151
- end
152
-
153
- if Girb.configuration&.debug && result.is_a?(Hash) && result[:error]
154
- puts "[girb] Tool error: #{result[:error]}"
155
- end
156
-
157
- result
158
- rescue StandardError => e
159
- { error: "Tool execution failed: #{e.class} - #{e.message}" }
115
+ # Not used tool execution is handled by the caller's tool loop
116
+ define_method(:execute) do |**_args|
117
+ raise "Tool execution should be handled by the caller, not by the provider"
160
118
  end
161
119
  end
162
120
 
@@ -176,10 +134,15 @@ module Girb
176
134
  return [] unless response.tool_call?
177
135
 
178
136
  response.tool_calls.map do |_id, tool_call|
179
- {
137
+ fc = {
138
+ id: tool_call.id,
180
139
  name: tool_call.name.to_s,
181
140
  args: tool_call.arguments || {}
182
141
  }
142
+ if tool_call.respond_to?(:thought_signature) && tool_call.thought_signature
143
+ fc[:metadata] = { thought_signature: tool_call.thought_signature }
144
+ end
145
+ fc
183
146
  end
184
147
  end
185
148
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GirbRubyLlm
4
- VERSION = "0.1.2"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: girb-ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rira100000000