text message process works
This commit is contained in:
+23
-6
@@ -227,8 +227,11 @@ function _agentLoop(agent::yiemAgent)
|
||||
else # _processMessage() done and no followUp message.
|
||||
agent.agentEventSink("_agentLoop 4 agent._state.messages length $(length(agent._state.messages))")
|
||||
result = agent._state.messages[end]
|
||||
filtered_content = [c for c in result.content if !(c isa reasoningContent)]
|
||||
put!(agent.outputChannel, result)
|
||||
agent.agentEventSink(result.content[1].text)
|
||||
if !isempty(filtered_content) && filtered_content[1] isa textContent
|
||||
agent.agentEventSink(filtered_content[1].text)
|
||||
end
|
||||
processingTask = nothing # reset
|
||||
newUserMsg = nothing # reset
|
||||
result = nothing # reset
|
||||
@@ -381,7 +384,7 @@ function _processMessage(
|
||||
# Extract tool calls from LLM response content blocks
|
||||
hasToolCalls, toolCallList, assistant_msg = _extractToolCalls(response)
|
||||
agentEventSink("hasToolCalls: $hasToolCalls\ntoolCallList: $toolCallList _state.messages length $(length(agentMsgHistory))")
|
||||
agentEventSink(string(assistant_msg))
|
||||
agentEventSink("assistant_msg " * string(assistant_msg))
|
||||
agentEventSink("_processMessage 11-1")
|
||||
|
||||
# Add assistant message (tool calls or text) to history for next LLM turn
|
||||
@@ -406,6 +409,7 @@ function _processMessage(
|
||||
|
||||
# save toolResults to messages
|
||||
for toolResult in toolResultBatch.messages
|
||||
agentEventSink("toolResult " * string(toolResult))
|
||||
push!(agentMsgHistory, toolResult)
|
||||
end
|
||||
agentEventSink("_processMessage 14 _state.messages length $(length(agentMsgHistory))")
|
||||
@@ -554,7 +558,9 @@ The message is constructed from:
|
||||
`(hasToolCalls, toolCallList, message)` where message is `assistantMessageToolCall`
|
||||
when tool calls exist, `assistantMessage` otherwise
|
||||
|
||||
# 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")]))
|
||||
# Example
|
||||
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"))
|
||||
"""
|
||||
function _extractToolCalls(response)
|
||||
hasToolCalls = false
|
||||
@@ -642,14 +648,15 @@ function _extractToolCalls(response)
|
||||
end
|
||||
if reasoning_text isa String
|
||||
reasoning_text = reasoning_text
|
||||
elseif reasoning_text isa textContent
|
||||
elseif reasoning_text isa reasoningContent
|
||||
reasoning_text = reasoning_text.text
|
||||
else
|
||||
reasoning_text = ""
|
||||
end
|
||||
reasoning_content = !isempty(reasoning_text) ? [textContent(reasoning_text)] : textContent[]
|
||||
reasoning_content = !isempty(reasoning_text) ? [reasoningContent(reasoning_text)] : reasoningContent[]
|
||||
|
||||
# Collect content blocks from response.content array (Format 2: OpenAI-style)
|
||||
# or as a plain string (Format 1: LMStudio.jl non-tool-calls response)
|
||||
content_from_response = get(response, "content", nothing)
|
||||
content_blocks = Vector{messageContent}()
|
||||
if content_from_response isa Vector
|
||||
@@ -662,12 +669,22 @@ function _extractToolCalls(response)
|
||||
elseif get(block, "type", "") == "tool_calls"
|
||||
# tool_calls blocks — don't add text content for these
|
||||
elseif get(block, "type", "") == "reasoning"
|
||||
push!(content_blocks, textContent(get(block, "text", "")))
|
||||
push!(content_blocks, reasoningContent(get(block, "text", "")))
|
||||
else
|
||||
push!(content_blocks, textContent(get(block, "text", "")))
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif content_from_response isa AbstractString && !isempty(content_from_response)
|
||||
push!(content_blocks, textContent(content_from_response))
|
||||
end
|
||||
|
||||
# Format 1 fallback: response["message"]["content"] as plain string
|
||||
if isempty(content_blocks) && msg !== nothing && msg isa AbstractDict
|
||||
msg_content = get(msg, "content", nothing)
|
||||
if msg_content isa AbstractString && !isempty(msg_content)
|
||||
push!(content_blocks, textContent(msg_content))
|
||||
end
|
||||
end
|
||||
|
||||
# Combine reasoning_content field + content array blocks, deduplicating reasoning
|
||||
|
||||
+13
-10
@@ -4,8 +4,8 @@
|
||||
messageContent, agentMessage, agent,
|
||||
# Model types
|
||||
modelCost, llmModel, llmUsage,
|
||||
# Message content types
|
||||
textContent, imageContent,
|
||||
# Message content types
|
||||
textContent, imageContent, reasoningContent,
|
||||
# Message types
|
||||
userMessage, assistantMessageToolCall, assistantMessage, toolResultMessage,
|
||||
# Tool types
|
||||
@@ -31,6 +31,13 @@ using GeneralUtils
|
||||
|
||||
const Timestamp = DateTime
|
||||
|
||||
struct agentToolCall # A tool invocation from the LLM
|
||||
type::String # Always "function"
|
||||
id::String # Unique tool call identifier
|
||||
name::String # Tool name
|
||||
arguments::Dict{String, Any} # Parsed tool arguments
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------------------ #
|
||||
# LLM model info #
|
||||
# ------------------------------------------------------------------------------------------------ #
|
||||
@@ -75,6 +82,10 @@ struct imageContent <: messageContent # Image message content
|
||||
mimeType::String # MIME type (e.g., "image/png")
|
||||
end
|
||||
|
||||
struct reasoningContent <: messageContent # LLM reasoning/thinking content
|
||||
text::String # The reasoning text
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------------ #
|
||||
# Message types #
|
||||
@@ -404,14 +415,6 @@ function agentState(
|
||||
end
|
||||
|
||||
|
||||
struct agentToolCall # A tool invocation from the LLM
|
||||
type::String # Always "function"
|
||||
id::String # Unique tool call identifier
|
||||
name::String # Tool name
|
||||
arguments::Dict{String, Any} # Parsed tool arguments
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Context for preparing the next conversation turn.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import YiemAgent.agentCore: _extractToolCalls
|
||||
@test assistant_msg.role == "assistant"
|
||||
@test assistant_msg.stopReason == "tool_calls"
|
||||
@test length(assistant_msg.content) == 1
|
||||
@test assistant_msg.content[1] isa textContent
|
||||
@test assistant_msg.content[1] isa reasoningContent
|
||||
@test assistant_msg.content[1].text == "Let me check the weather."
|
||||
end
|
||||
|
||||
@@ -241,7 +241,9 @@ import YiemAgent.agentCore: _extractToolCalls
|
||||
@test has_toolcalls == false
|
||||
@test length(tc_list) == 0
|
||||
@test length(assistant_msg.content) == 2
|
||||
@test assistant_msg.content[1] isa reasoningContent
|
||||
@test assistant_msg.content[1].text == "Thinking..."
|
||||
@test assistant_msg.content[2] isa textContent
|
||||
@test assistant_msg.content[2].text == "Here's the answer."
|
||||
end
|
||||
|
||||
@@ -307,7 +309,9 @@ import YiemAgent.agentCore: _extractToolCalls
|
||||
)
|
||||
has_toolcalls, tc_list, assistant_msg = _extractToolCalls(response)
|
||||
@test length(assistant_msg.content) == 2
|
||||
@test assistant_msg.content[1] isa reasoningContent
|
||||
@test assistant_msg.content[1].text == "internal thought"
|
||||
@test assistant_msg.content[2] isa textContent
|
||||
@test assistant_msg.content[2].text == "output"
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user