Files
YiemAgent/src/api.jl
T
2026-08-11 18:57:53 +07:00

401 lines
6.4 KiB
Julia

module api
export prompt
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
DataFrames
using GeneralUtils
using ..type, ..utils, ..agentCore, ..toolRegistry
# ---------------------------------------------- 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 `takeResponse(agent)` to receive the agent's response after sending a message.
- Use `followUp(agent, msg)` to send messages while the agent is still processing.
# Examples
```jldoctest
julia> runAgent(agent, "Hello!")
yiemAgent(...)
```
"""
function runAgent(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 `runAgent(agent, msg)` to send a message before calling this function.
# Examples
```jldoctest
julia> response = takeResponse(agent)
assistantMessage(...)
```
"""
function takeResponse(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 `runAgent(agent, msg)` for the primary message and `followUp(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> followUp(agent, "Also consider red wines")
yiemAgent(...)
```
"""
function followUp(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 `stopAgent`, 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> stopAgent(agent)
```
"""
function stopAgent(agent::yiemAgent)
put!(agent.inputChannel, :shutdown)
try
fetch(agent._agent_loop)
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