মূল কনটেন্টে যান
এআই টিউটোরিয়াল

SGLang-এর মাধ্যমে এক-টোকেন শ্রেণিবিন্যাসের জন্য OpenJev ডিপ্লয় ও ব্যবহার

Modal-এ openjev-sglang ডিপ্লয় করা, Noul, Choice ও Score মূল্যায়ন জমা দেওয়া, সার্ভিসের স্বাস্থ্য পরীক্ষা করা, সীমা ও প্রমাণীকরণ কনফিগার করা এবং API-কে বিদ্যমান SGLang ব্যাকএন্ডের সঙ্গে সংযুক্ত করা শিখুন।

SGLang-এর মাধ্যমে এক-টোকেন শ্রেণিবিন্যাসের জন্য OpenJev ডিপ্লয় ও ব্যবহার

openjev-sglang কী?

openjev-sglang হলো এমন একটি সার্ভার, যা SGLang-এ Qwen3.6-35B-A3B ব্যবহার করে TypeSafe/Jev HTTP API বাস্তবায়ন করে। এটি দীর্ঘ উত্তর তৈরি করার পরিবর্তে শ্রেণিবিন্যাসের প্রশ্নগুলোকে দক্ষ প্রথম-টোকেন সম্ভাব্যতা রিডআউটে রূপান্তর করে।

ডিফল্ট ডিপ্লয়মেন্টে প্রতিটি NVIDIA B200 GPU-এর জন্য একটি করে SGLang কনটেইনার চালানো হয়। SGLang 0.5.19-এ এর Rust ফ্রন্টএন্ড, radix caching এবং breakable prefill CUDA graph রয়েছে। একটি পৃথক Python প্রসেস FastAPI, uvloop, Rust-ভিত্তিক Hugging Face tokenizer এবং localhost-এ SGLang-এর সঙ্গে pooled asynchronous connection ব্যবহার করে পাবলিক API পরিচালনা করে।

TypeSafe-এ কোনো অনুরোধ পাঠানো হয় না। পাবলিক endpoint evaluation API প্রকাশ করে, আর SGLang-এর generation ও administration route-গুলো localhost-এ ব্যক্তিগত থাকে।

মূল বৈশিষ্ট্য

  • তিন ধরনের evaluation: yes/no সম্ভাব্যতার জন্য Noul, শ্রেণিবদ্ধ নির্বাচন-এর জন্য Choice এবং ক্রমবদ্ধ স্তরের জন্য Score।
  • এক-টোকেন inference: তৈরি করা chain of thought ছাড়াই answer-label log probability থেকে প্রতিটি প্রশ্নের মূল্যায়ন করা হয়।
  • Shared-prefix caching: প্রশ্নের শাখাগুলো একসঙ্গে চলার আগে একটি সাধারণ prompt prefix warm করা হয়।
  • সর্বোচ্চ 64টি প্রশ্ন: ডিফল্টভাবে Choice এবং Score প্রশ্নে 2 থেকে 64টি উত্তর সমর্থিত।
  • গঠিত state: state এবং instructions string, JSON object অথবা array হতে পারে।
  • অপারেশনাল endpoint: health, liveness, model catalogue, limits, OpenAPI schema, Swagger UI এবং একটি interactive Scalar reference অন্তর্ভুক্ত রয়েছে।
  • Modal deployment: সরবরাহ করা অ্যাপ্লিকেশন autoscaling, persistent model cache, startup recovery এবং scale-to-zero আচরণ সমর্থন করে।
  • কনফিগারযোগ্য সুরক্ষা: request limit, concurrency control, timeout এবং ঐচ্ছিক Bearer authentication উপলভ্য।

পূর্বশর্ত

uv ইনস্টল করুন এবং প্রদত্ত cloud deployment ব্যবহার করার পরিকল্পনা থাকলে একটি Modal account সংগ্রহ করুন। প্রথম build-এ একটি বড় SGLang image import করা হয়, আর প্রথম GPU start-এ model weight ডাউনলোড এবং kernel compile বা capture করা হয়।

CUDA dependency-গুলো SGLang container-এর মধ্যেই থাকে। আপনার workstation-এ uv sync চালালে শুধু API, deployment utility এবং test ইনস্টল হয়।

প্রকল্প ইনস্টল করুন

রিপোজিটরি clone করুন, এর directory-তে প্রবেশ করুন এবং Python environment synchronize করুন:

uv sync

বর্তমান মেশিনে Modal authenticate করা না থাকলে এর setup সম্পন্ন করুন:

uv run modal setup

একটি অস্থায়ী Modal deployment চালান

End-to-end test-এর জন্য একটি অস্থায়ী server চালু করুন, অন্তর্ভুক্ত inference check চালান এবং পরে server বন্ধ করুন:

uv run modal run modal_app.py

এই command smoke-test report-টি smoke-result.json হিসেবে সংরক্ষণ করে। Report-এ startup wait time, inference latency এবং উপলভ্য cache-usage তথ্য থাকে।

একটি স্থায়ী endpoint deploy করুন

অ্যাপ্লিকেশনটিকে persistent Modal service হিসেবে deploy করুন:

uv run modal deploy modal_app.py

Deployment https://YOUR-SERVER.us-west.modal.direct-এর মতো একটি URL প্রদর্শন করে। সহজে ব্যবহারের জন্য এটি একটি environment variable-এ সংরক্ষণ করুন:

export OPENJEV_URL="https://YOUR-SERVER.us-west.modal.direct"

ডিফল্ট Modal endpoint-এ ইচ্ছাকৃতভাবে authentication নেই। এটি US West routing region ব্যবহার করে এবং কনফিগার করা US region-গুলোতে compute schedule করতে পারে। কোনো explicit container cap নেই, এবং পাঁচ মিনিট নিষ্ক্রিয় থাকার পর instance-গুলো scale to zero হয়।

Scale to zero হওয়া একটি server চালু হওয়ার সময় HTTP 503 ফেরত দেয়। অন্তর্ভুক্ত smoke command স্বয়ংক্রিয়ভাবে এই startup response-গুলোতে retry করে।

Deployed URL-এর বিরুদ্ধে smoke test চালান:

uv run openjev smoke "$OPENJEV_URL"

Smoke test তিন ধরনের answer-ই পরীক্ষা করে, 64টি answer-সহ একটি question যাচাই করে, মৌলিক semantic check চালায় এবং 65টি answer প্রত্যাখ্যাত হচ্ছে কি না তা নিশ্চিত করে।

আপনার প্রথম evaluation জমা দিন

প্রধান route হলো POST /v1/systemone। নিচের request-টি Noul, Choice এবং Score question ব্যবহার করে একটি customer message শ্রেণিবদ্ধ করে:

curl "$OPENJEV_URL/v1/systemone" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "jev-latest",
    "state": [
      {"role": "system", "content": "আপনি একজন support assistant।"},
      {"role": "user", "content": "আমার কাছ থেকে দুবার টাকা নেওয়া হয়েছে। অনুগ্রহ করে অতিরিক্ত চার্জটি ফেরত দিন।"}
    ],
    "questions": {
      "refund": {
        "type": "noul",
        "instructions": "ব্যবহারকারী কি refund চাইছেন?"
      },
      "department": {
        "type": "choice",
        "instructions": "এটি কোন department-এর পরিচালনা করা উচিত?",
        "criteria": {
          "billing": "Payment এবং refund",
          "technical": "Software bug"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "অনুরোধটি কতটা জরুরি?",
        "criteria": ["সাধারণ", "জরুরি", "অতি জরুরি"]
      }
    }
  }'

আপনি JSON একটি file-এ রেখেও shell quoting ছাড়াই জমা দিতে পারেন:

curl "$OPENJEV_URL/v1/systemone" \
  -H 'Content-Type: application/json' \
  --data-binary @examples/request.json

প্রশ্নের ধরন বোঝা

  • Noul: yes-এর normalized probability ফেরত দেয়।
  • Choice: সর্বোচ্চ-সম্ভাবনাময় option এবং সম্পূর্ণ probability distribution ফেরত দেয়। উত্তরের option name মূল অবস্থায় সংরক্ষিত থাকে।
  • Score: প্রতিটি level index ও তার probability থেকে গণনা করা zero-based expected level এবং একটি legend ফেরত দেয়।

পছন্দের বিকল্পগুলো মডেলের কাছে বর্ণনার আগে অক্ষর-লেবেল হিসেবে উপস্থাপিত হয়। উত্তর-চাবিগুলো সাধারণত মডেলের কাছ থেকে গোপন রাখা হয়। কোনো বিকল্পের বর্ণনা null হলে, তার চাবিটিই অর্থ সরবরাহ করে।

স্টেট ও নির্দেশনা প্রদান

state এবং instructions উভয়ই স্ট্রিং, JSON অবজেক্ট বা অ্যারে গ্রহণ করে। চ্যাট মেসেজের একটি তালিকা, অথবা ঠিক {"messages": [...]} ধারণকারী কোনো অবজেক্ট, মডেলের নেটিভ চ্যাট টেমপ্লেট দিয়ে রেন্ডার করা হয়।

মূল মেসেজের ভূমিকা ও অবজেক্টগুলো অপরিবর্তিত রাখা হয়, এবং প্রতিটি শ্রেণিবিন্যাস প্রশ্ন আরেকটি user turn হিসেবে যোগ করা হয়। অন্যান্য কাঠামোবদ্ধ মানগুলো অক্ষত অবস্থায় একটি user message-এ সিরিয়ালাইজ করা হয়। messages-এর পাশাপাশি অন্যান্য ফিল্ড ধারণকারী অবজেক্টও সংরক্ষিত থাকে, যাতে মেটাডেটা নীরবে বাদ না পড়ে।

চ্যাট স্টেট কেবল টেক্সট কনটেন্ট সমর্থন করে। ছবি, অডিও ও ভিডিও কনটেন্ট সমর্থিত নয়।

সার্ভিসের এন্ডপয়েন্টগুলো দেখুন

  • POST /v1/systemone Noul, Choice এবং Score মূল্যায়ন সম্পাদন করে।
  • GET /v1/models TypeSafe ও OpenAI-ধাঁচের ফিল্ডসহ একটি মডেল ক্যাটালগ ফেরত দেয়।
  • GET /v1/limits গ্রহণ-সীমা জানায়।
  • GET /health readiness, SGLang-এর স্বাস্থ্য এবং স্টার্টআপের সময়কাল জানায়।
  • GET /health/live API প্রক্রিয়াটি সচল আছে কি না পরীক্ষা করে।
  • GET / সম্পাদনাযোগ্য অনুরোধের উদাহরণসহ একটি Scalar API রেফারেন্স খোলে।
  • GET /docs বিল্ট-ইন Swagger UI খোলে।
  • GET /openapi.json তৈরি করা OpenAPI স্কিমা ফেরত দেয়।

অনুরোধের মডেল নাম jev-latest একটি সামঞ্জস্যতা অ্যালিয়াস। ডিফল্ট পাবলিক মডেল ID হলো Qwen/Qwen3.6-35B-A3B, আর ওজনের উৎস হিসেবে অভ্যন্তরীণভাবে NVIDIA-এর রিপোজিটরি ব্যবহৃত হয়।

ইনফারেন্স কীভাবে কাজ করে

  1. API স্কিমা, উত্তরের সংখ্যা, বডির আকার, কনটেক্সটের দৈর্ঘ্য এবং মোট টোকেন বাজেট যাচাই করে।
  2. এটি চিন্তন নিষ্ক্রিয় রেখে একবার নেটিভ চ্যাট টেমপ্লেট রেন্ডার করে, তারপর একটি শেয়ার করা প্রিফিক্সকে স্বাধীনভাবে টোকেনাইজ করা প্রশ্নের সাফিক্সগুলো থেকে আলাদা করে।
  3. এটি max_new_tokens=1 সহ সাধারণ প্রিফিক্সটি SGLang-এ পাঠায়। স্যাম্পল করা টোকেনটি বাদ দেওয়া হয়, ফলে সুযোগমতো radix-cache পুনর্ব্যবহারের জন্য প্রিফিক্সটি উপলব্ধ থাকে।
  4. এটি প্রতি প্রশ্নের জন্য একটি করে ব্রাঞ্চ সমান্তরালভাবে জমা দেয়। প্রতিটি ব্রাঞ্চে থাকে প্রিফিক্স, প্রশ্নের সাফিক্স, assistant header এবং Answer: marker।
  5. এটি কেবল বৈধ উত্তর-লেবেলগুলোর জন্য log probability চায় এবং স্থিতিশীল softmax দিয়ে সেগুলো পুনঃস্বাভাবিকীকরণ করে।

Nটি প্রশ্নের জন্য সার্ভার N + 1টি এক-টোকেন কল করে: একটি ক্যাশ-উষ্ণীকরণ কল এবং প্রতিটি প্রশ্নের জন্য একটি কল। তৈরি করা টোকেন উপেক্ষা করা হয়, তাই কোনো autoregressive continuation বা তৈরি করা chain of thought নেই। Speculative decoding সক্রিয় নয়।

লেবেলগুলো A থেকে Z পর্যন্ত চলে, এরপর AA ও AB-এর মতো যাচাইকৃত একক-টোকেন সংমিশ্রণ ব্যবহার করে। স্টার্টআপের সময় সক্রিয় tokenizer দিয়ে সব 64টি লেবেল যাচাই করা হয়। এর ফলে 10 বা 64-এর মতো বহু-টোকেন সংখ্যাসূচক লেবেল ব্যবহার এড়ানো যায়।

ব্যবহার ও ক্যাশ মেট্রিক বুঝুন

রেসপন্স হেডার x-openjev-prefix-tokens অনুরোধ করা সাধারণ প্রিফিক্সের আকার জানায়। SGLang ক্যাশের সংখ্যা সরবরাহ করলে, x-openjev-cached-tokens-এ ব্রাঞ্চ ক্যাশ হিটগুলোর যোগফল থাকে।

SGLang 0.5.19-এর Rust frontend ওই ক্যাশের সংখ্যা প্রকাশ করে না। সেই কনফিগারেশনে হেডারটি অনুপস্থিত থাকে এবং smoke report-এ শূন্য নয়, null রেকর্ড করা হয়। Scheduler log-এ তবু প্রকৃত ক্যাশ হিট দেখা যেতে পারে। Server-Timing হেডার prompt preparation, shared prefill এবং branch inference আলাদা করে দেখায়।

usage.input_tokens হলো warm-up ও branch call-গুলোতে রিপোর্ট করা সম্পূর্ণ prompt count-এর যোগফল, যার মধ্যে cached token-ও রয়েছে। usage.output_tokens হলো N + 1। এই পরিসংখ্যানগুলো অনন্যভাবে গণনা করা টোকেন বা TypeSafe-এর বিলিং-অনুমানের বদলে backend-এর কার্যকলাপ বর্ণনা করে।

সীমা ও প্রমাণীকরণ কনফিগার করুন

ডিফল্ট গ্রহণ-সীমার মধ্যে রয়েছে 64টি প্রশ্ন, প্রতিটি Choice বা Score প্রশ্নের জন্য 2 থেকে 64টি উত্তর, 2 MiB JSON body, প্রতি ব্রাঞ্চে আউটপুটসহ 32,768টি টোকেন, মোট জমা দেওয়া ইনপুট টোকেন 262,144টি, 16টি সমসাময়িক মূল্যায়ন এবং 64টি সমসাময়িক backend call।

অবৈধ অনুরোধ ইনফারেন্সের আগে HTTP 422 ফেরত দেয়। সীমার চেয়ে বড় বডি 413 ফেরত দেয়, অতিরিক্ত চাপ 529 ও Retry-After সহ ফেরত দেয়, এবং backend timeout 504 ফেরত দেয়। ব্যর্থ বা বাতিল হওয়া মূল্যায়ন সহোদর ব্রাঞ্চগুলো বাতিল করে এবং SGLang-এ সেগুলো বন্ধ করার চেষ্টা করে।

কনফিগারেশন OPENJEV_* environment variable-এর মাধ্যমে করা যায়। সাধারণ সেটিংসের মধ্যে রয়েছে:

  • OPENJEV_MODEL: ডিফল্ট হলো nvidia/Qwen3.6-35B-A3B-NVFP4।
  • OPENJEV_SERVED_MODEL_NAME: পাবলিক মডেল ID প্রতিস্থাপন করে এবং SGLang-এ পাঠানো হয়।
  • OPENJEV_REVISION: নির্দিষ্ট checkpoint revision নির্বাচন করে।
  • OPENJEV_FRONTEND: ডিফল্ট হলো rust; python একটি স্পষ্ট fallback।
  • OPENJEV_MAX_INPUT_TOKENS: ডিফল্ট হলো 32768।
  • OPENJEV_MAX_TOTAL_INPUT_TOKENS: ডিফল্ট হলো 262144।
  • OPENJEV_MAX_CONCURRENT_REQUESTS: ডিফল্ট হলো 16।
  • OPENJEV_MAX_CONCURRENT_BRANCHES: ডিফল্ট হলো 64।
  • OPENJEV_REQUEST_TIMEOUT: ডিফল্ট হলো 120 সেকেন্ড।
  • OPENJEV_TEMPERATURE: ডিফল্ট হলো 1.0 এবং লেবেল স্বাভাবিকীকরণে প্রভাব ফেলে।
  • OPENJEV_API_KEY: পাবলিক API-এর জন্য ঐচ্ছিক Bearer authentication সক্রিয় করে।
  • OPENJEV_BACKEND_API_KEY: SGLang-এর জন্য আলাদা ঐচ্ছিক Bearer key কনফিগার করে।

Modal লঞ্চার স্থানীয় পরিবেশ থেকে স্বয়ংক্রিয়ভাবে OPENJEV_PROFILE, OPENJEV_FRONTEND এবং OPENJEV_SERVED_MODEL_NAME ফরওয়ার্ড করে। অন্যান্য রিমোট সেটিংস image.env(...)-এ যোগ করুন, অথবা ক্রেডেনশিয়ালের জন্য একটি Modal Secret ব্যবহার করুন।

বিদ্যমান SGLang ব্যাকএন্ড দিয়ে স্থানীয়ভাবে চালান

ইতিমধ্যে চালু থাকা ব্যাকএন্ড ব্যবহার করতে API-কে তার স্থানীয় URL নির্দেশ করুন:

uv run openjev serve --connect http://127.0.0.1:30000

ব্যাকএন্ডে একই মডেল ও টোকেনাইজার রিভিশন ব্যবহার করতে হবে। এটিকে নির্বাচিত টোকেনের লগ সম্ভাব্যতা, রেডিক্স ক্যাশিং এবং পর্যাপ্ত কনটেক্সট দৈর্ঘ্যও সমর্থন করতে হবে।

যে B200 হোস্ট বা কনটেইনারে আলাদা Python পরিবেশে SGLang 0.5.19 ইনস্টল করা আছে, সেখানে তার ইন্টারপ্রেটার স্পষ্টভাবে উল্লেখ করুন:

uv run openjev serve --sglang-python /path/to/sglang/bin/python

ডেভেলপমেন্ট ও যাচাই

অফলাইন পরীক্ষা, টোকেনাইজার ইন্টিগ্রেশন যাচাই, লিন্টিং এবং স্কিমা তৈরির জন্য অন্তর্ভুক্ত কমান্ডগুলো ব্যবহার করুন:

uv run pytest
uv run pytest -m integration
uv run ruff check .
uv run openjev schema

সাধারণ পরীক্ষাগুলো অফলাইনে ইউনিট ও API আচরণ যাচাই করে। ইন্টিগ্রেশন পরীক্ষায় একটি বাস্তব টোকেনাইজার এবং একটি ছোট Hugging Face ডাউনলোড ব্যবহার করা হয়, তবে GPU প্রয়োজন হয় না। স্কিমা তৈরির জন্যও GPU বা মডেল ডাউনলোডের কোনোটিই প্রয়োজন হয় না।

উন্নত ডিপ্লয়মেন্ট পরামর্শ

GPU কনটেইনার চালু রাখুন

পাঁচ মিনিট নিষ্ক্রিয় থাকার পর Modal পরিষেবাটিকে শূন্যে স্কেল করে। নিষ্ক্রিয় GPU ব্যবহারের খরচ কমানোর চেয়ে ধারাবাহিকভাবে কম স্টার্টআপ লেটেন্সি বেশি গুরুত্বপূর্ণ হলে modal_app.py-তে min_containers=1 সেট করুন।

প্রথমবার চালুর ইনিশিয়ালাইজেশনের জন্য প্রস্তুত থাকুন

প্রথম GPU স্টার্টে ওজন ডাউনলোড হয় এবং কার্নেল কম্পাইল বা ক্যাপচার করা হয়। মডেলের ওজন, SGLang টিউনিং ডেটা এবং Triton কম্পাইলেশন ডেটা openjev-huggingface Modal Volume-এ সংরক্ষিত থাকে। পরবর্তী স্টার্টগুলোতে এসব ফাইল পুনরায় ব্যবহার করা হয়, তবে স্টার্টআপের সময় CUDA গ্রাফ ক্যাপচার এখনও সম্পন্ন হয়।

ক্যাশ পুনর্ব্যবহারকে সুযোগসাপেক্ষ হিসেবে বিবেচনা করুন

রেডিক্স পুনর্ব্যবহার প্রতি-রিকোয়েস্টে নির্দিষ্ট কোনো KV সেশন নয়। হাইব্রিড Qwen মডেলের পুনরাবৃত্ত স্টেট, ক্যাশ-পেজের সীমানা, ক্যাশের চাপ এবং সমসাময়িক ট্রাফিক হিটের সংখ্যা কমাতে পারে। ব্যাকএন্ড --mamba-radix-cache-strategy extra_buffer ব্যবহার করে।

আত্মবিশ্বাসের মান সতর্কতার সঙ্গে ব্যাখ্যা করুন

OpenJev আত্মবিশ্বাসকে 1 - H(probabilities) / log(number_of_options) হিসেবে সংজ্ঞায়িত করে এবং শূন্য থেকে একের মধ্যে সীমাবদ্ধ রাখে। সমসত্ত্ব সম্ভাব্যতা শূন্য আত্মবিশ্বাস তৈরি করে, আর একটি একক ফলাফলে কেন্দ্রীভূত সম্ভাব্যতা এক আত্মবিশ্বাস তৈরি করে।

এই সম্ভাব্যতাগুলো সরবরাহ করা বিকল্পগুলোর শর্তাধীন এবং প্রম্পটের শব্দচয়ন ও লেবেলের ক্রমের ওপর নির্ভর করতে পারে। এগুলো সঠিকতার ক্যালিব্রেটেড অনুমান নয়।

ব্যাকএন্ড ব্যর্থতার জন্য পরিকল্পনা রাখুন

SGLang অপ্রত্যাশিতভাবে বন্ধ হলে API-ও বন্ধ হয়ে যায়। এরপর Modal লঞ্চার কনটেইনারটি বন্ধ করে, যাতে Modal সেটিকে প্রতিস্থাপন করতে পারে; মৃত ইনফারেন্স ব্যাকএন্ডের সঙ্গে সংযুক্ত কোনো পাবলিক HTTP প্রসেস চালু থাকে না। স্বাভাবিক শাটডাউনে উভয় ওয়াচারই সুশৃঙ্খলভাবে নিষ্ক্রিয় হয়।

উপসংহার

openjev-sglang Qwen3.6-এ Jev-সামঞ্জস্যপূর্ণ শ্রেণিবিন্যাস চালানোর একটি কেন্দ্রীভূত উপায় দেয়, যেখানে এক-টোকেন সম্ভাব্যতা দ্রুত পড়া যায়। এর Modal কনফিগারেশন স্কেলযোগ্য B200 ডিপ্লয়মেন্টের সুবিধা দেয়, আর এর স্থানীয় কমান্ডগুলো পরীক্ষা চালানো ও বিদ্যমান SGLang পরিষেবার সঙ্গে সংযোগ সমর্থন করে। স্মোক টেস্ট দিয়ে শুরু করুন, আপনার সীমা ও প্রমাণীকরণ সেটিংস যাচাই করুন এবং ট্রাফিক বাড়ার সঙ্গে টাইমিং ও ক্যাশ হেডার পর্যবেক্ষণ করুন।