SSE Akışı
AI çalıştırma sonuçları, Sunucu Tarafından Gönderilen Etkinlikler (SSE) aracılığıyla gerçek zamanlı olarak yayınlanır. Artan belirteçleri, araç ilerlemesini, iş akışı adımlarını ve terminal olaylarını meydana geldikçe almak için bir çalıştırmanın akış uç noktasına bağlanın.
Akış Uç Noktaları
| Method | Path | Description | Auth | Rate Limit |
|---|---|---|---|---|
| GET | /ai/runs/:runId/stream | Stream events for a feature run | JWT + Entitlement | 20/min |
| GET | /ai/sessions/:sessionId/messages/:messageId/stream | Stream events for a chat message | JWT + Entitlement | 20/min |
Bir Akışa Bağlanma
'Accept: text/event-stream' başlığıyla bir GET isteği göndererek (veya tarayıcının 'EventSource' API'sini kullanarak) bir SSE bağlantısı açın.
POST /ai/features/:featureKey/runs dosyasındaki "id"yi "$RUN_ID" olarak kullanın.
curl -N https://api.chainabit.com/api/v1/ai/runs/$RUN_ID/stream \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: text/event-stream"// Using EventSource (browser)
const runId = process.env.RUN_ID; // from POST /ai/features/:featureKey/runs's data.id
const url = `${BASE_URL}/ai/runs/${runId}/stream`;
const eventSource = new EventSource(url, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
eventSource.addEventListener("message.delta", (e) => {
const data = JSON.parse(e.data);
process.stdout.write(data.payload.delta);
});
eventSource.addEventListener("run.settlement.completed", (e) => {
console.log("Run complete");
eventSource.close();
});
eventSource.addEventListener("run.error", (e) => {
const data = JSON.parse(e.data);
console.error("Error:", data.payload.error);
eventSource.close();
});import requests, os
run_id = os.environ["RUN_ID"] # from POST /ai/features/:featureKey/runs's data["id"]
with requests.get(
f"{BASE_URL}/ai/runs/{run_id}/stream",
headers={"Authorization": f"Bearer {TOKEN}"},
stream=True,
) as response:
for line in response.iter_lines(decode_unicode=True):
if line.startswith("data: "):
import json
event = json.loads(line[6:])
print(event["type"], event["payload"])Olay Çerçevesi Formatı
Her olay standart bir SSE çerçevesi olarak gönderilir:
id: 1
retry: 3000
event: message.delta
data: {"eventId":1,"type":"message.delta","runId":"550e8400-...","timestamp":"2026-03-17T14:00:00.123Z","payload":{"delta":"Hello"}}| Field | Description |
|---|---|
id | Monotonically increasing event ID |
retry | Reconnection interval in milliseconds (3000ms) |
event | Event type name |
data | JSON envelope with full event details |
Veri Zarfı Alanları
| Field | Type | Description |
|---|---|---|
eventId | number | Sequential event counter for this run |
type | string | Event type |
runId | string | The AI run UUID |
stepId | string | undefined | Step ID |
timestamp | string | ISO 8601 event timestamp |
payload | object | Event-specific data |
Etkinlik Türleri
Kategoriye göre düzenlenen 30'dan fazla etkinlik türü:
Bağlantı
| Event Type | Description | Terminal |
|---|---|---|
stream.connected | Emitted once per connection after headers flush. Carries { runId }. Not buffered — will not appear in event replay. | No |
Yetenek Yönlendirme
| Event Type | Description | Terminal |
|---|---|---|
capability.resolved | The requested AI capability was routed to an executable path | No |
Yaşam Döngüsünü Çalıştır
| Event Type | Description | Terminal |
|---|---|---|
run.started | Run processing has begun | No |
run.status.changed | Run status transition | No |
run.model.switched | Active model changed | No |
run.cancel_requested | Cancellation was requested | No |
run.cancelling | Cancellation in progress | No |
run.cancelled | Run cancelled successfully | Yes |
run.failed | Run failed with an error | Yes |
run.error | Runtime/timeout error | Yes |
run.model.switched Yükü
{
"eventId": 3,
"type": "run.model.switched",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T12:00:00.300Z",
"payload": {
"runId": "550e8400-e29b-41d4-a716-446655440000",
"fromModelKey": "gemini-2.0-flash",
"fromProvider": "google",
"toModelKey": "gemini-2.5-flash",
"toProvider": "google",
"reason": "model_not_found"
}
}Bu etkinlik bilgilendirme amaçlıdır; çalışma yeni modelle kesintisiz olarak devam etmektedir. Müşteriler bunu bir durum göstergesi olarak gösterebilir ancak herhangi bir işlem yapmaları gerekmez.
Mesaj Akışı
| Event Type | Description | Terminal |
|---|---|---|
message.delta | Incremental token content | No |
message.completed | Full message has been assembled | No |
message.suggestions | Follow-up suggestion cards for the completed assistant message | No |
message.partially_completed | Partial completion | No |
message.suggestions Yükü
{
"eventId": 11,
"type": "message.suggestions",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T12:00:01.000Z",
"payload": {
"messageId": "cm5msg002",
"suggestions": [
{
"suggestion": "Can you make that more practical for this week?",
"priority": 1
},
{
"suggestion": "What would the first 10 minutes look like?",
"priority": 2
}
]
}
}"message.suggestions", "message.completed"dan hemen sonra (aynı onay işaretiyle) ve "run.settlement.completed"dan önce yayınlanır. Öneri çipleri, asistan yanıtını yazan çağrı modelinin aynısı tarafından üretilir, böylece yanıt kesinleştiği anda ulaşırlar. Yanıtın kopyalanmasının öneri metnini içermemesi için, müşterilerin bu kartları asistan indiriminin dışında oluşturması gerekir.
Araç Çağrıları
Araç olayları, bir konuşma sırasında Chao yerleşik araçları (takvim aramaları, bit sorguları, zincirleme/zincirleme işlemleri, bellek ve bit oluşturma veya tamamlama gibi yazma işlemleri) çağırdığında meydana gelir. Tek bir turda birden fazla takım paralel olarak çağrılabilir.
| Event Type | Description | Terminal |
|---|---|---|
tool.started | Tool call initiated | No |
tool.progress | Tool execution progress | No |
tool.completed | Tool call finished successfully | No |
tool.failed | Tool call failed (non-terminal — Chao handles errors gracefully) | No |
tool.degraded | Tool or capability returned a graceful fallback state | No |
"tool.started" Yükü
{
"eventId": 4,
"type": "tool.started",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"stepId": "tc_abc123",
"timestamp": "2026-04-12T10:00:01.100Z",
"payload": {
"toolKey": "calendar.today",
"callId": "tc_abc123",
"input": {},
"activity": {
"activityType": "tool_read",
"origin": "chao",
"label": "Reading calendar",
"detail": "Starting tool execution.",
"renderHint": "tool_card",
"status": "active",
"subjectType": "calendar",
"subjectId": "tc_abc123"
}
}
}tool.completed Yükü
{
"eventId": 5,
"type": "tool.completed",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"stepId": "tc_abc123",
"timestamp": "2026-04-12T10:00:01.160Z",
"payload": {
"toolKey": "calendar.today",
"callId": "tc_abc123",
"executionMs": 58,
"success": true,
"data": { "items": [] },
"activity": {
"activityType": "tool_read",
"origin": "chao",
"label": "Calendar complete",
"detail": "Tool execution completed.",
"renderHint": "tool_card",
"status": "completed",
"subjectType": "calendar",
"subjectId": "tc_abc123"
}
}
}"tool.failed" Yükü
{
"eventId": 5,
"type": "tool.failed",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"stepId": "tc_abc123",
"timestamp": "2026-04-12T10:00:06.105Z",
"payload": {
"toolKey": "bits.get",
"callId": "tc_abc123",
"executionMs": 5001,
"success": false,
"error": "Tool execution timed out",
"activity": {
"activityType": "tool_read",
"origin": "chao",
"label": "Tool failed",
"detail": "Tool execution timed out",
"renderHint": "tool_card",
"status": "failed",
"subjectType": "bits",
"subjectId": "tc_abc123"
}
}
}'tool.failed' bir terminal olayı değildir. Chao hata sonucunu alır ve yanıt oluşturmaya devam eder.
'payload.data'yı takım gözlem çıktısı olarak değerlendirin. Bir kartın oluşturulmasına yardımcı olabilir, ancak Chao'nun asistan mesajı kullanıcıya yönelik sentez olarak kalır.
Araç kartı normalleştirmesi
Ön uç ve mobil istemciler, oluşturma öncesinde araç benzeri yükleri normalleştirmelidir:
| Normalized field | Preferred lookup |
|---|---|
toolKey | payload.toolKey ?? payload.key |
callId | payload.callId ?? payload.toolCallId ?? envelope.stepId |
input | payload.input ?? payload.args |
output | payload.data ?? payload.output |
payload.activity mevcut olduğunda, activityType, origin, label, detail, renderHint, status, subjectType ve subjectIdden kartları oluşturun.
Onay Kapıları
Chao'nun bir oturumda "mode: "approval" (varsayılan) ile bir yazma veya yok etme eylemi gerçekleştirmesi gerektiğinde, duraklar ve kullanıcının araç çağrısını onaylamasını veya reddetmesini bekler. Bu akışı üç olay yönetir:
| Event Type | Description | Terminal |
|---|---|---|
tool.approval_required | Chao requires user approval before executing a write/destructive tool | No |
tool.approval_response | User approved or rejected the pending tool call | No |
tool.approval_timeout | Approval window (60 s) expired; tool was not executed | No |
tool.approval_required Yükü
{
"eventId": 6,
"type": "tool.approval_required",
"runId": "550e8400-e29b-41d4-a716-446655440000",
"stepId": "tc_abc123",
"timestamp": "2026-04-12T10:00:01.500Z",
"payload": {
"runId": "550e8400-e29b-41d4-a716-446655440000",
"toolCallId": "tc_abc123",
"toolKey": "bits.create",
"input": { "title": "Review Q2 metrics", "priority": "high" },
"activity": {
"activityType": "approval",
"origin": "chao",
"label": "Approval needed",
"detail": "Review this action before Chao runs it.",
"renderHint": "status_card",
"status": "pending",
"subjectType": "bits",
"subjectId": "tc_abc123"
}
}
}Kullanıcının kararını şu uç noktalardan biriyle gönderin:
| Decision | Endpoint | Body |
|---|---|---|
| Approve | POST /ai/runs/:runId/tools/:toolCallId/approve | none |
| Reject | POST /ai/runs/:runId/tools/:toolCallId/reject | { "reason"?: string } |
Onayın ardından Chao devam eder ve "tool.approval_response", "tool.started" ve ardından "tool.completed" ifadesini yayınlar. Reddedilirse veya zaman aşımına uğrarsa Chao devam eder ve sonucu açıklar.
Muhakeme ve Etkinlik Özetleri
Kamu müşterileri, ham özel Düşünce Zinciri değil, güvenli etkinlik özetlerini göstermelidir. Araç, onay, planlama ve yetenek olayları arasında "payload.activity"; bu nesneyi durum kartları ve zaman çizelgeleri için kullanın.
| Event Type | Description | Terminal |
|---|---|---|
tool.progress | Tool progress/activity update | No |
tool.approval_required | Approval activity card | No |
plan.step_added | Planned action summary | No |
capability.resolved | Capability routing summary | No |
Teşhis cot.* etkinlikleri bazı dahili/hata ayıklama oturumlarında görünebilir. Bunlar kamuya açık araç kartı sözleşmesi değildir ve varsayılan olarak son kullanıcılara gösterilmemelidir.
İş Akışı Adımları
| Event Type | Description | Terminal |
|---|---|---|
workflow.step.started | Workflow step began | No |
workflow.step.completed | Workflow step finished | No |
workflow.step.failed | Workflow step failed | No |
workflow.completed | Entire workflow finished | Yes |
Yerleşim
| Event Type | Description | Terminal |
|---|---|---|
run.settlement.pending | Credit settlement is processing | No |
run.settlement.completed | Settlement done, run fully finished | Yes |
Terminal olayları kısa bir ek sürenin ardından akışın otomatik olarak kapanmasına neden olur.
Son Olay Kimliği ile Yeniden Bağlantı
Bağlantı koparsa kaldığınız yerden devam etmek için "Last-Event-ID" başlığını kullanarak yeniden bağlanın:
curl -N https://api.chainabit.com/api/v1/ai/runs/$RUN_ID/stream \
-H "Authorization: Bearer $TOKEN" \
-H "Last-Event-ID: 5"Sunucu, canlı yayına geçmeden önce "eventId > 5" olan tüm etkinlikleri yeniden oynatır. Çalıştırma tamamlandıktan sonra olaylar 15 dakika süreyle ara belleğe alınır.
JavaScript Yeniden Bağlantı Örneği
function connectStream(runId, lastEventId = null) {
const url = new URL(`${BASE_URL}/ai/runs/${runId}/stream`);
const headers = { Authorization: `Bearer ${TOKEN}` };
if (lastEventId) {
headers["Last-Event-ID"] = String(lastEventId);
}
const eventSource = new EventSource(url, { headers });
let latestId = lastEventId || 0;
eventSource.onmessage = (e) => {
latestId = Number(e.lastEventId);
};
eventSource.onerror = () => {
eventSource.close();
// Reconnect after retry interval (3 seconds)
setTimeout(() => connectStream(runId, latestId), 3000);
};
return eventSource;
}Davranışı Yeniden Dene
Her SSE çerçevesindeki "yeniden dene: 3000" alanı, uyumlu istemcilere (tarayıcı "EventSource" dahil) bağlantı kesilirse 3 saniye sonra otomatik olarak yeniden bağlanmaları talimatını verir. Sunucu, kaçırılan olayları yeniden oynatmak için yeniden bağlantı sırasında "Son Olay Kimliği" başlığını izler.
Hata Olayları
'run.error' olayı akış sırasında bir çalışma zamanı hatasına işaret eder:
event: run.error
data: {"eventId":8,"type":"run.error","runId":"...","timestamp":"...","payload":{"error":"Stream timeout exceeded","code":"stream_timeout"}}| Payload Field | Type | Description |
|---|---|---|
error | string | Human-readable error message |
code | string | Error code — see table below |
| Code | Meaning |
|---|---|
execution_error | General error during LLM execution |
stream_timeout | Connection open for more than 5 minutes |
provider_error | Unhandled upstream provider failure |
empty_response | Provider returned no text and no tool calls. The run fails; no automatic failover is attempted for this error code. |
'run.error' bir terminal olayıdır; akış gönderildikten sonra kapanır.
Yayın Zaman Aşımı
Yayınlar 5 dakika sürekli bağlantıdan sonra otomatik olarak kapatılır. Çalıştırma o zamana kadar tamamlanmadıysa, "stream_timeout" koduyla birlikte bir "run.error" olayı yayınlanır. İstemcinin, etkinlikleri almaya devam edebilmesi için "Son Etkinlik Kimliği" ile yeniden bağlanması gerekir.
Bağlantı Yaşam Döngüsü
Basit çalıştırma (araç çağrısı yok):
Client Server
│ │
│──── GET /ai/runs/:id/stream ────────►│
│ │ Set headers (text/event-stream)
│◄──── event: stream.connected ────────│
│◄──── (replay missed events) ─────────│
│ │
│◄──── event: message.delta ───────────│ (real-time events)
│◄──── event: message.delta ───────────│
│◄──── event: message.completed ───────│
│◄──── event: run.settlement.completed─│ (terminal)
│ │
│──── connection closed ───────────────│Otomatik araç çağrılarıyla Chao çalıştırması:
Client Server
│ │
│──── GET /ai/runs/:id/stream ────────►│
│◄──── event: stream.connected ────────│
│◄──── event: run.started ─────────────│
│◄──── event: tool.started ────────────│ (card opens with activity)
│◄──── event: tool.progress ───────────│ (optional status update)
│◄──── event: tool.completed ──────────│
│◄──── event: message.delta ───────────│ (LLM writes response)
│◄──── event: message.completed ───────│
│◄──── event: run.settlement.completed─│ (terminal)
│ │
│──── connection closed ───────────────│Onay kapısıyla Chao çalıştırması (varsayılan mod):
Client Server
│ │
│──── GET /ai/runs/:id/stream ────────►│
│◄──── event: stream.connected ────────│
│◄──── event: run.started ─────────────│
│◄──── event: tool.approval_required ──│ (run pauses)
│ │
│──── POST /ai/runs/:id/tools/:toolCallId/approve ─►│
│◄──── event: tool.approval_response ──│
│◄──── event: tool.started ────────────│
│◄──── event: tool.completed ──────────│
│◄──── event: message.delta ───────────│
│◄──── event: message.completed ───────│
│◄──── event: run.settlement.completed─│ (terminal)
│ │
│──── connection closed ───────────────│Hayatta Tut
Proxy zaman aşımlarını önlemek için her 15 saniyede bir kalp atışı yorumu (: canlı tutma) gönderilir.
Yanıt Başlıkları
| Header | Value |
|---|---|
Content-Type | text/event-stream; charset=utf-8 |
Cache-Control | no-cache, no-transform |
Connection | keep-alive |
X-Accel-Buffering | no |