Connection settings for the TypeSafe API, resolved once and passed around.
The client is a plain struct rather than a process: nothing about talking to the API needs shared state, so a struct keeps concurrency trivial (build once, use from any number of tasks) and keeps supervision trees out of your way.
Configuration precedence
Each setting is resolved from, in order:
- the options passed to
new/1 - application config:
config :typesafe_api, api_key: "...", model: "..." - environment variables:
TYPESAFE_API_KEY,TYPESAFE_BASE_URL,TYPESAFE_DEFAULT_MODEL - the built-in defaults (
https://api.typesafe.ai,jev-latest, 10 000 ms)
The API key has no default; new/1 raises ArgumentError when none is found.
Placement
Because it is a plain struct with no process behind it, a client is safe to
share across processes: build one and pass it into every task, GenServer or
controller that needs it. Build it in a function, not in a module attribute.
A module attribute is evaluated at compile time, which reads the API key
from whatever environment compiled the release rather than the one running
it. A function called at application start, or a small Application.get_env
lookup memoized in :persistent_term, keeps configuration at runtime where
it belongs.
req_options merges rather than replaces. The client's req_options are
applied first, then the ones passed to a single call, so a per-call option
wins over the same key on the client. Anything the client sets and the call
does not mention survives.
Connections
Req picks a Finch connection pool by the connect_options it is given,
so two requests with different connect_options do not share a pool.
Varying that option per call spins up a separate pool each time and throws
away connection reuse. Set connect_options once on the client, in
req_options, and leave it out of per-call options.
The API key is redacted by this module's Inspect implementation, so it
does not leak through inspect/1, a crash dump or a logged struct. It is
never copied into telemetry metadata or into TypeSafe.Error bodies either;
those carry the request as sent minus the Authorization header.
Summary
Functions
Builds a client, resolving configuration as described in the module docs.
Types
@type t() :: %TypeSafe.Client{ api_key: String.t(), base_url: String.t(), model: String.t(), req_options: keyword(), retry: TypeSafe.Retry.t(), timeout: pos_integer() }
Functions
Builds a client, resolving configuration as described in the module docs.
Options
:api_key(String.t/0) - API key. Falls back to app config, thenTYPESAFE_API_KEY.:base_url(String.t/0) - API base URL. Falls back toTYPESAFE_BASE_URL, then the public API.:model(String.t/0) - Default model. Falls back toTYPESAFE_DEFAULT_MODEL, thenjev-latest.:timeout(pos_integer/0) - Per-operation timeout in milliseconds. Defaults to 10 000.:retry- Retry policy; seeTypeSafe.Retry.new/1. The default value is[].:req_options(keyword/0) - Escape hatch: options merged into the underlyingReq.Request. The default value is[].