This commit is contained in:
2026-08-07 23:13:13 +07:00
parent 8e356f06bc
commit e60cbc67c4
5 changed files with 347 additions and 7 deletions
+59
View File
@@ -0,0 +1,59 @@
"""
Execute the get_weather tool.
# Arguments
- `toolCallId::String`: Unique identifier for this tool call
- `args::Dict{String,Any}`: Parsed arguments from the LLM
- `signal::Union{Nothing,abortSignal}`: Optional abort signal
- `onPartialResult::Function`: Callback for streaming partial results
# Returns
- `agentToolResult`: Result content with weather data
"""
function execute_tool(toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal}, onPartialResult::Function) :: agentToolResult
city = get(args, "city", "")
units = get(args, "units", "celsius")
# Validate required arguments
if isempty(city)
return agentToolResult(
[textContent("Error: 'city' argument is required.")],
Dict{Any,Any}(), nothing, false
)
end
# Simulate weather fetch — replace with actual API call
# You can call onPartialResult() here for streaming progress updates:
# onPartialResult(Dict("status" => "Fetching weather data..."))
# onPartialResult(Dict("status" => "Processing..."))
temp = units == "fahrenheit" ? "72" : "22"
unit_symbol = units == "celsius" ? "°C" : "°F"
return agentToolResult(
[textContent("Weather in $(city): Sunny, $(temp)$(unit_symbol)")],
Dict{Any,Any}(), nothing, false
)
end
"""
Define and return the get_weather agentTool.
"""
function get_tool() :: agentTool
return agentTool(
name = "get_weather",
label = "Weather Lookup",
description = "Fetch current weather and forecast for a given city.",
inputSchema = Dict{String,Any}(
"type" => "object",
"properties" => Dict(
"city" => Dict("type" => "string", "description" => "City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'"),
"units" => Dict("type" => "string", "enum" => ["celsius", "fahrenheit"], "default" => "celsius", "description" => "Temperature scale")
),
"required" => ["city"]
),
execute = execute_tool, # reference the function defined above
prepareArguments = nothing,
parallelToolExecute = false
)
end
+142
View File
@@ -0,0 +1,142 @@
module toolRegistry
export load_tools, register_tool, get_tools, list_tools, clear_tools
using ..type
# Global registry — populated at runtime by load_tools() or register_tool()
const _registry = Vector{agentTool}()
"""
Load all tool modules from a directory.
Scans `dir` for `.jl` files. Each file must define a function named
`get_tool() :: agentTool`. Files are sorted alphabetically so tool
registration order is deterministic.
# Tool file format
Each `.jl` file defines one function `get_tool()` that returns an `agentTool`:
```julia
# src/tools/get_weather.jl
function get_tool() :: agentTool
return agentTool(
name = "get_weather",
label = "Weather Lookup",
description = "Fetch current weather and forecast for a given city.",
inputSchema = Dict{String,Any}(
"type" => "object",
"properties" => Dict(
"city" => Dict("type" => "string", "description" => "City and country"),
"units" => Dict("type" => "string", "enum" => ["celsius", "fahrenheit"], "default" => "celsius")
),
"required" => ["city"]
),
execute = (toolCallId, args, signal, onPartialResult) -> begin
city = args["city"]
return agentToolResult(
[textContent("Sunny, 22C in $(city)")],
Dict{Any,Any}(), nothing, false
)
end,
prepareArguments = nothing,
parallelToolExecute = false
)
end
```
# Arguments
- `dir::String`: Directory path to scan for `.jl` tool files
# Returns
- `Vector{agentTool}`: All loaded tools
# Errors
- Throws `ArgumentError` if a tool file does not define a `get_tool` function
"""
function load_tools(dir::String)::Vector{agentTool}
if !isdir(dir)
throw(ArgumentError("Tool directory does not exist: $dir"))
end
tools = agentTool[]
jl_files = filter(f -> endswith(f, ".jl"), readdir(dir))
sort!(jl_files)
for filename in jl_files
filepath = joinpath(dir, filename)
println("[toolRegistry] Loading tool from: $filepath")
# Include the file in the current module scope so all types resolve
# (agentTool, textContent, agentToolResult, etc. are all available)
include(filepath)
# Validate that get_tool was defined (include() places it in current module scope)
if !isdefined(:get_tool)
throw(ArgumentError(
"Tool file $(filepath) does not define a `get_tool()` function. " *
"Each tool file must define: function get_tool() :: agentTool ... end"
))
end
# Call get_tool() — it runs in current scope where types are visible
tool = get_tool()
if !(tool isa agentTool)
throw(ArgumentError(
"get_tool() in $(filepath) did not return an agentTool instance, got: $(typeof(tool))"
))
end
push!(_registry, tool)
push!(tools, tool)
println("[toolRegistry] Loaded tool: $(tool.name)$(tool.label)")
end
return tools
end
"""
Register a single agentTool into the global registry.
# Arguments
- `tool::agentTool`: The tool to register
# Returns
- `Vector{agentTool}`: Updated registry
"""
function register_tool(tool::agentTool)::Vector{agentTool}
push!(_registry, tool)
println("[toolRegistry] Registered tool: $(tool.name)")
return _registry
end
"""
Get all registered tools.
# Returns
- `Vector{agentTool}`: Copy of the registry
"""
function get_tools()::Vector{agentTool}
return deepcopy(_registry)
end
"""
List all registered tool names and labels.
# Returns
- `Vector{Tuple{String,String}}`: Pairs of (name, label)
"""
function list_tools()::Vector{Tuple{String,String}}
return [(t.name, t.label) for t in _registry]
end
"""
Clear all registered tools from the global registry.
"""
function clear_tools()::Nothing
empty!(_registry)
println("[toolRegistry] Registry cleared")
return nothing
end
end # module