lib/vnext_genai/stream_handler/behaviour.ex

defmodule GenAI.StreamHandler do
  
  def begin_stream(handler, session, context, options) when is_atom(handler) do
    handler.begin_stream(handler, session, context, options)
  end
  def begin_stream(%{__struct__: h} = handler, session, context, options) do
    h.begin_stream(handler, session, context, options)
  end

end

defmodule GenAI.StreamHandler.Behaviour do
  @callback begin_stream(handler :: term, session :: term, context :: term, options :: term) :: {:ok, {{state :: term, handler :: term}, session :: term}} | {:error, reason :: term}
  @callback handle_event(event :: term, state :: term) :: {:cont, state :: term} | {:halt, state :: term}
end

defmodule GenAI.StreamHandler.Default do
  @behaviour GenAI.StreamHandler.Behaviour
  
  defmodule Accumulator do
    defstruct [
      headers: nil,
      status: nil,
      chunks: [],
      buffer: "",
      completed: false,
      error: nil,
      trailers: nil
    ]
  end
  
  @doc """
  Initialize streaming with default handler that returns handle_event callback
  """
  def begin_stream(_handler, session, _context, _options) do
    # Initialize state for collecting stream chunks
    initial_state = %Accumulator{}
    
    # Return initial state and callback function reference
    {:ok, {{initial_state, &__MODULE__.handle_event/2}, session}}
  end
  
  @doc """
  Handle streaming events from Finch.stream_while
  Processes status, headers, data chunks, and trailers
  """
  def handle_event(event, state) do
    case event do
      {:status, status} ->
        # Store HTTP status
        {:cont, Map.put(state, :status, status)}
        
      {:headers, headers} ->
        # Store headers
        {:cont, Map.put(state, :headers, headers)}
        
      {:data, chunk} ->
        # Accumulate data chunks
        updated_state = state
          |> Map.update(:chunks, [chunk], &(&1 ++ [chunk]))
          |> Map.update(:buffer, chunk, &(&1 <> chunk))
        {:cont, updated_state}
        
      {:trailers, trailers} ->
        # Store trailers if any
        {:cont, Map.put(state, :trailers, trailers)}
        
      _ ->
        # Continue with current state for unknown events
        {:cont, state}
    end
  end
end