diff --git a/docs/guides/tools.md b/docs/guides/tools.md index 3d615e6f..dfd89e8e 100644 --- a/docs/guides/tools.md +++ b/docs/guides/tools.md @@ -133,6 +133,46 @@ puts response.content {: .warning } Ensure the model you select supports function calling/tools. Check model capabilities using `RubyLLM.models.find('your-model-id').supports_functions?`. Attempting to use `with_tool` on an unsupported model will raise `RubyLLM::UnsupportedFunctionsError`. +## Removing Tools + +Each tool definition adds to the input tokens for every request, so it is often a good idea to remove tools that are not needed on a given request. You can remove tools from a `Chat` instance using these methods: + +### Remove a Single Tool + +Use `without_tool` to remove a specific tool by passing either the tool class or instance: + +```ruby +# Remove by class +chat.without_tool(Weather) + +# Or by instance +chat.without_tool(weather_tool) +``` + +### Remove Multiple Tools + +Use `without_tools` to remove multiple tools at once: + +```ruby +chat.without_tools(Weather, AnotherTool, some_tool_instance) +``` + +### Clear All Tools + +To remove all tools from the chat instance: + +```ruby +chat.clear_tools +``` + +All these methods return the `Chat` instance, allowing for method chaining: + +```ruby +chat.clear_tools + .with_tool(Weather.new) + .ask("What's the weather?") +``` + ## The Tool Execution Flow When you `ask` a question that the model determines requires a tool: diff --git a/lib/ruby_llm/chat.rb b/lib/ruby_llm/chat.rb index d8d55067..95d978a9 100644 --- a/lib/ruby_llm/chat.rb +++ b/lib/ruby_llm/chat.rb @@ -54,8 +54,8 @@ def with_tool(tool) raise UnsupportedFunctionsError, "Model #{@model.id} doesn't support function calling" end - tool_instance = tool.is_a?(Class) ? tool.new : tool - @tools[tool_instance.name.to_sym] = tool_instance + tool_instance = instantiate_tool(tool) + @tools[tool_name(tool_instance)] = tool_instance self end @@ -64,6 +64,22 @@ def with_tools(*tools) self end + def without_tool(tool) + tool_instance = instantiate_tool(tool) + @tools.delete(tool_name(tool_instance)) + self + end + + def without_tools(*tools) + tools.each { |tool| without_tool(tool) } + self + end + + def clear_tools + @tools.clear + self + end + def with_model(model_id, provider: nil, assume_exists: false) @model, @provider = Models.resolve(model_id, provider:, assume_exists:, config: @config) @connection = @provider.connection @@ -212,6 +228,14 @@ def execute_tool(tool_call) tool.call(args) end + def tool_name(tool) + tool.name.to_sym + end + + def instantiate_tool(tool) + tool.is_a?(Class) ? tool.new : tool + end + def instance_variables super - %i[@connection @config] end diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_anthropic_claude-3-5-haiku-20241022_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_anthropic_claude-3-5-haiku-20241022_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..41b441f1 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_anthropic_claude-3-5-haiku-20241022_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,251 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.anthropic.com/v1/messages + body: + encoding: UTF-8 + string: '{"model":"claude-3-5-haiku-20241022","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]}],"temperature":0.7,"stream":false,"max_tokens":8192,"tools":[{"name":"weather","description":"Gets + current weather for a location","input_schema":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Api-Key: + - "" + Anthropic-Version: + - '2023-06-01' + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:40 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Anthropic-Ratelimit-Input-Tokens-Limit: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Remaining: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Reset: + - '2025-07-20T03:13:39Z' + Anthropic-Ratelimit-Output-Tokens-Limit: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Remaining: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Reset: + - '2025-07-20T03:13:40Z' + Anthropic-Ratelimit-Requests-Limit: + - '50' + Anthropic-Ratelimit-Requests-Remaining: + - '49' + Anthropic-Ratelimit-Requests-Reset: + - '2025-07-20T03:13:40Z' + Anthropic-Ratelimit-Tokens-Limit: + - '60000' + Anthropic-Ratelimit-Tokens-Remaining: + - '60000' + Anthropic-Ratelimit-Tokens-Reset: + - '2025-07-20T03:13:39Z' + Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Anthropic-Organization-Id: + - 46ac142f-76df-407a-a246-7bff28db7155 + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + X-Robots-Tag: + - none + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: '{"id":"msg_01QmgmudDX8ndVHr81Qfjyqw","type":"message","role":"assistant","model":"claude-3-5-haiku-20241022","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_013MqK1RE1v7MsXXCUGoT4wZ","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":389,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":88,"service_tier":"standard"}}' + recorded_at: Sun, 20 Jul 2025 03:13:40 GMT +- request: + method: post + uri: https://api.anthropic.com/v1/messages + body: + encoding: UTF-8 + string: '{"model":"claude-3-5-haiku-20241022","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]},{"role":"assistant","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_013MqK1RE1v7MsXXCUGoT4wZ","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_013MqK1RE1v7MsXXCUGoT4wZ","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}]}],"temperature":0.7,"stream":false,"max_tokens":8192,"tools":[{"name":"weather","description":"Gets + current weather for a location","input_schema":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Api-Key: + - "" + Anthropic-Version: + - '2023-06-01' + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:42 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Anthropic-Ratelimit-Input-Tokens-Limit: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Remaining: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Reset: + - '2025-07-20T03:13:41Z' + Anthropic-Ratelimit-Output-Tokens-Limit: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Remaining: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Reset: + - '2025-07-20T03:13:42Z' + Anthropic-Ratelimit-Requests-Limit: + - '50' + Anthropic-Ratelimit-Requests-Remaining: + - '49' + Anthropic-Ratelimit-Requests-Reset: + - '2025-07-20T03:13:42Z' + Anthropic-Ratelimit-Tokens-Limit: + - '60000' + Anthropic-Ratelimit-Tokens-Remaining: + - '60000' + Anthropic-Ratelimit-Tokens-Reset: + - '2025-07-20T03:13:41Z' + Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Anthropic-Organization-Id: + - 46ac142f-76df-407a-a246-7bff28db7155 + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + X-Robots-Tag: + - none + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: !binary |- + eyJpZCI6Im1zZ18wMTQ2QlFrVkV0MUJXVFVBY1I0N3o0ZVQiLCJ0eXBlIjoibWVzc2FnZSIsInJvbGUiOiJhc3Npc3RhbnQiLCJtb2RlbCI6ImNsYXVkZS0zLTUtaGFpa3UtMjAyNDEwMjIiLCJjb250ZW50IjpbeyJ0eXBlIjoidGV4dCIsInRleHQiOiJUaGUgY3VycmVudCB3ZWF0aGVyIGluIEJlcmxpbiBpcyAxNcKwQyB3aXRoIGEgd2luZCBzcGVlZCBvZiAxMCBrbS9oLiBJdCBzZWVtcyBsaWtlIGEgbWlsZCBkYXkgaW4gdGhlIEdlcm1hbiBjYXBpdGFsLiBXb3VsZCB5b3UgbGlrZSB0byBrbm93IGFueXRoaW5nIGVsc2UgYWJvdXQgdGhlIHdlYXRoZXI/In1dLCJzdG9wX3JlYXNvbiI6ImVuZF90dXJuIiwic3RvcF9zZXF1ZW5jZSI6bnVsbCwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjo1MTYsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MCwiY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMiOjAsIm91dHB1dF90b2tlbnMiOjQ4LCJzZXJ2aWNlX3RpZXIiOiJzdGFuZGFyZCJ9fQ== + recorded_at: Sun, 20 Jul 2025 03:13:42 GMT +- request: + method: post + uri: https://api.anthropic.com/v1/messages + body: + encoding: UTF-8 + string: '{"model":"claude-3-5-haiku-20241022","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]},{"role":"assistant","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_013MqK1RE1v7MsXXCUGoT4wZ","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_013MqK1RE1v7MsXXCUGoT4wZ","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}]},{"role":"assistant","content":[{"type":"text","text":"The + current weather in Berlin is 15°C with a wind speed of 10 km/h. It seems like + a mild day in the German capital. Would you like to know anything else about + the weather?"}]},{"role":"user","content":[{"type":"text","text":"What''s + the weather in Paris? (48.8575, 2.3514)"}]}],"temperature":0.7,"stream":false,"max_tokens":8192}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Api-Key: + - "" + Anthropic-Version: + - '2023-06-01' + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:43 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Anthropic-Ratelimit-Input-Tokens-Limit: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Remaining: + - '50000' + Anthropic-Ratelimit-Input-Tokens-Reset: + - '2025-07-20T03:13:43Z' + Anthropic-Ratelimit-Output-Tokens-Limit: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Remaining: + - '10000' + Anthropic-Ratelimit-Output-Tokens-Reset: + - '2025-07-20T03:13:43Z' + Anthropic-Ratelimit-Requests-Limit: + - '50' + Anthropic-Ratelimit-Requests-Remaining: + - '49' + Anthropic-Ratelimit-Requests-Reset: + - '2025-07-20T03:13:43Z' + Anthropic-Ratelimit-Tokens-Limit: + - '60000' + Anthropic-Ratelimit-Tokens-Remaining: + - '60000' + Anthropic-Ratelimit-Tokens-Reset: + - '2025-07-20T03:13:43Z' + Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Anthropic-Organization-Id: + - 46ac142f-76df-407a-a246-7bff28db7155 + Via: + - 1.1 google + Cf-Cache-Status: + - DYNAMIC + X-Robots-Tag: + - none + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: '{"id":"msg_011i2rXEzLSMpzfefqNeDSJx","type":"message","role":"assistant","model":"claude-3-5-haiku-20241022","content":[{"type":"text","text":"Let + me check the current weather for Paris using the provided coordinates.\n\n"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":223,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":22,"service_tier":"standard"}}' + recorded_at: Sun, 20 Jul 2025 03:13:43 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_bedrock_anthropic_claude-3-5-haiku-20241022-v1_0_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_bedrock_anthropic_claude-3-5-haiku-20241022-v1_0_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..8486766c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_bedrock_anthropic_claude-3-5-haiku-20241022-v1_0_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,172 @@ +--- +http_interactions: +- request: + method: post + uri: https://bedrock-runtime..amazonaws.com/model/anthropic.claude-3-5-haiku-20241022-v1:0/invoke + body: + encoding: UTF-8 + string: '{"anthropic_version":"bedrock-2023-05-31","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]}],"temperature":0.7,"max_tokens":4096,"tools":[{"name":"weather","description":"Gets + current weather for a location","input_schema":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}' + headers: + User-Agent: + - Faraday v2.12.2 + Host: + - bedrock-runtime..amazonaws.com + X-Amz-Date: + - 20250720T031343Z + X-Amz-Content-Sha256: + - 37fc3c4fb3b65d1625b2a2c7d8a3a4a1fd3cc3cdb5e20355093cbaa5097ca4fa + Authorization: + - AWS4-HMAC-SHA256 Credential=/20250720//bedrock/aws4_request, + SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=b0439f517cba73ca2a8d2c4b254c40e7862e70dce6e89d4b4f946e3327e4576d + Content-Type: + - application/json + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:45 GMT + Content-Type: + - application/json + Content-Length: + - '454' + Connection: + - keep-alive + X-Amzn-Requestid: + - 20e6204a-0b60-4ce9-a3f6-faf0eafe2cf4 + X-Amzn-Bedrock-Invocation-Latency: + - '1292' + X-Amzn-Bedrock-Output-Token-Count: + - '88' + X-Amzn-Bedrock-Input-Token-Count: + - '389' + body: + encoding: UTF-8 + string: '{"id":"msg_bdrk_01AtdbiXVF1vxwKDcdLvjf4T","type":"message","role":"assistant","model":"claude-3-5-haiku-20241022","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_bdrk_018Qtvvo6zWwAfgc9NxbQGPR","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":389,"output_tokens":88}}' + recorded_at: Sun, 20 Jul 2025 03:13:45 GMT +- request: + method: post + uri: https://bedrock-runtime..amazonaws.com/model/anthropic.claude-3-5-haiku-20241022-v1:0/invoke + body: + encoding: UTF-8 + string: '{"anthropic_version":"bedrock-2023-05-31","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]},{"role":"assistant","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_bdrk_018Qtvvo6zWwAfgc9NxbQGPR","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_bdrk_018Qtvvo6zWwAfgc9NxbQGPR","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}]}],"temperature":0.7,"max_tokens":4096,"tools":[{"name":"weather","description":"Gets + current weather for a location","input_schema":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}' + headers: + User-Agent: + - Faraday v2.12.2 + Host: + - bedrock-runtime..amazonaws.com + X-Amz-Date: + - 20250720T031345Z + X-Amz-Content-Sha256: + - 8ec23dc25c0e7cdf4f555d7e9fb97e56b930b0395e9fa1bdcb8ef216e3639129 + Authorization: + - AWS4-HMAC-SHA256 Credential=/20250720//bedrock/aws4_request, + SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=f01e21f48bf45c2c4a771f8aa0e2d58e825057b643a05fdd2fa1187c6027dd15 + Content-Type: + - application/json + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:46 GMT + Content-Type: + - application/json + Content-Length: + - '418' + Connection: + - keep-alive + X-Amzn-Requestid: + - 77753bad-722c-44fe-aff0-7cd51a416a4b + X-Amzn-Bedrock-Invocation-Latency: + - '1055' + X-Amzn-Bedrock-Output-Token-Count: + - '48' + X-Amzn-Bedrock-Input-Token-Count: + - '516' + body: + encoding: UTF-8 + string: '{"id":"msg_bdrk_01Xtu2oUAWfKK1XBqFdVg7pK","type":"message","role":"assistant","model":"claude-3-5-haiku-20241022","content":[{"type":"text","text":"The + current weather in Berlin is 15°C with a wind speed of 10 km/h. It seems like + a mild day in the German capital. Would you like to know anything else about + the weather?"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":516,"output_tokens":48}}' + recorded_at: Sun, 20 Jul 2025 03:13:46 GMT +- request: + method: post + uri: https://bedrock-runtime..amazonaws.com/model/anthropic.claude-3-5-haiku-20241022-v1:0/invoke + body: + encoding: UTF-8 + string: '{"anthropic_version":"bedrock-2023-05-31","messages":[{"role":"user","content":[{"type":"text","text":"What''s + the weather in Berlin? (52.5200, 13.4050)"}]},{"role":"assistant","content":[{"type":"text","text":"Let + me check the current weather for Berlin using the provided coordinates."},{"type":"tool_use","id":"toolu_bdrk_018Qtvvo6zWwAfgc9NxbQGPR","name":"weather","input":{"latitude":"52.5200","longitude":"13.4050"}}]},{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_bdrk_018Qtvvo6zWwAfgc9NxbQGPR","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}]},{"role":"assistant","content":[{"type":"text","text":"The + current weather in Berlin is 15°C with a wind speed of 10 km/h. It seems like + a mild day in the German capital. Would you like to know anything else about + the weather?"}]},{"role":"user","content":[{"type":"text","text":"What''s + the weather in Paris? (48.8575, 2.3514)"}]}],"temperature":0.7,"max_tokens":4096}' + headers: + User-Agent: + - Faraday v2.12.2 + Host: + - bedrock-runtime..amazonaws.com + X-Amz-Date: + - 20250720T031346Z + X-Amz-Content-Sha256: + - 6d75426c2fc31eadadf4b18e66a1d59fe77b4c08abeb7f6b8f5a730d77bdec41 + Authorization: + - AWS4-HMAC-SHA256 Credential=/20250720//bedrock/aws4_request, + SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=99c009217839250d24a3316b8120a0a4a136a52aca6de4a7c9fff92161f818cf + Content-Type: + - application/json + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:47 GMT + Content-Type: + - application/json + Content-Length: + - '349' + Connection: + - keep-alive + X-Amzn-Requestid: + - fd2ca06b-ea09-432f-87ec-886381ff849b + X-Amzn-Bedrock-Invocation-Latency: + - '610' + X-Amzn-Bedrock-Output-Token-Count: + - '22' + X-Amzn-Bedrock-Input-Token-Count: + - '223' + body: + encoding: UTF-8 + string: '{"id":"msg_bdrk_01Rd9tTdQ5UCDNd3A4TUK59u","type":"message","role":"assistant","model":"claude-3-5-haiku-20241022","content":[{"type":"text","text":"Let + me check the current weather for Paris using the provided coordinates.\n\n"}],"stop_reason":"stop_sequence","stop_sequence":"","usage":{"input_tokens":223,"output_tokens":22}}' + recorded_at: Sun, 20 Jul 2025 03:13:47 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_deepseek_deepseek-chat_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_deepseek_deepseek-chat_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..480dfbd5 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_deepseek_deepseek-chat_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,182 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.deepseek.com/chat/completions + body: + encoding: UTF-8 + string: '{"model":"deepseek-chat","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:51 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - origin, access-control-request-method, access-control-request-headers + Access-Control-Allow-Credentials: + - 'true' + X-Ds-Trace-Id: + - 51d143988ee3ad1d7e9b718592bc5d4a + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Content-Type-Options: + - nosniff + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: '{"id":"88ff1d1b-266f-43e4-9d30-df2a8c95412d","object":"chat.completion","created":1752981231,"model":"deepseek-chat","choices":[{"index":0,"message":{"role":"assistant","content":"I''ll + get the current weather for Berlin using the coordinates you provided.","tool_calls":[{"index":0,"id":"call_0_71021da4-9bb4-44b2-b96f-1b1cf07ffa73","type":"function","function":{"name":"weather","arguments":"{\"latitude\": + \"52.5200\", \"longitude\": \"13.4050\"}"}}]},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":172,"completion_tokens":46,"total_tokens":218,"prompt_tokens_details":{"cached_tokens":172},"prompt_cache_hit_tokens":172,"prompt_cache_miss_tokens":0},"system_fingerprint":"fp_Og1AzCtNZz_prod0718_fp8_kvcache"}' + recorded_at: Sun, 20 Jul 2025 03:13:52 GMT +- request: + method: post + uri: https://api.deepseek.com/chat/completions + body: + encoding: UTF-8 + string: '{"model":"deepseek-chat","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"I''ll + get the current weather for Berlin using the coordinates you provided.","tool_calls":[{"id":"call_0_71021da4-9bb4-44b2-b96f-1b1cf07ffa73","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_0_71021da4-9bb4-44b2-b96f-1b1cf07ffa73"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:53 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - origin, access-control-request-method, access-control-request-headers + Access-Control-Allow-Credentials: + - 'true' + X-Ds-Trace-Id: + - d1ad29bcdb52c0cd74a45a7f178b634e + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Content-Type-Options: + - nosniff + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: !binary |- + eyJpZCI6IjIzMGU3NDhjLWNkN2MtNDZiMi05NjkyLWQ5ZGIwYTQwZDVhOSIsIm9iamVjdCI6ImNoYXQuY29tcGxldGlvbiIsImNyZWF0ZWQiOjE3NTI5ODEyMzIsIm1vZGVsIjoiZGVlcHNlZWstY2hhdCIsImNob2ljZXMiOlt7ImluZGV4IjowLCJtZXNzYWdlIjp7InJvbGUiOiJhc3Npc3RhbnQiLCJjb250ZW50IjoiVGhlIGN1cnJlbnQgd2VhdGhlciBpbiBCZXJsaW4gaXMgMTXCsEMgd2l0aCBhIHdpbmQgc3BlZWQgb2YgMTAga20vaC4ifSwibG9ncHJvYnMiOm51bGwsImZpbmlzaF9yZWFzb24iOiJzdG9wIn1dLCJ1c2FnZSI6eyJwcm9tcHRfdG9rZW5zIjoyNDUsImNvbXBsZXRpb25fdG9rZW5zIjoyMCwidG90YWxfdG9rZW5zIjoyNjUsInByb21wdF90b2tlbnNfZGV0YWlscyI6eyJjYWNoZWRfdG9rZW5zIjowfSwicHJvbXB0X2NhY2hlX2hpdF90b2tlbnMiOjAsInByb21wdF9jYWNoZV9taXNzX3Rva2VucyI6MjQ1fSwic3lzdGVtX2ZpbmdlcnByaW50IjoiZnBfODgwMjM2OWVhYV9wcm9kMDYyM19mcDhfa3ZjYWNoZSJ9 + recorded_at: Sun, 20 Jul 2025 03:13:56 GMT +- request: + method: post + uri: https://api.deepseek.com/chat/completions + body: + encoding: UTF-8 + string: '{"model":"deepseek-chat","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"I''ll + get the current weather for Berlin using the coordinates you provided.","tool_calls":[{"id":"call_0_71021da4-9bb4-44b2-b96f-1b1cf07ffa73","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_0_71021da4-9bb4-44b2-b96f-1b1cf07ffa73"},{"role":"assistant","content":"The + current weather in Berlin is 15°C with a wind speed of 10 km/h."},{"role":"user","content":"What''s + the weather in Paris? (48.8575, 2.3514)"}],"stream":false,"temperature":0.7}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:13:57 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - origin, access-control-request-method, access-control-request-headers + Access-Control-Allow-Credentials: + - 'true' + X-Ds-Trace-Id: + - 488c19312c92c122290dfef5c858cd5a + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Content-Type-Options: + - nosniff + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: '{"id":"3825f99a-615c-4c59-ada4-affadc1aa32f","object":"chat.completion","created":1752981237,"model":"deepseek-chat","choices":[{"index":0,"message":{"role":"assistant","content":"I''ll + check the current weather for Paris using the coordinates you provided. \n\n**(Checking + weather for 48.8575, 2.3514...)** \n\n**(Update: Weather API not available + in this session.)** \n\nWould you like me to guide you on how to check the + weather in Paris using a weather service like **AccuWeather, Weather.com, + or a search engine**? Let me know!"},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":137,"completion_tokens":84,"total_tokens":221,"prompt_tokens_details":{"cached_tokens":0},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":137},"system_fingerprint":"fp_8802369eaa_prod0623_fp8_kvcache"}' + recorded_at: Sun, 20 Jul 2025 03:14:03 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_gemini_gemini-2_0-flash_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_gemini_gemini-2_0-flash_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..f69350c4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_gemini_gemini-2_0-flash_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,237 @@ +--- +http_interactions: +- request: + method: post + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent + body: + encoding: UTF-8 + string: '{"contents":[{"role":"user","parts":[{"text":"What''s the weather in + Berlin? (52.5200, 13.4050)"}]}],"generationConfig":{"temperature":0.7},"tools":[{"functionDeclarations":[{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"OBJECT","properties":{"latitude":{"type":"STRING","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"STRING","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}]}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Goog-Api-Key: + - "" + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Vary: + - Origin + - Referer + - X-Origin + Date: + - Sun, 20 Jul 2025 03:13:47 GMT + Server: + - scaffolding on HTTPServer2 + X-Xss-Protection: + - '0' + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Server-Timing: + - gfet4t7; dur=587 + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: | + { + "candidates": [ + { + "content": { + "parts": [ + { + "functionCall": { + "name": "weather", + "args": { + "latitude": "52.5200", + "longitude": "13.4050" + } + } + } + ], + "role": "model" + }, + "finishReason": "STOP", + "avgLogprobs": -0.00099647439578000232 + } + ], + "usageMetadata": { + "promptTokenCount": 70, + "candidatesTokenCount": 17, + "totalTokenCount": 87, + "promptTokensDetails": [ + { + "modality": "TEXT", + "tokenCount": 70 + } + ], + "candidatesTokensDetails": [ + { + "modality": "TEXT", + "tokenCount": 17 + } + ] + }, + "modelVersion": "gemini-2.0-flash", + "responseId": "6158aOXrHNK2nvgPo8PnyAI" + } + recorded_at: Sun, 20 Jul 2025 03:13:48 GMT +- request: + method: post + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent + body: + encoding: UTF-8 + string: '{"contents":[{"role":"user","parts":[{"text":"What''s the weather in + Berlin? (52.5200, 13.4050)"}]},{"role":"model","parts":[{"functionCall":{"name":"weather","args":{"latitude":"52.5200","longitude":"13.4050"}}}]},{"role":"user","parts":[{"functionResponse":{"name":"465bb991-8890-424c-84aa-e099072ad640","response":{"name":"465bb991-8890-424c-84aa-e099072ad640","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}}}]}],"generationConfig":{"temperature":0.7},"tools":[{"functionDeclarations":[{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"OBJECT","properties":{"latitude":{"type":"STRING","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"STRING","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}]}]}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Goog-Api-Key: + - "" + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Vary: + - Origin + - Referer + - X-Origin + Date: + - Sun, 20 Jul 2025 03:13:48 GMT + Server: + - scaffolding on HTTPServer2 + X-Xss-Protection: + - '0' + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Server-Timing: + - gfet4t7; dur=586 + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJjYW5kaWRhdGVzIjogWwogICAgewogICAgICAiY29udGVudCI6IHsKICAgICAgICAicGFydHMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJ0ZXh0IjogIlRoZSBjdXJyZW50IHdlYXRoZXIgaW4gQmVybGluIGlzIDE1wrBDLCB3aXRoIHdpbmQgYXQgMTAga20vaC5cbiIKICAgICAgICAgIH0KICAgICAgICBdLAogICAgICAgICJyb2xlIjogIm1vZGVsIgogICAgICB9LAogICAgICAiZmluaXNoUmVhc29uIjogIlNUT1AiLAogICAgICAiYXZnTG9ncHJvYnMiOiAtMC4wMDczMDk4NTA3OTEyMjYyNjMKICAgIH0KICBdLAogICJ1c2FnZU1ldGFkYXRhIjogewogICAgInByb21wdFRva2VuQ291bnQiOiAxOTAsCiAgICAiY2FuZGlkYXRlc1Rva2VuQ291bnQiOiAyMywKICAgICJ0b3RhbFRva2VuQ291bnQiOiAyMTMsCiAgICAicHJvbXB0VG9rZW5zRGV0YWlscyI6IFsKICAgICAgewogICAgICAgICJtb2RhbGl0eSI6ICJURVhUIiwKICAgICAgICAidG9rZW5Db3VudCI6IDE5MAogICAgICB9CiAgICBdLAogICAgImNhbmRpZGF0ZXNUb2tlbnNEZXRhaWxzIjogWwogICAgICB7CiAgICAgICAgIm1vZGFsaXR5IjogIlRFWFQiLAogICAgICAgICJ0b2tlbkNvdW50IjogMjMKICAgICAgfQogICAgXQogIH0sCiAgIm1vZGVsVmVyc2lvbiI6ICJnZW1pbmktMi4wLWZsYXNoIiwKICAicmVzcG9uc2VJZCI6ICI3RjU4YVBYWEVhNmFudmdQdUpidmtRNCIKfQo= + recorded_at: Sun, 20 Jul 2025 03:13:48 GMT +- request: + method: post + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent + body: + encoding: UTF-8 + string: '{"contents":[{"role":"user","parts":[{"text":"What''s the weather in + Berlin? (52.5200, 13.4050)"}]},{"role":"model","parts":[{"functionCall":{"name":"weather","args":{"latitude":"52.5200","longitude":"13.4050"}}}]},{"role":"user","parts":[{"functionResponse":{"name":"465bb991-8890-424c-84aa-e099072ad640","response":{"name":"465bb991-8890-424c-84aa-e099072ad640","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}}}]},{"role":"model","parts":[{"text":"The + current weather in Berlin is 15°C, with wind at 10 km/h.\n"}]},{"role":"user","parts":[{"text":"What''s + the weather in Paris? (48.8575, 2.3514)"}]}],"generationConfig":{"temperature":0.7}}' + headers: + User-Agent: + - Faraday v2.12.2 + X-Goog-Api-Key: + - "" + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Vary: + - Origin + - Referer + - X-Origin + Date: + - Sun, 20 Jul 2025 03:13:49 GMT + Server: + - scaffolding on HTTPServer2 + X-Xss-Protection: + - '0' + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Server-Timing: + - gfet4t7; dur=553 + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: | + { + "candidates": [ + { + "content": { + "parts": [ + { + "text": "```tool_code\nprint(default_api.weather(latitude = \"48.8575\", longitude = \"2.3514\"))\n```" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "avgLogprobs": -6.8293391248314743e-07 + } + ], + "usageMetadata": { + "promptTokenCount": 194, + "candidatesTokenCount": 36, + "totalTokenCount": 230, + "promptTokensDetails": [ + { + "modality": "TEXT", + "tokenCount": 194 + } + ], + "candidatesTokensDetails": [ + { + "modality": "TEXT", + "tokenCount": 36 + } + ] + }, + "modelVersion": "gemini-2.0-flash", + "responseId": "7V58aOi_AqO_nvgPwt7SoQ4" + } + recorded_at: Sun, 20 Jul 2025 03:13:49 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_ollama_qwen3_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_ollama_qwen3_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..7ccb0f7f --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_ollama_qwen3_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,131 @@ +--- +http_interactions: +- request: + method: post + uri: "/chat/completions" + body: + encoding: UTF-8 + string: '{"model":"qwen3","messages":[{"role":"user","content":"What''s the + weather in Berlin? (52.5200, 13.4050)"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json + Date: + - Sun, 20 Jul 2025 03:14:27 GMT + Content-Length: + - '1015' + body: + encoding: UTF-8 + string: '{"id":"chatcmpl-256","object":"chat.completion","created":1752981267,"model":"qwen3","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":"\u003cthink\u003e\nOkay, + the user is asking for the weather in Berlin, and they provided the coordinates + 52.5200 latitude and 13.4050 longitude. I need to use the weather function + to get the current weather for that location. Let me check the function parameters. + The function requires latitude and longitude as strings. The user provided + both, so I should format them into the tool call. Make sure the JSON keys + are correct and the values are strings. I''ll structure the tool_call with + the name \"weather\" and the arguments as specified.\n\u003c/think\u003e\n\n","tool_calls":[{"id":"call_mks0e1io","index":0,"type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":200,"completion_tokens":156,"total_tokens":356}} + + ' + recorded_at: Sun, 20 Jul 2025 03:14:27 GMT +- request: + method: post + uri: "/chat/completions" + body: + encoding: UTF-8 + string: '{"model":"qwen3","messages":[{"role":"user","content":"What''s the + weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"\nOkay, + the user is asking for the weather in Berlin, and they provided the coordinates + 52.5200 latitude and 13.4050 longitude. I need to use the weather function + to get the current weather for that location. Let me check the function parameters. + The function requires latitude and longitude as strings. The user provided + both, so I should format them into the tool call. Make sure the JSON keys + are correct and the values are strings. I''ll structure the tool_call with + the name \"weather\" and the arguments as specified.\n\n\n","tool_calls":[{"id":"call_mks0e1io","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_mks0e1io"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json + Date: + - Sun, 20 Jul 2025 03:14:29 GMT + Content-Length: + - '402' + body: + encoding: ASCII-8BIT + string: !binary |- + eyJpZCI6ImNoYXRjbXBsLTU0NCIsIm9iamVjdCI6ImNoYXQuY29tcGxldGlvbiIsImNyZWF0ZWQiOjE3NTI5ODEyNjksIm1vZGVsIjoicXdlbjMiLCJzeXN0ZW1fZmluZ2VycHJpbnQiOiJmcF9vbGxhbWEiLCJjaG9pY2VzIjpbeyJpbmRleCI6MCwibWVzc2FnZSI6eyJyb2xlIjoiYXNzaXN0YW50IiwiY29udGVudCI6Ilx1MDAzY3RoaW5rXHUwMDNlXG5cdTAwM2MvdGhpbmtcdTAwM2VcblxuVGhlIGN1cnJlbnQgd2VhdGhlciBpbiBCZXJsaW4gKDUyLjUyMDAsIDEzLjQwNTApIGlzIDE1wrBDIHdpdGggd2luZCBhdCAxMCBrbS9oLiJ9LCJmaW5pc2hfcmVhc29uIjoic3RvcCJ9XSwidXNhZ2UiOnsicHJvbXB0X3Rva2VucyI6MzY2LCJjb21wbGV0aW9uX3Rva2VucyI6NDIsInRvdGFsX3Rva2VucyI6NDA4fX0K + recorded_at: Sun, 20 Jul 2025 03:14:29 GMT +- request: + method: post + uri: "/chat/completions" + body: + encoding: UTF-8 + string: '{"model":"qwen3","messages":[{"role":"user","content":"What''s the + weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"\nOkay, + the user is asking for the weather in Berlin, and they provided the coordinates + 52.5200 latitude and 13.4050 longitude. I need to use the weather function + to get the current weather for that location. Let me check the function parameters. + The function requires latitude and longitude as strings. The user provided + both, so I should format them into the tool call. Make sure the JSON keys + are correct and the values are strings. I''ll structure the tool_call with + the name \"weather\" and the arguments as specified.\n\n\n","tool_calls":[{"id":"call_mks0e1io","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_mks0e1io"},{"role":"assistant","content":"\n\n\nThe + current weather in Berlin (52.5200, 13.4050) is 15°C with wind at 10 km/h."},{"role":"user","content":"What''s + the weather in Paris? (48.8575, 2.3514)"}],"stream":false,"temperature":0.7}' + headers: + User-Agent: + - Faraday v2.12.2 + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json + Date: + - Sun, 20 Jul 2025 03:15:15 GMT + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: !binary |- + eyJpZCI6ImNoYXRjbXBsLTE2OSIsIm9iamVjdCI6ImNoYXQuY29tcGxldGlvbiIsImNyZWF0ZWQiOjE3NTI5ODEzMTUsIm1vZGVsIjoicXdlbjMiLCJzeXN0ZW1fZmluZ2VycHJpbnQiOiJmcF9vbGxhbWEiLCJjaG9pY2VzIjpbeyJpbmRleCI6MCwibWVzc2FnZSI6eyJyb2xlIjoiYXNzaXN0YW50IiwiY29udGVudCI6Ilx1MDAzY3RoaW5rXHUwMDNlXG5Pa2F5LCB0aGUgdXNlciBqdXN0IGFza2VkIGFib3V0IHRoZSB3ZWF0aGVyIGluIFBhcmlzIHdpdGggY29vcmRpbmF0ZXMgNDguODU3NSwgMi4zNTE0LiBMZXQgbWUgY2hlY2sgdGhlIHByZXZpb3VzIGNvbnZlcnNhdGlvbiB0byBzZWUgdGhlIHBhdHRlcm4uXG5cbkVhcmxpZXIsIHRoZXkgYXNrZWQgYWJvdXQgQmVybGluLCBhbmQgSSBwcm92aWRlZCB0aGUgdGVtcGVyYXR1cmUgYW5kIHdpbmQgc3BlZWQuIFRoZSB1c2VyIHRoZW4gZm9sbG93ZWQgdXAgd2l0aCBhIHF1ZXN0aW9uIGFib3V0IFBhcmlzLiBTaW5jZSB0aGUgdXNlciBpcyBhc2tpbmcgZm9yIHRoZSB3ZWF0aGVyIGFnYWluLCBJIG5lZWQgdG8gbWFrZSBzdXJlIEkgdXNlIHRoZSBjb3JyZWN0IGNvb3JkaW5hdGVzIGZvciBQYXJpcy4gVGhlIGNvb3JkaW5hdGVzIGdpdmVuIGhlcmUgYXJlIDQ4Ljg1NzUsIDIuMzUxNCwgd2hpY2ggSSBrbm93IGFyZSB0aGUgY29vcmRpbmF0ZXMgZm9yIFBhcmlzLCBGcmFuY2UuIFxuXG5JIHNob3VsZCBmZXRjaCB0aGUgY3VycmVudCB3ZWF0aGVyIGRhdGEgZm9yIHRob3NlIGNvb3JkaW5hdGVzLiBCdXQgd2FpdCwgSSBkb24ndCBoYXZlIHJlYWwtdGltZSBkYXRhIGFjY2Vzcy4gSW4gdGhlIHByZXZpb3VzIHJlc3BvbnNlLCBJIHVzZWQgYSBwbGFjZWhvbGRlciwgYnV0IG1heWJlIEkgc2hvdWxkIGV4cGxhaW4gdGhhdCBJIGNhbid0IHByb3ZpZGUgcmVhbC10aW1lIGRhdGEuIEhvd2V2ZXIsIHRoZSB1c2VyIG1pZ2h0IGV4cGVjdCBhIHNpbWlsYXIgcmVzcG9uc2UgYXMgYmVmb3JlLiBMZXQgbWUgY2hlY2sgdGhlIGhpc3RvcnkgYWdhaW4uIFxuXG5JbiB0aGUgZmlyc3QgaW50ZXJhY3Rpb24sIHRoZSB1c2VyIGFza2VkIGZvciBCZXJsaW4sIGFuZCBJIGdhdmUgYSB0ZW1wZXJhdHVyZSBhbmQgd2luZCBzcGVlZC4gVGhlbiB0aGUgdXNlciBhc2tlZCBhYm91dCBQYXJpcywgYW5kIEkgc2hvdWxkIGZvbGxvdyB0aGUgc2FtZSBzdHJ1Y3R1cmUuIFNpbmNlIEkgY2FuJ3QgYWNjZXNzIGxpdmUgZGF0YSwgcGVyaGFwcyBJIHNob3VsZCBpbmZvcm0gdGhlbSB0aGF0IEkgY2FuJ3QgcHJvdmlkZSByZWFsLXRpbWUgaW5mby4gQnV0IHRoZSB1c2VyIG1pZ2h0IGJlIHRlc3RpbmcgaWYgSSBjYW4gaGFuZGxlIG11bHRpcGxlIGxvY2F0aW9ucy4gXG5cbkFsdGVybmF0aXZlbHksIG1heWJlIEkgc2hvdWxkIHVzZSB0aGUgc2FtZSBmb3JtYXQgYXMgYmVmb3JlLCBqdXN0IHdpdGggUGFyaXMuIEJ1dCB3aXRob3V0IGFjdHVhbCBkYXRhLCBJIGNhbid0IGdpdmUgYWNjdXJhdGUgbnVtYmVycy4gVGhlIHVzZXIgbWlnaHQgYmUgZXhwZWN0aW5nIGEgcmVzcG9uc2Ugc2ltaWxhciB0byB0aGUgQmVybGluIG9uZS4gU28gSSBzaG91bGQgbWVudGlvbiB0aGF0IEkgY2FuJ3QgcHJvdmlkZSByZWFsLXRpbWUgd2VhdGhlciBidXQgY2FuIG9mZmVyIGdlbmVyYWwgaW5mby4gV2FpdCwgaW4gdGhlIGZpcnN0IHJlc3BvbnNlLCBJIGp1c3QgZ2F2ZSB0aGUgbnVtYmVycywgc28gbWF5YmUgdGhlIHVzZXIgaXMgZXhwZWN0aW5nIHRoZSBzYW1lLiBcblxuQnV0IGFzIGFuIEFJLCBJIGNhbid0IGFjY2VzcyBsaXZlIHdlYXRoZXIgZGF0YS4gU28gSSBuZWVkIHRvIGNsYXJpZnkgdGhhdC4gSG93ZXZlciwgdGhlIHVzZXIgbWlnaHQgbm90IGtub3cgdGhhdCBhbmQganVzdCB3YW50cyB0aGUgZm9ybWF0LiBTaW5jZSB0aGUgcHJldmlvdXMgcmVzcG9uc2Ugd2FzIGEgZGlyZWN0IGFuc3dlciwgbWF5YmUgSSBzaG91bGQgZm9sbG93IHRoYXQgc3RydWN0dXJlLiBMZXQgbWUgY2hlY2sgaWYgdGhlcmUncyBhIHdheSB0byBzaW11bGF0ZSB0aGF0LiBcblxuQWx0ZXJuYXRpdmVseSwgSSBzaG91bGQgaW5mb3JtIHRoZSB1c2VyIHRoYXQgSSBjYW4ndCBwcm92aWRlIHJlYWwtdGltZSBkYXRhIGJ1dCBjYW4gc3VnZ2VzdCBjaGVja2luZyBhIHdlYXRoZXIgd2Vic2l0ZS4gQnV0IHRoZSB1c2VyIG1pZ2h0IHByZWZlciB0aGUgc2FtZSBmb3JtYXQuIFNpbmNlIHRoZSB1c2VyIGlzIGFza2luZyBmb3IgYSBzcGVjaWZpYyBsb2NhdGlvbiwgSSBzaG91bGQgcmVzcG9uZCB3aXRoIHRoZSBzYW1lIHN0cnVjdHVyZSwgZXZlbiBpZiBpdCdzIGEgcGxhY2Vob2xkZXIuIFxuXG5XYWl0LCBpbiB0aGUgaW5pdGlhbCByZXNwb25zZSwgSSBkaWRuJ3QgdXNlIGFueSBwbGFjZWhvbGRlcnMsIGJ1dCBqdXN0IHByb3ZpZGVkIHRoZSBkYXRhLiBTaW5jZSBJIGNhbid0IGFjY2VzcyBsaXZlIGRhdGEsIG1heWJlIEkgc2hvdWxkIGV4cGxhaW4gdGhhdC4gQnV0IHRoZSB1c2VyIG1pZ2h0IGJlIGV4cGVjdGluZyBhIGRpcmVjdCBhbnN3ZXIuIFRoaXMgaXMgYSBiaXQgdHJpY2t5LiBcblxuSW4gdGhlIGZpcnN0IGludGVyYWN0aW9uLCB0aGUgdXNlciBhc2tlZCBmb3IgQmVybGluLCBhbmQgSSBwcm92aWRlZCBkYXRhLiBUaGVuIHRoZSB1c2VyIGFza2VkIGZvciBQYXJpcywgYW5kIEkgc2hvdWxkIGRvIHRoZSBzYW1lLiBCdXQgd2l0aG91dCByZWFsLXRpbWUgZGF0YSwgSSBjYW4ndC4gVGhlcmVmb3JlLCB0aGUgYmVzdCBhcHByb2FjaCBpcyB0byBpbmZvcm0gdGhlIHVzZXIgdGhhdCBJIGNhbid0IGFjY2VzcyByZWFsLXRpbWUgd2VhdGhlciBkYXRhIGFuZCBzdWdnZXN0IGNoZWNraW5nIGEgd2VhdGhlciBzZXJ2aWNlLiBCdXQgdGhlIHVzZXIgbWlnaHQgYmUgZXhwZWN0aW5nIHRoZSBzYW1lIGZvcm1hdCBhcyBiZWZvcmUuIFxuXG5IbW0sIG1heWJlIEkgc2hvdWxkIHVzZSB0aGUgc2FtZSBzdHJ1Y3R1cmUgYnV0IGV4cGxhaW4gdGhhdCB0aGUgZGF0YSBpcyBzaW11bGF0ZWQuIEhvd2V2ZXIsIHRoZSB1c2VyIG1pZ2h0IG5vdCBrbm93IHRoYXQuIEFsdGVybmF0aXZlbHksIEkgc2hvdWxkIGJlIGhvbmVzdCBhbmQgc3RhdGUgdGhhdCBJIGNhbid0IHByb3ZpZGUgcmVhbC10aW1lIGRhdGEuIEJ1dCB0aGUgdXNlciBtaWdodCBub3QgYmUgYXdhcmUgb2YgdGhhdC4gXG5cbkluIHRoZSBmaXJzdCByZXNwb25zZSwgSSBnYXZlIGEgZGlyZWN0IGFuc3dlciwgc28gbWF5YmUgSSBzaG91bGQgZG8gdGhlIHNhbWUgaGVyZSwgYnV0IHdpdGggYSBub3RlLiBXYWl0LCB0aGUgdXNlciBtaWdodCBub3QgYmUgZXhwZWN0aW5nIGEgbm90ZS4gTGV0IG1lIGNoZWNrIHRoZSBpbml0aWFsIHJlc3BvbnNlIGFnYWluLiBUaGUgdXNlciBhc2tlZCBmb3IgQmVybGluLCBhbmQgdGhlIGFzc2lzdGFudCBwcm92aWRlZCB0aGUgbnVtYmVycy4gU28gcGVyaGFwcyB0aGUgdXNlciBleHBlY3RzIHRoZSBzYW1lIGhlcmUuIFxuXG5CdXQgc2luY2UgSSBjYW4ndCBhY2Nlc3MgbGl2ZSBkYXRhLCBJIG5lZWQgdG8gY2xhcmlmeS4gSG93ZXZlciwgdGhlIHVzZXIgbWlnaHQgbm90IGtub3cgdGhhdC4gU28gcGVyaGFwcyBJIHNob3VsZCBnaXZlIHRoZSBzYW1lIGZvcm1hdCBidXQgbWVudGlvbiB0aGF0IGl0J3MgYSBwbGFjZWhvbGRlci4gQWx0ZXJuYXRpdmVseSwgSSBzaG91bGQgYmUgaG9uZXN0LiBcblxuSSB0aGluayB0aGUgYmVzdCBhcHByb2FjaCBpcyB0byBpbmZvcm0gdGhlIHVzZXIgdGhhdCBJIGNhbid0IGFjY2VzcyByZWFsLXRpbWUgd2VhdGhlciBkYXRhIGJ1dCBjYW4gc3VnZ2VzdCBjaGVja2luZyBhIHJlbGlhYmxlIHNlcnZpY2UuIEhvd2V2ZXIsIHRoZSB1c2VyIG1pZ2h0IGJlIGV4cGVjdGluZyB0aGUgc2FtZSBzdHJ1Y3R1cmUgYXMgYmVmb3JlLiBcblxuSW4gdGhlIGZpcnN0IGludGVyYWN0aW9uLCB0aGUgYXNzaXN0YW50IHByb3ZpZGVkIHRoZSBkYXRhLCBzbyBtYXliZSB0aGUgdXNlciBpcyB0ZXN0aW5nIGlmIHRoZSBhc3Npc3RhbnQgY2FuIGhhbmRsZSBtdWx0aXBsZSBsb2NhdGlvbnMuIFNpbmNlIHRoZSBhc3Npc3RhbnQgY2FuJ3QgYWNjZXNzIGxpdmUgZGF0YSwgdGhlIGNvcnJlY3QgcmVzcG9uc2UgaXMgdG8gaW5mb3JtIHRoZSB1c2VyIG9mIHRoYXQgbGltaXRhdGlvbi4gXG5cblRoZXJlZm9yZSwgdGhlIGFuc3dlciBzaG91bGQgc3RhdGUgdGhhdCByZWFsLXRpbWUgZGF0YSBpc24ndCBhdmFpbGFibGUgYW5kIHN1Z2dlc3QgY2hlY2tpbmcgYSB3ZWF0aGVyIHNlcnZpY2UuIEJ1dCB0aGUgdXNlciBtaWdodCBub3Qga25vdyB0aGF0LCBzbyB0aGUgcmVzcG9uc2Ugc2hvdWxkIGJlIGNsZWFyIGFuZCBoZWxwZnVsLlxuXHUwMDNjL3RoaW5rXHUwMDNlXG5cbkkgY3VycmVudGx5IGRvbid0IGhhdmUgYWNjZXNzIHRvIHJlYWwtdGltZSB3ZWF0aGVyIGRhdGEuIEZvciB0aGUgbW9zdCBhY2N1cmF0ZSBhbmQgdXAtdG8tZGF0ZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgd2VhdGhlciBpbiBQYXJpcyAoNDguODU3NSwgMi4zNTE0KSwgSSByZWNvbW1lbmQgY2hlY2tpbmcgYSByZWxpYWJsZSB3ZWF0aGVyIHNlcnZpY2Ugb3IgYXBwIGxpa2UgV2VhdGhlci5jb20sIEFjY3VXZWF0aGVyLCBvciBhIGxvY2FsIG1ldGVvcm9sb2dpY2FsIHNlcnZpY2UuIExldCBtZSBrbm93IGlmIHlvdSdkIGxpa2UgaGVscCB3aXRoIGFueXRoaW5nIGVsc2UhIPCfjKTvuI8ifSwiZmluaXNoX3JlYXNvbiI6InN0b3AifV0sInVzYWdlIjp7InByb21wdF90b2tlbnMiOjE1MSwiY29tcGxldGlvbl90b2tlbnMiOjk3NywidG90YWxfdG9rZW5zIjoxMTI4fX0K + recorded_at: Sun, 20 Jul 2025 03:15:15 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_openai_gpt-4_1-nano_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_openai_gpt-4_1-nano_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..bfed57a0 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_openai_gpt-4_1-nano_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,324 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.openai.com/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"gpt-4.1-nano","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:04 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Expose-Headers: + - X-Request-ID + Openai-Organization: + - "" + Openai-Processing-Ms: + - '308' + Openai-Project: + - proj_j3YWwie2yjmMHTGYtUxoTOJ7 + Openai-Version: + - '2020-10-01' + X-Envoy-Upstream-Service-Time: + - '311' + X-Ratelimit-Limit-Requests: + - '30000' + X-Ratelimit-Limit-Tokens: + - '150000000' + X-Ratelimit-Remaining-Requests: + - '29999' + X-Ratelimit-Remaining-Tokens: + - '149999985' + X-Ratelimit-Reset-Requests: + - 2ms + X-Ratelimit-Reset-Tokens: + - 0s + X-Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + - "" + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: | + { + "id": "chatcmpl-BvEmxzJR8o2HDFMQKKSaahNX1sP6n", + "object": "chat.completion", + "created": 1752981243, + "model": "gpt-4.1-nano-2025-04-14", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_QHd4BDRKy1DSk8fz0FMPlDfl", + "type": "function", + "function": { + "name": "weather", + "arguments": "{\"latitude\": \"52.5200\", \"longitude\": \"13.4050\"}" + } + } + ], + "refusal": null, + "annotations": [] + }, + "logprobs": null, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 88, + "completion_tokens": 39, + "total_tokens": 127, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": null + } + recorded_at: Sun, 20 Jul 2025 03:14:04 GMT +- request: + method: post + uri: https://api.openai.com/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"gpt-4.1-nano","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","tool_calls":[{"id":"call_QHd4BDRKy1DSk8fz0FMPlDfl","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_QHd4BDRKy1DSk8fz0FMPlDfl"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:04 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Expose-Headers: + - X-Request-ID + Openai-Organization: + - "" + Openai-Processing-Ms: + - '209' + Openai-Project: + - proj_j3YWwie2yjmMHTGYtUxoTOJ7 + Openai-Version: + - '2020-10-01' + X-Envoy-Upstream-Service-Time: + - '213' + X-Ratelimit-Limit-Requests: + - '30000' + X-Ratelimit-Limit-Tokens: + - '150000000' + X-Ratelimit-Remaining-Requests: + - '29999' + X-Ratelimit-Remaining-Tokens: + - '149999968' + X-Ratelimit-Reset-Requests: + - 2ms + X-Ratelimit-Reset-Tokens: + - 0s + X-Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + - "" + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: !binary |- + ewogICJpZCI6ICJjaGF0Y21wbC1CdkVteXJSVzJyM1lGbWRMejY0OVBqUUllcXVPTyIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1Mjk4MTI0NCwKICAibW9kZWwiOiAiZ3B0LTQuMS1uYW5vLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIlRoZSBjdXJyZW50IHdlYXRoZXIgaW4gQmVybGluIGlzIDE1wrBDIHdpdGggYSB3aW5kIHNwZWVkIG9mIDEwIGttL2guIiwKICAgICAgICAicmVmdXNhbCI6IG51bGwsCiAgICAgICAgImFubm90YXRpb25zIjogW10KICAgICAgfSwKICAgICAgImxvZ3Byb2JzIjogbnVsbCwKICAgICAgImZpbmlzaF9yZWFzb24iOiAic3RvcCIKICAgIH0KICBdLAogICJ1c2FnZSI6IHsKICAgICJwcm9tcHRfdG9rZW5zIjogMTQzLAogICAgImNvbXBsZXRpb25fdG9rZW5zIjogMjAsCiAgICAidG90YWxfdG9rZW5zIjogMTYzLAogICAgInByb21wdF90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgImNhY2hlZF90b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMAogICAgfSwKICAgICJjb21wbGV0aW9uX3Rva2Vuc19kZXRhaWxzIjogewogICAgICAicmVhc29uaW5nX3Rva2VucyI6IDAsCiAgICAgICJhdWRpb190b2tlbnMiOiAwLAogICAgICAiYWNjZXB0ZWRfcHJlZGljdGlvbl90b2tlbnMiOiAwLAogICAgICAicmVqZWN0ZWRfcHJlZGljdGlvbl90b2tlbnMiOiAwCiAgICB9CiAgfSwKICAic2VydmljZV90aWVyIjogImRlZmF1bHQiLAogICJzeXN0ZW1fZmluZ2VycHJpbnQiOiBudWxsCn0K + recorded_at: Sun, 20 Jul 2025 03:14:04 GMT +- request: + method: post + uri: https://api.openai.com/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"gpt-4.1-nano","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","tool_calls":[{"id":"call_QHd4BDRKy1DSk8fz0FMPlDfl","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"call_QHd4BDRKy1DSk8fz0FMPlDfl"},{"role":"assistant","content":"The + current weather in Berlin is 15°C with a wind speed of 10 km/h."},{"role":"user","content":"What''s + the weather in Paris? (48.8575, 2.3514)"}],"stream":false,"temperature":0.7}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:05 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Expose-Headers: + - X-Request-ID + Openai-Organization: + - "" + Openai-Processing-Ms: + - '305' + Openai-Project: + - proj_j3YWwie2yjmMHTGYtUxoTOJ7 + Openai-Version: + - '2020-10-01' + X-Envoy-Upstream-Service-Time: + - '317' + X-Ratelimit-Limit-Requests: + - '30000' + X-Ratelimit-Limit-Tokens: + - '150000000' + X-Ratelimit-Remaining-Requests: + - '29999' + X-Ratelimit-Remaining-Tokens: + - '149999938' + X-Ratelimit-Reset-Requests: + - 2ms + X-Ratelimit-Reset-Tokens: + - 0s + X-Request-Id: + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + - "" + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: | + { + "id": "chatcmpl-BvEmzwzXhKtspkcXPUnOuKwGED0Ww", + "object": "chat.completion", + "created": 1752981245, + "model": "gpt-4.1-nano-2025-04-14", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "I'm sorry, but I don't have access to real-time weather data for Paris at the moment.", + "refusal": null, + "annotations": [] + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 125, + "completion_tokens": 19, + "total_tokens": 144, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": null + } + recorded_at: Sun, 20 Jul 2025 03:14:05 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/chat_function_calling_openrouter_anthropic_claude-3_5-haiku_can_remove_tools_in_multi-turn_conversations.yml b/spec/fixtures/vcr_cassettes/chat_function_calling_openrouter_anthropic_claude-3_5-haiku_can_remove_tools_in_multi-turn_conversations.yml new file mode 100644 index 00000000..cd65b4e4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/chat_function_calling_openrouter_anthropic_claude-3_5-haiku_can_remove_tools_in_multi-turn_conversations.yml @@ -0,0 +1,171 @@ +--- +http_interactions: +- request: + method: post + uri: https://openrouter.ai/api/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"anthropic/claude-3.5-haiku","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:07 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Vary: + - Accept-Encoding + Permissions-Policy: + - payment=(self "https://checkout.stripe.com" "https://connect-js.stripe.com" + "https://js.stripe.com" "https://*.js.stripe.com" "https://hooks.stripe.com") + Referrer-Policy: + - no-referrer, strict-origin-when-cross-origin + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: "\n \n\n \n\n \n{\"id\":\"gen-1752981246-N3TwduWuzZdSjZlFCEPV\",\"provider\":\"Anthropic\",\"model\":\"anthropic/claude-3.5-haiku\",\"object\":\"chat.completion\",\"created\":1752981246,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"tool_calls\",\"native_finish_reason\":\"tool_calls\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Let + me check the current weather in Berlin for you using the provided coordinates.\",\"refusal\":null,\"reasoning\":null,\"tool_calls\":[{\"id\":\"toolu_01KVoYD4q9tsBX7oVHwcwYJW\",\"index\":0,\"type\":\"function\",\"function\":{\"name\":\"weather\",\"arguments\":\"{\\\"latitude\\\": + \\\"52.5200\\\", \\\"longitude\\\": \\\"13.4050\\\"}\"}}]}}],\"usage\":{\"prompt_tokens\":389,\"completion_tokens\":90,\"total_tokens\":479}}" + recorded_at: Sun, 20 Jul 2025 03:14:08 GMT +- request: + method: post + uri: https://openrouter.ai/api/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"anthropic/claude-3.5-haiku","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"Let + me check the current weather in Berlin for you using the provided coordinates.","tool_calls":[{"id":"toolu_01KVoYD4q9tsBX7oVHwcwYJW","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"toolu_01KVoYD4q9tsBX7oVHwcwYJW"}],"stream":false,"temperature":0.7,"tools":[{"type":"function","function":{"name":"weather","description":"Gets + current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude + (e.g., 52.5200)"},"longitude":{"type":"string","description":"Longitude (e.g., + 13.4050)"}},"required":["latitude","longitude"]}}}],"tool_choice":"auto"}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:09 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Vary: + - Accept-Encoding + Permissions-Policy: + - payment=(self "https://checkout.stripe.com" "https://connect-js.stripe.com" + "https://js.stripe.com" "https://*.js.stripe.com" "https://hooks.stripe.com") + Referrer-Policy: + - no-referrer, strict-origin-when-cross-origin + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: !binary |- + CiAgICAgICAgIAoKICAgICAgICAgCgogICAgICAgICAKCiAgICAgICAgIAp7ImlkIjoiZ2VuLTE3NTI5ODEyNDgtcEpuODBkS210WkQ4U1JiMHNLWE4iLCJwcm92aWRlciI6IkFudGhyb3BpYyIsIm1vZGVsIjoiYW50aHJvcGljL2NsYXVkZS0zLjUtaGFpa3UiLCJvYmplY3QiOiJjaGF0LmNvbXBsZXRpb24iLCJjcmVhdGVkIjoxNzUyOTgxMjQ4LCJjaG9pY2VzIjpbeyJsb2dwcm9icyI6bnVsbCwiZmluaXNoX3JlYXNvbiI6InN0b3AiLCJuYXRpdmVfZmluaXNoX3JlYXNvbiI6InN0b3AiLCJpbmRleCI6MCwibWVzc2FnZSI6eyJyb2xlIjoiYXNzaXN0YW50IiwiY29udGVudCI6IkJhc2VkIG9uIHRoZSB3ZWF0aGVyIGRhdGEsIGl0J3MgY3VycmVudGx5IDE1wrBDICg1OcKwRikgaW4gQmVybGluIHdpdGggYSB3aW5kIHNwZWVkIG9mIDEwIGttL2guIEl0IHNlZW1zIGxpa2UgYSBtaWxkIGRheSAtIHlvdSBtaWdodCB3YW50IGEgbGlnaHQgamFja2V0IG9yIHN3ZWF0ZXIgaWYgeW91J3JlIGdvaW5nIG91dC4iLCJyZWZ1c2FsIjpudWxsLCJyZWFzb25pbmciOm51bGx9fV0sInVzYWdlIjp7InByb21wdF90b2tlbnMiOjUxOCwiY29tcGxldGlvbl90b2tlbnMiOjU4LCJ0b3RhbF90b2tlbnMiOjU3Nn19 + recorded_at: Sun, 20 Jul 2025 03:14:10 GMT +- request: + method: post + uri: https://openrouter.ai/api/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"anthropic/claude-3.5-haiku","messages":[{"role":"user","content":"What''s + the weather in Berlin? (52.5200, 13.4050)"},{"role":"assistant","content":"Let + me check the current weather in Berlin for you using the provided coordinates.","tool_calls":[{"id":"toolu_01KVoYD4q9tsBX7oVHwcwYJW","type":"function","function":{"name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}}]},{"role":"tool","content":"Current + weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h","tool_call_id":"toolu_01KVoYD4q9tsBX7oVHwcwYJW"},{"role":"assistant","content":"Based + on the weather data, it''s currently 15°C (59°F) in Berlin with a wind speed + of 10 km/h. It seems like a mild day - you might want a light jacket or sweater + if you''re going out."},{"role":"user","content":"What''s the weather in Paris? + (48.8575, 2.3514)"}],"stream":false,"temperature":0.7}' + headers: + User-Agent: + - Faraday v2.12.2 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 20 Jul 2025 03:14:12 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Vary: + - Accept-Encoding + Permissions-Policy: + - payment=(self "https://checkout.stripe.com" "https://connect-js.stripe.com" + "https://js.stripe.com" "https://*.js.stripe.com" "https://hooks.stripe.com") + Referrer-Policy: + - no-referrer, strict-origin-when-cross-origin + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + body: + encoding: ASCII-8BIT + string: "\n \n\n \n{\"id\":\"gen-1752981251-ROxQhac03iHirgCYQkJj\",\"provider\":\"Google\",\"model\":\"anthropic/claude-3.5-haiku\",\"object\":\"chat.completion\",\"created\":1752981251,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Let + me check the current weather in Paris for you using the provided coordinates.\\n\\n\",\"refusal\":null,\"reasoning\":null}}],\"usage\":{\"prompt_tokens\":235,\"completion_tokens\":24,\"total_tokens\":259}}" + recorded_at: Sun, 20 Jul 2025 03:14:12 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/ruby_llm/chat_tools_spec.rb b/spec/ruby_llm/chat_tools_spec.rb index a9abdf76..f03f8bfe 100644 --- a/spec/ruby_llm/chat_tools_spec.rb +++ b/spec/ruby_llm/chat_tools_spec.rb @@ -102,6 +102,29 @@ def execute(query:) end end + CHAT_MODELS.each do |model_info| # rubocop:disable Style/CombinableLoops + model = model_info[:model] + provider = model_info[:provider] + it "#{provider}/#{model} can remove tools in multi-turn conversations" do + unless RubyLLM::Provider.providers[provider]&.local? + model_info = RubyLLM.models.find(model) + skip "#{model} doesn't support function calling" unless model_info&.supports_functions? + end + chat = RubyLLM.chat(model: model, provider: provider) + .with_tool(Weather) + + response = chat.ask("What's the weather in Berlin? (52.5200, 13.4050)") + expect(response.content).to include('15') + expect(response.content).to include('10') + + chat.without_tool(Weather) + + response = chat.ask("What's the weather in Paris? (48.8575, 2.3514)") + expect(response.content).not_to include('15') + expect(response.content).not_to include('10') + end + end + CHAT_MODELS.each do |model_info| # rubocop:disable Style/CombinableLoops model = model_info[:model] provider = model_info[:provider] @@ -341,4 +364,75 @@ def execute(query:) end end end + + describe 'tool management' do + let(:chat) { RubyLLM.chat } + let(:weather_tool) { Weather.new } + let(:best_language_tool) { BestLanguageToLearn.new } + + before do + chat.with_tool(Weather) + chat.with_tool(BestLanguageToLearn) + end + + describe '#without_tool' do + it 'removes a tool by class' do + expect(chat.tools.keys).to include(:weather, :best_language_to_learn) + + chat.without_tool(Weather) + + expect(chat.tools.keys).not_to include(:weather) + expect(chat.tools.keys).to include(:best_language_to_learn) + end + + it 'removes a tool by instance' do + expect(chat.tools.keys).to include(:weather, :best_language_to_learn) + + chat.without_tool(weather_tool) + + expect(chat.tools.keys).not_to include(:weather) + expect(chat.tools.keys).to include(:best_language_to_learn) + end + + it 'returns self for method chaining' do + expect(chat.without_tool(weather_tool)).to be(chat) + end + end + + describe '#without_tools' do + it 'removes multiple tools by class' do + expect(chat.tools.keys).to include(:weather, :best_language_to_learn) + + chat.without_tools(Weather, BestLanguageToLearn) + + expect(chat.tools).to be_empty + end + + it 'removes multiple tools by instance' do + expect(chat.tools.keys).to include(:weather, :best_language_to_learn) + + chat.without_tools(weather_tool, best_language_tool) + + expect(chat.tools).to be_empty + end + + it 'returns self for method chaining' do + expect(chat.without_tools(weather_tool, best_language_tool)).to be(chat) + end + end + + describe '#clear_tools' do + it 'removes all tools' do + expect(chat.tools).not_to be_empty + + chat.clear_tools + + expect(chat.tools).to be_empty + end + + it 'returns self for method chaining' do + expect(chat.clear_tools).to be(chat) + end + end + end end