SDK پایتون — نصب، استریم و tool calling
پکیج رسمی openai در پایتون با یوتاپن: نصب، ساخت کلاینت، chat، استریم، tool calling، embeddings و مدیریت خطا با کد قابل اجرا.
بهروزرسانی: ۱۶ شهریور ۱۴۰۵
پکیج رسمی openai (نسخهٔ ۱ به بالا) بدون هیچ patch یا wrapper با یوتاپن کار میکند. همهٔ نمونههای این صفحه علیه gateway اجرا و تأیید شدهاند.
نصب و کلاینت
pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.uttapen.ir/v1",
api_key=os.environ["UTTAPEN_API_KEY"], # sk-up-...
)
اگر ترجیح میدهی کد را دست نزنی، فقط متغیرهای محیطی OPENAI_BASE_URL و OPENAI_API_KEY را ست کن؛ OpenAI() بدون آرگومان آنها را میخواند. گزینههای مفید سازنده: timeout (پیشفرض ۱۰ دقیقه)، max_retries (پیشفرض ۲؛ روی 429 و 5xx)، و default_headers برای فرستادن همیشگی X-Uttapen-Include-Meta.
Chat
resp = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[
{"role": "system", "content": "پاسخ کوتاه و فارسی بده."},
{"role": "user", "content": "تفاوت index و unique index در Postgres چیست؟"},
],
max_tokens=300,
temperature=0.3,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
برای هدرهای هزینه، نسخهٔ raw را بگیر:
raw = client.chat.completions.with_raw_response.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "سلام"}],
)
print(raw.headers["x-uttapen-cost-toman"], raw.headers["x-uttapen-balance-toman"])
resp = raw.parse() # همان آبجکت ChatCompletion
استریم
stream = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "یک تابع Python برای اعتبارسنجی کد ملی بنویس."}],
stream=True,
extra_headers={"X-Uttapen-Include-Meta": "1"},
)
for chunk in stream:
if chunk.object == "uttapen.meta":
print(f"\n[هزینه: {chunk.model_extra['cost_toman']} تومان، موجودی: {chunk.model_extra['balance_toman']}]")
continue
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
رویداد uttapen.meta بدون choices است؛ همیشه قبل از خواندن chunk.choices[0] وجودش را چک کن. برای لغو، stream.close() را صدا بزن یا از with client.chat.completions.create(...) as stream: استفاده کن تا اتصال با خروج از بلوک بسته شود. جزئیات فرمت در استریم.
Tool calling
import json
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "دمای فعلی یک شهر",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
},
}]
def get_weather(city: str) -> dict:
return {"city": city, "temp_c": 31}
messages = [{"role": "user", "content": "هوای تهران چطور است؟"}]
resp = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages, tools=tools)
msg = resp.choices[0].message
messages.append(msg)
for call in msg.tool_calls or []:
result = get_weather(**json.loads(call.function.arguments))
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result, ensure_ascii=False)})
final = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages, tools=tools)
print(final.choices[0].message.content)
حلقهٔ کامل با چند ابزار و استریم در Tool calling.
Embeddings
emb = client.embeddings.create(
model="openai/text-embedding-3-small",
input=["قرارداد اجاره", "اجارهنامهٔ ملک مسکونی", "دستور پخت قورمهسبزی"],
)
vectors = [d.embedding for d in emb.data]
print(len(vectors), len(vectors[0]), emb.usage.prompt_tokens)
input میتواند رشته یا فهرست رشته باشد؛ برای batch بزرگ، فهرست بفرست تا یک درخواست شمرده شود. شناسهٔ مدل embeddings را از صفحهٔ مدلها بردار.
Async
import asyncio
from openai import AsyncOpenAI
aclient = AsyncOpenAI(base_url="https://api.uttapen.ir/v1", api_key=os.environ["UTTAPEN_API_KEY"])
async def ask(q: str) -> str:
r = await aclient.chat.completions.create(model="openai/gpt-5-nano", messages=[{"role": "user", "content": q}], max_tokens=100)
return r.choices[0].message.content
async def main():
answers = await asyncio.gather(*(ask(q) for q in ["۱+۱؟", "پایتخت فرانسه؟", "رنگ آسمان؟"]))
print(answers)
asyncio.run(main())
با gather روی صدها درخواست، سقف دقیقهای کلید را میزنی؛ با asyncio.Semaphore همزمانی را محدود کن (محدودیت نرخ).
مدیریت خطا
import time
import openai
try:
resp = client.chat.completions.create(model="openai/gpt-5-mini", messages=messages)
except openai.AuthenticationError:
raise SystemExit("کلید uttapen نامعتبر یا لغوشده است")
except openai.NotFoundError as e:
print("مدل یافت نشد:", e.message)
except openai.RateLimitError as e:
time.sleep(int(e.response.headers.get("retry-after", "5")))
except openai.APIStatusError as e:
if e.status_code == 402:
info = e.body["error"].get("uttapen", {})
print("شارژ لازم:", info.get("required_toman"), "تومان", info.get("topup_url"))
else:
print(e.status_code, e.code, e.message)
except openai.APIConnectionError:
print("اتصال برقرار نشد؛ شبکه را چک کن")
e.code همان error.code است و e.response.headers["x-uttapen-request-id"] شناسهای است که در گزارش خطا به ما میدهی. جدول کامل در خطاها.
فیلدهای اضافی
هر فیلدی که SDK نمیشناسد (مثل reasoning، provider، models) را با extra_body بفرست:
resp = client.chat.completions.create(
model="deepseek/deepseek-r1",
messages=messages,
extra_body={"reasoning": {"effort": "low"}, "models": ["openai/o3-mini"]},
)
استفاده در سرور (FastAPI و Django)
کلاینت را یک بار در سطح ماژول بساز و در همهٔ درخواستها همان را استفاده کن؛ OpenAI() یک connection pool از httpx دارد و ساختنش بهازای هر درخواست، هم کند است هم اتصالهای نیمهباز میگذارد. در FastAPI از AsyncOpenAI استفاده کن تا حلقهٔ رویداد بلوکه نشود؛ در Django همزمان (OpenAI) کافی است مگر با ASGI کار کنی. برای درخواستهایی که چند دقیقه طول میکشند (مدلهای استدلالی، سند بلند)، کار را به یک worker (Celery، RQ، Dramatiq) بده و نتیجه را با شناسهٔ درخواست ذخیره کن؛ نگه داشتن اتصال HTTP کاربر برای دو دقیقه ایدهٔ خوبی نیست.
# core/llm.py — یک نمونه برای کل پروژه
import httpx
from openai import OpenAI
client = OpenAI(
base_url="https://api.uttapen.ir/v1",
api_key=os.environ["UTTAPEN_API_KEY"],
timeout=httpx.Timeout(120.0, connect=10.0),
max_retries=1,
)
X-Uttapen-Request-Id را کنار لاگ خودت بنویس؛ با with_raw_response یا از e.response.headers در خطاها در دسترس است. اگر سرور پشت proxy خروجی است، http_client=httpx.Client(proxy="http://...") را به سازنده بده.
اشتباههای رایج
OPENAI_API_KEYقدیمی در محیط. اگرapi_keyرا صریح ندهی و متغیر محیطی کلید OpenAI را داشته باشد، SDK آن را برمیدارد و401میگیری. همیشهapi_keyرا صریح ازUTTAPEN_API_KEYبده.- تکرار دوبرابر. SDK خودش
429و5xxرا تکرار میکند. اگر لایهٔ تکرار خودت را داری،max_retries=0بگذار وگرنه یک خطای گذرا ۹ بار زده میشود. - شمارش توکن با tiktoken. برای مدلهای غیر-OpenAI فقط تقریب است؛ عدد واقعی در
usageپاسخ است. - نسخهٔ پکیج.
openai>=1.0لازم است؛ نسخهٔ ۰٫x قرارداد دیگری داشت. درrequirements.txtنسخه را pin کن. - خروجی فارسی در ویندوز. اگر کنسول متن را خراب نشان میدهد،
PYTHONIOENCODING=utf-8بگذار؛ به API ربطی ندارد.
Responses API
client.responses.create(...) فعلاً 404 میگیرد. از chat.completions استفاده کن؛ همهٔ قابلیتها (tools، تصویر، JSON، reasoning) آنجا هست (migration).