docs.bend
bend-openai-sdk@0.1.0.1 · 237 lines · 0 definitions
1# An unofficial OpenAI SDK for Bend, with typed requests, streaming, and tool calling.
2
3import Base
4
5# # openai-bend
6#
7# **An unofficial OpenAI SDK for Bend, with typed requests, streaming, and tool calling.**
8#
9# Write your application in Bend. Use OpenAI's official TypeScript SDK for the network connection. A small, authenticated companion process connects the two over localhost.
10#
11# [Examples](https://github.com/gouveags/openai-bend/tree/main/examples) · [Source](https://github.com/gouveags/openai-bend) · [Report an issue](https://github.com/gouveags/openai-bend/issues) · [Anthropic companion project](https://github.com/gouveags/anthropic-bend)
12#
13# > Experimental companion v0.1.1 / Bend library v0.1.0. This is a community project, not an official OpenAI or Bend project. It implements the foreground Responses API. It is not a feature-complete replacement for the Python or TypeScript SDKs.
14#
15# ## Install from Bend Hub
16#
17# [**bend-openai-sdk@0.1.0.1**](https://hub.bend-lang.com/n/bend-openai-sdk) · [Packaged documentation](https://github.com/gouveags/openai-bend/blob/main/docs.bend) · [Source and examples](https://github.com/gouveags/openai-bend)
18#
19# Use **Bend 2.0.34 or newer** for named imports. Bend fetches the exact release, resolves it to an immutable content hash, and verifies the downloaded files. There is no separate install command for the Bend modules.
20#
21# ```bend
22# import bend-openai-sdk@0.1.0.1/openai.bend as OpenAI
23# import bend-openai-sdk@0.1.0.1/json.bend as Json
24# ```
25#
26# Install and start the localhost companion below as well: the Hub package contains the Bend modules, documentation and MIT license, not the Bun companion or its dependencies. Provider API keys stay in the companion process.
27#
28# Release `0.1.0.1` fixes the JSON parser constructor collision with Bend 2.0.28+ and updates the example CLIs for Bend 2.0.34. The SDK API and bridge protocol are unchanged. Existing imports of the [previous hash](https://hub.bend-lang.com/0x7d1714d98ca88352be07e0302e547a19) remain available, but require the older compiler they were published with. Use `0.1.0.1` on current Bend. Published versions are immutable; future updates use a new version.
29#
30# ## What you get
31#
32# | Capability | v0.1.0 |
33# | --------------------------------------------------- | ------------------------------------------------------------------ |
34# | Create responses | Typed request builder plus arbitrary JSON options |
35# | Streaming | Every semantic event, including unknown future events |
36# | Response helpers | Text, function calls, usage and complete raw JSON |
37# | Structured outputs | JSON Schema request helper; application-controlled decoding |
38# | Function calling | Tool definitions, typed call extraction and function-output helper |
39# | Images and files | References in request JSON; no upload helper |
40# | Cancellation | Close a stream, or cancel by ID over a separate connection |
41# | Error metadata | API status, request ID and Retry-After |
42# | Native / JavaScript | Same Bend API and companion protocol |
43# | Uploads, pagination, background responses, Realtime | Not implemented |
44#
45# Function execution is deliberately explicit: the library never executes a command or function just because the model named it. Refusals, failed/incomplete responses, annotations and provider-specific details remain available in the raw response or event.
46#
47# ## Requirements
48#
49# - Linux or macOS. The tested platform is Linux x86_64; macOS is not yet validated.
50# - [Bend 2.0.34+](https://github.com/bendlang/bend), [Bun](https://bun.sh/), and Git.
51# - Clang 14+ and Make for native builds.
52# - An OpenAI API key and access to your selected model for real inference.
53#
54# The companion is a separate local process. **Installing the Bend Hub package does not install Bun, the companion, or its dependencies.**
55#
56# ## Install the companion
57#
58# ```sh
59# git clone https://github.com/gouveags/openai-bend.git
60# cd openai-bend
61# git checkout main
62# bun install --frozen-lockfile
63# ```
64#
65# Load your API key into `OPENAI_API_KEY` using your secret manager or shell's private input facility. Do not put it in source code, commit an environment file, or paste it into a command that will be saved in shell history.
66#
67# Generate a separate local bridge token:
68#
69# ```sh
70# export OPENAI_BEND_TOKEN="$(openssl rand -hex 32)"
71# export OPENAI_BEND_PORT=42101
72# export OPENAI_MODEL='your-available-model-id'
73# bun run bridge
74# ```
75#
76# Expected startup message:
77#
78# ```text
79# openai-bend 0.1.1 listening on 127.0.0.1:42101 (protocol 1)
80# ```
81#
82# Your Bend process needs `OPENAI_BEND_TOKEN`, `OPENAI_BEND_PORT` and your model selection. **It does not need the OpenAI API key.** When using separate terminals, share the same bridge token through your local secret-management mechanism. The bridge does not print credentials.
83#
84# ## Run an example
85#
86# From another terminal with the bridge token available:
87#
88# ```sh
89# make build/request.js
90# bun build/request.js example-1 once \
91# "{\"model\":\"$OPENAI_MODEL\",\"input\":\"Say hello.\"}"
92#
93# # Stream every event:
94# bun build/request.js example-2 stream \
95# "{\"model\":\"$OPENAI_MODEL\",\"input\":\"Explain interaction nets briefly.\"}"
96# ```
97#
98# The command prints the result or each semantic event, then `finished`. Provider, protocol and transport failures exit nonzero. `finished` means the transport ended correctly: inspect the response status or terminal event to distinguish completed, failed and incomplete model output.
99#
100# For native execution:
101#
102# ```sh
103# make build/request
104# ./build/request example-3 once \
105# "{\"model\":\"$OPENAI_MODEL\",\"input\":\"Say hello.\"}"
106# ```
107#
108# The Makefile emits C using Bend and compiles with Clang `-O1` by default to keep development builds manageable. Override `CFLAGS` for an optimized build after validating it on your machine. This project makes no performance claim.
109#
110# ## Use the Bend library
111#
112# Within this checkout, a minimal typed request looks like this:
113#
114# ```bend
115# import Base
116# import ./openai.bend as OpenAI
117#
118# def start(token: String, model: String) ->
119# IO(Result<&1, &1, OpenAI.Error, OpenAI.Stream>):
120# OpenAI.create(
121# OpenAI.Client{42101, token},
122# "my-unique-request-id",
123# OpenAI.text_request(model, "Say hello."))
124# ```
125#
126# `create` and `stream` return an owned stream handle. Call `OpenAI.next` until it returns:
127#
128# - `Event{stream, kind, data, request_id}` — process the event and use the returned stream for the next read.
129# - `Finished{}` — the connection has closed cleanly.
130# - `Failed{error}` — the connection has closed with a transport, API or protocol error.
131#
132# For nonstreaming calls, the event kind is `result`. Streaming calls first expose `metadata`, followed by provider event names such as `response.output_text.delta`. All events include their original JSON payload. `OpenAI.response(data)` extracts a typed response from a `result` payload; for a terminal streaming event, pass its nested `response` object.
133#
134# Call `OpenAI.close(stream)` if you stop consuming early. To cancel a blocked request from another Bend computation, use `OpenAI.cancel(client, request_id)`. Cancellation requests an upstream abort; it cannot guarantee that remote processing or billing has not already happened.
135#
136# See [the complete reader](https://github.com/gouveags/openai-bend/blob/main/examples/request.bend), [hello](https://github.com/gouveags/openai-bend/blob/main/examples/hello.bend), [structured outputs](https://github.com/gouveags/openai-bend/blob/main/examples/structured.bend), [tools](https://github.com/gouveags/openai-bend/blob/main/examples/tools.bend), and [cancellation](https://github.com/gouveags/openai-bend/blob/main/examples/cancel.bend). All examples read `OPENAI_BEND_PORT` and `OPENAI_BEND_TOKEN`.
137#
138# ## Structured outputs and tools
139#
140# `OpenAI.Request{model, input, options}` accepts a JSON input and additional fields. Use `Json.Text` for a simple prompt, or a `Json.Array` for messages, image/file references and tool results.
141#
142# ```bend
143# # Add these fields to Request.options:
144# Json.Field{"text", OpenAI.json_schema("person", schema)}
145# Json.Field{"tools", Json.Array{[
146# OpenAI.function_tool("weather", "Look up weather", schema)
147# ]}}
148# ```
149#
150# The schema must meet [OpenAI's supported JSON Schema rules](https://developers.openai.com/api/docs/guides/structured-outputs). Structured output is not automatically converted into an arbitrary Bend datatype: inspect the response status/refusal, parse its text with `Json.read`, then validate your application's fields.
151#
152# For tools, inspect `Response.calls`. Each `FunctionCall` contains `id`, `call_id`, `name`, JSON-encoded `arguments`, and the complete raw item. Validate the name and arguments against an application-owned allowlist before executing anything. Return a result with `OpenAI.function_output(call_id, output)`.
153#
154# For a stored-response continuation, submit that item in the next input with `previous_response_id` set to the response ID. Repeat instructions and tool definitions as needed; instructions are not automatically inherited. For stateless/manual replay, follow the [official continuation guidance](https://developers.openai.com/api/docs/guides/function-calling), including reasoning items. There is no automatic tool loop in this release.
155#
156# ## JSON and forward compatibility
157#
158# `json.bend` supplies `Null`, `Boolean`, `Number`, `Text`, `Array` and `Object`, with `Field` entries for objects. `Json.get` returns `None` for an absent property and `Some{Null{}}` for explicit null. Omit an option by leaving it out of the field list.
159#
160# Numbers are stored as decimal strings and do not pass through Bend's `F32`. The companion uses JavaScript JSON and the official SDK, so its normal IEEE-754 numeric limitations still apply. Do not send integers beyond JavaScript's safe range expecting lossless end-to-end arithmetic.
161#
162# Use valid JSON number strings in `Json.Number`. Unknown response fields and event types are preserved. `OpenAI.open_raw` accepts an arbitrary JSON request object for newly added fields, but supports only the allowlisted Responses create operation. Request options cannot change the companion's URL, credentials or SDK method.
163#
164# ## Architecture and security
165#
166# ```text
167# Bend application ── authenticated localhost TCP ── companion
168# │
169# official openai SDK
170# │ HTTPS
171# OpenAI API
172# ```
173#
174# - The companion binds only `127.0.0.1`. It is not a public server and must not be exposed through a proxy or tunnel.
175# - A token of at least 32 characters is required. Use a randomly generated token, not a memorable password. API credentials remain in the companion process.
176# - One request per connection; cancellation uses a separate connection and request ID. Use unique IDs for simultaneous requests.
177# - JSON decoding is limited to 64 nesting levels and 65536 tokens. Requests are capped at 1 MiB; output frames at 4 MiB; concurrent connections at 32. Partial requests expire after 10 seconds.
178# - The upstream deadline defaults to 120 seconds and can be configured with `OPENAI_BEND_DEADLINE_MS`, up to 600000. Socket inactivity is also bounded.
179# - The official SDK owns retries (two retries configured in the CLI). The bridge does not replay requests after disconnect or restart. Do not automatically retry a partly received inference result without considering duplicate execution/cost.
180# - Frames are newline-delimited JSON, with non-ASCII characters escaped. The Bend reader handles fragmented/coalesced frames and Unicode surrogate pairs.
181# - The CLI fixes the upstream URL to `https://api.openai.com/v1`. Alternate upstream clients exist only as programmatic configuration for tests or an operator-controlled integration.
182# - No telemetry or prompt logging is implemented by this project. Provider error messages are returned to the authenticated caller and may contain submitted content. Example programs intentionally print responses.
183#
184# The bridge token is sent over local TCP without TLS. This design assumes a trusted local machine and companion. It does not protect against a compromised local account, privileged process, malicious replacement bridge, or someone who can read your process environment. Bend's termination/type checks do not prove the socket runtime, provider behavior or the entire SDK secure. The source marks the few externally driven or structurally unrecognized recursive functions `@unsafe` explicitly.
185#
186# ## Development and validation
187#
188# ```sh
189# bun install --frozen-lockfile
190# make test
191# bun audit
192# ```
193#
194# Tests use the actual OpenAI TypeScript SDK against a local HTTP mock, plus real TCP connections and compiled Bend clients. They require no real API key and make no inference requests to OpenAI. A successful local mock run proves protocol and integration behavior, not model behavior or account access.
195#
196# The dependency lockfile is committed. Formatting and TypeScript checks are part of `make test`. See [compatibility.json](https://github.com/gouveags/openai-bend/blob/main/compatibility.json) for the pinned compiler, companion protocol and SDK versions.
197#
198# ## Troubleshooting
199#
200# | Symptom | Check |
201# | ------------------------------------- | ------------------------------------------------------------------------------- |
202# | Connection refused | Start the companion and check the port. |
203# | Unauthorized / request-ID mismatch | Use the same bridge token in both processes. |
204# | API 401/403 | Check the companion's API key and project/model access. |
205# | API 429 | Inspect error metadata; avoid adding another unbounded retry loop. |
206# | Truncated stream | Treat the partial output as incomplete; do not silently call it success. |
207# | Native build consumes too much memory | Use the tested compiler and default Makefile flags; report a reproducible case. |
208#
209# ## License
210#
211# MIT. The OpenAI TypeScript SDK is a separately licensed dependency. Bend is a separate project. Contributions and reproducible bug reports are welcome at [GitHub](https://github.com/gouveags/openai-bend/issues).
212#
213# ### Companion 0.1.1
214#
215# The command-line examples return a nonzero exit status for failed or incomplete responses. SDK diagnostic logging is disabled. This companion patch retains the library API from v0.1.0 and works with the named Hub release above.
216#
217# ## Releases and live validation
218#
219# [Release workflow and credential renewal](https://github.com/gouveags/openai-bend/blob/main/docs/releasing.md). New Hub versions publish automatically after their version bump lands on `main` and CI succeeds.
220#
221# For an explicitly authorized real-provider check, load your API key into the environment securely, build with the compiler in `compatibility.json`, then run:
222#
223# ```sh
224# make build
225# SDK_LIVE_TEST=1 bun run test:live
226# ```
227#
228# This makes two billed requests (JavaScript nonstreaming and native streaming), each limited to 64 output tokens with retries disabled. It checks the actual reply and completion status. Regular `make test` uses local mocks and does not call a paid API. Provider keys are never needed by the release workflow.
229#
230# Live smoke validation on September 30, 2026 passed with `gpt-4.1-nano`: both the JavaScript nonstreaming and native streaming clients received a completed `SDK_OK` reply. This verifies basic real-provider integration, not every model or API feature.
231#
232
233def repository() -> String:
234 "https://github.com/gouveags/openai-bend"
235
236def version() -> String:
237 "0.1.0"