172 lines
5.0 KiB
Julia
172 lines
5.0 KiB
Julia
module toolRegistry
|
|
|
|
export loadTools, registerTool, getTools, clearTools
|
|
|
|
using ..type
|
|
|
|
# Global registry — populated at runtime by loadTools() or registerTool()
|
|
const _registry = Vector{agentTool}()
|
|
|
|
# Auto-register the built-in listTools tool
|
|
function __init__()
|
|
registerTool(_listTool())
|
|
end
|
|
|
|
"""
|
|
List tool definition — lets the agent query available tools for collision detection
|
|
when creating new tools via writeTool.
|
|
"""
|
|
function _listTool()::agentTool
|
|
return agentTool(
|
|
name = "listTools",
|
|
label = "List Tools",
|
|
description = "List all available tools with their names, labels, and descriptions. Use this before creating a new tool to check for name collisions.",
|
|
inputSchema = Dict{String,Any}(
|
|
"type" => "object",
|
|
"properties" => Dict{String,Any}(),
|
|
"required" => Any[]
|
|
),
|
|
execute = (toolCallId, args, signal, onPartialResult) -> begin
|
|
tools = getTools()
|
|
if isempty(tools)
|
|
result_text = "No tools registered."
|
|
else
|
|
lines = String["- $(t.name): $(t.label) — $(t.description)" for t in tools]
|
|
result_text = "Available tools:\n" * join(lines, "\n")
|
|
end
|
|
return agentToolResult(
|
|
[textContent(result_text)],
|
|
Dict{Any,Any}("count" => length(tools)),
|
|
nothing, false
|
|
)
|
|
end,
|
|
prepareArguments = nothing,
|
|
validateRequiredArgs = nothing,
|
|
parallelToolExecute = false
|
|
)
|
|
end
|
|
|
|
"""
|
|
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 Bangkok")],
|
|
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
|
|
|
|
"""
|
|
Clear all registered tools from the global registry.
|
|
"""
|
|
function clearTools()::Nothing
|
|
empty!(_registry)
|
|
println("[toolRegistry] Registry cleared")
|
|
return nothing
|
|
end
|
|
|
|
end # module
|