Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/active_agent/providers/open_ai/chat_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def process_stream_chunk(api_response_event)

# If we have a delta, we need to update a message in the stack
message = find_or_create_message(api_message.index)
message = message_merge_delta(message, api_message.delta.as_json.deep_symbolize_keys)
message_merge_delta(message, api_message.delta.deep_to_h)

# Stream back content changes as they come in
if api_message.delta.content
Expand Down Expand Up @@ -138,7 +138,7 @@ def process_stream_chunk(api_response_event)
def process_function_calls(api_function_calls)
api_function_calls.each do |api_function_call|
content = instrument("tool_call.active_agent", tool_name: api_function_call.dig(:function, :name)) do
case api_function_call[:type]
case api_function_call[:type].to_s
when "function"
process_tool_call_function(api_function_call[:function])
else
Expand Down Expand Up @@ -243,7 +243,7 @@ def hash_merge_delta(hash, delta)
end
end
when String
hash[key] += value
hash[key] += value.to_s
else
hash[key] = value
end
Expand Down
126 changes: 126 additions & 0 deletions test/providers/open_ai/chat_provider_test.rb
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

Check failure on line 18 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/EmptyLinesAroundClassBody: Extra empty line detected at class body beginning.
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

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 34 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.
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

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 62 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.
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

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 72 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceAfterComma: Space missing after comma.

Check failure on line 72 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.
Copy link

Copilot AI Feb 27, 2026

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.

Suggested change
messages: [{ content: "What's the weather in Boston?",role: "user" }],
messages: [{ content: "What's the weather in Boston?", role: "user" }],

Copilot uses AI. Check for mistakes.
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

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 115 in test/providers/open_ai/chat_provider_test.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.
additionalProperties: false
},
strict: true
}
}
]
end
end
end
end
end
Loading