update
This commit is contained in:
+4
-3
@@ -526,10 +526,11 @@ function prepareToolCall(
|
||||
prepared = prepareToolCallArguments(tool, toolCall)
|
||||
validatedArgs = validateToolArguments(tool, prepared)
|
||||
|
||||
# 2. beforeToolCall hook — can block
|
||||
#WORKING 2. beforeToolCall hook — can block
|
||||
if config.beforeToolCall !== nothing
|
||||
before = config.beforeToolCall(
|
||||
assistantMsgCtx(assistantMsg, toolCall, validatedArgs, context), signal
|
||||
beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context),
|
||||
signal
|
||||
)
|
||||
if signal !== nothing && signal.aborted
|
||||
return immediateOutcome(createErrorToolResult("Operation aborted"), true)
|
||||
@@ -687,7 +688,7 @@ function finalizeExecutedToolCall(
|
||||
if config.afterToolCall !== nothing
|
||||
try
|
||||
after = config.afterToolCall(
|
||||
afterCtx(assistantMsg, prep.toolCall, prep.args, result, isError, context), signal
|
||||
afterToolCallContext(assistantMsg, prep.toolCall, prep.args, result, isError, context), signal
|
||||
)
|
||||
if after !== nothing
|
||||
result = merge(result, dict(:content=>get(after,:content,result.content),
|
||||
|
||||
+142
-1
@@ -5,11 +5,152 @@ export prompt
|
||||
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
||||
DataFrames
|
||||
using GeneralUtils
|
||||
using ..type, ..utils
|
||||
using ..type, ..utils, ..toolRegistry
|
||||
|
||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||
|
||||
|
||||
"""
|
||||
docstring
|
||||
"""
|
||||
mutable struct yiemAgent <: agent # High-level agent wrapper
|
||||
_state::agentState # Current state (prompt, model, messages, tools, etc.)
|
||||
|
||||
# user sends prompt message to agent. if agent is idle, it process user message right away.
|
||||
# if agent is running, it process user message after the current tool call finished.
|
||||
inputChannel::Channel
|
||||
|
||||
# Buffers messages the user sends while the agent is busy. Processed after all inputChannel
|
||||
# messages are handled and the agent is idle (not using a tool call).
|
||||
followUpChannel::Channel
|
||||
|
||||
# agent sends response message to user after processing all user messages in inputChannel
|
||||
# and all followUp messages.
|
||||
outputChannel::Channel
|
||||
|
||||
_agent_loop::Union{Task, Nothing} # agent loop running in the background
|
||||
|
||||
# Preprocess/transform messages and context (modify, filter, prune, inject context from memory,
|
||||
# reorder, ...) for a single LLM call in _process_message()'s loop.
|
||||
# returns new Vector{agentMessage}
|
||||
prepareContext::Union{Function, Nothing}
|
||||
|
||||
# Convert prepareContext()'s new Vector{agentMessage} to LLM message format
|
||||
formatMsgForLLM::Function
|
||||
|
||||
# Actually invoke the LLM to get a completion response. The LLM response comes back as an
|
||||
# assistantMessage whose content is an array of content blocks.
|
||||
# Each block has a type — "text", "thinking", or "toolCall".
|
||||
# The code filters for type === "toolCall" blocks, then passes them to executeToolCalls().
|
||||
llmCall::Function
|
||||
|
||||
# Callback invoked before executing a tool call (ask for user permission/confirmation/abort, etc..)
|
||||
beforeToolCall::Union{Function, Nothing}
|
||||
|
||||
executeToolCalls::Function # execute tool calls ()
|
||||
|
||||
# Callback invoked after executing a tool call to sanitize tools output so the output is ready
|
||||
# to be converted into toolResults message
|
||||
afterToolCall::Union{Function, Nothing}
|
||||
# prepareNextTurn::Union{Function, Nothing} # Callback to prepare the next conversation turn
|
||||
# prepareNextTurnWithContext::Union{Function, Nothing} # Same but receives context
|
||||
sessionId::Union{String, Nothing} # Optional session identifier
|
||||
maxRetryDelayMs::Union{Int64, Nothing} # Maximum delay between retries (ms)
|
||||
parallelToolExecute::Bool # Default: false
|
||||
agentEventSink::Function # agent emits its status via this function
|
||||
_tool_store::Any # Reference to the toolStore for runtime registration
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new yiemAgent instance with a background loop task.
|
||||
|
||||
Spawns a background `@spawn` task that runs the agent loop, listening
|
||||
on `inputChannel` and `followUpChannel` channels concurrently.
|
||||
|
||||
# Keyword Arguments
|
||||
- `systemPrompt::String`: System prompt for the agent
|
||||
- `model`: LLM model to use
|
||||
- `tools::OrderedDict{String, agentTool}`: Available tools keyed by name (default: empty)
|
||||
- `messages::Vector{agentMessage}`: Initial conversation messages (default: empty)
|
||||
- `formatMsgForLLM::Function`: Convert agent messages to LLM message format (default: `defaultformatMsgForLLM`)
|
||||
- `llmCall::Function`: Function to invoke the LLM (required)
|
||||
- `prepareContext::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`)
|
||||
- `parallelToolExecute::Bool`: Run tool calls in parallel (default: `false`)
|
||||
- `agentEventSink::Function`: Callback to receive agent events
|
||||
- `tool_store::Union{Any, Nothing}`: toolStore for runtime tool registration (default: `nothing`)
|
||||
|
||||
# Returns
|
||||
- A new `yiemAgent` instance with an active background task
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> store = toolStore(name="agent1")
|
||||
julia> tools = loadTools(store, "src/tools")
|
||||
julia> agent = yiemAgent(systemPrompt="You are a helpful assistant", model=my_model, tools=tools, llmCall=..., tool_store=store)
|
||||
yiemAgent(agentState(...), Channel(...), Channel(...), Channel(...), ..., store)
|
||||
"""
|
||||
function yiemAgent(
|
||||
toolsFolderPath::String,
|
||||
llmCall::Function,
|
||||
;
|
||||
systemPrompt::String="You are helpful assistant.",
|
||||
model=nothing,
|
||||
messages::Vector{agentMessage}=agentMessage[],
|
||||
prepareContext::Function=prepareContext,
|
||||
formatMsgForLLM::Function=formatMsgForLLM,
|
||||
beforeToolCall::Function,
|
||||
afterToolCall::Function,
|
||||
# prepareNextTurn::Union{Function, Nothing}=nothing,
|
||||
# prepareNextTurnWithContext::Union{Function, Nothing}=nothing,
|
||||
sessionId::Union{String, Nothing}=nothing,
|
||||
maxRetryDelayMs::Union{Int64, Nothing}=nothing,
|
||||
parallelToolExecute::Bool=false,
|
||||
agentEventSink::Function,
|
||||
tool_store::Union{Any, Nothing}=nothing,
|
||||
)
|
||||
# Create channels: input (user -> agent), followUp (async queue), output (agent -> user)
|
||||
inputChannel = Channel(16)
|
||||
followUp = Channel(32)
|
||||
outputChannel = Channel(16)
|
||||
|
||||
# load tools from toolsFolderPath
|
||||
toolStore = YiemAgent.toolStore(name="myagent")
|
||||
loadTools(toolStore, toolsFolderPath)
|
||||
|
||||
# Create struct with a placeholder task, then spawn and replace it
|
||||
agent = yiemAgent(
|
||||
agentState(systemPrompt, model, getTools(toolStore), messages),
|
||||
inputChannel,
|
||||
followUp,
|
||||
outputChannel,
|
||||
nothing, # placeholder — replaced below
|
||||
prepareContext,
|
||||
formatMsgForLLM,
|
||||
llmCall,
|
||||
beforeToolCall,
|
||||
afterToolCall,
|
||||
# prepareNextTurn,
|
||||
# prepareNextTurnWithContext,
|
||||
sessionId,
|
||||
maxRetryDelayMs,
|
||||
parallelToolExecute,
|
||||
agentEventSink,
|
||||
tool_store,
|
||||
)
|
||||
|
||||
# Spawn the background loop and attach it
|
||||
agent._agent_loop = @spawn _agent_loop(agent)
|
||||
|
||||
return agent
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Send a message to the agent's input channel.
|
||||
|
||||
+9
-9
@@ -633,7 +633,7 @@ function prepareToolCall(
|
||||
- On failure: throws `ArgumentError(error_string)`, caught by the try-catch below
|
||||
|
||||
4. **Run `beforeToolCall` hook** — if `config.beforeToolCall !== nothing`
|
||||
- Passes `assistantMsgCtx(assistantMsg, toolCall, validatedArgs, context)` and `signal`
|
||||
- Passes `beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context)` and `signal`
|
||||
- Hook can return `nothing` (proceed), or `Dict(:block => true, :reason => "...")` (reject)
|
||||
- If `signal.aborted == true` → `immediateOutcome(createErrorToolResult("Operation aborted"), true)`
|
||||
- If `before.block == true` → `immediateOutcome(createErrorToolResult(get(before, :reason, "blocked")), true)`
|
||||
@@ -722,10 +722,10 @@ function finalizeExecutedToolCall(
|
||||
```
|
||||
|
||||
2. **Run `afterToolCall` hook** — if `config.afterToolCall !== nothing`:
|
||||
- Passes `afterCtx(assistantMsg, prep.toolCall, prep.args, result, isError, context)` and `signal`
|
||||
- Passes `afterToolCallContext(assistantMsg, prep.toolCall, prep.args, result, isError, context)` and `signal`
|
||||
- Hook can mutate the result:
|
||||
```julia
|
||||
after = config.afterToolCall(afterCtx(...))
|
||||
after = config.afterToolCall(afterToolCallContext(...))
|
||||
if after !== nothing
|
||||
result = merge(result, dict(
|
||||
:content => get(after, :content, result.content),
|
||||
@@ -1146,8 +1146,8 @@ The `agentEventSink` function is a user-provided callback that receives all even
|
||||
| `prepareContext` | `(state::agentState) -> agentContext` | Before each LLM call | Filter tools, inject context, modify system prompt |
|
||||
| `formatMsgForLLM` | `(ctx::agentContext) -> Dict` | After `prepareContext` | Convert to LLM-specific format |
|
||||
| `llmCall` | `(messages::Dict) -> assistantMessage` | After formatting | Actually invoke the LLM API |
|
||||
| `beforeToolCall` | `(msgCtx::assistantMsgCtx, signal) -> Union{Nothing, Dict}` | In `prepareToolCall` | Ask for user permission, block execution, abort |
|
||||
| `afterToolCall` | `(afterCtx::afterCtx, signal) -> Union{Nothing, Dict}` | In `finalizeExecutedToolCall` | Mutate result, mask data, flip `terminate` |
|
||||
| `beforeToolCall` | `(msgCtx::beforeToolCallContext, signal) -> Union{Nothing, Dict}` | In `prepareToolCall` | Ask for user permission, block execution, abort |
|
||||
| `afterToolCall` | `(afterToolCallContext::afterToolCallContext, signal) -> Union{Nothing, Dict}` | In `finalizeExecutedToolCall` | Mutate result, mask data, flip `terminate` |
|
||||
| `agentEventSink` | `(event) -> nothing` | Throughout lifecycle | Emit events for TUI, logging, monitoring |
|
||||
|
||||
### `beforeToolCall` Hook
|
||||
@@ -1157,7 +1157,7 @@ The `agentEventSink` function is a user-provided callback that receives all even
|
||||
```julia
|
||||
if config.beforeToolCall !== nothing
|
||||
before = config.beforeToolCall(
|
||||
assistantMsgCtx(assistantMsg, toolCall, validatedArgs, context), signal
|
||||
beforeToolCallContext(assistantMsg, toolCall, validatedArgs, context), signal
|
||||
)
|
||||
if signal !== nothing && signal.aborted
|
||||
return immediateOutcome(createErrorToolResult("Operation aborted"), true)
|
||||
@@ -1182,7 +1182,7 @@ end
|
||||
if config.afterToolCall !== nothing
|
||||
try
|
||||
after = config.afterToolCall(
|
||||
afterCtx(assistantMsg, prep.toolCall, prep.args, result, isError, context), signal
|
||||
afterToolCallContext(assistantMsg, prep.toolCall, prep.args, result, isError, context), signal
|
||||
)
|
||||
if after !== nothing
|
||||
result = merge(result, dict(
|
||||
@@ -1606,8 +1606,8 @@ All functions in the file are scoped under `_tool_myTool`, preventing name colli
|
||||
| `agentState` | `type.jl:310` | Mutable runtime state (systemPrompt, model, tools, messages, pendingToolCalls, activeRun, errorMessage) |
|
||||
| `agentLoopConfig` | `type.jl:403` | Loop config (tools, beforeToolCall, afterToolCall, toolExecution) |
|
||||
| `abortSignal` | `type.jl:416` | Abort flag (`aborted::Bool`) |
|
||||
| `assistantMsgCtx` | `type.jl:445` | Context for beforeToolCall (message, toolCall, args, context) |
|
||||
| `afterCtx` | `type.jl:463` | Context for afterToolCall (message, toolCall, args, result, isError, context) |
|
||||
| `beforeToolCallContext` | `type.jl:445` | Context for beforeToolCall (message, toolCall, args, context) |
|
||||
| `afterToolCallContext` | `type.jl:463` | Context for afterToolCall (message, toolCall, args, result, isError, context) |
|
||||
|
||||
### Event Types
|
||||
|
||||
|
||||
+9
-141
@@ -13,8 +13,8 @@
|
||||
# Context types
|
||||
agentContext, agentState, agentToolCall, prepareNextTurnContext,
|
||||
# Loop & execution types
|
||||
agentLoopConfig, abortSignal, agentToolResult,
|
||||
assistantMsgCtx, afterCtx,
|
||||
agentLoopConfig, abortSignal, agentToolResult,beforeToolCallContext,
|
||||
beforeToolCallResult, afterToolCallContext,
|
||||
# Event types
|
||||
toolExecStartEvent, toolExecUpdateEvent, toolExecEndEvent,
|
||||
# Agent
|
||||
@@ -442,13 +442,18 @@ Context passed to the `beforeToolCall` hook.
|
||||
- `args::Dict{String,Any}`: Validated tool arguments
|
||||
- `context::agentContext`: Current conversation context
|
||||
"""
|
||||
struct assistantMsgCtx
|
||||
struct beforeToolCallContext
|
||||
message::assistantMessage
|
||||
toolCall::agentToolCall
|
||||
args::Dict{String,Any}
|
||||
context::agentContext
|
||||
end
|
||||
|
||||
struct beforeToolCallResult
|
||||
block::Bool
|
||||
reason::String
|
||||
end
|
||||
|
||||
"""
|
||||
Context passed to the `afterToolCall` hook.
|
||||
|
||||
@@ -460,7 +465,7 @@ Context passed to the `afterToolCall` hook.
|
||||
- `isError::Bool`: Whether execution resulted in an error
|
||||
- `context::agentContext`: Current conversation context
|
||||
"""
|
||||
struct afterCtx
|
||||
struct afterToolCallContext
|
||||
message::assistantMessage
|
||||
toolCall::agentToolCall
|
||||
args::Dict{String,Any}
|
||||
@@ -521,143 +526,6 @@ end
|
||||
|
||||
abstract type agent end
|
||||
|
||||
"""
|
||||
docstring
|
||||
"""
|
||||
mutable struct yiemAgent <: agent # High-level agent wrapper
|
||||
_state::agentState # Current state (prompt, model, messages, tools, etc.)
|
||||
|
||||
# user sends prompt message to agent. if agent is idle, it process user message right away.
|
||||
# if agent is running, it process user message after the current tool call finished.
|
||||
inputChannel::Channel
|
||||
|
||||
# Buffers messages the user sends while the agent is busy. Processed after all inputChannel
|
||||
# messages are handled and the agent is idle (not using a tool call).
|
||||
followUpChannel::Channel
|
||||
|
||||
# agent sends response message to user after processing all user messages in inputChannel
|
||||
# and all followUp messages.
|
||||
outputChannel::Channel
|
||||
|
||||
_agent_loop::Union{Task, Nothing} # agent loop running in the background
|
||||
|
||||
# Preprocess/transform messages and context (modify, filter, prune, inject context from memory,
|
||||
# reorder, ...) for a single LLM call in _process_message()'s loop.
|
||||
# returns new Vector{agentMessage}
|
||||
prepareContext ::Union{Function, Nothing}
|
||||
|
||||
# Convert prepareContext()'s new Vector{agentMessage} to LLM message format
|
||||
formatMsgForLLM::Function
|
||||
|
||||
# Actually invoke the LLM to get a completion response. The LLM response comes back as an
|
||||
# assistantMessage whose content is an array of content blocks.
|
||||
# Each block has a type — "text", "thinking", or "toolCall".
|
||||
# The code filters for type === "toolCall" blocks, then passes them to executeToolCalls().
|
||||
llmCall::Function
|
||||
|
||||
# Callback invoked before executing a tool call (ask for user permission/confirmation/abort, etc..)
|
||||
beforeToolCall::Union{Function, Nothing}
|
||||
|
||||
executeToolCalls::Function # execute tool calls ()
|
||||
|
||||
# Callback invoked after executing a tool call to sanitize tools output so the output is ready
|
||||
# to be converted into toolResults message
|
||||
afterToolCall::Union{Function, Nothing}
|
||||
# prepareNextTurn::Union{Function, Nothing} # Callback to prepare the next conversation turn
|
||||
# prepareNextTurnWithContext::Union{Function, Nothing} # Same but receives context
|
||||
sessionId::Union{String, Nothing} # Optional session identifier
|
||||
maxRetryDelayMs::Union{Int64, Nothing} # Maximum delay between retries (ms)
|
||||
parallelToolExecute::Bool # Default: false
|
||||
agentEventSink::Function # agent emits its status via this function
|
||||
_tool_store::Any # Reference to the toolStore for runtime registration
|
||||
end
|
||||
|
||||
"""
|
||||
Create a new yiemAgent instance with a background loop task.
|
||||
|
||||
Spawns a background `@spawn` task that runs the agent loop, listening
|
||||
on `inputChannel` and `followUpChannel` channels concurrently.
|
||||
|
||||
# Keyword Arguments
|
||||
- `systemPrompt::String`: System prompt for the agent
|
||||
- `model`: LLM model to use
|
||||
- `tools::OrderedDict{String, agentTool}`: Available tools keyed by name (default: empty)
|
||||
- `messages::Vector{agentMessage}`: Initial conversation messages (default: empty)
|
||||
- `formatMsgForLLM::Function`: Convert agent messages to LLM message format (default: `defaultformatMsgForLLM`)
|
||||
- `llmCall::Function`: Function to invoke the LLM (required)
|
||||
- `prepareContext::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`)
|
||||
- `parallelToolExecute::Bool`: Run tool calls in parallel (default: `false`)
|
||||
- `agentEventSink::Function`: Callback to receive agent events
|
||||
- `tool_store::Union{Any, Nothing}`: toolStore for runtime tool registration (default: `nothing`)
|
||||
|
||||
# Returns
|
||||
- A new `yiemAgent` instance with an active background task
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
julia> store = toolStore(name="agent1")
|
||||
julia> tools = loadTools(store, "src/tools")
|
||||
julia> agent = yiemAgent(systemPrompt="You are a helpful assistant", model=my_model, tools=tools, llmCall=..., tool_store=store)
|
||||
yiemAgent(agentState(...), Channel(...), Channel(...), Channel(...), ..., store)
|
||||
"""
|
||||
function yiemAgent(
|
||||
; systemPrompt::String="You are helpful assistant.",
|
||||
model=nothing,
|
||||
tools::OrderedDict{String, agentTool}=OrderedDict{String, agentTool}(),
|
||||
messages::Vector{agentMessage}=agentMessage[],
|
||||
prepareContext::Union{Function, Nothing}=nothing,
|
||||
formatMsgForLLM::Function=defaultformatMsgForLLM,
|
||||
llmCall::Function,
|
||||
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,
|
||||
maxRetryDelayMs::Union{Int64, Nothing}=nothing,
|
||||
parallelToolExecute::Bool=false,
|
||||
agentEventSink::Function,
|
||||
tool_store::Union{Any, Nothing}=nothing,
|
||||
)
|
||||
# Create channels: input (user -> agent), followUp (async queue), output (agent -> user)
|
||||
inputChannel = Channel(16)
|
||||
followUp = Channel(32)
|
||||
outputChannel = Channel(16)
|
||||
|
||||
# Create struct with a placeholder task, then spawn and replace it
|
||||
agent = yiemAgent(
|
||||
agentState(systemPrompt, model, tools, messages),
|
||||
inputChannel,
|
||||
followUp,
|
||||
outputChannel,
|
||||
nothing, # placeholder — replaced below
|
||||
prepareContext,
|
||||
formatMsgForLLM,
|
||||
llmCall,
|
||||
beforeToolCall,
|
||||
afterToolCall,
|
||||
# prepareNextTurn,
|
||||
# prepareNextTurnWithContext,
|
||||
sessionId,
|
||||
maxRetryDelayMs,
|
||||
parallelToolExecute,
|
||||
agentEventSink,
|
||||
tool_store,
|
||||
)
|
||||
|
||||
# Spawn the background loop and attach it
|
||||
agent._agent_loop = @spawn _agent_loop(agent)
|
||||
|
||||
return agent
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
preparedToolCall(tool, toolCall, args)
|
||||
|
||||
|
||||
+12
-1
@@ -1,6 +1,7 @@
|
||||
module utils
|
||||
|
||||
export clearhistory, availableWineToText, prepareContext, formatMsgForLLM, validateRequiredArgs, validateToolArguments, _userMessageToOpenAI,
|
||||
export clearhistory, availableWineToText, prepareContext, formatMsgForLLM, validateRequiredArgs,
|
||||
validateToolArguments, _userMessageToOpenAI,
|
||||
_assistantMessageToOpenAI, _toolResultMessageToOpenAI, _messageContentToBlocks
|
||||
|
||||
using UUIDs, Dates, DataStructures, HTTP, JSON
|
||||
@@ -219,6 +220,16 @@ function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
||||
end
|
||||
|
||||
|
||||
function beforeToolCall(context::beforeToolCallContext, signal::abortSignal)::beforeToolCallResult
|
||||
# final context check
|
||||
|
||||
# seek user approval via UI
|
||||
|
||||
# other check
|
||||
|
||||
return beforeToolCallResult(false, "N/A")
|
||||
end
|
||||
|
||||
"""
|
||||
Convert a userMessage to OpenAI message format.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user