Skip to main content

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 -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."}]
}'

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 is xhigh.
  • 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/responses supports creating a response only, no retrieval or deletion.