update
This commit is contained in:
+15
-8
@@ -3,7 +3,7 @@ module agentCore
|
|||||||
export yiemAgent, _agentLoop, OpenAiToUserMessage
|
export yiemAgent, _agentLoop, OpenAiToUserMessage
|
||||||
|
|
||||||
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
||||||
DataFrames, Base.Threads
|
DataFrames, Base.Threads, NATS
|
||||||
using GeneralUtils
|
using GeneralUtils
|
||||||
using ..type, ..utils, ..toolRegistry
|
using ..type, ..utils, ..toolRegistry
|
||||||
|
|
||||||
@@ -342,7 +342,6 @@ function _processMessage(
|
|||||||
agentEventSink("_processMessage 1")
|
agentEventSink("_processMessage 1")
|
||||||
# loop until llmCall() response didn't use tool calls
|
# loop until llmCall() response didn't use tool calls
|
||||||
final_response = nothing
|
final_response = nothing
|
||||||
while true
|
|
||||||
|
|
||||||
""" example message in inputChannel
|
""" example message in inputChannel
|
||||||
Dict(
|
Dict(
|
||||||
@@ -357,6 +356,10 @@ function _processMessage(
|
|||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
while true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 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)
|
||||||
agentEventSink("_processMessage 2")
|
agentEventSink("_processMessage 2")
|
||||||
@@ -377,22 +380,26 @@ function _processMessage(
|
|||||||
# call prepareContext()
|
# call prepareContext()
|
||||||
state = agentState(systemPrompt, nothing, tools, messages)
|
state = agentState(systemPrompt, nothing, tools, messages)
|
||||||
agentEventSink("_processMessage 8")
|
agentEventSink("_processMessage 8")
|
||||||
preparedContext = prepareContext(state)
|
preparedContext = prepareContext(state, agentEventSink)
|
||||||
agentEventSink("_processMessage 8")
|
agentEventSink("_processMessage 8")
|
||||||
# Call formatMessagesForLLM() to format for LLM
|
# Call formatMessagesForLLM() to format for LLM
|
||||||
formattedMessages = formatMessagesForLLM(preparedContext)
|
formattedMessages = formatMessagesForLLM(preparedContext, agentEventSink)
|
||||||
|
|
||||||
agentEventSink("_processMessage 10")
|
agentEventSink("_processMessage 10")
|
||||||
# Call llmCall() (blocking — the task waits here)
|
# Call llmCall() (blocking — the task waits here)
|
||||||
response = llmCall(formattedMessages)
|
response = llmCall(formattedMessages)
|
||||||
agentEventSink(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")]))
|
||||||
|
"""
|
||||||
|
|
||||||
|
agentEventSink(string(response))
|
||||||
agentEventSink("_processMessage 11")
|
agentEventSink("_processMessage 11")
|
||||||
error("debug marker")
|
#WORKING Check if LLM used tool calls (inspect content for tool_call blocks)
|
||||||
# Check if LLM used tool calls (inspect content for tool_call blocks)
|
|
||||||
hasToolCalls = false
|
hasToolCalls = false
|
||||||
toolCallList = agentToolCall[]
|
toolCallList = agentToolCall[]
|
||||||
|
|
||||||
for content_block in response.content
|
for content_block in response.content # extract response
|
||||||
if content_block isa Dict
|
if content_block isa Dict
|
||||||
if get(content_block, :type, "") == "tool_calls"
|
if get(content_block, :type, "") == "tool_calls"
|
||||||
hasToolCalls = true
|
hasToolCalls = true
|
||||||
|
|||||||
+2
-2
@@ -291,7 +291,7 @@ Snapshot of the agent's conversation context.
|
|||||||
# Arguments
|
# Arguments
|
||||||
- `systemPrompt::String`: System prompt for the agent
|
- `systemPrompt::String`: System prompt for the agent
|
||||||
- `messages::Vector{agentMessage}`: Conversation messages
|
- `messages::Vector{agentMessage}`: Conversation messages
|
||||||
- `tools::Union{Dict{String, agentTool}, Nothing}`: Available tools keyed by name for O(1) lookup
|
- `tools::Union{OrderedDict{String, agentTool}, Nothing}`: Available tools keyed by name for O(1) lookup
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
- A new `agentContext` instance
|
- A new `agentContext` instance
|
||||||
@@ -299,7 +299,7 @@ Snapshot of the agent's conversation context.
|
|||||||
struct agentContext # Snapshot of the agent's conversation context
|
struct agentContext # Snapshot of the agent's conversation context
|
||||||
systemPrompt::String # System prompt for the agent
|
systemPrompt::String # System prompt for the agent
|
||||||
messages::Vector{agentMessage} # Conversation messages
|
messages::Vector{agentMessage} # Conversation messages
|
||||||
tools::Union{Dict{String, agentTool}, Nothing} # Available tools keyed by name
|
tools::Union{OrderedDict{String, agentTool}, Nothing} # Available tools keyed by name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+53
-20
@@ -2,10 +2,10 @@ module utils
|
|||||||
|
|
||||||
export clearhistory, availableWineToText, prepareContext, formatMsgForLLM, validateRequiredArgs,
|
export clearhistory, availableWineToText, prepareContext, formatMsgForLLM, validateRequiredArgs,
|
||||||
validateToolArguments, _userMessageToOpenAI,
|
validateToolArguments, _userMessageToOpenAI,
|
||||||
_assistantMessageToOpenAI, _toolResultMessageToOpenAI, _messageContentToBlocks,
|
_assistantMessageToOpenAI, _toolResultMessageToOpenAI, _messageContentToBlocks, _toolsToOpenAI,
|
||||||
beforeToolCall, afterToolCall, agentEventSink
|
beforeToolCall, afterToolCall, agentEventSink
|
||||||
|
|
||||||
using UUIDs, Dates, DataStructures, HTTP, JSON
|
using UUIDs, Dates, DataStructures, HTTP, JSON, NATS
|
||||||
using GeneralUtils
|
using GeneralUtils
|
||||||
using ..type
|
using ..type
|
||||||
|
|
||||||
@@ -75,7 +75,6 @@ function availableWineToText(vecd::Vector)::String
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
prepareContext(state::agentState) -> agentContext
|
prepareContext(state::agentState) -> agentContext
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ prepareContext(state).messages == deepcopy(state.messages)
|
|||||||
# end
|
# end
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function prepareContext(state::agentState)::agentContext
|
function prepareContext(state::agentState, agentEventSink)::agentContext
|
||||||
|
|
||||||
#TODO filter tools from state.tools based on user intend in user message and tool description
|
#TODO filter tools from state.tools based on user intend in user message and tool description
|
||||||
filteredTools = state.tools
|
filteredTools = state.tools
|
||||||
@@ -157,7 +156,7 @@ formatMsgForLLm(ctx) == Dict("messages" => [
|
|||||||
])
|
])
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
function formatMsgForLLM(ctx::agentContext, agentEventSink)::Dict{String, Any}
|
||||||
|
|
||||||
""" openai message format example
|
""" openai message format example
|
||||||
msg = Dict(
|
msg = Dict(
|
||||||
@@ -185,18 +184,12 @@ function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
|||||||
Dict("type" => "text", "text" => "let me check."),
|
Dict("type" => "text", "text" => "let me check."),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
Dict(
|
|
||||||
"role" => "toolResult",
|
|
||||||
"content" => [
|
|
||||||
Dict("type" => "text", "text" => "name: Chateau Montelena ..."),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
"tools"=> [
|
"tools"=> [
|
||||||
Dict(
|
Dict(
|
||||||
"type" => "function",
|
"type" => "function",
|
||||||
"function" => Dict(
|
"function" => Dict(
|
||||||
"name" => "get_weather",
|
"name" => "getWeather",
|
||||||
"description" => "Get current weather",
|
"description" => "Get current weather",
|
||||||
"parameters" => Dict(
|
"parameters" => Dict(
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
@@ -212,8 +205,10 @@ function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
|||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
openaiReadyMsg = Dict{String, Any}()
|
||||||
|
# openaiReadyMsg["model"] = "gemma-4-E4B-it-UD-Q4_K_XL"
|
||||||
messages = Vector{Dict{String, Any}}()
|
messages = Vector{Dict{String, Any}}()
|
||||||
|
agentEventSink("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(
|
||||||
@@ -221,7 +216,7 @@ function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
|||||||
"content" => [Dict("type" => "text", "text" => ctx.systemPrompt)]
|
"content" => [Dict("type" => "text", "text" => ctx.systemPrompt)]
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
agentEventSink("formatMsgForLLM 2")
|
||||||
# Conversation messages
|
# Conversation messages
|
||||||
for msg in ctx.messages
|
for msg in ctx.messages
|
||||||
if msg isa userMessage
|
if msg isa userMessage
|
||||||
@@ -232,14 +227,18 @@ function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
|||||||
push!(messages, _toolResultMessageToOpenAI(msg))
|
push!(messages, _toolResultMessageToOpenAI(msg))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
agentEventSink("formatMsgForLLM 3")
|
||||||
|
# Convert ctx.tools into OpenAI tools format
|
||||||
|
tools_array = _toolsToOpenAI(ctx.tools, agentEventSink)
|
||||||
|
agentEventSink("formatMsgForLLM 4")
|
||||||
|
openaiReadyMsg["messages"] = messages
|
||||||
|
openaiReadyMsg["temperature"] = 0.7
|
||||||
|
|
||||||
#WORKING convert ctx.tools into openai's tools format.
|
if !isempty(tools_array)
|
||||||
# ctx.tools has the following format
|
openaiReadyMsg["tools"] = tools_array
|
||||||
# tools = OrderedDict{String, YiemAgent.type.agentTool}("getTime" => YiemAgent.type.agentTool("getTime", "Time Lookup", "Get current local time for a timezone or city.", Dict{String, Any}("properties" => Dict("city" => Dict("type" => "string", "description" => "City name as fallback"), "timezone" => Dict("type" => "string", "description" => "IANA timezone, e.g. 'America/New_York'")), "required" => Any[], "type" => "object"), YiemAgent.toolRegistry._tool_getTime.executeTool, nothing, YiemAgent.toolRegistry._tool_getTime.validateRequiredArgs, false), "getWeather" => YiemAgent.type.agentTool("getWeather", "Weather Lookup", "Fetch current weather and forecast for a given city.", Dict{String, Any}("properties" => Dict{String, Dict{String}}("units" => Dict{String, Any}("default" => "celsius", "type" => "string", "description" => "Temperature scale", "enum" => ["celsius", "fahrenheit"]), "city" => Dict("type" => "string", "description" => "City and country, e.g., 'San Francisco, CA' or 'Tokyo, Japan'")), "required" => ["city"], "type" => "object"), YiemAgent.toolRegistry._tool_getWeather.executeTool, nothing, nothing, false), "listTools" => YiemAgent.type.agentTool("listTools", "List Tools", "List all available tools with their names, labels, and descriptions. Use this before creating a new tool to check for name collisions.", Dict{String, Any}("properties" => Dict{String, Any}(), "required" => Any[], "type" => "object"), YiemAgent.toolRegistry.var"#listTool##0#listTool##1"{YiemAgent.toolRegistry.toolStore}(YiemAgent.toolRegistry.toolStore(OrderedDict{String, YiemAgent.type.agentTool}(#= circular reference @-4 =#), "myagent")), nothing, nothing, false))
|
end
|
||||||
|
|
||||||
|
return openaiReadyMsg
|
||||||
|
|
||||||
return Dict("messages" => messages)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#TODO
|
#TODO
|
||||||
@@ -331,6 +330,40 @@ function _messageContentToBlocks(contents::Vector{messageContent})::Vector{Dict{
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
_toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}) -> Vector{Dict{String, Any}}
|
||||||
|
|
||||||
|
Convert an OrderedDict of agentTool definitions into OpenAI function tool format.
|
||||||
|
|
||||||
|
Returns an empty vector when `tools` is `nothing` or empty.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
```julia
|
||||||
|
_toolsToOpenAI(nothing) # => Dict{String, Any}[]
|
||||||
|
_toolsToOpenAI(tools) # => [Dict("type" => "function", "function" => Dict("name" => "getWeather", ...))]
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
function _toolsToOpenAI(tools::Union{OrderedDict{String, agentTool}, Nothing}, agentEventSink)::Vector{Dict{String, Any}}
|
||||||
|
tools_array = Vector{Dict{String, Any}}()
|
||||||
|
agentEventSink("_toolsToOpenAI 1")
|
||||||
|
agentEventSink(string(typeof(tools)))
|
||||||
|
if tools !== nothing
|
||||||
|
for (_, tool) in tools
|
||||||
|
push!(tools_array, Dict(
|
||||||
|
"type" => "function",
|
||||||
|
"function" => Dict(
|
||||||
|
"name" => tool.name,
|
||||||
|
"description" => tool.description,
|
||||||
|
"parameters" => tool.inputSchema
|
||||||
|
)
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
agentEventSink("_toolsToOpenAI 2")
|
||||||
|
return tools_array
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
validateRequiredArgs(args::Dict{String,Any}, inputSchema::Dict{String,Any}) -> Union{Nothing,String}
|
validateRequiredArgs(args::Dict{String,Any}, inputSchema::Dict{String,Any}) -> Union{Nothing,String}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user