update
This commit is contained in:
+76
-119
@@ -3,9 +3,8 @@ using YiemAgent
|
||||
using YiemAgent.toolRegistry
|
||||
using YiemAgent.type
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# loadTools() unit tests #
|
||||
# ------------------------------------------------------------------ #
|
||||
# Path to the real tools directory
|
||||
TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
||||
|
||||
@testset "loadTools" begin
|
||||
|
||||
@@ -16,138 +15,99 @@ using YiemAgent.type
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 2. loadTools throws if a .jl file does not define getTool() #
|
||||
# Must run before any other loadTools call (getTool persists in #
|
||||
# module scope after include()). #
|
||||
# Must run BEFORE any other loadTools call (getTool binding #
|
||||
# persists in module scope after include()). #
|
||||
# ------------------------------------------------------------------ #
|
||||
bad_dir = mktempdir()
|
||||
write(joinpath(bad_dir, "noTool.jl"), "x = 42\n")
|
||||
@test_throws ArgumentError loadTools(bad_dir)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 3. loadTools loads tool files that define getTool() #
|
||||
# 3. loadTools loads actual tool files from src/tools/ #
|
||||
# ------------------------------------------------------------------ #
|
||||
tmpdir = mktempdir()
|
||||
|
||||
# Create a valid tool file (must use bare type names — include() places file in toolRegistry scope)
|
||||
valid_tool_echo = """
|
||||
function getTool()::agentTool
|
||||
return agentTool(
|
||||
name = "testEcho",
|
||||
label = "Echo Test",
|
||||
description = "Echoes the input argument",
|
||||
inputSchema = Dict{String,Any}(
|
||||
"type" => "object",
|
||||
"properties" => Dict("message" => Dict("type" => "string")),
|
||||
"required" => Any["message"]
|
||||
),
|
||||
execute = (toolCallId, args, signal, onPartialResult) -> begin
|
||||
return agentToolResult(
|
||||
[textContent("echo: " * string(args["message"]))],
|
||||
Dict{Any,Any}(), nothing, false
|
||||
)
|
||||
end,
|
||||
prepareArguments = nothing,
|
||||
validateRequiredArgs = nothing,
|
||||
parallelToolExecute = false
|
||||
)
|
||||
end
|
||||
"""
|
||||
write(joinpath(tmpdir, "getEcho.jl"), valid_tool_echo)
|
||||
|
||||
loaded = loadTools(tmpdir)
|
||||
loaded = loadTools(TOOLS_DIR)
|
||||
@test !isempty(loaded)
|
||||
@test length(loaded) >= 1
|
||||
@test length(loaded) == 3
|
||||
|
||||
names = [t.name for t in loaded]
|
||||
@test "testEcho" in names
|
||||
|
||||
# Check agentTool fields
|
||||
echo_tool = filter(t -> t.name == "testEcho", loaded)
|
||||
@test !isempty(echo_tool)
|
||||
@test echo_tool[1].label == "Echo Test"
|
||||
@test echo_tool[1].description == "Echoes the input argument"
|
||||
@test echo_tool[1].parallelToolExecute == false
|
||||
@test echo_tool[1].execute !== nothing
|
||||
@test "getTime" in names
|
||||
@test "getWeather" in names
|
||||
@test "writeTool" in names
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 4. loadTools returns tools sorted alphabetically #
|
||||
# 4. loadTools returns tools sorted alphabetically by filename #
|
||||
# (getTime.jl < getWeather.jl < writeTool.jl) #
|
||||
# because 'T' < 'W' in ASCII #
|
||||
# ------------------------------------------------------------------ #
|
||||
sorted_dir = mktempdir()
|
||||
|
||||
tool_a = """
|
||||
function getTool()::agentTool
|
||||
return agentTool(
|
||||
name = "alphaTool",
|
||||
label = "Alpha Tool",
|
||||
description = "First tool",
|
||||
inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]),
|
||||
execute = (toolCallId, args, signal, onPartialResult) ->
|
||||
agentToolResult([textContent("alpha")], Dict{Any,Any}(), nothing, false),
|
||||
prepareArguments = nothing,
|
||||
validateRequiredArgs = nothing,
|
||||
parallelToolExecute = false
|
||||
)
|
||||
end
|
||||
"""
|
||||
|
||||
tool_m = """
|
||||
function getTool()::agentTool
|
||||
return agentTool(
|
||||
name = "midTool",
|
||||
label = "Mid Tool",
|
||||
description = "Middle tool",
|
||||
inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]),
|
||||
execute = (toolCallId, args, signal, onPartialResult) ->
|
||||
agentToolResult([textContent("mid")], Dict{Any,Any}(), nothing, false),
|
||||
prepareArguments = nothing,
|
||||
validateRequiredArgs = nothing,
|
||||
parallelToolExecute = false
|
||||
)
|
||||
end
|
||||
"""
|
||||
|
||||
tool_z = """
|
||||
function getTool()::agentTool
|
||||
return agentTool(
|
||||
name = "zuluTool",
|
||||
label = "Zulu Tool",
|
||||
description = "Last tool",
|
||||
inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]),
|
||||
execute = (toolCallId, args, signal, onPartialResult) ->
|
||||
agentToolResult([textContent("zulu")], Dict{Any,Any}(), nothing, false),
|
||||
prepareArguments = nothing,
|
||||
validateRequiredArgs = nothing,
|
||||
parallelToolExecute = false
|
||||
)
|
||||
end
|
||||
"""
|
||||
|
||||
write(joinpath(sorted_dir, "zTool.jl"), tool_z)
|
||||
write(joinpath(sorted_dir, "aTool.jl"), tool_a)
|
||||
write(joinpath(sorted_dir, "mTool.jl"), tool_m)
|
||||
|
||||
loaded_sorted = loadTools(sorted_dir)
|
||||
# loadTools returns only tools loaded from the directory, in file-sorted order
|
||||
@test length(loaded_sorted) == 3
|
||||
@test loaded_sorted[1].name == "alphaTool"
|
||||
@test loaded_sorted[2].name == "midTool"
|
||||
@test loaded_sorted[3].name == "zuluTool"
|
||||
@test loaded[1].name == "getTime"
|
||||
@test loaded[2].name == "getWeather"
|
||||
@test loaded[3].name == "writeTool"
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 5. getTools returns a deep copy (mutations don't affect registry) #
|
||||
# 5. Verify loaded tool fields are correct #
|
||||
# ------------------------------------------------------------------ #
|
||||
# getTime
|
||||
time_tool = loaded[1]
|
||||
@test time_tool.name == "getTime"
|
||||
@test time_tool.label == "Time Lookup"
|
||||
@test time_tool.validateRequiredArgs !== nothing
|
||||
@test time_tool.parallelToolExecute == false
|
||||
@test time_tool.inputSchema["required"] == Any[]
|
||||
|
||||
# getWeather
|
||||
weather = loaded[2]
|
||||
@test weather.name == "getWeather"
|
||||
@test weather.label == "Weather Lookup"
|
||||
@test weather.execute !== nothing
|
||||
@test weather.parallelToolExecute == false
|
||||
@test weather.inputSchema["required"] == ["city"]
|
||||
|
||||
# writeTool
|
||||
wt = loaded[3]
|
||||
@test wt.name == "writeTool"
|
||||
@test wt.label == "Create Tool"
|
||||
@test wt.execute !== nothing
|
||||
@test "name" in wt.inputSchema["required"]
|
||||
@test "executeCode" in wt.inputSchema["required"]
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 6. Tool execution returns valid results #
|
||||
# ------------------------------------------------------------------ #
|
||||
sig = nothing
|
||||
op = x -> x # no-op partial result callback
|
||||
|
||||
# execute getTime
|
||||
result_t = time_tool.execute("call-1", Dict{String,Any}("city" => "Tokyo"), sig, op)
|
||||
@test result_t isa agentToolResult
|
||||
@test result_t.content[1] isa textContent
|
||||
@test occursin("Tokyo", result_t.content[1].text)
|
||||
|
||||
# execute getTime with timezone
|
||||
result_tz = time_tool.execute("call-2", Dict{String,Any}("timezone" => "America/New_York"), sig, op)
|
||||
@test result_tz isa agentToolResult
|
||||
@test occursin("America/New_York", result_tz.content[1].text)
|
||||
|
||||
# execute getWeather
|
||||
result_w = weather.execute("call-3", Dict{String,Any}("city" => "Bangkok"), sig, op)
|
||||
@test result_w isa agentToolResult
|
||||
@test result_w.content[1] isa textContent
|
||||
@test occursin("Bangkok", result_w.content[1].text)
|
||||
|
||||
# execute getWeather with units
|
||||
result_w2 = weather.execute("call-4", Dict{String,Any}("city" => "London", "units" => "fahrenheit"), sig, op)
|
||||
@test occursin("72°F", result_w2.content[1].text)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 7. getTools / registerTool / clearTools #
|
||||
# ------------------------------------------------------------------ #
|
||||
registry_tools = getTools()
|
||||
@test !isempty(registry_tools)
|
||||
orig_count = length(registry_tools)
|
||||
@test any(t -> t.name == "getTime", registry_tools)
|
||||
@test any(t -> t.name == "getWeather", registry_tools)
|
||||
|
||||
# Clear and add a new tool via registerTool
|
||||
clearTools()
|
||||
registry_after_clear = getTools()
|
||||
@test isempty(registry_after_clear)
|
||||
@test isempty(getTools())
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 6. registerTool adds to global registry #
|
||||
# ------------------------------------------------------------------ #
|
||||
test_tool = agentTool(
|
||||
name = "manualTool",
|
||||
label = "Manual Tool",
|
||||
@@ -163,13 +123,10 @@ end
|
||||
reg = getTools()
|
||||
@test any(t -> t.name == "manualTool", reg)
|
||||
@test count(t -> t.name == "manualTool", reg) == 1
|
||||
|
||||
# parallelToolExecute flag
|
||||
manual_entry = filter(t -> t.name == "manualTool", reg)
|
||||
@test manual_entry[1].parallelToolExecute == true
|
||||
@test reg[1].parallelToolExecute == true
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 7. getTools returns deep copy #
|
||||
# 8. getTools returns deep copy (mutations don't affect registry) #
|
||||
# ------------------------------------------------------------------ #
|
||||
copy1 = getTools()
|
||||
copy2 = getTools()
|
||||
|
||||
Reference in New Issue
Block a user