مستندات درگاه هوش مصنوعی پنزی
درگاه پنزی یک API واحد و سازگار با OpenAI است که درخواستهای شما را به OpenAI، Anthropic و OpenRouter هدایت میکند. با یک کلید به همه مدلها دسترسی دارید و نیازی به ساخت حساب جداگانه نزد هر ارائهدهنده نیست.
| مورد | مقدار |
|---|---|
| Base URL | https://ai.penzy.net/v1 |
| احراز هویت | Authorization: Bearer sk-pz-… یا x-api-key: sk-pz-… |
| فرمت | JSON — کاملاً سازگار با OpenAI Chat Completions؛ بهعلاوه Anthropic Messages بومی |
| استریم | Server-Sent Events با "stream": true |
| نام مدل | provider/model — مثلاً openai/gpt-4o-mini |
نکته: اگر کدی دارید که با OpenAI کار میکند، تنها کافی است base_url و api_key را عوض کنید. همه چیز دیگر — حتی برای Claude و Gemini — همانطور کار میکند.
شروع سریع
۱. کلید API خود را از مدیر سامانه دریافت کنید (با sk-pz- شروع میشود) و آن را در متغیر محیطی قرار دهید:
export PENZY_API_KEY="sk-pz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"۲. اولین درخواست را ارسال کنید:
curl https://ai.penzy.net/v1/chat/completions \
-H "Authorization: Bearer $PENZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "پایتخت ایران کجاست؟"}
]
}'
# pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://ai.penzy.net/v1",
api_key=os.environ["PENZY_API_KEY"],
)
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "پایتخت ایران کجاست؟"},
],
)
print(resp.choices[0].message.content)
// npm install openai
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://ai.penzy.net/v1",
apiKey: process.env.PENZY_API_KEY,
});
const resp = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "پایتخت ایران کجاست؟" },
],
});
console.log(resp.choices[0].message.content);
۳. برای استفاده از Claude یا هر مدل دیگری، فقط مقدار model را عوض کنید:
resp = client.chat.completions.create(
model="anthropic/claude-sonnet-4-5", # Claude
# model="openrouter/google/gemini-2.5-flash", # Gemini via OpenRouter
# model="openrouter/deepseek/deepseek-chat", # DeepSeek via OpenRouter
messages=[{"role": "user", "content": "Hello!"}],
)پاسخ نمونه:
{
"id": "chatcmpl-9f2c...",
"object": "chat.completion",
"created": 1762950000,
"model": "openai/gpt-4o-mini",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "پایتخت ایران تهران است." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 9, "total_tokens": 33 }
}احراز هویت
هر درخواست باید کلید API را در هدر Authorization بهصورت Bearer ارسال کند. برای سازگاری با SDK رسمی Anthropic، هدر x-api-key نیز پذیرفته میشود.
POST /v1/chat/completions HTTP/1.1
Host: ai.penzy.net
Authorization: Bearer sk-pz-xxxxxxxxxxxxxxxx
Content-Type: application/jsonامنیت: کلید خود را هرگز در کد سمت کلاینت (مرورگر یا اپلیکیشن موبایل) یا مخزن Git قرار ندهید. در صورت افشا، فوراً به مدیر سامانه اطلاع دهید تا کلید باطل و کلید جدید صادر شود.
هر کلید میتواند این محدودیتها را داشته باشد (توسط مدیر تعیین میشود):
- مدلهای مجاز — مثلاً فقط
openai/*یاanthropic/claude-* - سقف بودجه — دلاری، بهصورت روزانه، ماهانه یا کل
- محدودیت نرخ — حداکثر تعداد درخواست در دقیقه (RPM)
- تاریخ انقضا
وضعیت و مصرف کلید خود را در داشبورد مصرف یا با GET /v1/me ببینید.
نامگذاری مدلها
شناسه هر مدل به شکل provider/model است. پیشوند، ارائهدهندهای را که درخواست به آن ارسال میشود تعیین میکند:
| پیشوند | ارائهدهنده | نمونه |
|---|---|---|
openai/ | OpenAI (مستقیم) | openai/gpt-4o، openai/o4-mini، openai/text-embedding-3-small |
anthropic/ | Anthropic (مستقیم) | anthropic/claude-sonnet-4-5، anthropic/claude-haiku-4-5 |
openrouter/ | OpenRouter | openrouter/google/gemini-2.5-pro، openrouter/x-ai/grok-4 |
میانبرها
- نام بدون پیشوند که با
gpt-،o1/o3/o4،text-embedding-یاdall-eشروع شود ← OpenAI. مثال:gpt-4o-mini - نام بدون پیشوند که با
claudeشروع شود ← Anthropic. مثال:claude-sonnet-4-5 - نام دوبخشی که پیشوندش یک ارائهدهنده نیست ← OpenRouter. مثال:
meta-llama/llama-3.3-70b-instruct
فهرست کامل و بهروز مدلها همراه قیمت هر میلیون توکن در صفحه مدلها و از طریق GET /v1/models در دسترس است.
Chat Completions
اصلیترین endpoint برای گفتوگو با همه مدلها. درخواست و پاسخ دقیقاً مطابق OpenAI Chat Completions است. برای مدلهای Anthropic، درگاه بهطور خودکار پیامها، ابزارها، تصاویر و استریم را به فرمت Claude تبدیل میکند.
پارامترهای بدنه
| پارامتر | نوع | توضیح |
|---|---|---|
modelrequired | string | شناسه مدل، مثلاً anthropic/claude-sonnet-4-5 |
messagesrequired | array | آرایه پیامها با نقشهای system، developer، user، assistant، tool |
stream | boolean | دریافت پاسخ بهصورت تدریجی (SSE). پیشفرض false |
stream_options | object | {"include_usage": true} برای دریافت مصرف توکن در آخرین chunk |
max_tokens / max_completion_tokens | integer | حداکثر توکن خروجی. برای Claude در صورت عدم تعیین ۸۱۹۲ در نظر گرفته میشود |
temperature | number | ۰ تا ۲ (برای Claude حداکثر ۱ اعمال میشود) |
top_p | number | نمونهبرداری هستهای |
stop | string | array | توالیهای توقف |
tools | array | تعریف توابع قابل فراخوانی — جزئیات |
tool_choice | string | object | auto، none، required یا {"type":"function","function":{"name":"…"}} |
parallel_tool_calls | boolean | اجازه فراخوانی همزمان چند ابزار |
response_format | object | {"type":"json_object"} یا json_schema — جزئیات |
reasoning_effort | string | low، medium، high برای مدلهای استدلالی — جزئیات |
user | string | شناسه کاربر نهایی شما (برای ردیابی سوءاستفاده) |
سایر پارامترهای استاندارد OpenAI (مانند seed، logprobs، frequency_penalty) برای مدلهای OpenAI و OpenRouter بدون تغییر منتقل میشوند. پارامترهای اختصاصی OpenRouter (مثل provider، transforms، models) نیز پشتیبانی میشوند.
گفتوگوی چندمرحلهای
messages = [{"role": "system", "content": "تو یک دستیار برنامهنویسی هستی."}]
while True:
user = input("you: ")
messages.append({"role": "user", "content": user})
r = client.chat.completions.create(model="anthropic/claude-sonnet-4-5", messages=messages)
answer = r.choices[0].message.content
messages.append({"role": "assistant", "content": answer})
print("ai:", answer)استریم (Server-Sent Events)
با "stream": true پاسخ بهصورت قطعات chat.completion.chunk و در قالب SSE ارسال میشود و با data: [DONE] پایان مییابد. این قابلیت برای همه ارائهدهندگان یکسان است.
stream = client.chat.completions.create(
model="anthropic/claude-sonnet-4-5",
messages=[{"role": "user", "content": "یک داستان کوتاه بنویس"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n\ntokens:", chunk.usage.total_tokens)
const stream = await client.chat.completions.create({
model: "anthropic/claude-sonnet-4-5",
messages: [{ role: "user", content: "یک داستان کوتاه بنویس" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
curl -N https://ai.penzy.net/v1/chat/completions \
-H "Authorization: Bearer $PENZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-4o-mini","stream":true,
"messages":[{"role":"user","content":"Count to 5"}]}'
// فقط در محیط سرور یا با کلید محدود استفاده کنید
const res = await fetch("https://ai.penzy.net/v1/chat/completions", {
method: "POST",
headers: { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "openai/gpt-4o-mini", stream: true,
messages: [{ role: "user", content: "Hi" }] }),
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
const lines = buf.split("\n"); buf = lines.pop();
for (const line of lines) {
if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
const delta = JSON.parse(line.slice(6)).choices[0]?.delta?.content;
if (delta) output.textContent += delta;
}
}
فرمت خام رویدادها:
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"سلام"}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Tool / Function Calling
ابزارها را با فرمت استاندارد OpenAI تعریف کنید. این قابلیت برای مدلهای OpenAI، Claude و بیشتر مدلهای OpenRouter کار میکند.
import json
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "هوای تهران چطوره؟"}]
r = client.chat.completions.create(model="anthropic/claude-sonnet-4-5",
messages=messages, tools=tools)
msg = r.choices[0].message
if msg.tool_calls:
messages.append(msg) # assistant message with tool_calls
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = {"city": args["city"], "temp_c": 18, "sky": "clear"} # your code
messages.append({"role": "tool", "tool_call_id": call.id,
"content": json.dumps(result)})
final = client.chat.completions.create(model="anthropic/claude-sonnet-4-5",
messages=messages, tools=tools)
print(final.choices[0].message.content)
const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Get current weather for a city",
parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
},
}];
const messages = [{ role: "user", content: "هوای تهران چطوره؟" }];
const r = await client.chat.completions.create({ model: "openai/gpt-4o", messages, tools });
const msg = r.choices[0].message;
if (msg.tool_calls) {
messages.push(msg);
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
messages.push({ role: "tool", tool_call_id: call.id,
content: JSON.stringify({ city: args.city, temp_c: 18 }) });
}
const final = await client.chat.completions.create({ model: "openai/gpt-4o", messages, tools });
console.log(final.choices[0].message.content);
}
در حالت استریم، آرگومانهای ابزار بهصورت تکهتکه در delta.tool_calls[i].function.arguments میآیند و finish_reason برابر tool_calls خواهد بود.
ورودی تصویر (Vision)
تصویر را بهصورت URL یا Data URL با base64 در محتوای پیام بفرستید. برای Claude، درگاه بهطور خودکار آن را به بلوک image تبدیل میکند.
import base64
with open("invoice.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
r = client.chat.completions.create(
model="anthropic/claude-sonnet-4-5",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "مبلغ کل این فاکتور چقدر است؟"},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
# or: {"type": "image_url", "image_url": {"url": "https://example.com/a.jpg"}}
],
}],
)
خروجی JSON و Structured Output
با response_format خروجی را به JSON محدود کنید. برای مدلهای OpenAI از json_schema با strict استفاده کنید. برای Claude، اسکیما به دستورالعمل سیستمی تبدیل میشود (یا از tool calling برای خروجی ساختیافته کاملاً تضمینشده استفاده کنید).
r = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Extract: Ali Rezaei, 32, Tehran"}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"},
},
"required": ["name", "age", "city"],
"additionalProperties": False,
},
},
},
)
print(r.choices[0].message.content) # {"name":"Ali Rezaei","age":32,"city":"Tehran"}استدلال (Reasoning / Extended Thinking)
برای مدلهای استدلالی OpenAI (سری o و GPT-5) از reasoning_effort استفاده کنید. برای Claude همین پارامتر به extended thinking با بودجه متناظر تبدیل میشود و متن استدلال در فیلد reasoning_content برگردانده میشود. همچنین میتوانید پارامتر بومی thinking را مستقیماً ارسال کنید.
r = client.chat.completions.create(
model="anthropic/claude-sonnet-4-5",
messages=[{"role": "user", "content": "اثبات کن جذر ۲ گنگ است."}],
reasoning_effort="medium", # low | medium | high
# extra_body={"thinking": {"type": "enabled", "budget_tokens": 10000}},
)
print(r.choices[0].message.content)| reasoning_effort | بودجه thinking در Claude |
|---|---|
low | 2,048 توکن |
medium | 8,192 توکن |
high | 24,576 توکن |
Anthropic Messages API (بومی)
اگر از SDK رسمی Anthropic یا ابزارهایی مانند Claude Code استفاده میکنید، این endpoint درخواست را بدون هیچ تبدیلی به Anthropic ارسال میکند؛ بنابراین همه قابلیتها (prompt caching، extended thinking، PDF، citations، ابزارهای سمت سرور و…) در دسترس هستند. هدرهای anthropic-version و anthropic-beta منتقل میشوند.
این endpoint برای مدلهای anthropic/* (یا نامهای بدون پیشوند claude-…) طراحی شده است. برای استفاده از سایر مدلها از Chat Completions استفاده کنید. شمارش توکن نیز از طریق POST /v1/messages/count_tokens در دسترس است.
curl https://ai.penzy.net/v1/messages \
-H "x-api-key: $PENZY_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'
# pip install anthropic
import anthropic
client = anthropic.Anthropic(
base_url="https://ai.penzy.net", # بدون /v1 — SDK خودش اضافه میکند
api_key="sk-pz-...",
)
msg = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(msg.content[0].text)
// npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://ai.penzy.net", apiKey: process.env.PENZY_API_KEY });
const msg = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
});
console.log(msg.content[0].text);
Responses API
API جدید OpenAI برای مدلهای openai/* (و مدلهای OpenRouter که آن را پشتیبانی میکنند). درخواست بدون تغییر به ارائهدهنده منتقل میشود.
r = client.responses.create(
model="openai/gpt-4o-mini",
input="سه نکته برای نوشتن کد تمیز بگو",
)
print(r.output_text)Embeddings
تولید بردار برای جستوجوی معنایی و RAG. مدلهای openai/* و مدلهای embedding در OpenRouter.
e = client.embeddings.create(
model="openai/text-embedding-3-small",
input=["سلام دنیا", "Hello world"],
)
print(len(e.data[0].embedding)) # 1536
curl https://ai.penzy.net/v1/embeddings \
-H "Authorization: Bearer $PENZY_API_KEY" -H "Content-Type: application/json" \
-d '{"model":"openai/text-embedding-3-small","input":"سلام دنیا"}'
تولید تصویر
تولید تصویر با مدلهای OpenAI مانند openai/gpt-image-1 و openai/dall-e-3.
img = client.images.generate(
model="openai/gpt-image-1",
prompt="A Persian garden at sunset, watercolor",
size="1024x1024",
)
import base64; open("out.png", "wb").write(base64.b64decode(img.data[0].b64_json))همچنین POST /v1/moderations و POST /v1/completions (legacy) برای مدلهای سازگار پشتیبانی میشوند.
فهرست مدلها
مدلهایی که کلید شما اجازه استفاده از آنها را دارد، همراه با طول context و قیمت (دلار به ازای هر یک میلیون توکن).
curl https://ai.penzy.net/v1/models -H "Authorization: Bearer $PENZY_API_KEY"
for m in client.models.list():
print(m.id)
{
"object": "list",
"data": [
{
"id": "anthropic/claude-sonnet-4-5-20250929",
"object": "model",
"owned_by": "anthropic",
"name": "Claude Sonnet 4.5",
"context_length": 1000000,
"pricing": { "input_per_million": 3, "output_per_million": 15, "currency": "USD" }
}
]
}
مصرف و اطلاعات کلید
اطلاعات کلید، بودجه باقیمانده، مجموع مصرف و ریز مصرف ۳۰ روز اخیر به تفکیک مدل و روز. همین اطلاعات در داشبورد مصرف بهصورت گرافیکی نمایش داده میشود.
curl https://ai.penzy.net/v1/me -H "Authorization: Bearer $PENZY_API_KEY"
{
"key": { "name": "backend-prod", "prefix": "sk-pz-AbCd…wxyz", "owner": "Team A", "rpm_limit": 60, "allowed_models": "*" },
"budget": { "limit_usd": 50, "period": "monthly", "spent_usd": 12.41, "remaining_usd": 37.59 },
"totals": { "requests": 1840, "prompt_tokens": 2210400, "completion_tokens": 512330, "cost_usd": 31.07 },
"last_30_days": { "by_model": [ … ], "daily": [ … ] },
"recent": [ … ]
}
Python
از کتابخانه رسمی openai استفاده کنید. میتوانید بهجای کد، متغیرهای محیطی را تنظیم کنید:
pip install openai
export OPENAI_BASE_URL="https://ai.penzy.net/v1"
export OPENAI_API_KEY="sk-pz-..."
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI() # reads OPENAI_BASE_URL / OPENAI_API_KEY
async def ask(model, q):
r = await client.chat.completions.create(model=model, messages=[{"role": "user", "content": q}])
return model, r.choices[0].message.content
async def main():
models = ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5", "openrouter/google/gemini-2.5-flash"]
for model, answer in await asyncio.gather(*(ask(m, "یک جمله درباره ایران") for m in models)):
print(f"[{model}] {answer}\n")
asyncio.run(main())
import requests
r = requests.post(
"https://ai.penzy.net/v1/chat/completions",
headers={"Authorization": "Bearer sk-pz-..."},
json={"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hi"}]},
timeout=120,
)
r.raise_for_status()
print(r.json()["choices"][0]["message"]["content"])
Node.js / TypeScript
import OpenAI from "openai";
export const ai = new OpenAI({
baseURL: "https://ai.penzy.net/v1",
apiKey: process.env.PENZY_API_KEY!,
timeout: 120_000,
maxRetries: 2,
});
const r = await ai.chat.completions.create({
model: "openrouter/deepseek/deepseek-chat",
messages: [{ role: "user", content: "Explain closures in JS" }],
});
console.log(r.choices[0].message.content);
const r = await fetch("https://ai.penzy.net/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PENZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "anthropic/claude-haiku-4-5",
messages: [{ role: "user", content: "Hi" }],
}),
});
const data = await r.json();
console.log(data.choices[0].message.content);
PHP
<?php
$ch = curl_init("https://ai.penzy.net/v1/chat/completions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("PENZY_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"model" => "openai/gpt-4o-mini",
"messages" => [["role" => "user", "content" => "سلام"]],
]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res["choices"][0]["message"]["content"];
use Illuminate\Support\Facades\Http;
$res = Http::withToken(env('PENZY_API_KEY'))
->timeout(120)
->post('https://ai.penzy.net/v1/chat/completions', [
'model' => 'anthropic/claude-sonnet-4-5',
'messages' => [['role' => 'user', 'content' => 'سلام']],
])->throw()->json();
return $res['choices'][0]['message']['content'];
// composer require openai-php/client
$client = OpenAI::factory()
->withBaseUri('https://ai.penzy.net/v1')
->withApiKey(getenv('PENZY_API_KEY'))
->make();
$r = $client->chat()->create([
'model' => 'openai/gpt-4o-mini',
'messages' => [['role' => 'user', 'content' => 'Hello']],
]);
echo $r->choices[0]->message->content;
Anthropic SDK
SDK رسمی Anthropic را با base_url="https://ai.penzy.net" (بدون /v1) پیکربندی کنید. نمونه در بخش Anthropic Messages آمده است. متغیرهای محیطی معادل:
export ANTHROPIC_BASE_URL="https://ai.penzy.net"
export ANTHROPIC_API_KEY="sk-pz-..."LangChain
# pip install langchain-openai
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
llm = ChatOpenAI(
base_url="https://ai.penzy.net/v1",
api_key="sk-pz-...",
model="anthropic/claude-sonnet-4-5",
temperature=0.3,
)
print(llm.invoke("What is RAG?").content)
emb = OpenAIEmbeddings(base_url="https://ai.penzy.net/v1", api_key="sk-pz-...",
model="openai/text-embedding-3-small")
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "openai/gpt-4o-mini",
apiKey: process.env.PENZY_API_KEY,
configuration: { baseURL: "https://ai.penzy.net/v1" },
});
const res = await llm.invoke("What is RAG?");
Vercel AI SDK
// npm install ai @ai-sdk/openai-compatible
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { streamText } from "ai";
const penzy = createOpenAICompatible({
name: "penzy",
baseURL: "https://ai.penzy.net/v1",
apiKey: process.env.PENZY_API_KEY,
includeUsage: true,
});
const result = streamText({
model: penzy("anthropic/claude-sonnet-4-5"),
prompt: "Write a haiku about Tehran",
});
for await (const text of result.textStream) process.stdout.write(text);Claude Code
Claude Code از Anthropic Messages API استفاده میکند و میتواند مستقیماً به درگاه پنزی متصل شود:
export ANTHROPIC_BASE_URL="https://ai.penzy.net"
export ANTHROPIC_AUTH_TOKEN="sk-pz-..."
# اختیاری: انتخاب مدل
export ANTHROPIC_MODEL="claude-sonnet-4-5"
claude
// ~/.claude/settings.json
{
"env": {
"ANTHROPIC_BASE_URL": "https://ai.penzy.net",
"ANTHROPIC_AUTH_TOKEN": "sk-pz-..."
}
}
کلیدی که برای Claude Code استفاده میکنید باید به مدلهای anthropic/* دسترسی داشته باشد.
Cursor، Continue و سایر ابزارها
هر ابزاری که «OpenAI-compatible endpoint» یا «Custom OpenAI base URL» را پشتیبانی کند، با درگاه پنزی کار میکند. مقادیر عمومی:
| تنظیم | مقدار |
|---|---|
| Base URL / API Base | https://ai.penzy.net/v1 |
| API Key | sk-pz-… |
| Model | یکی از شناسههای صفحه مدلها |
Continue (VS Code / JetBrains)
models:
- name: Claude via Penzy
provider: openai
model: anthropic/claude-sonnet-4-5
apiBase: https://ai.penzy.net/v1
apiKey: sk-pz-...
- name: GPT-4o via Penzy
provider: openai
model: openai/gpt-4o
apiBase: https://ai.penzy.net/v1
apiKey: sk-pz-...Cursor
در Settings → Models گزینه Override OpenAI Base URL را روی https://ai.penzy.net/v1 قرار دهید، کلید را وارد و مدل دلخواه (مثلاً openai/gpt-4o) را اضافه کنید.
Open WebUI / LibreChat / n8n
در بخش اتصال OpenAI، آدرس https://ai.penzy.net/v1 و کلید خود را وارد کنید. فهرست مدلها بهطور خودکار از /v1/models خوانده میشود.
خطاها
خطاها با کد HTTP مناسب و بدنهای سازگار با OpenAI بازگردانده میشوند (در /v1/messages با فرمت Anthropic):
{
"error": {
"message": "Budget exhausted for this key ($50.0012 of $50 – monthly).",
"type": "insufficient_quota",
"code": "budget_exceeded"
}
}| HTTP | code | معنا و راهحل |
|---|---|---|
| 400 | invalid_request_error | بدنه نامعتبر یا پارامتر ناقص. پیام خطا را بررسی کنید. |
| 401 | invalid_api_key | کلید ارسال نشده یا نامعتبر است. |
| 402 | budget_exceeded | بودجه کلید در دوره جاری تمام شده است. |
| 403 | key_disabled، key_expired، model_not_allowed، model_disabled | کلید غیرفعال/منقضی است یا اجازه استفاده از این مدل را ندارد. |
| 404 | model_not_found | مدل شناختهشده نیست. شناسه را از /v1/models بردارید. |
| 429 | rate_limit_exceeded | از سقف درخواست در دقیقه عبور کردهاید؛ با تأخیر نمایی دوباره تلاش کنید. ممکن است از سمت ارائهدهنده نیز صادر شود. |
| 502 | upstream_error | خطا در ارتباط با ارائهدهنده (پیام با نام ارائهدهنده شروع میشود). |
| 503 | provider_unavailable | ارائهدهنده در این درگاه فعال نیست. |
هر پاسخ هدر x-request-id دارد؛ هنگام گزارش مشکل آن را برای پشتیبانی ارسال کنید.
محدودیتها و بودجه
- بودجه: هزینه هر درخواست بر اساس توکنهای ورودی/خروجی و قیمت مدل (صفحه مدلها) محاسبه و از بودجه کلید کسر میشود. دورههای روزانه و ماهانه بر اساس زمان UTC بازنشانی میشوند.
- RPM: در صورت تعیین، درخواستهای بیش از سقف در یک پنجره ۶۰ ثانیهای با 429 رد میشوند.
- حجم درخواست: حداکثر ۵۰ مگابایت (برای تصاویر base64 کافی است).
- زمان پاسخ: حداکثر ۱۰ دقیقه برای هر درخواست؛ برای پاسخهای طولانی از استریم استفاده کنید.
بهترین روشها
- برای پاسخهای طولانی همیشه از استریم استفاده کنید تا تجربه کاربر بهتر و احتمال timeout کمتر شود.
- برای خطاهای
429و5xxتلاش مجدد با exponential backoff پیاده کنید (SDKهای رسمی این کار را خودکار انجام میدهند). - برای هر سرویس یا محیط (dev/prod) کلید جداگانه بگیرید تا مصرف قابل ردیابی و کلید قابل ابطال باشد.
- برای کارهای ساده از مدلهای سبکتر (
gpt-4o-mini،claude-haiku،gemini-flash) استفاده کنید؛ هزینه تا ده برابر کمتر است. - هرگز کلید را در فرانتاند قرار ندهید؛ درخواستها را از طریق backend خود ارسال کنید.
سؤالات متداول
آیا دادههای من ذخیره میشود؟
درگاه محتوای پیامها را ذخیره نمیکند؛ تنها متادیتای مصرف (مدل، تعداد توکن، هزینه، زمان و وضعیت) برای گزارشگیری ثبت میشود. سیاست نگهداری داده هر ارائهدهنده بر درخواستها اعمال میشود.
چرا نام مدل Claude در پاسخ با نام درخواستی متفاوت است؟
نامهای بدون تاریخ (مثل claude-sonnet-4-5) نام مستعار آخرین نسخه هستند و ممکن است ارائهدهنده نسخه دقیق را برگرداند.
تفاوت anthropic/… و openrouter/anthropic/… چیست؟
اولی مستقیماً به API رسمی Anthropic میرود؛ دومی از طریق OpenRouter. هر دو مدل یکسانی هستند ولی قیمت، محدودیتها و قابلیتهای جانبی ممکن است اندکی متفاوت باشد.
کلید جدید یا افزایش بودجه میخواهم.
با مدیر سامانه تماس بگیرید. کلیدها تنها از پنل مدیریت صادر میشوند.