Download GPQA-mini/index.mjs from zelk12/BenchPack_for_BenchLocal: direct link, hf CLI and curl.
- Browser
- Download file 7.94 kB
-
https://huggingface.co/zelk12/BenchPack_for_BenchLocal/resolve/main/GPQA-mini/index.mjs
- Command line
-
hf download hf://zelk12/BenchPack_for_BenchLocal/GPQA-mini/index.mjs
-
curl -L -o index.mjs https://huggingface.co/zelk12/BenchPack_for_BenchLocal/resolve/main/GPQA-mini/index.mjs
7.94 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 = fs.existsSync(path.join(__dirname, "benchlocal.pack.json")) | |
| ? path.join(__dirname, "benchlocal.pack.json") | |
| : path.join(__dirname, "benchlocal.pack.txt"); | |
| const scenariosPath = fs.existsSync(path.join(__dirname, "scenarios.json")) | |
| ? path.join(__dirname, "scenarios.json") | |
| : path.join(__dirname, "scenarios.txt"); | |
| function readJsonSafe(filePath, fallback) { | |
| try { | |
| if (fs.existsSync(filePath)) { | |
| const raw = fs.readFileSync(filePath, "utf8").replace(/^\uFEFF/, "").trim(); | |
| return JSON.parse(raw); | |
| } | |
| } catch (err) { | |
| console.error(`[GPQA-Polyglot] Ошибка чтения ${filePath}:`, err.message); | |
| } | |
| return fallback; | |
| } | |
| const defaultManifest = { | |
| schemaVersion: 1, | |
| protocolVersion: 1, | |
| id: "GPQA-mini", | |
| name: "GPQA-Polyglot (45 задач STEM уровня PhD)", | |
| version: "1.0.0", | |
| description: "Сложный Google-Proof бенчмарк уровня аспирантуры (10 вариантов ответов A-J, случайный порог 10%): 15 RU, 15 EN и 15 MIX задач", | |
| entry: "./index.mjs", // ИСПРАВЛЕНО (было: entry": "./index.mjs",) | |
| samplingDefaults: { temperature: 0.0 }, | |
| capabilities: { tools: false, multiTurn: false, streamingProgress: false, verification: false } | |
| }; | |
| export const manifest = readJsonSafe(manifestPath, defaultManifest); | |
| function loadScenarios() { | |
| return readJsonSafe(scenariosPath, []); | |
| } | |
| export function listScenarios() { | |
| return loadScenarios().map(s => ({ | |
| id: s.id, | |
| name: s.name, | |
| category: s.category || "Общие", | |
| description: s.name | |
| })); | |
| } | |
| export function getScenarios() { | |
| return listScenarios(); | |
| } | |
| /** | |
| * Валидатор извлечения ответа: | |
| * 1. Ищет конструкцию "Final Answer: (X)" или "Final Answer: X". | |
| * 2. Если префикс опущен, ищет последнюю пару скобок с буквой: (X) или [X]. | |
| */ | |
| export function extractAnswer(rawText) { | |
| const text = (rawText || "").trim(); | |
| if (!text) return null; | |
| // Поддерживает как вариант со скобками: (A), так и без них: Final Answer: A | |
| const pattern = /(?:final\s*(?:verdict|answer)|итоговый\s*(?:ответ|вердикт)|ответ)[\s:=]*[\[\(]?\s*([A-J])\s*[\]\)]?/gi; | |
| const explicitMatches = Array.from(text.matchAll(pattern)); | |
| if (explicitMatches.length > 0) { | |
| return explicitMatches[explicitMatches.length - 1][1].toUpperCase(); | |
| } | |
| const bracketMatches = Array.from(text.matchAll(/[\[\(]([A-J])[\]\)]/g)); | |
| if (bracketMatches.length > 0) { | |
| return bracketMatches[bracketMatches.length - 1][1].toUpperCase(); | |
| } | |
| return null; | |
| } | |
| 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, | |
| max_tokens: 4096 | |
| }; | |
| let text = ""; | |
| let rawResponseData = null; | |
| try { | |
| const response = await fetch(`${baseUrl}/chat/completions`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${apiKey}` | |
| }, | |
| body: JSON.stringify(requestBody) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status} ${response.statusText}`); | |
| } | |
| rawResponseData = await response.json(); | |
| text = (rawResponseData.choices?.[0]?.message?.content || "").trim(); | |
| } catch (err) { | |
| return { | |
| scenarioId: scenario.id, | |
| status: "fail", | |
| score: 0, | |
| summary: `Ошибка API: ${err.message}`, | |
| rawLog: `error=${err.message}` | |
| }; | |
| } | |
| const extracted = extractAnswer(text); | |
| const passed = extracted === scenario.correct_answer.toUpperCase(); | |
| const rawLog = [ | |
| `model=${input.model.model}`, | |
| `scenario=${scenario.id} ${scenario.name}`, | |
| `category=${scenario.category}`, | |
| `user_turn_1=${scenario.prompt}`, | |
| `assistant_turn_1=${text}`, | |
| `extracted_choice=${extracted || "NONE"}`, | |
| `expected_choice=${scenario.correct_answer}`, | |
| `verdict=${passed ? "pass" : "fail"}`, | |
| `score=${passed ? 100 : 0}`, | |
| `summary=${passed ? "Правильный ответ" : `Неверно (выбрано: ${extracted || 'ничего'}, ожидалось: ${scenario.correct_answer})`}`, | |
| `\n--- RAW API RESPONSE ---`, | |
| JSON.stringify(rawResponseData, null, 2) | |
| ].join("\n"); | |
| return { | |
| scenarioId: scenario.id, | |
| status: passed ? "pass" : "fail", | |
| score: passed ? 100 : 0, | |
| summary: passed ? "Правильный ответ" : `Выбрано '${extracted || "нет"}', ожидалось '${scenario.correct_answer}'`, | |
| rawLog: rawLog | |
| }; | |
| }, | |
| async dispose() {} | |
| }; | |
| } | |
| export function scoreModelResults(results) { | |
| const scored = results || []; | |
| const allScenarios = loadScenarios(); | |
| const scenarioMap = new Map(allScenarios.map(s => [s.id, s])); | |
| const total = scored.reduce((sum, r) => sum + (r.score || 0), 0); | |
| const max = scored.length * 100; | |
| const categoryStats = {}; | |
| for (const r of scored) { | |
| const s = scenarioMap.get(r.scenarioId); | |
| const cat = s?.category || "Общие"; | |
| if (!categoryStats[cat]) { | |
| categoryStats[cat] = { total: 0, count: 0 }; | |
| } | |
| categoryStats[cat].total += (r.score || 0); | |
| categoryStats[cat].count += 1; | |
| } | |
| const categories = Object.entries(categoryStats).map(([name, stat]) => ({ | |
| name, | |
| score: stat.count > 0 ? Math.round((stat.total / (stat.count * 100)) * 100) : 0 | |
| })); | |
| return { | |
| totalScore: max > 0 ? Math.round((total / max) * 100) : 0, | |
| categories | |
| }; | |
| } | |
| export default { | |
| manifest, | |
| listScenarios, | |
| getScenarios, | |
| prepare, | |
| createRuntime: prepare, | |
| scoreModelResults | |
| }; |