update
This commit is contained in:
+104
-9
@@ -96,7 +96,7 @@ or pruning and reordering messages before formatting and calling the LLM.
|
||||
messages, and tools to be sent to the LLM
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
```jldoctest
|
||||
# Default: returns an agentContext with deep copies of system prompt, messages, and tools
|
||||
prepareContext(state).messages == deepcopy(state.messages)
|
||||
|
||||
@@ -146,7 +146,7 @@ LLM message formats for different APIs/providers.
|
||||
an array of OpenAI-format message dicts
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
```jldoctest
|
||||
# Default output:
|
||||
formatMsgForLLm(ctx) == Dict("messages" => [
|
||||
Dict("role" => "system", "content" => [...]),
|
||||
@@ -265,7 +265,7 @@ Common use cases:
|
||||
- `nothing` is treated as allow (equivalent to `beforeToolCallResult(false, "N/A")`).
|
||||
|
||||
# Example
|
||||
```julia
|
||||
```jldoctest
|
||||
function beforeToolCall(context::beforeToolCallContext, signal::abortSignal)
|
||||
if context.toolCall.name == "deleteFile"
|
||||
# Block file deletion unless explicitly approved
|
||||
@@ -320,11 +320,106 @@ function afterToolCall(context::afterToolCallContext, signal::abortSignal
|
||||
end
|
||||
|
||||
|
||||
#TODO
|
||||
function eventSink(x)
|
||||
|
||||
"""
|
||||
Create an event sink for logging messages and/or publishing to NATS.
|
||||
|
||||
# Fields
|
||||
- `natsConn`: NATS connection, or `nothing` for file-only sink
|
||||
- `topic`: NATS topic to publish to, or `nothing` for file-only sink
|
||||
- `service_name`: Optional prefix added to log entries
|
||||
- `log_file`: Path to the log file
|
||||
- `max_log_entries`: Maximum number of entries to keep in the log file
|
||||
|
||||
# Callable interface
|
||||
```julia
|
||||
(es::eventSink)(msg::String; log=true, publish=true) -> Nothing
|
||||
```
|
||||
Write a timestamped log entry to file, publish to NATS, or both.
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
using NATS
|
||||
|
||||
conn = NATS.connect()
|
||||
|
||||
# With NATS — logs to file and publishes to "agent.events"
|
||||
es = eventSink(natsConn=conn, topic="agent.events", service_name="agent1")
|
||||
es("agent started") # both file + NATS (default)
|
||||
es("task done"; log=false) # NATS only
|
||||
es("checkpoint"; publish=false) # file only
|
||||
|
||||
# File only — no NATS connection
|
||||
es2 = eventSink(log_file="./agent.log", service_name="worker")
|
||||
es2("ready") # file only
|
||||
|
||||
NATS.drain(conn)
|
||||
```
|
||||
"""
|
||||
struct eventSink
|
||||
natsConn::Union{NATS.Connection, Nothing}
|
||||
topic::Union{String, Nothing}
|
||||
service_name::String
|
||||
log_file::String
|
||||
max_log_entries::Int
|
||||
end
|
||||
|
||||
eventSink(; natsConn=nothing, topic=nothing, service_name="", log_file="./log/eventSink.md", max_log_entries=100) =
|
||||
eventSink(natsConn, topic, service_name, log_file, max_log_entries)
|
||||
|
||||
"""
|
||||
rotate!(es::eventSink) -> Nothing
|
||||
|
||||
Rewrite the log file keeping only the last `max_log_entries` entries.
|
||||
Call after writing when the entry count reaches the limit.
|
||||
"""
|
||||
function rotate!(es::eventSink)
|
||||
content = read(es.log_file, String)
|
||||
entries = split(content, r"- \[", keepempty=false)
|
||||
open(es.log_file, "w") do f
|
||||
write(f, "- [")
|
||||
for e in entries[2:end]
|
||||
write(f, "- [", e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
(es::eventSink)(msg::String; log=true, publish=true) -> Nothing
|
||||
|
||||
Write a timestamped log entry to file, publish to NATS, or both.
|
||||
|
||||
# Arguments
|
||||
- `msg`: The message to log or publish.
|
||||
- `log`: If `true`, append the entry to `es.log_file` (default `true`).
|
||||
- `publish`: If `true`, publish to the NATS topic (default `true`; no-op if no NATS connection was provided).
|
||||
"""
|
||||
function (es::eventSink)(msg::String; log::Bool=true, publish::Bool=true)
|
||||
timestamp = Dates.format(now(), "yyyy-mm-ddTHH:MM:SS.sss")
|
||||
svc_prefix = isempty(es.service_name) ? "" : "[$(es.service_name)] "
|
||||
entry = "- [$timestamp] $svc_prefix$msg\n"
|
||||
if log
|
||||
log_dir = dirname(es.log_file)
|
||||
mkpath(log_dir)
|
||||
open(es.log_file, "a") do f
|
||||
write(f, entry)
|
||||
end
|
||||
# Rotate after write — keep only last max_log_entries entries
|
||||
content = read(es.log_file, String)
|
||||
entries = split(content, r"- \[", keepempty=false)
|
||||
if length(entries) > es.max_log_entries
|
||||
rotate!(es)
|
||||
end
|
||||
end
|
||||
if publish && es.topic !== nothing && es.natsConn !== nothing
|
||||
try
|
||||
NATS.publish(es.natsConn, es.topic, entry)
|
||||
catch e
|
||||
@warn "eventSink NATS publish failed" exception=e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Convert a userMessage to OpenAI message format.
|
||||
@@ -432,7 +527,7 @@ Convert an OrderedDict of agentTool definitions into OpenAI function tool format
|
||||
Returns an empty vector when `tools` is `nothing` or empty.
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
```jldoctest
|
||||
_toolsToOpenAI(nothing) # => Dict{String, Any}[]
|
||||
_toolsToOpenAI(tools) # => [Dict("type" => "function", "function" => Dict("name" => "getWeather", ...))]
|
||||
```
|
||||
@@ -478,7 +573,7 @@ coercion, format validation, cross-field constraints).
|
||||
- `String` error message listing missing fields otherwise
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
```jldoctest
|
||||
schema = Dict("required" => ["city"])
|
||||
args = Dict{String,Any}()
|
||||
validateRequiredArgs(args, schema) # => "Missing required arguments: city"
|
||||
@@ -533,7 +628,7 @@ false `toolExecutionStart` events.
|
||||
and converted to an `immediateOutcome`
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
```jldoctest
|
||||
# With validateRequiredArgs hook set on the tool
|
||||
validateToolArguments(toolWithHook, tc) # => validated args or throws
|
||||
|
||||
|
||||
Reference in New Issue
Block a user