update
This commit is contained in:
+41
-8
@@ -152,19 +152,24 @@ The `_listTool()` is auto-registered in `__init__()` (line 16-18), so `listTools
|
|||||||
### Registration API
|
### Registration API
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
# Auto-load from directory
|
# Auto-load from directory (returns OrderedDict keyed by tool name)
|
||||||
tools = loadTools("src/tools") # OrderedDict{String, agentTool}
|
tools = loadTools("src/tools") # OrderedDict{String, agentTool}
|
||||||
|
|
||||||
# Manual registration
|
# Manual registration (adds to global _registry)
|
||||||
registerTool(my_tool) # Adds to global _registry
|
registerTool(my_tool)
|
||||||
|
|
||||||
# Query
|
# Query (returns OrderedDict keyed by tool name, in registration order)
|
||||||
all_tools = getTools() # Vector{agentTool} (deep copy)
|
all_tools = getTools() # OrderedDict{String, agentTool} — O(1) lookup + deterministic order
|
||||||
|
|
||||||
# Clear
|
# Clear
|
||||||
clearTools() # Empties _registry
|
clearTools() # Empties _registry
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Why `OrderedDict` for `getTools()`?** The internal `_registry` is a `Vector{agentTool}` for ordered iteration (used by `listTools`). `getTools()` builds an `OrderedDict` from `_registry` so callers get:
|
||||||
|
- O(1) lookup by tool name
|
||||||
|
- Deterministic iteration order (registration order: `listTools` auto-registered first, then tools loaded alphabetically by filename)
|
||||||
|
- Consistency with `agentState.tools` (also `OrderedDict{String, agentTool}`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. The Agent Loop — High-Level Flow
|
## 4. The Agent Loop — High-Level Flow
|
||||||
@@ -1272,6 +1277,8 @@ end
|
|||||||
```julia
|
```julia
|
||||||
# src/tools/myTool.jl
|
# src/tools/myTool.jl
|
||||||
|
|
||||||
|
using Dates # ← tool declares its own dependencies (registry injects only `using ..type`)
|
||||||
|
|
||||||
# Optional: helper functions
|
# Optional: helper functions
|
||||||
function helper_function(...)
|
function helper_function(...)
|
||||||
...
|
...
|
||||||
@@ -1313,18 +1320,44 @@ function getTool()::agentTool
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
Each tool file **declares its own dependencies** via `using` statements at the top of the file. The registry does **not** inject any standard library packages — if a tool needs `Dates`, `JSON`, `HTTP`, `CSV`, or any other package, it must include its own `using` statements.
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# src/tools/getTime.jl
|
||||||
|
using Dates
|
||||||
|
|
||||||
|
function executeTool(...)
|
||||||
|
now() # Dates.now requires `using Dates`
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# src/tools/myApiTool.jl
|
||||||
|
using HTTP, JSON
|
||||||
|
|
||||||
|
function executeTool(...)
|
||||||
|
response = HTTP.get("https://api.example.com")
|
||||||
|
data = JSON.parse(String(response.body))
|
||||||
|
...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Module Isolation
|
### Module Isolation
|
||||||
|
|
||||||
When `loadTools()` loads a file, it wraps it in a dynamically created submodule:
|
When `loadTools()` loads a file, it wraps it in a dynamically created submodule. The registry injects **only** `using ..type` to make core types (`agentTool`, `textContent`, `agentToolResult`, `abortSignal`, etc.) available:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
# User writes in src/tools/myTool.jl:
|
# User writes in src/tools/myTool.jl:
|
||||||
|
using Dates, HTTP, JSON # ← tool's own dependencies
|
||||||
|
|
||||||
function getTool()::agentTool ... end
|
function getTool()::agentTool ... end
|
||||||
|
|
||||||
# loadTools() creates:
|
# loadTools() creates:
|
||||||
module _tool_myTool
|
module _tool_myTool
|
||||||
using ..type
|
using ..type # ← injected by registry (core types only)
|
||||||
using Dates, UUIDs, DataStructures, JSON
|
using Dates, HTTP, JSON # ← from tool file
|
||||||
# (user's code here)
|
# (user's code here)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Dates
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Validate required arguments for the getTime tool.
|
Validate required arguments for the getTime tool.
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -113,13 +113,13 @@ function loadTools(dir::String)::OrderedDict{String, agentTool}
|
|||||||
# and constructing the module AST by hand is fragile.
|
# and constructing the module AST by hand is fragile.
|
||||||
# Instead, we generate the full module source as a string,
|
# Instead, we generate the full module source as a string,
|
||||||
# parse it, and eval the resulting expression.
|
# parse it, and eval the resulting expression.
|
||||||
# Also import Dates, UUIDs, DataStructures, JSON — common dependencies
|
# Each tool file declares its own dependencies via `using` statements
|
||||||
# that tool files use (and that the ..type module transitively uses).
|
# at the top of the file — the registry only injects `using ..type`
|
||||||
|
# to make core types (agentTool, textContent, etc.) available.
|
||||||
file_content = read(filepath, String)
|
file_content = read(filepath, String)
|
||||||
module_code = """
|
module_code = """
|
||||||
module $(mod_name)
|
module $(mod_name)
|
||||||
using ..type
|
using ..type
|
||||||
using Dates, UUIDs, DataStructures, JSON
|
|
||||||
$(file_content)
|
$(file_content)
|
||||||
end
|
end
|
||||||
"""
|
"""
|
||||||
@@ -175,13 +175,19 @@ function registerTool(tool::agentTool)::Vector{agentTool}
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Get all registered tools.
|
Get all registered tools as an `OrderedDict{String, agentTool}` keyed by tool name.
|
||||||
|
|
||||||
|
The internal `_registry` is a `Vector` for ordered iteration (used by `listTools`).
|
||||||
|
This function builds an `OrderedDict` from `_registry` so callers get:
|
||||||
|
- O(1) lookup by name
|
||||||
|
- Deterministic iteration order (registration order: alphabetical by filename)
|
||||||
|
- Consistency with `agentState.tools` (also `OrderedDict{String, agentTool}`)
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
- `Vector{agentTool}`: Copy of the registry
|
- `OrderedDict{String, agentTool}`: Copy of the registry keyed by tool name, in registration order
|
||||||
"""
|
"""
|
||||||
function getTools()::Vector{agentTool}
|
function getTools()::OrderedDict{String, agentTool}
|
||||||
return deepcopy(_registry)
|
return OrderedDict{String, agentTool}(t.name => t for t in _registry)
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using JSON
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Tool that writes new Julia tool module files to disk.
|
Tool that writes new Julia tool module files to disk.
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -102,8 +102,13 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
registry_tools = getTools()
|
registry_tools = getTools()
|
||||||
@test !isempty(registry_tools)
|
@test !isempty(registry_tools)
|
||||||
@test any(t -> t.name == "getTime", registry_tools)
|
@test "getTime" in keys(registry_tools)
|
||||||
@test any(t -> t.name == "getWeather", registry_tools)
|
@test "getWeather" in keys(registry_tools)
|
||||||
|
# listTools is auto-registered via __init__() → first key, then tools loaded alphabetically
|
||||||
|
@test collect(keys(registry_tools))[1] == "listTools"
|
||||||
|
@test collect(keys(registry_tools))[2] == "getTime"
|
||||||
|
@test collect(keys(registry_tools))[3] == "getWeather"
|
||||||
|
@test collect(keys(registry_tools))[4] == "writeTool"
|
||||||
|
|
||||||
clearTools()
|
clearTools()
|
||||||
@test isempty(getTools())
|
@test isempty(getTools())
|
||||||
@@ -121,9 +126,9 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
)
|
)
|
||||||
registerTool(test_tool)
|
registerTool(test_tool)
|
||||||
reg = getTools()
|
reg = getTools()
|
||||||
@test any(t -> t.name == "manualTool", reg)
|
@test haskey(reg, "manualTool")
|
||||||
@test count(t -> t.name == "manualTool", reg) == 1
|
@test length(reg) == 1
|
||||||
@test reg[1].parallelToolExecute == true
|
@test reg["manualTool"].parallelToolExecute == true
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
# 8. getTools returns deep copy (mutations don't affect registry) #
|
# 8. getTools returns deep copy (mutations don't affect registry) #
|
||||||
|
|||||||
Reference in New Issue
Block a user