# Public API — стриминг

---

## Python HTTPX

### 1. Обычный стриминг

```python
import httpx
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # уникальный ID запроса; необходим для переподключения к стриму

form_data = {
    "content": "Привет! Как дела?",
    "info": json.dumps({
        "ttft": 0.5,
        "tbt": 0.1,
    }),
}
headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with httpx.stream("POST", url, json=form_data, headers=headers, timeout=120) as response:
    buffer = ""
    for chunk in response.iter_bytes():
        buffer += chunk.decode()
        while "\n\n" in buffer:
            message, buffer = buffer.split("\n\n", 1)
            event = data = None
            for line in message.splitlines():
                if line.startswith("event:"):
                    event = line[6:].strip()
                elif line.startswith("data:"):
                    data = json.loads(line[5:].strip())
            if event == "token":
                print(data["content"], end="", flush=True)
            elif event == "error":
                print(data["detail"])
```

### 2. Стриминг с прикреплённым файлом

```python
import httpx
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # уникальный ID запроса; необходим для переподключения к стриму
filename = "example.png"

form_data = {
    "content": "Привет, что на картинке?",
    "info": json.dumps({
        "ttft": 0.5,
        "tbt": 0.1,
    }),
}
headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with open(filename, "rb") as f:
    files = {"file": (filename, f)}
    with httpx.stream("POST", url, data=form_data, files=files, headers=headers, timeout=120) as response:
        buffer = ""
        for chunk in response.iter_bytes():
            buffer += chunk.decode()
            while "\n\n" in buffer:
                message, buffer = buffer.split("\n\n", 1)
                event = data = None
                for line in message.splitlines():
                    if line.startswith("event:"):
                        event = line[6:].strip()
                    elif line.startswith("data:"):
                        data = json.loads(line[5:].strip())
                if event == "token":
                    print(data["content"], end="", flush=True)
                elif event == "error":
                    print(data["detail"])
```

### 3. Переподключение к стриму

```python
import httpx
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # тот же ключ, что при POST; необходим для переподключения
offset = 10  # номер последнего полученного event; стрим продолжится с этого места

headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with httpx.stream("GET", url, params={"offset": offset}, headers=headers, timeout=120) as response:
    buffer = ""
    for chunk in response.iter_bytes():
        buffer += chunk.decode()
        while "\n\n" in buffer:
            message, buffer = buffer.split("\n\n", 1)
            event = data = None
            for line in message.splitlines():
                if line.startswith("event:"):
                    event = line[6:].strip()
                elif line.startswith("data:"):
                    data = json.loads(line[5:].strip())
            if event == "token":
                print(data["content"], end="", flush=True)
            elif event == "error":
                print(data["detail"])
```

---

## Python Requests

### 1. Обычный стриминг

```python
import requests
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # уникальный ID запроса; необходим для переподключения к стриму

form_data = {
    "content": "Привет! Как дела?",
    "info": json.dumps({
        "ttft": 0.5,
        "tbt": 0.1,
    }),
}
headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with requests.post(url, json=form_data, headers=headers, stream=True, timeout=120) as response:
    buffer = ""
    for chunk in response.iter_content(chunk_size=1024):
        buffer += chunk.decode()
        while "\n\n" in buffer:
            message, buffer = buffer.split("\n\n", 1)
            event = data = None
            for line in message.splitlines():
                if line.startswith("event:"):
                    event = line[6:].strip()
                elif line.startswith("data:"):
                    data = json.loads(line[5:].strip())
            if event == "token":
                print(data["content"], end="", flush=True)
            elif event == "error":
                print(data["detail"])
```

### 2. Стриминг с прикреплённым файлом

```python
import requests
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # уникальный ID запроса; необходим для переподключения к стриму
filename = "example.png"

form_data = {
    "content": "Привет, что на картинке?",
    "info": json.dumps({
        "ttft": 0.5,
        "tbt": 0.1,
    }),
}
headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with open(filename, "rb") as f:
    files = {"file": (filename, f)}
    with requests.post(url, data=form_data, files=files, headers=headers, stream=True, timeout=120) as response:
        buffer = ""
        for chunk in response.iter_content(chunk_size=1024):
            buffer += chunk.decode()
            while "\n\n" in buffer:
                message, buffer = buffer.split("\n\n", 1)
                event = data = None
                for line in message.splitlines():
                    if line.startswith("event:"):
                        event = line[6:].strip()
                    elif line.startswith("data:"):
                        data = json.loads(line[5:].strip())
                if event == "token":
                    print(data["content"], end="", flush=True)
                elif event == "error":
                    print(data["detail"])
```

### 3. Переподключение к стриму

```python
import requests
import json

url = "https://api.air.fail/public/text/text_test_model/stream"
api_key = "<API-ключ>"
idempotency_key = "stream-example-1"  # тот же ключ, что при POST; необходим для переподключения
offset = 10  # номер последнего полученного event; стрим продолжится с этого места

headers = {
    "Authorization": api_key,
    "Idempotency-Key": idempotency_key,
}

with requests.get(url, params={"offset": offset}, headers=headers, stream=True, timeout=120) as response:
    buffer = ""
    for chunk in response.iter_content(chunk_size=1024):
        buffer += chunk.decode()
        while "\n\n" in buffer:
            message, buffer = buffer.split("\n\n", 1)
            event = data = None
            for line in message.splitlines():
                if line.startswith("event:"):
                    event = line[6:].strip()
                elif line.startswith("data:"):
                    data = json.loads(line[5:].strip())
            if event == "token":
                print(data["content"], end="", flush=True)
            elif event == "error":
                print(data["detail"])
```

---

## Python OpenAI SDK


### 1. Обычный стриминг

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.air.fail/public/openai",
    api_key="<API-ключ>",
    default_headers={
        "Idempotency-Key": "stream-example-1",  # уникальный ID запроса; необходим для переподключения к стриму
    },
)

for event in client.responses.create(
    model="text_test_model",
    instructions="Ты умный ассистент",
    input="Напиши короткий тост на день рождения",
    stream=True,
    metadata={
        "ttft": 0.5,
        "tbt": 0.1,
    },
):
    print(event.model_dump_json(warnings=False))
```

### 2. Стриминг с прикреплённым файлом

```python
import base64

from openai import OpenAI

client = OpenAI(
    base_url="https://api.air.fail/public/openai",
    api_key="<API-ключ>",
    default_headers={
        "Idempotency-Key": "stream-example-1",  # уникальный ID запроса; необходим для переподключения к стриму
    },
)
filename = "example.png"

with open(filename, "rb") as f:
    image_base64 = base64.b64encode(f.read()).decode("utf-8")

for event in client.responses.create(
    model="text_test_model",
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_text", "text": "Опиши, что изображено на картинке."},
                {
                    "type": "input_image",
                    "image_url": f"data:image/png;base64,{image_base64}",
                },
            ],
        }
    ],
    stream=True,
    metadata={
        "ttft": 0.5,
        "tbt": 0.1,
    },
):
    print(event.model_dump_json(warnings=False))
```

### 3. Переподключение к стриму

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.air.fail/public/openai",
    api_key="<API-ключ>",
    default_headers={
        "Idempotency-Key": "stream-example-1",  # тот же ключ, что при POST; необходим для переподключения
    },
)

with client.responses.stream(
    response_id="stream-example-1",
    starting_after=0,  # номер последнего полученного event; стрим продолжится с этого места
) as stream:
    for event in stream:
        print(event.model_dump_json(warnings=False))
```

---

## cURL

### 1. Обычный стриминг

```bash
# Idempotency-Key — уникальный ID запроса; необходим для переподключения
curl -N -X POST "https://api.air.fail/public/text/text_test_model/stream" \
  -H "Authorization: <API-ключ>" \
  -H "Idempotency-Key: stream-example-1" \
  -F "content=Привет! Как дела?" \
  -F 'info={"ttft":0.5,"tbt":0.1}'
```

### 2. Стриминг с прикреплённым файлом

```bash
# Idempotency-Key — уникальный ID запроса; необходим для переподключения
curl -N -X POST "https://api.air.fail/public/text/text_test_model/stream" \
  -H "Authorization: <API-ключ>" \
  -H "Idempotency-Key: stream-example-1" \
  -F "content=Привет, что на картинке?" \
  -F 'info={"ttft":0.5,"tbt":0.1}' \
  -F "file=@example.png"
```

### 3. Переподключение к стриму

```bash
# Idempotency-Key — тот же ключ, что при POST; необходим для переподключения
# offset — номер последнего полученного event; стрим продолжится с этого места
curl -N -X GET "https://api.air.fail/public/text/text_test_model/stream?offset=10" \
  -H "Authorization: <API-ключ>" \
  -H "Idempotency-Key: stream-example-1"
```
