add yiemAgent
This commit is contained in:
@@ -1,10 +1,28 @@
|
|||||||
# check if this column has vector embedding. if there is one, seach vector version instead
|
|
||||||
column_name_embedding = column_name * "_embedding"
|
|
||||||
if occursin(column_name_embedding, tables_schema[column_name_embedding])
|
|
||||||
vector_column = Dict(
|
using Base.Threads
|
||||||
"table_name"=> table_name,
|
|
||||||
"column_name"=> column_name_embedding,
|
println("Active Julia threads: ", nthreads())
|
||||||
"operator"=> "vector_similarity",
|
|
||||||
"value"=> column_obj["value"]
|
# A CPU-heavy helper function
|
||||||
)
|
function compute_work(id, iterations)
|
||||||
|
println(" [Start] Task $id on Thread #", threadid())
|
||||||
|
|
||||||
|
total = 0.0
|
||||||
|
for i in 1:iterations
|
||||||
|
total += sin(i) * cos(i)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
println(" [Done] Task $id on Thread #", threadid())
|
||||||
|
return total
|
||||||
|
end
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 1. Basic @spawn and fetch
|
||||||
|
# ====================================================================
|
||||||
|
println("\n--- 1. Single Task Spawning ---")
|
||||||
|
|
||||||
|
# Threads.@spawn creates a Task and schedules it onto an available worker thread
|
||||||
|
task1 = Threads.@spawn compute_work("A", 10_000_000)
|
||||||
|
println(typeof(task1))
|
||||||
+152
-15
@@ -1,5 +1,6 @@
|
|||||||
module type
|
module type
|
||||||
export agent, sommelier, companion, virtualcustomer, agentContext
|
export agent, sommelier, companion, virtualcustomer, agentContext, yiemAgent,
|
||||||
|
run_agent, take_response, follow_up, stop_agent
|
||||||
|
|
||||||
|
|
||||||
using Dates, UUIDs, DataStructures, JSON, NATS
|
using Dates, UUIDs, DataStructures, JSON, NATS
|
||||||
@@ -168,6 +169,9 @@ end
|
|||||||
|
|
||||||
abstract type agent end
|
abstract type agent end
|
||||||
|
|
||||||
|
"""
|
||||||
|
docstring
|
||||||
|
"""
|
||||||
mutable struct yiemAgent <: agent # High-level agent wrapper
|
mutable struct yiemAgent <: agent # High-level agent wrapper
|
||||||
_state::agentState # Current state (prompt, model, messages, tools, etc.)
|
_state::agentState # Current state (prompt, model, messages, tools, etc.)
|
||||||
|
|
||||||
@@ -176,13 +180,15 @@ mutable struct yiemAgent <: agent # High-level agent wrapper
|
|||||||
# if agent is running, it process user message after
|
# if agent is running, it process user message after
|
||||||
# the current tool call finished.
|
# the current tool call finished.
|
||||||
|
|
||||||
followUpQueue::Channel # Messages queued via followUp() during agent is
|
followUpQueue::Channel # Messages queued via follow_up() during agent is
|
||||||
# running. After the agent loop process all input_ch
|
# running. After the agent loop process all input_ch
|
||||||
# and the agent isn't use tool call. it then process
|
# and the agent isn't using tool call, it processes
|
||||||
# followUp message
|
# followUp messages
|
||||||
|
|
||||||
output_ch::Channel # agent respond message to user after it process all
|
output_ch::Channel # agent sends response message to user after processing
|
||||||
# user message in input_ch and all followUp message.
|
# all user messages in input_ch and all followUp messages.
|
||||||
|
|
||||||
|
_task::Union{Task, Nothing} # Background task running the agent loop
|
||||||
|
|
||||||
formatMsgForLLM::Function # Convert agent messages to LLM message format
|
formatMsgForLLM::Function # Convert agent messages to LLM message format
|
||||||
preprocessMessages ::Union{Function, Nothing} # Preprocess/transform messages before sending to LLM
|
preprocessMessages ::Union{Function, Nothing} # Preprocess/transform messages before sending to LLM
|
||||||
@@ -190,35 +196,43 @@ mutable struct yiemAgent <: agent # High-level agent wrapper
|
|||||||
afterToolCall::Union{Function, Nothing} # Callback invoked after 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
|
prepareNextTurn::Union{Function, Nothing} # Callback to prepare the next conversation turn
|
||||||
prepareNextTurnWithContext::Union{Function, Nothing} # Same but receives context
|
prepareNextTurnWithContext::Union{Function, Nothing} # Same but receives context
|
||||||
activeRun::Union{Bool, Nothing} # tracks the currently executing agent run state
|
|
||||||
sessionId::Union{String, Nothing} # Optional session identifier
|
sessionId::Union{String, Nothing} # Optional session identifier
|
||||||
maxRetryDelayMs::Union{Int64, Nothing} # Maximum delay between retries (ms)
|
maxRetryDelayMs::Union{Int64, Nothing} # Maximum delay between retries (ms)
|
||||||
toolExecution::toolExecutionMode # Default: run tool calls sequentially or in parallel
|
toolExecution::toolExecutionMode # Default: run tool calls sequentially or in parallel
|
||||||
end
|
end
|
||||||
|
|
||||||
# Outer constructor — clean keyword API
|
"""
|
||||||
|
docstring
|
||||||
|
"""
|
||||||
function yiemAgent(
|
function yiemAgent(
|
||||||
; systemPrompt::String="",
|
; systemPrompt::String="",
|
||||||
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[], modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
model=nothing,
|
||||||
tools::Vector{agentTool}=agentTool[],
|
tools::Vector{agentTool}=agentTool[],
|
||||||
messages::Vector{agentMessage}=agentMessage[],
|
messages::Vector{agentMessage}=agentMessage[],
|
||||||
formatMsgForLLM::Function=defaultformatMsgForLLM,
|
formatMsgForLLM::Function=defaultformatMsgForLLM,
|
||||||
preprocessMessages ::Union{Function, Nothing}=nothing,
|
preprocessMessages::Union{Function, Nothing}=nothing,
|
||||||
beforeToolCall::Union{Function, Nothing}=nothing,
|
beforeToolCall::Union{Function, Nothing}=nothing,
|
||||||
afterToolCall::Union{Function, Nothing}=nothing,
|
afterToolCall::Union{Function, Nothing}=nothing,
|
||||||
prepareNextTurn::Union{Function, Nothing}=nothing,
|
prepareNextTurn::Union{Function, Nothing}=nothing,
|
||||||
prepareNextTurnWithContext::Union{Function, Nothing}=nothing,
|
prepareNextTurnWithContext::Union{Function, Nothing}=nothing,
|
||||||
sessionId::Union{String, Nothing}=nothing,
|
sessionId::Union{String, Nothing}=nothing,
|
||||||
maxRetryDelayMs::Union{Int64, Nothing}=nothing,
|
maxRetryDelayMs::Union{Int64, Nothing}=nothing,
|
||||||
toolExecution::toolExecutionMode=EXECUTION_PARALLEL,
|
toolExecution=nothing,
|
||||||
)
|
)
|
||||||
new(
|
# Create channels: input (user -> agent), followUp (async queue), output (agent -> user)
|
||||||
|
input_ch = Channel(16)
|
||||||
|
followUp = Channel(32)
|
||||||
|
output_ch = Channel(16)
|
||||||
|
|
||||||
|
# Create struct with a placeholder task, then spawn and replace it
|
||||||
|
agent = yiemAgent(
|
||||||
agentState(systemPrompt, model, tools, messages),
|
agentState(systemPrompt, model, tools, messages),
|
||||||
Channel(16),
|
input_ch,
|
||||||
|
followUp,
|
||||||
|
output_ch,
|
||||||
|
nothing, # placeholder — replaced below
|
||||||
formatMsgForLLM,
|
formatMsgForLLM,
|
||||||
preprocessMessages,
|
preprocessMessages,
|
||||||
onPayload,
|
|
||||||
onResponse,
|
|
||||||
beforeToolCall,
|
beforeToolCall,
|
||||||
afterToolCall,
|
afterToolCall,
|
||||||
prepareNextTurn,
|
prepareNextTurn,
|
||||||
@@ -227,6 +241,129 @@ function yiemAgent(
|
|||||||
maxRetryDelayMs,
|
maxRetryDelayMs,
|
||||||
toolExecution,
|
toolExecution,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Spawn the background loop and attach it
|
||||||
|
agent._task = @spawn _agent_loop(agent)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
try
|
||||||
|
put!(agent.output_ch, assistantMessage(
|
||||||
|
role="assistant",
|
||||||
|
content=[textContent("Agent error: $(sprint(showerror, e))")],
|
||||||
|
api="", model="", usage=nothing,
|
||||||
|
stopReason="error",
|
||||||
|
errorMessage=strip(sprint(showerror, e)),
|
||||||
|
timestamp=now(),
|
||||||
|
))
|
||||||
|
catch e2
|
||||||
|
@error "Failed to send error response" error=e2
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
# TODO: 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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user