SDKs
Using the official OpenAI Python and TypeScript SDKs against Telluvian.
The API implements both the OpenAI Chat Completions and Responses
interfaces, so the official OpenAI SDKs work unmodified. Point base_url at
Telluvian and use your Telluvian key. There is no Telluvian SDK to install and
nothing to wrap.
This page covers client.chat.completions. For client.responses and
server-side conversation state, see Responses API.
Everything on this page was run against the API
The snippets below are executed against a server implementing the real
response schema, not written from memory. Where an SDK needs something
non-obvious — like extra_body in Python — it is called out.
Install
pip install openaiClient setup
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.telluvian.ai/v1",
api_key=os.environ["TELLUVIAN_API_KEY"],
)Note the /v1
The base URL includes /v1. The SDKs append paths like /chat/completions
to whatever you give them, so omitting it produces a 404 that looks like a
routing bug.
Reading scores off a response
scores and tokens are extensions to the OpenAI schema. The SDKs keep
unrecognised response fields, so they are readable as ordinary attributes.
response = client.chat.completions.create(
model="google/gemma-4-31B-it",
messages=[{"role": "user", "content": "When was the Eiffel Tower built?"}],
)
print(response.choices[0].message.content)
# The Eiffel Tower opened in 1889.
# Extension fields — the SDK keeps them in `model_extra`, and attribute
# access works. Your type checker will not know about them.
print(response.tokens) # ['The', ' Eiffel', ' Tower', ' opened', ' in', ' 1889', '.']
print(response.scores) # {'hallucination': [0.01, 0.02, 0.02, 0.05, 0.03, 0.61, 0.01]}Finding the flagged span
tokens[i] and scores["hallucination"][i] are aligned and the same length,
which is what makes the scores usable — you can point at the exact claim rather
than at the whole answer.
THRESHOLD = 0.5 # pick your own; see Reading the scores
flagged = [
tok for tok, score
in zip(response.tokens, response.scores["hallucination"])
if score > THRESHOLD
]
print("".join(flagged)) # ' 1889'Reassembling the text
"".join(tokens) equals choices[0].message.content exactly. You never need
to re-tokenise to line the scores up with the text.
Streaming
Each content chunk carries one score per probe for that chunk's token.
stream = client.chat.completions.create(
model="google/gemma-4-31B-it",
messages=[{"role": "user", "content": "Tell me about Ada Lovelace."}],
stream=True,
)
usage = None
for chunk in stream:
# The final chunk carries usage and no content; role-only chunks carry
# neither. Guard both rather than assuming every chunk has a delta.
if chunk.usage is not None:
usage = chunk.usage
delta = chunk.choices[0].delta.content
if not delta:
continue
scores = getattr(chunk, "scores", None)
score = scores.get("hallucination") if scores else None
print(delta, score)
print(usage) # CompletionUsage(prompt_tokens=9, completion_tokens=7, ...)Usage arrives without asking
Token counts are attached to the final chunk of every stream. You do not need
stream_options: {include_usage: true} — it is always there, and it is what
your request is billed from.
Turning scoring off
This is the one place the two SDKs differ.
# Python's SDK validates arguments against the OpenAI schema, so a probe
# extension must go through extra_body or it is rejected before sending.
response = client.chat.completions.create(
model="google/gemma-4-31B-it",
messages=[{"role": "user", "content": "Hello"}],
extra_body={"include_scores": False},
)
print(response.scores) # NoneSee include_scores for what this changes, and
Pricing for what it saves.
Listing models
for model in client.models.list().data:
extra = model.model_extra or {}
print(model.id, extra.get("pricing"), extra.get("probe"))Other OpenAI-compatible tooling
Anything that speaks Chat Completions and lets you set a base URL should work —
LangChain, LlamaIndex, Vercel AI SDK, and so on. The caveat is always the same:
tools that re-serialise responses into their own types will drop scores and
tokens, because those are not part of the OpenAI schema. If a framework is
losing your scores, call the endpoint directly for the scored path.