یک کلاینت سازگار با OpenAI را به درگاه وصل کنید. کلید کلاینت و یک مسیر به آن بدهید.
۱. کلید را در یک متغیر محیطی بگذارید
همهٔ نمونههای این صفحه کلید را از این متغیر میخوانند. کلید را در فایل پیکربندی نچسبانید.
bash
export HOOSHYAR_API_KEY="<your client key>"
۲. یک مسیر انتخاب کنید
مسیر نام یک نوع کار است. مدیر سیستم مسیرها و مدل پشت هر مسیر را تعیین میکند.
برای اینکه درگاه مسیر را انتخاب کند، auto را به عنوان مدل بفرستید. برای انتخاب مسیر توسط خودتان، نام آن را به عنوان مدل بفرستید یا سرآیند x-task-type را بفرستید.
برای دیدن مسیرهایی که کلیدتان میتواند استفاده کند، /v1/models را فراخوانی کنید.
۳. کلاینت خود را پیکربندی کنید
Claude Code
محل: فایل پروفایل پوسته (~/.zshrc، ~/.bashrc) یا یک .envrc برای هر پروژه
bash
export ANTHROPIC_BASE_URL="https://gateway.example.com"
export ANTHROPIC_AUTH_TOKEN="${HOOSHYAR_API_KEY}"
# Optional: pin every request from this shell to one route instead of
# letting the classifier choose per message.
# export ANTHROPIC_CUSTOM_HEADERS="x-task-type: <ROUTE_NAME>"
Claude Code از API پیامهای Anthropic استفاده میکند و درگاه این API را مستقیم ارائه میکند. در همان پوسته ANTHROPIC_API_KEY را حذف کنید، وگرنه بر توکن بالا مقدم میشود.
model نام مسیر است. درگاه هر مسیر را در /v1/models به عنوان یک مدل نشان میدهد؛ پس تغییر مدل در Continue مسیر را تغییر میدهد.
OpenAI SDK (Python)
محل: هر جا که کلاینت را میسازید
python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://gateway.example.com/v1",
api_key=os.environ["HOOSHYAR_API_KEY"],
max_retries=2, # 3 tries in total, with backoff and jitter
)
resp = client.chat.completions.create(
model="auto", # a ROUTE name; use "auto" to let the classifier choose
messages=[{"role": "user", "content": "Hello"}],
)
OpenAI SDK (TypeScript)
محل: هر جا که کلاینت را میسازید
typescript
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://gateway.example.com/v1",
apiKey: process.env.HOOSHYAR_API_KEY!,
maxRetries: 2, // 3 tries in total, with backoff and jitter
});
const resp = await client.chat.completions.create({
model: "auto", // a ROUTE name; use "auto" to let the classifier choose
messages: [{ role: "user", content: "Hello" }],
});
برنامه یا ربات (fetch، با تلاش دوباره)
محل: ماژولی که درگاه را فرا میخواند
typescript
// Retry a refused or reset connection and a 502, 503 or 504. 3 tries,
// about 30 s at most. Every other status is the answer: return it.
const RETRY_STATUS = new Set([502, 503, 504]);
const BASE_DELAY_MS = [2_000, 8_000]; // before try 2, before try 3
export async function complete(body: object, sessionId: string): Promise<Response> {
for (let attempt = 0; ; attempt++) {
const last = attempt >= BASE_DELAY_MS.length;
try {
const res = await fetch("https://gateway.example.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.HOOSHYAR_API_KEY}`,
"Content-Type": "application/json",
"x-session-id": sessionId, // the same id on every try
},
body: JSON.stringify(body),
});
if (last || !RETRY_STATUS.has(res.status)) return res;
await res.body?.cancel();
await sleep(delayMs(attempt, res.headers.get("retry-after")));
} catch (err) {
if (last) throw err; // refused, reset, DNS: no answer at all
await sleep(delayMs(attempt, null));
}
}
}
// Retry-After when the gateway sends one (max 15 s), else 50-100 % of the base.
function delayMs(attempt: number, retryAfter: string | null): number {
const seconds = Number(retryAfter);
if (seconds > 0) return Math.min(seconds, 15) * 1_000;
const base = BASE_DELAY_MS[attempt]!;
return base / 2 + Math.random() * (base / 2);
}
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// One session per conversation. A stream that breaks after the first token
// is NOT retried: fetch returns at the headers, so the read error reaches you.
// Show the partial answer, or tell the user and start the turn again.
const res = await complete({ model: "auto", messages }, `chat-${chatId}`);
فقط درخواستی را دوباره میفرستد که هنوز پاسخی ندارد: اتصالِ ردشده یا قطعشده، و 502، 503 و 504. یک 4xx (مسدودسازی گاردریل، رد دسترسی، محدودیت نرخ) خودِ پاسخ است. شناسهٔ نشست را حداکثر ۶۴ نویسه نگه دارید: درگاه شناسهٔ بلندتر را ثبت نمیکند.
سریعترین راه برای آزمودن کلید. x-task-type مسیر را صریح نام میبرد؛ با model: "auto" و بدون این سرآیند، دستهبند یک مسیر انتخاب میکند.
تصویر، صوت، دستهای (batch) و رتبهبندی مجدد (rerank)
این سطحها از مسیر گفتگو استفاده نمیکنند. به جای <MODEL_ID> نام مدلی را بگذارید که مدیر سیستم به شما میدهد.
تصویر
محل: یک ترمینال، یا همان کلاینت SDK
bash
curl https://gateway.example.com/v1/images/generations \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"model":"auto","prompt":"a wide shot of a lighthouse at dusk","n":1}'
# Editing an existing image is multipart, and the model is a FORM FIELD —
# send it BEFORE the file part (see the note below).
curl https://gateway.example.com/v1/images/edits \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-F "model=<MODEL_ID>" \
-F "prompt=make the sky clearer" \
-F "image=@photo.png"
تولید تصویر مسیریابی میشود: به مسیر image_generation میرود و برای هر تصویری که برمیگردد هزینه دارد. ویرایش و تنوع تصویر چندبخشی (multipart) هستند و مسیریابی نمیشوند: یک مدل مشخص نام ببرید. model را پیش از بخش فایل بفرستید؛ کلید محدودی که model را پس از فایل بفرستد رد میشود.
صوت
محل: یک ترمینال، یا همان کلاینت SDK
bash
# Speech (text in, AUDIO BYTES out) — routed to the `tts` route,
# priced per character of input.
curl https://gateway.example.com/v1/audio/speech \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"model":"auto","input":"Hello from Hooshyar.","voice":"alloy"}' \
--output speech.mp3
# Transcription (multipart) — name a concrete model, `model` first.
curl https://gateway.example.com/v1/audio/transcriptions \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-F "model=<MODEL_ID>" \
-F "file=@meeting.m4a"
تبدیل متن به گفتار صوت خام برمیگرداند؛ آن را در یک فایل بریزید و به عنوان JSON نخوانید. رونویسی چندبخشی است، پس همان قاعدهٔ «model پیش از فایل» برقرار است. مدت صوتِ رونویسی ثبت نمیشود، چون درگاه فایل را نمیخواند.
دستهای (Batch)
محل: یک ترمینال، یا همان کلاینت SDK
bash
# 1. Upload the input file (one JSON request per line).
curl https://gateway.example.com/v1/files \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-F "purpose=batch" \
-F "file=@requests.jsonl"
# 2. Submit the batch, naming the file id from step 1.
curl https://gateway.example.com/v1/batches \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"input_file_id":"file-…","endpoint":"/v1/chat/completions","completion_window":"24h"}'
# 3. Poll it. When the provider finishes, this is also what records the
# cost — so polling your own batch is doing everyone a favour.
curl https://gateway.example.com/v1/batches/batch_… \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}"
برای هر دو گام همان کلید را به کار ببرید: یک batch فقط فایل ورودیای را میپذیرد که همان کلید بارگذاری کرده است. نتیجهها در بازهٔ تکمیل (تا ۲۴ ساعت) میرسند. هزینه فعلاً با نرخ عادی محاسبه میشود؛ آن را یک برآورد بیش از واقع (over-estimate) بدانید.
رتبهبندی مجدد (Rerank)
محل: یک ترمینال، یا همان کلاینت SDK
bash
curl https://gateway.example.com/v1/rerank \
-H "Authorization: Bearer ${HOOSHYAR_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model":"auto",
"query":"how do I rotate a client key",
"documents":["key rotation guide","billing FAQ","routing overview"]
}'
به مسیر rerank میرود (بدون متنِ قابل دستهبندی، مانند تصویر و گفتار). سندها شمرده میشوند ولی برای هر سند هزینهای حساب نمیشود، چون ارائهدهندهها برای هر جستجو هزینه میگیرند.
پخش جریانی (streaming)
پخش جریانی مانند API اوپنایآی کار میکند. درگاه جریان را بدون تغییر به کلاینت شما میفرستد.