test/openagents/voice/config_test.exs

58e6347eeb72 · 2 KB

defmodule OpenAgents.Voice.ConfigTest do
  use ExUnit.Case, async: true

  alias OpenAgents.Voice.Config

  test "admits one explicit native Realtime artifact without fallback" do
    config = Config.build!(valid_config())

    assert config.architecture == :openai_realtime
    assert config.model == "gpt-realtime-2.1"
    assert config.voice == "marin"

    assert Config.session_payload(config) == %{
             type: "realtime",
             model: "gpt-realtime-2.1",
             reasoning: %{effort: "low"},
             parallel_tool_calls: false,
             audio: %{
               input: %{
                 noise_reduction: %{type: "near_field"},
                 transcription: %{model: "gpt-4o-mini-transcribe", language: "en"},
                 turn_detection: %{
                   type: "semantic_vad",
                   create_response: false,
                   interrupt_response: true
                 }
               },
               output: %{voice: "marin"}
             }
           }
  end

  test "rejects unreviewed models, voices, and excessive session duration" do
    assert_raise ArgumentError, ~r/unadmitted Realtime model/, fn ->
      valid_config() |> Keyword.put(:model, "gpt-5.6-luna") |> Config.build!()
    end

    assert_raise ArgumentError, ~r/unadmitted voice artifact/, fn ->
      valid_config() |> Keyword.put(:voice, "leda") |> Config.build!()
    end

    assert_raise ArgumentError, ~r/invalid session duration/, fn ->
      valid_config() |> Keyword.put(:maximum_session_seconds, 3_301) |> Config.build!()
    end
  end

  test "attaches only bounded server-composed instructions and function definitions" do
    config =
      valid_config()
      |> Config.build!()
      |> Config.with_context("You are OpenAgents.", [
        %{
          "type" => "function",
          "name" => "memory_list",
          "description" => "List memory",
          "parameters" => %{"type" => "object", "properties" => %{}}
        }
      ])

    payload = Config.session_payload(config)
    assert payload.instructions == "You are OpenAgents."
    assert payload.tool_choice == "auto"
    assert [%{"name" => "memory_list"}] = Enum.map(payload.tools, &Map.take(&1, ["name"]))
  end

  test "enabled voice fails boot validation without a server credential" do
    config = Config.build!(valid_config())

    assert_raise ArgumentError, ~r/requires OPENAI_API_KEY/, fn ->
      Config.validate_runtime!(config, fn _name -> nil end)
    end

    assert :ok = Config.validate_runtime!(config, fn "OPENAI_API_KEY" -> "runtime-secret" end)

    disabled = Config.build!(Keyword.put(valid_config(), :enabled, false))
    assert :ok = Config.validate_runtime!(disabled, fn _name -> nil end)
  end

  defp valid_config do
    [
      enabled: true,
      architecture: :openai_realtime,
      provider: "openai",
      model: "gpt-realtime-2.1",
      voice: "marin",
      reasoning_effort: "low",
      maximum_session_seconds: 3_000
    ]
  end
end