Documentation/Libraries

OpenAI-compatible API client

On this page

Pure BASIL REST and incremental Server-Sent Events (SSE), using BASIL’s existing HTTP/TLS, JSON, files, tasks, and SHA-256 library. No Python process, curl process, or new C bridge runs in an application.

Install as a source dependency

In your application’s basic.toml:

[dependencies]
OpenAI = {path = "../basic/libraries/openai"}

Then IMPORT OpenAI and run basic build in the application directory. The path can be relative to the application’s manifest. Neither Python nor code generation is needed to build or run an application.

Packaging limitation: use the source dependency, rather than building this library into a .blib. The current compiler’s precompiled-library ABI rejects the public Stream type because it owns a TCPConnection. Source imports work and preserve ownership/cleanup. The compiler and runtime have not been changed.

Chat completions

IMPORT OpenAI

client = OpenAI.NewClient("YOUR_API_KEY", "https://api.openai.com/v1")
request = OpenAI.ChatRequest("YOUR_MODEL", [OpenAI.User("Hello!")])
request = OpenAI.WithField(request, "max_completion_tokens", JsonNumber(256))
reply = OpenAI.ChatCreate(client, request)
PRINT OpenAI.OutputText(reply)

Library functions expose FAILS OpenAI.APIError. Application functions should catch or declare it. APIError has Kind, Status, Code, Message, Parameter, RequestID, Body (bounded error text), and ResponseHeaders.

The base URL includes the API prefix. For Fireworks, use https://api.fireworks.ai/inference/v1 and a Fireworks model identifier. Providers implement different subsets of the OpenAI API and may require different options; e.g. Fireworks chat uses max_tokens. The library does not silently translate one provider’s model parameters into another provider’s parameters.

Stream reasoning, answers, and tool calls

stream = OpenAI.ChatStream(client, request)
completion = OpenAI.NewCompletion()
WHILE NOT OpenAI.IsFinished(stream)
    IF LET event = OpenAI.NextEvent(BYREF stream) THEN
        OpenAI.Accumulate(BYREF completion, event)
        thinking = OpenAI.ReasoningDelta(event)
        answer = OpenAI.TextDelta(event)
        ' Consume each nonempty delta immediately.
        ' event.Data retains the complete JSON event, including provider fields.
    END IF
WEND
assistantMessage = OpenAI.CompletionMessage(completion)

Accumulate retains multiple choices, fragmented tool names/IDs/arguments, reasoning, refusals, finish reasons, and usage. Enable stream_options.include_usage in your request to ask for final usage. CompletionMessage(completion, 0, TRUE) includes Fireworks-style reasoning_content when continuing a conversation; leave the last argument false for providers that do not accept that field. Tool calls are returned to your application, which decides whether and how to execute them and sends ToolResult(id, content) messages. This client does not execute model-requested tools automatically.

ReasoningDelta reads provider-supplied reasoning_content / reasoning and Responses reasoning text/summary events. Some models expose no reasoning at all; the client cannot reveal reasoning the server does not supply.

Use CloseStream(BYREF stream) when stopping early. Streams own their connection and normal BASIL ownership cleanup also releases it on return/error/task cancellation. NextSSE exposes unparsed SSE messages; NextEvent decodes JSON. SSE framing handles split UTF-8, BOM, comments, CR/LF/CRLF, multiline data, IDs, and retry metadata. Chat/Completions streams require [DONE]; Responses also recognizes its completed/failed/incomplete terminal events. A missing terminal marker is an error. Inspect response.failed / response.incomplete event data; these terminal Responses events are returned to the application.

The client retries opening a stream, never a partially consumed response. SSE retry is metadata, not an instruction to reconnect/replay an inference request.

Responses

request = OpenAI.ResponseRequest("YOUR_MODEL", "Hello!")
reply = OpenAI.ResponsesCreate(client, request)
PRINT OpenAI.OutputText(reply)

stream = OpenAI.ResponseStream(client, request)
WHILE NOT OpenAI.IsFinished(stream)
    IF LET event = OpenAI.NextEvent(BYREF stream) THEN
        ' TextDelta / ReasoningDelta also understand Responses events.
        ' Other events, including tool calls, remain available in event.Data.
    END IF
WEND

Complete REST access

ENDPOINTS.md lists 354 named HTTP operations from the pinned 2026-11-09 official reference snapshot. Coverage includes Chat Completions, Responses, embeddings, moderation, images, audio, files/uploads, batches, fine-tuning, vector stores, assistants/threads/runs, containers, videos, conversations, evals, administration, and additional resources in the snapshot. The catalogue records the method, path, source link, and source hash. Named wrappers preserve unrestricted JSON request/response bodies instead of duplicating all Python SDK schema classes in BASIL.

Each named method accepts escaped string path identifiers, a JSON body where applicable, and a query dictionary. Convenience overloads omit the query. JSON methods have a Raw companion preserving status, headers, and bytes; its signature includes the body (if applicable) and query explicitly. Binary methods already return HTTPResponse. Multipart operations accept LIST OF OpenAI.Part and also offer MultipartRaw. Documented SSE operations offer a Stream companion; its signature includes the query dictionary.

query = OpenAI.Parameters()
Put(BYREF query, "limit", "20")
models = OpenAI.ModelsList(client, query)
model = OpenAI.ModelsRetrieve(client, "some-model-id")

' Provider extensions or newly introduced endpoints need no SDK update:
result = OpenAI.RequestJSON(client, "POST", "/some/path", OpenAI.Object())

Generic transport functions are RequestJSON, RequestJSONRaw, RequestRaw, OpenStream (POST JSON with stream=true), OpenEventStream (explicit method, body and query), and OpenBodyStream + ReadChunk (incremental binary). Pass API paths beginning with /, without a query string or host; use a query dictionary separately. Named wrappers escape IDs automatically; generic paths can use Segment(id) for an individual identifier.

ListPage / NextPage implement ordinary data / has_more / after cursor pagination. Resource-specific pagination formats and asynchronous job polling remain accessible through ordinary requests; callers control their loops.

Files, images, audio and binary responses

parts = [OpenAI.TextPart("purpose", "assistants"), OpenAI.ReadFilePart("file", "notes.txt", "text/plain")]
uploaded = OpenAI.FilesCreate(client, parts)

response = OpenAI.FilesContentRetrieve(client, "file-id")
' response.Body is LIST OF BYTE; never decoded as UTF-8.

FilePart(name, filename, bytes, contentType) builds a part from memory. ReadFilePart reads a stable file snapshot and reports changes during reading. Repeated part names support array fields. Multipart boundaries are checked against binary contents, and names/filenames are quoted and validated. RequestMultipartJSON parses JSON; RequestMultipart preserves arbitrary bytes, including plain-text transcription formats. OpenMultipartStream consumes SSE responses to multipart requests; include a text part stream=true explicitly.

Download(client, path, file, query) writes to an already-open BASIL File incrementally. On failure a partial output file may remain; callers can use a temporary file and rename after success. Base64Encode / Base64Decode help with data URLs and JSON-encoded media. TextPart(text) and ImagePart(url, detail) construct multimodal chat content parts. Use JSON directly for other media types.

REST audio uploads/generation and Realtime session/control HTTP endpoints are included. Bidirectional audio and WebSocket/WebRTC transports are outside this first iteration.

Configuration and reliability

Client values are immutable-style configuration: each With... function returns a new client. Available helpers:

  • WithRetries(client, count): 0–10 retries, default 2; connection failures and HTTP 408/409/429/5xx. Numeric retry-after / retry-after-ms is honored up to 60 seconds; otherwise exponential 500ms–8s delay. HTTP-date retry hints are not interpreted. Consider disabling retries for operations where replay is unsafe, or supply a server-supported idempotency key with WithHeader.
  • WithTimeout(client, duration): header/body/write deadlines; default 30s header and 120s body/write. Body timeout is the underlying exchange deadline, not a per-token idle timer. Increase it for long inference streams.
  • WithLimits(client, requestBytes, responseBytes): default 64MiB request and 256MiB response. SSE has a 1MiB per-line/event limit. Multipart uploads are assembled in memory; use bounded upload parts for very large files.
  • WithHeader(client, name, value): project, organization, beta, idempotency and custom headers. Authentication/framing headers are reserved.
  • WithTLS(client, options): existing BASIL TLS options (e.g. a custom trust store).

TLS certificate/hostname verification uses the existing BASIL HTTP/TLS defaults. The SDK never follows redirects with authentication. Error bodies are bounded to 16KiB of text in APIError (the underlying response still obeys its configured response byte limit). Raw response helpers preserve all successful bytes/headers.

Webhooks

VerifyWebhook(payloadBytes, headers, secret) validates Standard Webhooks v1 HMAC-SHA256 signatures over the original body bytes, supporting whsec_ base64 secrets, multiple signatures during rotation, and a default ±300s timestamp tolerance. UnwrapWebhook additionally parses the verified JSON. Optional nowSeconds and toleranceSeconds support deterministic tests. The signed ID still needs application-level deduplication if processing must happen once. Asymmetric v1a webhook signatures are not implemented.

Tests and maintenance

python3 tests/run.py

Python is only a local mock HTTP server/test runner. Tests compile and exercise real BASIL code, use no external service, and incur no inference charges. Set BASIL=/path/to/basic to select another compiler. The runner otherwise uses this repository’s build/selfhost/stage3.

The companion TUI lives in ~/src/bt/ai-chat. Its PTY tests cover actual input, streaming redraw, history, cancellation, resizing, exit and terminal restoration. Live Fireworks checks covered authentication/model listing and bounded streamed Chat Completions, including reasoning and answer deltas. Other endpoint families are bindings based on the pinned reference, not claims of live Fireworks support or live verification against every OpenAI resource.

Edit src/*.bas.in, then run python3 generate.py to update checked-in source. python3 spec/fetch.py intentionally refreshes the official reference catalog using the network; review its changes, then regenerate. Ordinary builds never fetch documentation or run either generator.

References: official API reference , Responses streaming , webhooks , Fireworks reasoning .

Portable packaging

The source package is self-contained, including a generated local copy of the standard BASIL SHA-256 module used for HMAC/webhooks. generate.py refreshes that copy from stdlib/sha256.bas; runtime use requires no generator. With the compiler’s native-resource .blib support, basic build --manifest basic.toml -o build/openai.blib produces a portable dependency, including the HTTP/stream resource interface.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.