Shrink collection summary to fit OpenAI token limit

Deduplicate records by musicbrainz_id (merging formats)
and cap genres to 3 per record, reducing the summary from
~32K to ~24K estimated tokens.
This commit is contained in:
Claudio Ortolina
2026-04-13 12:33:15 +01:00
parent ac1954f9c8
commit 8108645718
4 changed files with 86 additions and 28 deletions
+5 -14
View File
@@ -8,9 +8,10 @@ defmodule MusicLibrary.Chats.CollectionChat do
alias MusicLibrary.Chats.Prompt
@impl true
@spec stream_response([map()], String.t(), (String.t() -> any())) :: :ok | {:error, term()}
def stream_response(messages, collection_summary, callback) do
instructions = build_instructions(collection_summary)
@spec stream_response([map()], {String.t(), non_neg_integer()}, (String.t() -> any())) ::
:ok | {:error, term()}
def stream_response(messages, {summary, record_count}, callback) do
instructions = build_instructions(summary, record_count)
OpenAI.chat_stream(messages,
on_chunk: callback,
@@ -19,9 +20,7 @@ defmodule MusicLibrary.Chats.CollectionChat do
)
end
defp build_instructions(collection_summary) do
record_count = count_records(collection_summary)
defp build_instructions(collection_summary, record_count) do
Prompt.build("""
Answer questions about the user's music collection. \
Use the provided collection catalog as your primary reference. \
@@ -31,12 +30,4 @@ defmodule MusicLibrary.Chats.CollectionChat do
#{collection_summary}\
""")
end
defp count_records(""), do: 0
defp count_records(summary) do
summary
|> String.split("\n")
|> length()
end
end
+23 -6
View File
@@ -191,7 +191,9 @@ defmodule MusicLibrary.Collection do
|> MapSet.new()
end
@spec collection_summary() :: String.t()
@max_genres_per_record 3
@spec collection_summary() :: {String.t(), non_neg_integer()}
def collection_summary do
records =
from(r in Record,
@@ -201,16 +203,31 @@ defmodule MusicLibrary.Collection do
)
|> Repo.all()
records
|> Enum.map_join("\n", &format_record_line/1)
record_count = length(records)
summary =
records
|> Enum.group_by(& &1.musicbrainz_id)
|> Enum.map(fn {_id, group} -> format_group(group) end)
|> Enum.sort()
|> Enum.join("\n")
{summary, record_count}
end
defp format_record_line(record) do
defp format_group(records) do
record = hd(records)
artist_names = Record.artist_names(record)
genres = record.genres || []
formats = records |> Enum.map(& &1.format) |> Enum.uniq() |> Enum.join("/")
genres =
records
|> Enum.flat_map(&(&1.genres || []))
|> Enum.uniq()
|> Enum.take(@max_genres_per_record)
base =
"#{artist_names} - #{record.title} (#{record.release_date || "Unknown"}, #{record.format}, #{record.type})"
"#{artist_names} - #{record.title} (#{record.release_date || "Unknown"}, #{formats}, #{record.type})"
if genres == [] do
base