update
This commit is contained in:
+4
-8
@@ -516,7 +516,7 @@ function prepareToolCall(
|
|||||||
signal::Union{Nothing, abortSignal},
|
signal::Union{Nothing, abortSignal},
|
||||||
)::Union{preparedToolCall,immediateOutcome}
|
)::Union{preparedToolCall,immediateOutcome}
|
||||||
|
|
||||||
tool = find(t -> t.name == toolCall.name, context.tools)
|
tool = get(context.tools, toolCall.name, nothing)
|
||||||
if tool === nothing
|
if tool === nothing
|
||||||
return immediateOutcome(createErrorToolResult("Tool $toolCall.name not found"), true)
|
return immediateOutcome(createErrorToolResult("Tool $toolCall.name not found"), true)
|
||||||
end
|
end
|
||||||
@@ -996,13 +996,9 @@ function executeToolCalls(
|
|||||||
|
|
||||||
hasSequential = false
|
hasSequential = false
|
||||||
for tc in toolCalls
|
for tc in toolCalls
|
||||||
for t in context.tools
|
t = get(context.tools, tc.name, nothing)
|
||||||
if t.name == tc.name && !t.parallelToolExecute
|
if t !== nothing && !t.parallelToolExecute
|
||||||
hasSequential = true
|
hasSequential = true
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if hasSequential
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module toolRegistry
|
|||||||
export loadTools, registerTool, getTools, clearTools
|
export loadTools, registerTool, getTools, clearTools
|
||||||
|
|
||||||
using Dates
|
using Dates
|
||||||
using JSON
|
using JSON, DataStructures
|
||||||
using ..type
|
using ..type
|
||||||
|
|
||||||
# Global registry — populated at runtime by loadTools() or registerTool()
|
# Global registry — populated at runtime by loadTools() or registerTool()
|
||||||
@@ -92,12 +92,12 @@ end
|
|||||||
# Errors
|
# Errors
|
||||||
- Throws `ArgumentError` if a tool file does not define a `getTool` function
|
- Throws `ArgumentError` if a tool file does not define a `getTool` function
|
||||||
"""
|
"""
|
||||||
function loadTools(dir::String)::Vector{agentTool}
|
function loadTools(dir::String)::OrderedDict{String, agentTool}
|
||||||
if !isdir(dir)
|
if !isdir(dir)
|
||||||
throw(ArgumentError("Tool directory does not exist: $dir"))
|
throw(ArgumentError("Tool directory does not exist: $dir"))
|
||||||
end
|
end
|
||||||
|
|
||||||
tools = agentTool[]
|
tools = OrderedDict{String, agentTool}()
|
||||||
jl_files = filter(f -> endswith(f, ".jl") && !occursin(r"(?i)registry", f), readdir(dir))
|
jl_files = filter(f -> endswith(f, ".jl") && !occursin(r"(?i)registry", f), readdir(dir))
|
||||||
sort!(jl_files)
|
sort!(jl_files)
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ function loadTools(dir::String)::Vector{agentTool}
|
|||||||
# functions. Without this, GC could collect the module.
|
# functions. Without this, GC could collect the module.
|
||||||
push!(_tool_modules, mod)
|
push!(_tool_modules, mod)
|
||||||
push!(_registry, tool)
|
push!(_registry, tool)
|
||||||
push!(tools, tool)
|
tools[tool.name] = tool
|
||||||
println("[toolRegistry] Loaded tool: $(tool.name) — $(tool.label)")
|
println("[toolRegistry] Loaded tool: $(tool.name) — $(tool.label)")
|
||||||
catch e
|
catch e
|
||||||
if e isa UndefVarError || occursin("getTool", sprint(showerror, e))
|
if e isa UndefVarError || occursin("getTool", sprint(showerror, e))
|
||||||
|
|||||||
+15
-15
@@ -291,7 +291,7 @@ Snapshot of the agent's conversation context.
|
|||||||
# Arguments
|
# Arguments
|
||||||
- `systemPrompt::String`: System prompt for the agent
|
- `systemPrompt::String`: System prompt for the agent
|
||||||
- `messages::Vector{agentMessage}`: Conversation messages
|
- `messages::Vector{agentMessage}`: Conversation messages
|
||||||
- `tools::Union{Vector{agentTool}, Nothing}`: Available tools
|
- `tools::Union{Dict{String, agentTool}, Nothing}`: Available tools keyed by name for O(1) lookup
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
- A new `agentContext` instance
|
- A new `agentContext` instance
|
||||||
@@ -299,7 +299,7 @@ Snapshot of the agent's conversation context.
|
|||||||
struct agentContext # Snapshot of the agent's conversation context
|
struct agentContext # Snapshot of the agent's conversation context
|
||||||
systemPrompt::String # System prompt for the agent
|
systemPrompt::String # System prompt for the agent
|
||||||
messages::Vector{agentMessage} # Conversation messages
|
messages::Vector{agentMessage} # Conversation messages
|
||||||
tools::Union{Vector{agentTool}, Nothing} # Available tools
|
tools::Union{Dict{String, agentTool}, Nothing} # Available tools keyed by name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -308,9 +308,9 @@ end
|
|||||||
# ------------------------------------------------------------------------------------------------ #
|
# ------------------------------------------------------------------------------------------------ #
|
||||||
|
|
||||||
mutable struct agentState # Mutable runtime state of an agent
|
mutable struct agentState # Mutable runtime state of an agent
|
||||||
systemPrompt::String # System prompt text
|
systemPrompt::String # System prompt for the agent
|
||||||
model::llmModel # LLM model to use
|
model::llmModel # LLM model to use
|
||||||
tools::Vector{agentTool} # Available tools
|
tools::OrderedDict{String, agentTool} # Available tools keyed by name, insertion-ordered
|
||||||
|
|
||||||
# messages history includes userMessage, assistantMessage, toolResultMessage. NO system prompt
|
# messages history includes userMessage, assistantMessage, toolResultMessage. NO system prompt
|
||||||
messages::Vector{agentMessage}
|
messages::Vector{agentMessage}
|
||||||
@@ -329,7 +329,7 @@ new state from external references.
|
|||||||
# Arguments
|
# Arguments
|
||||||
- `systemPrompt::String`: System prompt text
|
- `systemPrompt::String`: System prompt text
|
||||||
- `model::llmModel`: LLM model to use (defaults to an unknown model)
|
- `model::llmModel`: LLM model to use (defaults to an unknown model)
|
||||||
- `tools::Vector{agentTool}`: Available tools (deep copied)
|
- `tools::OrderedDict{String, agentTool}`: Available tools keyed by name (deep copied)
|
||||||
- `messages::Vector{agentMessage}`: Conversation messages (deep copied)
|
- `messages::Vector{agentMessage}`: Conversation messages (deep copied)
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
@@ -338,13 +338,13 @@ new state from external references.
|
|||||||
# Examples
|
# Examples
|
||||||
```julia
|
```julia
|
||||||
julia> state = agentState(systemPrompt="You are a helpful assistant")
|
julia> state = agentState(systemPrompt="You are a helpful assistant")
|
||||||
agentState("You are a helpful assistant", ..., agentTool[], agentMessage[], String[], nothing)
|
agentState("You are a helpful assistant", OrderedDict{String, agentTool}(), agentMessage[], String[], nothing)
|
||||||
```
|
|
||||||
"""
|
"""
|
||||||
function agentState(
|
function agentState(
|
||||||
systemPrompt::String="",
|
systemPrompt::String="",
|
||||||
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[], modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[],
|
||||||
tools::Vector{agentTool}=agentTool[],
|
modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
||||||
|
tools::OrderedDict{String, agentTool}=OrderedDict{String, agentTool}(),
|
||||||
messages::Vector{agentMessage}=agentMessage[],
|
messages::Vector{agentMessage}=agentMessage[],
|
||||||
)
|
)
|
||||||
agentState(
|
agentState(
|
||||||
@@ -395,13 +395,13 @@ end
|
|||||||
Configuration for the agent tool execution loop.
|
Configuration for the agent tool execution loop.
|
||||||
|
|
||||||
# Arguments
|
# Arguments
|
||||||
- `tools::Vector{agentTool}`: Available tools
|
- `tools::OrderedDict{String, agentTool}`: Available tools keyed by name
|
||||||
- `beforeToolCall::Union{Function, Nothing}`: Callback before tool execution
|
- `beforeToolCall::Union{Function, Nothing}`: Callback before tool execution
|
||||||
- `afterToolCall::Union{Function, Nothing}`: Callback after tool execution
|
- `afterToolCall::Union{Function, Nothing}`: Callback after tool execution
|
||||||
- `toolExecution::String`: Execution mode — "sequential" or "parallel"
|
- `toolExecution::String`: Execution mode — "sequential" or "parallel"
|
||||||
"""
|
"""
|
||||||
struct agentLoopConfig
|
struct agentLoopConfig
|
||||||
tools::Vector{agentTool}
|
tools::OrderedDict{String, agentTool}
|
||||||
beforeToolCall::Union{Function, Nothing}
|
beforeToolCall::Union{Function, Nothing}
|
||||||
afterToolCall::Union{Function, Nothing}
|
afterToolCall::Union{Function, Nothing}
|
||||||
toolExecution::String
|
toolExecution::String
|
||||||
@@ -580,7 +580,7 @@ on `inputChannel` and `followUpChannel` channels concurrently.
|
|||||||
# Keyword Arguments
|
# Keyword Arguments
|
||||||
- `systemPrompt::String`: System prompt for the agent
|
- `systemPrompt::String`: System prompt for the agent
|
||||||
- `model`: LLM model to use
|
- `model`: LLM model to use
|
||||||
- `tools::Vector{agentTool}`: Available tools (default: empty)
|
- `tools::OrderedDict{String, agentTool}`: Available tools keyed by name (default: empty)
|
||||||
- `messages::Vector{agentMessage}`: Initial conversation messages (default: empty)
|
- `messages::Vector{agentMessage}`: Initial conversation messages (default: empty)
|
||||||
- `formatMsgForLLM::Function`: Convert agent messages to LLM message format (default: `defaultformatMsgForLLM`)
|
- `formatMsgForLLM::Function`: Convert agent messages to LLM message format (default: `defaultformatMsgForLLM`)
|
||||||
- `llmCall::Function`: Function to invoke the LLM (required)
|
- `llmCall::Function`: Function to invoke the LLM (required)
|
||||||
@@ -599,14 +599,14 @@ on `inputChannel` and `followUpChannel` channels concurrently.
|
|||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
```julia
|
```julia
|
||||||
julia> agent = yiemAgent(systemPrompt="You are a helpful assistant", model=my_model)
|
julia> tools = loadTools("src/tools")
|
||||||
|
julia> agent = yiemAgent(systemPrompt="You are a helpful assistant", model=my_model, tools=tools, llmCall=...)
|
||||||
yiemAgent(agentState(...), Channel(...), Channel(...), Channel(...), ..., ...)
|
yiemAgent(agentState(...), Channel(...), Channel(...), Channel(...), ..., ...)
|
||||||
```
|
|
||||||
"""
|
"""
|
||||||
function yiemAgent(
|
function yiemAgent(
|
||||||
; systemPrompt::String="You are helpful assistant.",
|
; systemPrompt::String="You are helpful assistant.",
|
||||||
model=nothing,
|
model=nothing,
|
||||||
tools::Vector{agentTool}=agentTool[],
|
tools::OrderedDict{String, agentTool}=OrderedDict{String, agentTool}(),
|
||||||
messages::Vector{agentMessage}=agentMessage[],
|
messages::Vector{agentMessage}=agentMessage[],
|
||||||
prepareContext::Union{Function, Nothing}=nothing,
|
prepareContext::Union{Function, Nothing}=nothing,
|
||||||
formatMsgForLLM::Function=defaultformatMsgForLLM,
|
formatMsgForLLM::Function=defaultformatMsgForLLM,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
@test !isempty(loaded)
|
@test !isempty(loaded)
|
||||||
@test length(loaded) == 3
|
@test length(loaded) == 3
|
||||||
|
|
||||||
names = [t.name for t in loaded]
|
names = [k for k in keys(loaded)]
|
||||||
@test "getTime" in names
|
@test "getTime" in names
|
||||||
@test "getWeather" in names
|
@test "getWeather" in names
|
||||||
@test "writeTool" in names
|
@test "writeTool" in names
|
||||||
@@ -39,15 +39,15 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
# (getTime.jl < getWeather.jl < writeTool.jl) #
|
# (getTime.jl < getWeather.jl < writeTool.jl) #
|
||||||
# because 'T' < 'W' in ASCII #
|
# because 'T' < 'W' in ASCII #
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
@test loaded[1].name == "getTime"
|
@test collect(keys(loaded))[1] == "getTime"
|
||||||
@test loaded[2].name == "getWeather"
|
@test collect(keys(loaded))[2] == "getWeather"
|
||||||
@test loaded[3].name == "writeTool"
|
@test collect(keys(loaded))[3] == "writeTool"
|
||||||
|
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
# 5. Verify loaded tool fields are correct #
|
# 5. Verify loaded tool fields are correct #
|
||||||
# ------------------------------------------------------------------ #
|
# ------------------------------------------------------------------ #
|
||||||
# getTime
|
# getTime
|
||||||
time_tool = loaded[1]
|
time_tool = loaded["getTime"]
|
||||||
@test time_tool.name == "getTime"
|
@test time_tool.name == "getTime"
|
||||||
@test time_tool.label == "Time Lookup"
|
@test time_tool.label == "Time Lookup"
|
||||||
@test time_tool.validateRequiredArgs !== nothing
|
@test time_tool.validateRequiredArgs !== nothing
|
||||||
@@ -55,7 +55,7 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
@test time_tool.inputSchema["required"] == Any[]
|
@test time_tool.inputSchema["required"] == Any[]
|
||||||
|
|
||||||
# getWeather
|
# getWeather
|
||||||
weather = loaded[2]
|
weather = loaded["getWeather"]
|
||||||
@test weather.name == "getWeather"
|
@test weather.name == "getWeather"
|
||||||
@test weather.label == "Weather Lookup"
|
@test weather.label == "Weather Lookup"
|
||||||
@test weather.execute !== nothing
|
@test weather.execute !== nothing
|
||||||
@@ -63,7 +63,7 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
@test weather.inputSchema["required"] == ["city"]
|
@test weather.inputSchema["required"] == ["city"]
|
||||||
|
|
||||||
# writeTool
|
# writeTool
|
||||||
wt = loaded[3]
|
wt = loaded["writeTool"]
|
||||||
@test wt.name == "writeTool"
|
@test wt.name == "writeTool"
|
||||||
@test wt.label == "Create Tool"
|
@test wt.label == "Create Tool"
|
||||||
@test wt.execute !== nothing
|
@test wt.execute !== nothing
|
||||||
@@ -123,7 +123,7 @@ TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools")
|
|||||||
reg = getTools()
|
reg = getTools()
|
||||||
@test any(t -> t.name == "manualTool", reg)
|
@test any(t -> t.name == "manualTool", reg)
|
||||||
@test count(t -> t.name == "manualTool", reg) == 1
|
@test count(t -> t.name == "manualTool", reg) == 1
|
||||||
@test reg[1].parallelToolExecute == true
|
@test reg[1].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