Text generation
Generate text with the OpenAI-compatible Chat Completions API and the newer single-input Responses API.
The gateway serves text through two OpenAI-shaped surfaces: Chat Completions
(/v1/chat/completions), the messages-array API most SDKs default to, and Responses
(/v1/responses), a newer single-input API that folds tool calls, reasoning, and multi-turn
state into one object. Both bill the same way and run the same models — pick per call site, not
once for the whole app.
Chat Completions
POST /v1/chat/completions takes a required model and a messages array — each message has a
role (system, user, assistant, or tool) and content. A system message sets behavior
for the whole conversation; alternating user/assistant messages carry the turn history.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://gateway.venna.net/v1",
apiKey: process.env.VENNA_API_KEY,
});
const completion = await client.chat.completions.create({
model: "venna-fast",
messages: [
{ role: "system", content: "You are a terse release-notes writer." },
{ role: "user", content: "Summarize: fixed a race in the retry queue." },
],
});
console.log(completion.choices[0]?.message.content);
console.log(completion.usage);import os
from openai import OpenAI
client = OpenAI(
base_url="https://gateway.venna.net/v1",
api_key=os.environ["VENNA_API_KEY"],
)
completion = client.chat.completions.create(
model="venna-fast",
messages=[
{"role": "system", "content": "You are a terse release-notes writer."},
{"role": "user", "content": "Summarize: fixed a race in the retry queue."},
],
)
print(completion.choices[0].message.content)
print(completion.usage)curl https://gateway.venna.net/v1/chat/completions \
-H "Authorization: Bearer $VENNA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "venna-fast",
"messages": [
{ "role": "system", "content": "You are a terse release-notes writer." },
{ "role": "user", "content": "Summarize: fixed a race in the retry queue." }
]
}'The response is a chat.completion object: a choices array — index into choices[0].message
for the assistant reply, and check choices[0].finish_reason (stop, length, tool_calls, or
content_filter) to know why generation ended — plus a usage block with prompt_tokens,
completion_tokens, and total_tokens, which is what you're billed against.
To keep a conversation going, append the assistant's reply and the next user turn to messages
and resend the whole array — the gateway is stateless per request; it has no server-side memory
of prior turns on this endpoint.
Streaming
Set "stream": true to get the same completion as server-sent events instead of one JSON blob —
useful for showing tokens as they generate rather than waiting on the full response.
const stream = await client.chat.completions.create({
model: "venna-fast",
messages: [{ role: "user", content: "Stream a haiku about throughput." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta.content ?? "");
}stream = client.chat.completions.create(
model="venna-fast",
messages=[{"role": "user", "content": "Stream a haiku about throughput."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)curl https://gateway.venna.net/v1/chat/completions \
-H "Authorization: Bearer $VENNA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "venna-fast",
"messages": [
{ "role": "user", "content": "Stream a haiku about throughput." }
],
"stream": true
}'Each SSE frame is a data: <chunk> line carrying a delta instead of a full message; the
stream closes with a literal data: [DONE]. Set stream_options.include_usage: true to get a
final chunk with the usage block, since it isn't attached to any single delta.
Mid-stream failures
A failure after generation has started arrives as a data: {"error": ...} frame followed by
data: [DONE] — check for an error key on each chunk rather than assuming every frame is a
valid delta.
Common controls
Only documented fields
These are the fields the gateway's OpenAPI schema actually accepts on
/v1/chat/completions. Sampling params (temperature, top_p, frequency_penalty,
presence_penalty, seed) and hard limits (max_tokens / max_completion_tokens, n,
stop) are model-agnostic; response_format and tool calling depend on the target model.
Prop
Type
const completion = await client.chat.completions.create({
model: "venna-fast",
messages: [
{ role: "user", content: "Extract {name, age} from: Priya is 29." },
],
temperature: 0,
max_tokens: 200,
response_format: { type: "json_object" },
});Responses API
POST /v1/responses is the newer alternative: instead of a messages array you send a single
input — a plain string for a one-shot prompt, or an array of typed items for multi-turn and
tool-result history. It folds in reasoning, tool calls, and chained turns (previous_response_id)
as first-class output items instead of overloading message.content.
const response = await client.responses.create({
model: "venna-fast",
input: "In one sentence, what is Venna?",
});
const text = response.output
.flatMap((item) => (item.type === "message" ? item.content : []))
.filter((part) => part.type === "output_text")
.map((part) => part.text)
.join("");
console.log(text);
console.log(response.usage);response = client.responses.create(
model="venna-fast",
input="In one sentence, what is Venna?",
)
for item in response.output:
if item.type == "message":
for part in item.content:
if part.type == "output_text":
print(part.text)
print(response.usage)curl https://gateway.venna.net/v1/responses \
-H "Authorization: Bearer $VENNA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "venna-fast",
"input": "In one sentence, what is Venna?"
}'The result is a response object, not a chat.completion — status (completed, incomplete,
failed, ...) tells you how it ended, output is an array of typed items (a message item's
content holds output_text parts, same shape as the SDK snippet above walks), and usage
reports input_tokens / output_tokens instead of prompt_tokens / completion_tokens.
Reach for Responses over Chat Completions when you want:
- Chained turns without resending history — pass
previous_response_idinstead of the whole transcript. - Structured, non-text output items — reasoning traces and tool calls arrive as distinct
outputitems rather than packed into onemessage.contentstring. - Background runs —
background: truequeues the run and returns immediately instead of holding the connection open (not combinable withstream: trueyet).
Otherwise, Chat Completions is the simpler default — it's what every OpenAI-compatible SDK and proxy already speaks, per the quickstart.
Streaming works the same way: set "stream": true and read response.* SSE events
(response.created, progressive output_text.delta, then a terminal completed / incomplete
/ failed event) instead of Chat Completions' delta chunks.
Next steps
- Tooling — function calling and built-in tools on top of both endpoints.
- Quickstart — auth, base URL, and the fastest first call.
- API reference: Chat — full request/response schema for
/v1/chat/completions.