بالنسبة لاختيار نموذج كود Claude، استخدم «هايكو» (Haiku) للتغييرات الصغيرة التي يمكن اختبارها، و«سونيت» (Sonnet) للتنفيذ الروتيني، و«أوبوس» (Opus) للأعمال التي تنطوي على قدر أكبر من الغموض أو تتطلب قدرًا أكبر من الاستدلال. توصي شركة Anthropic حاليًا باستخدام Opus 5.5 كنقطة انطلاق عامة؛ وتحدد مهامك، وحجم عبء المراجعة، وميزانيتك ما إذا كان النموذج الأصغر حجمًا هو الخيار الأنسب.
لتغيير النماذج، افتح /النموذج. الصحافة أدخل لحفظ إعداد افتراضي، أو s لتغيير الإعدادات في الجلسة الحالية فقط. للتشغيل لمرة واحدة، استخدم كلود -- طراز أوبوس. تختلف مدة استمرار هذه الخيارات، لذا فإن تحديد المدة التي يجب أن يستمر فيها التبديل لا يقل أهمية عن اختيار النموذج نفسه.
يتضمن عمل البرمجة أيضًا البحث والتوضيحات والتوثيق والأصول الإبداعية. تجمع منصة GlobalGPT بين نماذج الذكاء الاصطناعي الرائدة والعديد من أدوات الذكاء الاصطناعي في لوحة تحكم واحدة بأسعار معقولة، بينما تعمل واجهة سطر الأوامر (CLI) الخاصة بها على ربط تلك الأدوات بواجهة المحطة الطرفية وسير عمل التطوير. قم ببناء سير عملك باستخدام GlobalGPT للحفاظ على ترابط تلك المهام ذات الصلة أثناء انتقالك من مرحلة الفكرة إلى مرحلة الكود العامل.

ما هو النموذج الذي يجب استخدامه في كود Claude؟
ابدأ بشكل العمل. فالوظيفة القصيرة ذات المخرجات المتوقعة الواضحة تتطلب تقييمًا مختلفًا عن الخلل المتقطع الذي يمتد عبر عدة خدمات. والسؤال المفيد هنا هو: هل يمكن للنموذج أن يُحدث تغييرًا مقبولًا من خلال مراجعة وتكرار معقولين؟.
| الطراز الحالي | ما الذي صُمم من أجله | مهمة عملية للبدء |
|---|---|---|
| كلود هايكو 4.5 | عمل سريع وفعال في إطار عقد محدود النطاق | إعادة هيكلة بسيطة، أو تحويل متكرر، أو حالة اختبار بسيطة |
| كلود سونيت 5 | توازن بين الذكاء والسرعة في أعمال البرمجة اليومية | ميزة ذات أنماط محددة، أو إصلاح روتيني لخلل، أو شرح للكود |
| Claude Opus 5.5 | التفكير المعقد وأعمال الترميز الفاعلي الأطول | فشل غامض، أو مفاضلة معمارية، أو تغيير مصحوب بقيود متفاعلة |
| Claude Fable 5.1 | التحقيقات الأصعب والأطول مدةً | تصعيد عندما لا يزال العمل الصعب يتفوق على مستوى الجهد الأكبر الذي يبذله فريق «أوبوس» |
يُعد «Haiku» خيارًا جديرًا بالتقييم عندما تكون الأخطاء سهلة الكشف والتغييرات طفيفة. أما «Sonnet» فهو خيار معقول عندما يتطلب التنفيذ اليومي مزيدًا من السياق والتقدير. ويستحق «Opus» النظر فيه عندما يشكل فهم المشكلة جزءًا جوهريًّا من المهمة. أما «Fable» فيستحق قرارًا منفصلاً بالنسبة للأعمال الصعبة؛ فقد كان خارج نطاق اختبار البرمجة الذي شمل النماذج الثلاثة.
هذه مجرد نقاط انطلاق، وليست ضمانات تلقائية للجودة. فقد تصبح الاستجابة الأرخص تكلفة باهظة إذا تطلبت عدة مراجعات أو مراجعة شاملة. وعلى العكس من ذلك، قد لا يضيف النموذج الأكبر حجمًا سوى القليل إلى تحويل بسيط اجتاز بالفعل فحوصاتك. للاستخدام الأوسع نطاقًا خارج نطاق كود Claude،, قارن بين عائلات طرازات Claude.

إن نظرة عامة رسمية على الطراز تسرد نوافذ السياق ذات 1 مليون رمز لـ Fable 5.1 و Opus 5.5 و Sonnet 5، و 200 ألف رمز لـ Haiku 4.5. هذه هي مواصفات واجهة برمجة التطبيقات (API). يحدد مزود كود Claude الخاص بك وحسابك وتكوينك خيارات السياق المتاحة في الجلسة. وتُعد النافذة الكبيرة مفيدة للسياق الضروري؛ لكن ملؤها بمواد غير ذات صلة لا يزال يشكل عبئًا على النموذج.
كيفية تبديل النماذج خلال جلسة واحدة
في إطار جلسة تفاعلية، تم تنظيم /النموذج لفتح أداة اختيار الطراز. حدد صف الطراز واضغط على s إذا كان هذا تعديلاً لمرة واحدة. الضغط على أدخل يحفظ التحديد كإعداد افتراضي للمستخدم. عند الكتابة /نموذج السوناتة كما أنه يحفظه مباشرةً في الإصدارات الحالية.
اختر الأمر وفقًا للمدة التي يجب أن يستمر فيها التغيير
| هدفك | ماذا تفعل | هل تم حفظ التغييرات الافتراضية؟ |
|---|---|---|
| تبديل هذه الجلسة التفاعلية فقط | افتح /النموذج, ، حدد طرازًا، ثم اضغط على s. | لا. |
| حفظ الطراز المفضل | افتح /النموذج واضغط على أدخل, ، أو اكتب /نموذج السوناتة. | نعم، عندما تكون إعدادات المستخدم قابلة للكتابة. |
| ابدأ جلسة واحدة على Opus | الإطلاق كلود -- طراز أوبوس. | لا. |
| العودة إلى الإعداد الافتراضي لوقت التشغيل الخاص بالحساب | الاستخدام /النموذج الافتراضي. | يلغي تجاوز الإعدادات النموذجية؛ وقد يعتمد الإعداد الافتراضي الذي تم تحديده على إعدادات الحساب والمؤسسة. |
| التحقق من النموذج النشط | تشغيل /الحالة. | لا. |
قد تؤثر إعدادات المشروع أو الإعدادات المُدارة، وتجاوزات البيئة، والجلسات المستأنفة على عملية التشغيل التالية. تحقق من النموذج النشط عندما تكون قابلية التكرار أمرًا مهمًا.
في حالة جلسة محطة طرفية جديدة، اختر النموذج عند بدء التشغيل. تنطبق هذه الأوامر أيضًا عند تشغيل Claude Code في المحطة الطرفية المدمجة في VS Code. تحدد الأمثلة إصدارًا بشكل صريح:
claude --model claude-opus-5-5
claude --model claude-sonnet-5قم بتشغيل الأمر الخاص بالجلسة التي تريدها؛ والأمثلة الواردة هي خيارات بديلة. يمكنك تشغيل محطات طرفية مختلفة باستخدام --النموذج القيم دون الحاجة إلى تغيير الإعداد الافتراضي المشترك المحفوظ مرارًا وتكرارًا. استخدم /الحالة للتحقق من النموذج النشط.
أسماء العائلات مثل أوبوس و سونيت يمكن الانتقال إلى إصدارات أحدث. كما أن دقة هذه الملفات تعتمد على المزود: حيث تقوم واجهة برمجة التطبيقات (API) Anthropic حاليًا بتعيينها إلى Opus 5.5 وSonnet 5، في حين قد تختلف التعيينات باختلاف مزودي الخدمات السحابية. استخدم معرّف النموذج الكامل عندما تحتاج إلى تشغيل قابل للتكرار، وتحقق من الاسم الخاص بالمزود إذا كانت بيئتك تتطلب ذلك.
يتطلب برنامج Opus 5.5 إصدار Claude Code v2.1.280 أو أحدث؛ بينما يتطلب برنامج Sonnet 5 الإصدار v2.1.197 أو أحدث. الـ وثائق تكوين النموذج وهو المرجع الخاص بالأسماء المستعارة الحالية ومتطلبات الإصدار وتعيينات المزودين. الأوامر الواردة هنا هي أمثلة توضيحية في الوثيقة؛ أما قسم التدريب العملي لدينا فيقوم بتقييم الكود الذي تم إنشاؤه بشكل منفصل.

كيفية تعيين نموذج افتراضي
في حالة التخلف الشخصي عن السداد، اختر نموذجًا من /النموذج واضغط على أدخل, ، أو اكتب مجموعة محددة مثل /نموذج السوناتة. يكتب الرمز Claude نموذج الحقل الموجود في إعدادات المستخدم الخاصة بك عندما يكون هذا الملف قابلاً للكتابة. افتح جلسة عمل جديدة وتحقق من النموذج النشط الخاص بها لتأكيد النتيجة.
لا يتجاوز الإعداد المحفوظ جميع الإعدادات الأخرى. علامة بدء التشغيل،, ANTHROPIC_MODEL في بيئة العمل أو المشروع أو الإعدادات المُدارة، أو إذا كانت الإعدادات الافتراضية للمؤسسة مُعدة لتجاوز اختيارات المستخدم، فيمكن عند بدء التشغيل اختيار خيار آخر. كما قد تحتفظ الجلسة المستأنفة بالنموذج الموجود في النص المسجل الخاص بها. تحقق من معلومات بدء التشغيل و /الحالة قبل أن يفترض أن الفرصة قد ضاعت.
/النموذج الافتراضي تقوم بمسح التخصيصات التي أجريتها على النموذج وتعود إلى الإعدادات الافتراضية لوقت التشغيل الخاصة بحسابك وتكوينك. الكلمة الافتراضي هي قيمة إعادة تعيين، وليست اسم نموذج ثابت واحد من طراز Claude. ولا ينبغي استخدامها كمعرّف قابل للتكرار للنموذج.
لإجراء إعدادات متقدمة،, ANTHROPIC_DEFAULT_MODEL يوفر خيارًا بديلاً أوليًا بدءًا من الإصدار v2.1.236 فصاعدًا. ويظهر هذا الخيار أسفل خيارات التشغيل والبيئة والإعدادات والتنظيم الصريحة، ويتجاهل القيم التي تشمل هايكو و opusplan. بالنسبة للتفضيل الشخصي العادي، يُعد أداة الاختيار أسهل في التحقق منها. انظر إعدادات رمز Claude عندما يتعلق الأمر بتكوين مشترك.
ما الذي تفعله «أوبوسبلان» فعليًّا
opusplan يستخدم «أوبوس» في مرحلة التخطيط و«سونيت» في مرحلة التنفيذ. وتكمن جاذبية هذا النهج في بساطته: استخدام نموذج استدلالي أقوى لوضع الخطوات التفصيلية للنهج، ثم نموذج برمجة يومي لتنفيذ تلك الخطة.
تتبع شركة «أوبوسبلان» نمط العمل التالي
سلوك التوجيه الموثق. تعتمد الإصدارات المرتبطة بهذه الأسماء على مزود الخدمة والإعدادات الخاصة بك.
هذه قاعدة تعتمد على الوضع. فهي لا تفحص كل موجه وتختار تلقائيًّا المجموعة المناسبة الأقل تكلفة. ولا تزال تعيينات المزود وقيود الوصول سارية على النماذج الأساسية. كما أن الاسم لا يضمن تحقيق وفورات: فالتكلفة الإجمالية تعتمد على حجم التخطيط والتنفيذ والمراجعة ومعالجة السياق التي تتطلبها المهمة.
فكر في الأمر على أنه تغيير، حيث يستفيد التصميم من تحليل أعمق ويتبع التنفيذ خطة واضحة. أما بالنسبة لإصلاح بسيط، فقد لا يكون هذا التخطيط الإضافي ضروريًا.
ما الذي أظهرته مهمتا البرمجة هاتان؟
ولإعطاء فكرة ملموسة عن عملية اختيار النموذج، قمنا باختبار عملية إعادة هيكلة محدودة لنظام يونيكود وإصلاح لمحدد المسار، مع متطلبات متفاعلة. وتبين أن الفرق بين الدالة الصحيحة والتسليم الكامل المطلوب كان أكثر أهمية من مجرد النتيجة.
كيف قمنا باختبار نتائج البرمجة
في 28 سبتمبر 2026، قمنا بإعطاء نماذج Claude Haiku 4.5 وClaude Sonnet 5 وClaude Opus 5.5 نفس المهمتين في لغة جافا سكريبت، وصولاً إلى نموذج GlobalGPT. أرجع كل نموذج شفرة المصدر؛ وقمنا بفحص الشفرة وإجراء فحوصات محلية ثابتة. تأتي تعليمات تبديل النموذج الخاصة بـ Claude Code الواردة في أجزاء أخرى من هذا الدليل من وثائقها الرسمية.
كان لكل طلب حد أقصى للإخراج يبلغ 4,096 رمزًا، دون استخدام أي أدوات أو سجل محادثة، مع استخدام الإعدادات الافتراضية للنموذج. وقد احتفظنا بأول رد قابل للتقييم، بما في ذلك الردود غير المكتملة. وقد سُمح بإعادة محاولة واحدة لخطأ خدمة واحد في مهمة التوجيه الخاصة بـ «سونيت»؛ حيث أسفرت سبعة طلبات عن ستة مخرجات.
تقيس هاتان المهمتان الصغيرتان الرمز المُرجَع واكتمال التسليم. ولا تقيسان تنفيذ الوكيل في Claude Code، أو استكشاف المستودع، أو جلسة التطوير بأكملها. ولم يتم الإبلاغ عن التكاليف الفعلية بالدولار. ولا يُمكن استنتاج ترتيب عام للسرعة أو الموثوقية استنادًا إلى ملاحظة واحدة لكل مهمة.
عرض معرّفات الطرازات الدقيقة وشروط التقييم
المعرّفات المطلوبة: claude-haiku-4-5-20251001, كلود-سونيت-5, و كلود - أوبوس 5-5. أُجريت الاختبارات المحلية باستخدام Node.js الإصدار 24.19.0. وتستهدف التعليمات الإصدارات من Node.js 18 فما فوق؛ ولم يتم اختبار Node.js 18 نفسه.
تم تحديد ثماني مجموعات سلوكية لمهمة «الرخويات» واثنتي عشرة مجموعة لمهمة «محدد المسار». ويتم الإبلاغ عن الامتثال للتنسيق والتسليم الكامل بشكل منفصل. أما عملية مراجعة الكود التي تتم لاحقًا، فيتم تصنيفها بشكل منفصل ولا تؤثر على تلك الأرقام. ولم نقم بتعديل شفرة المصدر المُرجعة لجعلها مستوفية للشروط.
المهمة 1: الحفاظ على ترميز يونيكود في عملية إعادة هيكلة "السلاغ" الصغيرة
كان على الدالة أن تحتفظ بالأحرف غير اللاتينية وعلامات الجمع، وأن تعيد توحيد علامات التشكيل المُفككة، وأن تحافظ على سلوكها الحالي وفقًا لمعايير ASCII.
| نموذج | فحوصات السلوك | تنسيق الرد | إتمام عملية التسليم المطلوبة |
|---|---|---|---|
| كلود هايكو 4.5 | 8/8 | خطأ: غلاف Markdown | نعم: وظيفة كاملة |
| كلود سونيت 5 | 8/8 | البيانات المرسلة: JSON في صيغتها الأولية | نعم: وظيفة كاملة |
| Claude Opus 5.5 | 8/8 | البيانات المرسلة: JSON في صيغتها الأولية | نعم: وظيفة كاملة |
النتيجة الملاحظة: اجتازت الوظائف الثلاث جميعها نفس المجموعات الثماني. كما التزمت كل من «Sonnet» و«Opus» بمتطلبات الاستجابة بتنسيق JSON الخام. أما «Haiku» فقد غلفت استجابتها بتنسيق Markdown، ووصفت ملاحظة المراجعة الخاصة بها تقنية NFC خطأً بأنها «تفتيت الأحرف»؛ في حين أن كودها استدعى تقنية NFC بشكل صحيح.
فيما يتعلق بهذه عملية إعادة الهيكلة المحدودة، لم تؤدِ عملية التحديث إلى تحسين أداء الوظيفة التي خضعت للاختبار. ولا تزال متطلبات التنسيق ودقة الشرح المصاحب بحاجة إلى مراجعة.
عرض النص التوجيهي الكامل الذي تمت مشاركته ورمز الإدخال
SYSTEM
You are completing a bounded JavaScript coding task. Return only the requested JSON object with complete source files and brief review notes. Do not claim to have edited files, run commands, or used tools. You have no tool access. Do not include hidden reasoning.
USER
Implement the following Node.js 18+ CommonJS function from its current source. Return complete replacement source for src/slugify.js. Do not add dependencies, imports, I/O, or other files.
Required behavior:
1. slugify(value) returns an empty string for every non-string input.
2. Normalize string input to Unicode NFC, then lowercase it using JavaScript toLowerCase().
3. Preserve Unicode letters, numbers, and combining marks. Replace each consecutive run of all other characters with one ASCII hyphen.
4. Trim leading and trailing hyphens. Preserve the CommonJS export { slugify }.
5. Existing ASCII behavior must continue to work. Examples: Hello, World! -> hello-world; two words -> two-words; null -> empty string.
6. Additional examples: Café déjà vu -> café-déjà-vu; decomposed Cafe\u0301 -> café; 中文 测试 42 -> 中文-测试-42; **hi___there** -> hi-there; only punctuation or whitespace -> empty string.
Current src/slugify.js:
```javascript
function slugify(value) {
if (typeof value !== "string") return "";
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
module.exports = { slugify };
```
Return exactly one JSON object with this shape:
{"files":{"src/slugify.js":"complete JavaScript source as a JSON string"},"review_notes":["at most four brief implementation or assumption notes"]}
No Markdown wrapper. You are returning code for someone else to evaluate locally; do not say that tests passed or that you ran them.
اطلع على الرد الكامل لـ Claude Haiku 4.5 كما تم إرساله
```json
{
"files": {
"src/slugify.js": "function slugify(value) {\n if (typeof value !== \"string\") return \"\";\n \n // Normalize to NFC and lowercase\n const normalized = value.normalize(\"NFC\").toLowerCase();\n \n // Replace consecutive runs of non-letter, non-number, non-combining-mark characters with a single hyphen\n // Unicode letter categories: L* (Lu, Ll, Lt, Lm, Lo)\n // Unicode number categories: N* (Nd, Nl, No)\n // Unicode combining mark categories: M* (Mn, Mc, Me)\n const slugged = normalized.replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\");\n \n // Trim leading and trailing hyphens\n return slugged.replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };"
},
"review_notes": [
"Uses String.prototype.normalize(\"NFC\") to decompose composed characters like é into base+combining mark, then toLowerCase() applies to normalized string",
"Unicode property escapes (\\p{L}, \\p{N}, \\p{M}) with /u flag preserve all Unicode letters, numbers, and combining marks while replacing other consecutive characters with single hyphens",
"Existing ASCII behavior is preserved: punctuation and whitespace are replaced with hyphens, then leading/trailing hyphens are trimmed",
"Non-string inputs return empty string as required; normalized decomposed characters (e.g., Cafe\\u0301) are handled correctly by NFC normalization"
]
}
```عرض الرد الكامل لـ Claude Sonnet 5 كما تم إرساله
{"files":{"src/slugify.js":"function slugify(value) {\n if (typeof value !== \"string\") return \"\";\n return value\n .normalize(\"NFC\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };\n"},"review_notes":["Used Unicode property escapes (\\p{L}, \\p{N}, \\p{M}) with the u flag to preserve letters, numbers, and combining marks across scripts.","NFC normalization ensures decomposed sequences like Cafe + combining acute compose to café before lowercasing.","Runs of any other characters (punctuation, whitespace, symbols) collapse to a single hyphen, then leading/trailing hyphens are trimmed."]}اطلع على الرد الكامل لـ Claude Opus 5.5 كما تم إرساله
{"files":{"src/slugify.js":"function slugify(value) {\n if (typeof value !== \"string\") return \"\";\n return value\n .normalize(\"NFC\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\p{M}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nmodule.exports = { slugify };\n"},"review_notes":["The input is normalized to NFC before lowercasing, so decomposed input like Cafe\\u0301 composes to café.","A Unicode property regex with the u flag keeps letters (\\p{L}), numbers (\\p{N}) and combining marks (\\p{M}). Each run of any other characters, including underscores and whitespace, becomes one hyphen.","The explicit trim() was removed because the final step strips leading and trailing hyphens, which covers surrounding whitespace.","Lowercasing happens after NFC normalization, as the spec orders. toLowerCase can occasionally produce non-NFC output for rare characters, and it is not re-normalized here."]}المهمة 2: إصلاح محدد المسار وإرجاع اختباراته
كان على الدالة معالجة حالات تطابق البادئة الأطول، وأولوية الطرق، وحدود المقاطع، والقواعد غير الصحيحة، والمدخلات الثابتة. كما تضمنت الاستجابة المطلوبة ملف اختبار جديدًا.
| نموذج | فحوصات السلوك | تنسيق الرد | إتمام عملية التسليم المطلوبة |
|---|---|---|---|
| كلود هايكو 4.5 | 12/12 في الدالة الكاملة فقط | خطأ: JSON مقطوع | رقم: ملف الاختبار مقطوع |
| كلود سونيت 5 | 12/12 في الدالة الكاملة فقط | خطأ: JSON مقطوع | رقم: ملف الاختبار مقطوع |
| Claude Opus 5.5 | 12/12 في الدالة الكاملة فقط | خطأ: JSON مقطوع | رقم: ملف الاختبار مقطوع |
النتيجة الملاحظة: لم يكمل أي نموذج الاستجابة المطلوبة المكونة من ملفين ضمن الحد الأقصى المحدد للمخرجات المشتركة. توقفت النماذج الثلاثة جميعها عند 4,096 رمز إكمال مُبلغ عنه. وكان كل منها قد أعاد بالفعل دالة توجيه كاملة، اجتازت المجموعات الاثنتي عشرة للسلوك الثابت؛ أما ملفات الاختبار الجديدة فكانت غير مكتملة ولم يتم تنفيذها.
لقد استخرجنا فقط كل سلسلة من شفرة المصدر المغلقة بالكامل. أما الردود الأصلية المقتطعة فتظل كما هي أدناه. ولذلك، فإن الحصول على درجة نجاح في الدالة لا يعني أن الطلب بأكمله قد نجح.
نتيجة إضافية لمراجعة الكود: تعادل في الجذر المتساوي
بعد إجراء الفحوصات المعتادة، لاحظنا أن «هايكو» قد عادت إلى حالتها الطبيعية /// إلى سلسلة فارغة بدلاً من /. استخدم فحص محلي منفصل قاعدتين من نوع GET، مع البادئات /// و /, ، مستهدفًا أولاً و ثانيًا بهذا الترتيب. بالنسبة لـ /أي شيء, ، وينص العقد على أولاً.
عودة «هايكو» ثانيًا; ؛ عودة «سونيت» و«أوبوس» أولاً. وقد تم إجراء هذا الفحص الإضافي بناءً على نتائج مراجعة الكود، وهو منفصل عن الفحوصات الاثني عشر التي تم تحديدها قبل بدء الاختبار. ولا يؤثر هذا الفحص على درجات تلك الفحوصات.
واجهت «سونيت» خطأً واحدًا في الخدمة قبل أن تُرجع محاولة الإعادة المسموح بها الرد المقتطع المعروض. ويُعد هذا الفشل في الخدمة أمرًا منفصلاً عن جودة الكود. ولم تتلق أي نموذج محاولة أخرى لتحسين إجابتها أو إكمالها.
عرض النص التوجيهي الكامل الذي تمت مشاركته ورمز الإدخال
SYSTEM
You are completing a bounded JavaScript coding task. Return only the requested JSON object with complete source files and brief review notes. Do not claim to have edited files, run commands, or used tools. You have no tool access. Do not include hidden reasoning.
USER
Repair a Node.js 18+ CommonJS route selector. Return complete replacement src/router.js and an additional test/router-added.test.js using node:test and node:assert/strict. Do not add dependencies or edit existing tests. Do not use I/O other than the test runner, network, child processes, timers, or any files beyond the two requested outputs.
Contract for chooseRoute(request, rules):
1. Invalid request or rules returns null, without throwing. Request must be a non-null, non-array object; method must be a nonempty ASCII-letter string; path must be a string beginning with /. Rules must be an array.
2. Before matching, remove the request path's query and fragment starting at the earliest ? or #. Do not URL-decode or collapse interior slashes.
3. Ignore invalid rules. A valid rule is a non-null, non-array object with method '*' or a nonempty ASCII-letter string, prefix a string starting with / containing neither ? nor #, and target a nonempty string.
4. Compare methods case-insensitively. Prefer any matching exact-method rule over all wildcard-method '*' rules, even if a wildcard has a longer prefix. Use wildcard rules only if no exact-method route matches the path.
5. Remove trailing slashes from a rule prefix, except that an all-slash prefix normalizes to /. A prefix matches the path only at a segment boundary: /api matches /api and /api/users, never /apix. Root / matches every valid request path.
6. Within the chosen method group, choose the longest normalized matching prefix. On equal prefix length, choose the earlier rule in the original input array. Return its target. Return null if none matches.
7. Do not mutate the request, rules array, or rule objects, including order or prefix strings. Preserve module.exports = { chooseRoute }.
8. Add tests for segment boundaries, query/fragment removal, method precedence, ties, malformed inputs and immutability. Existing simple GET /api/users and POST-no-match cases must still pass.
Current src/router.js:
```javascript
function chooseRoute(request, rules) {
if (!request || !Array.isArray(rules)) return null;
for (const rule of rules) {
if (request.method === rule.method && request.path.startsWith(rule.prefix)) {
return rule.target;
}
}
return null;
}
module.exports = { chooseRoute };
```
Existing regression cases (unchanged): rules [{method:'GET',prefix:'/api',target:'api'},{method:'GET',prefix:'/api/admin',target:'admin'}]; GET /api/users returns 'api'; POST /api/users returns null.
Return exactly one JSON object with this shape:
{"files":{"src/router.js":"complete JavaScript source as a JSON string","test/router-added.test.js":"complete JavaScript tests as a JSON string"},"review_notes":["at most four brief implementation or assumption notes"]}
No Markdown wrapper. You are returning code for someone else to evaluate locally; do not say you edited files, ran tests, or observed passing results.
اطلع على الرد الكامل لـ Claude Haiku 4.5 كما تم إرساله
هذا هو النص الكامل للرد الذي تم تلقيه. ينتهي النص في منتصف الإخراج لأنه وصل إلى الحد الأقصى؛ ولم يتم استكمال المحتوى الناقص.
```json
{
"files": {
"src/router.js": "function chooseRoute(request, rules) {\n // Validate request\n if (request === null || Array.isArray(request) || typeof request !== 'object') {\n return null;\n }\n if (typeof request.method !== 'string' || request.method.length === 0 || !/^[a-zA-Z]+$/.test(request.method)) {\n return null;\n }\n if (typeof request.path !== 'string' || !request.path.startsWith('/')) {\n return null;\n }\n\n // Validate rules\n if (!Array.isArray(rules)) {\n return null;\n }\n\n // Remove query and fragment from path\n const cleanPath = request.path.split(/[?#]/)[0];\n\n // Filter and validate rules\n const validRules = [];\n for (const rule of rules) {\n if (rule === null || Array.isArray(rule) || typeof rule !== 'object') {\n continue;\n }\n \n const method = rule.method;\n if (typeof method !== 'string' || (method !== '*' && (method.length === 0 || !/^[a-zA-Z]+$/.test(method)))) {\n continue;\n }\n \n const prefix = rule.prefix;\n if (typeof prefix !== 'string' || !prefix.startsWith('/') || prefix.includes('?') || prefix.includes('#')) {\n continue;\n }\n \n const target = rule.target;\n if (typeof target !== 'string' || target.length === 0) {\n continue;\n }\n \n validRules.push(rule);\n }\n\n // Normalize prefixes and partition into exact-method and wildcard groups\n const exactMethodRules = [];\n const wildcardRules = [];\n \n for (const rule of validRules) {\n const normalizedPrefix = rule.prefix === '/' ? '/' : rule.prefix.replace(/\/+$/, '');\n \n const entry = {\n originalRule: rule,\n normalizedPrefix: normalizedPrefix,\n method: rule.method\n };\n \n if (rule.method === '*') {\n wildcardRules.push(entry);\n } else {\n exactMethodRules.push(entry);\n }\n }\n\n const requestMethod = request.method.toUpperCase();\n\n // Helper to check if a prefix matches the path at a segment boundary\n function prefixMatches(prefix, path) {\n if (prefix === '/') {\n return true;\n }\n if (!path.startsWith(prefix)) {\n return false;\n }\n // Check segment boundary: next character must be '/' or end of string\n const nextChar = path[prefix.length];\n return nextChar === undefined || nextChar === '/';\n }\n\n // Try exact-method rules first\n let bestMatch = null;\n let bestLength = -1;\n \n for (const entry of exactMethodRules) {\n if (entry.method.toUpperCase() === requestMethod) {\n if (prefixMatches(entry.normalizedPrefix, cleanPath)) {\n if (entry.normalizedPrefix.length > bestLength) {\n bestMatch = entry.originalRule;\n bestLength = entry.normalizedPrefix.length;\n }\n }\n }\n }\n \n if (bestMatch !== null) {\n return bestMatch.target;\n }\n\n // Try wildcard rules only if no exact-method match\n bestMatch = null;\n bestLength = -1;\n \n for (const entry of wildcardRules) {\n if (prefixMatches(entry.normalizedPrefix, cleanPath)) {\n if (entry.normalizedPrefix.length > bestLength) {\n bestMatch = entry.originalRule;\n bestLength = entry.normalizedPrefix.length;\n }\n }\n }\n \n if (bestMatch !== null) {\n return bestMatch.target;\n }\n\n return null;\n}\n\nmodule.exports = { chooseRoute };",
"test/router-added.test.js": "const test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router.js');\n\n// Regression: existing test cases\ntest('GET /api/users matches /api prefix (exact method)', () => {\n const rules = [\n { method: 'GET', prefix: '/api', target: 'api' },\n { method: 'GET', prefix: '/api/admin', target: 'admin' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'api');\n});\n\ntest('POST /api/users returns null (no matching method)', () => {\n const rules = [\n { method: 'GET', prefix: '/api', target: 'api' },\n { method: 'GET', prefix: '/api/admin', target: 'admin' }\n ];\n assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, rules), null);\n});\n\n// Invalid request handling\ntest('null request returns null', () => {\n assert.equal(chooseRoute(null, []), null);\n});\n\ntest('array request returns null', () => {\n assert.equal(chooseRoute([], []), null);\n});\n\ntest('request with missing method returns null', () => {\n assert.equal(chooseRoute({ path: '/api' }, []), null);\n});\n\ntest('request with empty method returns null', () => {\n assert.equal(chooseRoute({ method: '', path: '/api' }, []), null);\n});\n\ntest('request with non-letter method returns null', () => {\n assert.equal(chooseRoute({ method: 'GET-POST', path: '/api' }, []), null);\n});\n\ntest('request with numeric method returns null', () => {\n assert.equal(chooseRoute({ method: '123', path: '/api' }, []), null);\n});\n\ntest('request with missing path returns null', () => {\n assert.equal(chooseRoute({ method: 'GET' }, []), null);\n});\n\ntest('request with path not starting with / returns null', () => {\n assert.equal(chooseRoute({ method: 'GET', path: 'api' }, []), null);\n});\n\ntest('null rules returns null', () => {\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, null), null);\n});\n\ntest('non-array rules returns null', () => {\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, { method: 'GET' }), null);\n});\n\n// Query and fragment removal\ntest('query string is removed before matching', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users?foo=bar' }, rules), 'found');\n});\n\ntest('fragment is removed before matching', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users#section' }, rules), 'found');\n});\n\ntest('earliest ? or # is used as boundary', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users?foo#bar' }, rules), 'found');\n});\n\ntest('fragment before query is used as boundary', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users#foo?bar' }, rules), 'found');\n});\n\n// Segment boundary matching\ntest('prefix /api matches /api/users at segment boundary', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('prefix /api does not match /apix (no segment boundary)', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n});\n\ntest('prefix /api matches /api exactly at segment boundary', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('root / matches any valid path', () => {\n const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/anything/here' }, rules), 'root');\n});\n\ntest('root / matches /', () => {\n const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/' }, rules), 'root');\n});\n\n// Prefix normalization (trailing slash removal)\ntest('trailing slash is removed from prefix', () => {\n const rules = [{ method: 'GET', prefix: '/api/', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('multiple trailing slashes are removed', () => {\n const rules = [{ method: 'GET', prefix: '/api///', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'found');\n});\n\ntest('all-slash prefix normalizes to single /', () => {\n const rules = [{ method: 'GET', prefix: '///', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\n// Method case-insensitivity\ntest('method comparison is case-insensitive', () => {\n const rules = [{ method: 'get', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('request method case does not affect matching', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'found' }];\n assert.equal(chooseRoute({ method: 'get', path: '/api' }, rules), 'found');\n});\n\n// Exact-method vs wildcard precedence\ntest('exact-method rule preferred over wildcard with longer prefix', () => {\n const rules = [\n { method: '*', prefix: '/api/admin/users', target: 'wildcard' },\n { method: 'GET', prefix: '/api', target: 'exact' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/test' }, rules), 'exact');\n});\n\ntest('wildcard rule used when no exact-method matches', () => {\n const rules = [\n { method: '*', prefix: '/api', target: 'wildcard' },\n { method: 'GET', prefix: '/other', target: 'exact' }\n ];\n assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, rules), 'wildcard');\n});\n\n// Longest prefix matching\ntest('longest matching prefix is chosen', () => {\n const rules = [\n { method: 'GET', prefix: '/api', target: 'short' },\n { method: 'GET', prefix: '/api/admin', target: 'long' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/users' }, rules), 'long');\n});\n\n// Tie-breaking (first rule wins on equal prefix length)\ntest('first rule wins on equal prefix length', () => {\n const rules = [\n { method: 'GET', prefix: '/api', target: 'first' },\n { method: 'GET', prefix: '/api', target: 'second' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'first');\n});\n\n// Invalid rules are ignored\ntest('null rule is ignored', () => {\n const rules = [\n null,\n { method: 'GET', prefix: '/api', target: 'found' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('array rule is ignored', () => {\n const rules = [\n [],\n { method: 'GET', prefix: '/api', target: 'found' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'found');\n});\n\ntest('rule with invalid method is ignored', () => {\n const rules = [\n { method: '', prefix: '/api', target: 'invalid' },\n { method: 'GET', prefix: '/api', target: 'valid' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with non-letter method (if not wildcard) is ignored', () => {\n const rules = [\n { method: 'G3T', prefix: '/api', target: 'invalid' },\n { method: 'GET', prefix: '/api', target: 'valid' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with prefix not starting with / is ignored', () => {\n const rules = [\n { method: 'GET', prefix: 'api', target: 'invalid' },\n { method: 'GET', prefix: '/api', target: 'valid' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with query in prefix is ignored', () => {\n const rules = [\n { method: 'GET', prefix: '/api?foo=bar', target: 'invalid' },\n { method: 'GET', prefix: '/api', target: 'valid' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'valid');\n});\n\ntest('rule with fragment in prefix is ignored', () => {\n const rules = [\n { method: 'GET', prefix: '/api#section', target: عرض الرد الكامل لـ Claude Sonnet 5 كما تم إرساله
هذا هو النص الكامل للرد الذي تم تلقيه. ينتهي النص في منتصف الإخراج لأنه وصل إلى الحد الأقصى؛ ولم يتم استكمال المحتوى الناقص.
{"files":{"src/router.js":"function isValidRequest(request) {\n return (\n request !== null &&\n typeof request === 'object' &&\n !Array.isArray(request) &&\n typeof request.method === 'string' &&\n request.method.length > 0 &&\n /^[A-Za-z]+$/.test(request.method) &&\n typeof request.path === 'string' &&\n request.path.startsWith('/')\n );\n}\n\nfunction isValidRule(rule) {\n if (rule === null || typeof rule !== 'object' || Array.isArray(rule)) {\n return false;\n }\n const { method, prefix, target } = rule;\n\n if (typeof method !== 'string') return false;\n if (method !== '*' && !(method.length > 0 && /^[A-Za-z]+$/.test(method))) {\n return false;\n }\n\n if (\n typeof prefix !== 'string' ||\n !prefix.startsWith('/') ||\n prefix.includes('?') ||\n prefix.includes('#')\n ) {\n return false;\n }\n\n if (typeof target !== 'string' || target.length === 0) return false;\n\n return true;\n}\n\nfunction normalizePrefix(prefix) {\n if (/^\/+$/.test(prefix)) return '/';\n return prefix.replace(/\/+$/, '');\n}\n\nfunction prefixMatches(normalizedPrefix, path) {\n if (normalizedPrefix === '/') return true;\n if (path === normalizedPrefix) return true;\n return path.startsWith(normalizedPrefix + '/');\n}\n\nfunction stripQueryAndFragment(path) {\n let cutIndex = path.length;\n for (let i = 0; i < path.length; i++) {\n const c = path[i];\n if (c === '?' || c === '#') {\n cutIndex = i;\n break;\n }\n }\n return path.slice(0, cutIndex);\n}\n\nfunction chooseRoute(request, rules) {\n if (!isValidRequest(request) || !Array.isArray(rules)) return null;\n\n const path = stripQueryAndFragment(request.path);\n const reqMethodLower = request.method.toLowerCase();\n\n let bestExact = null; // { len, idx, target }\n let bestWildcard = null;\n\n for (let idx = 0; idx < rules.length; idx++) {\n const rule = rules[idx];\n if (!isValidRule(rule)) continue;\n\n const normPrefix = normalizePrefix(rule.prefix);\n if (!prefixMatches(normPrefix, path)) continue;\n\n const len = normPrefix.length;\n\n if (rule.method === '*') {\n if (\n bestWildcard === null ||\n len > bestWildcard.len ||\n (len === bestWildcard.len && idx < bestWildcard.idx)\n ) {\n bestWildcard = { len, idx, target: rule.target };\n }\n } else if (rule.method.toLowerCase() === reqMethodLower) {\n if (\n bestExact === null ||\n len > bestExact.len ||\n (len === bestExact.len && idx < bestExact.idx)\n ) {\n bestExact = { len, idx, target: rule.target };\n }\n }\n }\n\n if (bestExact !== null) return bestExact.target;\n if (bestWildcard !== null) return bestWildcard.target;\n return null;\n}\n\nmodule.exports = { chooseRoute };\n","test/router-added.test.js":"const test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router');\n\ntest('segment boundary: prefix matches at slash boundary only', () => {\n const rules = [\n { method: 'GET', prefix: '/api', target: 'api' },\n ];\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api/users' }, rules),\n 'api'\n );\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api' }, rules),\n 'api'\n );\n assert.equal(\n chooseRoute({ method: 'GET', path: '/apix' }, rules),\n null\n );\n});\n\ntest('root prefix matches every valid path', () => {\n const rules = [{ method: 'GET', prefix: '/', target: 'root' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/' }, rules), 'root');\n assert.equal(\n chooseRoute({ method: 'GET', path: '/anything/here' }, rules),\n 'root'\n );\n});\n\ntest('trailing slashes in rule prefix are normalized', () => {\n const rules = [{ method: 'GET', prefix: '/api///', target: 'api' }];\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api/users' }, rules),\n 'api'\n );\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n});\n\ntest('all-slash prefix normalizes to /', () => {\n const rules = [{ method: 'GET', prefix: '////', target: 'root' }];\n assert.equal(\n chooseRoute({ method: 'GET', path: '/whatever' }, rules),\n 'root'\n );\n});\n\ntest('query and fragment are stripped before matching', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'api' }];\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api/users?x=1&y=2' }, rules),\n 'api'\n );\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api/users#section' }, rules),\n 'api'\n );\n assert.equal(\n chooseRoute({ method: 'GET', path: '/api/users?x=1#frag' }, rules),\n 'api'\n );\n});\n\ntest('query/fragment stripping does not decode or collapse slashes', () => {\n const rules = [{ method: 'GET', prefix: '/a//b', target: 'weird' }];\n assert.equal(\n chooseRoute({ method: 'GET', path: '/a//b?x=1' }, rules),اطلع على الرد الكامل لـ Claude Opus 5.5 كما تم إرساله
هذا هو النص الكامل للرد الذي تم تلقيه. ينتهي النص في منتصف الإخراج لأنه وصل إلى الحد الأقصى؛ ولم يتم استكمال المحتوى الناقص.
{"files":{"src/router.js":"'use strict';\n\nconst LETTERS = /^[A-Za-z]+$/;\n\nfunction isPlainObject(value) {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isMethodName(value) {\n return typeof value === 'string' && value.length > 0 && LETTERS.test(value);\n}\n\nfunction stripQueryAndFragment(path) {\n const q = path.indexOf('?');\n const h = path.indexOf('#');\n let cut = -1;\n if (q !== -1 && h !== -1) cut = Math.min(q, h);\n else if (q !== -1) cut = q;\n else if (h !== -1) cut = h;\n return cut === -1 ? path : path.slice(0, cut);\n}\n\nfunction normalizePrefix(prefix) {\n let end = prefix.length;\n while (end > 0 && prefix[end - 1] === '/') end -= 1;\n return end === 0 ? '/' : prefix.slice(0, end);\n}\n\nfunction prefixMatches(path, prefix) {\n if (prefix === '/') return true;\n return path === prefix || path.startsWith(prefix + '/');\n}\n\nfunction chooseRoute(request, rules) {\n try {\n if (!isPlainObject(request) || !Array.isArray(rules)) return null;\n const method = request.method;\n const rawPath = request.path;\n if (!isMethodName(method)) return null;\n if (typeof rawPath !== 'string' || rawPath[0] !== '/') return null;\n\n const path = stripQueryAndFragment(rawPath);\n const upperMethod = method.toUpperCase();\n\n let bestExact = null;\n let bestWildcard = null;\n\n const count = rules.length;\n for (let i = 0; i < count; i += 1) {\n const rule = rules[i];\n if (!isPlainObject(rule)) continue;\n const ruleMethod = rule.method;\n const prefix = rule.prefix;\n const target = rule.target;\n const isWildcard = ruleMethod === '*';\n if (!isWildcard && !isMethodName(ruleMethod)) continue;\n if (typeof prefix !== 'string' || prefix[0] !== '/') continue;\n if (prefix.indexOf('?') !== -1 || prefix.indexOf('#') !== -1) continue;\n if (typeof target !== 'string' || target.length === 0) continue;\n\n const normalized = normalizePrefix(prefix);\n if (!prefixMatches(path, normalized)) continue;\n\n const candidate = { length: normalized.length, target };\n if (isWildcard) {\n if (bestWildcard === null || candidate.length > bestWildcard.length) bestWildcard = candidate;\n } else if (ruleMethod.toUpperCase() === upperMethod) {\n if (bestExact === null || candidate.length > bestExact.length) bestExact = candidate;\n }\n }\n\n if (bestExact !== null) return bestExact.target;\n if (bestWildcard !== null) return bestWildcard.target;\n return null;\n } catch (err) {\n return null;\n }\n}\n\nmodule.exports = { chooseRoute };\n","test/router-added.test.js":"'use strict';\n\nconst test = require('node:test');\nconst assert = require('node:assert/strict');\nconst { chooseRoute } = require('../src/router.js');\n\nconst baseRules = [\n { method: 'GET', prefix: '/api', target: 'api' },\n { method: 'GET', prefix: '/api/admin', target: 'admin' }\n];\n\ntest('existing regression cases still hold', () => {\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, baseRules), 'api');\n assert.equal(chooseRoute({ method: 'POST', path: '/api/users' }, baseRules), null);\n});\n\ntest('longest prefix wins within method group', () => {\n assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/x' }, baseRules), 'admin');\n assert.equal(chooseRoute({ method: 'GET', path: '/api/admin' }, baseRules), 'admin');\n});\n\ntest('segment boundaries', () => {\n const rules = [{ method: 'GET', prefix: '/api', target: 'api' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/api/' }, rules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/api/users' }, rules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n assert.equal(chooseRoute({ method: 'GET', path: '/ap' }, rules), null);\n});\n\ntest('trailing slashes in prefixes are normalized, all-slash is root', () => {\n const rules = [{ method: 'GET', prefix: '/api///', target: 'api' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api' }, rules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/apix' }, rules), null);\n const rootRules = [{ method: 'GET', prefix: '///', target: 'root' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/' }, rootRules), 'root');\n assert.equal(chooseRoute({ method: 'GET', path: '/anything/else' }, rootRules), 'root');\n assert.equal(chooseRoute({ method: 'GET', path: '/x' }, [{ method: 'GET', prefix: '/', target: 'r' }]), 'r');\n});\n\ntest('query and fragment are removed at earliest ? or #', () => {\n assert.equal(chooseRoute({ method: 'GET', path: '/api?next=/api/admin' }, baseRules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/api#/admin' }, baseRules), 'api');\n assert.equal(chooseRoute({ method: 'GET', path: '/apix?/api' }, baseRules), null);\n assert.equal(chooseRoute({ method: 'GET', path: '/api/admin#frag?q' }, baseRules), 'admin');\n assert.equal(chooseRoute({ method: 'GET', path: '/api?q#/admin' }, baseRules), 'api');\n});\n\ntest('no URL decoding and no slash collapsing', () => {\n const rules = [{ method: 'GET', prefix: '/api/admin', target: 'admin' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/api%2Fadmin' }, rules), null);\n assert.equal(chooseRoute({ method: 'GET', path: '/api//admin' }, rules), null);\n assert.equal(chooseRoute({ method: 'GET', path: '/api//admin' }, baseRules), 'api');\n});\n\ntest('methods compare case-insensitively', () => {\n const rules = [{ method: 'get', prefix: '/a', target: 'lower' }];\n assert.equal(chooseRoute({ method: 'GET', path: '/a' }, rules), 'lower');\n assert.equal(chooseRoute({ method: 'gEt', path: '/a/b' }, rules), 'lower');\n});\n\ntest('exact method beats longer wildcard; wildcard used only as fallback', () => {\n const rules = [\n { method: '*', prefix: '/api/admin', target: 'wild-admin' },\n { method: 'GET', prefix: '/', target: 'get-root' },\n { method: 'POST', prefix: '/other', target: 'post-other' },\n { method: '*', prefix: '/api', target: 'wild-api' }\n ];\n assert.equal(chooseRoute({ method: 'GET', path: '/api/admin/x' }, rules), 'get-root');\n assert.equal(chooseRoute({ method: 'POST', path: '/api/admin/x' }, rules), 'wild-admin');\n assert.equal(chooseRoute({ method: 'POST', path: '/api/x' }, rules), 'wild-api');\n assert.equal(chooseRoute({ method: 'POST', path: '/other/1' }, rules), 'post-other');\n assert.equal(chooseRoute({ method: 'PUT', path: '/nothing' }, rules), null);\n});\n\ntest('ties go to earlier rule in input order', () => {\n const rules = [\n { method: 'GET', prefكان الحد الأقصى البالغ 4,096 رمزًا هو ميزانية المخرجات التي تم اختيارها لهذا الاختبار، وليس الحد الأقصى الموثق لأي نموذج. وتشير نتيجة التوجيه إلى ضرورة إجراء فحص عملي قبل تقديم طلب أكبر: تقدير حجم التنفيذ والاختبارات والتفسير الذي تتوقع الحصول عليه. قد يكون من المناسب تقسيم المخرجات أو تخصيص مساحة أكبر للمخرجات؛ ولم نقم بإعادة تشغيل هذه المخرجات باستخدام إعدادات مختلفة.
بالنسبة لمهمتك الخاصة، قم بتقييم ثلاثة أمور بشكل منفصل: ما إذا كانت الاستجابة كاملة، وما إذا كان الكود يجتاز اختبارات مفيدة، وما إذا كانت المراجعة تكشف عن ثغرات لم تكتشفها تلك الاختبارات. إن اجتياز اثني عشر اختبارًا للوظائف لم يجعل أيًا من الاستجابات المقتطعة المكونة من ملفين تسليمًا مكتملًا.
ما الذي يتغير في التكلفة بناءً على اختيار الطراز؟
Claude Code هي أداة البرمجة؛ أما Haiku وSonnet وOpus وFable فهي خيارات للنماذج المتاحة داخل البيئة المدعومة. وتعد طريقة المصادقة والدفع خيارًا آخر. ولا يؤدي تغيير النموذج إلى تحويل الاشتراك إلى حساب API أو إلغاء جميع حدود الاستخدام.
| مسار الفوترة | ما الذي يجب مشاهدته | كيفية تفسير اختيار النموذج |
|---|---|---|
| الوصول عبر الاشتراك في Claude | استخدام الخطة المشمولة وأي أرصدة استخدام تم تفعيلها بشكل منفصل | قد تختلف طريقة استهلاك الحصص بين الطرز المختلفة؛ يرجى التحقق من الطرز المتاحة في الباقة والحدود المحددة لها |
| الوصول إلى واجهة برمجة التطبيقات (API) Anthropic | رموز الإدخال/الإخراج، والتخزين المؤقت، ورسوم واجهة برمجة التطبيقات (API) المطبقة | استخدم قائمة الأسعار الحالية والاستخدام الفعلي، ثم قم بتأكيد الرسوم في لوحة التحكم الخاصة بالفوترة |
| مزود الخدمات السحابية أو البوابة | معرّفات المزود المدعومة، والأسعار، وسجلات الفوترة | قد لا يتطابق السعر المعلن في قائمة الأسعار لنظام Anthropic مع المبلغ الذي يتقاضاه مزود الخدمة الخاص بك |
في كود Claude،, /الاستخدام يعرض معلومات رمز الجلسة، وأشرطة استخدام الاشتراك (إن وجدت). ويُعد المبلغ المعبَّر عنه بالدولار تقديرًا يتم حسابه محليًّا استنادًا إلى الأسعار المدرجة في القائمة أو الأسعار المُعدَّة مسبقًا؛ وهو ليس فاتورة. بالنسبة لمشتركي الإصدارين Pro وMax، لا يعني هذا المبلغ الخاص بالجلسة أنه تمت إضافة فاتورة إضافية لخدمة واجهة برمجة التطبيقات (API) إلى الاشتراك. اقرأ المزيد حول Claude: مسارات التسعير والفوترة إذا كنت تفكر في طريقة الدفع.
أسعار API الحالية ومثال على الرمز المكافئ
الأسعار الأساسية العادية لـ Anthropic لكل مليون توكن إدخال/إخراج هي Haiku 4.5: $1/$5؛ Sonnet 5: $2/$10؛ أوبوس 5.5: $4/$20؛ Fable 5.1: $10/$50, ، تم التحقق من ذلك في 28 سبتمبر 2026. يُبقي الرسم البياني التالي حجم التداول للرمز ثابتًا حتى يسهل مقارنة الأرقام.
نفس حجم التوكن، أربع تكاليف مختلفة لواجهة برمجة التطبيقات (API)
مثال: 100,000 رمز إدخال غير مخزّن مؤقتًا + 10,000 رمز إخراج, ، باستخدام الأسعار القياسية لقائمة واجهة برمجة التطبيقات (API) الخاصة بشركة Anthropic، والتي تم التحقق منها في 28 سبتمبر 2026.
اطلع على الأسعار وطريقة الحساب
| نموذج | الإدخال / 1م | الإخراج / 1 م | المجموع في المثال |
|---|---|---|---|
| هايكو 4.5 | $1 | $5 | $0.15 |
| السوناتة الخامسة | $2 | $10 | $0.30 |
| الأوبوس 5.5 | $4 | $20 | $0.60 |
| Fable 5.1 | $10 | $50 | $1.50 |
الحساب: 0.1 × معدل الإدخال + 0.01 × معدل الإخراج. بالنسبة لـ Opus 5.5: 0.1 × $4 + 0.01 × $20 = $0.60.
هذا مثال توضيحي يستند إلى عدد ثابت من الرموز. تستهلك المهام الفعلية أعدادًا مختلفة من الرموز؛ لذا، فإن عمليات الكتابة والقراءة في ذاكرة التخزين المؤقت، ومحاولات إعادة التنفيذ، والأدوات، وفوترة المزود، وحدود الاشتراك تتطلب حسابًا منفصلاً.

تتمثل المهمة الحقيقية لـ «Claude Code» في سلسلة من الطلبات. فقد يتم إرسال السياق السابق مرة أخرى، أو قد يجري النموذج مزيدًا من التحليل، وقد تؤدي الأدوات أو الوكلاء الإضافيون إلى توليد المزيد من العمل. كما أن التخزين المؤقت للموجهات له معدلات كتابة وقراءة متميزة. ولذلك، فإن الشريط الأدنى في المثال لا يمثل أقل تكلفة لكل تغيير مقبول.
خمس طرق للتحكم في التكاليف دون المساس بجودة العمل
- حدد معايير القبول قبل اختيار النموذج. حدد السلوك المتوقع، والحالات الاستثنائية، والقيود الحالية. فالمهمة الصغيرة الواضحة أسهل في تفويضها من الناحية الاقتصادية، كما يسهل مراجعتها.
- قدم السياق ذي الصلة. يرجى تضمين الملفات وتفاصيل الأخطاء التي يحتاجها النموذج. أما بالنسبة للأعمال غير ذات الصلة، فيُفضل بدء محادثة جديدة بدلاً من الاستمرار في محادثة قديمة لمجرد وجودها.
- افصل بين القدرة والجهد. توصي Anthropic بالبدء بمستوى الجهد الافتراضي للنموذج. إذا قرأ النموذج السياق الصحيح ولم يتمكن مع ذلك من حل المشكلة، فقد يكون من المفيد استخدام نموذج أقوى. أما إذا تخطى النموذج مرحلة القراءة أو التحقق، فيجب مراجعة إعدادات المهمة واختيار مستوى الجهد.
- ميزانية الإجابة بأكملها. يحتاج كل من شفرة المصدر والاختبارات والتفسيرات إلى مساحة كافية لعرض النتائج. وتوضح مهمتنا الثانية لماذا لا تُعد الدالة الكاملة بحد ذاتها دليلاً على أن التسليم المطلوب يتناسب مع الميزانية.
- قارن بين الأعمال المقبولة. سجل النتائج المفيدة، ومحاولات الإعادة، ووقت المراجعة، وبيانات الفوترة الفعلية عبر مهام تمثيلية. افصل أخطاء المزود عن حالات فشل جودة النموذج.
تتوافق هذه الاستراتيجيات مع دليل التكاليف الرسمي و إرشادات Anthropic بشأن النموذج والجهد. قد يظل الحد الأسبوعي أو الحد الخاص بالجلسة على مستوى الخطة ساريًا بعد تغيير النموذج؛ أما الحد الخاص بنموذج معين فهو حالة مختلفة. تحقق من الحد الفعلي قبل إجراء التبديل بشكل متكرر.
إذا كان عملك يشمل البرمجة والبحث والكتابة ووسائل الإعلام، فقم أيضًا بمقارنة سير العمل الذي تدفع مقابله. مساحة العمل متعددة النماذج في GlobalGPT يجمع هذه الوظائف في لوحة تحكم واحدة مع إمكانية الوصول عبر واجهة سطر الأوامر (CLI) لمهام التطوير. ومن المفيد أخذ هذه القيمة الأوسع نطاقًا لسير العمل في الاعتبار عند تقييم مسار واجهة برمجة التطبيقات (API) المدفوع حسب الاستخدام؛ فالرسم البياني الخاص بواجهة برمجة التطبيقات (API) أعلاه ليس قائمة أسعار اشتراك GlobalGPT.
لماذا قد لا يثبت النموذج الذي اخترته
| الأعراض | فحص محتمل | الخطوة العملية التالية |
|---|---|---|
| تستخدم الجلسة التالية النموذج القديم | ربما كان هذا التبديل مقتصرًا على الجلسة فقط | استخدم إجراء «Enter» في أداة الاختيار للحفظ، ثم حدد جلسة جديدة |
| يتم حفظ التفضيل، لكن يتم تشغيل نموذج مختلف | قد تكون الأولوية لإعداد «الإطلاق» أو «القذيفة» أو «المشروع» أو «الإدارة» أو «المنظمة» | اقرأ معلومات بدء التشغيل وتحقق من الإعدادات ذات الصلة مع مالكها |
| المحادثة المستأنفة لها نموذج مختلف | قد تستعيد الجلسة المحفوظة نموذجها السابق | تحقق من /status؛ واختر الطراز المطلوب بعناية من أجل استئناف العمل |
| النموذج مفقود أو تم رفضه | توافر المزود، أو قواعد المؤسسة، أو إصدار قديم من كود Claude | تحقق من معرّفات الطرازات المدعومة، وإمكانية الوصول إلى الحساب، ومتطلبات الإصدار |
| يُترجم اسم العائلة نفسه بطرق مختلفة في أماكن أخرى | تختلف الأسماء المستعارة وتعيينات المزودين | استخدم المعرّف الكامل الصحيح للمزود عندما يتعين أن يظل الإصدار ثابتًا |
| لا يؤدي التبديل إلى مسح حد الاستخدام | قد ينطبق هذا الحد على الخطة بأكملها | اقرأ رسالة الاستخدام وأعد ضبط الفترة بدلاً من الافتراض بأن التبديل العائلي كافٍ |
إذا تعذر على الرمز Claude كتابة إعدادات المستخدم الخاصة بك، فيمكن أن يستمر الخيار التفاعلي ساريًا خلال الجلسة الحالية دون أن يتم حفظه. وقد يحتاج ملف الإعدادات المُدار أو الذي تم إنشاؤه إلى تعديل من قبل مالكه بدلاً من محاولة أخرى لاختيار خيار آخر. تجنب إزالة القيود المؤسسية لمجرد إظهار خيار ما.
قم بإنشاء سير عمل أكثر شمولاً باستخدام GlobalGPT
يُعد اختيار النموذج جزءًا من سير العمل. فقد تحتاج إلى البحث في إحدى المكتبات، أو صياغة دالة، أو شرح تغيير ما لأحد زملائك في الفريق، أو إنشاء وثائق، أو إعداد عناصر مرئية. يجمع GlobalGPT بين العديد من النماذج الرائدة ووظائف الذكاء الاصطناعي في مساحة عمل واحدة ميسورة التكلفة، بحيث يمكن تنفيذ تلك المهام من خلال لوحة التحكم نفسها.

بالنسبة للأعمال المتعلقة بالمحطات، فإن GlobalGPT CLI يربط أدوات المنصة ببيئات التطوير. ويستخدم الإعداد الموثق لحساب GlobalGPT الخاص بك لتسجيل الدخول ورصيد الحساب العادي. ويمكن لواجهة سطر الأوامر (CLI) دعم سير عمل أوسع نطاقًا في مجال البرمجة والإبداع، في حين يظل النموذج الخاص بـ Claude Code خيارًا متعمدًا من خلال عناصر التحكم في النموذج.
على سبيل المثال، قم بصياغة ومراجعة خطة التنفيذ، واستخدم نموذجًا آخر لتقديم شرح موجز، ثم قم بإعداد الوثائق أو الموارد المصاحبة للإصدار. اتبع الدليل من أجل ربط واجهة CLI لجهاز GlobalGPT برمز جهاز Claude عندما ترغب في دمج تلك الأداة. قم بإعداد نموذج الاستدلال الخاص برمز Claude بشكل منفصل وفقًا لمهمة الترميز.
اختر نموذجًا أوليًّا، وحدد المعايير التي تُحدد النجاح، وانتقل إلى مستوى أعلى عندما تتطلب المهمة ذلك. راجع النتيجة النهائية بالكامل قبل اعتبار العمل منجزًا. قم ببناء سير عملك باستخدام GlobalGPT عندما ترغب في أن تكون أدوات اختيار النماذج، والبحث، والبرمجة، والأدوات الإبداعية مترابطة في إطار عملك اليومي.
الأسئلة الشائعة
كيف يمكنني تغيير النموذج في كود Claude؟
قم بتشغيل الأمر /model لفتح أداة الاختيار. اضغط على الحرف s لتغيير الإعدادات في الجلسة الحالية فقط، أو اضغط على مفتاح Enter لحفظ النموذج المحدد كإعداد افتراضي. لاختيار نموذج لجلسة جديدة واحدة، قم بتشغيل الأمر claude –model متبوعًا باسم النموذج.
هل يؤدي /model إلى تغيير الإعدادات الافتراضية المحفوظة لدي؟
في الإصدارات التفاعلية الحالية، يؤدي اختيار نموذج باستخدام مفتاح Enter أو كتابة /model sonnet إلى حفظ الاختيار في إعدادات المستخدم إذا كانت قابلة للتعديل. أما الضغط على الحرف s في أداة الاختيار، فهو يقتصر على الجلسة الحالية فقط. ومع ذلك، قد تكون للإعدادات الأخرى الأولوية عند التشغيل التالي.
ما هو نموذج رمز Claude الافتراضي؟
يعتمد الإعداد الافتراضي لوقت التشغيل على حسابك ومزود الخدمة وإعداداتك. تعمل القيمة الافتراضية على مسح تجاوز النموذج؛ وهي ليست معرّف نموذج ثابتًا واحدًا. تحقق من النموذج النشط باستخدام الأمر /status وافحص أداة الاختيار بدلاً من افتراض أن كل حساب يبدأ بنفس الإصدار.
ما هي وظيفة برنامج Opusplan؟
يستخدم النظام «أوبوس» (Opus) في وضع التخطيط و«سونيت» (Sonnet) أثناء التنفيذ. وهو يتبع عملية الانتقال بين الأوضاع بدلاً من اختيار النموذج الأقل تكلفة تلقائيًا لكل موجه. وتعتمد التكلفة الفعلية على العمل المنجز في كل مرحلة.
لماذا لا يظهر أحد النماذج في أداة الاختيار؟
قد يعتمد التوفر على مزود الخدمة وحسابك والقيود المفروضة من قبل المؤسسة وإصدار كود Claude. يرجى مراجعة وثائق الطراز الحالي والطرازات المسموح بها في بيئتك. كما يمكن أن يشير الاسم المستعار للعائلة إلى إصدار مختلف لدى مزود خدمة آخر.
هل يؤدي اختيار الطراز الأرخص دائمًا إلى خفض تكلفة المهمة؟
لا. سعر التوكن ليس سوى عامل واحد فقط. فإجمالي الاستخدام، والإجابات غير المكتملة، ومحاولات الإعادة، والجهد المبذول في المراجعة، كلها عوامل تؤثر على تكلفة التغيير المقبول. في عملية إعادة الهيكلة الصغيرة التي أجريناها، اجتازت الوظائف الثلاث الاختبار، بينما تم اقتطاع الردود الثلاثة الأكبر حجمًا المكونة من ملفين عند الحد الأقصى المشترك للإخراج.
المصادر
- تكوين طراز الرمز Claude — اختيار النموذج، والإعدادات الافتراضية، والأسماء المستعارة، والاختلافات بين مزودي الخدمة، و«opusplan».
- تكاليف رمز Claude — تقديرات الاستخدام، وحصص الاشتراك، والتخزين المؤقت، وضوابط التكلفة.
- نظرة عامة على طراز Anthropic و تسعير واجهة برمجة التطبيقات (API) — مواصفات الطراز الحالي وأسعاره المعلنة.
- إرشادات بشأن النموذج والجهد — الاختلافات العملية بين القدرة والسياق والجهد.
- وثائق واجهة سطر الأوامر لـ GlobalGPT — سير العمل في المحطة الطرفية والإعداد القائم على الحساب.







