SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-10-30

কিভাবে নিজের সার্ভারে LiteLLM গেটওয়ে সেটআপ করবেন

আপনার সব LLM প্রোভাইডারের জন্য একটি OpenAI compatible endpoint তৈরি করুন। LiteLLM ব্যবহার করে ভার্চুয়াল কি, বাজেট কন্ট্রোল এবং ফলব্যাক ফিচারসহ আপনার নিজস্ব VPS-এ এটি হোস্ট করুন।

একটি self-hosted LLM gateway যা করে

LiteLLM হলো একটি ওপেন সোর্স LLM gateway যা আপনি নিজেই হোস্ট করেন: এটি একটি HTTP endpoint যা আপনার সমস্ত অ্যাপ্লিকেশন কল করে এবং এটি প্রতিটি অনুরোধকে সংশ্লিষ্ট প্রোভাইডারের কাছে পাঠিয়ে দেয়। LLM মানে হলো large language model। এই gateway-টি OpenAI chat completions API (application programming interface) সমর্থন করে, তাই যে কোনো client library যা আগে থেকেই OpenAI-এর সাথে কাজ করে, তা দুটি পরিবর্তনের পরেই এর সাথে কাজ করবে: base URL এবং key।

এই একটি বাড়তি স্তর বা indirection-ই হলো মূল বিষয়। আপনার অ্যাপ্লিকেশনগুলোতে আর প্রোভাইডারের credentials রাখার প্রয়োজন হয় না। একটি মডেল পরিবর্তন করতে হলে পাঁচটি সার্ভিসের কোড পরিবর্তন না করে, সার্ভারের কনফিগারেশন ফাইলে মাত্র এক লাইন পরিবর্তন করলেই হয়। এবং যেহেতু প্রতিটি কল একটি প্রসেসের মধ্য দিয়ে যায়, তাই আপনার কাছে বাজেট নির্ধারণ এবং খরচের হিসাব রাখার একটি কেন্দ্রীয় জায়গা থাকে।

এটি চালু করার পর আপনি যা যা পাবেন:

  • একটি endpoint। অ্যাপ্লিকেশনগুলো https://gateway.example.com/v1-কে লক্ষ্য করে এবং আপনার তৈরি করা কোনো মডেলের নাম, যেমন bulk বা strong-এর জন্য অনুরোধ জানায়।
  • Virtual keys। প্রতিটি অ্যাপ্লিকেশন তার নিজস্ব key পায়, যার সাথে নিজস্ব model allowlist এবং spend ceiling যুক্ত থাকে। আপনি অন্যদের কোনো ক্ষতি না করেই একটি key বাতিল করতে পারেন।
  • Fallbacks। কোনো কল ব্যর্থ হলে বা prompt অনেক বড় হলে তা স্বয়ংক্রিয়ভাবে অন্য একটি মডেলের মাধ্যমে পুনরায় চেষ্টা করা হয়।
  • লগ করা রেকর্ড। প্রতিটি অনুরোধের সাথে খরচের হিসাবসহ একটি রেকর্ড সংরক্ষিত হয়, ফলে "কোন অ্যাপ কত খরচ করেছে" তার উত্তর সহজেই পাওয়া যায়।

কেন নিজে গেটওয়ে পরিচালনা করবেন

একটি ম্যানেজড রাউটার দেখতে একই রকম হলেও, প্রতিটি অনুরোধের মাঝে অন্য কারো প্রসেস কাজ করে। নিজে এটি পরিচালনা করলে আপনার প্রোভাইডার কি (keys) এবং প্রম্পট টেক্সট আপনার নিয়ন্ত্রণে থাকা বক্সে সংরক্ষিত থাকে। এর একটি বাস্তব খরচ আছে: এখন আপনি এমন একটি কম্পোনেন্ট পরিচালনা করছেন যার ওপর প্রতিটি অ্যাপ্লিকেশন নির্ভরশীল। এই গাইডের শেষ অংশে সেই খরচ সম্পর্কে আলোচনা করা হয়েছে, কারণ বেশিরভাগ লেখায় এই গুরুত্বপূর্ণ অংশটি বাদ পড়ে যায়।

আপনার যা প্রয়োজন

  • Ubuntu 24.04 চালিত একটি VPS (virtual private server), যেখানে Docker এবং Compose plugin ইনস্টল করা আছে।
  • একটি domain name যা সার্ভারের IP-কে নির্দেশ করে (যদি বাইরের কোনো মেশিন TLS (transport layer security)-এর মাধ্যমে gateway-তে যুক্ত হতে চায়)।
  • অন্তত একটি provider API key।

Gateway-টি কোনো inference চালায় না। এটি অনুরোধগুলো forward করে এবং উত্তরগুলো stream করে ফেরত পাঠায়, তাই এর CPU লোড মডেলের আকারের পরিবর্তে অনুরোধের পরিমাণের ওপর নির্ভর করে। একটি 1 vCPU-এর সার্ভার কোনো সমস্যা ছাড়াই বেশ কিছু অভ্যন্তরীণ অ্যাপ্লিকেশন চালাতে পারে। যা বৃদ্ধি পায় তা হলো database, কারণ gateway প্রতিটি অনুরোধের জন্য একটি করে spend row লিখে রাখে।

প্রথমে config.yaml লিখুন

config ফাইলটি নির্ধারণ করে যে ক্লায়েন্ট কোন মডেলগুলোর জন্য অনুরোধ করতে পারবে। চারটি শীর্ষ-স্তরের সেকশন গুরুত্বপূর্ণ: model_list, litellm_settings, router_settings এবং general_settings।

model_list:
  - model_name: bulk
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  num_retries: 2
  request_timeout: 120
  json_logs: true
  set_verbose: false

router_settings:
  allowed_fails: 3
  cooldown_time: 30
  fallbacks: [{"bulk": ["strong"]}]
  context_window_fallbacks: [{"bulk": ["strong"]}]

general_settings:
  background_health_checks: true
  health_check_interval: 300

model_name হলো সেই নাম যা আপনার ক্লায়েন্টরা পাঠায়। litellm_params.model হলো আসল মডেল, যা provider/model হিসেবে লেখা হয়। মডেলের নাম ভেন্ডরের নামের পরিবর্তে কাজের ধরন অনুযায়ী দিন। যে অ্যাপ্লিকেশনটি bulk-এর জন্য অনুরোধ করে, সেটি কাজ চালিয়ে যেতে পারে যখন আপনি পরের মাসে সিদ্ধান্ত নেন যে bulk একটি ভিন্ন মডেল হওয়া উচিত।

api_key: os.environ/ANTHROPIC_API_KEY LiteLLM-কে রানটাইমে সেই ভেরিয়েবলটি পড়তে বলে। আক্ষরিক কী (literal key) কখনোই ফাইলে থাকে না, যা গুরুত্বপূর্ণ কারণ config.yaml হলো সেই ফাইল যা আপনি কমিট করেন।

দুটি এন্ট্রি ইচ্ছাকৃতভাবে strong নামটি শেয়ার করে। যখন একাধিক ডিপ্লয়মেন্ট একই model_name বহন করে, তখন রাউটার সেগুলোকে অদলবদলযোগ্য হিসেবে বিবেচনা করে এবং সেই নামের অনুরোধগুলোকে সেগুলোর মধ্যে ছড়িয়ে দেয়। এভাবেই strong একটি প্রোভাইডারের খারাপ সময়ের মধ্যেও টিকে থাকে: একবার একটি ডিপ্লয়মেন্ট কুলডাউনে (নিচে দেখুন) চলে গেলে, strong-এর জন্য নতুন অনুরোধগুলো অন্যটিতে চলে যায়।

num_retries: 2 একটি রিট্রাইযোগ্য ত্রুটির ক্ষেত্রে সর্বোচ্চ দুইবার পুনরায় চেষ্টা করে। সেই রিট্রাইগুলো একই মডেল গ্রুপের মধ্যে অন্য একটি উপলব্ধ ডিপ্লয়মেন্ট নির্বাচন করতে পারে: LiteLLM-এর আর্কিটেকচার ডকুমেন্টেশন, যা 2026-10-29 তারিখে যাচাই করা হয়েছে, গ্রুপ-মধ্যবর্তী রিট্রাই এবং ক্রস-গ্রুপ ফলব্যাককে আলাদা ধাপ হিসেবে বর্ণনা করে। তাই strong-এর জন্য একটি রিট্রাই ওয়েটেড ফেইলওভার সক্রিয় না করেই অন্য প্রোভাইডারে যেতে পারে। একটি ফলব্যাক শুধুমাত্র তখনই কাজ করে যখন সেই রিট্রাইগুলো শেষ হয়ে যায়।

allowed_fails এবং cooldown_time হলো রাউটার অপশন, তাই সেগুলো router_settings-এর অধীনে থাকে। প্রক্সি সেই সেকশন থেকে তার রাউটার তৈরি করে, এবং v1.95.0 ভার্সনে litellm_settings-এর অধীনে লেখা একটি cooldown_time কখনোই সেখানে পৌঁছায় না, তাই কুলডাউনগুলো নীরবে 5 সেকেন্ডের ডিফল্টে থেকে যায়। cooldown_time: 30 নির্ধারণ করে যে একটি ডিপ্লয়মেন্ট কুলডাউন হওয়ার পর কতক্ষণ রোটেশনের বাইরে থাকবে। কনফিগারেশন রেফারেন্স allowed_fails-কে সেই ফেইলর কাউন্ট হিসেবে বর্ণনা করে যা একটি ডিপ্লয়মেন্টকে কুলডাউন করার আগে cooldown_time-এর মধ্যে অতিক্রম করতে হয়। v1.95.0 সোর্সে, allowed_fails: 3 হলো ডিফল্ট, এবং ডিফল্ট অবস্থায় রাউটার সেই বিল্ট-ইন ট্রিগারগুলো মেনে চলে; ফেইলর কাউন্ট শুধুমাত্র তখনই কার্যকর হয় যখন আপনি একটি ভিন্ন সংখ্যা সেট করেন। সেই ট্রিগারগুলোর মধ্যে রয়েছে একাধিক ডিপ্লয়মেন্ট থাকা গ্রুপের জন্য 429 রেসপন্সে তাৎক্ষণিক কুলডাউন, এমন গ্রুপের জন্য অন্তত 5টি অনুরোধের পর বর্তমান মিনিটে 50%-এর বেশি ব্যর্থতা, এবং এমন ত্রুটি যা রাউটার রিট্রাই করবে না, যেমন ভুল কী-এর কারণে 401। একটি আলাদা ট্রিগার একটি একক ডিপ্লয়মেন্টকেও কুলডাউন করে যখন বর্তমান মিনিটের সমস্ত অনুরোধ ব্যর্থ হয় এবং ট্রাফিক SINGLE_DEPLOYMENT_TRAFFIC_FAILURE_THRESHOLD-এ পৌঁছায়, যার ডিফল্ট হলো 1000 অনুরোধ। একটি প্রোভাইডার যখন হঠাৎ 500 এরর দিতে থাকে, তখন প্রতিটি অনুরোধে সেটিকে চেষ্টা করা বন্ধ হয়ে যায়, যেখানে প্রতি কয়েক মিনিটে একটি বিচ্ছিন্ন 500 এরর কখনোই কুলডাউন ট্রিগার করে না।

fallbacks এবং context_window_fallbacks-এর আলাদা ট্রিগার রয়েছে, এবং দ্বিতীয়টি হলো সেই দরকারী ট্রিগার যা মানুষ এড়িয়ে যায়।

  • fallbacks কার্যকর হয় যখন প্রাথমিক কলটি ব্যর্থ হয়।
  • context_window_fallbacks কার্যকর হয় যখন প্রোভাইডার অনুরোধটিকে মডেলের কনটেক্সট উইন্ডোর চেয়ে বড় হওয়ার কারণে প্রত্যাখ্যান করে, যাতে একটি বড় প্রম্পট কলকারীকে এরর না পাঠিয়ে এমন একটি মডেলে পাঠানো হয় যেখানে সেটির জন্য জায়গা আছে।

এছাড়াও content_policy_fallbacks রয়েছে, যা কন্টেন্ট পলিসির কারণে প্রোভাইডারের প্রত্যাখ্যানের জন্য ব্যবহৃত হয়। এটি শুধুমাত্র তখনই সেট করুন যদি আপনার কাছে সেই কলগুলো পাঠানোর মতো উপযুক্ত কোনো জায়গা থাকে।

Docker Compose ব্যবহার করে VPS-এ LiteLLM ডেপ্লয় করা

তিনটি ফাইল ধারণকারী একটি ডিরেক্টরি তৈরি করুন: config.yaml, docker-compose.yml এবং .env। আপস্ট্রিম কুইকস্টার্ট latest ট্যাগটি পুল করে। এর পরিবর্তে একটি নির্দিষ্ট রিলিজ ট্যাগ ব্যবহার করুন, যাতে docker compose up -d পরবর্তী মাসে আপনাকে সেই একই গেটওয়ে দেয় যা আজ দিচ্ছে, এবং রোলব্যাক করা একটি লাইনের কাজ হয়ে দাঁড়ায়।

services:
  litellm:
    image: ghcr.io/berriai/litellm:v1.95.0
    restart: unless-stopped
    command: ["--config", "/app/config.yaml", "--num_workers", "1"]
    ports:
      - "127.0.0.1:4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    env_file: .env
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: litellm
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Compose এখানে .env ফাইলটি দুইবার পড়ে। একবার compose ফাইলের ভেতরে থাকা ${POSTGRES_PASSWORD} প্রতিস্থাপন করতে, এবং অন্যবার env_file এর মাধ্যমে প্রতিটি ভেরিয়েবল কন্টেইনারের ভেতরে পাঠাতে।

v1.95.0 ছিল 2026 সালের আগস্ট মাসের বর্তমান রিলিজ। প্রজেক্টের রিলিজ পেজ চেক করুন এবং ডেপ্লয় করার সময় যেটি বর্তমান রিলিজ সেটিই ব্যবহার করুন। প্রতিটি রিলিজ একটি সিগনেচার প্রকাশ করে, তাই ইমেজটিকে বিশ্বাস করার আগে আপনি তা যাচাই করতে পারেন:

cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0

পোর্ট লাইনটি হলো 127.0.0.1:4000:4000, যা শুধুমাত্র লুপব্যাক ইন্টারফেসে পোর্টটি পাবলিশ করে। এর পরিবর্তে 4000:4000 লিখলে আপনার গেটওয়ে পুরো ইন্টারনেট থেকে অ্যাক্সেসযোগ্য হয়ে যাবে, কারণ Docker তার নিজস্ব নিয়ম iptables FORWARD চেইনে যোগ করে এবং সেগুলো ufw-এর আগেই মূল্যায়ন করা হয়, তাই ufw deny 4000 এটিকে আটকাতে পারে না। এটি একটি সেলফ-হোস্টেড গেটওয়ে উন্মুক্ত হয়ে যাওয়ার সবচেয়ে সাধারণ উপায়: দেখুন কিভাবে Docker ufw-কে পাশ কাটিয়ে কন্টেইনার পোর্ট পাবলিশ করে। বাইরের ট্রাফিক এর পরিবর্তে রিভার্স প্রক্সির মাধ্যমে আসে।

প্রোভাইডার কি (keys) ইমেজ থেকে দূরে রাখুন

.env ফাইলটিতে সমস্ত গোপন তথ্য (secrets) থাকে। রানটাইমে এটি এনভায়রনমেন্ট ভেরিয়েবল হিসেবে পাস করা হয়, তাই এটি কখনোই ইমেজের ভেতরে বেক (baked) করা হয় না এবং এটি কখনোই কমিট করা হয় না।

LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...

প্রকৃত র‍্যান্ডমনেস ব্যবহার করে দুটি LiteLLM কি তৈরি করুন, তারপর ফাইলটিকে লক করে দিন:

printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .env

LITELLM_MASTER_KEY হলো অ্যাডমিন ক্রেডেনশিয়াল। এটি ম্যানেজমেন্ট API-কে অথেন্টিকেট করে এবং /ui-এ অবস্থিত অ্যাডমিন UI-এর পাসওয়ার্ড হিসেবে কাজ করে। কোনো অ্যাপ্লিকেশনের কাছেই এটি থাকা উচিত নয়।

LITELLM_SALT_KEY ডাটাবেসে সংরক্ষিত প্রোভাইডার ক্রেডেনশিয়ালগুলোকে এনক্রিপ্ট করে। এটি একবার সেট করুন এবং আর পরিবর্তন করবেন না। পরবর্তীতে এটি পরিবর্তন করলে আগে থেকে সংরক্ষিত ক্রেডেনশিয়ালগুলো ডিক্রিপ্ট করা সম্ভব হবে না; ফলে গেটওয়ে স্বাভাবিকভাবে চালু হলেও সেই প্রোভাইডারগুলোতে করা প্রতিটি কল অথেন্টিকেশন ব্যর্থতার কারণে আটকে যাবে।

STORE_MODEL_IN_DB=True আপনাকে config.yaml ফাইলটি স্পর্শ না করেই অ্যাডমিন UI থেকে মডেল যোগ এবং এডিট করার সুবিধা দেয়। এটি সুবিধাজনক, তবে এটি আপনার তথ্যের উৎসকে (source of truth) দুই ভাগে বিভক্ত করে ফেলে। কোনটি অথরিটেটিভ বা মূল উৎস হবে তা নির্ধারণ করুন এবং সেই সিদ্ধান্তটি কনফিগারেশনের পাশে লিখে রাখুন।

কনফিগারেশন ফাইল থেকে কি (keys) দূরে রাখার পেছনে যে যুক্তি কাজ করে, এজেন্টকে দেওয়া টুলস থেকেও সেগুলোকে দূরে রাখার পেছনে একই যুক্তি কাজ করে। AI এজেন্ট থেকে প্রোভাইডার সিক্রেট দূরে রাখা অংশে এই প্যাটার্নটি আলোচনা করা হয়েছে এবং Docker Compose-এ env ফাইল এবং সিক্রেট অংশে এর কৌশলগুলো বর্ণনা করা হয়েছে।

সার্ভিসটি চালু করুন এবং প্রথম বুট পর্যবেক্ষণ করুন:

docker compose up -d
docker compose logs -f litellm

এটি সঠিকভাবে কাজ করছে কি না তা যাচাই করুন

এখানে দুটি unauthenticated প্রোব এবং একটি authenticated প্রোব রয়েছে, এবং বিভিন্ন কারণে সেগুলো ব্যর্থ হয়।

curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness

/health/liveliness-এর জন্য কোনো auth-এর প্রয়োজন হয় না এবং প্রসেসটি চলমান থাকলে এটি "I'm alive!" প্রদান করে। /health/readiness-এর জন্যও কোনো auth লাগে না। এটি একটি JSON অবজেক্ট রিটার্ন করে যাতে "status": "healthy" এবং একটি db ফিল্ড থাকে, অথবা ডাটাবেস unreachable হলে এটি 503 এরর দেয়। আপনার মনিটরিং readiness-এর দিকে নির্দেশ করুন, কারণ liveliness এমন একটি গেটওয়েতেও green থাকে যা কোনো একটি ভার্চুয়াল কি (virtual key) খুঁজে পেতে ব্যর্থ হচ্ছে।

authenticated চেকটি প্রোভাইডারদের সাথে যোগাযোগ করে:

curl -s http://127.0.0.1:4000/health \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

এটি healthy_endpoints এবং unhealthy_endpoints অ্যারে দিয়ে উত্তর দেয়। unhealthy_endpoints-এ থাকা কোনো মডেলে authentication এরর আসার অর্থ হলো .env-এ থাকা প্রোভাইডার কি (provider key) ভুল বা অনুপস্থিত, যা সেই ব্যর্থতা যা আপনি এখন খুঁজে বের করতে চাচ্ছেন। যেহেতু background_health_checks: true সেট করা আছে, তাই প্রক্সি নিজেই প্রতি health_check_interval সেকেন্ড পরপর এই প্রোবগুলো চালায় এবং /health সর্বশেষ ফলাফলটি রিটার্ন করে, ফলে এটি পোল (poll) করলে প্রতিবার আপনার প্রোভাইডারদের কাছে কোনো টেস্ট রিকোয়েস্ট যায় না।

Virtual key এবং প্রতি-key budget

প্রতিটি application-এর নিজস্ব key থাকে। এই key master key ব্যবহার করে তৈরি করা হয়।

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "key_alias": "nightly-summariser",
    "models": ["bulk"],
    "max_budget": 5,
    "budget_duration": "30d",
    "rpm_limit": 60,
    "tpm_limit": 200000
  }'

Response-এ key field থাকে, যার শুরু sk- দিয়ে। এই string-টিই application পায়, এবং application আর অন্য কিছু পায় না।

  • models হলো এই key কী কী request করতে পারবে তার allowlist। উপরের key-টি শুধু bulk চাইতে পারে, অন্য কিছু নয়।
  • max_budget: 5-এর সঙ্গে budget_duration: "30d" প্রতি calendar month-এ পাঁচ US dollar। LiteLLM প্রতি মাসের 1 তারিখে midnight-এ 30d budget reset করে। UTC ব্যবহার করা হয়, যদি না আপনি litellm_settings-এর অধীনে timezone সেট করেন। এটি rolling 30 days অনুযায়ী reset হয় না। তাই key-টি পাঁচ dollar খরচ করলে পরবর্তী 1 তারিখ পর্যন্ত কাজ বন্ধ থাকে।
  • rpm_limit এবং tpm_limit এই key-এর জন্য একাই প্রতি মিনিটে request এবং token-এর সীমা নির্ধারণ করে।
  • key_alias হলো ছয় সপ্তাহ পর spend log-এ আপনি যে নামটি চিনতে পারবেন। এটি সবসময় সেট করুন।

Budget শেষ হয়ে গেলে call-টি budget_exceeded ধরনের error এবং নিচের মতো একটি message দিয়ে ব্যর্থ হয়:

Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget>

Status code release অনুযায়ী পরিবর্তিত হয়। নির্দিষ্ট v1.95.0-এ এটি HTTP 429। v1.104.0 থেকে default হলো HTTP 422। litellm_settings-এর অধীনে budget_exceeded_status_code: 429 সেট করলে আগের code ফিরে আসে। প্রকৃত rpm_limit এবং tpm_limit hit হলেও উভয় release-এ 429 ফেরত আসে। প্রতিটি upgrade-এর পরে কোনটি পাচ্ছেন তা পরীক্ষা করুন।

Status code-ই এই বিষয়টি বিভ্রান্তিকর করে তোলে, কারণ কোনো code-ই "budget" বলে না। কোনো client library 429-কে rate limit হিসেবে জানায় এবং backoff ব্যবহার করে retry করতে পারে, যদিও budget reset না হওয়া পর্যন্ত তা ফিরে আসবে না। আবার এটি 422-কে এমন request হিসেবে জানায় যা process করা যায়নি। Status code-এর পাশে response body log করুন। তা না হলে প্রতিবার budget শেষ হওয়াকে অন্য কোনো সমস্যার মতো দেখাবে।

একই management API ব্যবহার করে key পরীক্ষা ও পরিবর্তন করুন:

curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

curl -s -X POST http://127.0.0.1:4000/key/update \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key": "sk-...", "max_budget": 25}'

Gateway-এ প্রয়োগ করা budget তখনও কার্যকর থাকে, যখন সমস্যাটি agent নিজেই তৈরি করেছে। এই কারণেই এটি VPS-এ AI agent-এর খরচ নিয়ন্ত্রণের মূল ভিত্তি।

সাশ্রয়ী মডেলের মাধ্যমে বাল্ক কাজ সম্পন্ন করা

একটি ক্লায়েন্টকে গেটওয়ের দিকে নির্দেশ করুন। Base URL, key এবং model name ব্যবহার করুন:

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "bulk",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

যেকোনো OpenAI client library একইভাবে কাজ করে: base_url-কে https://gateway.example.com/v1 এবং api_key-কে virtual key হিসেবে সেট করুন।

এখন caller-এর অজান্তেই config.yaml-এ থাকা routing policy কার্যকর হবে। bulk-এর জন্য করা একটি অনুরোধ সাশ্রয়ী মডেলে চলে যাবে। যদি সেই কলটি তার retries-এর পরেও ব্যর্থ হয়, তবে অনুরোধটি strong-এর বিপরীতে পুনরায় চেষ্টা করা হবে। যদি prompt-টি bulk-এর জন্য খুব দীর্ঘ হয়, তবে context_window_fallbacks কোনো error না পাঠিয়ে সেটিকে strong-এ পাঠিয়ে দেবে। classification pass বা summarising backlog-এর মতো বাল্ক কাজগুলো ডিফল্টভাবে সাশ্রয়ী মডেলে চলে, এবং শুধুমাত্র জটিল অনুরোধগুলোর ক্ষেত্রেই বেশি খরচ হয়।

টুল ব্যবহারকারী এজেন্টদের ক্ষেত্রে গেটওয়ে এখানেই তার উপযোগিতা প্রমাণ করে। একটি একই VPS-এ থাকা MCP (model context protocol) server এবং এটিকে নিয়ন্ত্রণকারী এজেন্ট—উভয়ই একটি endpoint-এর দিকে নির্দেশ করতে পারে, ফলে কোনো কিছু redeploy না করেই তাদের পেছনের মডেল পরিবর্তন করা সম্ভব।

কীভাবে বুঝবেন যে fallback ঘটেছে?

এটি এমন একটি failure mode, যার কারণে খরচ বেড়ে যায়, কারণ বাইরে থেকে কিছুই নষ্ট মনে হয় না। সফল fallback-এর ফলে HTTP 200 এবং সাধারণ response body ফেরত আসে। আপনার সস্তা model এক দিন offline থাকতে পারে, প্রতিটি call নীরবে ব্যয়বহুল model দিয়ে পরিবেশিত হতে পারে, আর প্রথম প্রমাণটি দেখা যেতে পারে invoice-এ।

প্রমাণ response header-এ থাকে। সেগুলো চাইুন:

curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
  | grep -i '^x-litellm'
  • x-litellm-model-group হলো যে model-এর অধীনে request route করা হয়েছে তার নাম, যেমন bulk। x-litellm-model-id হলো যে deployment উত্তর দিয়েছে তার ID; /v1/model/info?litellm_model_id=... ব্যবহার করে সেটি খুঁজে দেখতে পারেন। ওই ID যদি আপনার bulk deployment-গুলোর কোনোটি না হয়, তাহলে fallback ঘটেছে।
  • x-litellm-attempted-fallbacks এবং x-litellm-attempted-retries এগুলোর সংখ্যা গণনা করে। সুস্থ call-এ কোনোটিই 0-এর বেশি হওয়ার কথা নয়।
  • x-litellm-response-cost হলো ওই একটি call-এর US dollar-এ খরচ।
  • x-litellm-call-id হলো সেই identifier, যা ব্যবহার করে আপনার logs-এ একই call খুঁজে পাবেন।

প্রতিটি request-এ x-litellm-attempted-fallbacks record করুন এবং এটি 0-এর বেশি হলেই alert দিন। এই একটি সংখ্যাই কার্যকর routing policy এবং নীরবে "সব সময় ব্যয়বহুল model ব্যবহার করুন"-এ পরিণত হওয়া routing policy-এর মধ্যে পার্থক্য তৈরি করে।

এর পূর্ণাঙ্গ সংস্করণ হলো tracing। এর জন্য আলাদা setup প্রয়োজন: agent call tracing-এর জন্য self-hosted Langfuse। LiteLLM callback সরবরাহ করে, তাই credentials-সহ এটি সংযুক্ত করতে দুই লাইনই যথেষ্ট।

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.com

Langfuse v3 এবং পরবর্তী সংস্করণের জন্য LiteLLM-এর Langfuse page এখন তার langfuse_otel preset ব্যবহারের সুপারিশ করে; উপরের langfuse callback-টিও এখনও documented আছে। failure_callback-এর পাশাপাশি success_callback-ও সেট করুন। এটি বাদ দিলে শুধু সেই trace-গুলোই সংরক্ষিত হবে, যেখানে কোনো সমস্যা ঘটেনি। এর বাইরে, LiteLLM প্রতিটি request-এর জন্য Postgres-এ একটি spend row লেখে এবং /ui-এর Admin UI ওই table পড়ে। Traffic বাড়লে table-টি বড় হয়, তাই ছোট disk-এ এটি monitor করুন।

গেটওয়েকে একটি reverse proxy-এর পেছনে রাখুন

বক্সের বাইরের কোনো কিছু যেন সরাসরি port 4000-এ পৌঁছাতে না পারে। nginx বা Caddy-তে TLS termination সম্পন্ন করুন এবং অনুরোধগুলো loopback address-এ ফরোয়ার্ড করুন।

location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 600s;
}

এই লাইনগুলোর মধ্যে দুটি লাইন মানুষ প্রায়ই বাদ দিয়ে দেয়। proxy_buffering off গুরুত্বপূর্ণ কারণ একটি streaming completion হলো server-sent events-এর একটি সিরিজ, এবং buffering চালু থাকলে nginx ডেটা ফরোয়ার্ড করার আগে তা বাফারে জমা করে, যা ক্লায়েন্টের কাছে ডেটা পৌঁছাতে দেরি করায়। proxy_read_timeout 600s গুরুত্বপূর্ণ কারণ nginx-এর ডিফল্ট 60 সেকেন্ডের সময়সীমা হলো গেটওয়ে থেকে দুটি রিডের মধ্যবর্তী সর্বোচ্চ অপেক্ষার সময়, পুরো রেসপন্সের জন্য কোনো সময়সীমা নয়। একটি non-streamed completion জেনারেশন শেষ না হওয়া পর্যন্ত কোনো ডেটা পাঠায় না, তাই দীর্ঘ জেনারেশনের ক্ষেত্রে 60 সেকেন্ডের মধ্যে কোনো বাইট না পৌঁছালে ক্লায়েন্ট একটি 504 error পায় এবং error log-এ upstream timed out (110: Connection timed out) while reading response header from upstream রেকর্ড হয়। একটি streamed completion যা নিয়মিত chunks পাঠায়, তা 60 সেকেন্ডের বেশি সময় ধরে চলতে পারে; এটি কেবল তখনই timeout হয় যদি মডেলটি সেই সময়ের জন্য কোনো ডেটা না পাঠায়।

সার্টিফিকেটের জন্য, nginx-এ Let's Encrypt সহ Certbot ব্যবহার করা সবচেয়ে সহজ উপায়। যদি সার্ভারে ইতিমধ্যে একাধিক কন্টেইনার চলমান থাকে, তবে একাধিক Compose অ্যাপের সামনে Traefik ব্যবহার করলে তা রাউটিং এবং সার্টিফিকেট ব্যবস্থাপনা এক জায়গা থেকেই সম্পন্ন করতে পারে।

গেটওয়ে এখন একটি সিঙ্গেল পয়েন্ট অফ ফেইলর

আপনি যা তৈরি করেছেন সে সম্পর্কে সৎ থাকুন। আপনার মালিকানাধীন প্রতিটি অ্যাপ্লিকেশন এখন একটি VPS-এ থাকা একটি কন্টেইনারের ওপর নির্ভরশীল। এটি ডাউন থাকলে কোনো মডেল কল করা সম্ভব হবে না, এমনকি যেসব প্রোভাইডার পুরোপুরি সচল আছে তাদের ক্ষেত্রেও। এর ফলে চারটি বিষয় ঘটে।

  • একটি ভুল কনফিগারেশন সবকিছু একসাথে ডাউন করে দেয়। restart: unless-stopped ক্র্যাশ হওয়া সার্ভিসকে রিস্টার্ট করে, এবং এটি এমন একটি কন্টেইনারকে বারবার রিস্টার্ট করে যা config.yaml পার্স করতে পারে না। প্রতিটি কনফিগারেশন পরিবর্তনের পর docker compose logs litellm পড়ুন এবং কনফিগারেশন পরিবর্তনের সময় হাতে পর্যাপ্ত সময় রাখুন যাতে আপনি তা পর্যবেক্ষণ করতে পারেন।
  • ক্যাশ মিস হলে Postgres রিকোয়েস্ট পাথে থাকে। যখন কি (key) ক্যাশে থাকে না তখন ভার্চুয়াল কি লুকআপ এটি ব্যবহার করে; রেসপন্সের পর স্পেন্ড রেকর্ডিং অ্যাসিনক্রোনাসলি চলে। /health/readiness থেকে 503 এরর আসা মানেই আপনার জন্য সতর্কবার্তা যে গেটওয়ে চলছে কিন্তু এর কোনোটিই করতে পারছে না।
  • প্রথমে কোর ব্যবহার করুন, তারপর ইনস্ট্যান্স যোগ করুন। প্রজেক্টের প্রোডাকশন গাইডেন্সে প্রতি ইনস্ট্যান্সে একটি ওয়ার্কার (--num_workers 1) রাখার কথা বলা হয়েছে, যা মূলত Kubernetes বা অন্য কোনো পড অটোস্কেলারের জন্য প্রযোজ্য যারা আপনার হয়ে ইনস্ট্যান্স যোগ করে। একটি সিঙ্গেল VM বা এই ধরনের বেয়ার কন্টেইনারের ক্ষেত্রে বিষয়টি উল্টো: ওয়ার্কারের সংখ্যা মেশিনের vCPU সংখ্যার সমান সেট করুন। উপরে উল্লিখিত 1 vCPU বক্সে এটি এখনও --num_workers 1; বড় বক্সে এটি বাড়িয়ে দিন এবং মনে রাখবেন প্রতিটি ওয়ার্কারের নিজস্ব মেমোরি প্রয়োজন হয়। লোড ব্যালেন্সারের পেছনে দ্বিতীয় একটি গেটওয়ে বসালে সিঙ্গেল কন্টেইনারের ঝুঁকি কমে, কিন্তু ডাটাবেসের ঝুঁকি থেকেই যায়।
  • যা পুনরায় তৈরি করতে পারবেন না তার ব্যাকআপ রাখুন। এটি হলো config.yaml এবং .env, সাথে ডাটাবেসের একটি pg_dump। LITELLM_SALT_KEY হারিয়ে ফেললে ডাম্পের ভেতরে থাকা এনক্রিপ্টেড প্রোভাইডার ক্রেডেনশিয়ালগুলো অকেজো হয়ে যাবে, তাই env ফাইল এবং ডাম্প একই ব্যাকআপ জবের অন্তর্ভুক্ত হওয়া উচিত: অফ-বক্স স্টোরেজে restic ব্যাকআপ।

আপগ্রেড করার অর্থ হলো ইমেজ ট্যাগ এডিট করা এবং docker compose up -d চালানো। LiteLLM ডিফল্টভাবে স্টার্টআপের সময় prisma migrate deploy চালায়, তাই নতুন কন্টেইনার প্রথমবার বুট হওয়ার সময় ডাটাবেস স্কিমা মাইগ্রেট করে নেয়। ট্যাগ পরিবর্তন করার আগে ডাম্প নিয়ে রাখুন, কারণ পুরনো ইমেজ ফিরিয়ে আনলে ইতিমধ্যে সম্পন্ন হওয়া মাইগ্রেশন রিভার্স হয় না।

আপনি যে ভার্সনে পৌঁছাচ্ছেন শুধু সেটি নয়, বরং মাঝখানের প্রতিটি ভার্সনের রিলিজ নোট পড়ুন। v1.95.0 থেকে v1.104.0 বা তার পরবর্তী ভার্সনে যাওয়ার সময় বেশ কিছু ব্রেকিং পরিবর্তন ঘটে: উপরের বাজেট স্ট্যাটাস কোড, এমন একটি প্রক্সি যা মাস্টার কি সেট করা না থাকলে, খালি থাকলে বা সর্বজনবিদিত হলে স্টার্ট হতে অস্বীকার করে, এবং এমন একটি প্রক্সি যা স্টার্টআপের সময় ডাটাবেস সেটআপ ব্যর্থ হলে বন্ধ হয়ে যায়, যা restart: unless-stopped এর অধীনে রিস্টার্ট লুপ হিসেবে দেখা দেয়। v1.102.x বা তার আগের ভার্সন থেকে আসলে, v1.104.0 এর নোটে আপনাকে দুটি LiteLLM_SpendLogs ইনডেক্স নিজে তৈরি করতে বলা হয়েছে।

FAQ

LiteLLM কি প্রতিটি কলে উল্লেখযোগ্য ল্যাটেন্সি যোগ করে?

প্রকল্পটির README (অক্টোবর 2026) অনুযায়ী, 1000 রিকোয়েস্ট প্রতি সেকেন্ডে এর 95তম পার্সেন্টাইল ল্যাটেন্সি 8 ms। এটিকে ভেন্ডরের দেওয়া পরিসংখ্যান হিসেবে গণ্য করুন। আপনার ল্যাটেন্সিকে মূলত প্রভাবিত করবে আপনার অ্যাপ্লিকেশন এবং গেটওয়ের মধ্যকার নেটওয়ার্ক দূরত্ব, কারণ প্রতিটি কলের সাথে একটি অতিরিক্ত রাউন্ড ট্রিপ যোগ হচ্ছে। গেটওয়েটিকে সেই একই অঞ্চলে চালান যেখানে অ্যাপ্লিকেশনগুলো রয়েছে, তারপর একটি বাস্তব রেসপন্সে x-litellm-overhead-duration-ms হেডার ব্যবহার করে আপনার নিজস্ব ওভারহেড পরিমাপ করুন।

Nginx সামনে বসানোর পর স্ট্রিমিং কাজ করা বন্ধ করে দিল কেন?

কারণ Nginx ডিফল্টভাবে আপস্ট্রিম রেসপন্স বাফার করে এবং একটি স্ট্রিমিং কমপ্লিশন হলো সার্ভার-সেন্ট ইভেন্টের একটি সিরিজ। proxy_buffering চালু থাকলে, Nginx ক্লায়েন্টের কাছে পাঠানোর আগে বাফারে ডেটা জমা করে, যা ডেলিভারিতে দেরি ঘটায়। লোকেশন ব্লকে proxy_buffering off; সেট করুন। একই ব্লকে proxy_read_timeout-এর মানও বাড়িয়ে দিন। ডিফল্টভাবে, গেটওয়ে থেকে 60 সেকেন্ড ডেটা না পেলে Nginx সংযোগ বিচ্ছিন্ন করে দেয়, যা দীর্ঘ নন-স্ট্রিমিং জেনারেশনের ক্ষেত্রে সহজেই ঘটে এবং ক্লায়েন্ট একটি 504 এরর পায়।

একটি ভার্চুয়াল কি-এর বাজেট শেষ হয়ে গেলে কী হয়?

কলটি budget_exceeded টাইপের একটি এরর এবং Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget> ফরম্যাটের একটি মেসেজ দিয়ে ব্যর্থ হয়। v1.95.0 ভার্সনে স্ট্যাটাস থাকে HTTP 429; v1.104.0 থেকে ডিফল্ট স্ট্যাটাস হলো HTTP 422। কোনো কোডই সরাসরি বাজেট শেষ হওয়ার কথা বলে না, আর এখানেই বিভ্রান্তি তৈরি হয়: ক্লায়েন্ট লাইব্রেরি 429-কে রেট লিমিট হিসেবে রিপোর্ট করে এবং পুনরায় চেষ্টা (retry) করতে পারে, ফলে ব্যবহারকারীরা মেসেজটি না পড়ে থ্রটলিংয়ের পেছনে সময় নষ্ট করেন। স্ট্যাটাস কোডের পাশাপাশি রেসপন্স বডি লগ করুন। মাস্টার কি-এর বিপরীতে /key/info?key=sk-... ব্যবহার করে কি-এর প্রকৃত অবস্থান নিশ্চিত করুন এবং বাজেট কম সেট করা থাকলে /key/update ব্যবহার করে তা বাড়িয়ে দিন।

গেটওয়ে কি হোস্ট করা মডেলের পাশাপাশি লোকাল মডেলেও রুট করতে পারে?

হ্যাঁ, এবং এটি model_list-এ আরও একটি এন্ট্রি হিসেবে যোগ করা যায়। ollama_chat/ প্রিফিক্স এবং একটি api_base ব্যবহার করুন, উদাহরণস্বরূপ api_base: http://ollama:11434-এর পাশাপাশি model: ollama_chat/llama3.1। Compose-এর ডিফল্ট নেটওয়ার্কিংয়ে, কন্টেইনারের ভেতরে localhost বলতে সেই কন্টেইনারকেই বোঝায়, তাই Docker নেটওয়ার্কে Compose সার্ভিস নেম বা হোস্টের অ্যাড্রেস ব্যবহার করুন, কখনোই 127.0.0.1 ব্যবহার করবেন না। হোস্ট নেটওয়ার্কিং, যেখানে কন্টেইনার হোস্টের নেটওয়ার্ক স্ট্যাক শেয়ার করে, সেটি এর ব্যতিক্রম। লোকাল মডেল সেটআপ করা একটি আলাদা কাজ: দেখুন Ollama ব্যবহার করে VPS-এ LLM সেলফ-হোস্টিং।

এই লেখাটি কি কাজে লেগেছে?