From 2fd363d9ef438c6d68d5d259a4a1e41458114f7c Mon Sep 17 00:00:00 2001 From: narawat Date: Sat, 12 Sep 2026 16:12:56 +0700 Subject: [PATCH 1/2] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3f8466f..5d1d1e9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "YiemAgent" uuid = "e012c34b-7f78-48e0-971c-7abb83b6f0a2" -version = "0.8.0" +version = "0.9.0" authors = ["narawat lamaiin "] [deps] -- 2.52.0 From 4624ad99ab17936b1e61e0555df5a7ed8ffed9b8 Mon Sep 17 00:00:00 2001 From: narawat Date: Sat, 12 Sep 2026 17:04:00 +0700 Subject: [PATCH 2/2] update --- src/agentCore.jl | 90 +++++---------------------------------------- src/toolRegistry.jl | 3 -- src/utils.jl | 15 +++----- 3 files changed, 14 insertions(+), 94 deletions(-) diff --git a/src/agentCore.jl b/src/agentCore.jl index 57524ea..5c2cc74 100644 --- a/src/agentCore.jl +++ b/src/agentCore.jl @@ -264,18 +264,14 @@ function _agentLoop(agent::yiemAgent) while true while newUserMsg === nothing 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. newUserMsg = take!(agent.inputChannel) - # agent.eventSink("new user msg\n$newUserMsg"; publish=true) else # check followUp message after _processMessage() is done if typeof(processingTask) == Task && istaskdone(processingTask) == true - agent.eventSink("_agentLoop 2 agent._state.messages length $(length(agent._state.messages))"; publish=true) # if agent runs is done but followUpChannel has messages, # put new message in inputChannel instead if isready(agent.followUpChannel) - agent.eventSink("_agentLoop 3 agent._state.messages length $(length(agent._state.messages))"; publish=true) while isready(agent.followUpChannel) followUpMsg = take!(agent.followUpChannel) put!(agent.inputChannel, followUpMsg) @@ -283,19 +279,12 @@ function _agentLoop(agent::yiemAgent) processingTask = nothing # reset result = nothing # reset else # _processMessage() done and no followUp message. - agent.eventSink("_agentLoop 4 agent._state.messages length $(length(agent._state.messages))"; publish=true) result = deepcopy(agent._state.messages[end]) - agent.eventSink("_agentLoop 4-1 "; publish=true) # filter out reasoningContent in-place filter!(c -> !(c isa reasoningContent), result.content) - agent.eventSink("_agentLoop 5 "; publish=true) # format output respondToUI = _assistantMessageToOpenAI(result) - agent.eventSink("_agentLoop 6 "; publish=true) put!(agent.outputChannel, respondToUI) - if !isempty(result.content) && result.content[1] isa textContent - agent.eventSink(result.content[1].text; publish=true) - end processingTask = nothing # reset result = nothing # reset end @@ -325,7 +314,6 @@ function _agentLoop(agent::yiemAgent) else # spawn new _processMessage() if it is not already running. if processingTask === nothing - agent.eventSink("_agentLoop 5 agent._state.messages length $(length(agent._state.messages))"; publish=true) # discard all messages in followUpChannel while isready(agent.followUpChannel) _ = take!(agent.followUpChannel) @@ -404,7 +392,6 @@ function _processMessage( afterToolCall::Union{Function, Nothing}, parallelToolExecute::Bool, )::Nothing - eventSink("_processMessage 1 _state.messages length $(length(agentMsgHistory))"; publish=true) # loop until llmCall() response didn't use tool calls final_response = nothing @@ -425,43 +412,29 @@ function _processMessage( try # Drain inputChannel and convert OpenAI-format messages to userMessage type while isready(inputChannel) - eventSink("_processMessage 2 _state.messages length $(length(agentMsgHistory))"; publish=true) newUserMsg_openai = take!(inputChannel) - eventSink("_processMessage 3 _state.messages length $(length(agentMsgHistory))"; publish=true) if newUserMsg_openai === :shutdown - eventSink("_processMessage 4 _state.messages length $(length(agentMsgHistory))"; publish=true) # Re-emit shutdown signal for the loop to handle put!(inputChannel, :shutdown) break end - eventSink("_processMessage 5 _state.messages length $(length(agentMsgHistory))"; publish=true) newUserMsg = OpenAiToUserMessage(newUserMsg_openai) push!(agentMsgHistory, newUserMsg) - eventSink("_processMessage 6 _state.messages length $(length(agentMsgHistory))"; publish=true) end - eventSink("_processMessage 7 _state.messages length $(length(agentMsgHistory))"; publish=true) # call prepareContext() state = agentState(systemPrompt, nothing, tools, agentMsgHistory) - eventSink("_processMessage 8 _state.messages length $(length(agentMsgHistory))"; publish=true) preparedContext = prepareContext(state, eventSink) - eventSink("_processMessage 9 _state.messages length $(length(agentMsgHistory))"; publish=true) # Call formatMessagesForLLM() to format for LLM formattedMessages = formatMessagesForLLM(preparedContext, eventSink) - - # eventSink("_processMessage 10 formattedMessages $formattedMessages"; publish=true) + eventSink("Thinking...") """ 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=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 hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response) - 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 push!(agentMsgHistory, assistant_msg) @@ -476,21 +449,14 @@ function _processMessage( ) signal = abortSignal(false) - eventSink("_processMessage 12 _state.messages length $(length(agentMsgHistory))"; publish=true) - # call executeToolCalls() toolResultBatch = executeToolCalls(preparedContext, assistant_msg, toolCallList, config, signal, eventSink) - eventSink("_processMessage 13 _state.messages length $(length(agentMsgHistory))"; publish=true) - # save toolResults to messages for toolResult in toolResultBatch.messages - # eventSink("toolResult " * string(toolResult); publish=true) push!(agentMsgHistory, toolResult) end - eventSink("_processMessage 14 _state.messages length $(length(agentMsgHistory))"; publish=true) if toolResultBatch.terminate - eventSink("_processMessage 15 _state.messages length $(length(agentMsgHistory))"; publish=true) # If toolResultBatch requested termination, build a final response final_content = [textContent("Tool execution completed.")] for toolResult in toolResultBatch.messages @@ -504,7 +470,6 @@ function _processMessage( end end end - eventSink("_processMessage 16 _state.messages length $(length(agentMsgHistory))"; publish=true) final_response = assistantMessage( role="assistant", content=final_content, @@ -523,7 +488,6 @@ function _processMessage( break end else - eventSink("_processMessage 17 _state.messages length $(length(agentMsgHistory))"; publish=true) # LLM did not use tool calls — break end @@ -538,8 +502,6 @@ function _processMessage( end end - - eventSink("_processMessage 18 _state.messages length $(length(agentMsgHistory))"; publish=true) return nothing end @@ -979,47 +941,37 @@ function prepareToolCall( signal::abortSignal, eventSink )::Union{preparedToolCall,immediateOutcome} - eventSink("prepareToolCall 1"; publish=true) # pick a called tool from tool store tool = get(context.tools, toolCall.name, nothing) if tool === nothing - eventSink("prepareToolCall 2"; publish=true) return immediateOutcome(createErrorToolResult("Tool $toolCall.name not found"), true) end try - eventSink("prepareToolCall 3"; publish=true) # 1. prepare arguments (tool-specific transform) prepared = prepareToolCallArguments(tool, toolCall) - eventSink("prepared " * string(prepared.arguments); publish=true) - eventSink("prepareToolCall 4"; publish=true) validatedArgs = validateToolArguments(tool, prepared) - eventSink("validatedArgs " * string(validatedArgs); publish=true) - eventSink("prepareToolCall 5"; publish=true) # 2. beforeToolCall hook — can block if config.beforeToolCall !== nothing - eventSink("prepareToolCall 6"; publish=true) before = config.beforeToolCall( beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context), signal, eventSink ) - eventSink("prepareToolCall 7"; publish=true) + if signal.aborted - eventSink("prepareToolCall 8"; publish=true) return immediateOutcome(createErrorToolResult("Operation aborted"), true) end if before !== nothing && before.block - eventSink("prepareToolCall 9"; publish=true) return immediateOutcome( createErrorToolResult(get(before, :reason, "Tool execution was blocked")), true) end end - eventSink("prepareToolCall 10"; publish=true) + return preparedToolCall(tool, toolCall, validatedArgs) catch e bt = catch_backtrace() @@ -1029,7 +981,6 @@ function prepareToolCall( end eventSink(errMsg; publish=true) - return immediateOutcome(createErrorToolResult(sprint(showerror, e)), true) end end @@ -1080,11 +1031,9 @@ function executePreparedToolCall( signal::Union{Nothing,abortSignal}, eventSink, )::executedOutcome - eventSink("executePreparedToolCall 1"; publish=true) try result = prep.tool.execute(prep.toolCall.id, prep.args, signal, eventSink) - # eventSink("tool execute raw result: " * sprint(show, result); publish=true) return executedOutcome(result, false) catch e bt = catch_backtrace() @@ -1165,10 +1114,9 @@ function finalizeExecutedToolCall( signal::Union{Nothing,abortSignal}, eventSink )::finalizedOutcome - eventSink("finalizeExecutedToolCall 1"; publish=true) + result = executed.result isError = executed.isError - # eventSink("finalizeExecutedToolCall result before afterToolCall\n$result"; publish=true) if config.afterToolCall !== nothing try after = config.afterToolCall( @@ -1176,7 +1124,6 @@ function finalizeExecutedToolCall( signal, eventSink ) - eventSink("finalizeExecutedToolCall 3"; publish=true) if after !== nothing result = after isError = false @@ -1193,7 +1140,6 @@ function finalizeExecutedToolCall( isError = true end end - eventSink("finalizeExecutedToolCall 4"; publish=true) return finalizedOutcome(prep.toolCall, result, isError) end @@ -1259,39 +1205,25 @@ function executeToolCallsSequential( signal::abortSignal, eventSink, )::agentToolCallBatch - eventSink("executeToolCallsSequential 1"; publish=true) finalizedCalls = finalizedOutcome[] messages = toolResultMessage[] for tc in toolCalls - eventSink("start tool execute $(tc.id), $(tc.name), $(tc.arguments)"; publish=true) prep = prepareToolCall(context, assistantMsg, tc, config, signal, eventSink) - eventSink("executeToolCallsSequential " * string(prep.args); publish=true) if prep isa immediateOutcome - eventSink("executeToolCallsSequential 2-1"; publish=true) finalized = finalizedOutcome(tc, prep.result, prep.isError) - eventSink("executeToolCallsSequential 2-2"; publish=true) else - eventSink("executeToolCallsSequential 3"; publish=true) executed = executePreparedToolCall(prep, signal, eventSink) - - eventSink("executeToolCallsSequential 3-1"; publish=true) finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config, signal, eventSink) - eventSink("executeToolCallsSequential 3-2"; publish=true) 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!(finalizedCalls, finalized) - eventSink("executeToolCallsSequential 5"; publish=true) if signal !== nothing && signal.aborted break end end - eventSink("executeToolCallsSequential 6"; publish=true) return agentToolCallBatch(messages, shouldTerminate(finalizedCalls)) end @@ -1364,21 +1296,21 @@ function executeToolCallsParallel( entries = Union{finalizedOutcome,Task}[] 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) if prep isa immediateOutcome finalized = finalizedOutcome(tc, prep.result, prep.isError) - eventSink(toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name, - finalized.result, finalized.isError)) + toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name, + finalized.result, finalized.isError) push!(entries, finalized) else t = Task() do 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)) + toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name, + finalized.result, finalized.isError) return finalized end schedule(t) @@ -1463,7 +1395,6 @@ function executeToolCalls( eventSink, )::agentToolCallBatch - eventSink("_executeToolCalls 1"; publish=true) hasSequential = false for tc in toolCalls t = get(context.tools, tc.name, nothing) @@ -1472,13 +1403,10 @@ function executeToolCalls( break end end - eventSink("_executeToolCalls 2"; publish=true) if config.toolExecution == "sequential" || hasSequential - eventSink("_executeToolCalls 3"; publish=true) return executeToolCallsSequential(context, assistantMsg, toolCalls, config, signal, eventSink) else - eventSink("_executeToolCalls 4"; publish=true) return executeToolCallsParallel(context, assistantMsg, toolCalls, config, signal, eventSink) end diff --git a/src/toolRegistry.jl b/src/toolRegistry.jl index cceb5c1..49e5ad8 100644 --- a/src/toolRegistry.jl +++ b/src/toolRegistry.jl @@ -121,9 +121,7 @@ function register_mcp_tools(tools::OrderedDict{String, agentTool}, mcpserver; ev end new_count = 0 try - eventSink("register_mcp_tools 1") response = mcpserver("tools/list", sessionId; eventSink=eventSink) - eventSink("register_mcp_tools 2") # Parse JSON-RPC 2.0 response envelope if haskey(response, "error") rpc_error = response["error"] @@ -247,7 +245,6 @@ function listTool(tools::OrderedDict{String, agentTool}, mcpserver; eventSink=no eventSink) -> begin # Discover and register MCP tools (idempotent — skips already registered) new_count = register_mcp_tools(tools, mcpserver; eventSink=eventSink, sessionId=sessionId) - eventSink("tools dump: " * sprint(show, tools)) # Always include listTools itself in the count total = length(tools) diff --git a/src/utils.jl b/src/utils.jl index 1a4c565..63f546a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -208,7 +208,6 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any} openaiReadyMsg = Dict{String, Any}() # openaiReadyMsg["model"] = "gemma-4-E4B-it-UD-Q4_K_XL" messages = Vector{Dict{String, Any}}() - eventSink("formatMsgForLLM 1") # System prompt as system message if !isempty(ctx.systemPrompt) push!(messages, Dict( @@ -216,7 +215,6 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any} "content" => [Dict("type" => "text", "text" => ctx.systemPrompt)] )) end - eventSink("formatMsgForLLM 2") # Conversation messages for msg in ctx.messages if msg isa userMessage @@ -229,10 +227,8 @@ function formatMsgForLLM(ctx::agentContext, eventSink)::Dict{String, Any} push!(messages, _toolResultMessageToOpenAI(msg)) end end - eventSink("formatMsgForLLM 3") # Convert ctx.tools into OpenAI tools format tools_array = _toolsToOpenAI(ctx.tools, eventSink) - eventSink("formatMsgForLLM 4") openaiReadyMsg["messages"] = messages openaiReadyMsg["temperature"] = 0.7 @@ -278,7 +274,7 @@ end function beforeToolCall(context::beforeToolCallContext, signal::abortSignal, eventSink )::beforeToolCallResult - eventSink("beforeToolCall: $(context.toolCall.name) args=$(context.args)") + # eventSink("beforeToolCall: $(context.toolCall.name) args=$(context.args)") return beforeToolCallResult(false, "N/A") end @@ -306,11 +302,11 @@ Common use cases: - `agentToolResult(...)` to return a modified result (content, details, usage, terminate flag can all be overridden). -""" #WORKING arrange result from searchWine tool +""" # arrange result from searchWine tool function afterToolCall(context::afterToolCallContext, signal::abortSignal, eventSink )::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 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}} tools_array = Vector{Dict{String, Any}}() - eventSink("_toolsToOpenAI 1") - eventSink(string(typeof(tools))) + if tools !== nothing for (_, tool) in tools push!(tools_array, Dict( @@ -442,7 +437,7 @@ function _toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}, e )) end end - eventSink("_toolsToOpenAI 2") + return tools_array end -- 2.52.0