स्थापना और पहला अनुरोध
एक आभासी वातावरण बनाएँ और SDK इंस्टॉल करें। openai पैकेज का संस्करण 1 या उससे ऊपर माना गया है।
python -m venv .venv
source .venv/bin/activate
pip install --upgrade openaiAPI कुंजी पृष्ठ से कुंजी प्राप्त करें; नए उपयोगकर्ता एक क्लिक के साथ $0.50 का ट्रायल क्रेडिट प्राप्त कर सकते हैं, जो 7 दिनों के लिए मान्य है और कार्ड की आवश्यकता नहीं है। कुंजी को एक एनवायरनमेंट वेरिएबल में रखें, फिर पहला कॉल करें:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.glmapi.cc/v1",
api_key=os.environ["GLM_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise writing assistant."},
{"role": "user", "content": "Suggest five titles for a space-western novel."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)यह पूरा इंटीग्रेशन है। सेवा एक स्वतंत्र, OpenAI-संगत एंडपॉइंट है जो एक ओपन-वेट मॉडल के लिए है, इसलिए यह GLM के पीछे के विक्रेता या किसी अन्य की आधिकारिक API नहीं है। केवल POST /v1/chat/completions और GET /v1/models मौजूद हैं। क्विकस्टार्ट समर्थित अनुरोध फ़ील्डों की सूची देता है।
शुरुआत से दो छोटी आदतें मदद करती हैं। पहला, client.models.list() को एक स्मोक टेस्ट में एक बार कॉल करें: यह GET /v1/models पर जाता है, इसलिए यह साबित करता है कि कुंजी, बेस URL और नेटवर्क पथ सभी काम कर रहे हैं, इससे पहले कि आप एक भी टोकन खर्च करें। दूसरा, विकास के दौरान resp.usage प्रिंट करें। प्रॉम्प्ट और कंप्लीशन टोकन गणना आपको शुरुआत में बताती है कि क्या सिस्टम प्रॉम्प्ट आपकी सोच से बड़ा है, और $0.15 प्रति 1M इनपुट और $0.50 प्रति 1M आउटपुट पर आप एक कॉल की कीमत अपने दिमाग में लगा सकते हैं।
मल्टी-टर्न चैट और सैंपलिंग सेटिंग्स
API अवस्थाहीन है, इसलिए आप संवाद सूची स्वयं रखते हैं और हर बार उसे भेजते हैं। एक छोटा क्लास काफी है:
class Chat:
def __init__(self, client, system, max_turns=12):
self.client = client
self.system = system
self.max_turns = max_turns
self.history = []
def say(self, text):
self.history.append({"role": "user", "content": text})
window = self.history[-self.max_turns * 2:]
resp = self.client.chat.completions.create(
model="uncensored",
messages=[{"role": "system", "content": self.system}] + window,
temperature=0.9,
top_p=0.95,
frequency_penalty=0.3,
)
reply = resp.choices[0].message.content
self.history.append({"role": "assistant", "content": reply})
return replyस्लिस self.history[-self.max_turns * 2:] पिछले बारह उपयोगकर्ता और सहायक युग्मों को रखता है। एक वास्तविक एप्लिकेशन कुछ शुरुआती संदेशों को भी पिन कर सकता है, जैसे कि एक कैरेक्टर शीट या महत्वपूर्ण तथ्य, और बीच के हिस्से को सारांशित कर सकता है। आखिरी 12 टर्न्स तक ट्रिम करने से संवाद बढ़ने पर इनपुट लागत स्थिर रहती है। कॉन्टेक्स्ट विंडो प्रॉम्प्ट और कंप्लीशन दोनों के लिए 100,000 टोकन की है, इसलिए यह एक लागत चयन है, आवश्यकता से ज्यादा। पैरामीटर temperature, top_p, stop, seed और पेनल्टीज़ पास किए जाते हैं। प्रॉम्प्ट टेस्ट करते समय दोहराव वाले रन के लिए seed सेट करें, और लंबाई सीमित करना चाहते हों तभी max_tokens सेट करें, अन्यथा उसे छोड़ दें।
स्ट्रीमिंग प्रतिक्रियाएं
स्ट्रीमिंग सर्वर-सेंट इवेंट्स का उपयोग करता है और टोकन को जनरेट होते ही देता है, जो किसी भी इंटरैक्टिव टूल के लिए महत्वपूर्ण है। stream=True पास करें और इटरेट करें:
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Describe a harbour at dawn in 120 words."}],
stream=True,
)
parts = []
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta.content
if delta:
parts.append(delta)
print(delta, end="", flush=True)
text = "".join(parts)chunk.choices पर गार्ड महत्वपूर्ण है क्योंकि अंतिम चंक उपयोग जानकारी लेता है और इसमें खाली चॉइसेस लिस्ट हो सकती है। एक ऑपरेशनल बिंदु: एक स्ट्रीम किए गए अनुरोध में कॉन्करेंसी स्लॉट स्ट्रीम खत्म होने तक बना रहता है। लूप को एक try ब्लॉक में घेरें जिसमें एक finally क्लॉज़ हो जो स्ट्रीम को बंद कर दे यदि आपका कोड बीच में कोई त्रुटि देता है, उदाहरण के लिए जब क्लाइंट डिस्कनेक्ट हो जाता है। हमेशा स्ट्रीम को समाप्त या बंद करें, अन्यथा स्ट्रीम्स को छोड़ने वाला लूप कॉन्करेंसी लिमिट पर पहुँच जाएगा और कोई उपयोगी काम नहीं करेगा।
JSON मोड
जब आउटपुट अन्य कोड को फीड करता है, तो एक JSON ऑब्जेक्ट माँगें। प्रॉम्प्ट में JSON का उल्लेख करें, ताकि मॉडल को पता चले कि आप किस आकार की उम्मीद कर रहे हैं:
import json
resp = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with a JSON object only."},
{"role": "user", "content": "Give a character with keys name, age, motto."},
],
)
data = json.loads(resp.choices[0].message.content)
print(data["name"])फिर भी, प्रोडक्शन कोड में json.loads को एक try ब्लॉक में घेरें। JSON मोड वैध आउटपुट की संभावना बढ़ाता है, और एक स्टॉप सीक्वेंस या आपकी अपनी सीमा द्वारा कटा हुआ अनुरोध अब भी अधूरा (ट्रंकेटेड) ऑब्जेक्ट छोड़ सकता है। समान इनपुट के साथ एक बार फिर से प्रयास करना एक सस्ता समाधान है, और चूँकि विफल अनुरोधों के लिए चार्ज नहीं किया जाता है, एक रीट्राई की लागत केवल सफल कॉल की होती है।
टूल कॉलिंग
फ़ंक्शन कॉलिंग OpenAI के आकार का अनुसरण करती है: आप tools में फ़ंक्शन वर्णित करते हैं, मॉडल tool_calls लौटाता है, आप उन्हें चलाते हैं और role tool के साथ परिणाम वापस भेजते हैं। एक पूर्ण राउंड ट्रिप:
import json
tools = [{
"type": "function",
"function": {
"name": "roll_dice",
"description": "Roll a die with the given number of sides.",
"parameters": {
"type": "object",
"properties": {"sides": {"type": "integer"}},
"required": ["sides"],
},
},
}]
messages = [{"role": "user", "content": "Roll a twenty-sided die for me."}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = {"value": 17, "sides": args["sides"]} # replace with real logic
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
else:
print(msg.content)लूप महत्वपूर्ण है, पासे की बारीकियों से ज्यादा। मॉडल कॉल का अनुरोध करता है, आपका कोड उसे चलाता है, और आप सहायक संदेश और टूल रिजल्ट दोनों को जोड़ते हैं फिर से पूछने से पहले। सहायक संदेश छोड़ दें, तो रिजल्ट में tool_call_id के मिलान के लिए कुछ नहीं बचेगा, जो भ्रमित करने वाले दूसरे जवाब का सामान्य कारण है। tool_choice को auto पर सेट करने पर मॉडल तय करता है कि टूल कॉल करना है या नहीं, इसलिए हमेशा उस शाखा को हैंडल करें जहाँ वह सीधे जवाब देता है, जैसा कि ऊपर दिए गए कोड में है। स्ट्रीम किए गए रिस्पॉन्स में tool_calls भी हो सकते हैं, जिन्हें आप इंडेक्स द्वारा जोड़कर पार्स करते हैं।
कार्रवाई करने से पहले आर्ग्युमेंट्स को सत्यापित करें। मॉडल एक गलत फ़ॉर्मेट वाला कॉल उत्पन्न कर सकता है, और आपका कोड आर्ग्युमेंट्स को अविश्वसनीय इनपुट के रूप में मानना चाहिए, जैसे कि यूजर से कुछ भी।
सीमाओं के भीतर Async बैच
एक साथ कई प्रॉम्प्ट्स के लिए, अपनी कन्करेंसी सीमा से मेल खाने वाले सेमाफोर के साथ AsyncOpenAI का उपयोग करें। एक पूर्ण कुंजी 8 समानांतर अनुरोधों की अनुमति देती है, एक ट्रायल कुंजी 2:
import asyncio, os
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.glmapi.cc/v1",
api_key=os.environ["GLM_API_KEY"],
)
gate = asyncio.Semaphore(8) # use 2 on a trial key
async def one(prompt):
async with gate:
r = await client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
async def main():
prompts = [f"Write a one-line tagline for product {i}." for i in range(40)]
results = await asyncio.gather(*(one(p) for p in prompts))
print(len(results))
asyncio.run(main())सेमाफोर कॉन्करेंसी को संभालता है, लेकिन प्रति मिनट 300 अनुरोध की सीमा को नहीं। लंबे बैच के लिए एक छोटा विलंब या टोकन-बकेट लिमिटर जोड़ें। 1M इनपुट टोकन के लिए $0.15 और 1M आउटपुट टोकन के लिए $0.50 की दर से, लगभग 60 टोकन इनपुट और 25 टोकन आउटपुट वाले 40 टैगलाइन अनुरोधों का कुल खर्च $0.00036 + $0.0005 = $0.00086 होता है, इसलिए सीमाएं, न कि कीमत, बैच डिज़ाइन को आकार देती हैं। टॉप-अप के लिए मूल्य निर्धारण पृष्ठ देखें।
रेट लिमिट, पुन: प्रयास और हेडर
सीमा से अधिक होने पर, API HTTP 429 लौटाता है। त्रुटि कोड rate_limited प्रति मिनट बहुत अधिक अनुरोधों के लिए है और concurrency एक साथ बहुत अधिक के लिए। अस्थायी ओवरलोड भी Retry-After: 5 हेडर के साथ 429 लौटाता है। SDK कुछ विफलताओं को स्वयं पुनः प्रयास करता है और आप क्लाइंट पर max_retries सेट कर सकते हैं, लेकिन स्पष्ट हैंडलिंग अधिक स्पष्ट है:
import time
from openai import RateLimitError
def ask(prompt, tries=4):
for attempt in range(tries):
try:
raw = client.chat.completions.with_raw_response.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
print("balance:", raw.headers.get("X-Balance-USD"))
return raw.parse().choices[0].message.content
except RateLimitError as e:
wait = float(e.response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("still rate limited")यहाँ with_raw_response आपको X-Balance-USD, X-Request-Id और रेट-लिमिट हेडर्स तक पहुंच देता है। किसी भी त्रुटि को रिपोर्ट करने के लिए अनुरोध ID लॉग करें। अपनी कुंजी उस नोटबुक में कभी न रखें जिसे आप कमिट करते हैं या फ्रंट-एंड कोड में।
बिना क्रेडिट खर्च किए परीक्षण और सामान्य गलतियाँ
Python इंटीग्रेशन का ज्यादातर हिस्सा इंफ्रास्ट्रक्चर है, और इंफ्रास्ट्रक्चर को लाइव मॉडल की जरूरत नहीं है। नेटवर्क कॉल को एक फंक्शन के पीछे रखें, और आपका टेस्ट उसे बदल सकता है। केवल स्टैंडर्ड लाइब्रेरी के साथ:
from unittest.mock import MagicMock
def reply_text(client, prompt):
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content.strip()
def test_reply_text_strips_whitespace():
fake = MagicMock()
fake.chat.completions.create.return_value.choices = [
MagicMock(message=MagicMock(content=" hello "))
]
assert reply_text(fake, "hi") == "hello"केवल किनारों पर ही कुछ वास्तविक कॉल चलाएँ: हर डिप्लॉय के लिए एक स्मोक टेस्ट, और अपने सहेजे गए प्रॉम्प्ट सेट का कभी-कभार एक रन। लगभग 100 टोकन इनपुट और 20 टोकन आउटपुट वाले एकल स्मोक टेस्ट की लागत $0.000015 + $0.00001 = $0.000025 होती है, जो कि कुछ भी नहीं है, और विफल अनुरोधों पर कोई चार्ज नहीं होता है। $0.50 का मुफ़्त ट्रायल क्रेडिट एक परीक्षण सेट की आवश्यकता से कहीं अधिक है।
प्रॉम्प्ट वर्क के लिए, seed और temperature को स्थिर रखें ताकि आप सिस्टम प्रॉम्प्ट के दो संस्करणों की समान शर्तों पर तुलना कर सकें। रनों के बीच प्रॉम्प्ट और सैम्पलिंग सेटिंग्स दोनों को बदलने से परिणाम पढ़ने लायक नहीं रहते। प्रत्येक रन के आउटपुट को X-Request-Id हेडर से प्राप्त अनुरोध ID के साथ सहेजें, और आप किसी आश्चर्यजनक उत्तर का बाद में पता लगा सकते हैं।
Python की सामान्य गलतियाँ
ये Python उपयोगकर्ताओं से आने वाले अधिकांश सहायता प्रश्नों का कारण बनते हैं:
- कुंजी को केवल
OPENAI_API_KEYके रूप में पास करना। यदि आप कोड मेंbase_urlसेट करते हैं लेकिन कुंजी वेरिएबल भूल जाते हैं, तो SDK खाली या गलत कुंजी भेजता है।api_keyको स्पष्ट रूप से पास करें। /v1को भूल जाना। बेस URL/v1पर समाप्त होता है। SDK स्वयं/chat/completionsजोड़ता है।- अन्य सेवा से मॉडल नाम का उपयोग करना। केवल
uncensoredयहाँ मान्य है। - असीमित कन्करेंसी।
asyncio.gatherसेमाफोर के बिना हज़ारों कोरूटीन पर काम करता है, जिससेconcurrencyत्रुटियों की दीवार उत्पन्न होती है। - स्ट्रीम्ड प्रतिक्रिया को एक स्ट्रिंग के रूप में मानना। डेल्टा को जोड़ें और खाली
choicesसूची वाले चंक को छोड़ दें। - कॉन्टेक्स्ट सीमा को अनदेखा करना। इतिहास और कम्प्लीशन को 100,000 टोकन में फिट होना चाहिए। उस सीमा तक पहुँचने से पहले, पुराने टर्न्स को ट्रिम करें।
टॉप-अप और वर्तमान रेट कार्ड के लिए, मूल्य निर्धारण पृष्ठ देखें। सेवा केवल टेक्स्ट के लिए है, इसलिए इमेज, ऑडियो या एम्बेडिंग कॉल को वायर अप करने की आवश्यकता नहीं है, और प्रॉम्प्ट को प्रशिक्षण के लिए उपयोग नहीं किया जाता है।
अधिक गाइड
प्रश्न और उत्तर
मुझे किस Python पैकेज की आवश्यकता है?
मानक openai पैकेज, संस्करण 1 या बाद का। base_url को https://api.glmapi.cc/v1 पर सेट करें और मॉडल को uncensored पर।
क्या स्ट्रीमिंग काम करती है?
हाँ। stream=True पास करें और चंक्स पर इटरेट करें। स्ट्रीम उपयोग जानकारी के साथ समाप्त होती है, और यह समाप्त होने तक एक कन्करेंसी स्लॉट को घेरे रखती है।
मैं समानांतर अनुरोध कितने चला सकता हूँ?
एक पूर्ण कुंजी 8 समानांतर अनुरोध और प्रति मिनट 300 की अनुमति देती है। एक ट्रायल कुंजी 2 और 60 की अनुमति देती है।
क्या मैं LangChain या LiteLLM का उपयोग कर सकता हूँ?
दोनों बेस URL, कुंजी और मॉडल नाम uncensored देकर एक OpenAI-संगत एंडपॉइंट से बात कर सकते हैं। सटीक पैरामीटर नामों के लिए प्रत्येक प्रोजेक्ट के दस्तावेज़ जाँचें।
आपकी कुंजी बस एक फ़ॉर्म दूर है
एक खाता बनाएँ, कुंजी कॉपी करें, बेस URL बदलें। सेटअप यही है।
