Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48d89033cc | |||
| befbf62e3b | |||
| 4e476bd39d | |||
| 66ced320f0 | |||
| 1817630795 | |||
| dbac0db9b9 | |||
| ce46888663 | |||
| 2a2d44ab0c | |||
| 0620983b48 | |||
| af81503f8b | |||
| 4624ad99ab | |||
| 2fd363d9ef |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
name = "YiemAgent"
|
name = "YiemAgent"
|
||||||
uuid = "e012c34b-7f78-48e0-971c-7abb83b6f0a2"
|
uuid = "e012c34b-7f78-48e0-971c-7abb83b6f0a2"
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
authors = ["narawat lamaiin <narawat@outlook.com>"]
|
authors = ["narawat lamaiin <narawat@outlook.com>"]
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|||||||
+58
-88
@@ -170,7 +170,7 @@ function yiemAgent(
|
|||||||
|
|
||||||
tools = OrderedDict{String, agentTool}()
|
tools = OrderedDict{String, agentTool}()
|
||||||
registerAllTools(tools, mcpServer; eventSink=eventSink, sessionId=sessionId)
|
registerAllTools(tools, mcpServer; eventSink=eventSink, sessionId=sessionId)
|
||||||
|
eventSink("instantiate an agent")
|
||||||
# Create struct with a placeholder task, then spawn and replace it
|
# Create struct with a placeholder task, then spawn and replace it
|
||||||
agent = yiemAgent(
|
agent = yiemAgent(
|
||||||
agentState(systemPrompt, model, tools, messages),
|
agentState(systemPrompt, model, tools, messages),
|
||||||
@@ -194,7 +194,7 @@ function yiemAgent(
|
|||||||
|
|
||||||
# Spawn the background loop and attach it
|
# Spawn the background loop and attach it
|
||||||
agent._agentLoop = @spawn _agentLoop(agent)
|
agent._agentLoop = @spawn _agentLoop(agent)
|
||||||
|
eventSink("agent instantiated")
|
||||||
return agent
|
return agent
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -260,42 +260,36 @@ function _agentLoop(agent::yiemAgent)
|
|||||||
agent.inputChannel -> nothing
|
agent.inputChannel -> nothing
|
||||||
agent.followUpChannel -> nothing
|
agent.followUpChannel -> nothing
|
||||||
"""
|
"""
|
||||||
|
agent.eventSink("_agentLoop starting")
|
||||||
while true
|
while true
|
||||||
while newUserMsg === nothing
|
while newUserMsg === nothing
|
||||||
if isready(agent.inputChannel)
|
if isready(agent.inputChannel)
|
||||||
agent.eventSink("_agentLoop 1 agent._state.messages length $(length(agent._state.messages))"; publish=true)
|
|
||||||
# agent process new user msg immediately after the current tool call finished.
|
# agent process new user msg immediately after the current tool call finished.
|
||||||
newUserMsg = take!(agent.inputChannel)
|
newUserMsg = take!(agent.inputChannel)
|
||||||
# agent.eventSink("new user msg\n$newUserMsg"; publish=true)
|
agent.eventSink("take new user message")
|
||||||
else
|
else
|
||||||
# check followUp message after _processMessage() is done
|
# check followUp message after _processMessage() is done
|
||||||
if typeof(processingTask) == Task && istaskdone(processingTask) == true
|
if typeof(processingTask) == Task && istaskdone(processingTask) == true
|
||||||
agent.eventSink("_agentLoop 2 agent._state.messages length $(length(agent._state.messages))"; publish=true)
|
agent.eventSink("_processMessage() done")
|
||||||
# if agent runs is done but followUpChannel has messages,
|
# if agent runs is done but followUpChannel has messages,
|
||||||
# put new message in inputChannel instead
|
# put new message in inputChannel instead
|
||||||
if isready(agent.followUpChannel)
|
if isready(agent.followUpChannel)
|
||||||
agent.eventSink("_agentLoop 3 agent._state.messages length $(length(agent._state.messages))"; publish=true)
|
|
||||||
while isready(agent.followUpChannel)
|
while isready(agent.followUpChannel)
|
||||||
|
agent.eventSink("put followUp message to input channel")
|
||||||
followUpMsg = take!(agent.followUpChannel)
|
followUpMsg = take!(agent.followUpChannel)
|
||||||
put!(agent.inputChannel, followUpMsg)
|
put!(agent.inputChannel, followUpMsg)
|
||||||
end
|
end
|
||||||
processingTask = nothing # reset
|
processingTask = nothing # reset
|
||||||
result = nothing # reset
|
result = nothing # reset
|
||||||
else # _processMessage() done and no followUp message.
|
else # _processMessage() done and no followUp message.
|
||||||
agent.eventSink("_agentLoop 4 agent._state.messages length $(length(agent._state.messages))"; publish=true)
|
agent.eventSink("no followUp message")
|
||||||
result = deepcopy(agent._state.messages[end])
|
result = deepcopy(agent._state.messages[end])
|
||||||
agent.eventSink("_agentLoop 4-1 "; publish=true)
|
|
||||||
# filter out reasoningContent in-place
|
# filter out reasoningContent in-place
|
||||||
filter!(c -> !(c isa reasoningContent), result.content)
|
filter!(c -> !(c isa reasoningContent), result.content)
|
||||||
agent.eventSink("_agentLoop 5 "; publish=true)
|
|
||||||
# format output
|
# format output
|
||||||
respondToUI = _assistantMessageToOpenAI(result)
|
respondToUI = _assistantMessageToOpenAI(result)
|
||||||
agent.eventSink("_agentLoop 6 "; publish=true)
|
|
||||||
put!(agent.outputChannel, respondToUI)
|
put!(agent.outputChannel, respondToUI)
|
||||||
if !isempty(result.content) && result.content[1] isa textContent
|
agent.eventSink("put result to output channel")
|
||||||
agent.eventSink(result.content[1].text; publish=true)
|
|
||||||
end
|
|
||||||
processingTask = nothing # reset
|
processingTask = nothing # reset
|
||||||
result = nothing # reset
|
result = nothing # reset
|
||||||
end
|
end
|
||||||
@@ -325,7 +319,7 @@ function _agentLoop(agent::yiemAgent)
|
|||||||
else
|
else
|
||||||
# spawn new _processMessage() if it is not already running.
|
# spawn new _processMessage() if it is not already running.
|
||||||
if processingTask === nothing
|
if processingTask === nothing
|
||||||
agent.eventSink("_agentLoop 5 agent._state.messages length $(length(agent._state.messages))"; publish=true)
|
agent.eventSink("spawn new _processMessage()")
|
||||||
# discard all messages in followUpChannel
|
# discard all messages in followUpChannel
|
||||||
while isready(agent.followUpChannel)
|
while isready(agent.followUpChannel)
|
||||||
_ = take!(agent.followUpChannel)
|
_ = take!(agent.followUpChannel)
|
||||||
@@ -347,8 +341,10 @@ function _agentLoop(agent::yiemAgent)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
put!(processMessageInputCh, newUserMsg)
|
put!(processMessageInputCh, newUserMsg)
|
||||||
|
agent.eventSink("put new user msg to processMessageInputCh")
|
||||||
newUserMsg = nothing # reset
|
newUserMsg = nothing # reset
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
catch e
|
catch e
|
||||||
bt = catch_backtrace()
|
bt = catch_backtrace()
|
||||||
@@ -356,7 +352,7 @@ function _agentLoop(agent::yiemAgent)
|
|||||||
showerror(io, e, bt)
|
showerror(io, e, bt)
|
||||||
println(io)
|
println(io)
|
||||||
end
|
end
|
||||||
eventSink(errMsg; publish=true)
|
agent.eventSink(errMsg; publish=true)
|
||||||
|
|
||||||
# On any error, send error response and exit the loop
|
# On any error, send error response and exit the loop
|
||||||
@error "Agent loop failed" error=e
|
@error "Agent loop failed" error=e
|
||||||
@@ -404,10 +400,11 @@ function _processMessage(
|
|||||||
afterToolCall::Union{Function, Nothing},
|
afterToolCall::Union{Function, Nothing},
|
||||||
parallelToolExecute::Bool,
|
parallelToolExecute::Bool,
|
||||||
)::Nothing
|
)::Nothing
|
||||||
eventSink("_processMessage 1 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
# loop until llmCall() response didn't use tool calls
|
# loop until llmCall() response didn't use tool calls
|
||||||
final_response = nothing
|
final_response = nothing
|
||||||
|
|
||||||
|
eventSink("▶ _processMessage: entered")
|
||||||
|
|
||||||
""" example message in inputChannel
|
""" example message in inputChannel
|
||||||
Dict(
|
Dict(
|
||||||
"role" => "user",
|
"role" => "user",
|
||||||
@@ -425,48 +422,39 @@ function _processMessage(
|
|||||||
try
|
try
|
||||||
# Drain inputChannel and convert OpenAI-format messages to userMessage type
|
# Drain inputChannel and convert OpenAI-format messages to userMessage type
|
||||||
while isready(inputChannel)
|
while isready(inputChannel)
|
||||||
eventSink("_processMessage 2 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
newUserMsg_openai = take!(inputChannel)
|
newUserMsg_openai = take!(inputChannel)
|
||||||
eventSink("_processMessage 3 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
if newUserMsg_openai === :shutdown
|
if newUserMsg_openai === :shutdown
|
||||||
eventSink("_processMessage 4 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
# Re-emit shutdown signal for the loop to handle
|
# Re-emit shutdown signal for the loop to handle
|
||||||
put!(inputChannel, :shutdown)
|
put!(inputChannel, :shutdown)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
eventSink("_processMessage 5 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
newUserMsg = OpenAiToUserMessage(newUserMsg_openai)
|
newUserMsg = OpenAiToUserMessage(newUserMsg_openai)
|
||||||
push!(agentMsgHistory, newUserMsg)
|
push!(agentMsgHistory, newUserMsg)
|
||||||
eventSink("_processMessage 6 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
eventSink("▶ _processMessage: user msg pushed")
|
||||||
end
|
end
|
||||||
eventSink("_processMessage 7 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
# call prepareContext()
|
# call prepareContext()
|
||||||
state = agentState(systemPrompt, nothing, tools, agentMsgHistory)
|
state = agentState(systemPrompt, nothing, tools, agentMsgHistory)
|
||||||
eventSink("_processMessage 8 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
preparedContext = prepareContext(state, eventSink)
|
preparedContext = prepareContext(state, eventSink)
|
||||||
eventSink("_processMessage 9 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
# Call formatMessagesForLLM() to format for LLM
|
# Call formatMessagesForLLM() to format for LLM
|
||||||
formattedMessages = formatMessagesForLLM(preparedContext, eventSink)
|
formattedMessages = formatMessagesForLLM(preparedContext, eventSink)
|
||||||
|
|
||||||
# eventSink("_processMessage 10 formattedMessages $formattedMessages"; publish=true)
|
eventSink("▶ _processMessage: context")
|
||||||
|
eventSink("Thinking...")
|
||||||
""" response example
|
""" response example
|
||||||
response = JSON.Object{String, Any}("finish_reason" => "tool_calls", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "", "reasoning_content" => "Here's a thinking process:\n\n1. **Identify User Request**: The user is asking for the weather in Bangkok.\n2. **Locate Relevant Tool**: I have a `getWeather` function available.\n3. **Check Function Parameters**:\n - `city` (required): City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\n - `units` (optional, default \"celsius\"): Temperature scale (\"celsius\" or \"fahrenheit\")\n4. **Prepare Parameters**:\n - `city`: \"Bangkok, Thailand\" (adding country for clarity, though just \"Bangkok\" might work, following the example format is safer)\n - `units`: Not specified, so I'll use the default (\"celsius\")\n5. **Execute Tool Call**: Call `getWeather` with `city: \"Bangkok, Thailand\"`\n6. **Anticipate Response**: The function will return current weather and forecast data for Bangkok. I'll then format it nicely for the user.\n - *Self-Correction/Verification during thought*: The prompt says \"city: City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\". I'll use \"Bangkok, Thailand\". The `units` parameter is optional, so I'll omit it to use the default.\n - Proceed with tool call.✅\n", "tool_calls" => Any[JSON.Object{String, Any}("type" => "function", "function" => JSON.Object{String, Any}("name" => "getWeather", "arguments" => "{\"city\":\"Bangkok, Thailand\"}"), "id" => "6fOilR5QPcdppbAAHluhkRyUDu3oWMAL")]))
|
response = JSON.Object{String, Any}("finish_reason" => "tool_calls", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "", "reasoning_content" => "Here's a thinking process:\n\n1. **Identify User Request**: The user is asking for the weather in Bangkok.\n2. **Locate Relevant Tool**: I have a `getWeather` function available.\n3. **Check Function Parameters**:\n - `city` (required): City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\n - `units` (optional, default \"celsius\"): Temperature scale (\"celsius\" or \"fahrenheit\")\n4. **Prepare Parameters**:\n - `city`: \"Bangkok, Thailand\" (adding country for clarity, though just \"Bangkok\" might work, following the example format is safer)\n - `units`: Not specified, so I'll use the default (\"celsius\")\n5. **Execute Tool Call**: Call `getWeather` with `city: \"Bangkok, Thailand\"`\n6. **Anticipate Response**: The function will return current weather and forecast data for Bangkok. I'll then format it nicely for the user.\n - *Self-Correction/Verification during thought*: The prompt says \"city: City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\". I'll use \"Bangkok, Thailand\". The `units` parameter is optional, so I'll omit it to use the default.\n - Proceed with tool call.✅\n", "tool_calls" => Any[JSON.Object{String, Any}("type" => "function", "function" => JSON.Object{String, Any}("name" => "getWeather", "arguments" => "{\"city\":\"Bangkok, Thailand\"}"), "id" => "6fOilR5QPcdppbAAHluhkRyUDu3oWMAL")]))
|
||||||
"""
|
"""
|
||||||
response = llmCall(formattedMessages, eventSink=eventSink)
|
response = llmCall(formattedMessages, eventSink=eventSink)
|
||||||
# eventSink("LLM response " * string(response); publish=true)
|
|
||||||
|
|
||||||
eventSink("_processMessage 11 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
# Extract tool calls from LLM response content blocks
|
# Extract tool calls from LLM response content blocks
|
||||||
hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response)
|
hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response, eventSink)
|
||||||
eventSink("hasToolCalls: $hasToolCalls\ntoolCallList: $toolCallList _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
eventSink("assistant_msg " * string(assistant_msg); publish=true)
|
|
||||||
eventSink("_processMessage 11-1"; publish=true)
|
|
||||||
|
|
||||||
# Add assistant message (tool calls or text) to history for next LLM turn
|
# Add assistant message (tool calls or text) to history for next LLM turn
|
||||||
push!(agentMsgHistory, assistant_msg)
|
push!(agentMsgHistory, assistant_msg)
|
||||||
|
eventSink("▶ _processMessage: LLM response")
|
||||||
|
|
||||||
if hasToolCalls && length(toolCallList) > 0
|
if hasToolCalls && length(toolCallList) > 0
|
||||||
|
eventSink("▶ _processMessage: tool calls")
|
||||||
|
|
||||||
# Build context and config for executeToolCalls
|
# Build context and config for executeToolCalls
|
||||||
|
|
||||||
config = agentLoopConfig(
|
config = agentLoopConfig(
|
||||||
@@ -476,21 +464,16 @@ function _processMessage(
|
|||||||
)
|
)
|
||||||
|
|
||||||
signal = abortSignal(false)
|
signal = abortSignal(false)
|
||||||
eventSink("_processMessage 12 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
|
|
||||||
# call executeToolCalls()
|
# call executeToolCalls()
|
||||||
toolResultBatch = executeToolCalls(preparedContext, assistant_msg, toolCallList, config,
|
toolResultBatch = executeToolCalls(preparedContext, assistant_msg, toolCallList, config,
|
||||||
signal, eventSink)
|
signal, eventSink)
|
||||||
eventSink("_processMessage 13 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
eventSink("▶ _processMessage: tool batch")
|
||||||
|
|
||||||
# save toolResults to messages
|
# save toolResults to messages
|
||||||
for toolResult in toolResultBatch.messages
|
for toolResult in toolResultBatch.messages
|
||||||
# eventSink("toolResult " * string(toolResult); publish=true)
|
|
||||||
push!(agentMsgHistory, toolResult)
|
push!(agentMsgHistory, toolResult)
|
||||||
end
|
end
|
||||||
eventSink("_processMessage 14 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
if toolResultBatch.terminate
|
if toolResultBatch.terminate
|
||||||
eventSink("_processMessage 15 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
eventSink("▶ _processMessage: TERMINATE branch")
|
||||||
# If toolResultBatch requested termination, build a final response
|
# If toolResultBatch requested termination, build a final response
|
||||||
final_content = [textContent("Tool execution completed.")]
|
final_content = [textContent("Tool execution completed.")]
|
||||||
for toolResult in toolResultBatch.messages
|
for toolResult in toolResultBatch.messages
|
||||||
@@ -504,7 +487,6 @@ function _processMessage(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("_processMessage 16 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
final_response = assistantMessage(
|
final_response = assistantMessage(
|
||||||
role="assistant",
|
role="assistant",
|
||||||
content=final_content,
|
content=final_content,
|
||||||
@@ -523,7 +505,7 @@ function _processMessage(
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
eventSink("_processMessage 17 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
eventSink("▶ _processMessage: FINAL response")
|
||||||
# LLM did not use tool calls —
|
# LLM did not use tool calls —
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -535,11 +517,11 @@ function _processMessage(
|
|||||||
end
|
end
|
||||||
|
|
||||||
eventSink(err_msg; publish=true)
|
eventSink(err_msg; publish=true)
|
||||||
|
eventSink("▶ _processMessage: CAUGHT ERROR")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
eventSink("▶ _processMessage: exited")
|
||||||
eventSink("_processMessage 18 _state.messages length $(length(agentMsgHistory))"; publish=true)
|
|
||||||
return nothing
|
return nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -649,9 +631,12 @@ The message is constructed from:
|
|||||||
1) llm_useTool = JSON.Object{String, Any}("finish_reason" => "tool_calls", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "", "reasoning_content" => "Here's a thinking process:\n\n1. **Identify User Request**: The user is asking for the weather in Bangkok.\n2. **Locate Relevant Tool**: I have a `getWeather` function available.\n3. **Check Function Parameters**:\n - `city` (required): City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\n - `units` (optional, default \"celsius\"): Temperature scale (\"celsius\" or \"fahrenheit\")\n4. **Prepare Parameters**:\n - `city`: \"Bangkok, Thailand\" (adding country for clarity, though just \"Bangkok\" might work, following the example format is safer)\n - `units`: Not specified, so I'll use the default (\"celsius\")\n5. **Execute Tool Call**: Call `getWeather` with `city: \"Bangkok, Thailand\"`\n6. **Anticipate Response**: The function will return current weather and forecast data for Bangkok. I'll then format it nicely for the user.\n - *Self-Correction/Verification during thought*: The prompt says \"city: City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\". I'll use \"Bangkok, Thailand\". The `units` parameter is optional, so I'll omit it to use the default.\n - Proceed with tool call.✅\n", "tool_calls" => Any[JSON.Object{String, Any}("type" => "function", "function" => JSON.Object{String, Any}("name" => "getWeather", "arguments" => "{\"city\":\"Bangkok, Thailand\"}"), "id" => "6fOilR5QPcdppbAAHluhkRyUDu3oWMAL")]))
|
1) llm_useTool = JSON.Object{String, Any}("finish_reason" => "tool_calls", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "", "reasoning_content" => "Here's a thinking process:\n\n1. **Identify User Request**: The user is asking for the weather in Bangkok.\n2. **Locate Relevant Tool**: I have a `getWeather` function available.\n3. **Check Function Parameters**:\n - `city` (required): City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\n - `units` (optional, default \"celsius\"): Temperature scale (\"celsius\" or \"fahrenheit\")\n4. **Prepare Parameters**:\n - `city`: \"Bangkok, Thailand\" (adding country for clarity, though just \"Bangkok\" might work, following the example format is safer)\n - `units`: Not specified, so I'll use the default (\"celsius\")\n5. **Execute Tool Call**: Call `getWeather` with `city: \"Bangkok, Thailand\"`\n6. **Anticipate Response**: The function will return current weather and forecast data for Bangkok. I'll then format it nicely for the user.\n - *Self-Correction/Verification during thought*: The prompt says \"city: City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'\". I'll use \"Bangkok, Thailand\". The `units` parameter is optional, so I'll omit it to use the default.\n - Proceed with tool call.✅\n", "tool_calls" => Any[JSON.Object{String, Any}("type" => "function", "function" => JSON.Object{String, Any}("name" => "getWeather", "arguments" => "{\"city\":\"Bangkok, Thailand\"}"), "id" => "6fOilR5QPcdppbAAHluhkRyUDu3oWMAL")]))
|
||||||
2) llm_notUseTool = JSON.Object{String, Any}("finish_reason" => "stop", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "The weather in London, UK is currently Sunny with a temperature of 22°C.", "reasoning_content" => "The user asked for the weather in London.\nI called the `getWeather` tool for London, UK.\nThe response indicates it's Sunny and 22°C.\nI will convey this information to the user.\n"))
|
2) llm_notUseTool = JSON.Object{String, Any}("finish_reason" => "stop", "index" => 0, "message" => JSON.Object{String, Any}("role" => "assistant", "content" => "The weather in London, UK is currently Sunny with a temperature of 22°C.", "reasoning_content" => "The user asked for the weather in London.\nI called the `getWeather` tool for London, UK.\nThe response indicates it's Sunny and 22°C.\nI will convey this information to the user.\n"))
|
||||||
"""
|
"""
|
||||||
function _extractToolCalls(response)
|
function _extractToolCalls(response, eventSink=nothing)
|
||||||
hasToolCalls = false
|
hasToolCalls = false
|
||||||
toolCallList = agentToolCall[]
|
toolCallList = agentToolCall[]
|
||||||
|
if eventSink !== nothing
|
||||||
|
eventSink("▶ _extractToolCalls: parsing response")
|
||||||
|
end
|
||||||
|
|
||||||
# Helper: parse args (JSON string -> Dict, or pass through)
|
# Helper: parse args (JSON string -> Dict, or pass through)
|
||||||
parse_args(raw) = raw isa AbstractDict && !(raw isa Dict{String,Any}) ?
|
parse_args(raw) = raw isa AbstractDict && !(raw isa Dict{String,Any}) ?
|
||||||
@@ -979,47 +964,40 @@ function prepareToolCall(
|
|||||||
signal::abortSignal,
|
signal::abortSignal,
|
||||||
eventSink
|
eventSink
|
||||||
)::Union{preparedToolCall,immediateOutcome}
|
)::Union{preparedToolCall,immediateOutcome}
|
||||||
eventSink("prepareToolCall 1"; publish=true)
|
|
||||||
|
|
||||||
# pick a called tool from tool store
|
# pick a called tool from tool store
|
||||||
|
eventSink("▶ prepareToolCall: resolving")
|
||||||
tool = get(context.tools, toolCall.name, nothing)
|
tool = get(context.tools, toolCall.name, nothing)
|
||||||
|
|
||||||
if tool === nothing
|
if tool === nothing
|
||||||
eventSink("prepareToolCall 2"; publish=true)
|
eventSink("▶ prepareToolCall: NOT FOUND")
|
||||||
return immediateOutcome(createErrorToolResult("Tool $toolCall.name not found"), true)
|
return immediateOutcome(createErrorToolResult("Tool $toolCall.name not found"), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
try
|
try
|
||||||
eventSink("prepareToolCall 3"; publish=true)
|
|
||||||
# 1. prepare arguments (tool-specific transform)
|
# 1. prepare arguments (tool-specific transform)
|
||||||
prepared = prepareToolCallArguments(tool, toolCall)
|
prepared = prepareToolCallArguments(tool, toolCall)
|
||||||
eventSink("prepared " * string(prepared.arguments); publish=true)
|
|
||||||
eventSink("prepareToolCall 4"; publish=true)
|
|
||||||
validatedArgs = validateToolArguments(tool, prepared)
|
validatedArgs = validateToolArguments(tool, prepared)
|
||||||
eventSink("validatedArgs " * string(validatedArgs); publish=true)
|
eventSink("▶ prepareToolCall: validated")
|
||||||
eventSink("prepareToolCall 5"; publish=true)
|
|
||||||
# 2. beforeToolCall hook — can block
|
# 2. beforeToolCall hook — can block
|
||||||
if config.beforeToolCall !== nothing
|
if config.beforeToolCall !== nothing
|
||||||
eventSink("prepareToolCall 6"; publish=true)
|
|
||||||
|
|
||||||
before = config.beforeToolCall(
|
before = config.beforeToolCall(
|
||||||
beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context),
|
beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context),
|
||||||
signal,
|
signal,
|
||||||
eventSink
|
eventSink
|
||||||
)
|
)
|
||||||
eventSink("prepareToolCall 7"; publish=true)
|
|
||||||
if signal.aborted
|
if signal.aborted
|
||||||
eventSink("prepareToolCall 8"; publish=true)
|
|
||||||
return immediateOutcome(createErrorToolResult("Operation aborted"), true)
|
return immediateOutcome(createErrorToolResult("Operation aborted"), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if before !== nothing && before.block
|
if before !== nothing && before.block
|
||||||
eventSink("prepareToolCall 9"; publish=true)
|
|
||||||
return immediateOutcome(
|
return immediateOutcome(
|
||||||
createErrorToolResult(get(before, :reason, "Tool execution was blocked")), true)
|
createErrorToolResult(get(before, :reason, "Tool execution was blocked")), true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("prepareToolCall 10"; publish=true)
|
|
||||||
return preparedToolCall(tool, toolCall, validatedArgs)
|
return preparedToolCall(tool, toolCall, validatedArgs)
|
||||||
catch e
|
catch e
|
||||||
bt = catch_backtrace()
|
bt = catch_backtrace()
|
||||||
@@ -1029,7 +1007,6 @@ function prepareToolCall(
|
|||||||
end
|
end
|
||||||
|
|
||||||
eventSink(errMsg; publish=true)
|
eventSink(errMsg; publish=true)
|
||||||
|
|
||||||
return immediateOutcome(createErrorToolResult(sprint(showerror, e)), true)
|
return immediateOutcome(createErrorToolResult(sprint(showerror, e)), true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1080,11 +1057,11 @@ function executePreparedToolCall(
|
|||||||
signal::Union{Nothing,abortSignal},
|
signal::Union{Nothing,abortSignal},
|
||||||
eventSink,
|
eventSink,
|
||||||
)::executedOutcome
|
)::executedOutcome
|
||||||
eventSink("executePreparedToolCall 1"; publish=true)
|
|
||||||
|
|
||||||
|
eventSink("▶ execute: START")
|
||||||
try
|
try
|
||||||
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, eventSink)
|
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, eventSink)
|
||||||
# eventSink("tool execute raw result: " * sprint(show, result); publish=true)
|
eventSink("▶ execute: END")
|
||||||
return executedOutcome(result, false)
|
return executedOutcome(result, false)
|
||||||
catch e
|
catch e
|
||||||
bt = catch_backtrace()
|
bt = catch_backtrace()
|
||||||
@@ -1093,6 +1070,7 @@ function executePreparedToolCall(
|
|||||||
println(io)
|
println(io)
|
||||||
end
|
end
|
||||||
eventSink(errMsg; publish=true)
|
eventSink(errMsg; publish=true)
|
||||||
|
eventSink("▶ execute: ERROR")
|
||||||
|
|
||||||
return executedOutcome(createErrorToolResult(sprint(showerror, e)), true)
|
return executedOutcome(createErrorToolResult(sprint(showerror, e)), true)
|
||||||
end
|
end
|
||||||
@@ -1165,10 +1143,10 @@ function finalizeExecutedToolCall(
|
|||||||
signal::Union{Nothing,abortSignal},
|
signal::Union{Nothing,abortSignal},
|
||||||
eventSink
|
eventSink
|
||||||
)::finalizedOutcome
|
)::finalizedOutcome
|
||||||
eventSink("finalizeExecutedToolCall 1"; publish=true)
|
|
||||||
|
eventSink("▶ finalize: START")
|
||||||
result = executed.result
|
result = executed.result
|
||||||
isError = executed.isError
|
isError = executed.isError
|
||||||
# eventSink("finalizeExecutedToolCall result before afterToolCall\n$result"; publish=true)
|
|
||||||
if config.afterToolCall !== nothing
|
if config.afterToolCall !== nothing
|
||||||
try
|
try
|
||||||
after = config.afterToolCall(
|
after = config.afterToolCall(
|
||||||
@@ -1176,7 +1154,6 @@ function finalizeExecutedToolCall(
|
|||||||
signal,
|
signal,
|
||||||
eventSink
|
eventSink
|
||||||
)
|
)
|
||||||
eventSink("finalizeExecutedToolCall 3"; publish=true)
|
|
||||||
if after !== nothing
|
if after !== nothing
|
||||||
result = after
|
result = after
|
||||||
isError = false
|
isError = false
|
||||||
@@ -1193,7 +1170,7 @@ function finalizeExecutedToolCall(
|
|||||||
isError = true
|
isError = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("finalizeExecutedToolCall 4"; publish=true)
|
eventSink("▶ finalize: END")
|
||||||
return finalizedOutcome(prep.toolCall, result, isError)
|
return finalizedOutcome(prep.toolCall, result, isError)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1259,39 +1236,27 @@ function executeToolCallsSequential(
|
|||||||
signal::abortSignal,
|
signal::abortSignal,
|
||||||
eventSink,
|
eventSink,
|
||||||
)::agentToolCallBatch
|
)::agentToolCallBatch
|
||||||
eventSink("executeToolCallsSequential 1"; publish=true)
|
|
||||||
finalizedCalls = finalizedOutcome[]
|
finalizedCalls = finalizedOutcome[]
|
||||||
messages = toolResultMessage[]
|
messages = toolResultMessage[]
|
||||||
|
eventSink("▶ executeToolCallsSequential: batch start")
|
||||||
|
|
||||||
for tc in toolCalls
|
for tc in toolCalls
|
||||||
eventSink("start tool execute $(tc.id), $(tc.name), $(tc.arguments)"; publish=true)
|
|
||||||
prep = prepareToolCall(context, assistantMsg, tc, config, signal, eventSink)
|
prep = prepareToolCall(context, assistantMsg, tc, config, signal, eventSink)
|
||||||
eventSink("executeToolCallsSequential " * string(prep.args); publish=true)
|
|
||||||
|
|
||||||
if prep isa immediateOutcome
|
if prep isa immediateOutcome
|
||||||
eventSink("executeToolCallsSequential 2-1"; publish=true)
|
|
||||||
finalized = finalizedOutcome(tc, prep.result, prep.isError)
|
finalized = finalizedOutcome(tc, prep.result, prep.isError)
|
||||||
eventSink("executeToolCallsSequential 2-2"; publish=true)
|
|
||||||
else
|
else
|
||||||
eventSink("executeToolCallsSequential 3"; publish=true)
|
|
||||||
executed = executePreparedToolCall(prep, signal, eventSink)
|
executed = executePreparedToolCall(prep, signal, eventSink)
|
||||||
|
|
||||||
eventSink("executeToolCallsSequential 3-1"; publish=true)
|
|
||||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config,
|
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config,
|
||||||
signal, eventSink)
|
signal, eventSink)
|
||||||
eventSink("executeToolCallsSequential 3-2"; publish=true)
|
|
||||||
end
|
end
|
||||||
eventSink("executeToolCallsSequential 4"; publish=true)
|
|
||||||
# eventSink("finalized $(finalized.toolCall.id), $(finalized.toolCall.name),
|
|
||||||
# $(finalized.result), $(finalized.isError)"; publish=true)
|
|
||||||
push!(messages, createToolResultMessage(finalized))
|
push!(messages, createToolResultMessage(finalized))
|
||||||
push!(finalizedCalls, finalized)
|
push!(finalizedCalls, finalized)
|
||||||
eventSink("executeToolCallsSequential 5"; publish=true)
|
|
||||||
if signal !== nothing && signal.aborted
|
if signal !== nothing && signal.aborted
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("executeToolCallsSequential 6"; publish=true)
|
eventSink("▶ executeToolCallsSequential: batch end")
|
||||||
return agentToolCallBatch(messages, shouldTerminate(finalizedCalls))
|
return agentToolCallBatch(messages, shouldTerminate(finalizedCalls))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1362,23 +1327,24 @@ function executeToolCallsParallel(
|
|||||||
)::agentToolCallBatch
|
)::agentToolCallBatch
|
||||||
|
|
||||||
entries = Union{finalizedOutcome,Task}[]
|
entries = Union{finalizedOutcome,Task}[]
|
||||||
|
eventSink("▶ executeToolCallsParallel: batch start")
|
||||||
|
|
||||||
for tc in toolCalls
|
for tc in toolCalls
|
||||||
eventSink(toolExecStartEvent(tc.id, tc.name, tc.arguments); publish=true)
|
toolExecStartEvent(tc.id, tc.name, tc.arguments)
|
||||||
|
|
||||||
prep = prepareToolCall(context, assistantMsg, tc, config, signal, eventSink)
|
prep = prepareToolCall(context, assistantMsg, tc, config, signal, eventSink)
|
||||||
|
|
||||||
if prep isa immediateOutcome
|
if prep isa immediateOutcome
|
||||||
finalized = finalizedOutcome(tc, prep.result, prep.isError)
|
finalized = finalizedOutcome(tc, prep.result, prep.isError)
|
||||||
eventSink(toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
||||||
finalized.result, finalized.isError))
|
finalized.result, finalized.isError)
|
||||||
push!(entries, finalized)
|
push!(entries, finalized)
|
||||||
else
|
else
|
||||||
t = Task() do
|
t = Task() do
|
||||||
executed = executePreparedToolCall(prep, signal, eventSink)
|
executed = executePreparedToolCall(prep, signal, eventSink)
|
||||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config, signal)
|
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config, signal)
|
||||||
eventSink(toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
||||||
finalized.result, finalized.isError))
|
finalized.result, finalized.isError)
|
||||||
return finalized
|
return finalized
|
||||||
end
|
end
|
||||||
schedule(t)
|
schedule(t)
|
||||||
@@ -1401,6 +1367,7 @@ function executeToolCallsParallel(
|
|||||||
push!(messages, createToolResultMessage(f))
|
push!(messages, createToolResultMessage(f))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
eventSink("▶ executeToolCallsParallel: batch end")
|
||||||
return agentToolCallBatch(messages, shouldTerminate(finalizedCalls))
|
return agentToolCallBatch(messages, shouldTerminate(finalizedCalls))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1463,7 +1430,6 @@ function executeToolCalls(
|
|||||||
eventSink,
|
eventSink,
|
||||||
)::agentToolCallBatch
|
)::agentToolCallBatch
|
||||||
|
|
||||||
eventSink("_executeToolCalls 1"; publish=true)
|
|
||||||
hasSequential = false
|
hasSequential = false
|
||||||
for tc in toolCalls
|
for tc in toolCalls
|
||||||
t = get(context.tools, tc.name, nothing)
|
t = get(context.tools, tc.name, nothing)
|
||||||
@@ -1472,13 +1438,11 @@ function executeToolCalls(
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("_executeToolCalls 2"; publish=true)
|
eventSink("▶ executeToolCalls: dispatch")
|
||||||
if config.toolExecution == "sequential" || hasSequential
|
if config.toolExecution == "sequential" || hasSequential
|
||||||
eventSink("_executeToolCalls 3"; publish=true)
|
|
||||||
return executeToolCallsSequential(context, assistantMsg, toolCalls, config, signal,
|
return executeToolCallsSequential(context, assistantMsg, toolCalls, config, signal,
|
||||||
eventSink)
|
eventSink)
|
||||||
else
|
else
|
||||||
eventSink("_executeToolCalls 4"; publish=true)
|
|
||||||
return executeToolCallsParallel(context, assistantMsg, toolCalls, config, signal,
|
return executeToolCallsParallel(context, assistantMsg, toolCalls, config, signal,
|
||||||
eventSink)
|
eventSink)
|
||||||
end
|
end
|
||||||
@@ -1519,8 +1483,11 @@ OpenAiToUserMessage(msg)
|
|||||||
# => userMessage("user", [textContent("Hello")], DateTime(...))
|
# => userMessage("user", [textContent("Hello")], DateTime(...))
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function OpenAiToUserMessage(msg::AbstractDict)::userMessage
|
function OpenAiToUserMessage(msg::AbstractDict, eventSink=nothing)::userMessage
|
||||||
content_blocks = Vector{messageContent}()
|
content_blocks = Vector{messageContent}()
|
||||||
|
if eventSink !== nothing
|
||||||
|
eventSink("▶ OpenAiToUserMessage: parsing")
|
||||||
|
end
|
||||||
|
|
||||||
raw_content = get(msg, "content", Any[])
|
raw_content = get(msg, "content", Any[])
|
||||||
if raw_content isa Vector
|
if raw_content isa Vector
|
||||||
@@ -1544,6 +1511,9 @@ function OpenAiToUserMessage(msg::AbstractDict)::userMessage
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if eventSink !== nothing
|
||||||
|
eventSink("▶ OpenAiToUserMessage: done")
|
||||||
|
end
|
||||||
return userMessage(content=content_blocks)
|
return userMessage(content=content_blocks)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -121,9 +121,7 @@ function register_mcp_tools(tools::OrderedDict{String, agentTool}, mcpserver; ev
|
|||||||
end
|
end
|
||||||
new_count = 0
|
new_count = 0
|
||||||
try
|
try
|
||||||
eventSink("register_mcp_tools 1")
|
|
||||||
response = mcpserver("tools/list", sessionId; eventSink=eventSink)
|
response = mcpserver("tools/list", sessionId; eventSink=eventSink)
|
||||||
eventSink("register_mcp_tools 2")
|
|
||||||
# Parse JSON-RPC 2.0 response envelope
|
# Parse JSON-RPC 2.0 response envelope
|
||||||
if haskey(response, "error")
|
if haskey(response, "error")
|
||||||
rpc_error = response["error"]
|
rpc_error = response["error"]
|
||||||
@@ -200,7 +198,7 @@ Add `tool` to `tools`, overwriting any existing tool with the same name.
|
|||||||
function registerTool(tools::OrderedDict{String, agentTool}, tool::agentTool; eventSink=nothing
|
function registerTool(tools::OrderedDict{String, agentTool}, tool::agentTool; eventSink=nothing
|
||||||
)::OrderedDict{String, agentTool}
|
)::OrderedDict{String, agentTool}
|
||||||
tools[tool.name] = tool
|
tools[tool.name] = tool
|
||||||
println("[toolRegistry] Registered tool: $(tool.name)")
|
eventSink("[toolRegistry] Registered tool: $(tool.name)")
|
||||||
return tools
|
return tools
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -247,7 +245,6 @@ function listTool(tools::OrderedDict{String, agentTool}, mcpserver; eventSink=no
|
|||||||
eventSink) -> begin
|
eventSink) -> begin
|
||||||
# Discover and register MCP tools (idempotent — skips already registered)
|
# Discover and register MCP tools (idempotent — skips already registered)
|
||||||
new_count = register_mcp_tools(tools, mcpserver; eventSink=eventSink, sessionId=sessionId)
|
new_count = register_mcp_tools(tools, mcpserver; eventSink=eventSink, sessionId=sessionId)
|
||||||
eventSink("tools dump: " * sprint(show, tools))
|
|
||||||
|
|
||||||
# Always include listTools itself in the count
|
# Always include listTools itself in the count
|
||||||
total = length(tools)
|
total = length(tools)
|
||||||
@@ -285,7 +282,9 @@ Register all tools for an agent:
|
|||||||
"""
|
"""
|
||||||
function registerAllTools(tools::OrderedDict{String, agentTool}, mcpserver=nothing; eventSink=nothing, sessionId=nothing)
|
function registerAllTools(tools::OrderedDict{String, agentTool}, mcpserver=nothing; eventSink=nothing, sessionId=nothing)
|
||||||
list_t = listTool(tools, mcpserver; eventSink=eventSink, sessionId=sessionId)
|
list_t = listTool(tools, mcpserver; eventSink=eventSink, sessionId=sessionId)
|
||||||
|
eventSink("registering tools")
|
||||||
registerTool(tools, list_t; eventSink=eventSink)
|
registerTool(tools, list_t; eventSink=eventSink)
|
||||||
|
eventSink("registering tools done")
|
||||||
end
|
end
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|||||||
+5
-10
@@ -208,7 +208,6 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any}
|
|||||||
openaiReadyMsg = Dict{String, Any}()
|
openaiReadyMsg = Dict{String, Any}()
|
||||||
# openaiReadyMsg["model"] = "gemma-4-E4B-it-UD-Q4_K_XL"
|
# openaiReadyMsg["model"] = "gemma-4-E4B-it-UD-Q4_K_XL"
|
||||||
messages = Vector{Dict{String, Any}}()
|
messages = Vector{Dict{String, Any}}()
|
||||||
eventSink("formatMsgForLLM 1")
|
|
||||||
# System prompt as system message
|
# System prompt as system message
|
||||||
if !isempty(ctx.systemPrompt)
|
if !isempty(ctx.systemPrompt)
|
||||||
push!(messages, Dict(
|
push!(messages, Dict(
|
||||||
@@ -216,7 +215,6 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any}
|
|||||||
"content" => [Dict("type" => "text", "text" => ctx.systemPrompt)]
|
"content" => [Dict("type" => "text", "text" => ctx.systemPrompt)]
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
eventSink("formatMsgForLLM 2")
|
|
||||||
# Conversation messages
|
# Conversation messages
|
||||||
for msg in ctx.messages
|
for msg in ctx.messages
|
||||||
if msg isa userMessage
|
if msg isa userMessage
|
||||||
@@ -229,10 +227,8 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any}
|
|||||||
push!(messages, _toolResultMessageToOpenAI(msg))
|
push!(messages, _toolResultMessageToOpenAI(msg))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("formatMsgForLLM 3")
|
|
||||||
# Convert ctx.tools into OpenAI tools format
|
# Convert ctx.tools into OpenAI tools format
|
||||||
tools_array = _toolsToOpenAI(ctx.tools, eventSink)
|
tools_array = _toolsToOpenAI(ctx.tools, eventSink)
|
||||||
eventSink("formatMsgForLLM 4")
|
|
||||||
openaiReadyMsg["messages"] = messages
|
openaiReadyMsg["messages"] = messages
|
||||||
openaiReadyMsg["temperature"] = 0.7
|
openaiReadyMsg["temperature"] = 0.7
|
||||||
|
|
||||||
@@ -278,7 +274,7 @@ end
|
|||||||
function beforeToolCall(context::beforeToolCallContext, signal::abortSignal, eventSink
|
function beforeToolCall(context::beforeToolCallContext, signal::abortSignal, eventSink
|
||||||
)::beforeToolCallResult
|
)::beforeToolCallResult
|
||||||
|
|
||||||
eventSink("beforeToolCall: $(context.toolCall.name) args=$(context.args)")
|
# eventSink("beforeToolCall: $(context.toolCall.name) args=$(context.args)")
|
||||||
|
|
||||||
return beforeToolCallResult(false, "N/A")
|
return beforeToolCallResult(false, "N/A")
|
||||||
end
|
end
|
||||||
@@ -306,11 +302,11 @@ Common use cases:
|
|||||||
- `agentToolResult(...)` to return a modified result (content, details, usage,
|
- `agentToolResult(...)` to return a modified result (content, details, usage,
|
||||||
terminate flag can all be overridden).
|
terminate flag can all be overridden).
|
||||||
|
|
||||||
""" #WORKING arrange result from searchWine tool
|
""" # arrange result from searchWine tool
|
||||||
function afterToolCall(context::afterToolCallContext, signal::abortSignal, eventSink
|
function afterToolCall(context::afterToolCallContext, signal::abortSignal, eventSink
|
||||||
)::Union{agentToolResult, Nothing}
|
)::Union{agentToolResult, Nothing}
|
||||||
|
|
||||||
eventSink("afterToolCall: $(context.toolCall.name) result=$(context.result) error=$(context.isError)")
|
# eventSink("afterToolCall: $(context.toolCall.name) result=$(context.result) error=$(context.isError)")
|
||||||
|
|
||||||
return nothing
|
return nothing
|
||||||
end
|
end
|
||||||
@@ -428,8 +424,7 @@ _toolsToOpenAI(tools) # => [Dict("type" => "function", "function" => Dict("na
|
|||||||
"""
|
"""
|
||||||
function _toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}, eventSink)::Vector{Dict{String, Any}}
|
function _toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}, eventSink)::Vector{Dict{String, Any}}
|
||||||
tools_array = Vector{Dict{String, Any}}()
|
tools_array = Vector{Dict{String, Any}}()
|
||||||
eventSink("_toolsToOpenAI 1")
|
|
||||||
eventSink(string(typeof(tools)))
|
|
||||||
if tools !== nothing
|
if tools !== nothing
|
||||||
for (_, tool) in tools
|
for (_, tool) in tools
|
||||||
push!(tools_array, Dict(
|
push!(tools_array, Dict(
|
||||||
@@ -442,7 +437,7 @@ function _toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}, e
|
|||||||
))
|
))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
eventSink("_toolsToOpenAI 2")
|
|
||||||
return tools_array
|
return tools_array
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user