Chat, streaming, tool calling
Chat runs through two endpoints: /v1/chat/completions in OpenAI format and /v1/messages in Anthropic format. Both take the same models from the model list (category Chat).
Streaming
Both endpoints stream via server-sent events when you set "stream": true.
- curl
- Python
- TypeScript
- Anthropic (Python)
curl -N https://api.openhippo.io/v1/chat/completions \
-H "Authorization: Bearer $OPENHIPPO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openhippo/gpt-oss-120b",
"stream": true,
"messages": [{"role": "user", "content": "Explain SSE in two sentences."}]
}'
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.openhippo.io/v1", api_key=os.environ["OPENHIPPO_API_KEY"])
stream = client.chat.completions.create(
model="openhippo/gpt-oss-120b",
messages=[{"role": "user", "content": "Explain SSE in two sentences."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.openhippo.io/v1",
apiKey: process.env.OPENHIPPO_API_KEY,
});
const stream = await client.chat.completions.create({
model: "openhippo/gpt-oss-120b",
messages: [{ role: "user", content: "Explain SSE in two sentences." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");
import os
from anthropic import Anthropic
client = Anthropic(base_url="https://api.openhippo.io", auth_token=os.environ["OPENHIPPO_API_KEY"])
with client.messages.stream(
model="openhippo/gpt-oss-120b",
max_tokens=512,
messages=[{"role": "user", "content": "Explain SSE in two sentences."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print()
Tool calling
Tool calling (function calling) in OpenAI format is covered by our tests for:
- self-hosted:
openhippo/Qwen3-VL-30B-A3B-Instruct(hippo-vision),openhippo/Qwen3.8-27B(hippo-coding),openhippo/gpt-oss-120b(hippo-chat-large), - partner:
aki-io/glm5.3-754b,aki-io/qwen3.6-35b,aki-io/deepseek-v4.1-flash-552b,mistral/mistral-large-4,mistral/glm-5.3,telekom/glm-5.2,telekom/qwen3.6-35b,tensorx/glm-5.3-flash,tensorx/kimi-k3,tensorx/deepseek-v4-pro.
Tested are simple calls, several tools to choose from and streaming with tools, each with tool_choice: "auto".
import json, os
from openai import OpenAI
client = OpenAI(base_url="https://api.openhippo.io/v1", api_key=os.environ["OPENHIPPO_API_KEY"])
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What is the weather in Augsburg?"}]
response = client.chat.completions.create(model="openhippo/gpt-oss-120b", messages=messages, tools=tools)
message = response.choices[0].message
if not message.tool_calls:
print(message.content) # answered without a tool
else:
messages.append(message)
for call in message.tool_calls:
args = json.loads(call.function.arguments)
print(call.function.name, args)
result = {"city": args.get("city"), "temp_c": 14} # your tool's result
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
final = client.chat.completions.create(model="openhippo/gpt-oss-120b", messages=messages, tools=tools)
print(final.choices[0].message.content)
The model decides itself whether to call a tool, so the example checks tool_calls before reading it.
Not yet documented: tool_choice values other than "auto" (e.g. a forced tool or "none") and parallel tool calls (parallel_tool_calls). Questions: support@openhippo.io.
Images
Only models in the Vision category understand images (see Models). Send the image as image_url, either as a URL or as a data URI (data:image/png;base64,...):
import base64, os
from openai import OpenAI
client = OpenAI(base_url="https://api.openhippo.io/v1", api_key=os.environ["OPENHIPPO_API_KEY"])
with open("photo.jpg", "rb") as f:
image = base64.b64encode(f.read()).decode()
response = client.chat.completions.create(
model="hippo-vision",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What is in this picture?"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image}"}},
],
}],
)
print(response.choices[0].message.content)
Some models without image understanding of their own have an alias ending in -vision. There a vision model of the same provider describes the image first; this describe step is billed in addition. Which model describes: Vision aliases.
OCR
Text recognition runs through chat completions with the model openhippo/GLM-OCR and one image per request. Send the image as a URL or as a data URI:
import base64, os
from openai import OpenAI
client = OpenAI(base_url="https://api.openhippo.io/v1", api_key=os.environ["OPENHIPPO_API_KEY"])
with open("invoice.png", "rb") as f:
image = base64.b64encode(f.read()).decode()
response = client.chat.completions.create(
model="openhippo/GLM-OCR",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Output the text of this document."},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image}"}},
],
}],
)
print(response.choices[0].message.content)
Reasoning
reasoning_effort (OpenAI format) and thinking (Anthropic format) are mapped to the levels of the respective model.
For hippo-coding:
- Levels
low,medium,xhigh; the default isxhigh. - Other values round up to the next level (e.g.
high→xhigh). "none"turns reasoning off.
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.openhippo.io/v1", api_key=os.environ["OPENHIPPO_API_KEY"])
response = client.chat.completions.create(
model="hippo-coding",
reasoning_effort="low",
messages=[{"role": "user", "content": "Write a Python function that checks for primes."}],
)
print(response.choices[0].message.content)
Known limits
- Unknown parameters are silently dropped instead of producing an error.
- Tool calling is tested only for the models named above.
- OCR with
openhippo/GLM-OCR: one image per request, see OCR. /v1/responsessupports creating a response only, no retrieval or deletion.