mcp works
This commit is contained in:
+109
-101
@@ -187,13 +187,13 @@ function yiemAgent(
|
||||
followUp = Channel(32)
|
||||
outputChannel = Channel(16)
|
||||
|
||||
# load tools (statically registered at module init)
|
||||
toolStore1 = toolStore(name="myagent")
|
||||
registerAllTools(toolStore1, mcpServer; eventSink=eventSink)
|
||||
|
||||
tools = OrderedDict{String, agentTool}()
|
||||
registerAllTools(tools, mcpServer; eventSink=eventSink)
|
||||
|
||||
# Create struct with a placeholder task, then spawn and replace it
|
||||
agent = yiemAgent(
|
||||
agentState(systemPrompt, model, getTools(toolStore1), messages),
|
||||
agentState(systemPrompt, model, tools, messages),
|
||||
inputChannel,
|
||||
followUp,
|
||||
outputChannel,
|
||||
@@ -442,112 +442,123 @@ function _processMessage(
|
||||
"""
|
||||
|
||||
while true
|
||||
# Drain inputChannel and convert OpenAI-format messages to userMessage type
|
||||
while isready(inputChannel)
|
||||
eventSink("_processMessage 2 _state.messages length $(length(agentMsgHistory))")
|
||||
newUserMsg_openai = take!(inputChannel)
|
||||
eventSink("_processMessage 3 _state.messages length $(length(agentMsgHistory))")
|
||||
if newUserMsg_openai === :shutdown
|
||||
eventSink("_processMessage 4 _state.messages length $(length(agentMsgHistory))")
|
||||
# Re-emit shutdown signal for the loop to handle
|
||||
put!(inputChannel, :shutdown)
|
||||
break
|
||||
try
|
||||
# Drain inputChannel and convert OpenAI-format messages to userMessage type
|
||||
while isready(inputChannel)
|
||||
eventSink("_processMessage 2 _state.messages length $(length(agentMsgHistory))")
|
||||
newUserMsg_openai = take!(inputChannel)
|
||||
eventSink("_processMessage 3 _state.messages length $(length(agentMsgHistory))")
|
||||
if newUserMsg_openai === :shutdown
|
||||
eventSink("_processMessage 4 _state.messages length $(length(agentMsgHistory))")
|
||||
# Re-emit shutdown signal for the loop to handle
|
||||
put!(inputChannel, :shutdown)
|
||||
break
|
||||
end
|
||||
eventSink("_processMessage 5 _state.messages length $(length(agentMsgHistory))")
|
||||
newUserMsg = OpenAiToUserMessage(newUserMsg_openai)
|
||||
push!(agentMsgHistory, newUserMsg)
|
||||
eventSink("_processMessage 6 _state.messages length $(length(agentMsgHistory))")
|
||||
end
|
||||
eventSink("_processMessage 5 _state.messages length $(length(agentMsgHistory))")
|
||||
newUserMsg = OpenAiToUserMessage(newUserMsg_openai)
|
||||
push!(agentMsgHistory, newUserMsg)
|
||||
eventSink("_processMessage 6 _state.messages length $(length(agentMsgHistory))")
|
||||
end
|
||||
eventSink("_processMessage 7 _state.messages length $(length(agentMsgHistory))")
|
||||
# call prepareContext()
|
||||
state = agentState(systemPrompt, nothing, tools, agentMsgHistory)
|
||||
eventSink("_processMessage 8 _state.messages length $(length(agentMsgHistory))")
|
||||
preparedContext = prepareContext(state, eventSink, llmCall)
|
||||
eventSink("_processMessage 9 _state.messages length $(length(agentMsgHistory))")
|
||||
# Call formatMessagesForLLM() to format for LLM
|
||||
formattedMessages = formatMessagesForLLM(preparedContext, eventSink)
|
||||
|
||||
eventSink("_processMessage 10 formattedMessages $formattedMessages")
|
||||
eventSink("_processMessage 7 _state.messages length $(length(agentMsgHistory))")
|
||||
# call prepareContext()
|
||||
state = agentState(systemPrompt, nothing, tools, agentMsgHistory)
|
||||
eventSink("_processMessage 8 _state.messages length $(length(agentMsgHistory))")
|
||||
preparedContext = prepareContext(state, eventSink)
|
||||
eventSink("_processMessage 9 _state.messages length $(length(agentMsgHistory))")
|
||||
# Call formatMessagesForLLM() to format for LLM
|
||||
formattedMessages = formatMessagesForLLM(preparedContext, eventSink)
|
||||
|
||||
eventSink("_processMessage 10 formattedMessages $formattedMessages")
|
||||
|
||||
""" 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 = llmCall(formattedMessages)
|
||||
eventSink(" llmCall " * string(response))
|
||||
""" 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 = llmCall(formattedMessages)
|
||||
eventSink("LLM response " * string(response))
|
||||
|
||||
eventSink("_processMessage 11 _state.messages length $(length(agentMsgHistory))")
|
||||
# Extract tool calls from LLM response content blocks
|
||||
hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response)
|
||||
eventSink("hasToolCalls: $hasToolCalls\ntoolCallList: $toolCallList _state.messages length $(length(agentMsgHistory))")
|
||||
eventSink("assistant_msg " * string(assistant_msg))
|
||||
eventSink("_processMessage 11-1")
|
||||
eventSink("_processMessage 11 _state.messages length $(length(agentMsgHistory))")
|
||||
# Extract tool calls from LLM response content blocks
|
||||
hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response)
|
||||
eventSink("hasToolCalls: $hasToolCalls\ntoolCallList: $toolCallList _state.messages length $(length(agentMsgHistory))")
|
||||
eventSink("assistant_msg " * string(assistant_msg))
|
||||
eventSink("_processMessage 11-1")
|
||||
|
||||
# Add assistant message (tool calls or text) to history for next LLM turn
|
||||
push!(agentMsgHistory, assistant_msg)
|
||||
# Add assistant message (tool calls or text) to history for next LLM turn
|
||||
push!(agentMsgHistory, assistant_msg)
|
||||
|
||||
if hasToolCalls && length(toolCallList) > 0
|
||||
# Build context and config for executeToolCalls
|
||||
if hasToolCalls && length(toolCallList) > 0
|
||||
# Build context and config for executeToolCalls
|
||||
|
||||
config = agentLoopConfig(
|
||||
beforeToolCall,
|
||||
afterToolCall,
|
||||
parallelToolExecute ? "parallel" : "sequential",
|
||||
llmCall,
|
||||
)
|
||||
config = agentLoopConfig(
|
||||
beforeToolCall,
|
||||
afterToolCall,
|
||||
parallelToolExecute ? "parallel" : "sequential",
|
||||
)
|
||||
|
||||
signal = abortSignal(false)
|
||||
eventSink("_processMessage 12 _state.messages length $(length(agentMsgHistory))")
|
||||
signal = abortSignal(false)
|
||||
eventSink("_processMessage 12 _state.messages length $(length(agentMsgHistory))")
|
||||
|
||||
# call executeToolCalls()
|
||||
toolResultBatch = executeToolCalls(preparedContext, assistant_msg, toolCallList, config,
|
||||
signal, eventSink)
|
||||
eventSink("_processMessage 13 _state.messages length $(length(agentMsgHistory))")
|
||||
# call executeToolCalls()
|
||||
toolResultBatch = executeToolCalls(preparedContext, assistant_msg, toolCallList, config,
|
||||
signal, eventSink)
|
||||
eventSink("_processMessage 13 _state.messages length $(length(agentMsgHistory))")
|
||||
|
||||
# save toolResults to messages
|
||||
for toolResult in toolResultBatch.messages
|
||||
eventSink("toolResult " * string(toolResult))
|
||||
push!(agentMsgHistory, toolResult)
|
||||
end
|
||||
eventSink("_processMessage 14 _state.messages length $(length(agentMsgHistory))")
|
||||
if toolResultBatch.terminate
|
||||
eventSink("_processMessage 15 _state.messages length $(length(agentMsgHistory))")
|
||||
# If toolResultBatch requested termination, build a final response
|
||||
final_content = [textContent("Tool execution completed.")]
|
||||
# save toolResults to messages
|
||||
for toolResult in toolResultBatch.messages
|
||||
for content_block in toolResult.content
|
||||
if content_block isa textContent
|
||||
append!(final_content, [content_block])
|
||||
elseif content_block isa Dict
|
||||
if haskey(content_block, :text)
|
||||
push!(final_content, textContent(content_block[:text]))
|
||||
eventSink("toolResult " * string(toolResult))
|
||||
push!(agentMsgHistory, toolResult)
|
||||
end
|
||||
eventSink("_processMessage 14 _state.messages length $(length(agentMsgHistory))")
|
||||
if toolResultBatch.terminate
|
||||
eventSink("_processMessage 15 _state.messages length $(length(agentMsgHistory))")
|
||||
# If toolResultBatch requested termination, build a final response
|
||||
final_content = [textContent("Tool execution completed.")]
|
||||
for toolResult in toolResultBatch.messages
|
||||
for content_block in toolResult.content
|
||||
if content_block isa textContent
|
||||
append!(final_content, [content_block])
|
||||
elseif content_block isa Dict
|
||||
if haskey(content_block, :text)
|
||||
push!(final_content, textContent(content_block[:text]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
eventSink("_processMessage 16 _state.messages length $(length(agentMsgHistory))")
|
||||
final_response = assistantMessage(
|
||||
role="assistant",
|
||||
content=final_content,
|
||||
api=assistant_msg.api,
|
||||
model=assistant_msg.model,
|
||||
usage=assistant_msg.usage,
|
||||
stopReason="tool_use_terminated",
|
||||
errorMessage=if any(x -> x.isError, toolResultBatch.messages)
|
||||
"One or more tool calls failed"
|
||||
else
|
||||
nothing
|
||||
end,
|
||||
timestamp=now(),
|
||||
)
|
||||
push!(agentMsgHistory, final_response)
|
||||
break
|
||||
end
|
||||
eventSink("_processMessage 16 _state.messages length $(length(agentMsgHistory))")
|
||||
final_response = assistantMessage(
|
||||
role="assistant",
|
||||
content=final_content,
|
||||
api=assistant_msg.api,
|
||||
model=assistant_msg.model,
|
||||
usage=assistant_msg.usage,
|
||||
stopReason="tool_use_terminated",
|
||||
errorMessage=if any(x -> x.isError, toolResultBatch.messages)
|
||||
"One or more tool calls failed"
|
||||
else
|
||||
nothing
|
||||
end,
|
||||
timestamp=now(),
|
||||
)
|
||||
push!(agentMsgHistory, final_response)
|
||||
else
|
||||
eventSink("_processMessage 17 _state.messages length $(length(agentMsgHistory))")
|
||||
# LLM did not use tool calls —
|
||||
break
|
||||
end
|
||||
else
|
||||
eventSink("_processMessage 17 _state.messages length $(length(agentMsgHistory))")
|
||||
# LLM did not use tool calls —
|
||||
break
|
||||
catch e
|
||||
bt = catch_backtrace()
|
||||
err_msg = sprint() do io
|
||||
showerror(io, e, bt)
|
||||
println(io)
|
||||
end
|
||||
|
||||
eventSink(err_msg)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
eventSink("_processMessage 18 _state.messages length $(length(agentMsgHistory))")
|
||||
return nothing
|
||||
end
|
||||
@@ -1087,14 +1098,13 @@ function executePreparedToolCall(
|
||||
prep::preparedToolCall,
|
||||
signal::Union{Nothing,abortSignal},
|
||||
eventSink,
|
||||
llmCall::Union{Any,Nothing}=nothing,
|
||||
)::executedOutcome
|
||||
eventSink("executePreparedToolCall 1")
|
||||
|
||||
try #WORKING
|
||||
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, eventSink, llmCall)
|
||||
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, eventSink)
|
||||
eventSink("executePreparedToolCall 2")
|
||||
eventSink(result.content[1].text)
|
||||
eventSink("tool execute raw result: " * sprint(show, result))
|
||||
eventSink("executePreparedToolCall 3")
|
||||
return executedOutcome(result, false)
|
||||
catch e
|
||||
@@ -1271,7 +1281,6 @@ function executeToolCallsSequential(
|
||||
signal::abortSignal,
|
||||
eventSink,
|
||||
)::agentToolCallBatch
|
||||
llmCall = config.llmCall
|
||||
eventSink("executeToolCallsSequential 1")
|
||||
finalizedCalls = finalizedOutcome[]
|
||||
messages = toolResultMessage[]
|
||||
@@ -1287,9 +1296,10 @@ function executeToolCallsSequential(
|
||||
eventSink("executeToolCallsSequential 2-2")
|
||||
else
|
||||
eventSink("executeToolCallsSequential 3")
|
||||
executed = executePreparedToolCall(prep, signal, eventSink, llmCall)
|
||||
executed = executePreparedToolCall(prep, signal, eventSink)
|
||||
|
||||
eventSink("executeToolCallsSequential 3-1")
|
||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config,
|
||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config,
|
||||
signal, eventSink)
|
||||
eventSink("executeToolCallsSequential 3-2")
|
||||
end
|
||||
@@ -1374,7 +1384,6 @@ function executeToolCallsParallel(
|
||||
)::agentToolCallBatch
|
||||
|
||||
entries = Union{finalizedOutcome,Task}[]
|
||||
llmCall = config.llmCall
|
||||
|
||||
for tc in toolCalls
|
||||
eventSink(toolExecStartEvent(tc.id, tc.name, tc.arguments))
|
||||
@@ -1388,7 +1397,7 @@ function executeToolCallsParallel(
|
||||
push!(entries, finalized)
|
||||
else
|
||||
t = Task() do
|
||||
executed = executePreparedToolCall(prep, signal, eventSink, llmCall)
|
||||
executed = executePreparedToolCall(prep, signal, eventSink)
|
||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config, signal)
|
||||
eventSink(toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
||||
finalized.result, finalized.isError))
|
||||
@@ -1476,7 +1485,6 @@ function executeToolCalls(
|
||||
eventSink,
|
||||
)::agentToolCallBatch
|
||||
|
||||
llmCall = config.llmCall
|
||||
eventSink("_executeToolCalls 1")
|
||||
hasSequential = false
|
||||
for tc in toolCalls
|
||||
|
||||
Reference in New Issue
Block a user