sone centralgeliştirici

Chat Completions

POST /chat/completions ile OpenAI Chat Completions biçiminde mesaj gönderin; yanıtın sone alanı sohbetin kimliğini, durumunu ve zengin mesajları taşır.

POST https://pb.sonecentral.com/api/v1/chat/completions

OpenAI Chat Completions'ın istek ve yanıt biçimini kullanır. OpenAI SDK'ları, LangChain ve OpenAI uyumlu her istemci baseURL değiştirilerek çalışır.

İstek

{
  "model": "default",
  "messages": [
    { "role": "user", "content": "Bu hafta sonu açık mısınız?" }
  ],
  "metadata": { "conversation_id": "conv_x8k2m9p4q7w1r3t" },
  "stream": false
}

Alan

Tür

Sohbetin kimliği birden çok yoldan gelebilir. Sonecentral ilk dolu olanı şu sırayla kullanır:

  1. Gövdedeki conversation_id.
  2. Gövdedeki sone.conversation_id.
  3. X-Sone-Conversation-Id başlığı.
  4. metadata.conversation_id.
  5. Geçmişe olduğu gibi eklediğiniz bir asistan mesajının sone.conversation_id alanı. Birden çok varsa en sonuncusu.

Kimlik hiç gelmezse sohbet başka yollarla aranır: Sohbet sürekliliği.

Mesaj rolleri

RolNe olur
userZiyaretçinin mesajı. content metin ya da text parçalarından oluşan bir dizi olabilir.
assistantÖnceki asistan yanıtı. Sohbeti bulmak için okunur. Sunucu anahtarıyla yeni bir sohbet açılırken geçmiş olarak da aktarılır.
system, developerSunucu anahtarıyla asistana yalnız bilgi olarak iletilir, en fazla 2.000 karakter. Tarayıcı anahtarıyla yok sayılır. Ayrıntılar: Geliştirici bağlamı.
tool, functionYok sayılır.

Başka bir rol 400 invalid_role alır. Son mesaj metin taşıyan bir user mesajı değilse istek 400 missing_user_message alır. Yeni bir mesaj 4.000 karakterden uzunsa 400 message_too_long gelir.

Yeni mesajda görsel, ses ya da dosya parçası varsa istek 400 unsupported_content alır. Önceki mesajlardaki bu tür parçalar sessizce atlanır, yalnız metinleri okunur.

Yok sayılan ve desteklenmeyen alanlar

Asistanın dil modelini, üslubunu ve araçlarını Sonecentral'daki ayarlar belirler. Bu yüzden şu alanlar yok sayılır, hata vermez: temperature, top_p, max_tokens, max_completion_tokens, presence_penalty, frequency_penalty, seed, stop, logit_bias, logprobs, top_logprobs, reasoning_effort, verbosity, service_tier, store, parallel_tool_calls, tool_choice, function_call, audio, modalities. Burada sayılmayan bilinmeyen alanlar da yok sayılır.

Şunlar reddedilir:

AlanHata
tools, functions400 tools_not_supported. Asistan işletmenin kendi araçlarını kullanır.
n, 1 dışında400 unsupported_parameter
response_format, text dışında400 unsupported_parameter

Yanıt

{
  "id": "chatcmpl-9f2c4a7e1b8d3f6a0c5e2b9d",
  "object": "chat.completion",
  "created": 1760866000,
  "model": "k3m9x2p7q1w8e4r",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Evet, cumartesi 10.00 ile 18.00 arası açığız. Pazar kapalıyız.",
        "refusal": null,
        "annotations": [],
        "sone": {
          "conversation_id": "conv_x8k2m9p4q7w1r3t",
          "status": "answered",
          "messages": [
            {
              "id": "msg_p4q7w1x8k2m9a5z",
              "role": "assistant",
              "type": "text",
              "text": "Evet, cumartesi 10.00 ile 18.00 arası açığız. Pazar kapalıyız.",
              "created_at": 1760866000
            }
          ]
        }
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 7, "completion_tokens": 16, "total_tokens": 23 },
  "system_fingerprint": null,
  "sone": { "conversation_id": "conv_x8k2m9p4q7w1r3t", "status": "answered" }
}

model yanıt veren asistanın kimliğidir; "default" gönderseniz de seçilen asistanın kimliği gelir. Sohbetin kimliği ayrıca X-Sone-Conversation-Id yanıt başlığında gelir.

content, bu turda gönderilen bütün mesajların düz metnidir; birden çok mesaj araya boş satır, yani \n\n konularak birleştirilir. Yalnız content gösteren bir istemci de eksiksiz çalışır.

usage bilgi içindir; gönderilen yeni mesajların ve gelen metnin uzunluğundan, yaklaşık dört karakter bir belirteç sayılarak tahmin edilir. Faturalama bu sayılara göre yapılmaz.

sone alanı

OpenAI biçimine eklenen alan budur. Tam hali choices[0].message.sone içinde gelir. Aynı özet, messages olmadan, yanıtın üst düzeyinde de vardır.

Alan

Tür

statusAnlamıcontent
answeredAsistan yanıt verdi.Yanıt metni
handoffSohbet bu turda ekibe geçti ve ziyaretçiye bir mesaj yazıldı, genellikle devri söyleyen mesaj.Devir mesajı
humanSohbet ekipte. Asistan yanıt vermedi; mesaj ekibe ulaştı.Boş
silentYanıt gerekmedi, örneğin "teşekkürler" gibi bir kapanış.Boş

handoff ve human durumunda ekibin yanıtlarını nasıl okuyacağınız İnsan devri ve ekip yanıtları sayfasında.

TypeScript'te bu alanları kendiniz tanımlayın:

sone.ts
import type { SoneMessage } from './sone-message'; // Zengin mesajlar sayfasındaki tanım

export type SoneStatus = 'answered' | 'handoff' | 'human' | 'silent';

// Akışsız yanıtta choices[0].message.sone
export type SoneTurn = {
  conversation_id: string;
  status: SoneStatus;
  messages: SoneMessage[];
};

// Akışta bir parçanın üst düzeyindeki sone: her parçada yalnız biri dolu
export type SoneChunk = {
  message?: SoneMessage; // bir mesaj tamamlandı
  activity?: string; // asistan bir işe bakıyor: "Ürünlere bakılıyor"
  conversation_id?: string; // son parçada
  status?: SoneStatus; // son parçada
};

export const soneOfMessage = (message: unknown) => (message as { sone?: SoneTurn }).sone;
export const soneOfChunk = (chunk: unknown) => (chunk as { sone?: SoneChunk }).sone;

Python'da OpenAI modelleri bilinmeyen alanları korur:

sone = completion.choices[0].message.to_dict().get("sone", {})
print(sone.get("conversation_id"), sone.get("status"))

Akış

"stream": true ile yanıt text/event-stream olarak gelir. Parçalar şu sırayla gelir:

  1. İlk parça, anahtar, sınır ve paket kontrolleri geçip asistan yanıtlamaya başladığında gelir: delta rolü taşır. Bu kontrollerden geçmeyen istek akış yerine sıradan bir HTTP hatası alır.
  2. Asistan bir araç kullanırken durum parçası gelebilir: delta boştur, üst düzeydeki sone.activity "Ürünlere bakılıyor" gibi kısa bir metindir.
  3. Her mesajın metni kelime grupları halinde delta.content olarak akar. İkinci ve sonraki mesajlar \n\n ile başlar. Bir mesajın son parçası ayrıca üst düzeyde sone.message taşır: o mesajın tamamı, SoneMessage biçiminde. Metni boş bir mesaj, delta'sı boş tek bir parçayla gelir.
  4. Son parça: finish_reason "stop", delta boş, üst düzeyde sone içinde conversation_id ve status.
  5. stream_options.include_usage açıksa choices dizisi boş, usage dolu bir parça daha gelir. Bu durumda önceki bütün parçalarda "usage": null vardır.
  6. Akış data: [DONE] ile biter.

sone her zaman parçanın üst düzeyindedir, choices[0].delta içinde değildir. Sohbetin kimliği akış başlarken X-Sone-Conversation-Id yanıt başlığında da gelir.

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":null}],"sone":{"activity":"Çalışma saatlerine bakılıyor"}}

: ping

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{"content":"Evet, cumartesi 10.00 ile "},"logprobs":null,"finish_reason":null}]}

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{"content":"18.00 arası açığız. Pazar "},"logprobs":null,"finish_reason":null}]}

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{"content":"kapalıyız."},"logprobs":null,"finish_reason":null}],"sone":{"message":{"id":"msg_p4q7w1x8k2m9a5z","role":"assistant","type":"text","text":"Evet, cumartesi 10.00 ile 18.00 arası açığız. Pazar kapalıyız.","created_at":1760866000}}}

data: {"id":"chatcmpl-9f2c","object":"chat.completion.chunk","created":1760866000,"model":"k3m9x2p7q1w8e4r","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"sone":{"conversation_id":"conv_x8k2m9p4q7w1r3t","status":"answered"}}

data: [DONE]

Örnekteki kimlikler kısaltıldı.

import type { SoneMessage } from './sone-message';
import { soneOfChunk } from './sone';

const stream = await client.chat.completions.create({
  model: 'default',
  messages: [{ role: 'user', content: 'Bu hafta sonu açık mısınız?' }],
  stream: true,
});

const messages: SoneMessage[] = [];
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta.content ?? '');
  const sone = soneOfChunk(chunk);
  if (sone?.message) messages.push(sone.message); // zengin mesajı burada çizin
  if (sone?.activity) console.log(`\n${sone.activity}…`);
  if (sone?.status) console.log('\nsohbet:', sone.conversation_id, sone.status);
}

Akış başladıktan sonra bir hata olursa data: {"error": {…}} parçası ve ardından data: [DONE] gelir. OpenAI SDK'ları bu parçayı hata olarak fırlatır. Ayrıntılar: Hatalar.

Metnin neden kelime kelime değil mesaj mesaj ilerlediği ve bağlantının nasıl canlı tutulduğu: Akış.

Tekrar denemek

Her yeni mesajda yeni bir Idempotency-Key gönderin, en fazla 200 karakter. Ağ hatasından sonra aynı mesajı yeniden gönderirken aynı değeri koyun. Sonecentral aynı mesajı ikinci kez kaydetmez: yanıt verilmişse aynı yanıt yeniden döner, asistan iki kez yanıt vermez. Aynı anahtarı başka bir metinle gönderirseniz istek 422 idempotency_key_reused alır. Tarayıcı anahtarıyla, sohbetin kimliği olmadan başka bir ağ adresinden gelen deneme, örneğin sohbetin ilk mesajı, yeni sohbet açar: Tekrar denemek.

OpenAI SDK'ları bu başlığı kendileri eklemez. İsteğin seçeneklerinde verin; SDK'nın kendi yeniden denemeleri de aynı başlığı gönderir:

const key = crypto.randomUUID(); // mesaj başına bir kez üretin, yeniden denerken aynısını kullanın

const completion = await client.chat.completions.create(
  {
    model: 'default',
    metadata: { conversation_id: 'conv_x8k2m9p4q7w1r3t' },
    messages: [{ role: 'user', content: 'Bu hafta sonu açık mısınız?' }],
  },
  { headers: { 'Idempotency-Key': key } },
);

Bütün geçmişi ve sohbetin kimliğini her istekte gönderiyorsanız Sonecentral, ilk mesaj dışında, yeniden gönderilen mesajı geçmişin sonuna bakarak da tanır. Kimliği olmadan yalnız geçmişle gelen adsız bir deneme sohbeti bulmaz: yeni bir sohbet açar ve asistan yeniden yanıt verir. Sohbetin ilk mesajı, yalnız yeni mesajı gönderen ve kimliği saklamayan istemciler için tek güvenli yol anahtardır; bu yüzden her mesajda gönderin. Ayrıntılar: Tekrar denemek.

Hatalar

DurumcodeNe zaman
400missing_user_messageSon mesaj metin taşıyan bir user mesajı değil.
400invalid_roleTanınmayan bir mesaj rolü.
400invalid_contentBir mesajın content alanı metin ya da parça dizisi değil.
400unsupported_contentYeni mesajda görsel, ses ya da dosya var.
400message_too_longYeni bir mesaj 4.000 karakterden uzun.
400too_many_new_messagesSon asistan mesajından sonra 12'den fazla user mesajı var.
400tools_not_supportedtools ya da functions gönderildi.
400unsupported_parametern 1 dışında ya da response_format text dışında.
400conversation_model_mismatchSohbet başka bir asistanla açılmış.
404model_not_foundBu kimlikte, anahtarın çağırabildiği yayında bir asistan yok.
404conversation_not_foundGönderilen sohbet kimliği bu çalışma alanında bir API sohbeti değil.
409conversation_busyAynı sohbette önceki mesaj hâlâ yanıtlanıyor.
422idempotency_key_reusedAynı Idempotency-Key başka bir metinle gönderildi.

Bütün kodlar ve ne yapılacağı: Hatalar.

Bu sayfada