İçeriğe geç

Cookbook Hızlı Başlangıçları

Cookbook örnekleri examples/llmtr-cookbook/ altında secretsiz ve bağımsız bir paket olarak tutulur. Örnekler model kimliğini sabitlemek yerine anonim GET /v1/models yanıtından uygun bir Chat Completions modeli seçer.

Deponun erişim durumundan bağımsız olarak çalıştırılabilir kaynakların tamamı bu sayfada gösterilir. Kodları aşağıdaki dosya adlarıyla yerel klasörünüze kaydedin.

Terminal window
mkdir -p llmtr-cookbook/typescript llmtr-cookbook/python
cd llmtr-cookbook
npm init -y
npm pkg set type=module
npm install --save-dev tsx typescript @types/node
cp .env.example .env

.env içine kendi LLMTR API anahtarınızı yazın. Bu dosyayı Git'e eklemeyin.

TypeScript örneği Node.js'in yerleşik fetch uygulamasını kullanır:

Terminal window
npx tsx typescript/quickstart.ts
npx tsx typescript/streaming.ts

Python örneği yalnızca standart kütüphaneyi kullanır:

Terminal window
python python/quickstart.py
python python/streaming.py
LLMTR_API_KEY=
LLMTR_BASE_URL=https://llmtr.com
import { readFileSync } from "node:fs";
import { resolve } from "node:path";

export interface ModelCard {
  id: string;
  supported_operations?: string[];
  supported_endpoints?: string[];
}

interface ModelList {
  object: "list";
  data: ModelCard[];
}

function loadLocalEnv(): void {
  let body: string;
  try {
    body = readFileSync(resolve(process.cwd(), ".env"), "utf8");
  } catch {
    return;
  }

  for (const line of body.split(/\r?\n/)) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const separator = trimmed.indexOf("=");
    if (separator <= 0) continue;
    const key = trimmed.slice(0, separator).trim();
    const value = trimmed.slice(separator + 1).trim();
    if (key && process.env[key] === undefined) process.env[key] = value;
  }
}

loadLocalEnv();

export const baseUrl = (process.env.LLMTR_BASE_URL || "https://llmtr.com").replace(/\/$/, "");

export function requireApiKey(): string {
  const apiKey = process.env.LLMTR_API_KEY?.trim();
  if (!apiKey) {
    throw new Error("LLMTR_API_KEY is missing. Copy .env.example to .env and set it locally.");
  }
  return apiKey;
}

export async function discoverChatModel(): Promise<ModelCard> {
  const response = await fetch(`${baseUrl}/v1/models`, {
    headers: { Accept: "application/json" }
  });
  if (!response.ok) {
    throw new Error(`Model discovery failed with HTTP ${response.status}.`);
  }

  const payload = (await response.json()) as ModelList;
  if (payload.object !== "list" || !Array.isArray(payload.data)) {
    throw new Error("Model discovery returned an unexpected response shape.");
  }

  const model = payload.data.find(
    (candidate) =>
      candidate.supported_operations?.includes("CHAT_COMPLETIONS") &&
      candidate.supported_endpoints?.includes("/v1/chat/completions")
  );
  if (!model) {
    throw new Error("No public Chat Completions model is currently discoverable.");
  }
  return model;
}

export async function readSafeError(response: Response): Promise<string> {
  try {
    const payload = (await response.json()) as { error?: { message?: unknown; type?: unknown } };
    const message = typeof payload.error?.message === "string" ? payload.error.message : "Request failed";
    const type = typeof payload.error?.type === "string" ? payload.error.type : "unknown_error";
    return `${type}: ${message}`;
  } catch {
    return "Request failed with a non-JSON response.";
  }
}
import { baseUrl, discoverChatModel, readSafeError, requireApiKey } from "./shared.js";

const apiKey = requireApiKey();
const model = await discoverChatModel();

const response = await fetch(`${baseUrl}/v1/chat/completions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: model.id,
    messages: [{ role: "user", content: "Reply with one short sentence." }]
  })
});

if (!response.ok) {
  throw new Error(`Chat completion failed with HTTP ${response.status}: ${await readSafeError(response)}`);
}

const payload = (await response.json()) as {
  choices?: Array<{ message?: { content?: string } }>;
};
const content = payload.choices?.[0]?.message?.content;
if (typeof content !== "string") {
  throw new Error("Chat completion returned an unexpected response shape.");
}

console.log(content);
import { baseUrl, discoverChatModel, readSafeError, requireApiKey } from "./shared.js";

const apiKey = requireApiKey();
const model = await discoverChatModel();
const response = await fetch(`${baseUrl}/v1/chat/completions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    Accept: "text/event-stream",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: model.id,
    messages: [{ role: "user", content: "Count from one to three." }],
    stream: true,
    stream_options: { include_usage: true }
  })
});

if (!response.ok) {
  throw new Error(`Streaming failed with HTTP ${response.status}: ${await readSafeError(response)}`);
}
if (!response.body) throw new Error("Streaming response has no body.");

const decoder = new TextDecoder();
let buffer = "";
for await (const chunk of response.body) {
  buffer += decoder.decode(chunk, { stream: true });
  const lines = buffer.split(/\r?\n/);
  buffer = lines.pop() ?? "";
  for (const line of lines) {
    if (!line.startsWith("data:")) continue;
    const data = line.slice("data:".length).trim();
    if (!data || data === "[DONE]") continue;
    const event = JSON.parse(data) as { choices?: Array<{ delta?: { content?: string } }> };
    const content = event.choices?.[0]?.delta?.content;
    if (content) process.stdout.write(content);
  }
}
process.stdout.write("\n");
import json
import os
from pathlib import Path
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen


def _load_local_env() -> None:
    path = Path.cwd() / ".env"
    if not path.is_file():
        return
    for raw_line in path.read_text(encoding="utf-8").splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        key, value = line.split("=", 1)
        os.environ.setdefault(key.strip(), value.strip())


_load_local_env()
BASE_URL = os.environ.get("LLMTR_BASE_URL", "https://llmtr.com").rstrip("/")


def require_api_key() -> str:
    api_key = os.environ.get("LLMTR_API_KEY", "").strip()
    if not api_key:
        raise RuntimeError("LLMTR_API_KEY is missing. Copy .env.example to .env and set it locally.")
    return api_key


def discover_chat_model() -> str:
    request = Request(f"{BASE_URL}/v1/models", headers={"Accept": "application/json"})
    with urlopen(request, timeout=30) as response:
        payload = json.load(response)
    if payload.get("object") != "list" or not isinstance(payload.get("data"), list):
        raise RuntimeError("Model discovery returned an unexpected response shape.")
    for model in payload["data"]:
        if (
            "CHAT_COMPLETIONS" in model.get("supported_operations", [])
            and "/v1/chat/completions" in model.get("supported_endpoints", [])
        ):
            return str(model["id"])
    raise RuntimeError("No public Chat Completions model is currently discoverable.")


def safe_http_error(error: HTTPError) -> str:
    try:
        payload: dict[str, Any] = json.loads(error.read().decode("utf-8"))
        details = payload.get("error", {})
        return f"{details.get('type', 'unknown_error')}: {details.get('message', 'Request failed')}"
    except (UnicodeDecodeError, json.JSONDecodeError):
        return "Request failed with a non-JSON response."
import json
from urllib.error import HTTPError
from urllib.request import Request, urlopen

from shared import BASE_URL, discover_chat_model, require_api_key, safe_http_error


api_key = require_api_key()
model = discover_chat_model()
body = json.dumps(
    {
        "model": model,
        "messages": [{"role": "user", "content": "Reply with one short sentence."}],
    }
).encode("utf-8")
request = Request(
    f"{BASE_URL}/v1/chat/completions",
    data=body,
    method="POST",
    headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
)

try:
    with urlopen(request, timeout=60) as response:
        payload = json.load(response)
except HTTPError as error:
    raise RuntimeError(f"Chat completion failed with HTTP {error.code}: {safe_http_error(error)}") from error

try:
    print(payload["choices"][0]["message"]["content"])
except (KeyError, IndexError, TypeError) as error:
    raise RuntimeError("Chat completion returned an unexpected response shape.") from error
import json
import sys
from urllib.error import HTTPError
from urllib.request import Request, urlopen

from shared import BASE_URL, discover_chat_model, require_api_key, safe_http_error


api_key = require_api_key()
model = discover_chat_model()
body = json.dumps(
    {
        "model": model,
        "messages": [{"role": "user", "content": "Count from one to three."}],
        "stream": True,
        "stream_options": {"include_usage": True},
    }
).encode("utf-8")
request = Request(
    f"{BASE_URL}/v1/chat/completions",
    data=body,
    method="POST",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Accept": "text/event-stream",
        "Content-Type": "application/json",
    },
)

try:
    with urlopen(request, timeout=120) as response:
        for raw_line in response:
            line = raw_line.decode("utf-8").strip()
            if not line.startswith("data:"):
                continue
            data = line[len("data:") :].strip()
            if not data or data == "[DONE]":
                continue
            event = json.loads(data)
            content = event.get("choices", [{}])[0].get("delta", {}).get("content")
            if content:
                sys.stdout.write(content)
                sys.stdout.flush()
except HTTPError as error:
    raise RuntimeError(f"Streaming failed with HTTP {error.code}: {safe_http_error(error)}") from error

print()
  1. Anonim model kataloğunu alır.
  2. /v1/chat/completions desteği ilan eden bir model seçer.
  3. API anahtarını yalnız Authorization header'ına koyar.
  4. HTTP başarısızsa güvenli hata özetini gösterir.
  5. Streaming örneğinde SSE data: satırlarını işler ve [DONE] ile kapanır.

Örnekler prompt veya model yanıtını kalıcı depoya yazmaz. API anahtarını hata mesajına, stdout'a veya history kaydına eklemez.

OpenAI SDK kullanıyorsanız base_url / baseURL değerini https://llmtr.com/v1 olarak ve anahtarı LLMTR anahtarınızla değiştirin. Model kimliğini model keşfinden alın. Ayrıntılı eşleme için OpenAI/OpenRouter geçiş rehberini kullanın.

API sözleşmesini istemci üretimi veya doğrulama için kullanmak üzere OpenAPI ve Postman sayfasına bakın.