Download MMLU-Pro-mini/index.mjs from zelk12/BenchPack_for_BenchLocal: direct link, hf CLI and curl.
- Browser
- Download file 6.77 kB
-
https://huggingface.co/zelk12/BenchPack_for_BenchLocal/resolve/main/MMLU-Pro-mini/index.mjs
- Command line
-
hf download hf://zelk12/BenchPack_for_BenchLocal/MMLU-Pro-mini/index.mjs
-
curl -L -o index.mjs https://huggingface.co/zelk12/BenchPack_for_BenchLocal/resolve/main/MMLU-Pro-mini/index.mjs
6.77 kB
| import fs from "node:fs"; | |
| import path from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
| const manifestPath = path.join(__dirname, "benchlocal.pack.json"); | |
| const scenariosPath = path.join(__dirname, "scenarios.json"); | |
| export const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); | |
| function loadScenarios() { | |
| return JSON.parse(fs.readFileSync(scenariosPath, "utf8")); | |
| } | |
| export function listScenarios() { | |
| return loadScenarios().map((s) => ({ | |
| id: s.id, | |
| name: s.name, | |
| category: s.category || "General", | |
| description: `[${(s.language || "ru").toUpperCase()}] ${s.name}` | |
| })); | |
| } | |
| export function getScenarios() { | |
| return listScenarios(); | |
| } | |
| /** | |
| * Валидатор: ищет строго заданный маркер "Мой ответ: (БУКВА)" | |
| * Модель может свободно рассуждать, но в конце обязана выдать именно этот маркер. | |
| * Весь промежуточный текст, формулы и размышления игнорируются. | |
| */ | |
| function validateResponse(scenario, rawText) { | |
| const text = (rawText || "").trim(); | |
| const expected = (scenario.correctAnswer || "").trim().toUpperCase(); | |
| // Ищем маркер формата: "Мой ответ: (БУКВА)" (или англоязычный аналог "My answer: (LETTER)") | |
| const pattern = /(?:Мой\s*ответ|My\s*answer):\s*\(([A-J])\)/gi; | |
| const matches = [...text.matchAll(pattern)]; | |
| if (matches.length === 0) { | |
| return { | |
| pass: false, | |
| score: 0, | |
| extracted: null, | |
| reason: `Нарушен формат: не найдена обязательная конструкция 'Мой ответ: (БУКВА)'. Окончание ответа: "${text.slice(-100)}"` | |
| }; | |
| } | |
| // Берем последнее вхождение маркера в тексте (итоговый вердикт модели) | |
| const lastMatch = matches[matches.length - 1]; | |
| const extracted = lastMatch[1].toUpperCase(); | |
| if (extracted !== expected) { | |
| return { | |
| pass: false, | |
| score: 0, | |
| extracted, | |
| reason: `Неверный ответ: модель выбрала '(${extracted})', ожидалось '(${expected})'` | |
| }; | |
| } | |
| return { | |
| pass: true, | |
| score: 100, | |
| extracted, | |
| reason: `Успех: корректно выбран вариант '(${extracted})' в строгом формате 'Мой ответ: (X)'` | |
| }; | |
| } | |
| function resolveHelpers(context) { | |
| if (context && typeof context.getRequiredProvider === "function") { | |
| return context; | |
| } | |
| if (context?.helpers && typeof context.helpers.getRequiredProvider === "function") { | |
| return context.helpers; | |
| } | |
| return { | |
| getRequiredProvider(providerId) { | |
| const providers = context?.providers || []; | |
| const found = Array.isArray(providers) | |
| ? providers.find((p) => p.id === providerId || p.provider === providerId) | |
| : providers[providerId]; | |
| return found || { baseUrl: "http://127.0.0.1:11434/v1", apiKey: "none" }; | |
| }, | |
| getInferenceEndpoint(modelId) { | |
| return context?.endpoints?.[modelId] || null; | |
| } | |
| }; | |
| } | |
| export async function prepare(context) { | |
| const helpers = resolveHelpers(context); | |
| return { | |
| async runScenario(input) { | |
| const allScenarios = loadScenarios(); | |
| const scenario = allScenarios.find((s) => s.id === input.scenario.id); | |
| if (!scenario) { | |
| return { | |
| scenarioId: input.scenario.id, | |
| status: "fail", | |
| score: 0, | |
| summary: "Сценарий не найден", | |
| rawLog: "error=Scenario not found" | |
| }; | |
| } | |
| const provider = helpers.getRequiredProvider(input.model.provider, { enabledOnly: true }); | |
| const inference = helpers.getInferenceEndpoint ? helpers.getInferenceEndpoint(input.model.id) : null; | |
| const rawBaseUrl = (inference && inference.status === "running" ? inference.baseUrl : provider?.baseUrl) || "http://127.0.0.1:11434/v1"; | |
| const baseUrl = rawBaseUrl.replace(/\/+$/, ""); | |
| const apiKey = provider?.apiKey || "none"; | |
| const requestBody = { | |
| model: input.model.model, | |
| messages: [{ role: "user", content: scenario.prompt }], | |
| temperature: 0 | |
| }; | |
| const response = await fetch(`${baseUrl}/chat/completions`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${apiKey}` | |
| }, | |
| body: JSON.stringify(requestBody) | |
| }); | |
| const data = await response.json(); | |
| const text = (data.choices?.[0]?.message?.content || "").trim(); | |
| const result = validateResponse(scenario, text); | |
| const rawLog = [ | |
| `model=${input.model.model}`, | |
| `category=${scenario.category}`, | |
| `language=${scenario.language}`, | |
| `scenario=${scenario.id} - ${scenario.name}`, | |
| `prompt=${scenario.prompt}`, | |
| `assistant_full_cot_response=${text}`, | |
| `extracted_letter=${result.extracted || "NONE"}`, | |
| `expected_letter=${scenario.correctAnswer}`, | |
| `verdict=${result.pass ? "pass" : "fail"}`, | |
| `score=${result.score}`, | |
| `summary=${result.reason}`, | |
| `\n--- RAW API RESPONSE ---`, | |
| JSON.stringify(data, null, 2) | |
| ].join("\n"); | |
| return { | |
| scenarioId: scenario.id, | |
| category: scenario.category, | |
| language: scenario.language, | |
| status: result.pass ? "pass" : "fail", | |
| score: result.score, | |
| summary: result.reason, | |
| rawLog: rawLog | |
| }; | |
| }, | |
| async dispose() {} | |
| }; | |
| } | |
| export function scoreModelResults(results) { | |
| const scored = results || []; | |
| const total = scored.reduce((sum, r) => sum + (r.score || 0), 0); | |
| const max = scored.length * 100; | |
| const catMap = new Map(); | |
| for (const r of scored) { | |
| const cat = r.category || "General"; | |
| if (!catMap.has(cat)) { | |
| catMap.set(cat, { total: 0, count: 0 }); | |
| } | |
| const entry = catMap.get(cat); | |
| entry.total += r.score || 0; | |
| entry.count += 1; | |
| } | |
| const categories = Array.from(catMap.entries()).map(([name, data]) => ({ | |
| name, | |
| score: data.count > 0 ? Math.round((data.total / (data.count * 100)) * 100) : 0, | |
| count: data.count | |
| })); | |
| return { | |
| totalScore: max > 0 ? Math.round((total / max) * 100) : 0, | |
| categories | |
| }; | |
| } | |
| export default { | |
| manifest, | |
| listScenarios, | |
| getScenarios, | |
| prepare, | |
| createRuntime: prepare, | |
| scoreModelResults | |
| }; |