143 lines
3.9 KiB
Julia
143 lines
3.9 KiB
Julia
module toolRegistry
|
|
|
|
export loadTools, registerTool, getTools, listTools, clearTools
|
|
|
|
using ..type
|
|
|
|
# Global registry — populated at runtime by loadTools() or registerTool()
|
|
const _registry = Vector{agentTool}()
|
|
|
|
"""
|
|
Load all tool modules from a directory.
|
|
|
|
Scans `dir` for `.jl` files. Each file must define a function named
|
|
`getTool()::agentTool`. Files are sorted alphabetically so tool
|
|
registration order is deterministic.
|
|
|
|
# Tool file format
|
|
Each `.jl` file defines one function `getTool()` that returns an `agentTool`:
|
|
|
|
```julia
|
|
# src/tools/getWeather.jl
|
|
function getTool()::agentTool
|
|
return agentTool(
|
|
name = "getWeather",
|
|
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 `getTool` function
|
|
"""
|
|
function loadTools(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 getTool was defined (include() places it in current module scope)
|
|
if !isdefined(@__MODULE__, :getTool)
|
|
throw(ArgumentError(
|
|
"Tool file $(filepath) does not define a `getTool()` function. " *
|
|
"Each tool file must define: function getTool()::agentTool ... end"
|
|
))
|
|
end
|
|
|
|
# Call getTool() — it runs in current scope where types are visible
|
|
tool = getTool()
|
|
if !(tool isa agentTool)
|
|
throw(ArgumentError(
|
|
"getTool() 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 registerTool(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 getTools()::Vector{agentTool}
|
|
return deepcopy(_registry)
|
|
end
|
|
|
|
"""
|
|
List all registered tool names and labels.
|
|
|
|
# Returns
|
|
- `Vector{Tuple{String,String}}`: Pairs of (name, label)
|
|
"""
|
|
function listTools()::Vector{Tuple{String,String}}
|
|
return [(t.name, t.label) for t in _registry]
|
|
end
|
|
|
|
"""
|
|
Clear all registered tools from the global registry.
|
|
"""
|
|
function clearTools()::Nothing
|
|
empty!(_registry)
|
|
println("[toolRegistry] Registry cleared")
|
|
return nothing
|
|
end
|
|
|
|
end # module
|