-
-
Notifications
You must be signed in to change notification settings - Fork 342
feat(gemini): Add thought_signature support for Gemini 3 function cal… #542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ling Fixes crmne#521 Gemini 3 Pro models require thought_signature to be preserved and returned during multi-turn function calling conversations. Without this, the API returns an error: "Function call is missing a thought_signature in functionCall parts." Changes: - Add thought_signature attribute to ToolCall class - Extract thoughtSignature from Gemini API responses in extract_tool_calls - Include thoughtSignature in functionCall parts via format_tool_call - Include thoughtSignature in functionResponse parts via format_tool_result - Update MessageFormatter to store and pass signature metadata This is backward compatible - works with Gemini 2.5 (where signatures are optional) and other providers (which don't use thought signatures). See: https://ai.google.dev/gemini-api/docs/thought-signatures
|
This is really good, works well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch is throwing an error in an application that's using the ActiveRecord-backed models, for Chat, Message, ToolCall etc:
Example stacktrace:
/Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:57:in 'ActiveModel::AttributeAssignment#attribute_writer_missing': unknown attribute 'thought_signature' for ToolCall. (ActiveModel::UnknownAttributeError)
raise UnknownAttributeError.new(self, name)
^^^^^
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:74:in 'ActiveModel::AttributeAssignment#_assign_attribute'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:17:in 'block in ActiveRecord::AttributeAssignment#_assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:9:in 'Hash#each'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:9:in 'ActiveRecord::AttributeAssignment#_assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:34:in 'ActiveModel::AttributeAssignment#assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/api.rb:81:in 'ActiveModel::API#initialize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/core.rb:478:in 'ActiveRecord::Core#initialize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/inheritance.rb:76:in 'Class#new'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/inheritance.rb:76:in 'ActiveRecord::Inheritance::ClassMethods#new'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/reflection.rb:183:in 'ActiveRecord::Reflection::AbstractReflection#build_association'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/association.rb:384:in 'ActiveRecord::Associations::Association#build_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_association.rb:362:in 'ActiveRecord::Associations::CollectionAssociation#_create_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/has_many_association.rb:147:in 'ActiveRecord::Associations::HasManyAssociation#_create_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/association.rb:232:in 'ActiveRecord::Associations::Association#create!'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_proxy.rb:366:in 'ActiveRecord::Associations::CollectionProxy#create!'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:288:in 'block in RubyLLM::ActiveRecord::ChatMethods#persist_tool_calls'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:285:in 'Hash#each_value'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:285:in 'RubyLLM::ActiveRecord::ChatMethods#persist_tool_calls'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:279:in 'block in RubyLLM::ActiveRecord::ChatMethods#persist_message_completion'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/transaction.rb:626:in 'block in ActiveRecord::ConnectionAdapters::TransactionManager#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/concurrency/null_lock.rb:9:in 'ActiveSupport::Concurrency::NullLock#synchronize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/transaction.rb:623:in 'ActiveRecord::ConnectionAdapters::TransactionManager#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:367:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:359:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:233:in 'block in ActiveRecord::Transactions::ClassMethods#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:418:in 'ActiveRecord::ConnectionAdapters::ConnectionPool#with_connection'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_handling.rb:310:in 'ActiveRecord::ConnectionHandling#with_connection'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:232:in 'ActiveRecord::Transactions::ClassMethods#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:353:in 'ActiveRecord::Transactions#transaction'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:252:in 'RubyLLM::ActiveRecord::ChatMethods#persist_message_completion'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:236:in 'block in RubyLLM::ActiveRecord::ChatMethods#setup_persistence_callbacks'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/chat.rb:147:in 'RubyLLM::Chat#complete'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:198:in 'RubyLLM::ActiveRecord::ChatMethods#complete'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:192:in 'RubyLLM::ActiveRecord::ChatMethods#ask'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:51:in 'Agent#handle_chat'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:26:in 'block in Agent#run'
from <internal:kernel>:168:in 'Kernel#loop'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:20:in 'Agent#run'
from exe/code-o-matic:29:in '<main>'
Edit: the error that causes this stacktrace happens partway through a conversation when the user prompt causes the assistant (Gemini-3-pro) to call a tool defined by the application ('code-o-matic' in the stacktrace)
Reviewing the code I was going to make the comment that none of the changes seemed to address persistence, but I'm still feeling my way into this codebase so wasn't sure until I tested it in my app.
|
You need to run a migration to add though_signature column to the
tool_calls table. Then it will work
…On Mon, 22 Dec, 2025, 12:01 Mark Weston, ***@***.***> wrote:
***@***.**** commented on this pull request.
This branch is throwing an error in an application that's using the
ActiveRecord-backed models, for Chat, Message, ToolCall etc:
Example stacktrace:
/Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:57:in
'ActiveModel::AttributeAssignment#attribute_writer_missing': unknown
attribute 'thought_signature' for ToolCall.
(ActiveModel::UnknownAttributeError)
raise UnknownAttributeError.new(self, name)
^^^^^
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:74:in 'ActiveModel::AttributeAssignment#_assign_attribute'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:17:in 'block in ActiveRecord::AttributeAssignment#_assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:9:in 'Hash#each'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/attribute_assignment.rb:9:in 'ActiveRecord::AttributeAssignment#_assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/attribute_assignment.rb:34:in 'ActiveModel::AttributeAssignment#assign_attributes'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-8.0.2.1/lib/active_model/api.rb:81:in 'ActiveModel::API#initialize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/core.rb:478:in 'ActiveRecord::Core#initialize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/inheritance.rb:76:in 'Class#new'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/inheritance.rb:76:in 'ActiveRecord::Inheritance::ClassMethods#new'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/reflection.rb:183:in 'ActiveRecord::Reflection::AbstractReflection#build_association'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/association.rb:384:in 'ActiveRecord::Associations::Association#build_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_association.rb:362:in 'ActiveRecord::Associations::CollectionAssociation#_create_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/has_many_association.rb:147:in 'ActiveRecord::Associations::HasManyAssociation#_create_record'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/association.rb:232:in 'ActiveRecord::Associations::Association#create!'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_proxy.rb:366:in 'ActiveRecord::Associations::CollectionProxy#create!'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:288:in 'block in RubyLLM::ActiveRecord::ChatMethods#persist_tool_calls'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:285:in 'Hash#each_value'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:285:in 'RubyLLM::ActiveRecord::ChatMethods#persist_tool_calls'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:279:in 'block in RubyLLM::ActiveRecord::ChatMethods#persist_message_completion'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/transaction.rb:626:in 'block in ActiveRecord::ConnectionAdapters::TransactionManager#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/concurrency/null_lock.rb:9:in 'ActiveSupport::Concurrency::NullLock#synchronize'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/transaction.rb:623:in 'ActiveRecord::ConnectionAdapters::TransactionManager#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:367:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#within_new_transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:359:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:233:in 'block in ActiveRecord::Transactions::ClassMethods#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:418:in 'ActiveRecord::ConnectionAdapters::ConnectionPool#with_connection'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/connection_handling.rb:310:in 'ActiveRecord::ConnectionHandling#with_connection'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:232:in 'ActiveRecord::Transactions::ClassMethods#transaction'
from /Users/markweston/.asdf/installs/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/transactions.rb:353:in 'ActiveRecord::Transactions#transaction'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:252:in 'RubyLLM::ActiveRecord::ChatMethods#persist_message_completion'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:236:in 'block in RubyLLM::ActiveRecord::ChatMethods#setup_persistence_callbacks'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/chat.rb:147:in 'RubyLLM::Chat#complete'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:198:in 'RubyLLM::ActiveRecord::ChatMethods#complete'
from /Users/markweston/Dropbox/working_code/ruby_llm/lib/ruby_llm/active_record/chat_methods.rb:192:in 'RubyLLM::ActiveRecord::ChatMethods#ask'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:51:in 'Agent#handle_chat'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:26:in 'block in Agent#run'
from <internal:kernel>:168:in 'Kernel#loop'
from /Users/markweston/Dropbox/working_code/code-o-matic/lib/codeomatic/agent.rb:20:in 'Agent#run'
from exe/code-o-matic:29:in '<main>'
—
Reply to this email directly, view it on GitHub
<#542 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFOFXN2GLLPFTSG6KS32BT4C6F4FAVCNFSM6AAAAACPGM2H6WVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZTMMBSHAZTKMBUHE>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Fixes #521
Gemini 3 Pro models require thought_signature to be preserved and returned during multi-turn function calling conversations. Without this, the API returns an error:
"Function call is missing a thought_signature in functionCall parts."
Changes:
This is backward compatible - works with Gemini 2.5 (where signatures are optional) and other providers (which don't use thought signatures).
See: https://ai.google.dev/gemini-api/docs/thought-signatures
What this does
Adds support for Gemini 3's
thoughtSignaturefeature in function calling. Thought signatures are encrypted representations of the model's internal thought process that must be preserved across multi-turn conversations when using tools.The implementation:
thoughtSignaturefrom Gemini API responses when extracting tool callsToolCallobjectfunctionCallparts when replaying conversation historyfunctionResponseparts when returning tool resultsType of change
Scope check
Quality check
overcommit --installand all hooks passbundle exec rake vcr:record[provider_name]bundle exec rspecmodels.json,aliases.json)API changes
New:
ToolCall#thought_signatureattribute (optional, defaults to nil)Related issues
Fixes #521