> For the complete documentation index, see [llms.txt](https://docs.kontinent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kontinent.ai/documentation/dokumentation/features/streaming.md).

# Streaming

Setzen Sie `stream: true` bei einer Chat-Completion, um Tokens zu empfangen, während sie erzeugt werden — über Server-Sent Events (`text/event-stream`). Jedes Chat-Modell im Katalog streamt, auch die, die Kontinent über einen Nicht-OpenAI-Upstream erreicht (Claude auf Bedrock und auf Vertex): das Gateway dekodiert deren eigene Formate und gibt OpenAI-förmige Chunks aus.

Embeddings haben keine inkrementelle Form: `stream: true` an `/v1/embeddings` ist ein `400`, kein still ignoriertes Flag. Ein Vektor als Event-Stream verpackt nützt niemandem, und den Parameter scheinbar anzunehmen würde Sie auf Deltas warten lassen, die nie kommen.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const stream = await client.chat.completions.create({
  model: "mistral/mistral-small-latest",
  messages: [{ role: "user", content: "Schreibe ein Haiku über Europa." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
  if (chunk.usage) console.error("\nusage:", chunk.usage);
}
```

{% endtab %}

{% tab title="Python" %}

```python
stream = client.chat.completions.create(
    model="mistral/mistral-small-latest",
    messages=[{"role": "user", "content": "Schreibe ein Haiku über Europa."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\nusage:", chunk.usage)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -N https://api.kontinent.ai/v1/chat/completions \
  -H "Authorization: Bearer $KONTINENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"mistral/mistral-small-latest","stream":true,
       "messages":[{"role":"user","content":"Hallo!"}]}'
```

`-N` schaltet curls eigene Pufferung ab. Ohne das sehen Sie die ganze Antwort auf einmal und lernen über Streaming nichts.
{% endtab %}
{% endtabs %}

## Wire-Format

Jedes Event ist eine `data:`-Zeile mit einem JSON-Chunk, abgeschlossen durch einen Sentinel:

```
data: {"id":"chatcmpl-01J…","object":"chat.completion.chunk","created":1721822400,"model":"mistral/mistral-small-latest","choices":[{"index":0,"delta":{"role":"assistant","content":"Hal"},"finish_reason":null}]}

data: {"id":"chatcmpl-01J…","object":"chat.completion.chunk","created":1721822400,"model":"mistral/mistral-small-latest","choices":[{"index":0,"delta":{"content":"lo!"},"finish_reason":null}]}

data: {"id":"chatcmpl-01J…","object":"chat.completion.chunk","created":1721822400,"model":"mistral/mistral-small-latest","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-01J…","object":"chat.completion.chunk","created":1721822400,"model":"mistral/mistral-small-latest","choices":[],"usage":{"prompt_tokens":3,"completion_tokens":8,"total_tokens":11}}

data: [DONE]
```

Drei Dinge, auf die Sie sich verlassen können:

* Der **erste Chunk nennt meist die Rolle** (`delta.role: "assistant"`) und wiederholt sie nie. Garantiert auf den Chat-Pfaden, die Kontinent selbst baut (Claude über Bedrock und Vertex); bei durchreichenden Anbietern gehen deren eigene Chunks unverändert weiter, und einige lassen sie weg. Behandeln Sie die Rolle als optional und nehmen Sie `assistant` an. (`/v1/responses` hat weder Chat-Completion-Chunks noch `choices[].delta`: die benannten Events tragen ihre eigenen `delta`-Felder mit dem eigentlichen Text, und die Rolle steht als `"role": "assistant"` am Message-Item.)
* Der **Usage-Chunk hat ein leeres `choices`-Array**. Kontinent injiziert `stream_options: {"include_usage": true}` upstream, fragt ihn also immer an; setzen Sie `stream_options` selbst, gewinnt Ihr Wert. Lässt ein Anbieter die Usage trotzdem weg, schätzt das Gateway die Tokens — abgerechnet wird trotzdem, und der Datensatz wird intern als geschätzt markiert.
* **`[DONE]` heisst: die Antwort ist vollständig.** Es ist das einzige positive Signal, dass nichts fehlt; das negative steht unter [Fehler mitten im Stream](#fehler-mitten-im-stream).

### Keep-Alive-Kommentare

Solange das Modell noch arbeitet, schickt das Gateway SSE-Kommentarzeilen, damit niemand auf dem Weg ein denkendes Modell für eine tote Verbindung hält:

```
: KONTINENT PROCESSING
```

Ein Kommentar ist kein Event. Nach der [SSE-Spezifikation](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation) trägt jede Zeile, die mit `:` beginnt, keine Daten, und jeder konforme Client ignoriert sie. Sie dürfen sie für eine Ladeanzeige nutzen.

{% hint style="warning" %}
**Wer den Stream selbst zerlegt, muss Zeilen mit `:` am Anfang überspringen, bevor `JSON.parse` aufgerufen wird.** `: KONTINENT PROCESSING` an einen JSON-Parser zu geben wirft, und unbehandelt reisst es die Leseschleife ab — ein Fehler, der sich nur bei langsamen, teuren Anfragen zeigt, weil schnelle vor dem ersten Keep-Alive fertig sind.
{% endhint %}

## Den Stream parsen

Am sichersten ist es, ihn nicht selbst zu parsen. Diese Clients behandeln SSE-Framing, Kommentare, mehrzeilige `data:`-Felder und Pufferung korrekt:

* die offiziellen **OpenAI-SDKs** (`baseURL` auf `https://api.kontinent.ai/v1` setzen)
* das **Vercel AI SDK**
* [**eventsource-parser**](https://github.com/rexxars/eventsource-parser), wenn Sie das Framing abgeben, die Chunks aber selbst behandeln wollen

Wenn Sie die Schleife selbst schreiben, muss sie vier Dinge überleben: einen Chunk, der über zwei TCP-Reads verteilt ist, eine Kommentarzeile, den `[DONE]`-Sentinel und einen Fehler-Chunk.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const res = await fetch("https://api.kontinent.ai/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.KONTINENT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "bedrock/claude-sonnet-5",
    messages: [{ role: "user", content: "Hallo!" }],
    stream: true,
  }),
});

// Fehler VOR dem ersten Byte sind reines JSON mit echtem Statuscode.
if (!res.ok) {
  const { error } = await res.json();
  throw new Error(`${error.code}: ${error.message}`);
}
console.log("generation:", res.headers.get("X-Generation-Id"));
console.log("provider:", res.headers.get("X-Provider"));

const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
let complete = false;

// Eine benannte Schleife, damit `break read` aus der inneren beide verlässt.
read: while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  let nl: number;
  while ((nl = buffer.indexOf("\n")) !== -1) {
    const line = buffer.slice(0, nl).trim();
    buffer = buffer.slice(nl + 1);

    if (line === "" || line.startsWith(":")) continue; // leer oder Keep-Alive
    if (!line.startsWith("data:")) continue;

    const payload = line.slice(5).trim();
    if (payload === "[DONE]") { complete = true; break; }

    const chunk = JSON.parse(payload);

    // Fehler NACH dem ersten Byte kommen hier an; der Status war schon 200.
    if (chunk.error) {
      throw new Error(`${chunk.error.code}: ${chunk.error.message} (Antwort unvollständig)`);
    }
    if (chunk.usage) console.error("usage:", chunk.usage);

    const delta = chunk.choices?.[0]?.delta;
    if (delta?.reasoning) process.stderr.write(delta.reasoning); // Denken
    if (delta?.content) process.stdout.write(delta.content);     // Antwort
  }
  if (complete) break read;
}

// EOF ist kein Erfolg. Eine Verbindung, die vor `[DONE]` abbricht, hinterlässt
// eine abgeschnittene Antwort, die wie eine kurze aussieht.
if (!complete) throw new Error("Stream endete ohne [DONE]: Antwort unvollständig");
```

{% hint style="info" %}
Diese Schleife liest eine `data:`-Zeile pro Event — mehr schickt Kontinent nicht. Die SSE-Spezifikation erlaubt aber mehrere `data:`-Zeilen in einem Event, die mit Zeilenumbrüchen zu verbinden sind; wer das (oder `event:`- und `id:`-Felder) vollständig behandeln will, nimmt einen der Parser oben statt diese Schleife zu erweitern.
{% endhint %}
{% endtab %}

{% tab title="Python" %}

```python
import json, requests

with requests.post(
    "https://api.kontinent.ai/v1/chat/completions",
    headers={"Authorization": f"Bearer {api_key}"},
    json={
        "model": "bedrock/claude-sonnet-5",
        "messages": [{"role": "user", "content": "Hallo!"}],
        "stream": True,
    },
    stream=True,
) as r:
    # Fehler VOR dem ersten Byte sind reines JSON mit echtem Statuscode.
    if r.status_code != 200:
        err = r.json()["error"]
        raise RuntimeError(f"{err['code']}: {err['message']}")
    print("generation:", r.headers.get("X-Generation-Id"))
    print("provider:", r.headers.get("X-Provider"))

    complete = False
    for line in r.iter_lines(decode_unicode=True):
        if not line or line.startswith(":"):   # leer oder Keep-Alive
            continue
        if not line.startswith("data:"):
            continue
        payload = line[5:].strip()
        if payload == "[DONE]":
            complete = True
            break
        chunk = json.loads(payload)

        # Fehler NACH dem ersten Byte kommen hier an; der Status war schon 200.
        if "error" in chunk:
            raise RuntimeError(f"{chunk['error']['code']}: {chunk['error']['message']} (unvollständig)")
        if chunk.get("usage"):
            print("\nusage:", chunk["usage"])

        delta = (chunk.get("choices") or [{}])[0].get("delta", {})
        if delta.get("reasoning"):
            print(delta["reasoning"], end="", flush=True)   # Denken
        if delta.get("content"):
            print(delta["content"], end="", flush=True)     # Antwort

    # EOF ist kein Erfolg: ohne [DONE] ist die Antwort abgeschnitten.
    if not complete:
        raise RuntimeError("Stream endete ohne [DONE]: Antwort unvollständig")
```

{% endtab %}
{% endtabs %}

## Reasoning-Modelle

Das Denken streamt im selben `delta` und kommt **vor** der Antwort:

```
data: {"choices":[{"index":0,"delta":{"role":"assistant","reasoning":"Zehntel ","reasoning_details":[{"type":"reasoning.text","text":"Zehntel ","index":0}]}}]}

data: {"choices":[{"index":0,"delta":{"reasoning":"vergleichen…","reasoning_details":[{"type":"reasoning.text","text":"vergleichen…","index":0}]}}]}

data: {"choices":[{"index":0,"delta":{"content":"9,9 ist größer."}}]}
```

`delta.reasoning` ist ein String, den Sie aneinanderhängen und ausgeben können. `delta.reasoning_details` ist die strukturierte Form, die Sie zurückschicken, um ein denkendes Gespräch fortzusetzen — siehe [Reasoning](/documentation/dokumentation/features/reasoning.md) für den Anfrageparameter, die Detail-Typen und wie verschlüsseltes Denken gemeldet wird.

{% hint style="info" %}
Ein Reasoning-Modell kann vor seinem ersten Token lange still sein — es überlegt, es hängt nicht. Die Keep-Alive-Kommentare des Gateways überbrücken diese Lücke, und bei Bedrock und Vertex zählen die Denk-Deltas selbst als Upstream-Aktivität.
{% endhint %}

## Werkzeugaufrufe

Werkzeugaufrufe streamen als `delta.tool_calls`, bei jedem Chat-Modell. Die beiden Claude-Wege (Bedrock und Vertex) werden genau in diese Form übersetzt, das Wire-Format verrät also nicht, welcher Upstream geantwortet hat:

```
data: {"choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_a","type":"function","function":{"name":"get_weather","arguments":""}}]}}]}

data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]}}]}

data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\":\"Berlin\"}"}}]}}]}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}
```

Drei Regeln, und sie stammen von OpenAI:

* **`index` ist der Schlüssel, nicht die Reihenfolge des Eintreffens.** Das erste Fragment eines Aufrufs trägt `id`, `type` und `function.name`, jedes weitere nur noch Argumenttext. Zwei parallele Aufrufe verschränken sich, sammeln Sie also in einer Map mit `index` als Schlüssel.
* **Die Argumente sind ein JSON-String, der in Stücken ankommt.** Ein einzelnes Fragment ist kein gültiges JSON. Erst aneinanderhängen, dann `JSON.parse`, sobald `finish_reason` da ist.
* **`finish_reason: "tool_calls"`** beendet den Zug, wenn das Modell ein Werkzeug ausgeführt haben will. Jedes andere Ende, auch `"error"`, bedeutet, dass der Aufruf unvollständig sein kann: Führen Sie nie ein Werkzeug aus einem Stream aus, der nicht sauber endete.

Für die nächste Runde schicken Sie den Assistenten-Zug mit seinen gesammelten `tool_calls` zurück, danach je Aufruf eine Nachricht `{"role": "tool", "tool_call_id": "...", "content": "..."}`. Bei einem Reasoning-Modell spielen Sie zusätzlich die `reasoning_details` dieses Zuges zurück: Claude prüft sein eigenes Denken und setzt kein Denken fort, das es sich nicht selbst zuordnen kann. Die offiziellen SDKs übernehmen das Sammeln für Sie.

{% hint style="info" %}
Wo ein Upstream einen Werkzeugparameter nicht ausdrücken kann, wird die Anfrage mit einem `400` abgelehnt, das das Feld benennt, statt sie ohne ihn zu bedienen. Bei Claude über Bedrock betrifft das `tool_choice: "none"` und `parallel_tool_calls: false`, auf beiden Claude-Wegen das Erzwingen eines Werkzeugs (`"required"` oder eine benannte Funktion) bei aktiviertem `reasoning`, was Anthropic ablehnt. Siehe [API-Referenz](/documentation/dokumentation/reference/api-reference.md).
{% endhint %}

## `/v1/responses` streamen

Die Responses-API streamt **benannte** Events statt anonymer Chunks, jedes mit einer monoton steigenden `sequence_number`:

```
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_01J…","status":"in_progress",…}}

event: response.output_item.added
data: {"type":"response.output_item.added","sequence_number":1,"output_index":0,"item":{"type":"reasoning","id":"rs_01J…"}}

event: response.reasoning_text.delta
data: {"type":"response.reasoning_text.delta","sequence_number":2,"item_id":"rs_01J…","output_index":0,"content_index":0,"delta":"Zehntel "}

event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_01J…","output_index":1,"content_index":0,"delta":"9,9 ist größer."}

event: response.completed
data: {"type":"response.completed","sequence_number":11,"response":{"id":"resp_01J…","status":"completed","output":[…],"usage":{…}}}
```

Der vollständige Eventsatz: `response.created`, `response.output_item.added`, `response.reasoning_text.delta`, `response.reasoning_text.done`, `response.content_part.added`, `response.output_text.delta`, `response.output_text.done`, `response.content_part.done`, `response.function_call_arguments.delta`, `response.function_call_arguments.done`, `response.output_item.done` und `response.completed`. Keep-Alive-Kommentare und die Regel „kein `[DONE]` nach einem Fehler" gelten hier ebenso — eine abgebrochene Generierung endet mit `response.failed`, dessen Response-Objekt `status: "failed"`, ein `error` und die bis dahin erzeugte Ausgabe trägt.

Ein Werkzeugaufruf ist ein eigenes Output-Item vom Typ `function_call` mit `call_id`, `name` und `arguments`. Die Argumente streamen als `response.function_call_arguments.delta`-Fragmente und sind erst parsbar, wenn `response.function_call_arguments.done` sie vollständig wiederholt hat. Für die nächste Runde schicken Sie das Item in `input` zurück als `{"type": "function_call", "call_id": "...", "name": "...", "arguments": "..."}`, gefolgt von `{"type": "function_call_output", "call_id": "...", "output": "..."}`.

Rein verschlüsseltes Denken erzeugt **kein** `response.reasoning_text.delta` — es gibt keinen Klartext zu streamen —, der Block erreicht Sie aber trotzdem im Reasoning-Item des letzten Events.

Die Anfrageform steht in der [API-Referenz](/documentation/dokumentation/reference/api-reference.md).

## Abbrechen

Hören Sie auf zu lesen und schliessen Sie die Verbindung — in JavaScript mit `AbortController`, in Python durch Verlassen des `with`-Blocks. Kontinent schliesst daraufhin die Upstream-Verbindung, Anbieter, die Abbrüche beachten, stoppen also sofort.

```typescript
const controller = new AbortController();
const stream = await client.chat.completions.create(
  { model: "mistral/mistral-small-latest", messages, stream: true },
  { signal: controller.signal },
);
// später:
controller.abort();
```

{% hint style="warning" %}
Ein Abbruch macht die Anfrage **nicht kostenlos**. Die bis dahin erzeugten Tokens werden abgerechnet. Weil der Anbieter bei einem abgebrochenen Stream seinen Usage-Chunk nie schickt, wird diese Zahl aus dem geschätzt, was das Gateway empfangen hat.

Manche Upstreams beachten Abbrüche überhaupt nicht (darunter AWS Bedrock und Google) und rechnen die Generierung auf ihrer Seite fertig. Der Abbruch stoppt also die Bytes zu Ihnen, aber nicht immer die Arbeit.
{% endhint %}

## Zeitlimits und Keep-Alives

| Grenze                    | Wert          | Was sie misst                                                                        |
| ------------------------- | ------------- | ------------------------------------------------------------------------------------ |
| Gesamtdauer eines Streams | keine         | Eine lange Antwort ist keine kaputte.                                                |
| Upstream-Stille           | **90 s**      | Stille des *Anbieters*. Seine eigenen Chunks — auch Denk-Deltas — setzen sie zurück. |
| Keep-Alive-Kommentar      | alle **15 s** | Stille *Ihnen* gegenüber. Beweist, dass die Verbindung lebt.                         |
| Load-Balancer-Idle        | 300 s         | Kontinents eigener Rand.                                                             |

Das Keep-Alive setzt die 90 s **bewusst nicht** zurück. Täte es das, würde ein mitten in der Generierung gestorbener Anbieter die Verbindung unbegrenzt offen halten, während das Gateway Sie weiter beruhigt. Die zwei Mechanismen beantworten verschiedene Fragen: *ist der Client noch erreichbar* und *arbeitet der Anbieter noch*.

Wenn die 90 s greifen, bekommen Sie den Fehler-Chunk aus dem nächsten Abschnitt — keine stille Kürzung.

## Fehler mitten im Stream

Definiert das Modell Ausweichanbieter, werden Upstream-Fehler (`429`/`5xx`/Verbindungsfehler) beim nächsten Anbieter erneut versucht — **nur vor dem ersten Byte** an Sie. Das ist der letzte Moment, in dem ein anderer Anbieter die ganze Antwort noch liefern kann.

Nach dem ersten Byte steht der Statuscode bereits auf `200` und lässt sich nicht mehr ändern. Der Fehler wird deshalb **im Stream** gemeldet:

```
data: {"id":"chatcmpl-01J…","object":"chat.completion.chunk","created":1721822400,"model":"bedrock/claude-sonnet-5","provider":"bedrock","error":{"code":"upstream_error","message":"the provider failed mid-generation; the answer is incomplete"},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}
```

* Das `error`-Objekt steht auf **oberster Ebene**, neben den gewöhnlichen Chunk-Feldern.
* `finish_reason` ist `"error"`, damit auch ein Client, der nur Finish-Reasons verfolgt, es merkt.
* `provider` nennt den Anbieter, der abgebrochen hat — das macht den Fehler handhabbar.
* **Danach folgt kein `[DONE]`.** Erkennen Sie den Fehler, indem Sie jeden Chunk auf `error` oder auf `finish_reason: "error"` prüfen.

Behandeln Sie ihn als wiederholbar auf Ihrer Seite: der Text, den Sie haben, ist unvollständig, und die bereits erzeugten Tokens werden abgerechnet. Fehler *vor* dem Beginn des Streams sind gewöhnliches JSON mit echtem Statuscode (`400`, `401`, `402`, `429`, `502`, `503`) — siehe [Fehler behandeln](/documentation/dokumentation/features/errors.md).

## Antwortköpfe

Jede Antwort — gestreamt oder nicht, erfolgreich oder Fehler — trägt drei Köpfe:

| Kopf              | Bedeutung                                                                     |
| ----------------- | ----------------------------------------------------------------------------- |
| `X-Generation-Id` | Id des Nutzungsdatensatzes, den diese Anfrage schreibt                        |
| `X-Provider`      | der Anbieter, der tatsächlich bedient hat — **nach** einem möglichen Ausweich |
| `X-Served-Model`  | das Katalogmodell, das tatsächlich bedient hat                                |

`X-Generation-Id` ist der Schlüssel, um einen einzelnen Aufruf mit Ihren Logs und Ihrer Rechnung abzugleichen. Auf den Endpunkten, deren Antwortkörper Kontinent selbst baut (Bedrock, Vertex Anthropic, `/v1/responses`), steht dieser Wert im Feld `id` — **mit Präfix**: `chatcmpl-<id>` bei Chat-Completions, `resp_<id>` bei `/v1/responses`. Entfernen Sie das Präfix vor dem Vergleich; wörtlich gleich sind die beiden nicht.

`X-Provider` und `X-Served-Model` sind die einzige Stelle, an der Sie einen stillen Ausweich sehen. Für ein Souveränitätsprodukt ist das keine Debugging-Nettigkeit: Sie haben ein Modell auch danach gewählt, *wo* es läuft, und nach einem Ausweich kam die Antwort möglicherweise von Ihrer zweiten Wahl.

## Checkliste für eine robuste Stream-Schleife

1. HTTP-Status prüfen, bevor der Body gelesen wird.
2. `X-Generation-Id` und `X-Provider` festhalten.
3. Reads puffern und an Zeilenumbrüchen trennen — ein Chunk kann über zwei TCP-Reads gehen.
4. Leerzeilen und Zeilen mit `:` am Anfang überspringen.
5. Bei `[DONE]` aufhören — und *jedes* andere Ende als unvollständig behandeln.
6. Jeden Chunk auf `error` / `finish_reason: "error"` prüfen.
7. `usage` aus dem Chunk mit leerem `choices` lesen.
8. `delta.reasoning` getrennt von `delta.content` darstellen — Gedanken sind nicht die Antwort.
9. `delta.tool_calls` nach `index` sammeln, `function.arguments` aneinanderhängen und erst nach `finish_reason: "tool_calls"` parsen.

## Verwandt

{% content-ref url="/pages/cumy0nCmtk5YWI8fzzSH" %}
[Reasoning](/documentation/dokumentation/features/reasoning.md)
{% endcontent-ref %}

{% content-ref url="/pages/7R1ViKCJpzbvV0OeM7nk" %}
[Fehler behandeln](/documentation/dokumentation/features/errors.md)
{% endcontent-ref %}

{% content-ref url="/pages/K10It3CQs0qGNrzk0lXa" %}
[Maximale Verfügbarkeit](/documentation/dokumentation/models-and-routing/availability.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kontinent.ai/documentation/dokumentation/features/streaming.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
