402 lines
6.4 KiB
Julia
402 lines
6.4 KiB
Julia
module api
|
|
|
|
export prompt
|
|
|
|
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
|
DataFrames, Serde
|
|
using GeneralUtils
|
|
using ..type, ..util, ..llmfunction
|
|
|
|
# ---------------------------------------------- 100 --------------------------------------------- #
|
|
|
|
|
|
|
|
"""
|
|
Send a message to the agent's input channel.
|
|
|
|
Blocks if the input channel buffer is full (capacity 16 by default).
|
|
The agent processes messages from `inputChannel` in the background task.
|
|
|
|
# Arguments
|
|
- `agent::yiemAgent`: The agent instance to send a message to
|
|
- `msg`: The message to send (any type accepted by the agent's processing pipeline)
|
|
|
|
# Returns
|
|
- The same `agent` instance for chaining
|
|
|
|
# Notes
|
|
- Use `take_response(agent)` to receive the agent's response after sending a message.
|
|
- Use `follow_up(agent, msg)` to send messages while the agent is still processing.
|
|
|
|
# Examples
|
|
```jldoctest
|
|
julia> run_agent(agent, "Hello!")
|
|
yiemAgent(...)
|
|
```
|
|
"""
|
|
function run_agent(agent::yiemAgent, msg)
|
|
put!(agent.inputChannel, msg)
|
|
return agent
|
|
end
|
|
|
|
"""
|
|
Take a response from the agent's output channel.
|
|
|
|
Blocks until the agent sends a response.
|
|
|
|
# Arguments
|
|
- `agent::yiemAgent`: The agent instance to receive a response from
|
|
|
|
# Returns
|
|
- An `assistantMessage` instance representing the agent's response
|
|
|
|
# Notes
|
|
- Use `run_agent(agent, msg)` to send a message before calling this function.
|
|
|
|
# Examples
|
|
```jldoctest
|
|
julia> response = take_response(agent)
|
|
assistantMessage(...)
|
|
```
|
|
"""
|
|
function take_response(agent::yiemAgent)
|
|
return take!(agent.outputChannel)
|
|
end
|
|
|
|
"""
|
|
Send a follow-up message while the agent is still processing.
|
|
|
|
Follow-up messages are queued and processed after all `inputChannel` messages
|
|
and before any tool call results are sent.
|
|
|
|
# Arguments
|
|
- `agent::yiemAgent`: The agent instance to send a follow-up message to
|
|
- `msg`: The follow-up message to send
|
|
|
|
# Returns
|
|
- The same `agent` instance for chaining
|
|
|
|
# Notes
|
|
- Use `run_agent(agent, msg)` for the primary message and `follow_up(agent, msg)` for additional
|
|
messages while the agent is processing.
|
|
- Follow-up messages are buffered in a separate channel (capacity 32 by default).
|
|
|
|
# Examples
|
|
```jldoctest
|
|
julia> follow_up(agent, "Also consider red wines")
|
|
yiemAgent(...)
|
|
```
|
|
"""
|
|
function follow_up(agent::yiemAgent, msg)
|
|
put!(agent.followUpChannel, msg)
|
|
return agent
|
|
end
|
|
|
|
"""
|
|
Gracefully stop the agent.
|
|
|
|
Sends a `:shutdown` signal to the input channel, waits for the background task to finish,
|
|
then closes all channels (`inputChannel`, `outputChannel`, `followUpChannel`).
|
|
|
|
# Arguments
|
|
- `agent::yiemAgent`: The agent instance to stop
|
|
|
|
# Returns
|
|
- `nothing`
|
|
|
|
# Notes
|
|
- After calling `stop_agent`, the agent is no longer usable. A new agent must be created
|
|
for further interaction.
|
|
- If the background task throws a `TaskFailedException`, it is rethrown.
|
|
|
|
# Examples
|
|
```jldoctest
|
|
julia> stop_agent(agent)
|
|
```
|
|
"""
|
|
function stop_agent(agent::yiemAgent)
|
|
put!(agent.inputChannel, :shutdown)
|
|
try
|
|
fetch(agent._task)
|
|
catch e
|
|
if e isa TaskFailedException
|
|
rethrow(e)
|
|
end
|
|
end
|
|
close(agent.inputChannel)
|
|
close(agent.outputChannel)
|
|
close(agent.followUpChannel)
|
|
return nothing
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" Recursively convert dictionary-like variable (e.g. JSON.Object) into an OrderedDict.
|
|
|
|
The function walks any nested structure composed of `AbstractDict` (e.g., `JSON.Object`,
|
|
`Dict`, `OrderedDict`) and `AbstractArray` and produces a new tree where
|
|
every dictionary-like node is an `OrderedDict` and every array-like node is a `Vector{Any}`.
|
|
Scalar values (numbers, strings, booleans, `nothing`, etc.) are returned unchanged.
|
|
Does **not** mutate the input; it always allocates new containers.
|
|
|
|
# Arguments
|
|
- `x`
|
|
Any Julia value. If `x` is an `AbstractDict` it will be converted to an `OrderedDict`;
|
|
if it is an `AbstractArray` its elements will be processed recursively.
|
|
|
|
# Keyword Arguments
|
|
- `keytype::Type=Any`
|
|
The key type for the output OrderedDict. Use `String` for `OrderedDict{String,Any}`,
|
|
`Symbol` for `OrderedDict{Symbol,Any}`, or `Any` to preserve original key types.
|
|
- `sort_order::Union{Nothing, Vector}=nothing`
|
|
Vector of keys specifying the desired order. Keys are arranged in the specified order
|
|
first, followed by any remaining keys.
|
|
|
|
# Return
|
|
- A newly allocated nested structure composed of `OrderedDict{keytype,Any}` and `Vector{Any}`
|
|
that mirrors the input shape but uses ordered Julia containers.
|
|
|
|
# Notes
|
|
- The function treats any `AbstractDict` as a mapping source, so it works with
|
|
`JSON.Object`, `Dict`, `OrderedDict`, etc.
|
|
- Arrays are returned as `Vector{Any}` with their elements processed recursively.
|
|
|
|
# Examples
|
|
```jldoctest
|
|
julia> using JSON, DataStructures
|
|
julia> d = Dict(
|
|
"a" => 4,
|
|
"b" => 6,
|
|
"c" => Dict(
|
|
"d"=>7,
|
|
:e=>Dict(
|
|
"f"=>"hey",
|
|
"g"=>Dict(
|
|
"world"=>[1, "2", 3, Dict(:dd=>4.7)]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
julia> jsonstring = JSON.json(d)
|
|
julia> A1 = JSON.parse(jsonstring) # A1 type is JSON.Object
|
|
julia> A2 = dictify(A1; keytype=String)
|
|
OrderedDict{String,Any} with 3 entries:
|
|
"a" => 4
|
|
"b" => 6
|
|
"c" => OrderedDict("d"=>7, "e"=>Dict("f"=>"hey", "g"=>Dict("world"=>[1, "2", 3, 4.7])))
|
|
|
|
julia> A3 = dictify(A1; keytype=Symbol)
|
|
OrderedDict{Symbol,Any} with 3 entries:
|
|
:a => 4
|
|
:b => 6
|
|
:c => OrderedDict(:d=>7, :e=>Dict("f"=>"hey", "g"=>Dict("world"=>[1, "2", 3, 4.7])))
|
|
|
|
julia> B1 = dictify(d; keytype=String)
|
|
OrderedDict{String, Any} with 3 entries:
|
|
```
|
|
|
|
**With sort_order:**
|
|
```jldoctest
|
|
julia> d = Dict("a"=>1, "b"=>2, "c"=>3)
|
|
julia> dictify(d; sort_order=["c", "a"])
|
|
OrderedDict{String,Int} with 3 entries:
|
|
"c" => 3
|
|
"a" => 1
|
|
"b" => 2
|
|
```
|
|
"""
|
|
function dictify(x::T; keytype::Type=Any, sort_order::Union{Nothing, Vector}=nothing
|
|
)::OrderedDict where {T<:AbstractDict}
|
|
|
|
# this function is example
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end # module interface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|