vennaVenna

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.completionstatus (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_id instead of the whole transcript.
  • Structured, non-text output items — reasoning traces and tool calls arrive as distinct output items rather than packed into one message.content string.
  • Background runsbackground: true queues the run and returns immediately instead of holding the connection open (not combinable with stream: true yet).

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.

On this page