This commit is contained in:
2026-08-01 21:38:31 +07:00
parent 368307742e
commit 7c4e84ba93
+265 -106
View File
@@ -11,37 +11,36 @@ using GeneralUtils
# ============================================================================ # ============================================================================
# Message types # Message types
# ============================================================================ # ============================================================================
abstract type agentMessage end abstract type agentMessage end # Base type for all agent messages
struct userMessage <: agentMessage struct userMessage <: agentMessage # Message from the user
role::String role::String # Always "user"
content::Vector{messageContent} content::Vector{messageContent} # Text and/or image content
timestamp::Timestamp timestamp::Timestamp # When the message was sent
end end
struct assistantMessage <: agentMessage struct assistantMessage <: agentMessage # Message from the AI assistant
role::String role::String # Always "assistant"
content::Vector{messageContent} content::Vector{messageContent} # Text and/or image content
api::String api::String # API name used (e.g., "openai")
provider::String provider::String # Provider name (e.g., "anthropic")
model::String model::String # Model identifier
usage::Usage usage::Usage # Token usage for this message
stop_reason::String stopReason::String # Why generation stopped (e.g., "end_turn")
error_message::Union{String, Nothing} errorMessage::Union{String, Nothing} # Error if generation failed
timestamp::Timestamp timestamp::Timestamp # When the message was received
end end
struct toolResultMessage <: agentMessage struct toolResultMessage <: agentMessage # Result returned from a tool execution
role::String # Always "tool"
role::String toolCallId::String # ID matching the tool call
tool_call_id::String toolName::String # Name of the executed tool
tool_name::String content::Vector{messageContent} # Tool output content
content::Vector{messageContent} details::Any # Additional tool-specific details
details::Any usage::Union{Usage, Nothing} # Token usage if applicable
usage::Union{Usage, Nothing} addedToolNames::Union{Vector{String}, Nothing} # Tools added during execution
added_tool_names::Union{Vector{String}, Nothing} isError::Bool # Whether the tool call resulted in an error
is_error::Bool timestamp::Timestamp # When the result was recorded
timestamp::Timestamp
end end
@@ -49,15 +48,15 @@ end
# Message content types # Message content types
# ============================================================================ # ============================================================================
abstract type messageContent end abstract type messageContent end # Base type for message content
struct textContent <: messageContent struct textContent <: messageContent # Plain text message content
text::String text::String # The text content
end end
struct imageContent <: messageContent struct imageContent <: messageContent # Image message content
data::String data::String # Base64-encoded image data
mime_type::String mimeType::String # MIME type (e.g., "image/png")
end end
@@ -65,14 +64,14 @@ end
# Tool types # Tool types
# ============================================================================ # ============================================================================
struct agentTool{TParameters, TDetails} struct agentTool{TParameters, TDetails} # A tool available to the agent
name::String name::String # Tool identifier
label::String label::String # Human-readable tool name
description::String description::String # What the tool does
parameters::TParameters parameters::TParameters # Tool parameters schema (JSON schema)
execute::Function execute::Function # Tool execution function
prepare_arguments::Union{Function, Nothing} prepareArguments::Union{Function, Nothing} # Optional argument preparation callback
execution_mode::Union{ToolExecutionMode, Nothing} executionMode::Union{toolExecutionMode, Nothing} # Override: run tool calls sequentially or in parallel
end end
@@ -80,10 +79,10 @@ end
# Agent context # Agent context
# ============================================================================ # ============================================================================
struct agentContext struct agentContext # Snapshot of the agent's conversation context
system_prompt::String systemPrompt::String # System prompt for the agent
messages::Vector{agentMessage} messages::Vector{agentMessage} # Conversation messages
tools::Union{Vector{agentTool}, Nothing} tools::Union{Vector{agentTool}, Nothing} # Available tools
end end
@@ -92,35 +91,35 @@ end
# Assistant message event types # Assistant message event types
# ============================================================================ # ============================================================================
abstract type assistantMessageEvent end abstract type assistantMessageEvent end # Base type for assistant message streaming events
struct startEvent <: assistantMessageEvent struct startEvent <: assistantMessageEvent # Message generation started
partial::assistantMessage partial::assistantMessage # The partial message at this point
end end
struct textStartEvent <: assistantMessageEvent struct textStartEvent <: assistantMessageEvent # Text content block started
content_index::Int64 contentIndex::Int64 # Index of the content block
partial::assistantMessage partial::assistantMessage # The partial message at this point
end end
struct textDeltaEvent <: assistantMessageEvent struct textDeltaEvent <: assistantMessageEvent # Text content block received a chunk
content_index::Int64 contentIndex::Int64 # Index of the content block
delta::String delta::String # New text chunk
partial::assistantMessage partial::assistantMessage # The partial message at this point
end end
struct textEndEvent <: assistantMessageEvent struct textEndEvent <: assistantMessageEvent # Text content block completed
content_index::Int64 contentIndex::Int64 # Index of the content block
content::String content::String # Complete text content
partial::assistantMessage partial::assistantMessage # The partial message at this point
end end
struct doneEvent <: assistantMessageEvent struct doneEvent <: assistantMessageEvent # Message generation completed successfully
reason::String reason::String # Why generation stopped
usage::Usage usage::Usage # Token usage
message::assistantMessage message::assistantMessage # The completed message
end end
struct errorEvent <: assistantMessageEvent struct errorEvent <: assistantMessageEvent # Message generation encountered an error
reason::String reason::String # Error reason
error_message::Union{String, Nothing} errorMessage::Union{String, Nothing} # Human-readable error
usage::Usage usage::Usage # Token usage (partial)
error::assistantMessage error::assistantMessage # The error message
end end
@@ -129,48 +128,41 @@ end
# Agent state # Agent state
# ============================================================================ # ============================================================================
mutable struct agentState mutable struct agentState # Mutable runtime state of an agent
system_prompt::String systemPrompt::String # System prompt text
model::Model model::llmModel # LLM model to use
thinking_level::ThinkingLevel tools::Vector{agentTool} # Available tools
tools::Vector{agentTool} messages::Vector{agentMessage} # Conversation messages
messages::Vector{agentMessage} pendingToolCalls::Vector{String} # Tool call IDs waiting for results
is_streaming::Bool errorMessage::Union{String, Nothing} # Last error message
streaming_message::Union{agentMessage, Nothing} end
pending_tool_calls::Set{String}
error_message::Union{String, Nothing}
function agentState( function agentState(
system_prompt::String="", systemPrompt::String="",
model::Model=Model("", "", "unknown", "unknown", "", false, String[], ModelCost(0.0, 0.0, 0.0, 0.0), 0, 0), model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[], modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
thinking_level::ThinkingLevel=THINKING_OFF, tools::Vector{agentTool}=agentTool[],
tools::Vector{agentTool}=agentTool[], messages::Vector{agentMessage}=agentMessage[],
messages::Vector{agentMessage}=agentMessage[], )
agentState(
systemPrompt,
model,
deepcopy(tools),
deepcopy(messages),
Vector{String}(),
nothing,
) )
new(
system_prompt,
model,
thinking_level,
copy(tools),
copy(messages),
false,
nothing,
Set{String}(),
nothing,
)
end
end end
# ============================================================================ # ============================================================================
# Tool call types # Tool call types
# ============================================================================ # ============================================================================
struct toolCall struct toolCall # A tool invocation from the LLM
type::String type::String # Always "function"
id::String id::String # Unique tool call identifier
name::String name::String # Tool name
arguments::Dict{String, Any} arguments::Dict{String, Any} # Parsed tool arguments
partial_json::Union{String, Nothing} partialJson::Union{String, Nothing} # Raw JSON string during streaming
end end
@@ -178,12 +170,179 @@ end
# Next turn context # Next turn context
# ============================================================================ # ============================================================================
struct nextTurnContext struct nextTurnContext # Context for preparing the next conversation turn
message::assistantMessage message::assistantMessage # The assistant's message that just completed
tool_results::Vector{toolResultMessage} toolResults::Vector{toolResultMessage} # Tool results from this turn
context::agentContext context::agentContext # Current conversation context
new_messages::Vector{agentMessage} newMessages::Vector{agentMessage} # Messages to append to the context
end
# ============================================================================
# llmModel types
# ============================================================================
struct modelCost # Model pricing per 1M tokens
input::Float64 # Price per 1M input tokens
output::Float64 # Price per 1M output tokens
cache_read::Float64 # Price per 1M cached read tokens
cache_write::Float64 # Price per 1M cache write tokens
end
struct llmModel{Api} # LLM model configuration
id::String # Unique model identifier
name::String # Human-readable model name
api::Api # API type (parametric type)
provider::String # Provider name (e.g., "anthropic", "openai")
baseUrl::String # API endpoint base URL
reasoning::Bool # Whether the model supports chain-of-thought
input::Vector{String} # Supported input modalities (e.g., "text", "image")
cost::modelCost # Pricing information
contextWindow::Int64 # Maximum context length in tokens
maxTokens::Int64 # Maximum output tokens per completion
end
# ============================================================================
# Agent struct
# ============================================================================
mutable struct yiemAgent # High-level agent wrapper
_state::agentState # Current state (prompt, model, messages, tools, etc.)
conn::NATS.Connection # NATS connection for messaging
followUpQueue::pendingMessageQueue # Messages queued via followUp() when agent would stop
formatMsgForLLM::Function # Convert agent messages to LLM message format
preprocessMessages ::Union{Function, Nothing} # Preprocess/transform messages before sending
streamFunction::streamFn # Stream function for streaming responses
getApiKey::Union{Function, Nothing} # Callback to retrieve API key
onPayload::Union{Function, Nothing} # Callback when a payload is sent to the API
onResponse::Union{Function, Nothing} # Callback when a full response is received
beforeToolCall::Union{Function, Nothing} # Callback invoked before executing a tool call
afterToolCall::Union{Function, Nothing} # Callback invoked after executing a tool call
prepareNextTurn::Union{Function, Nothing} # Callback to prepare the next conversation turn
prepareNextTurnWithContext::Union{Function, Nothing} # Same but receives context
activeRun::Union{activeRun, Nothing} # Active run state (promise, abort controller)
sessionId::Union{String, Nothing} # Optional session identifier
thinkingBudgets::Union{Dict{String, Int64}, Nothing} # Per-model thinking token budgets
transport::String # Transport mode ("auto" or explicit)
maxRetryDelayMs::Union{Int64, Nothing} # Maximum delay between retries (ms)
toolExecution::toolExecutionMode # Default: run tool calls sequentially or in parallel
end
# Outer constructor — clean keyword API
function yiemAgent(
; systemPrompt::String="",
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[], modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
thinkingLevel::thinkingLevel=THINKING_OFF,
tools::Vector{agentTool}=agentTool[],
messages::Vector{agentMessage}=agentMessage[],
formatMsgForLLM::Function=defaultformatMsgForLLM,
preprocessMessages ::Union{Function, Nothing}=nothing,
streamFunction::streamFn=getDefaultStreamFn(),
getApiKey::Union{Function, Nothing}=nothing,
onPayload::Union{Function, Nothing}=nothing,
onResponse::Union{Function, Nothing}=nothing,
beforeToolCall::Union{Function, Nothing}=nothing,
afterToolCall::Union{Function, Nothing}=nothing,
prepareNextTurn::Union{Function, Nothing}=nothing,
prepareNextTurnWithContext::Union{Function, Nothing}=nothing,
sessionId::Union{String, Nothing}=nothing,
thinkingBudgets::Union{Dict{String, Int64}, Nothing}=nothing,
transport::String="auto",
maxRetryDelayMs::Union{Int64, Nothing}=nothing,
toolExecution::toolExecutionMode=EXECUTION_PARALLEL,
)
new(
agentState(systemPrompt, model, tools, messages),
Set{Tuple{Function, Ref{Bool}}}(),
pendingMessageQueue(QUEUE_ONE_AT_A_TIME),
pendingMessageQueue(QUEUE_ONE_AT_A_TIME),
formatMsgForLLM,
preprocessMessages ,
streamFunction,
getApiKey,
onPayload,
onResponse,
beforeToolCall,
afterToolCall,
prepareNextTurn,
prepareNextTurnWithContext,
nothing,
sessionId,
thinkingBudgets,
transport,
maxRetryDelayMs,
toolExecution,
)
end end
end # module type end # module type