# Streaming and function calling

Use the transcript/tool Chat Completions interface for explicit conversation history and function calls. These capabilities do not automatically apply to the source-aware answer adapter.

## Streaming support

| Endpoint/request | Behavior |
| --- | --- |
| POST /v1/answers | JSON answer; no public streaming mode. `POST /v1/answers` ignores `stream` and `tools`. |
| Chat with model=synderesis-spark and stream=true, with or without tools | Server-sent events after generation completes. |
| Chat with model=spark, pro or synderesis-pro, tools present, stream=true | The same transcript/tool SSE contract. |
| Chat that takes the answer path (including automatic selection) with stream=true | 400 unsupported_parameter. |
| Chat with automatic selection and tools or tool results | 400 unsupported_parameter; automatic selection never takes the tool path. |
| Browser workspace stream | A separate session/application contract, outside this customer SDK guide. |

**The customer chat SSE response is buffered.** The server obtains and meters a complete model response before emitting its frames. That is buffered delivery, not live token streaming. Setting stream=true does not promise earlier first-token delivery or expose the browser workspace's live stream.

## Request a stream

```bash
curl --fail-with-body --no-buffer https://www.synderesis.eu/v1/chat/completions \
  -H "Authorization: Bearer $SYNDERESIS_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"model":"synderesis-spark","messages":[{"role":"user","content":"Describe human dignity briefly."}],"max_completion_tokens":1024,"stream":true,"stream_options":{"include_usage":true}}'
```

The response content type is text/event-stream. Each JSON event is a data line followed by a blank line. A schematic sequence is:

```text
data: {"id":"chatcmpl-example","model":"synderesis-spark","created":1793966400,"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"Illustrative answer."},"finish_reason":null}]}

data: {"id":"chatcmpl-example","model":"synderesis-spark","created":1793966400,"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-example","model":"synderesis-spark","created":1793966400,"object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":120,"completion_tokens":30,"total_tokens":150}}

data: [DONE]
```

The first delta can contain a complete tool-call array instead of text. Streamed tool calls include index values. The separate usage chunk is included only when stream_options.include_usage is exactly true.

Parse SSE framing before JSON. A network read is not necessarily one event. Account for an empty choices array in the usage chunk. Preserve finish_reason and detect a missing DONE marker as an incomplete transfer. Do not automatically replay an interrupted generation; it can already have been metered.

Authentication, validation, quota and generation errors occur before this buffered stream opens and use the ordinary JSON error response.

## Function calling

Synderesis returns proposed function calls; **your application executes the functions**. The customer completion endpoint does not run arbitrary client code.

A function definition has this shape:

```json
{
  "type": "function",
  "function": {
    "name": "lookup_opening_hours",
    "description": "Read the published opening hours for a location.",
    "parameters": {
      "type": "object",
      "properties": {"location": {"type": "string"}},
      "required": ["location"],
      "additionalProperties": false
    }
  }
}
```

A non-empty tools array can contain 1–128 definitions, with unique function names. A name must be 1–64 ASCII letters, digits, underscores or hyphens. The serialised tool definitions are limited to 512 KiB; the complete request also has its 900 KiB limit.

The function object supports name, description, parameters and strict. strict must be boolean if supplied. Acceptance of strict does not establish a guarantee that returned arguments satisfy your whole JSON Schema. The handler checks tool-call names and that arguments decode to an object; your application must validate the actual schema and authorise the requested action.

tool_choice accepts auto, none, required, or:

```json
{"type":"function","function":{"name":"lookup_opening_hours"}}
```

A named choice must refer to a supplied function. parallel_tool_calls defaults to true. If several calls are returned, collect a result for each before continuing the conversation.

## The complete tool loop

1. Send system/user messages, function definitions and an explicit supported model: `synderesis-spark`, `spark`, `pro` or `synderesis-pro`. Omitting `model` selects automatic selection, which rejects tools with 400 unsupported_parameter.
2. Inspect choices[0].message.tool_calls.
3. Parse each function.arguments JSON string.
4. Validate its name and arguments against your local allowlist/schema and the current user's permissions.
5. Execute the allowed read/action in your own application.
6. Append the complete assistant tool-call message.
7. Append one tool-role message for each call, with the corresponding tool_call_id and a string content.
8. Send the extended transcript and tools again.

For example, a continuation can include:

```json
[
  {"role":"user","content":"When does the parish office open?"},
  {
    "role":"assistant",
    "content":null,
    "tool_calls":[{
      "id":"call_example",
      "type":"function",
      "function":{"name":"lookup_opening_hours","arguments":"{\"location\":\"parish office\"}"}
    }]
  },
  {"role":"tool","tool_call_id":"call_example","content":"Published hours: Monday 09:00–12:00."}
]
```

Tool results are untrusted data. Keep secrets and unnecessary personal data out of results. Request user approval for consequential actions according to your application's own requirements. Bound the number of turns, wall time, cost and parallel calls so a loop cannot run indefinitely.

Every completion in the loop is a separate request with its own usage. Successful function dispatch in your application does not imply that a later model response will succeed. Use your application's own idempotency and action ledger for external effects.

## Cancellation and timeouts

Closing a client stream or timing out does not guarantee that upstream compute, metering or an external action has been cancelled. Buffered SSE is generated after model work completes. Treat uncertain outcomes as uncertain, reconcile them, and avoid automatic repetition of consequential tool actions.

See [Production guidance](production.md#retries-and-uncertain-outcomes) for a bounded retry strategy.
