Telluvian

Responses API

The stateful /v1/responses endpoint, with server-side conversation history.

Telluvian implements OpenAI's Responses API as well as Chat Completions. The official SDKs work against it unchanged:

from openai import OpenAI

client = OpenAI(
    api_key="sk_live_...",
    base_url="https://api.telluvian.ai/v1",
)

response = client.responses.create(
    model="google/gemma-4-31B-it",
    input="Who was the first person on Mars?",
)

print(response.output_text)

What makes it different

Chat Completions is stateless: you re-send the whole conversation on every call. The Responses API is stateful — you send only the new input plus the id of the previous response, and we rebuild the history server-side.

first = client.responses.create(
    model="google/gemma-4-31B-it",
    input="My favourite colour is crimson.",
)

second = client.responses.create(
    model="google/gemma-4-31B-it",
    previous_response_id=first.id,
    input="What is my favourite colour?",   # the colour is not re-sent
)

print(second.output_text)   # "Crimson."

Which should you use?

Use Chat Completions if you already keep the conversation in your own application state — it is one fewer thing for us to hold, and it is what most existing code and frameworks expect.

Use Responses if you would otherwise be storing the transcript purely to replay it to us, or if you are following OpenAI examples written against it.

Probe scores

Scoring works the same way and is on by default. The per-token output rides on the content part, next to the text it describes:

response = client.responses.create(
    model="google/gemma-4-31B-it",
    input="Who was the first person on Mars?",
)

part = response.output[0].content[0]
for token, score in zip(part.tokens, part.scores["hallucination"]):
    if score is not None and score > 0.5:
        print(f"flagged: {token!r}")

The same null-entry rule applies as in Reading the scores: the array is always as long as tokens, but an individual entry may be null.

Pass include_scores: false through extra_body to turn scoring off and avoid the probe surcharge.

Streaming

The stream is a sequence of typed events, not the uniform chunks of Chat Completions:

stream = client.responses.create(
    model="google/gemma-4-31B-it",
    input="Tell me about Ada Lovelace.",
    stream=True,
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "response.completed":
        print(f"\n{event.response.usage.output_tokens} tokens")

Events emitted, in order:

EventCarries
response.createdThe response object, status: "in_progress"
response.in_progressSame
response.output_text.deltadelta (text), and scores for that token
response.output_text.doneThe complete text
response.completedThe final response, with usage

Managing stored responses

Responses are stored by default so they can be continued.

fetched = client.responses.retrieve(response.id)   # GET
client.responses.delete(response.id)               # DELETE

Pass store=False to skip storage entirely. The response is returned as normal but is not retrievable and cannot be used as a previous_response_id.

client.responses.create(
    model="google/gemma-4-31B-it",
    input="Nothing to keep.",
    store=False,
)

Field mapping

If you are porting from Chat Completions:

Chat CompletionsResponses
messagesinput (a string, or a list of messages)
a leading system messageinstructions
max_tokensmax_output_tokens
choices[0].message.contentoutput_text
usage.prompt_tokensusage.input_tokens
usage.completion_tokensusage.output_tokens
finish_reason: "length"status: "incomplete"

instructions apply to one call only

instructions is not inherited by a response that continues this one. If a later call in the chain needs the same instruction, pass it again — this matches OpenAI's behaviour, and it means a system prompt can never become silently sticky across a conversation.

Pricing

Identical to Chat Completions — the same per-token rates and the same probe surcharge. See Pricing. Continuing a chain re-sends the stored history to the model, so input tokens grow with the conversation, exactly as they would if you had re-sent it yourself.