TypeSafeAPI.HTTP (TypeSafe AI v0.1.0-alpha.3)

Copy Markdown View Source

Layer 1: the raw HTTP client. Maps in, maps out.

This module knows about URLs, headers, JSON, retries, telemetry, and how status codes map to TypeSafeAPI.Error types. It knows nothing about questions or answers. If the API grows a field tomorrow, this layer passes it through untouched, which is the point: the typed layer above can lag the API without the raw layer becoming useless.

client = TypeSafeAPI.new(api_key: "sk-...")

TypeSafeAPI.HTTP.post(client, "/v1/systemone", %{
  "state" => "Help! My payouts have been failing for 3 days.",
  "model" => "jev-latest",
  "questions" => %{"urgent" => %{"type" => "noul", "instructions" => "Is it urgent?"}}
})
#=> {:ok, %{"model" => "jev-latest", "answers" => %{...}, "usage" => %{...}}}

JSON is encoded with Elixir's built-in JSON module. Bodies may contain any term with a JSON.Encoder implementation, which is how the typed layer keeps Choice options in caller order.

Timeouts and connection pools

Req starts (or reuses) a Finch pool keyed by the connection settings of a request, so anything that changes those settings from call to call creates a pool per distinct value and throws connection reuse away.

What selects a pool:

  • the client's finch: option - names a pool you started and supervise yourself, which is the only way to control pool size. connect_options is then omitted entirely, connect_timeout included: those settings belong to your Finch child spec instead
  • req_options carrying connect_options: - a different value is a different pool. What this module sets as the connect timeout is merged under yours, so connect_timeout survives unless you set timeout: inside connect_options yourself
  • the client's connect_timeout, which is the connect_options timeout this module sets; it is fixed for the life of a client

What does not select a pool:

  • the client's timeout and the per-call :timeout, which means the same thing in TypeSafeAPI.evaluate_many/4; they set receive_timeout only, so two calls with different timeouts share a pool
  • :retry, :telemetry, headers, the body, the path

In short: vary :timeout per call as much as you like; set connect_options once, on the client, or hand the client a finch: pool of your own.

Summary

Types

Per-call options accepted by post/4 and get/3.

Result of request/5: the full response, not just the body.

Functions

Builds the Req.Request for a call without running it.

Sends a GET and returns the decoded JSON body.

Sends a JSON POST and returns the decoded JSON body.

Sends a request and returns the full TypeSafeAPI.HTTP.Response, including the status, headers, request_id, and how many retries it took.

The User-Agent sent with every request.

Types

call_option()

@type call_option() ::
  {:timeout, pos_integer()}
  | {:retry, TypeSafeAPI.Retry.t() | keyword()}
  | {:req_options, keyword()}
  | {:telemetry, map()}

Per-call options accepted by post/4 and get/3.

  • :timeout - overrides the client timeout for this call (milliseconds). Bounds waiting for the response only; the connect timeout is the client's connect_timeout, so this option never changes which Finch pool the call uses
  • :retry - a keyword list layers onto the client retry policy, changing only the settings it names; a %TypeSafeAPI.Retry{} replaces it wholesale
  • :req_options - extra Req options merged in last. The options this library owns (:retry, :auth, :base_url, :finch) are not yours to set here; use the matching client option
  • :telemetry - extra metadata merged into the telemetry events

full_response()

@type full_response() ::
  {:ok, TypeSafeAPI.HTTP.Response.t()} | {:error, TypeSafeAPI.Error.t()}

Result of request/5: the full response, not just the body.

response()

@type response() :: {:ok, map()} | {:error, TypeSafeAPI.Error.t()}

Functions

build(client, method, path, body, opts \\ [])

@spec build(TypeSafeAPI.Client.t(), :get | :post, String.t(), term(), [call_option()]) ::
  Req.Request.t()

Builds the Req.Request for a call without running it.

Exposed for inspection and for TypeSafeAPI.Test; most callers want post/4.

The per-call :timeout lands on receive_timeout; connect_options comes from the client alone, so two calls that differ only in :timeout build identical connect_options and share one connection pool.

Connection settings and retry: false are merged after every user option, so req_options can neither re-enable Req's own retry step on top of this library's nor drop the client's connect timeout.

get(client, path, opts \\ [])

Sends a GET and returns the decoded JSON body.

post(client, path, body, opts \\ [])

@spec post(TypeSafeAPI.Client.t(), String.t(), term(), [call_option()]) :: response()

Sends a JSON POST and returns the decoded JSON body.

request(client, method, path, body, opts \\ [])

@spec request(TypeSafeAPI.Client.t(), :get | :post, String.t(), term(), [
  call_option()
]) ::
  full_response()

Sends a request and returns the full TypeSafeAPI.HTTP.Response, including the status, headers, request_id, and how many retries it took.

body is JSON-encoded when present; pass nil for a bodiless request.

user_agent()

@spec user_agent() :: String.t()

The User-Agent sent with every request.