diff --git a/src/agentCore.jl b/src/agentCore.jl index 15b32d3..45a4738 100644 --- a/src/agentCore.jl +++ b/src/agentCore.jl @@ -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 diff --git a/src/api.jl b/src/api.jl index 50d6dbd..04812e3 100644 --- a/src/api.jl +++ b/src/api.jl @@ -18,7 +18,7 @@ The agent processes messages from `inputChannel` in the background task. # Arguments - `agent::yiemAgent`: The agent instance to send a message to -- `msg`: The message to send (any type accepted by the agent's processing pipeline) +- `msg`: The message to send, in OpenAI message format (e.g. `Dict("role" => "user", "content" => "Hello!")`) # Returns - The same `agent` instance for chaining @@ -29,11 +29,11 @@ The agent processes messages from `inputChannel` in the background task. # Examples ```jldoctest -julia> runAgent(agent, "Hello!") +julia> runAgent(agent, Dict("role" => "user", "content" => "Hello!")) yiemAgent(...) ``` """ -function runAgent(agent::yiemAgent, msg) +function runAgent(agent::yiemAgent, msg::AbstractDict{String, Any}) put!(agent.inputChannel, msg) return agent end @@ -70,7 +70,7 @@ and before any tool call results are sent. # Arguments - `agent::yiemAgent`: The agent instance to send a follow-up message to -- `msg`: The follow-up message to send +- `msg`: The follow-up message to send, in OpenAI message format (e.g. `Dict("role" => "user", "content" => "Also consider red wines")`) # Returns - The same `agent` instance for chaining @@ -82,11 +82,11 @@ and before any tool call results are sent. # Examples ```jldoctest -julia> followUp(agent, "Also consider red wines") +julia> followUp(agent, Dict("role" => "user", "content" => "Also consider red wines")) yiemAgent(...) ``` """ -function followUp(agent::yiemAgent, msg) +function followUp(agent::yiemAgent, msg::AbstractDict{String, Any}) put!(agent.followUpChannel, msg) return agent end diff --git a/src/toolRegistry.jl b/src/toolRegistry.jl index 105cfc5..80f6662 100644 --- a/src/toolRegistry.jl +++ b/src/toolRegistry.jl @@ -1,36 +1,12 @@ module toolRegistry -export toolStore, registerTool, registerAllTools, getTools, clearTools, listTool +export registerTool, registerAllTools, clearTools, listTool using Dates using JSON, DataStructures using ..type -""" - Per-agent isolated tool storage. -Each agent gets its own `toolStore` so tool registration is independent. - -# Fields -- `tools::OrderedDict{String, agentTool}` — keyed by name for O(1) lookup + ordered iteration -- `name::String` — identifier for debugging/logs -""" -struct toolStore - tools::OrderedDict{String, agentTool} - name::String -end - -""" - toolStore(; name="default") -> toolStore - -Create a new empty tool store. - -# Keyword Arguments -- `name::String`: Display name for logging (default: `"default"`) -""" -function toolStore(; name::String="default")::toolStore - toolStore(OrderedDict{String, agentTool}(), name) -end # ── MCP helper functions ──────────────────────────────────────────── @@ -40,7 +16,7 @@ Extract text from MCP tool result content array. Handles JSON-RPC 2.0 result content format: {"content": [{"type": "text", "text": "..."}], "isError": false} """ -function _extract_text_content(result::Dict)::String +function _extract_text_content(result::AbstractDict)::String content = get(result, "content", Any[]) if content isa Vector && !isempty(content) lines = String[] @@ -90,8 +66,7 @@ function _wrap_mcp_tool(mcpserver, tool_def::AbstractDict{String, Any}; eventSin inputSchema=params, execute=(toolCallId::String, args::AbstractDict{String, Any}, signal::Union{Nothing,abortSignal}, - eventSink, - llmCall=nothing) -> begin + eventSink) -> begin try response = mcpserver("tools/call", name, args) @@ -140,7 +115,7 @@ Handles pagination via `nextCursor`. Skips tools already registered. # Returns - `Int`: number of new tools registered """ -function register_mcp_tools(mcpserver, store::toolStore; eventSink=nothing)::Int +function register_mcp_tools(tools::OrderedDict{String, agentTool}, mcpserver; eventSink=nothing)::Int if mcpserver === nothing return 0 end @@ -148,13 +123,14 @@ function register_mcp_tools(mcpserver, store::toolStore; eventSink=nothing)::Int new_count = 0 try + eventSink("register_mcp_tools 1") response = mcpserver("tools/list") - + eventSink("register_mcp_tools 2") # Parse JSON-RPC 2.0 response envelope if haskey(response, "error") rpc_error = response["error"] err_msg = get(rpc_error, "message", "Unknown MCP error") - println("[toolRegistry:$(store.name)] MCP tools/list failed: $err_msg") + println("[toolRegistry] MCP tools/list failed: $err_msg") return 0 end if haskey(response, "result") @@ -166,12 +142,12 @@ function register_mcp_tools(mcpserver, store::toolStore; eventSink=nothing)::Int for tool_def in tools_array name = tool_def["name"] - if haskey(store.tools, name) + if haskey(tools, name) continue end - @show "tool_def $(typeof(tool_def))" + wrapped = _wrap_mcp_tool(mcpserver, tool_def, eventSink=eventSink) - store.tools[name] = wrapped + tools[name] = wrapped new_count += 1 end @@ -185,16 +161,16 @@ function register_mcp_tools(mcpserver, store::toolStore; eventSink=nothing)::Int cursor = get(response, "nextCursor", nothing) for tool_def in tools_array name = tool_def["name"] - if haskey(store.tools, name) + if haskey(tools, name) continue end wrapped = _wrap_mcp_tool(mcpserver, tool_def, eventSink=eventSink) - store.tools[name] = wrapped + tools[name] = wrapped new_count += 1 end end - println("[toolRegistry:$(store.name)] Discovered $new_count MCP tools. Total: $(length(store.tools))") + println("[toolRegistry] Discovered $new_count MCP tools. Total: $(length(tools))") catch e bt = catch_backtrace() err_msg = sprint() do io @@ -205,75 +181,60 @@ function register_mcp_tools(mcpserver, store::toolStore; eventSink=nothing)::Int eventSink(err_msg) errMsg = sprint(showerror, e) - println("[toolRegistry:$(store.name)] MCP tools/list failed: $errMsg") + println("[toolRegistry] MCP tools/list failed: $errMsg") end return new_count end """ - registerTool(store::toolStore, tool::agentTool) -> OrderedDict{String, agentTool} + registerTool(tools::OrderedDict{String, agentTool}, tool::agentTool) -> OrderedDict{String, agentTool} -Add `tool` to `store`, overwriting any existing tool with the same name. +Add `tool` to `tools`, overwriting any existing tool with the same name. # Arguments -- `store`: Tool store to modify +- `tools`: Tool dict to modify - `tool`: The `agentTool` to register # Returns -- The same `store.tools` dict (modified in place) +- The same `tools` dict (modified in place) """ -function registerTool(store::toolStore, tool::agentTool; eventSink=nothing +function registerTool(tools::OrderedDict{String, agentTool}, tool::agentTool; eventSink=nothing )::OrderedDict{String, agentTool} - store.tools[tool.name] = tool - println("[$(store.name)] Registered tool: $(tool.name)") - return store.tools + tools[tool.name] = tool + println("[toolRegistry] Registered tool: $(tool.name)") + return tools end """ -Return the tools registered in `store`. - -The returned dict is the **same object** stored inside `store`. +Remove all tools from the dict. # Arguments -- `store`: Tool store to query - -# Returns -- `OrderedDict{String, agentTool}`: Tools keyed by name, in registration order -""" -function getTools(store::toolStore)::OrderedDict{String, agentTool} - return store.tools -end - -""" -Remove all tools from `store`. - -# Arguments -- `store`: Tool store to clear +- `tools`: Tool dict to clear # Returns - `nothing` """ -function clearTools(store::toolStore)::Nothing - empty!(store.tools) - println("[$(store.name)] Registry cleared") +function clearTools(tools::OrderedDict{String, agentTool})::Nothing + empty!(tools) + println("[toolRegistry] Registry cleared") return nothing end # ── listTools tool (auto-discover new MCP tools at runtime) ───────── """ - listTool(store::toolStore, mcpserver) -> agentTool + listTool(tools::OrderedDict{String, agentTool}, mcpserver) -> agentTool MCP-aware listTools tool (JSON-RPC 2.0 protocol). First call: queries the MCP server via `mcpserver("tools/list")`, registers -all discovered tools into the shared `store.tools` (in-place mutation, with +all discovered tools into `tools` (in-place mutation, with pagination via nextCursor), then returns the full tool list. Subsequent calls: returns the current list (tools remain registered). """ -function listTool(store::toolStore, mcpserver)::agentTool +function listTool(tools::OrderedDict{String, agentTool}, mcpserver; eventSink=nothing)::agentTool return agentTool( name="listTools", label="List Tools", @@ -285,16 +246,17 @@ function listTool(store::toolStore, mcpserver)::agentTool ), execute=(toolCallId::String, args::AbstractDict{String, Any}, signal::Union{Nothing,abortSignal}, - eventSink, llmCall=nothing) -> begin + eventSink) -> begin # Discover and register MCP tools (idempotent — skips already registered) - new_count = register_mcp_tools(mcpserver, store) + new_count = register_mcp_tools(tools, mcpserver; eventSink=eventSink) + eventSink("tools dump: " * sprint(show, tools)) # Always include listTools itself in the count - total = length(store.tools) + total = length(tools) lines = String[ "- $(t.name): $(t.label) — $(t.description)" - for (k, t) in store.tools + for (k, t) in tools ] result_text = "Available tools ($total):\n" * join(lines, "\n") @@ -313,23 +275,19 @@ end # ── High-level API ────────────────────────────────────────────────── """ - registerAllTools(store::toolStore, mcpserver) -> toolStore + registerAllTools(tools::OrderedDict{String, agentTool}, mcpserver) Register all tools for an agent: 1. Auto-discover existing tools from the MCP server 2. Register the `listTools` tool so the agent can discover new tools at runtime # Arguments -- `store`: The tool store to populate +- `tools`: The tool dict to populate - `mcpserver`: A callable struct that communicates with the MCP server - -# Returns -- The populated `toolStore` """ -function registerAllTools(store::toolStore, mcpserver=nothing; eventSink=nothing)::toolStore - register_mcp_tools(mcpserver, store; eventSink=eventSink) - registerTool(store, listTool(store, mcpserver); eventSink=eventSink) - return store +function registerAllTools(tools::OrderedDict{String, agentTool}, mcpserver=nothing; eventSink=nothing) + list_t = listTool(tools, mcpserver; eventSink=eventSink) + registerTool(tools, list_t; eventSink=eventSink) end end # module diff --git a/src/type.jl b/src/type.jl index 0760cd9..013e809 100644 --- a/src/type.jl +++ b/src/type.jl @@ -358,7 +358,6 @@ struct agentContext # Snapshot of the agent's conversa systemPrompt::String # System prompt for the agent messages::Vector{agentMessage} # Conversation messages tools::Union{OrderedDict{String, agentTool}, Nothing} # Available tools keyed by name - llmCall::Union{Any, Nothing} # LLM call function (for tools that need it) end @@ -408,7 +407,7 @@ function agentState( agentState( systemPrompt, model, - deepcopy(tools), + tools, deepcopy(messages), Vector{String}(), nothing, @@ -443,7 +442,7 @@ end """ Configuration for the agent tool execution loop. -# Arguments +# Fields - `beforeToolCall::Union{Function, Nothing}`: Callback before tool execution - `afterToolCall::Union{Function, Nothing}`: Callback after tool execution - `toolExecution::String`: Execution mode — "sequential" or "parallel" @@ -452,7 +451,6 @@ struct agentLoopConfig beforeToolCall::Union{Function, Nothing} afterToolCall::Union{Function, Nothing} toolExecution::String - llmCall::Union{Any, Nothing} # LLM call function (for tools like searchWine) end """ diff --git a/src/utils.jl b/src/utils.jl index 57d9e59..515ee28 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -109,18 +109,18 @@ prepareContext(state).messages == deepcopy(state.messages) # end ``` """ -function prepareContext(state::agentState, eventSink, llmCall=nothing)::agentContext - +function prepareContext(state::agentState, eventSink)::agentContext + #TODO filter tools from state.tools based on user intend in user message and tool description filteredTools = state.tools #TODO add filtered tools to the current system prompt / modify systemPrompt here preparedSystemPrompt = state.systemPrompt - + #TODO add system prompt, adjust/modify and inject additional context into messages preparedMessages = deepcopy(state.messages) # messages that will be send to LLM - agentCtx = agentContext(preparedSystemPrompt, preparedMessages, filteredTools, llmCall) + agentCtx = agentContext(preparedSystemPrompt, preparedMessages, filteredTools) return agentCtx end