Skip to main content
CID222 Docs
GeliştirmeKılavuzlar

Entegrasyon örnekleri

Ağ geçidini cURL, Node.js, Python ve React'ten çağırın — çalışan bir SSE istemcisi, bağımsız bir tespit çağrısı ve sunucu tarafında bir konuşma.

  • Sürüm: 0.4
  • Rol: admin_user, normal_user
  • Tür: görev

Ağ geçidine karşı çalışan bir istemci kurun: kimlik doğrulayın, hangi modelleri çağırabileceğinizi keşfedin, olay akışını doğru okuyun, model kullanmadan metin tarayın ve konuşmayı sunucuda tutun. Bu sayfadaki her parça gerçek rotalara karşı çalışır.

Neye ihtiyacım var?

Lisans
Any
Rol
admin_usernormal_user

Ön koşullar

  • Ağ geçidine erişilebiliyor ve GET /health isteğine status ok yanıtı veriyor.
  • İlk açılış kurulumu tamamlanmış ve kurulu lisans geçerli — aksi hâlde aşağıdaki her rota 423 veya 402 döndürür.
  • Kiracınız veya kiracı grubunuz için bir sağlayıcı kimlik bilgisi var, böylece en az bir model çağrılabiliyor.
  • Sohbet, modeller ve tespit için bir ağ geçidi API anahtarınız (Authorization: Bearer cid_key_…) var; oturumları kullanacaksanız kullanıcı JWT'si almak için yerel oturum açma bilgileriniz var.

Aşağıdaki <gateway-host>, ağ geçidinin dinlediği adrestir — varsayılan olarak 3000 portu ya da önündeki ters proxy'nin yayımladığı adres. Rota önekleri tek biçimli değildir: sohbet /chat, oturumlar /sessions, modeller /models altındadır ve /api/v1 altındaki tek yüzey tespittir.

Warning

POST /chat/completions ve POST /sessions/:id/messages her zaman text/event-stream ile yanıt verir. Ağ geçidi modelin yanıtını tamponlar, filtreler ve tüm cevabı tek bir filtered-response olayı olarak yayar — token parçacıkları ve choices[] gövdesi yoktur. Her iki rotada da response.json() çağırmak başarısız olur.

Kimlik bilginizin hangi modellere eriştiğini doğrulayın

GET /models hem JWT hem de cid_key_ taşıyıcısı kabul eder ve yalnızca sağlayıcısının kiracınız için etkin kimlik bilgisi bulunan modelleri döndürür.

curl -s https://<gateway-host>/models \
  -H "Authorization: Bearer $CID222_API_KEY"

Yanıt bir model nesneleri dizisidir. Her biri, model alanında göndereceğiniz model_name değerini ve modeli sunan sağlayıcıyı taşır:

[
  {
    "id": "…",
    "model_name": "gpt-4o",
    "display_name": "GPT-4o (Vision)",
    "provider": { "id": "…", "code": "openai", "name": "OpenAI" },
    "supports_vision": true,
    "supports_streaming": true,
    "is_active": true,
    "created_at": "2026-01-01T00:00:00.000Z"
  }
]

Boş bir dizi, kiracınız için hiçbir sağlayıcı kimlik bilgisinin çözülmediği anlamına gelir ve biri çözülene kadar her sohbet çağrısı başarısız olur.

Komut satırından bir sohbet isteği gönderin

cURL'ün akışı tamponlamaması için -N kullanın.

curl -N -X POST https://<gateway-host>/chat/completions \
  -H "Authorization: Bearer $CID222_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "provider": "openai",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Hello, world!"}
    ],
    "max_tokens": 256
  }'

Terminal, model üretirken duraklar, sonra tüm cevabı tek bir çerçevede yazdırır, ardından sonlandırıcıyı:

data: {"type":"token_usage","usage":{"prompt_tokens":21,"completion_tokens":14,"total_tokens":35}}

data: {"id":"filtered-response","content":"Hello! How can I help you today?","finish_reason":"stop","filtered":false,"entities_masked":0}

data: [DONE]

Model adı belirsiz olduğunda provider gönderin. gpt-4o, gpt-4o-mini ve gpt-4-turbo hem openai hem azure_openai tarafından kaydedilir; Azure'un 3.5 modeli noktasız biçimde gpt-35-turbo'dur.

Akışı kendi kodunuzdan okuyun

Çözülmüş baytları tamponlayın ve çerçeveler arasındaki boş satırdan bölün — tek bir okuma birden çok çerçeve ya da bir çerçevenin yarısını getirebilir.

async function chat(userMessage) {
  const res = await fetch('https://<gateway-host>/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CID222_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-4o',
      provider: 'openai',
      messages: [
        { role: 'system', content: 'You are a helpful assistant.' },
        { role: 'user', content: userMessage },
      ],
    }),
  });
 
  if (!res.ok) throw new Error(`Gateway returned ${res.status}`);
 
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  let answer = '';
 
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
 
    const frames = buffer.split('\n\n');
    buffer = frames.pop() ?? '';
 
    for (const frame of frames) {
      const line = frame.split('\n').find((l) => l.startsWith('data: '));
      if (!line) continue;
      const data = line.slice(6).trim();
      if (data === '[DONE]') return answer;
 
      const event = JSON.parse(data);
 
      // Prompt blocked before the provider was called, or a server error.
      if (event.error) throw new Error(event.error);
      // Answer discarded by the output filter.
      if (event.type === 'output_content_rejected') throw new Error(event.reason);
      if (event.type === 'token_usage') continue;
 
      // The whole filtered answer, as one event.
      if (!event.type && typeof event.content === 'string') answer = event.content;
    }
  }
  return answer;
}

Her iki istemci de tam cevabı döndürür ya da bir politika reddinde hata fırlatır.

İçerik olmayan olayları ele alın

İstek ile cevap arasında ağ geçidi kontrol olayları yayabilir. Kullanmadıklarınızı yok sayın, ancak bilinmeyen bir type değerinin içerik işleyicinize düşmesine izin vermeyin.

OlayNe zaman görünürNe yapmalı
{"error":…,"type":"content_rejected"}İstem politikayı ihlal etti. Sonlandırıcı; sağlayıcı çağrısı ve token harcaması yokerror değerini kullanıcıya gösterin; aynı metni yeniden denemeyin
{"type":"user_locked",…}Kullanıcı bir LLM incelemesiyle kilitlenmiş. SonlandırıcıDurun ve review_id değerini gösterin
{"type":"security_warning",…}Bir inceleme kullanıcıyı işaretledi. Sonlandırıcı değilGünlüğe yazıp devam edin
{"type":"documents_processed",…}documents gönderdinizİsterseniz özetleri raporlayın
{"type":"model_routed",…} / {"type":"routing_evaluated",…}Model yönlendirme isteği değerlendirdiYalnızca telemetri
{"type":"token_optimized",…}İstem sıkıştırma çalıştıYalnızca telemetri
{"type":"token_usage",…}Sağlayıcı kullanım bildirdiğinde her zamanİstem ve tamamlama dağılımını kaydedin
{"type":"output_content_rejected",…}Cevap engellendi. Sonlandırıcıreason değerini gösterin; cevap atılır
{"id":"filtered-response",…}Cevapcontent alanını gösterin
{"type":"output_content_warning",…}Cevap işaretlendi ama teslim edildiCevabın yanında bir uyarı gösterin

Note

İsteminizin ne kadarının maskelendiğini bildiren user_message_processed olayı, /chat/completions üzerinde yalnızca dahili olarak yayılır ve istemciye hiç ulaşmaz. POST /sessions/:id/messages üzerinde ise istemciye ulaşır.

Model çağırmadan metin tarayın

POST /api/v1/guardrails/detect, hiçbir sağlayıcı devreye girmeden aynı tespit işlem hattını çalıştırır. 200 döndürür ve kararı gövdede belirtir; dolayısıyla bir engelleme HTTP hatası değildir.

import os
 
import requests
 
 
def detect(text: str) -> dict:
    response = requests.post(
        'https://<gateway-host>/api/v1/guardrails/detect',
        headers={
            'Authorization': f'Bearer {os.environ["CID222_API_KEY"]}',
            'Content-Type': 'application/json',
        },
        json={'text': text, 'check_type': 'prompt'},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()
 
 
result = detect('Email me at john@example.com')
print(result['action'])       # "mask"
print(result['maskedText'])   # "Email me at [EMAIL]"

action değeri reject, mask, flag veya allow olabilir. text 1–50.000 karakter olmalıdır. detectedEntities[].value bir yer tutucu ya da [REDACTED] değeridir: uç nokta bulduğu ham PII'yi asla geri döndürmez ve normalization.originalText bilinçli olarak boş bırakılır.

Oturum ile konuşmayı sunucuda tutun

Oturumlar geçmişi sunucu tarafında tutar; böylece tur başına tek bir mesaj gönderirsiniz. Yalnızca bir kullanıcı JWT'si kabul ederler — bir cid_key_ taşıyıcısı reddedilir, bu yüzden önce oturum açın.

# 1. Get a user JWT. Valid for JWT_EXPIRES_IN, 24 hours by default.
CID222_JWT=$(curl -s -X POST https://<gateway-host>/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "sarah_smith", "password": "<password>"}' \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
 
# 2. Create the session.
curl -s -X POST https://<gateway-host>/sessions \
  -H "Authorization: Bearer $CID222_JWT" \
  -H "Content-Type: application/json" \
  -d '{"session_name": "Demo"}'
 
# 3. Send a turn. The model is chosen per message, not per session.
curl -N -X POST https://<gateway-host>/sessions/<session-id>/messages \
  -H "Authorization: Bearer $CID222_JWT" \
  -H "Content-Type: application/json" \
  -d '{"content": "What can you help me with?", "model": "gpt-4o"}'

Mesaj çağrısı sohbetle aynı biçimde akar; kodlamaya değer tek fark şudur: bir oturum, istemi {"type":"content_rejected","reason":…} ile ve error alanı olmadan reddeder; bu nedenle /chat/completions için yazılmış bir işleyici bunu kaçırır.

Akışı tarayıcıya aktarın

Ağ geçidi kimlik bilgisini asla ön yüz koduna koymayın. Kendi rotanızda sonlandırın ve çerçeveleri olduğu gibi iletin; böylece tarayıcı aynı sözleşmeyi okur.

import { useCallback, useState } from 'react';
 
type Message = { role: 'user' | 'assistant'; content: string };
 
export function useChat() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [isLoading, setIsLoading] = useState(false);
 
  const sendMessage = useCallback(
    async (content: string) => {
      setIsLoading(true);
      const next: Message[] = [...messages, { role: 'user', content }];
      setMessages(next);
 
      try {
        // Your own route holds the key and pipes the gateway's SSE frames through.
        const res = await fetch('/api/chat', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ messages: next }),
        });
 
        const reader = res.body!.getReader();
        const decoder = new TextDecoder();
        let buffer = '';
 
        while (true) {
          const { done, value } = await reader.read();
          if (done) break;
          buffer += decoder.decode(value, { stream: true });
 
          const frames = buffer.split('\n\n');
          buffer = frames.pop() ?? '';
 
          for (const frame of frames) {
            const line = frame.split('\n').find((l) => l.startsWith('data: '));
            if (!line) continue;
            const data = line.slice(6).trim();
            if (data === '[DONE]') return;
 
            const event = JSON.parse(data);
            if (event.error) throw new Error(event.error);
            if (event.type === 'output_content_rejected') throw new Error(event.reason);
 
            // One event carries the whole answer — append it as a new message.
            if (!event.type && typeof event.content === 'string') {
              setMessages((prev) => [...prev, { role: 'assistant', content: event.content }]);
            }
          }
        }
      } finally {
        setIsLoading(false);
      }
    },
    [messages],
  );
 
  return { messages, sendMessage, isLoading };
}

Asistan mesajı tek adımda görünür. Ağ geçidi üzerinden daktilo efekti mümkün değildir; çünkü çıktı filtrelemesi cevabın herhangi bir bölümünü serbest bırakmadan önce tamamına ihtiyaç duyar.

Doğrulama

  1. curl -s https://<gateway-host>/health çağrısı {"status":"ok",…} döndürür.
  2. curl -s https://<gateway-host>/models -H "Authorization: Bearer $CID222_API_KEY" boş olmayan bir dizi döndürür ve çağırmayı düşündüğünüz model_name bu dizidedir.
  3. Sohbet istemciniz boş olmayan bir metin döndürür ve ham akış, id değeri filtered-response olan tam olarak bir çerçeve, ardından data: [DONE] içerir.
  4. detect("Email me at john@example.com") çağrısı mask değerinde bir action ve adresin bir yer tutucuyla değiştirildiği bir maskedText döndürür.
  5. Filtre kümenizin reddettiği bir istem gönderin. Akış, filtered-response olmadan bir content_rejected olayıyla sonlanır ve deneme, panoda Tüm Tespitler altında rejected eylemiyle görünür.

Başarısız olursa

  • Her rota code: "SETUP_REQUIRED" ile 423 döndürüyor → ilk açılış kurulumu tamamlanmamıştır; sihirbazı bitirin.
  • Her rota code: "LICENSE_EXPIRED" ile 402 döndürüyor → Kurulumdan sonra 402
  • GET /models [] döndürüyor → kiracınız için hiçbir sağlayıcı kimlik bilgisi çözülmüyor. Kimlik Bilgileri altından bir tane ekleyin ya da kiracıyı böyle bir kimlik bilgisi olan bir gruba ekleyin.
  • Dün çalışan bir rotada 401 → JWT'nin süresi dolmuştur ya da API anahtarı yeniden oluşturulmuştur.
  • /chat/completions üzerinde çalışan bir anahtarla /sessions üzerinde 401 → oturumlar yalnızca JWT kabul eder.
  • İstemci askıda kalıp zaman aşımına uğruyor → hiç gelmeyecek token parçacıklarını bekliyordur. Bunun yerine tek filtered-response olayını ele alın. Bkz. Hata yönetimi.

Sonraki adımlar

  • Hata yönetimi — durum kodları, üç hata gövdesi biçimi ve hangi hataların yeniden denemeye değer olduğu.
  • En iyi uygulamalar — kimlik bilgisi yönetimi, bağlantı yeniden kullanımı ve oturumlar ile durumsuz uç nokta arasında seçim.
  • Sohbet tamamlamaları — istek DTO'sunun tamamı ve eksiksiz olay listesi.
  • İçerik tespiti — tespit isteği ve yanıtının her alanı.

Son güncelleme

Bu sayfada