update
This commit is contained in:
@@ -14,6 +14,7 @@ module YiemAgent
|
||||
|
||||
include("tools/getWeather.jl")
|
||||
include("tools/getTime.jl")
|
||||
include("tools/searchWine.jl")
|
||||
include("tools/writeTool.jl")
|
||||
|
||||
include("toolRegistry.jl")
|
||||
@@ -22,6 +23,7 @@ module YiemAgent
|
||||
function register_all_tools(store::toolRegistry.toolStore)
|
||||
registerTool(store, getWeatherTool())
|
||||
registerTool(store, getTimeTool())
|
||||
registerTool(store, searchWineTool())
|
||||
registerTool(store, writeToolTool())
|
||||
registerTool(store, listTool(store))
|
||||
return store.tools
|
||||
|
||||
+12
-8
@@ -5,7 +5,7 @@ export yiemAgent, _agentLoop, OpenAiToUserMessage, _extractToolCalls,
|
||||
executeToolCallsParallel, executeToolCalls
|
||||
|
||||
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
||||
DataFrames, Base.Threads, NATS
|
||||
DataFrames, Base.Threads, NATS, LibPQ
|
||||
using GeneralUtils
|
||||
using ..type, ..utils, ..toolRegistry
|
||||
|
||||
@@ -379,7 +379,7 @@ function _processMessage(
|
||||
# call prepareContext()
|
||||
state = agentState(systemPrompt, nothing, tools, agentMsgHistory)
|
||||
agentEventSink("_processMessage 8 _state.messages length $(length(agentMsgHistory))")
|
||||
preparedContext = prepareContext(state, agentEventSink)
|
||||
preparedContext = prepareContext(state, agentEventSink, llmCall)
|
||||
agentEventSink("_processMessage 9 _state.messages length $(length(agentMsgHistory))")
|
||||
# Call formatMessagesForLLM() to format for LLM
|
||||
formattedMessages = formatMessagesForLLM(preparedContext, agentEventSink)
|
||||
@@ -409,6 +409,7 @@ function _processMessage(
|
||||
beforeToolCall,
|
||||
afterToolCall,
|
||||
parallelToolExecute ? "parallel" : "sequential",
|
||||
llmCall,
|
||||
)
|
||||
|
||||
signal = abortSignal(false)
|
||||
@@ -1000,13 +1001,14 @@ function executePreparedToolCall(
|
||||
prep::preparedToolCall,
|
||||
signal::Union{Nothing,abortSignal},
|
||||
agentEventSink,
|
||||
llmCall::Union{Any,Nothing}=nothing,
|
||||
)::executedOutcome
|
||||
agentEventSink("executePreparedToolCall 1")
|
||||
agentEventSink("executePreparedToolCall 2")
|
||||
agentEventSink("executePreparedToolCall 3")
|
||||
|
||||
try
|
||||
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, agentEventSink)
|
||||
result = prep.tool.execute(prep.toolCall.id, prep.args, signal, agentEventSink, llmCall)
|
||||
agentEventSink(result.content[1].text)
|
||||
agentEventSink("executePreparedToolCall 4")
|
||||
return executedOutcome(result, false)
|
||||
@@ -1184,6 +1186,7 @@ function executeToolCallsSequential(
|
||||
signal::abortSignal,
|
||||
agentEventSink,
|
||||
)::agentToolCallBatch
|
||||
llmCall = config.llmCall
|
||||
agentEventSink("executeToolCallsSequential 1")
|
||||
finalizedCalls = finalizedOutcome[]
|
||||
messages = toolResultMessage[]
|
||||
@@ -1199,8 +1202,7 @@ function executeToolCallsSequential(
|
||||
agentEventSink("executeToolCallsSequential 2-2")
|
||||
else
|
||||
agentEventSink("executeToolCallsSequential 3")
|
||||
#XXX
|
||||
executed = executePreparedToolCall(prep, signal, agentEventSink)
|
||||
executed = executePreparedToolCall(prep, signal, agentEventSink, llmCall)
|
||||
agentEventSink("executeToolCallsSequential 3-1")
|
||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config,
|
||||
signal, agentEventSink)
|
||||
@@ -1287,6 +1289,7 @@ function executeToolCallsParallel(
|
||||
)::agentToolCallBatch
|
||||
|
||||
entries = Union{finalizedOutcome,Task}[]
|
||||
llmCall = config.llmCall
|
||||
|
||||
for tc in toolCalls
|
||||
agentEventSink(toolExecStartEvent(tc.id, tc.name, tc.arguments))
|
||||
@@ -1300,7 +1303,7 @@ function executeToolCallsParallel(
|
||||
push!(entries, finalized)
|
||||
else
|
||||
t = Task() do
|
||||
executed = executePreparedToolCall(prep, signal, agentEventSink)
|
||||
executed = executePreparedToolCall(prep, signal, agentEventSink, llmCall)
|
||||
finalized = finalizeExecutedToolCall(context, assistantMsg, prep, executed, config, signal)
|
||||
agentEventSink(toolExecEndEvent(finalized.toolCall.id, finalized.toolCall.name,
|
||||
finalized.result, finalized.isError))
|
||||
@@ -1388,6 +1391,7 @@ function executeToolCalls(
|
||||
agentEventSink,
|
||||
)::agentToolCallBatch
|
||||
|
||||
llmCall = config.llmCall
|
||||
agentEventSink("_executeToolCalls 1")
|
||||
hasSequential = false
|
||||
for tc in toolCalls
|
||||
@@ -1400,11 +1404,11 @@ function executeToolCalls(
|
||||
agentEventSink("_executeToolCalls 2")
|
||||
if config.toolExecution == "sequential" || hasSequential
|
||||
agentEventSink("_executeToolCalls 3")
|
||||
return executeToolCallsSequential(context, assistantMsg, toolCalls, config, signal,
|
||||
return executeToolCallsSequential(context, assistantMsg, toolCalls, config, signal,
|
||||
agentEventSink)
|
||||
else
|
||||
agentEventSink("_executeToolCalls 4")
|
||||
return executeToolCallsParallel(context, assistantMsg, toolCalls, config, signal,
|
||||
return executeToolCallsParallel(context, assistantMsg, toolCalls, config, signal,
|
||||
agentEventSink)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,7 +45,7 @@ Execute the getTime tool.
|
||||
Returns mock time data for the given timezone or city.
|
||||
"""
|
||||
function getTimeExecute(toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal},
|
||||
onPartialResult)
|
||||
onPartialResult, llmCall=nothing)
|
||||
tz = get(args, "timezone", nothing)
|
||||
city = get(args, "city", "")
|
||||
if tz !== nothing
|
||||
|
||||
@@ -7,7 +7,7 @@ Execute the getWeather tool.
|
||||
Returns mock weather data for the given city and temperature units.
|
||||
"""
|
||||
function getWeatherExecute(toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal},
|
||||
agentEventSink)
|
||||
agentEventSink, llmCall=nothing)
|
||||
|
||||
agentEventSink("Getting weather...")
|
||||
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
using .type
|
||||
using LibPQ, DataFrames, JSON, DataStructures
|
||||
using Dates, Random, HTTP
|
||||
using GeneralUtils
|
||||
|
||||
# ── Database config — update for your environment ───────────────────────
|
||||
const DB_CONFIG = Dict{String,Any}(
|
||||
"host" => "localhost",
|
||||
"port" => 5432,
|
||||
"dbname" => "winedb",
|
||||
"user" => "postgres",
|
||||
"password" => "",
|
||||
)
|
||||
|
||||
"""
|
||||
Execute the search_wine_database! tool.
|
||||
|
||||
Uses the agent's LLM to generate SQL from the free-form text query,
|
||||
then executes it against the wine database and returns formatted results.
|
||||
"""
|
||||
function searchWineExecute(
|
||||
toolCallId::String,
|
||||
args::Dict{String,Any},
|
||||
signal::Union{Nothing,abortSignal},
|
||||
agentEventSink,
|
||||
llmCall,
|
||||
)::agentToolResult
|
||||
#WORKING
|
||||
search_query = get(args, "searchQuery", "")::String
|
||||
|
||||
if isempty(search_query)
|
||||
return agentToolResult(
|
||||
[textContent("Please provide a search query for the wine database.")],
|
||||
Dict{Any,Any}(), nothing, false
|
||||
)
|
||||
end
|
||||
|
||||
agentEventSink("searchWineExecute: query=$search_query")
|
||||
|
||||
# ── SQL generation prompt ───────────────────────────────────────────
|
||||
systemmsg = """
|
||||
# database_search_guidelines
|
||||
- Keep SQL queries focused only on the provided information.
|
||||
- Use wildcard character (%) to search more effectively.
|
||||
- Do not create any table in the database.
|
||||
- Text information in the database is usually stored in lower case.
|
||||
If your search returns empty, try using lower case to search.
|
||||
- Overly strict conditions usually yield empty results.
|
||||
- Use ILIKE for case-insensitive text matching.
|
||||
- Only output the SQL query — do not wrap it in backticks or add comments.
|
||||
|
||||
# situation
|
||||
You are a wine store database assistant. You will be given a user's
|
||||
natural language search query and the database table schema.
|
||||
|
||||
# objective
|
||||
Generate a single SQL query to find wines matching the user's request.
|
||||
|
||||
# your responsibility includes
|
||||
Fulfill the objective.
|
||||
|
||||
# you should respond with ONLY the SQL query string, ending with ';'
|
||||
"""
|
||||
|
||||
table_schema = """
|
||||
CREATE TABLE wine (
|
||||
wine_id uuid primary key default gen_random_uuid (),
|
||||
wine_name varchar(128) not null,
|
||||
winery varchar(128) not null,
|
||||
vintage integer not null,
|
||||
region varchar(128) not null,
|
||||
country varchar(128) not null,
|
||||
wine_type varchar(128) not null,
|
||||
grape varchar(128) not null,
|
||||
serving_temperature varchar(128) not null,
|
||||
intensity integer,
|
||||
sweetness integer,
|
||||
tannin integer,
|
||||
acidity integer,
|
||||
fizziness integer,
|
||||
tasting_notes text,
|
||||
image_url jsonb,
|
||||
manufacturer_sku text,
|
||||
note text,
|
||||
other_attributes jsonb,
|
||||
created_time timestamptz default current_timestamp,
|
||||
updated_time timestamptz default current_timestamp,
|
||||
description text
|
||||
);
|
||||
|
||||
CREATE TABLE retailer (
|
||||
retailer_id uuid primary key default gen_random_uuid (),
|
||||
retailer_name varchar(128) not null,
|
||||
retailer_username varchar(128) not null,
|
||||
retailer_password varchar(128) not null,
|
||||
retailer_address text not null,
|
||||
country varchar(128) not null,
|
||||
contact_person varchar(128) not null,
|
||||
telephone varchar(128) not null,
|
||||
email varchar(128) not null,
|
||||
note text,
|
||||
other_attributes jsonb,
|
||||
created_time timestamptz default current_timestamp,
|
||||
updated_time timestamptz default current_timestamp,
|
||||
description text
|
||||
);
|
||||
|
||||
CREATE TABLE retailer_wine (
|
||||
retailer_id uuid references retailer(retailer_id),
|
||||
wine_id uuid references wine(wine_id),
|
||||
constraint retailer_wine_id primary key (retailer_id, wine_id),
|
||||
price NUMERIC(10, 2),
|
||||
currency varchar(3) not null,
|
||||
created_time timestamptz default current_timestamp,
|
||||
updated_time timestamptz default current_timestamp
|
||||
);
|
||||
"""
|
||||
|
||||
context = "<internal_context_for_assistant>\n<database_table_schema>\n$table_schema\n</database_table_schema>\n</internal_context_for_assistant>\n\n"
|
||||
input = context * "User query: $search_query\n\nGenerate the SQL query:"
|
||||
|
||||
# ── Call LLM for SQL generation ────────────────────────────────────
|
||||
max_attempts = 5
|
||||
generated_sql = nothing
|
||||
|
||||
for attempt in 1:max_attempts
|
||||
msg = Dict(
|
||||
"messages" => [
|
||||
Dict(
|
||||
"role" => "system",
|
||||
"content" => [Dict("type" => "text", "text" => systemmsg)],
|
||||
),
|
||||
Dict(
|
||||
"role" => "user",
|
||||
"content" => [Dict("type" => "text", "text" => input)],
|
||||
),
|
||||
],
|
||||
"temperature" => 0.7,
|
||||
)
|
||||
|
||||
llm_response = llmCall(msg)
|
||||
|
||||
# Clean the response — extract SQL from potential markdown/code blocks
|
||||
sql_text = _clean_sql_response(llm_response)
|
||||
|
||||
# Validate it looks like SQL
|
||||
if _is_valid_sql(sql_text)
|
||||
generated_sql = sql_text
|
||||
agentEventSink("searchWine: generated SQL (attempt $attempt)\n$sql_text")
|
||||
break
|
||||
else
|
||||
agentEventSink("searchWine: invalid SQL attempt $attempt: $sql_text")
|
||||
end
|
||||
end
|
||||
|
||||
if generated_sql === nothing
|
||||
return agentToolResult(
|
||||
[textContent("Failed to generate a valid SQL query for your search. Please try rephrasing.")],
|
||||
Dict{Any,Any}("error" => "sql_generation_failed"), nothing, false
|
||||
)
|
||||
end
|
||||
|
||||
# ── Execute SQL ────────────────────────────────────────────────────
|
||||
try
|
||||
conn = LibPQ.Connection(DB_CONFIG)
|
||||
|
||||
# Ensure LIMIT to prevent large result sets
|
||||
sanitized_sql = _ensure_limit(generated_sql)
|
||||
agentEventSink("searchWine: executing\n$sanitized_sql")
|
||||
|
||||
result = LibPQ.execute(conn, sanitized_sql)
|
||||
close(conn)
|
||||
|
||||
if !LibPQ.hasdata(result)
|
||||
return agentToolResult(
|
||||
[textContent("No wines found matching your search. Try loosening your criteria.")],
|
||||
Dict{Any,Any}("count" => 0), nothing, false
|
||||
)
|
||||
end
|
||||
|
||||
df = DataFrame(result)
|
||||
num_rows, num_cols = size(df)
|
||||
|
||||
if num_cols > 30
|
||||
return agentToolResult(
|
||||
[textContent("The result has more than 30 columns. Please be more specific in your search.")],
|
||||
Dict{Any,Any}("error" => "too_many_columns"), nothing, false
|
||||
)
|
||||
end
|
||||
|
||||
# Randomly sample up to 2 rows for display if more than 2 results
|
||||
display_df = df
|
||||
if num_rows > 2
|
||||
idx = sample(1:num_rows, min(2, num_rows), replace=false)
|
||||
display_df = df[idx, :]
|
||||
end
|
||||
|
||||
# Convert to vector of dicts
|
||||
result_vec = GeneralUtils.dfToVectorDict(display_df)
|
||||
|
||||
# Fetch bottle images if available
|
||||
for d in result_vec
|
||||
image_url_json_str = get(d, "image_url", nothing)
|
||||
if image_url_json_str !== nothing && !isempty(string(image_url_json_str))
|
||||
try
|
||||
image_url_json_obj = JSON.parse(string(image_url_json_str))
|
||||
base_url = "http://192.168.88.106:8080/"
|
||||
if haskey(image_url_json_obj, "bottle")
|
||||
url = base_url * string(image_url_json_obj["bottle"])
|
||||
image_data = HTTP.get(url)
|
||||
image_base64_string = base64encode(image_data.body)
|
||||
d["image"] = image_base64_string
|
||||
end
|
||||
catch
|
||||
# Skip image fetch on error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Format results as readable text
|
||||
result_str = _format_wine_results(display_df)
|
||||
|
||||
return agentToolResult(
|
||||
[textContent(result_str)],
|
||||
Dict{Any,Any}(
|
||||
"count" => num_rows,
|
||||
"displayed" => size(display_df, 1),
|
||||
),
|
||||
nothing, false
|
||||
)
|
||||
|
||||
catch e
|
||||
errMsg = sprint(showerror, e)
|
||||
return agentToolResult(
|
||||
[textContent("Database error: $errMsg")],
|
||||
Dict{Any,Any}("error" => errMsg), nothing, false
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Extract a SQL query string from the LLM response, handling potential
|
||||
markdown code blocks, extra text, or JSON wrapping.
|
||||
"""
|
||||
function _clean_sql_response(response)::String
|
||||
text = string(response)
|
||||
|
||||
# Try to extract from code block
|
||||
if occursin("```", text)
|
||||
extracted = GeneralUtils.extract_triple_backtick_text(text)
|
||||
if !isempty(extracted)
|
||||
text = extracted[1]
|
||||
# Remove "sql\n" prefix if present
|
||||
if startswith(text, "sql\n") || startswith(text, "SQL\n")
|
||||
text = text[5:end]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove JSON wrapping if present
|
||||
text = strip(text)
|
||||
if startswith(text, "{") && occursin("action_input", text)
|
||||
# Parse as JSON and extract action_input
|
||||
try
|
||||
parsed = JSON.parse(text)
|
||||
if parsed isa Dict
|
||||
text = get(parsed, "action_input", text)
|
||||
end
|
||||
catch
|
||||
# Keep original
|
||||
end
|
||||
end
|
||||
|
||||
# Extract SQL keywords to find the actual query
|
||||
lines = split(strip(text), '\n')
|
||||
sql_lines = String[]
|
||||
for line in lines
|
||||
stripped = strip(line)
|
||||
if occursin(r"(?i)(SELECT|FROM|WHERE|JOIN|ORDER|LIMIT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|WITH)", stripped)
|
||||
# Take everything from this line to the end
|
||||
push!(sql_lines, line)
|
||||
elseif !isempty(sql_lines)
|
||||
# Continue collecting if we already found SQL
|
||||
push!(sql_lines, line)
|
||||
end
|
||||
end
|
||||
|
||||
result = join(sql_lines, "\n")
|
||||
|
||||
# Ensure it ends with semicolon
|
||||
result = strip(result)
|
||||
if !endswith(result, ";")
|
||||
result *= ";"
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
Check if a string looks like a valid SQL query.
|
||||
"""
|
||||
function _is_valid_sql(sql::String)::Bool
|
||||
sql = strip(sql)
|
||||
# Must start with a SQL keyword
|
||||
has_sql_keyword = occursin(r"(?i)(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|WITH)\s", sql) ||
|
||||
occursin(r"(?i)(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|WITH)\s*;", sql)
|
||||
# Must end with semicolon
|
||||
has_semicolon = endswith(sql, ";")
|
||||
# Must not be too short (reject single words)
|
||||
reasonable_length = length(sql) > 10
|
||||
return has_sql_keyword && has_semicolon && reasonable_length
|
||||
end
|
||||
|
||||
"""
|
||||
Ensure the SQL query has a LIMIT clause to prevent loading excessive data.
|
||||
"""
|
||||
function _ensure_limit(sql::String)::String
|
||||
sql = strip(sql)
|
||||
if !occursin(r"(?i)LIMIT", sql)
|
||||
# Remove existing semicolon, add LIMIT, re-add semicolon
|
||||
if endswith(sql, ";")
|
||||
sql = sql[1:end-1]
|
||||
end
|
||||
sql *= " ORDER BY RANDOM() LIMIT 2;"
|
||||
end
|
||||
return sql
|
||||
end
|
||||
|
||||
"""
|
||||
Format wine database results as human-readable text.
|
||||
"""
|
||||
function _format_wine_results(df::DataFrame)::String
|
||||
lines = String[]
|
||||
num_rows = size(df, 1)
|
||||
|
||||
for i in 1:num_rows
|
||||
row = df[i, :]
|
||||
push!(lines, "$(i). $(get(row, :wine_name, "Unknown")) $(get(row, :vintage, ""))")
|
||||
|
||||
winery = get(row, :winery, "Unknown")
|
||||
region = get(row, :region, "Unknown")
|
||||
country = get(row, :country, "Unknown")
|
||||
push!(lines, " Winery: $winery")
|
||||
push!(lines, " Region: $region, $country")
|
||||
|
||||
grape = get(row, :grape, "Unknown")
|
||||
wtype = get(row, :wine_type, "Unknown")
|
||||
push!(lines, " Grape: $grape")
|
||||
push!(lines, " Type: $wtype")
|
||||
|
||||
sweetness = get(row, :sweetness, "N/A")
|
||||
intensity = get(row, :intensity, "N/A")
|
||||
tannin_val = get(row, :tannin, "N/A")
|
||||
acidity = get(row, :acidity, "N/A")
|
||||
push!(lines, " Profile: Sweetness: $sweetness, Intensity: $intensity, Tannin: $tannin_val, Acidity: $acidity")
|
||||
|
||||
tasting = get(row, :tasting_notes, nothing)
|
||||
if tasting !== nothing && !isempty(string(tasting))
|
||||
tn = string(tasting)
|
||||
limit = min(200, length(tn))
|
||||
push!(lines, " Notes: $(tn[1:limit])$(length(tn) > limit ? "..." : "")")
|
||||
end
|
||||
|
||||
price = get(row, :price, "N/A")
|
||||
currency = get(row, :currency, "")
|
||||
retailer = get(row, :retailer_name, "N/A")
|
||||
push!(lines, " Price: $price $currency at $retailer")
|
||||
push!(lines, "")
|
||||
end
|
||||
|
||||
return join(lines, "\n")
|
||||
end
|
||||
|
||||
"""
|
||||
Define and return the searchWine agentTool.
|
||||
"""
|
||||
function searchWineTool()::agentTool
|
||||
return agentTool(
|
||||
name = "searchWine",
|
||||
label = "Search Wine Database",
|
||||
description = "Search the wine database for wines matching a free-text query. Uses the LLM to generate SQL and execute it against the database. Returns wine details including name, winery, vintage, tasting notes, and price.",
|
||||
inputSchema = Dict{String,Any}(
|
||||
"type" => "object",
|
||||
"properties" => Dict(
|
||||
"searchQuery" => Dict(
|
||||
"type" => "string",
|
||||
"description" => "Free-text description of the wine you're looking for, e.g., 'a light-bodied red wine from France under 50 dollars'",
|
||||
),
|
||||
),
|
||||
"required" => ["searchQuery"],
|
||||
),
|
||||
execute = searchWineExecute,
|
||||
prepareArguments = nothing,
|
||||
validateRequiredArgs = nothing,
|
||||
parallelToolExecute = false,
|
||||
)
|
||||
end
|
||||
@@ -130,7 +130,7 @@ function writeToolTool()::agentTool
|
||||
),
|
||||
"required" => ["name", "label", "description", "inputSchema", "executeCode"]
|
||||
),
|
||||
execute = (toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal}, onPartialResult) -> begin
|
||||
execute = (toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal}, onPartialResult, llmCall=nothing) -> begin
|
||||
tool_name = get(args, "name", "")::String
|
||||
tool_label = get(args, "label", tool_name)::String
|
||||
tool_description = get(args, "description", "")::String
|
||||
|
||||
@@ -358,6 +358,7 @@ struct agentContext # Snapshot of the agent's conversa
|
||||
systemPrompt::String # System prompt for the agent
|
||||
messages::Vector{agentMessage} # Conversation messages
|
||||
tools::Union{OrderedDict{String, agentTool}, Nothing} # Available tools keyed by name
|
||||
llmCall::Union{Any, Nothing} # LLM call function (for tools that need it)
|
||||
end
|
||||
|
||||
|
||||
@@ -451,6 +452,7 @@ struct agentLoopConfig
|
||||
beforeToolCall::Union{Function, Nothing}
|
||||
afterToolCall::Union{Function, Nothing}
|
||||
toolExecution::String
|
||||
llmCall::Union{Any, Nothing} # LLM call function (for tools like searchWine)
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
+2
-2
@@ -109,7 +109,7 @@ prepareContext(state).messages == deepcopy(state.messages)
|
||||
# end
|
||||
```
|
||||
"""
|
||||
function prepareContext(state::agentState, agentEventSink)::agentContext
|
||||
function prepareContext(state::agentState, agentEventSink, llmCall=nothing)::agentContext
|
||||
|
||||
#TODO filter tools from state.tools based on user intend in user message and tool description
|
||||
filteredTools = state.tools
|
||||
@@ -120,7 +120,7 @@ function prepareContext(state::agentState, agentEventSink)::agentContext
|
||||
#TODO add system prompt, adjust/modify and inject additional context into messages
|
||||
preparedMessages = deepcopy(state.messages) # messages that will be send to LLM
|
||||
|
||||
agentCtx = agentContext(preparedSystemPrompt, preparedMessages, filteredTools)
|
||||
agentCtx = agentContext(preparedSystemPrompt, preparedMessages, filteredTools, llmCall)
|
||||
|
||||
return agentCtx
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user