update
This commit is contained in:
+143
-115
@@ -31,6 +31,23 @@ struct userMessage <: agentMessage # Message from the user
|
||||
timestamp::Timestamp # When the message was sent
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new user message.
|
||||
|
||||
# Arguments
|
||||
- `role::String`: Always "user"
|
||||
- `content::Vector{messageContent}`: Text and/or image content
|
||||
- `timestamp::Timestamp`: When the message was sent
|
||||
|
||||
# Returns
|
||||
- A new `userMessage` instance
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> msg = userMessage(content=[textContent("Hello")])
|
||||
userMessage("user", [textContent("Hello")], DateTime(...))
|
||||
```
|
||||
"""
|
||||
function userMessage(; role="user", content=Vector{messageContent}(), timestamp=now())
|
||||
return userMessage(role, content, timestamp)
|
||||
end
|
||||
@@ -47,6 +64,29 @@ struct assistantMessage <: agentMessage # Message from the AI assistant
|
||||
timestamp::Timestamp # When the message was received
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new assistant message.
|
||||
|
||||
# Arguments
|
||||
- `role::String`: Always "assistant"
|
||||
- `content::Vector{messageContent}`: Text and/or image content
|
||||
- `api::String`: API name used (e.g., "openai")
|
||||
- `provider::String`: Provider name (e.g., "anthropic")
|
||||
- `model::String`: Model identifier
|
||||
- `usage::Usage`: Token usage for this message
|
||||
- `stopReason::String`: Why generation stopped (e.g., "end_turn")
|
||||
- `errorMessage::Union{String, Nothing}`: Error if generation failed
|
||||
- `timestamp::Timestamp`: When the message was received
|
||||
|
||||
# Returns
|
||||
- A new `assistantMessage` instance
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> msg = assistantMessage(content=[textContent("Hello!")], model="gpt-4")
|
||||
assistantMessage("assistant", [textContent("Hello!")], "", "", "gpt-4", ..., "end_turn", nothing, DateTime(...))
|
||||
```
|
||||
"""
|
||||
function assistantMessage(; role="assistant", content=Vector{messageContent}(),
|
||||
api="", provider="", model="", usage=Usage(0, 0), stopReason="end_turn",
|
||||
errorMessage=nothing, timestamp=now())
|
||||
@@ -65,6 +105,29 @@ struct toolResultMessage <: agentMessage # Result returned from a tool execut
|
||||
timestamp::Timestamp # When the result was recorded
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new tool result message.
|
||||
|
||||
# Arguments
|
||||
- `role::String`: Always "tool"
|
||||
- `toolCallId::String`: ID matching the tool call
|
||||
- `toolName::String`: Name of the executed tool
|
||||
- `content::Vector{messageContent}`: Tool output content
|
||||
- `details::Any`: Additional tool-specific details
|
||||
- `usage::Union{Usage, Nothing}`: Token usage if applicable
|
||||
- `addedToolNames::Union{Vector{String}, Nothing}`: Tools added during execution
|
||||
- `isError::Bool`: Whether the tool call resulted in an error
|
||||
- `timestamp::Timestamp`: When the result was recorded
|
||||
|
||||
# Returns
|
||||
- A new `toolResultMessage` instance
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> msg = toolResultMessage(toolCallId="call_123", toolName="search", content=[textContent("results")])
|
||||
toolResultMessage("tool", "call_123", "search", [textContent("results")], nothing, nothing, nothing, false, DateTime(...))
|
||||
```
|
||||
"""
|
||||
function toolResultMessage(; role="tool", toolCallId="", toolName="",
|
||||
content=Vector{messageContent}(), details=nothing, usage=nothing,
|
||||
addedToolNames=nothing, isError=false, timestamp=now())
|
||||
@@ -82,6 +145,21 @@ struct textContent <: messageContent # Plain text message content
|
||||
text::String # The text content
|
||||
end
|
||||
|
||||
"""
|
||||
Create plain text message content.
|
||||
|
||||
# Arguments
|
||||
- `text::String`: The text content
|
||||
|
||||
# Returns
|
||||
- A new `textContent` instance
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> content = textContent("Hello, world!")
|
||||
textContent("Hello, world!")
|
||||
```
|
||||
"""
|
||||
function textContent(; text="")
|
||||
return textContent(text)
|
||||
end
|
||||
@@ -91,6 +169,22 @@ struct imageContent <: messageContent # Image message content
|
||||
mimeType::String # MIME type (e.g., "image/png")
|
||||
end
|
||||
|
||||
"""
|
||||
Create image message content.
|
||||
|
||||
# Arguments
|
||||
- `data::String`: Base64-encoded image data
|
||||
- `mimeType::String`: MIME type (e.g., "image/png")
|
||||
|
||||
# Returns
|
||||
- A new `imageContent` instance
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> img = imageContent(data="base64data...", mimeType="image/png")
|
||||
imageContent("base64data...", "image/png")
|
||||
```
|
||||
"""
|
||||
function imageContent(; data="", mimeType="")
|
||||
return imageContent(data, mimeType)
|
||||
end
|
||||
@@ -135,6 +229,27 @@ mutable struct agentState # Mutable runtime state of an agen
|
||||
errorMessage::Union{String, Nothing} # Last error message
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new mutable agent state.
|
||||
|
||||
Creates a deep copy of the provided tools and messages to isolate the
|
||||
new state from external references.
|
||||
|
||||
# Arguments
|
||||
- `systemPrompt::String`: System prompt text
|
||||
- `model::llmModel`: LLM model to use (defaults to an unknown model)
|
||||
- `tools::Vector{agentTool}`: Available tools (deep copied)
|
||||
- `messages::Vector{agentMessage}`: Conversation messages (deep copied)
|
||||
|
||||
# Returns
|
||||
- A new `agentState` instance with an empty pending tool calls list and no error
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> state = agentState(systemPrompt="You are a helpful assistant")
|
||||
agentState("You are a helpful assistant", ..., agentTool[], agentMessage[], String[], nothing)
|
||||
```
|
||||
"""
|
||||
function agentState(
|
||||
systemPrompt::String="",
|
||||
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[], modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
||||
@@ -237,7 +352,34 @@ mutable struct yiemAgent <: agent # High-level agent wrapper
|
||||
end
|
||||
|
||||
"""
|
||||
docstring
|
||||
Create a new yiemAgent instance with a background loop task.
|
||||
|
||||
Spawns a background `@spawn` task that runs the agent loop, listening
|
||||
on `input_ch` and `followUpQueue` channels concurrently.
|
||||
|
||||
# Keyword Arguments
|
||||
- `systemPrompt::String`: System prompt for the agent
|
||||
- `model`: LLM model to use
|
||||
- `tools::Vector{agentTool}`: Available tools (default: empty)
|
||||
- `messages::Vector{agentMessage}`: Initial conversation messages (default: empty)
|
||||
- `formatMsgForLLM::Function`: Convert agent messages to LLM message format (default: `defaultformatMsgForLLM`)
|
||||
- `preprocessMessages::Union{Function, Nothing}`: Preprocess/transform messages before sending to LLM (default: `nothing`)
|
||||
- `beforeToolCall::Union{Function, Nothing}`: Callback invoked before executing a tool call (default: `nothing`)
|
||||
- `afterToolCall::Union{Function, Nothing}`: Callback invoked after executing a tool call (default: `nothing`)
|
||||
- `prepareNextTurn::Union{Function, Nothing}`: Callback to prepare the next conversation turn (default: `nothing`)
|
||||
- `prepareNextTurnWithContext::Union{Function, Nothing}`: Same but receives context (default: `nothing`)
|
||||
- `sessionId::Union{String, Nothing}`: Optional session identifier (default: `nothing`)
|
||||
- `maxRetryDelayMs::Union{Int64, Nothing}`: Maximum delay between retries in milliseconds (default: `nothing`)
|
||||
- `toolExecution`: Default tool execution mode (sequential or parallel) (default: `nothing`)
|
||||
|
||||
# Returns
|
||||
- A new `yiemAgent` instance with an active background task
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> agent = yiemAgent(systemPrompt="You are a helpful assistant", model=my_model)
|
||||
yiemAgent(agentState(...), Channel(...), Channel(...), Channel(...), ..., ...)
|
||||
```
|
||||
"""
|
||||
function yiemAgent(
|
||||
; systemPrompt::String="",
|
||||
@@ -283,120 +425,6 @@ function yiemAgent(
|
||||
return agent
|
||||
end
|
||||
|
||||
# ============================================================================
|
||||
# Agent loop — runs in background, processes messages from input_ch / followUp
|
||||
# ============================================================================
|
||||
|
||||
"""
|
||||
Private agent loop. Runs in a background @task.
|
||||
Waits on input_ch and followUpQueue concurrently via select().
|
||||
"""
|
||||
function _agent_loop(agent::yiemAgent) #WORKING
|
||||
try
|
||||
while true
|
||||
# Wait on either channel — the one with a message fires first
|
||||
msg = select(agent.input_ch, agent.followUpQueue).val
|
||||
|
||||
# Check for shutdown signal
|
||||
if msg === :shutdown
|
||||
break
|
||||
end
|
||||
|
||||
# Dispatch message through the processing pipeline
|
||||
result = _process_message(agent, msg)
|
||||
|
||||
# Send response to user
|
||||
put!(agent.output_ch, result)
|
||||
end
|
||||
catch e
|
||||
# On any error, send error response and exit the loop
|
||||
@error "Agent loop failed" error=e
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Process a single message through the agent pipeline.
|
||||
This is where you add your LLM call, tool execution, etc.
|
||||
"""
|
||||
function _process_message(agent::yiemAgent, msg)
|
||||
# PENDING Replace with actual processing logic
|
||||
#
|
||||
# 1. Add msg to agent._state.messages
|
||||
# 2. Call agent.formatMsgForLLM(agent._state) to format for LLM
|
||||
# 3. If preprocessMessages is set, call agent.preprocessMessages(...)
|
||||
# 4. Call the LLM (blocking — the task waits here)
|
||||
# 5. If agent has tools, handle tool calls in a loop
|
||||
# 6. Build assistantMessage and return it
|
||||
|
||||
# Placeholder: echo back the message as a simple response
|
||||
@warn "TODO: implement _process_message"
|
||||
return assistantMessage(
|
||||
role="assistant",
|
||||
content=[textContent("Received: $(msg)")],
|
||||
api="", model="", usage=nothing,
|
||||
stopReason="end_turn",
|
||||
errorMessage=nothing,
|
||||
timestamp=now(),
|
||||
)
|
||||
end
|
||||
|
||||
# ============================================================================
|
||||
# Public API — interaction helpers
|
||||
# ============================================================================
|
||||
|
||||
"""
|
||||
Send a message to the agent's input channel.
|
||||
Blocks if the input channel buffer is full (capacity 16 by default).
|
||||
"""
|
||||
function run_agent(agent::yiemAgent, msg)
|
||||
put!(agent.input_ch, msg)
|
||||
return agent
|
||||
end
|
||||
|
||||
"""
|
||||
Take a response from the agent's output channel.
|
||||
Blocks until the agent sends a response.
|
||||
"""
|
||||
function take_response(agent::yiemAgent)
|
||||
return take!(agent.output_ch)
|
||||
end
|
||||
|
||||
"""
|
||||
Send a follow-up message while the agent is still processing.
|
||||
Follow-up messages are processed after all input_ch messages
|
||||
and before any tool call results are sent.
|
||||
"""
|
||||
function follow_up(agent::yiemAgent, msg)
|
||||
put!(agent.followUpQueue, msg)
|
||||
return agent
|
||||
end
|
||||
|
||||
"""
|
||||
Gracefully stop the agent.
|
||||
Sends a :shutdown signal, waits for the task to finish, then closes all channels.
|
||||
"""
|
||||
function stop_agent(agent::yiemAgent)
|
||||
put!(agent.input_ch, :shutdown)
|
||||
try
|
||||
fetch(agent._task)
|
||||
catch e
|
||||
if e isa TaskFailedException
|
||||
rethrow(e)
|
||||
end
|
||||
end
|
||||
close(agent.input_ch)
|
||||
close(agent.output_ch)
|
||||
close(agent.followUpQueue)
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user