Files
YiemAgent/test/_extractToolCalls.jl
T
2026-08-13 05:56:08 +07:00

344 lines
12 KiB
Julia

using Test
using YiemAgent
using YiemAgent.agentCore
using YiemAgent.type
using JSON
# Import the function from the private module scope
import YiemAgent.agentCore: _extractToolCalls
@testset "_extractToolCalls" begin
# -------------------------------------------------------------- #
# Format 1: response["message"]["tool_calls"] (LMStudio.jl style) #
# -------------------------------------------------------------- #
@testset "single tool call via message format" begin
response = Dict{String,Any}(
"finish_reason" => "tool_calls",
"index" => 0,
"message" => Dict{String,Any}(
"role" => "assistant",
"content" => "",
"reasoning_content" => "Let me check the weather.",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getWeather",
"arguments" => "{\"city\":\"Bangkok, Thailand\"}",
),
"id" => "tc_001",
)
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getWeather"
@test tc_list[1].id == "tc_001"
@test tc_list[1].arguments["city"] == "Bangkok, Thailand"
end
@testset "multiple tool calls via message format" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"content" => "",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getWeather",
"arguments" => "{\"city\":\"Tokyo, Japan\"}",
),
"id" => "tc_001",
),
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getTime",
"arguments" => "{\"timezone\":\"Asia/Tokyo\"}",
),
"id" => "tc_002",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 2
@test tc_list[1].name == "getWeather"
@test tc_list[1].arguments["city"] == "Tokyo, Japan"
@test tc_list[2].name == "getTime"
@test tc_list[2].arguments["timezone"] == "Asia/Tokyo"
end
@testset "tool call with empty arguments string" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "listTools",
"arguments" => "{}",
),
"id" => "tc_empty",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "listTools"
@test tc_list[1].arguments == Dict{String,Any}()
end
@testset "tool call with missing id falls back to uuid" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getTime",
"arguments" => "{\"city\":\"NYC\"}",
),
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test !isempty(tc_list[1].id)
@test tc_list[1].name == "getTime"
end
@testset "tool call with non-string arguments (pre-parsed)" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getWeather",
"arguments" => Dict{String,Any}("city" => "London", "units" => "fahrenheit"),
),
"id" => "tc_parsed",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].arguments["city"] == "London"
@test tc_list[1].arguments["units"] == "fahrenheit"
end
# ----------------------------------------------------------- #
# Format 2: response.content blocks (OpenAI API style) #
# ----------------------------------------------------------- #
@testset "content blocks with tool_calls" begin
response = Dict{String,Any}(
"content" => Any[
Dict{String,Any}("type" => "text", "text" => "Let me check."),
Dict{String,Any}(
"type" => "tool_calls",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getWeather",
"arguments" => "{\"city\":\"Paris\"}",
),
"id" => "tc_block_1",
),
],
),
],
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getWeather"
@test tc_list[1].arguments["city"] == "Paris"
end
@testset "content blocks with tool_call (single-call format)" begin
response = Dict{String,Any}(
"content" => Any[
Dict{String,Any}(
"type" => "tool_call",
"id" => "tc_single",
"name" => "getTime",
"arguments" => Dict{String,Any}("timezone" => "Europe/London"),
),
],
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getTime"
@test tc_list[1].id == "tc_single"
@test tc_list[1].arguments["timezone"] == "Europe/London"
end
# ----------------------------------------------------------- #
# Format 2 via struct-like object (no .content field) #
# ----------------------------------------------------------- #
@testset "no tool calls found" begin
response = Dict{String,Any}(
"content" => Any[
Dict{String,Any}("type" => "text", "text" => "Hello world."),
],
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == false
@test length(tc_list) == 0
end
@testset "empty message" begin
response = Dict{String,Any}()
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == false
@test length(tc_list) == 0
end
@testset "message with empty tool_calls array" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == false
@test length(tc_list) == 0
end
@testset "Format 1 takes priority over Format 2" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getWeather",
"arguments" => "{\"city\":\"Format1\"}",
),
"id" => "tc_fmt1",
),
],
),
"content" => Any[
Dict{String,Any}(
"type" => "tool_call",
"id" => "tc_fmt2",
"name" => "getTime",
"arguments" => Dict{String,Any}("city" => "Format2"),
),
],
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getWeather"
end
# ----------------------------------------------------------- #
# edge cases #
# ----------------------------------------------------------- #
@testset "tool call with null arguments" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}(
"name" => "getTime",
"arguments" => nothing,
),
"id" => "tc_null",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getTime"
end
@testset "tool call with missing function key" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"id" => "tc_nofunc",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == ""
end
@testset "tool call with missing name in function block" begin
response = Dict{String,Any}(
"message" => Dict{String,Any}(
"role" => "assistant",
"tool_calls" => Any[
Dict{String,Any}(
"type" => "function",
"function" => Dict{String,Any}("arguments" => "{}"),
"id" => "tc_noname",
),
],
),
)
has_toolcalls, tc_list = _extractToolCalls(response)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == ""
end
@testset "message format with JSON.Object (JSON.parse result)" begin
json_str = JSON.json(Dict(
"message" => Dict(
"role" => "assistant",
"tool_calls" => [
Dict(
"type" => "function",
"function" => Dict("name" => "getWeather", "arguments" => "{\"city\":\"Test\"}"),
"id" => "tc_jsonobj",
),
],
),
))
parsed = JSON.parse(json_str)
has_toolcalls, tc_list = _extractToolCalls(parsed)
@test has_toolcalls == true
@test length(tc_list) == 1
@test tc_list[1].name == "getWeather"
@test tc_list[1].arguments["city"] == "Test"
end
end