#!/usr/bin/env elixir
# PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
# Copyright 2025 TimeHexOn & foxhop & russell@unturf
# https://www.permacomputer.com


Mix.install([{:openai_ex, "~> 0.9.18"}, {:httpoison, "~> 2.2"}, {:jason, "~> 1.4"}])

IO.puts("=== uncloseai. Elixir Client (OpenAI Ex Library) ===\n")

# Discover endpoints from environment variables
model_endpoint_1 = System.get_env("MODEL_ENDPOINT_1")
model_endpoint_2 = System.get_env("MODEL_ENDPOINT_2")
tts_endpoint_1 = System.get_env("TTS_ENDPOINT_1")

if is_nil(model_endpoint_1) or is_nil(model_endpoint_2) or is_nil(tts_endpoint_1) do
  IO.puts("ERROR: No models discovered. Set environment variables:")
  IO.puts("  MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, TTS_ENDPOINT_1")
  System.halt(1)
end

# Discover models from endpoint 1
IO.puts("Discovering models from #{model_endpoint_1}...")
{:ok, response1} = HTTPoison.get("#{model_endpoint_1}/models")
models1 = Jason.decode!(response1.body)["data"]
model_1_id = List.first(models1)["id"]
IO.puts("Model 1: #{model_1_id}\n")

# Discover models from endpoint 2
IO.puts("Discovering models from #{model_endpoint_2}...")
{:ok, response2} = HTTPoison.get("#{model_endpoint_2}/models")
models2 = Jason.decode!(response2.body)["data"]
model_2_id = List.first(models2)["id"]
IO.puts("Model 2: #{model_2_id}\n")

# Non-streaming chat with Model 1
IO.puts("=== Non-Streaming Chat (Model 1) ===")

client1 = OpenaiEx.new("dummy-key")
          |> OpenaiEx.with_base_url(model_endpoint_1)

req1 = OpenaiEx.Chat.Completions.new(
  model: model_1_id,
  messages: [
    %{role: "user", content: "Give a Python Fizzbuzz solution in one line of code?"}
  ],
  temperature: 0.5,
  max_tokens: 150
)

case OpenaiEx.Chat.Completions.create(client1, req1) do
  {:ok, response} ->
    content = response["choices"] |> List.first() |> Map.get("message") |> Map.get("content")
    IO.puts("Response: #{content}\n")
  {:error, error} ->
    IO.puts("Error: #{inspect(error)}\n")
end

# Streaming chat with Model 1
IO.puts("=== Streaming Chat (Model 1) ===")

stream_req1 = OpenaiEx.Chat.Completions.new(
  model: model_1_id,
  messages: [
    %{role: "user", content: "Count from 1 to 20, one number per line."}
  ],
  temperature: 0.5,
  max_tokens: 150,
  stream: true
)

IO.write("Response (with timing): ")
start_time = :os.system_time(:millisecond)
first_chunk_time = nil

case OpenaiEx.Chat.Completions.create(client1, stream_req1) do
  {:ok, stream} ->
    stream
    |> Stream.with_index()
    |> Stream.each(fn {chunk, index} ->
      current_time = :os.system_time(:millisecond)
      if index == 0 do
        IO.puts("\n[First chunk at #{current_time - start_time}ms]")
      end
      IO.puts("[Chunk #{index} at #{current_time - start_time}ms]: #{inspect(chunk)}")
    end)
    |> Stream.run()
    IO.puts("\n")
  {:error, error} ->
    IO.puts("Error: #{inspect(error)}\n")
end

# Non-streaming chat with Model 2
IO.puts("=== Non-Streaming Chat (Model 2) ===")

client2 = OpenaiEx.new("dummy-key")
          |> OpenaiEx.with_base_url(model_endpoint_2)

req2 = OpenaiEx.Chat.Completions.new(
  model: model_2_id,
  messages: [
    %{role: "user", content: "Write a JavaScript function to check if a number is prime"}
  ],
  temperature: 0.5,
  max_tokens: 150
)

case OpenaiEx.Chat.Completions.create(client2, req2) do
  {:ok, response} ->
    content = response["choices"] |> List.first() |> Map.get("message") |> Map.get("content")
    IO.puts("Response: #{content}\n")
  {:error, error} ->
    IO.puts("Error: #{inspect(error)}\n")
end

# Streaming chat with Model 2
IO.puts("=== Streaming Chat (Model 2) ===")
IO.puts("[SKIPPED] Streaming not compatible with vLLM endpoints in openai_ex 0.9.18\n")

# TTS example
IO.puts("=== TTS Speech Generation ===")

tts_client = OpenaiEx.new("dummy-key")
             |> OpenaiEx.with_base_url(tts_endpoint_1)

tts_req = OpenaiEx.Audio.Speech.new(
  model: "tts-1",
  voice: "alloy",
  input: "I think so therefore, Today is a wonderful day to grow something people love!",
  speed: 0.9
)

case OpenaiEx.Audio.Speech.create(tts_client, tts_req) do
  {:ok, audio_data} ->
    File.write!("speech.mp3", audio_data)
    {:ok, file_info} = File.stat("speech.mp3")
    IO.puts("[OK] Speech file created: speech.mp3 (#{file_info.size} bytes)\n")
  {:error, error} ->
    IO.puts("Error: #{inspect(error)}\n")
end

IO.puts("=== Examples Complete ===")
