اپنے VPS پر AI PR review agent کیسے سیٹ اپ کریں
اپنے سرور پر AI PR reviewer چلانے کا طریقہ سیکھیں۔ یہ گائیڈ آپ کو diff-scoped prompts، inline comments، اور سائز فلٹرز کے ساتھ مکمل کنٹرول فراہم کرتی ہے تاکہ آپ کا ڈیٹا محفوظ رہے۔
Self-hosted PR review agent کیا کرتا ہے
Self-hosted PR review agent آپ کے اپنے سرور پر چلنے والا ایک چھوٹا پروگرام ہے۔ یہ pull request (PR) کی diff کو پڑھتا ہے اور صرف تبدیل شدہ لائنیں ماڈل کو بھیجتا ہے۔ جو جواب موصول ہوتا ہے، اسے inline review comments کے طور پر پوسٹ کر دیا جاتا ہے۔ یہ کبھی آپ کی branch کو checkout نہیں کرتا، اور نہ ہی کسی ایسی فائل کو پڑھتا ہے جسے pull request میں تبدیل نہ کیا گیا ہو۔ اس کے پاس صرف ایک ماڈل API (application programming interface) کی اور ایک ایسا ٹوکن ہوتا ہے جو صرف تبصرہ کرنے کی اہلیت رکھتا ہے، اس کے علاوہ کوئی اور اختیار نہیں رکھتا۔
ایک ماڈل diff کو پڑھ سکتا ہے۔ یہ مسئلہ حل ہو چکا ہے۔ اہم بات یہ ہے کہ diff کہاں جاتی ہے اور اس کی کلید (key) کس کے پاس ہے۔ Hosted review bot کا مطلب یہ ہے کہ ہر نجی ریپوزٹری (private repository) کی ہر diff آپ کے نیٹ ورک سے باہر جاتی ہے، کسی تیسرے فریق کے لاگز میں محفوظ ہوتی ہے، اور ان کی retention policy کے تحت رہتی ہے۔ آپ کے اپنے VPS (virtual private server) پر، diff GitHub سے آپ کے سرور اور پھر ماڈل API تک جاتی ہے، اور آپ ان 40 لائنوں کے کوڈ کو خود پڑھ سکتے ہیں جو یہ فیصلہ کرتے ہیں کہ کیا بھیجا جانا ہے۔
شروع کرنے سے پہلے آپ کو کن چیزوں کی ضرورت ہے
- ایک VPS جس پر Ubuntu 24.04 چل رہا ہو اور اس کے ساتھ ایک self-hosted GitHub Actions runner پہلے سے repository میں رجسٹرڈ ہو۔ اسے رجسٹر کرتے وقت اضافی لیبل
pr-reviewدیں، کیونکہ نیچے دیا گیا workflow اسی لیبل کی بنیاد پر انتخاب کرتا ہے۔ - Claude Console سے حاصل کردہ ایک Anthropic API key۔
- ایک ایسی repository جہاں آپ کا کنٹرول ہو کہ pull request کون کھول سکتا ہے۔ ایک private repository اس کے لیے آسان صورت ہے۔ نیچے دیا گیا fork کا سیکشن public کیس کا احاطہ کرتا ہے، اور وہاں اس کا جواب کم آرام دہ ہے۔
VPS پر reviewer انسٹال کریں
runner سروس اسی غیر مراعات یافتہ (unprivileged) اکاؤنٹ کے تحت چلتی ہے جسے آپ نے ./svc.sh install چلانے کے دوران بنایا تھا۔ reviewer کو بھی اسی اکاؤنٹ کے تحت انسٹال کریں تاکہ job اسے sudo کے بغیر چلا سکے۔ نیچے دیے گئے runner کی جگہ اپنے اکاؤنٹ کا نام لکھیں۔
sudo apt update && sudo apt install -y gh python3-venv
sudo install -d -m 755 -o runner -g runner /opt/pr-review
sudo -u runner python3 -m venv /opt/pr-review/venv
sudo -u runner /opt/pr-review/venv/bin/pip install anthropic
gh --versiongh --version اگست 2026 تک Ubuntu 24.04 پر gh version 2.45.0 پرنٹ کرتا ہے۔ 2.20 کے بعد کا کوئی بھی ریلیز نیچے استعمال ہونے والا --input فلیگ رکھتا ہے۔ Command 'gh' not found پیغام کا مطلب ہے کہ universe کمپوننٹ فعال نہیں ہے، لہذا sudo add-apt-repository universe چلائیں اور دوبارہ کوشش کریں۔
کلید اور ٹوکن کہاں رہتے ہیں
دو راز، دو مختلف دورانیے۔ ان میں سے کوئی بھی ریپوزٹری میں نہیں جاتا۔
ANTHROPIC_API_KEY ایک ریپوزٹری سیکرٹ ہے، جسے Settings، پھر Secrets and variables، اور پھر Actions کے تحت سیٹ کیا جاتا ہے۔ GitHub اسے انکرپٹ کرتا ہے اور رن ٹائم پر سٹیپ کے ماحول میں شامل کر دیتا ہے۔ یہ کبھی بھی ڈسک پر فائل نہیں ہوتا اور نہ ہی git ہسٹری میں موجود ہوتا ہے۔
GITHUB_TOKEN مختلف طریقے سے کام کرتا ہے۔ Actions ہر جاب کے لیے ایک نیا ٹوکن بناتا ہے اور جاب ختم ہونے پر اسے ضائع کر دیتا ہے۔ وہ ٹوکن کیا کر سکتا ہے، اس کا تعین ورک فلو میں موجود permissions: بلاک سے ہوتا ہے، لہذا اصل میں کم سے کم استحقاق (least privilege) کا اطلاق یہیں ہوتا ہے:
permissions:
contents: read
pull-requests: writeوہ ٹوکن ریویو پوسٹ کر سکتا ہے۔ وہ کمٹ پش نہیں کر سکتا، برانچ مرج نہیں کر سکتا، ورک فلو فائل میں ترمیم نہیں کر سکتا، اور نہ ہی کسی دوسری ریپوزٹری کو چھیڑ سکتا ہے۔ ایک ایجنٹ جو تبصرہ کر سکتا ہے وہ ایک ریویور ہے۔ ایک ایجنٹ جو پش کر سکتا ہے وہ ایک کمٹر ہے، اور اس پر کسی نے اتفاق نہیں کیا تھا۔ ماڈل کی (model key) کے ساتھ بھی وہی احتیاط برتیں، کیونکہ یہ آپ کے اکاؤنٹ پر پیسے خرچ کرتی ہے۔ اس قسم کے مسائل کے بارے میں مزید معلومات keeping secrets out of an AI agent's reach میں موجود ہیں۔
Actions جاب لاگز میں عین سیکرٹ سٹرنگ کو *** سے بدل دیتا ہے۔ یہ صرف عین سٹرنگ سے میچ کرتا ہے، لہذا اگر آپ کسی کلید کو base64 انکوڈ کریں، دو لائنوں میں تقسیم کریں، یا ایک وقت میں ایک کریکٹر پرنٹ کریں تو وہ واضح طور پر ظاہر ہو جائے گی۔ ایسا کوئی ڈیبگ سٹیپ شامل نہ کریں جو ماحول (environment) کو ڈمپ کر دے۔
فورک سے آنے والی پل ریکویسٹ آپ کی API key کیوں نہیں دیکھ سکتی
GitHub کا اصول مختصر ہے: GITHUB_TOKEN کے استثنا کے ساتھ، جب کوئی ورک فلو کسی فورک شدہ ریپوزٹری سے ٹرگر ہوتا ہے تو secrets رنر (runner) تک نہیں پہنچائے جاتے۔ لہذا، فورک سے چلنے والا pull_request آپ کی اسکرپٹ کو بغیر ANTHROPIC_API_KEY کے شروع کرتا ہے، اور پہلی API کال invalid x-api-key کے ساتھ ناکام ہو جاتی ہے۔
اس کا پرکشش حل یہ ہے کہ ٹرگر کو pull_request_target پر تبدیل کر دیا جائے، جو بیس ریپوزٹری کے سیاق و سباق میں چلتا ہے اور اسے secrets مل جاتے ہیں۔ یہاں ایسا ہرگز نہ کریں۔ GitHub کی اپنی سیکیورٹی گائیڈنس کہتی ہے کہ ایسے ورک فلو "مراعات یافتہ (privileged) ہوتے ہیں، جس کا مطلب ہے کہ وہ دیگر مراعات یافتہ ورک فلو ٹرگرز کے ساتھ مین برانچ کی کیشے شیئر کرتے ہیں، اور انہیں ریپوزٹری تک رائٹ ایکسس اور ریفرنسڈ secrets تک رسائی حاصل ہو سکتی ہے"، اور یہ کہ اس نتیجے کو "ریپوزٹری پر قبضہ کرنے کے لیے استعمال کیا جا سکتا ہے"۔
یہی گائیڈنس رنر کے بارے میں واضح ہے: "Self-hosted رنرز کو GitHub پر عوامی ریپوزٹریز کے لیے تقریباً کبھی استعمال نہیں کیا جانا چاہیے، کیونکہ کوئی بھی صارف ریپوزٹری کے خلاف پل ریکویسٹ کھول سکتا ہے اور ماحول (environment) کو نقصان پہنچا سکتا ہے۔"
یہ دو ڈیزائن کے انتخاب کی وجہ بنتا ہے۔ جاب میں ایک گارڈ موجود ہے تاکہ یہ صرف ان برانچز پر چلے جو آپ کی اپنی ریپوزٹری پر پش کی گئی ہوں۔ اور ورک فلو میں کوئی actions/checkout مرحلہ بالکل نہیں ہے۔ ایجنٹ کے پاس ڈسک پر برانچ کبھی نہیں ہوتی، لہذا ایک دشمنانہ پل ریکویسٹ صرف وہ ٹیکسٹ ہے جو ماڈل کو بھیجا جاتا ہے۔ یہ آپ کے VPS پر کوئی بلڈ اسکرپٹ نہیں چلا سکتی، کیونکہ آپ کے VPS پر کوئی بھی چیز اسے رن نہیں کرتی۔ تاہم، ٹیکسٹ کا مطلب بے ضرر ہونا نہیں ہے: کسی اجنبی کی لکھی ہوئی diff ایک غیر معتبر ان پٹ ہے جو ماڈل تک پہنچتی ہے، یہ وہی ٹرسٹ باؤنڈری ہے جس کا سامنا آپ کو تب ہوتا ہے جب آپ کسی ایجنٹ کو ویب سرچ کرنے کی اجازت دیتے ہیں، اور یہاں اسے محدود رکھنے والی واحد چیز یہ ہے کہ یہ ایجنٹ صرف ایک کمنٹ پوسٹ کرنے کے سوا کچھ نہیں کر سکتا۔
مکمل ریپوزٹری کے بجائے صرف diff حاصل کریں
ایک درخواست کے ذریعے آپ پورا diff سادہ ٹیکسٹ کی صورت میں حاصل کر سکتے ہیں۔
export GH_TOKEN=your_token # in the workflow this comes from secrets.GITHUB_TOKEN
gh api /repos/OWNER/REPO/pulls/42 -H "Accept: application/vnd.github.diff"Accept: application/vnd.github.diff میڈیا ٹائپ وہ چیز ہے جو رسپانس کو pull request بیان کرنے والے JSON آبجیکٹ سے تبدیل کر کے براہ راست unified diff میں بدل دیتی ہے، اور gh api اس باڈی کو بغیر کسی تبدیلی کے پرنٹ کرتا ہے۔ آپ کو نظر آنے والی پہلی لائن diff --git a/ سے شروع ہونی چاہیے۔ gh: Not Found (HTTP 404) کا مطلب ہے کہ ٹوکن ریپوزٹری کو نہیں دیکھ سکتا، جس کا مطلب fine-grained پرسنل ٹوکن پر تقریباً ہمیشہ یہ ہوتا ہے کہ Pull requests کی اجازت (permission) شامل نہیں کی گئی تھی۔
ٹیکن خرچ کرنے سے پہلے فلٹر کریں
یہ سیکشن اس فرق کو واضح کرتا ہے کہ لوگ بوٹ کو پڑھیں گے یا اسے mute کر دیں گے۔ نیچے دیے گئے ہر فلٹر کو ماڈل کے ایک بائٹ بھی دیکھنے سے پہلے چلنا چاہیے۔
- Path filters. لاک فائلز، vendored ڈائریکٹریز، minified بنڈلز اور جنریٹ شدہ کوڈ کو فلٹر کریں۔
package-lock.jsonپر ماڈل کا تبصرہ محض شور ہے، اور یہ فائلیں اکثر diff کے زیادہ تر بائٹس پر مشتمل ہوتی ہیں۔ - A size cap. حد سے تجاوز کرنے پر، ریویو کو چھوڑ دیں اور کامیابی کے ساتھ exit کریں۔ 4,000 لائنوں کے refactor پر ایک ایماندارانہ لائن لکھیں کہ یہ خودکار ریویو کے لیے بہت بڑا ہے، بجائے اس کے کہ ساٹھ اندازے لگائے جائیں۔
- A severity threshold and a comment cap. ہائی اور میڈیم نوعیت کی رپورٹس دیں، زیادہ سے زیادہ دس، جن میں سب سے زیادہ شدت والی پہلے ہو۔ گیارہواں تبصرہ کوئی نہیں پڑھتا۔
اسکرپٹ
اسے /opt/pr-review/review.py کے طور پر محفوظ کریں۔ یہ اپنی کنفیگریشن environment سے پڑھتا ہے، لہذا ورک فلو کوڈ میں تبدیلی کیے بغیر ماڈلز کو تبدیل کر سکتا ہے۔
#!/usr/bin/env python3
"""Review only the changed lines of one pull request."""
import json
import os
import subprocess
import sys
import anthropic
REPO = os.environ["GITHUB_REPOSITORY"]
PR = os.environ["PR_NUMBER"]
MODEL = os.environ.get("REVIEW_MODEL", "claude-haiku-4-5-20251001")
MAX_DIFF_BYTES = int(os.environ.get("MAX_DIFF_BYTES", "120000"))
MIN_SEVERITY = os.environ.get("MIN_SEVERITY", "medium")
MAX_COMMENTS = 10
RANK = {"low": 0, "medium": 1, "high": 2}
SKIP = ("package-lock.json", "poetry.lock", "/vendor/", "/node_modules/", ".min.js")
raw_diff = subprocess.run(
["gh", "api", f"/repos/{REPO}/pulls/{PR}",
"-H", "Accept: application/vnd.github.diff"],
check=True, capture_output=True, text=True,
).stdoutہر فائل کے لیے diff کو الگ کرنا ہی path filtering کو ممکن بناتا ہے۔ ہر لائن کو نمبر دینا ہی اس بات کو یقینی بناتا ہے کہ ریویو کمنٹس درست جگہ پر لگیں۔ GitHub صرف اسی لائن پر ان لائن کمنٹ قبول کرتا ہے جو diff کا حصہ ہو، اس لیے ماڈل کو ایک حقیقی لائن نمبر کا حوالہ دینا پڑتا ہے۔ اسے نمبر فراہم کرنے کا مطلب یہ ہے کہ وہ خود سے نمبر ایجاد کرنے کے بجائے ایک درست نمبر کاپی کر سکتا ہے۔
def per_file(diff_text):
"""Split a unified diff into one string per file."""
sections, current = [], []
for line in diff_text.splitlines():
if line.startswith("diff --git ") and current:
sections.append("\n".join(current))
current = []
current.append(line)
if current:
sections.append("\n".join(current))
return sections
def annotate(section):
"""Prefix every line that exists in the new file with its line number."""
out, n, in_hunk = [], 0, False
for line in section.splitlines():
if line.startswith("@@"):
n = int(line.split("+")[1].split(",")[0].split(" ")[0])
in_hunk = True
out.append(line)
elif not in_hunk or line.startswith(("-", "\\")):
out.append(line)
else:
out.append(f"{n}\t{line}")
n += 1
return "\n".join(out)
kept = [s for s in per_file(raw_diff)
if not any(p in s.split("\n", 1)[0] for p in SKIP)]
payload = "\n".join(annotate(s) for s in kept)
if not payload.strip():
print("every changed file was filtered out")
raise SystemExit(0)
if len(payload) > MAX_DIFF_BYTES:
print(f"diff is {len(payload)} bytes, over the {MAX_DIFF_BYTES} cap")
raise SystemExit(0)hunk ہیڈر نمبرنگ کو برقرار رکھتا ہے۔ @@ -12,7 +12,9 @@ بتاتا ہے کہ نئی فائل کا hunk لائن 12 سے شروع ہوتا ہے، لہذا کاؤنٹر وہاں سے شروع ہوتا ہے اور صرف شامل کی گئی (added) اور غیر تبدیل شدہ (unchanged) لائنوں پر آگے بڑھتا ہے۔ ہٹائی گئی (removed) لائنیں بغیر نمبر کے گزر جاتی ہیں، کیونکہ وہ نئی فائل میں موجود نہیں ہوتیں۔ بیک سلیش سے شروع ہونے والی لائنوں پر موجود گارڈ اس no-newline مارکر کو چھوڑ دیتا ہے جسے git فائل کے آخر میں لکھتا ہے، ورنہ یہ ہر اگلی لائن کے نمبر کو ایک درجہ آگے دھکیل دیتا۔
دونوں ایگزٹ status 0 استعمال کرتے ہیں، 1 نہیں۔ ایک فلٹر شدہ یا حد سے زیادہ بڑی pull request کو سبز نشان (green check) دکھانا چاہیے۔ ایک سرخ نشان (red check) جس پر انسان عمل نہ کر سکے اسے نظر انداز کر دیا جاتا ہے، اور ایک بار جب ایک چیک نظر انداز ہو جائے، تو باقی سب بھی نظر انداز کر دیے جاتے ہیں۔
SYSTEM = (
"You review one pull request diff. Every line that exists in the new file is "
"prefixed with its line number and a tab character. "
"Report only defects you can see in the lines shown: a crash, a resource leak, "
"a security mistake, a wrong boundary condition, a broken contract with code "
"that is visible in this diff. Do not comment on style, naming or formatting. "
"Do not guess about code you cannot see. Leave out anything you are not "
"certain about. An empty findings list is a normal and common answer. "
'Reply with JSON only, in this shape: {"findings": [{"path": "src/app.py", '
'"line": 42, "severity": "high", "comment": "what is wrong, then why"}]} '
"Every line number must be one you can see in the left column of that file."
)
client = anthropic.Anthropic()
message = client.messages.create(
model=MODEL,
max_tokens=2000,
system=SYSTEM,
messages=[{"role": "user", "content": payload}],
)
print(f"stop={message.stop_reason} in={message.usage.input_tokens} "
f"out={message.usage.output_tokens}", file=sys.stderr)
text = message.content[0].text
findings = json.loads(text[text.find("{"):text.rfind("}") + 1])["findings"]
findings = [f for f in findings if RANK.get(f["severity"], 0) >= RANK[MIN_SEVERITY]]
findings.sort(key=lambda f: -RANK.get(f["severity"], 0))
del findings[MAX_COMMENTS:]
if not findings:
print("nothing above the severity threshold; posting no comment")
raise SystemExit(0)
review = {
"event": "COMMENT",
"body": f"Automated review of the changed lines. {len(findings)} finding(s).",
"comments": [
{"path": f["path"].removeprefix("b/"), "line": f["line"], "side": "RIGHT",
"body": f"**{f['severity']}** {f['comment']}"}
for f in findings
],
}
subprocess.run(
["gh", "api", "-X", "POST", f"/repos/{REPO}/pulls/{PR}/reviews", "--input", "-"],
input=json.dumps(review), text=True, check=True,
)اس بلاک میں تین تفصیلات بنیادی اہمیت کی حامل ہیں۔ JSON کو پہلے { اور آخری } کے درمیان کاٹا جاتا ہے کیونکہ ماڈل بعض اوقات اپنے جواب کو کوڈ فینس میں لپیٹ دیتا ہے، اور json.loads اس فینس کی وجہ سے رک جاتا ہے۔ path سے ابتدائی b/ کو ہٹا دیا جاتا ہے، کیونکہ یہ پریفکس diff ہیڈر سے آتا ہے اور GitHub کو repository-relative پاتھ درکار ہوتا ہے۔ اور --input - پورے ریویو کو ایک ہی API کال کے طور پر بھیجتا ہے، تاکہ دس نتائج ایک ہی نوٹیفکیشن کے طور پر پہنچیں نہ کہ دس الگ الگ نوٹیفکیشنز کے طور پر۔
جب رپورٹ کرنے کے لیے کچھ نہ ہو، تو اسکرپٹ کچھ پوسٹ نہیں کرتا۔ ایک بوٹ جو ہر pull request پر "no issues found" لکھتا ہے، وہ لوگوں کو اسے نظر انداز کرنے کی عادت ڈال دیتا ہے، اور پھر وہ اس اہم پیغام کو بھی نظر انداز کر دیتے ہیں جو واقعی اہمیت رکھتا ہے۔
ورک فلو میں اسے شامل کریں
اسے .github/workflows/pr-review.yml کے طور پر محفوظ کریں:
name: pr-review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
if: github.event.pull_request.head.repo.full_name == github.repository && !contains(github.event.pull_request.labels.*.name, 'no-ai-review')
runs-on: [self-hosted, linux, pr-review]
steps:
- name: Review the changed lines
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REVIEW_MODEL: claude-haiku-4-5-20251001
MIN_SEVERITY: medium
run: /opt/pr-review/venv/bin/python /opt/pr-review/review.pyGITHUB_REPOSITORY اس env: بلاک میں اس لیے نہیں ہے کیونکہ Actions اسے ہر جاب کے لیے پہلے ہی سیٹ کر دیتا ہے۔ concurrency گروپ بلنگ کے لیے اہم ہے: اس کے بغیر، ایک برانچ پر تین فوری اصلاحات (quick fixes) پش کرنے سے تین مکمل ریویوز چلتے ہیں اور آپ کو تینوں کی ادائیگی کرنی پڑتی ہے، جبکہ اس کے ساتھ صرف آخری والا باقی رہتا ہے۔
if: لائن دو کام کرتی ہے۔ پہلا حصہ فورکس (forks) سے آنے والی pull requests کو چھوڑ دیتا ہے، جو ویسے بھی بغیر کی (key) کے ناکام ہو جاتیں۔ دوسرا حصہ آپ کی ٹیم کو ایک آف سوئچ دیتا ہے: pull request میں no-ai-review لیبل شامل کریں تو جاب نہیں چلے گی۔
ایک pull request کھولیں اور دیکھیں کہ کیا ہوتا ہے:
gh run list --workflow=pr-review.yml --limit 3
gh run view --log
gh pr view 42 --commentsایک رن جو چند سیکنڈ میں مکمل ہو جائے اور لاگ میں nothing above the severity threshold; posting no comment دکھائے، وہ درست کام کر رہا ہے۔ ایک چھوٹی اور صاف ستھری pull request پر یہی متوقع نتیجہ ہے۔
خودکار pull request ریویو کی قیمت کیا ہے؟
diff دراصل ان پٹ کا تقریباً سارا حصہ ہوتا ہے، لہذا diff کا سائز ہی قیمت کا تعین کرتا ہے۔ نیچے 500 لائنوں پر مشتمل ایک diff اور سسٹم پرامپٹ کا نمونہ دیا گیا ہے، جس کے ٹوکنز کا شمار تخمینہ لگانے کے بجائے ٹوکن کاؤنٹنگ اینڈ پوائنٹ کے ذریعے کیا گیا ہے۔
The data behind this chart
[
{
"label": "Haiku 4.5",
"input_tokens": "8,000",
"output_tokens": "1,200"
},
{
"label": "Sonnet 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
},
{
"label": "Opus 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
}
]یہ diff Haiku 4.5 پر 8,000 ان پٹ ٹوکنز اور Sonnet 5 پر 10,400 ٹوکنز پر مشتمل ہے۔ متن ایک ہی ہے لیکن شمار مختلف ہے۔ 4.7 اور اس کے بعد کے Claude ماڈلز ایک نیا ٹوکنائزر استعمال کرتے ہیں جو ایک ہی ان پٹ کے لیے تقریباً 30 فیصد زیادہ ٹوکنز بناتا ہے، جس کی تفصیل Anthropic نے اپنے پرائسنگ پیج پر دی ہے۔ جب بھی آپ کسی نئے ماڈل کا موازنہ پرانے ماڈل سے صرف فی ملین ٹوکنز کی قیمت کی بنیاد پر کریں تو اس فرق کو مدنظر رکھیں۔
اگست 2026 تک کی لسٹ پرائسز یہ ہیں: Haiku 4.5 کی قیمت 1 ڈالر فی ملین ان پٹ ٹوکنز اور 5 ڈالر فی ملین آؤٹ پٹ ٹوکنز ہے۔ Sonnet 5 کی قیمت 31 اگست 2026 تک جاری رہنے والی تعارفی پرائسنگ کے تحت 2 ڈالر اور 10 ڈالر ہے، جس کے بعد یہ 3 ڈالر اور 15 ڈالر ہو جائے گی۔ Opus 5 کی قیمت 5 ڈالر اور 25 ڈالر ہے۔
The data behind this chart
[
{
"label": "Haiku 4.5",
"cost_per_pr_cents": 1.4,
"cost_200_prs_usd": "2.80"
},
{
"label": "Sonnet 5",
"cost_per_pr_cents": 3.64,
"cost_200_prs_usd": "7.28"
},
{
"label": "Opus 5",
"cost_per_pr_cents": 9.1,
"cost_200_prs_usd": "18.20"
}
]یہ Haiku 4.5 پر فی pull request 1.4 سینٹ اور Opus 5 پر 9.1 سینٹ بنتی ہے۔ ایک ٹیم جو ماہانہ 200 pull requests مرج کرتی ہے، وہ Haiku 4.5 پر تقریباً $2.80، Sonnet 5 پر $7.28، یا Opus 5 پر $18.20 ادا کرتی ہے۔ 1 ستمبر 2026 سے، Sonnet 5 کی قیمت کو 1.5 سے ضرب دیں۔
دو عوامل اصل بل کو اس تخمینے سے اوپر لے جاتے ہیں۔ synchronize ہر push پر ریویو کو ٹرگر کرتا ہے، لہذا ایک فعال برانچ جس پر آٹھ pushes ہوں، آٹھ ریویوز کی قیمت ادا کرے گی، اور concurrency رول صرف تب مدد کرتا ہے جب pushes ایک دوسرے کے قریب ہوں۔ یہ اعداد و شمار اس مفروضے پر مبنی ہیں کہ path filters درست کام کر رہے ہیں: ایک غیر فلٹر شدہ lock file اکیلے ہی ان پٹ کو دوگنا کر سکتی ہے۔
Prompt caching یہاں مدد نہیں کرتی۔ cached prefix کو کالز کے درمیان بائٹ کے لحاظ سے یکساں ہونا چاہیے، اور diff ہر بار مختلف ہوتا ہے۔ سسٹم پرامپٹ ہی واحد مستحکم حصہ ہے، اور یہ کم از کم قابل کیش لمبائی سے بہت چھوٹا ہے۔ عمومی اصول کے لیے، جب prompt caching فائدہ مند ہو دیکھیں، اور اوپر دیے گئے تین ماڈلز میں سے انتخاب کرنے کے لیے، کس کام کے لیے کون سا Claude ماڈل استعمال کریں دیکھیں۔
اسے آن کرنے سے پہلے اپنے diffs کی پیمائش کریں
payload بننے کے بعد یہ لائن شامل کریں، پھر پچھلے مہینے کی چند pull requests پر اسکرپٹ کو دستی طور پر چلائیں:
print(client.messages.count_tokens(
model=MODEL, system=SYSTEM, messages=[{"role": "user", "content": payload}]
).input_tokens)کاؤنٹ اینڈ پوائنٹ ماڈل کو نہیں چلاتا، اس لیے یہ ان پٹ یا آؤٹ پٹ ٹوکنز استعمال نہیں کرتا، اور یہ اسی ماڈل کا ٹوکنائزر استعمال کرتا ہے جس کا آپ نام لیتے ہیں۔ اسے اپنی ریپوزٹری کی دس حقیقی pull requests پر چلائیں اور اوسط (mean) کے بجائے میڈین (median) لیں، تاکہ ایک بہت بڑی مائیگریشن آپ کے تخمینے کو متاثر نہ کرے۔
ریویو بوٹس کو میوٹ کیوں کیا جاتا ہے، اور اس سے کیسے بچا جائے
دو رویے ان بوٹس پر اعتماد کو ختم کر دیتے ہیں، اور ان دونوں کا حل اوپر دیے گئے کوڈ میں موجود ہے۔
سب کچھ ایک ساتھ ریویو کرنا۔ جو بوٹ چالیس کمنٹس چھوڑتا ہے، ان میں سے ایک بھی نہیں پڑھا جاتا۔ شدت کی حد (severity threshold) اور دس کمنٹس کی حد (cap) شائستگی کے لیے نہیں ہیں، بلکہ یہ وہ چیزیں ہیں جو حقیقی نتائج کو نمایاں رکھتی ہیں۔ کمنٹس کو محدود کرنے سے پہلے شدت کے لحاظ سے ترتیب دینے کا مطلب یہ ہے کہ حد لاگو ہونے پر غیر اہم نتائج خارج ہوں گے، نہ کہ کوئی بے ترتیب دس نتائج۔
کسی ایسی چیز پر اعتماد کے ساتھ تبصرہ کرنا جسے وہ چیک نہیں کر سکتا۔ یہ وہ رویہ ہے جس کی وجہ سے انجینئرز اسے ہمیشہ کے لیے بند کر دیتے ہیں۔ ایک ماڈل جسے 40,000 لائنوں کے کوڈ بیس میں سے 200 لائنیں دکھائی جائیں، وہ پھر بھی یہ لکھے گا کہ "یہ redis_client.py میں کیش ان ویلیڈیشن کو توڑتا ہے" حالانکہ اس نے وہ فائل کبھی نہیں دیکھی۔ سسٹم پرامپٹ سادہ زبان میں اس کی حوصلہ شکنی کرتا ہے: صرف ان نقائص کی اطلاع دیں جو دکھائی گئی لائنوں میں نظر آئیں، اور کسی بھی ایسی چیز کو چھوڑ دیں جس کے بارے میں آپ کو یقین نہ ہو۔ ناکامی کا براہِ راست نام لینا، عمومی درستگی کا مطالبہ کرنے سے بہتر کام کرتا ہے، اور ماڈل کو یہ بتانا کہ خالی نتیجہ آنا معمول کی بات ہے، اسے دو لائنوں کی تبدیلی پر کچھ بھی من گھڑت کہنے سے روکتا ہے۔
ریویو کو COMMENT کے طور پر پوسٹ کریں، کبھی بھی REQUEST_CHANGES کے طور پر نہیں۔ ماڈل کی رائے کو مرج (merge) کو روکنے کے قابل نہیں ہونا چاہیے، اور جس لمحے ایسا ہوتا ہے، ڈیڈ لائن کا شکار کوئی بھی شخص بحث کرنے کے بجائے پورے ورک فلو کو ہی ہٹا دے گا۔
ناکامی کے طریقے اور وہ اسٹرنگز جو آپ کو نظر آئیں گی
جائزہ پوسٹ کرتے وقت HTTP 422 کا ایرر۔ gh پرنٹ کرتا ہے gh: Unprocessable Entity (HTTP 422) اور ریسپانس باڈی فیلڈ کا نام بتاتی ہے: Pull request review thread line must be part of the diff۔ GitHub اس تبصرے کو اینکر نہیں کر سکتا۔ اس کی عام وجوہات میں ماڈل کی طرف سے خود ساختہ لائن نمبر، کوئی ایسا path جس میں اب بھی b/ کا سابقہ موجود ہو، یا کسی ہٹائی گئی لائن پر تبصرہ شامل ہے، جس کے لیے side کو RIGHT کے بجائے LEFT پر سیٹ کرنے کی ضرورت ہوتی ہے۔ پوسٹ کرنے سے پہلے ریویو JSON کو پرنٹ کریں اور ایک تبصرے کو دستی طور پر diff کے ساتھ چیک کریں۔
ماڈل API سے invalid x-api-key۔ یہ مرحلہ پہلی messages.create کال پر ناکام ہو جاتا ہے۔ یا تو ریپوزٹری پر ANTHROPIC_API_KEY سیکرٹ سیٹ نہیں ہے، یا پھر پل ریکویسٹ کسی فورک (fork) سے آئی ہے جس کی وجہ سے Actions نے کوئی سیکرٹ پاس نہیں کیا۔ if: لائن میں موجود فورک گارڈ کو اسے اسکیپ کر دینا چاہیے تھا، لہذا پہلے اس لائن کو چیک کریں۔
gh: Resource not accessible by integration (HTTP 403)۔ جاب ٹوکن پل ریکویسٹ پر کچھ لکھ نہیں سکتا۔ permissions: بلاک میں pull-requests: write کا اضافہ کریں۔ اگر یہ پہلے سے موجود ہے تو Settings، پھر Actions، اور پھر General میں دیکھیں، جہاں آرگنائزیشن کی پالیسی ورک فلو ٹوکن کی اجازتوں کو محدود کر سکتی ہے۔
json.decoder.JSONDecodeError۔ ماڈل نے قابلِ تجزیہ (parseable) JSON واپس نہیں کیا۔ اس کی عام وجہ وہ ریسپانس ہے جو ٹوکن کی حد تک پہنچ کر آبجیکٹ کے درمیان میں ہی رک گیا۔ لاگ لائن بالکل اسی کے لیے stop_reason پرنٹ کرتی ہے: max_tokens کی ویلیو کا مطلب ہے کہ max_tokens کو بڑھائیں یا MAX_COMMENTS کو کم کریں۔
ورک فلو کبھی نہیں چلتا۔ gh run list پل ریکویسٹ کے لیے کچھ نہیں دکھاتا۔ چیک کریں کہ paths-ignore نے تمام تبدیل شدہ فائلوں کو فلٹر تو نہیں کر دیا، پھر فورک گارڈ اور لیبل گارڈ کو دیکھیں، اور پھر یہ کہ آیا VPS پر sudo systemctl status 'actions.runner.*' کے ساتھ رنر فعال ہے۔ آف لائن رنر جاب کو بغیر کسی ایرر میسج کے قطار (queue) میں چھوڑ دیتا ہے۔
ہر ریویو خالی واپس آتا ہے۔ ایک رن کے لیے MIN_SEVERITY کو low پر سیٹ کریں۔ اگر نتائج ظاہر ہوتے ہیں، تو تھریش ہولڈ اپنا کام کر رہا ہے۔ اگر کچھ ظاہر نہیں ہوتا، تو payload کو پرنٹ کریں اور تصدیق کریں کہ فلٹرز نے پورے diff کو ہٹا تو نہیں دیا ہے۔
اسے اپنے دیگر ایجنٹس کے ساتھ چلانا
ریویور (reviewer) چھوٹا ہے، اس لیے اسے اس سرور پر ڈالنا پرکشش لگتا ہے جہاں پہلے ہی سب کچھ چل رہا ہے۔ اگر ریپوزٹری (repository) اہم ہے تو اسے الگ رکھیں۔ یہ عمل ایک ایسا ٹوکن رکھتا ہے جو آپ کے کوڈ پر تبصرہ کر سکتا ہے اور ایک ایسی کلید (key) جو آپ کے پیسے خرچ کر سکتی ہے، اور ایک self-hosted رنر (runner) ڈیزائن کے لحاظ سے ایسی جگہ ہے جہاں ورک فلو کوڈ ایگزیکیوٹ ہوتا ہے۔ ایک مخصوص غیر مراعات یافتہ اکاؤنٹ جس کے پاس sudo کے حقوق نہ ہوں، اور جو ایسے میزبان (host) پر ہو جہاں کچھ اور نہ چل رہا ہو، بنیادی ضرورت ہے۔ اگر آپ ایسے انٹرایکٹو ایجنٹس بھی چلاتے ہیں جو کوڈ چیک آؤٹ کرتے ہیں، تو فی ایجنٹ ایک ڈسپوزایبل VM وہ طریقہ کار ہے جو کارآمد رہتا ہے، اور VPS پر کوڈنگ ایجنٹ چلانا عمومی سیٹ اپ کا احاطہ کرتا ہے۔ اگر Anthropic API آپ کے لیے نیا ہے، تو VPS پر پہلی Claude API ایپ اس سے شروع کرنے کے لیے ایک چھوٹی جگہ ہے۔
FAQ
کیا AI PR ریویو ایجنٹ کو میرے ریپوزٹری تک write رسائی درکار ہے؟
نہیں۔ اسے ریویو پوسٹ کرنے کے لیے pull-requests: write اور diff حاصل کرنے کے لیے contents: read کی ضرورت ہوتی ہے۔ یہ مکمل فہرست ہے، اور آپ اسے ورک فلو کے permissions: بلاک میں سیٹ کرتے ہیں، جو فی جاب GITHUB_TOKEN کی صلاحیتوں کو محدود کرتا ہے۔ ان دو لائنوں کے ساتھ ایجنٹ pull request پر تبصرہ تو کر سکتا ہے لیکن commit پش یا برانچ مرج نہیں کر سکتا۔ ریویوز کو REQUEST_CHANGES کے بجائے event: COMMENT کے ساتھ پوسٹ کریں تاکہ یہ مرج کو بلاک بھی نہ کر سکے۔
میرا ریویو کمنٹ HTTP 422 کے ساتھ کیوں ناکام ہو جاتا ہے؟
GitHub ایک ان لائن ریویو کمنٹ صرف اسی لائن پر قبول کرتا ہے جو pull request diff کا حصہ ہو، اور ایسا نہ ہونے پر Pull request review thread line must be part of the diff واپس کرتا ہے۔ چیک کریں کہ path ریپوزٹری کے لحاظ سے ہو اور اس میں diff ہیڈر کا کوئی b/ پریفکس نہ ہو، اور یہ کہ لائن نمبر اس فائل کے ہنک (hunk) کے اندر موجود ہو۔ side کو شامل شدہ یا غیر تبدیل شدہ لائن کے لیے RIGHT اور ہٹائی گئی لائن کے لیے LEFT ہونا چاہیے۔ ماڈل کو بھیجنے سے پہلے diff کی ہر لائن کے ساتھ اس کا نیا فائل لائن نمبر لگانے سے ماڈل خود سے نمبر بنانے سے رک جاتا ہے۔
کیا میں اسے عوامی ریپوزٹری پر چلا سکتا ہوں جس میں forks سے pull requests آتی ہوں؟
اس ڈیزائن کے ساتھ نہیں۔ GitHub فورک سے شروع ہونے والے ورک فلو کو secrets فراہم نہیں کرتا، اس لیے ماڈل کی (key) غائب ہوتی ہے اور رن ناکام ہو جاتا ہے۔ GitHub یہ بھی کہتا ہے کہ self-hosted رنرز کو "عوامی ریپوزٹریز کے لیے تقریباً کبھی استعمال نہیں کیا جانا چاہیے"، کیونکہ کوئی بھی ایسی pull request کھول سکتا ہے جس سے آپ کی مشین پر کوڈ چل جائے۔ عوامی پروجیکٹ کے لیے، یا تو ریویور کو صرف ریپوزٹری پر پش کی گئی برانچز تک محدود رکھیں، جو کہ if: گارڈ کا کام ہے، یا ریویو کے مرحلے کو GitHub-hosted رنر پر منتقل کر دیں اور یہ تسلیم کریں کہ diff آپ کے اپنے انفراسٹرکچر سے باہر جا رہا ہے۔
مجھے pull request ریویو کے لیے کون سا ماڈل استعمال کرنا چاہیے؟
Haiku 4.5 سے شروعات کریں۔ ڈیفیکٹ کی اقسام کی ایک مقررہ فہرست کے خلاف محدود diff کو پڑھنا کوئی مشکل استدلالی مسئلہ نہیں ہے، اور سب سے سستا ماڈل ماہانہ بل کو ایسی سطح پر رکھتا ہے جس پر کوئی بحث نہیں کرتا۔ اگر آپ کو لگے کہ یہ آپ کی زبان یا فریم ورک میں حقیقی بگز کو نظر انداز کر رہا ہے تو Sonnet 5 پر منتقل ہوں، اور مفروضے کے بجائے اس کی پیمائش کریں۔ Opus 5 تینوں میں سب سے مہنگا ہے، جس کا جواز ہر فیچر برانچ پر ہر پش کے مقابلے میں ریلیز برانچ پر دینا زیادہ آسان ہے۔