-
-
Notifications
You must be signed in to change notification settings - Fork 78
Fix OpenAI Chat stream: prevent tool_calls[].type becoming "functionf… #296
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?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "test_helper" | ||
|
|
||
| begin | ||
| require "openai" | ||
| rescue LoadError | ||
| puts "OpenAI gem not available, skipping OpenAI Chat provider tests" | ||
| return | ||
| end | ||
|
|
||
| require_relative "../../../lib/active_agent/providers/open_ai/chat_provider" | ||
|
|
||
| module Providers | ||
| module OpenAI | ||
| module Chat | ||
| class ChatProviderTest < ActiveSupport::TestCase | ||
|
|
||
| include WebMock::API | ||
|
|
||
| setup do | ||
| WebMock.enable! | ||
| @client = ::OpenAI::Client.new(base_url: "http://localhost", api_key: "test-key") | ||
| end | ||
|
|
||
| teardown do | ||
| WebMock.disable! | ||
| end | ||
|
|
||
| test "accumulates streaming tool call deltas into message_stack" do | ||
| stub_streaming_response(tool_calls_sse_response) | ||
|
|
||
| stream = @client.chat.completions.stream( | ||
| messages: [{ content: "What's the weather in Boston?", role: :user }], | ||
|
Check failure on line 34 in test/providers/open_ai/chat_provider_test.rb
|
||
| model: "qwen-plus", | ||
| tools: weather_tool | ||
| ) | ||
|
|
||
| chat_provider = ActiveAgent::Providers::OpenAI::ChatProvider.new | ||
|
|
||
| stream.each do |event| | ||
| chat_provider.send(:process_stream_chunk, event) | ||
| end | ||
|
|
||
| expected_message = { | ||
| index: 0, | ||
| role: :assistant, | ||
| tool_calls: [ | ||
| { | ||
| index: 0, | ||
| id: "call_123", | ||
| function: { | ||
| name: "get_weather", | ||
| arguments: '{"city":"Paris","units":"celsius"}' | ||
| }, | ||
| type: :function | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| assert_equal( | ||
| [expected_message], | ||
|
Check failure on line 62 in test/providers/open_ai/chat_provider_test.rb
|
||
| chat_provider.send(:message_stack), | ||
| "message_stack should contain one assistant message with merged tool_calls" | ||
| ) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def stub_streaming_response(response_body, request_options = {}) | ||
| default_request = { | ||
| messages: [{ content: "What's the weather in Boston?",role: "user" }], | ||
|
Check failure on line 72 in test/providers/open_ai/chat_provider_test.rb
|
||
| model: "qwen-plus", | ||
| stream: true | ||
| } | ||
|
|
||
| stub_request(:post, "http://localhost/chat/completions") | ||
| .with(body: hash_including(default_request.merge(request_options))) | ||
| .to_return( | ||
| status: 200, | ||
| headers: { "Content-Type" => "text/event-stream" }, | ||
| body: response_body | ||
| ) | ||
| end | ||
|
|
||
| def tool_calls_sse_response | ||
| <<~SSE | ||
| data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1234567890,"model":"qwen-plus","choices":[{"index":0,"content":null,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_123","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]} | ||
| data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1234567890,"model":"qwen-plus","choices":[{"index":0,"content":null,"delta":{"tool_calls":[{"index":0,"type":"function","function":{"arguments":"{\\"city\\":"}}]},"finish_reason":null}]} | ||
| data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1234567890,"model":"qwen-plus","choices":[{"index":0,"content":null,"delta":{"tool_calls":[{"index":0,"type":"function","function":{"arguments":"\\"Paris\\","}}]},"finish_reason":null}]} | ||
| data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1234567890,"model":"qwen-plus","choices":[{"index":0,"content":null,"delta":{"tool_calls":[{"index":0,"type":"function","function":{"arguments":"\\"units\\":\\"celsius\\"}"}}]},"finish_reason":null}]} | ||
| data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1234567890,"model":"qwen-plus","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]} | ||
| data: [DONE] | ||
| SSE | ||
| end | ||
|
|
||
| def weather_tool | ||
| [ | ||
| { | ||
| type: :function, | ||
| function: { | ||
| name: "get_weather", | ||
| parameters: { | ||
| type: "object", | ||
| properties: { | ||
| city: { type: "string" }, | ||
| units: { type: "string" } | ||
| }, | ||
| required: ["city", "units"], | ||
|
Check failure on line 115 in test/providers/open_ai/chat_provider_test.rb
|
||
| additionalProperties: false | ||
| }, | ||
| strict: true | ||
| } | ||
| } | ||
| ] | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
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.
Missing space after comma. The code has
"What's the weather in Boston?",role: "user"but should have a space after the comma for consistency with Ruby style conventions.