This commit is contained in:
2026-09-02 11:17:41 +07:00
parent 096baad09d
commit 6db50b6a52
6 changed files with 199 additions and 192 deletions
+2 -104
View File
@@ -3,7 +3,7 @@ module utils
export clearhistory, availableWineToText, prepareContext, formatMsgForLLM, validateRequiredArgs,
validateToolArguments, _userMessageToOpenAI,
_assistantMessageToOpenAI, _toolResultMessageToOpenAI, _messageContentToBlocks, _toolsToOpenAI,
beforeToolCall, afterToolCall, eventSink
beforeToolCall, afterToolCall
using UUIDs, Dates, DataStructures, HTTP, JSON, NATS
using GeneralUtils
@@ -310,7 +310,7 @@ Common use cases:
- `agentToolResult(...)` to return a modified result (content, details, usage,
terminate flag can all be overridden).
"""
""" #WORKING arrange result from searchWine tool
function afterToolCall(context::afterToolCallContext, signal::abortSignal
)::Union{agentToolResult, Nothing}
@@ -319,108 +319,6 @@ function afterToolCall(context::afterToolCallContext, signal::abortSignal
return nothing
end
"""
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.
"""