improving tool calling tracking
Some checks failed
CI/CD Pipeline / build (push) Failing after 5s

This commit is contained in:
2026-03-23 12:34:22 -06:00
parent 6fc4a686f8
commit e0ca44df23
12 changed files with 417 additions and 115 deletions

View File

@@ -11,8 +11,8 @@ defmodule ElixirAi.AiProvider do
id: Zoi.optional(Zoi.string()),
name: Zoi.string(),
model_name: Zoi.string(),
api_token: Zoi.string(),
completions_url: Zoi.string()
api_token: Zoi.nullish(Zoi.string()),
completions_url: Zoi.nullish(Zoi.string())
})
end

View File

@@ -9,67 +9,69 @@ defmodule ElixirAi.Conversation do
Zoi.object(%{
name: Zoi.string(),
model_name: Zoi.string(),
api_token: Zoi.string(),
completions_url: Zoi.string()
api_token: Zoi.nullish(Zoi.string()),
completions_url: Zoi.nullish(Zoi.string())
})
end
end
defmodule ConversationInfo do
defstruct [:name, :provider]
defstruct [:name, :category, :provider]
def schema do
Zoi.object(%{
name: Zoi.string(),
category: Zoi.string(),
provider:
Zoi.object(%{
name: Zoi.string(),
model_name: Zoi.string(),
api_token: Zoi.string(),
completions_url: Zoi.string()
api_token: Zoi.nullish(Zoi.string()),
completions_url: Zoi.nullish(Zoi.string())
})
})
end
end
def all_names do
sql = """
SELECT c.name,
json_build_object(
'name', p.name,
'model_name', p.model_name,
'api_token', p.api_token,
'completions_url', p.completions_url
) as provider
FROM conversations c
LEFT JOIN ai_providers p ON c.ai_provider_id = p.id
"""
sql = "SELECT name, category FROM conversations"
params = %{}
case DbHelpers.run_sql(sql, params, "conversations", ConversationInfo.schema()) do
schema = Zoi.object(%{name: Zoi.string(), category: Zoi.string()})
case DbHelpers.run_sql(sql, params, "conversations", schema) do
{:error, _} ->
[]
rows ->
Enum.map(rows, fn row ->
struct(ConversationInfo, Map.put(row, :provider, struct(Provider, row.provider)))
struct(ConversationInfo, row)
end)
end
end
def create(name, ai_provider_id) when is_binary(ai_provider_id) do
def create(name, ai_provider_id, category \\ "user-web", allowed_tools \\ nil)
def create(name, ai_provider_id, category, nil),
do: create(name, ai_provider_id, category, ElixirAi.AiTools.all_tool_names())
def create(name, ai_provider_id, category, allowed_tools)
when is_binary(ai_provider_id) and is_binary(category) and is_list(allowed_tools) do
case Ecto.UUID.dump(ai_provider_id) do
{:ok, binary_id} ->
sql = """
INSERT INTO conversations (
name,
ai_provider_id,
category,
allowed_tools,
inserted_at,
updated_at)
VALUES (
$(name),
$(ai_provider_id),
$(category),
$(allowed_tools)::jsonb,
$(inserted_at),
$(updated_at)
)
@@ -80,6 +82,8 @@ defmodule ElixirAi.Conversation do
params = %{
"name" => name,
"ai_provider_id" => binary_id,
"category" => category,
"allowed_tools" => Jason.encode!(allowed_tools),
"inserted_at" => timestamp,
"updated_at" => timestamp
}
@@ -97,6 +101,78 @@ defmodule ElixirAi.Conversation do
end
end
def find_allowed_tools(name) do
sql = "SELECT allowed_tools FROM conversations WHERE name = $(name) LIMIT 1"
params = %{"name" => name}
case DbHelpers.run_sql(sql, params, "conversations") do
{:error, :db_error} -> {:error, :db_error}
[] -> {:error, :not_found}
[row | _] -> {:ok, decode_json_list(row["allowed_tools"])}
end
end
defp decode_json_list(value) when is_list(value), do: value
defp decode_json_list(value) when is_binary(value) do
case Jason.decode(value) do
{:ok, list} when is_list(list) -> list
_ -> []
end
end
defp decode_json_list(_), do: []
def update_allowed_tools(name, tool_names) when is_list(tool_names) do
sql = """
UPDATE conversations
SET allowed_tools = $(allowed_tools)::jsonb, updated_at = $(updated_at)
WHERE name = $(name)
"""
params = %{
"name" => name,
"allowed_tools" => Jason.encode!(tool_names),
"updated_at" => now()
}
case DbHelpers.run_sql(sql, params, "conversations") do
{:error, :db_error} -> {:error, :db_error}
_ -> :ok
end
end
def find_tool_choice(name) do
sql = "SELECT tool_choice FROM conversations WHERE name = $(name) LIMIT 1"
params = %{"name" => name}
case DbHelpers.run_sql(sql, params, "conversations") do
{:error, :db_error} -> {:error, :db_error}
[] -> {:error, :not_found}
[row | _] -> {:ok, row["tool_choice"] || "auto"}
end
end
def update_tool_choice(name, tool_choice)
when tool_choice in ["auto", "none", "required"] do
sql = """
UPDATE conversations
SET tool_choice = $(tool_choice), updated_at = $(updated_at)
WHERE name = $(name)
"""
params = %{
"name" => name,
"tool_choice" => tool_choice,
"updated_at" => now()
}
case DbHelpers.run_sql(sql, params, "conversations") do
{:error, :db_error} -> {:error, :db_error}
_ -> :ok
end
end
def find_id(name) do
sql = "SELECT id FROM conversations WHERE name = $(name) LIMIT 1"
params = %{"name" => name}

View File

@@ -25,6 +25,7 @@ defmodule ElixirAi.Message do
role: Zoi.string(),
content: Zoi.nullish(Zoi.string()),
reasoning_content: Zoi.nullish(Zoi.string()),
tool_choice: Zoi.nullish(Zoi.string()),
inserted_at: Zoi.any()
})
end
@@ -125,6 +126,7 @@ defmodule ElixirAi.Message do
tm.role,
tm.content,
tm.reasoning_content,
tm.tool_choice,
tm.inserted_at
FROM text_messages tm
WHERE tm.conversation_id = $(conversation_id)
@@ -222,6 +224,7 @@ defmodule ElixirAi.Message do
prev_message_table,
role,
content,
tool_choice,
inserted_at
) VALUES (
$(conversation_id),
@@ -229,6 +232,7 @@ defmodule ElixirAi.Message do
$(prev_message_table),
$(role),
$(content),
$(tool_choice),
$(inserted_at)
)
"""
@@ -239,6 +243,7 @@ defmodule ElixirAi.Message do
"prev_message_table" => prev_table,
"role" => "user",
"content" => message[:content],
"tool_choice" => message[:tool_choice],
"inserted_at" => timestamp
}