-module(gllm). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/gllm.gleam"). -export([new_message/2, message_to_json/1, completion/4]). -export_type([client/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " The main gllm module.\n" " This module contains high-level functions and some types for interacting with OpenAI compatible APIs.\n" "\n" " It includes functionality for creating chat completion requests, handling responses,\n" " and managing API authentication. The client supports customizable base URLs\n" " and temperature parameters for controlling response randomness.\n" "\n" " ## Example\n" " ```gleam\n" " import gllm\n" " import gllm/types/message\n" " import glenvy/dotenv\n" " import glenvy/env\n" " import gllm\n" "\n" " let assert Ok(api_key) = env.string(\"OPENROUTER_API_KEY\")\n" " let base_url = \"https://openrouter.ai/api/v1\"\n" "\n" " let client = gllm.Client(api_key, base_url)\n" "\n" " let messages = [\n" " gllm.new_message(\"system\", \"You are a helpful assistant.\"),\n" " gllm.new_message(\"user\", \"Hello, how are you?\")\n" " ]\n" "\n" " gllm.completion(client, \"openai/gpt-oss:20b\", messages, 0.7)\n" " ```\n" ). -type client() :: {client, binary(), binary()}. -file("src/gllm.gleam", 67). ?DOC( " Creates a new message with the given role and content.\n" "\n" " ## Parameters\n" "\n" " - `role`: The role of the message sender (e.g., \"system\", \"user\", \"assistant\")\n" " - `content`: The text content of the message\n" "\n" " ## Returns\n" "\n" " Returns a `Message` type with optional fields set to `None`.\n" "\n" " ## Examples\n" "\n" " ```\n" " new_message(\"user\", \"Hello, world!\")\n" " // -> Message(\"user\", \"Hello, world!\", None, None, None)\n" " ```\n" ). -spec new_message(binary(), binary()) -> gllm@types@message:message(). new_message(Role, Content) -> {message, Role, Content, none, none, none}. -file("src/gllm.gleam", 91). ?DOC( " Converts a Message type to a JSON object for API requests.\n" "\n" " ## Parameters\n" "\n" " - `message`: The Message to convert\n" "\n" " ## Returns\n" "\n" " Returns a JSON object containing the role and content fields.\n" "\n" " ## Examples\n" "\n" " ```\n" " let msg = new_message(\"user\", \"Hello\")\n" " message_to_json(msg)\n" " // -> json.object([#(\"role\", json.string(\"user\")), #(\"content\", json.string(\"Hello\"))])\n" " ```\n" ). -spec message_to_json(gllm@types@message:message()) -> gleam@json:json(). message_to_json(Message) -> gleam@json:object( [{<<"role"/utf8>>, gleam@json:string(erlang:element(2, Message))}, {<<"content"/utf8>>, gleam@json:string(erlang:element(3, Message))}] ). -file("src/gllm.gleam", 124). ?DOC( " Sends a chat completion request to the API.\n" "\n" " ## Parameters\n" "\n" " - `client`: The API client containing authentication and endpoint information\n" " - `model`: The model identifier to use for completion (e.g., \"gpt-3.5-turbo\")\n" " - `messages`: A list of messages representing the conversation history\n" " - `temperature`: Controls randomness in the response (0.0 to 2.0, lower = more focused)\n" "\n" " ## Returns\n" "\n" " Returns `Ok(ChatCompletion)` on success, or `Error(ApiError)` on failure.\n" "\n" " ## Errors\n" "\n" " - `ApiError`: Unexpected JSON response from the API or Http error\n" "\n" " ## Example\n" "\n" " ```\n" " let client = Client(\"api-key\", \"https://api.openai.com\")\n" " let messages = [new_message(\"user\", \"Hello\")]\n" "\n" " completion(client, \"gpt-3.5-turbo\", messages, 0.7)\n" " // -> Ok(ChatCompletion(...))\n" " ```\n" ). -spec completion( client(), binary(), list(gllm@types@message:message()), float() ) -> {ok, gllm@types@chat_completion:chat_completion()} | {error, gllm@types@api_error:api_error()}. completion(Client, Model, Messages, Temperature) -> Base_url@1 = case gleam_stdlib:uri_parse(erlang:element(3, Client)) of {ok, Base_url} -> Base_url; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"gllm"/utf8>>, function => <<"completion"/utf8>>, line => 130, value => _assert_fail, start => 3758, 'end' => 3810, pattern_start => 3769, pattern_end => 3781}) end, Body = begin _pipe = gleam@json:object( [{<<"model"/utf8>>, gleam@json:string(Model)}, {<<"messages"/utf8>>, gleam@json:array( gleam@list:map(Messages, fun message_to_json/1), fun(X) -> X end )}, {<<"temperature"/utf8>>, gleam@json:float(Temperature)}] ), gleam@json:to_string(_pipe) end, Req = begin _pipe@1 = gleam@http@request:new(), _pipe@2 = gleam@http@request:set_method(_pipe@1, post), _pipe@3 = gleam@http@request:set_host( _pipe@2, gleam@option:unwrap( erlang:element(4, Base_url@1), <<"api.openai.com"/utf8>> ) ), _pipe@4 = gleam@http@request:set_path( _pipe@3, (<<(case erlang:element(6, Base_url@1) of <<""/utf8>> -> <<"/v1"/utf8>>; _ -> erlang:element(6, Base_url@1) end)/binary, "/chat/completions"/utf8>>) ), _pipe@5 = gleam@http@request:set_body(_pipe@4, Body), _pipe@6 = gleam@http@request:prepend_header( _pipe@5, <<"Content-Type"/utf8>>, <<"application/json"/utf8>> ), gleam@http@request:prepend_header( _pipe@6, <<"Authorization"/utf8>>, <<"Bearer "/utf8, (erlang:element(2, Client))/binary>> ) end, gleam@result:'try'( begin _pipe@7 = gleam@httpc:send(Req), gleam@result:map_error( _pipe@7, fun(Field@0) -> {http_error, Field@0} end ) end, fun(Resp) -> gleam@result:'try'( begin _pipe@8 = gllm@types@chat_completion:parse_chat_completion( erlang:element(4, Resp) ), gleam@result:map_error( _pipe@8, fun(Field@0) -> {json_decode_error, Field@0} end ) end, fun(Completion) -> {ok, Completion} end ) end ).