uttapen

یکپارچه‌سازی: LangChain، LlamaIndex، Cursor

تنظیم LangChain، LlamaIndex، Vercel AI SDK، Cursor، Continue و Open WebUI برای کار با یوتاپن؛ فقط base URL و کلید، بدون تغییر کد.

به‌روزرسانی: ۱۶ شهریور ۱۴۰۵

هر ابزاری که «OpenAI-compatible» است با uttapen کار می‌کند: آدرس https://api.uttapen.ir/v1، کلید sk-up-… و شناسهٔ مدل به شکل provider/model. تنظیمات LangChain، LlamaIndex و Vercel AI SDK در این صفحه علیه gateway اجرا و تأیید شده‌اند. برای Cursor، Continue و Open WebUI نام دقیق فیلدها از حافظه و مستندات آن ابزارهاست و ممکن است در نسخهٔ تو کمی فرق کند؛ جایی که مطمئن نیستیم گفته‌ایم.

LangChain (Python)

pip install langchain-openai
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

llm = ChatOpenAI(
    model="openai/gpt-5-mini",
    base_url="https://api.uttapen.ir/v1",
    api_key="sk-up-...",
    max_tokens=500,
)
print(llm.invoke("سه مزیت Postgres را بگو.").content)

for chunk in llm.stream("یک جملهٔ انگیزشی کوتاه"):
    print(chunk.content, end="", flush=True)

emb = OpenAIEmbeddings(
    model="openai/text-embedding-3-small",
    base_url="https://api.uttapen.ir/v1",
    api_key="sk-up-...",
    check_embedding_ctx_length=False,
)
vec = emb.embed_query("قرارداد اجاره")

check_embedding_ctx_length=False لازم است، چون LangChain به‌صورت پیش‌فرض متن را با tokenizer لوکال OpenAI می‌شکند و برای مدل‌های غیر-OpenAI درست کار نمی‌کند. Tool calling با llm.bind_tools([...]) و خروجی ساخت‌یافته با llm.with_structured_output(Schema) همان‌طور که با OpenAI کار می‌کند، اینجا هم کار می‌کند. در LangChain.js همین فیلدها روی new ChatOpenAI({ model, apiKey, configuration: { baseURL } }) هستند.

LlamaIndex (Python)

pip install llama-index-llms-openai-like llama-index-embeddings-openai
from llama_index.llms.openai_like import OpenAILike
from llama_index.core.llms import ChatMessage

llm = OpenAILike(
    model="openai/gpt-5-mini",
    api_base="https://api.uttapen.ir/v1",
    api_key="sk-up-...",
    is_chat_model=True,
    is_function_calling_model=True,
    context_window=128000,
)
print(llm.complete("سه مزیت Postgres را بگو.").text)
for chunk in llm.stream_chat([ChatMessage(role="user", content="سلام")]):
    print(chunk.delta, end="", flush=True)

از OpenAILike استفاده کن نه OpenAI؛ کلاس دوم شناسهٔ مدل را با فهرست ثابت مدل‌های OpenAI چک می‌کند و openai/… را رد می‌کند. is_chat_model=True الزامی است. برای embeddings، OpenAIEmbedding(model_name=..., api_base=..., api_key=...) از پکیج llama-index-embeddings-openai کار می‌کند؛ اگر نام مدل را رد کرد، از OpenAILikeEmbedding در llama-index-embeddings-openai-like استفاده کن.

Vercel AI SDK (Node و Next.js)

npm install ai @ai-sdk/openai-compatible
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText, streamText, embed } from "ai";

const uttapen = createOpenAICompatible({
  name: "uttapen",
  baseURL: "https://api.uttapen.ir/v1",
  apiKey: process.env.UTTAPEN_API_KEY,
  includeUsage: true,
});

const { text, usage } = await generateText({
  model: uttapen("openai/gpt-5-mini"),
  prompt: "سه مزیت Postgres را بگو.",
  maxOutputTokens: 300,
});

const result = streamText({ model: uttapen("anthropic/claude-sonnet-4.5"), prompt: "سلام" });
for await (const delta of result.textStream) process.stdout.write(delta);

const { embedding } = await embed({
  model: uttapen.embeddingModel("openai/text-embedding-3-small"),
  value: "قرارداد اجاره",
});

در route handler Next.js، result.toUIMessageStreamResponse() (یا toTextStreamResponse()) را برگردان و در کلاینت از useChat استفاده کن؛ کلید فقط در سرور می‌ماند. برای reasoning و فیلدهای اختصاصی از providerOptions: { uttapen: { reasoning: { effort: "low" } } } استفاده کن. @ai-sdk/openai (پکیج اختصاصی OpenAI) هم با baseURL کار می‌کند ولی بعضی قابلیت‌هایش (Responses API) با یوتاپن جواب نمی‌دهد؛ openai-compatible انتخاب امن است.

Cursor

در Cursor Settings ← Models:

  1. کلید OpenAI را با sk-up-… جایگزین کن.
  2. گزینهٔ Override OpenAI Base URL را روشن کن و https://api.uttapen.ir/v1 بگذار.
  3. با «Add model» شناسهٔ مدل را دقیقاً وارد کن: anthropic/claude-sonnet-4.5، openai/gpt-5، deepseek/deepseek-chat.
  4. مدل‌های داخلی Cursor را خاموش کن تا اشتباهی به سرور Cursor نروند.

با Base URL سفارشی، Cursor بعضی قابلیت‌های داخلی خودش (Tab completion، Composer با مدل‌های اختصاصی) را غیرفعال می‌کند؛ این محدودیت Cursor است. نام دقیق گزینه‌ها بین نسخه‌ها عوض شده؛ اگر «Override OpenAI Base URL» را نمی‌بینی، در همان صفحه دنبال فیلد base URL زیر OpenAI API Key بگرد.

Continue (VS Code و JetBrains)

در ~/.continue/config.yaml:

models:
  - name: Claude Sonnet via uttapen
    provider: openai
    model: anthropic/claude-sonnet-4.5
    apiBase: https://api.uttapen.ir/v1
    apiKey: sk-up-...
    roles: [chat, edit]
  - name: GPT-5 mini via uttapen
    provider: openai
    model: openai/gpt-5-mini
    apiBase: https://api.uttapen.ir/v1
    apiKey: sk-up-...
    roles: [chat, autocomplete]
  - name: Embeddings via uttapen
    provider: openai
    model: openai/text-embedding-3-small
    apiBase: https://api.uttapen.ir/v1
    apiKey: sk-up-...
    roles: [embed]

provider: openai با apiBase سفارشی درست است. اگر هنوز config.json قدیمی داری، همین فیلدها با نام apiBase در آرایهٔ models هستند. نام فیلد roles و مقادیرش را در مستندات نسخهٔ خودت چک کن؛ در نسخه‌های قدیمی tabAutocompleteModel و embeddingsProvider جدا بودند.

Open WebUI

در Admin Panel ← Settings ← Connections، زیر OpenAI API:

  • API Base URL: https://api.uttapen.ir/v1
  • API Key: sk-up-...

بعد از ذخیره، Open WebUI فهرست مدل‌ها را از GET /v1/models می‌گیرد و همهٔ ۴۰۰+ مدل در منوی انتخاب ظاهر می‌شوند؛ می‌توانی در Settings ← Models فقط چندتا را فعال بگذاری. اگر با Docker اجرا می‌کنی، همین دو مقدار را با متغیرهای OPENAI_API_BASE_URL و OPENAI_API_KEY هم می‌توانی بدهی. Open WebUI برای عنوان‌گذاری و جستجوی گفتگو درخواست‌های اضافه می‌فرستد؛ در Settings ← Interface یک مدل ارزان (مثل openai/gpt-5-nano) برای «Task model» انتخاب کن تا هزینهٔ پنهان نداشته باشی.

سایر ابزارها

هر ابزاری که «custom OpenAI endpoint» می‌پذیرد با همان سه مقدار کار می‌کند: aider (--openai-api-base https://api.uttapen.ir/v1 --model openai/anthropic/claude-sonnet-4.5 — پیشوند openai/ اول را خود aider برای endpoint سفارشی لازم دارد و بعدش شناسهٔ کامل مدل می‌آید)، Cline، Zed، LibreChat، n8n (credential از نوع OpenAI با Base URL)، Dify، Flowise. قاعده‌های مشترک:

  • اگر ابزار مسیر /v1 را خودش اضافه می‌کند و 404 می‌گیری، آدرس را بدون /v1 بده. با curl https://api.uttapen.ir/v1/models می‌توانی سریع بفهمی کدام شکل درست است.
  • اگر ابزار فهرست مدل را از GET /v1/models می‌گیرد، همه چیز خودکار است؛ اگر نه، شناسه را دقیق و با حروف کوچک وارد کن.
  • ابزارهایی که از Responses API استفاده می‌کنند (بعضی نسخه‌های جدید Codex CLI) فعلاً کار نمی‌کنند؛ حالت chat completions را در تنظیماتشان انتخاب کن.
  • برای هر ابزار یک کلید جدا با سقف ماهانه بساز تا مصرف هر کدام در group_by=key جدا دیده شود (کلیدها).