Add CollectionChat StreamProvider
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
defmodule MusicLibrary.Chats.CollectionChat do
|
||||
@moduledoc """
|
||||
Chat implementation for the music collection using OpenAI streaming with web search.
|
||||
"""
|
||||
|
||||
@behaviour MusicLibrary.Chats.StreamProvider
|
||||
|
||||
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)
|
||||
|
||||
OpenAI.chat_stream(messages, on_chunk: callback, instructions: instructions)
|
||||
end
|
||||
|
||||
defp build_instructions(collection_summary) do
|
||||
record_count = count_records(collection_summary)
|
||||
|
||||
Prompt.build("""
|
||||
Answer questions about the user's music collection. \
|
||||
Use the provided collection catalog as your primary reference. \
|
||||
The collection contains #{record_count} records.
|
||||
|
||||
Collection catalog:
|
||||
#{collection_summary}\
|
||||
""")
|
||||
end
|
||||
|
||||
defp count_records(""), do: 0
|
||||
|
||||
defp count_records(summary) do
|
||||
summary
|
||||
|> String.split("\n")
|
||||
|> length()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
defmodule MusicLibrary.Chats.CollectionChatTest do
|
||||
use ExUnit.Case
|
||||
|
||||
alias MusicLibrary.Chats.CollectionChat
|
||||
|
||||
defp sse_event(type, data) do
|
||||
json = JSON.encode!(%{type: type, delta: data})
|
||||
"event: #{type}\ndata: #{json}\n\n"
|
||||
end
|
||||
|
||||
defp completed_response do
|
||||
sse_event("response.output_text.delta", "Hello") <>
|
||||
"event: response.completed\ndata: {\"type\":\"response.completed\"}\n\n"
|
||||
end
|
||||
|
||||
defp stub_and_capture_instructions(test_pid) do
|
||||
Req.Test.stub(OpenAI.API, fn conn ->
|
||||
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
||||
request = JSON.decode!(body)
|
||||
send(test_pid, {:captured_instructions, request["instructions"]})
|
||||
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("text/event-stream")
|
||||
|> Plug.Conn.send_resp(200, completed_response())
|
||||
end)
|
||||
end
|
||||
|
||||
test "stream_response includes collection summary in instructions" do
|
||||
stub_and_capture_instructions(self())
|
||||
|
||||
summary =
|
||||
"Radiohead - OK Computer (1997-06-16, cd, album) [alternative rock, art rock]\nPink Floyd - The Wall (1979-11-30, vinyl, album) [progressive rock]"
|
||||
|
||||
assert :ok = CollectionChat.stream_response([], summary, fn _chunk -> :ok end)
|
||||
|
||||
assert_receive {:captured_instructions, instructions}
|
||||
assert instructions =~ summary
|
||||
assert instructions =~ "music collection"
|
||||
assert instructions =~ "collection catalog"
|
||||
assert instructions =~ "2 records"
|
||||
end
|
||||
|
||||
test "stream_response handles empty collection" do
|
||||
stub_and_capture_instructions(self())
|
||||
|
||||
assert :ok = CollectionChat.stream_response([], "", fn _chunk -> :ok end)
|
||||
|
||||
assert_receive {:captured_instructions, instructions}
|
||||
assert instructions =~ "0 records"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user