Image-Text-to-Text
GGUF
German
English
llama.cpp
mtp
speculative-decoding
qwen3
multimodal
conversational
Instructions to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0 # Run inference directly in the terminal: llama cli -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0 # Run inference directly in the terminal: llama cli -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0 # Run inference directly in the terminal: ./llama-cli -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Use Docker
docker model run hf.co/Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
- LM Studio
- Jan
- vLLM
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Davidmg0815/Qwen3.8-27B-MTP-GGUF" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Davidmg0815/Qwen3.8-27B-MTP-GGUF", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
- Ollama
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Ollama:
ollama run hf.co/Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
- Unsloth Studio
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Davidmg0815/Qwen3.8-27B-MTP-GGUF to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Davidmg0815/Qwen3.8-27B-MTP-GGUF to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for Davidmg0815/Qwen3.8-27B-MTP-GGUF to start chatting
- Pi
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Docker Model Runner:
docker model run hf.co/Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
- Lemonade
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Run and chat with the model
lemonade run user.Qwen3.8-27B-MTP-GGUF-Q8_0
List all available models
lemonade list
- Hermes Agent
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use Davidmg0815/Qwen3.8-27B-MTP-GGUF with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "Davidmg0815/Qwen3.8-27B-MTP-GGUF:Q8_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Upload bench/bench_skills.py with huggingface_hub
Browse files- bench/bench_skills.py +341 -0
bench/bench_skills.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Faehigkeits-Benchmark: Terminal und Programmierung, gegen einen llama-server.
|
| 3 |
+
|
| 4 |
+
Bewertet wird ausschliesslich maschinell:
|
| 5 |
+
* Terminal-Aufgaben — der gelieferte Shell-Einzeiler laeuft in einer
|
| 6 |
+
Wegwerf-Sandbox, geprueft wird die tatsaechliche Ausgabe bzw. der
|
| 7 |
+
Dateizustand danach.
|
| 8 |
+
* Code-Aufgaben — die gelieferte Funktion laeuft gegen feste Testfaelle.
|
| 9 |
+
|
| 10 |
+
Kein Modell benotet ein anderes. Wo etwas nicht laeuft, gilt es als
|
| 11 |
+
durchgefallen, und der Grund wird protokolliert.
|
| 12 |
+
|
| 13 |
+
./bench_skills.py --port 8290 --modell "Qwen3.8-27B Q8_0" --out ergebnis.json
|
| 14 |
+
"""
|
| 15 |
+
import argparse
|
| 16 |
+
import json
|
| 17 |
+
import os
|
| 18 |
+
import re
|
| 19 |
+
import shutil
|
| 20 |
+
import subprocess
|
| 21 |
+
import sys
|
| 22 |
+
import tempfile
|
| 23 |
+
import time
|
| 24 |
+
import urllib.request
|
| 25 |
+
|
| 26 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 27 |
+
import tasks_terminal
|
| 28 |
+
import tasks_code
|
| 29 |
+
|
| 30 |
+
# Wird aus main() gesetzt. Steuert, ob der Denkblock zugelassen wird.
|
| 31 |
+
REASONING = False
|
| 32 |
+
|
| 33 |
+
# Muster, die eine Shell-Antwort sofort disqualifizieren. Der Befehl laeuft zwar
|
| 34 |
+
# in einer Sandbox, aber ein 'rm -rf /' oder ein sudo hat dort trotzdem nichts
|
| 35 |
+
# verloren — das waere ein Treffer gegen das echte System.
|
| 36 |
+
GEFAEHRLICH = re.compile(
|
| 37 |
+
r"(^|[;&|`(\s])(sudo|doas|su)\s"
|
| 38 |
+
r"|rm\s+(-[a-zA-Z]*\s+)*(/|/\*|~|\$HOME)(\s|$)"
|
| 39 |
+
r"|mkfs|fdisk|dd\s+[^|]*of=/dev/"
|
| 40 |
+
r"|>\s*/dev/(sd|nvme)"
|
| 41 |
+
r"|shutdown|reboot|halt|init\s+0"
|
| 42 |
+
r"|:\(\)\s*\{.*\}\s*;\s*:" # Fork-Bombe
|
| 43 |
+
r"|curl[^|]*\|\s*(ba)?sh|wget[^|]*\|\s*(ba)?sh"
|
| 44 |
+
r"|chmod\s+-R\s+777\s+/"
|
| 45 |
+
r"|systemctl|pkill|killall"
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def frage(port, prompt, max_tokens, timeout):
|
| 50 |
+
"""Eine Anfrage an den Server. Liefert (text, messwerte, fehler).
|
| 51 |
+
|
| 52 |
+
messwerte enthaelt beide Token-Raten: die Generierrate (was der Nutzer als
|
| 53 |
+
Tempo empfindet) und die Prompt-Rate (wie schnell die Eingabe verarbeitet
|
| 54 |
+
wird). Nur die Generierrate zu melden verschweigt die halbe Wahrheit —
|
| 55 |
+
bei langen Eingaben dominiert die Prompt-Phase die Wartezeit."""
|
| 56 |
+
b = {
|
| 57 |
+
"messages": [{"role": "user", "content": prompt}],
|
| 58 |
+
"max_tokens": max_tokens,
|
| 59 |
+
"cache_prompt": False,
|
| 60 |
+
"temperature": 0.2,
|
| 61 |
+
"top_p": 0.9,
|
| 62 |
+
}
|
| 63 |
+
if not REASONING:
|
| 64 |
+
# NUR ueber chat_template_kwargs. Das Feld "enable_thinking" auf
|
| 65 |
+
# oberster Ebene wird von llama.cpp nicht an die Chat-Vorlage
|
| 66 |
+
# durchgereicht und bleibt wirkungslos — gemessen: 825 Zeichen
|
| 67 |
+
# Denkblock trotz enable_thinking=false, gegen 0 Zeichen hiermit.
|
| 68 |
+
b["chat_template_kwargs"] = {"enable_thinking": False}
|
| 69 |
+
body = json.dumps(b).encode()
|
| 70 |
+
req = urllib.request.Request(
|
| 71 |
+
f"http://127.0.0.1:{port}/v1/chat/completions",
|
| 72 |
+
data=body, headers={"Content-Type": "application/json"})
|
| 73 |
+
t0 = time.time()
|
| 74 |
+
leer = dict(tps=0.0, prompt_tps=0.0, tokens=0, prompt_tokens=0,
|
| 75 |
+
sekunden=0.0, ttft=0.0)
|
| 76 |
+
try:
|
| 77 |
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
| 78 |
+
d = json.load(r)
|
| 79 |
+
except Exception as e:
|
| 80 |
+
leer["sekunden"] = time.time() - t0
|
| 81 |
+
return "", leer, f"{type(e).__name__}: {e}"
|
| 82 |
+
sek = time.time() - t0
|
| 83 |
+
m = d.get("choices", [{}])[0].get("message", {})
|
| 84 |
+
txt = m.get("content") or ""
|
| 85 |
+
denken = m.get("reasoning_content") or ""
|
| 86 |
+
t = d.get("timings", {})
|
| 87 |
+
return txt, dict(
|
| 88 |
+
tps=t.get("predicted_per_second") or 0.0,
|
| 89 |
+
prompt_tps=t.get("prompt_per_second") or 0.0,
|
| 90 |
+
tokens=t.get("predicted_n") or 0,
|
| 91 |
+
prompt_tokens=t.get("prompt_n") or 0,
|
| 92 |
+
# Zeichen statt Token, weil der Server den Denkblock nicht getrennt
|
| 93 |
+
# zaehlt. Verhaeltnis Denken/Antwort ist damit trotzdem ablesbar.
|
| 94 |
+
denk_zeichen=len(denken),
|
| 95 |
+
antwort_zeichen=len(txt),
|
| 96 |
+
abbruch=d.get("choices", [{}])[0].get("finish_reason") or "",
|
| 97 |
+
# Zeit bis zum ersten Token: die Prompt-Phase in Millisekunden.
|
| 98 |
+
ttft=round((t.get("prompt_ms") or 0.0) / 1000.0, 3),
|
| 99 |
+
sekunden=round(sek, 2),
|
| 100 |
+
), ""
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def schaele_code(text):
|
| 104 |
+
"""Holt den Code aus der Antwort. Modelle liefern mal mit Zaun, mal ohne,
|
| 105 |
+
mal mit Vorrede — alle drei Faelle muessen durchgehen, sonst misst man
|
| 106 |
+
Formatierungsdisziplin statt Koennen."""
|
| 107 |
+
zaun = re.findall(r"```(?:python|py|bash|sh|shell)?\s*\n(.*?)```",
|
| 108 |
+
text, re.S)
|
| 109 |
+
if zaun:
|
| 110 |
+
return max(zaun, key=len).strip()
|
| 111 |
+
# Kein Zaun: alles nach einer etwaigen Vorrede nehmen.
|
| 112 |
+
return text.strip()
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def schaele_befehl(text):
|
| 116 |
+
"""Holt den Shell-Einzeiler. Nimmt die erste Zeile, die nach Befehl aussieht."""
|
| 117 |
+
roh = schaele_code(text)
|
| 118 |
+
zeilen = [z.strip() for z in roh.splitlines() if z.strip()]
|
| 119 |
+
zeilen = [z for z in zeilen if not z.startswith("#")]
|
| 120 |
+
if not zeilen:
|
| 121 |
+
return ""
|
| 122 |
+
# Ein '$' oder '> ' am Anfang ist Prompt-Deko, kein Befehl.
|
| 123 |
+
z = zeilen[0]
|
| 124 |
+
z = re.sub(r"^\s*(\$|>|%)\s+", "", z)
|
| 125 |
+
# Mehrzeilige Befehle mit Backslash-Fortsetzung zusammensetzen.
|
| 126 |
+
if z.endswith("\\") and len(zeilen) > 1:
|
| 127 |
+
z = " ".join(x.rstrip("\\").strip() for x in zeilen)
|
| 128 |
+
return z.strip()
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def baue_sandbox():
|
| 132 |
+
d = tempfile.mkdtemp(prefix="bench-sandbox-")
|
| 133 |
+
for name, inhalt in tasks_terminal.SANDBOX.items():
|
| 134 |
+
with open(os.path.join(d, name), "w") as f:
|
| 135 |
+
f.write(inhalt)
|
| 136 |
+
for name, groesse in tasks_terminal.SANDBOX_SIZED.items():
|
| 137 |
+
with open(os.path.join(d, name), "wb") as f:
|
| 138 |
+
f.write(b"\0" * groesse)
|
| 139 |
+
return d
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def lauf_terminal(aufgabe, befehl):
|
| 143 |
+
"""Fuehrt den Befehl in einer frischen Sandbox aus. Liefert (bestanden, grund)."""
|
| 144 |
+
if not befehl:
|
| 145 |
+
return False, "leere Antwort"
|
| 146 |
+
if GEFAEHRLICH.search(befehl):
|
| 147 |
+
return False, "abgelehnt: gefaehrliches Muster"
|
| 148 |
+
d = baue_sandbox()
|
| 149 |
+
try:
|
| 150 |
+
p = subprocess.run(["bash", "-c", befehl], cwd=d, timeout=15,
|
| 151 |
+
capture_output=True, text=True, errors="replace")
|
| 152 |
+
out = p.stdout
|
| 153 |
+
if aufgabe.get("datei_check"):
|
| 154 |
+
dateien = {}
|
| 155 |
+
for name in tasks_terminal.SANDBOX:
|
| 156 |
+
pfad = os.path.join(d, name)
|
| 157 |
+
if os.path.isfile(pfad):
|
| 158 |
+
dateien[name] = open(pfad, errors="replace").read()
|
| 159 |
+
ok = bool(aufgabe["datei_check"](dateien))
|
| 160 |
+
return ok, "" if ok else f"Dateizustand falsch (rc={p.returncode})"
|
| 161 |
+
if p.returncode != 0 and not out.strip():
|
| 162 |
+
return False, f"rc={p.returncode}: {p.stderr.strip()[:80]}"
|
| 163 |
+
try:
|
| 164 |
+
ok = bool(aufgabe["check"](out))
|
| 165 |
+
except Exception as e:
|
| 166 |
+
return False, f"Ausgabe unbrauchbar: {type(e).__name__}"
|
| 167 |
+
return ok, "" if ok else f"Ausgabe falsch: {out.strip()[:60]!r}"
|
| 168 |
+
except subprocess.TimeoutExpired:
|
| 169 |
+
return False, "Zeitueberschreitung (15s)"
|
| 170 |
+
except Exception as e:
|
| 171 |
+
return False, f"{type(e).__name__}: {e}"
|
| 172 |
+
finally:
|
| 173 |
+
shutil.rmtree(d, ignore_errors=True)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
# Wird als eigener Prozess gestartet, damit eine Endlosschleife oder ein
|
| 177 |
+
# Absturz im Modellcode den Benchmark nicht mitreisst.
|
| 178 |
+
RUNNER = r'''
|
| 179 |
+
import json, sys
|
| 180 |
+
quelle = sys.stdin.read()
|
| 181 |
+
auftrag = json.loads(quelle)
|
| 182 |
+
ns = {}
|
| 183 |
+
ergebnisse = []
|
| 184 |
+
try:
|
| 185 |
+
exec(auftrag["code"], ns)
|
| 186 |
+
except Exception as e:
|
| 187 |
+
print(json.dumps({"fehler": "Code laedt nicht: %s: %s" % (type(e).__name__, e)}))
|
| 188 |
+
sys.exit(0)
|
| 189 |
+
for ausdruck, erwartet in auftrag["tests"]:
|
| 190 |
+
try:
|
| 191 |
+
lokal = dict(ns)
|
| 192 |
+
# ERG als Sentinel, nicht "_r": Funktionsnamen wie suche_rotiert
|
| 193 |
+
# enthalten "_r" und landeten sonst faelschlich im exec-Zweig.
|
| 194 |
+
if "ERG" in ausdruck:
|
| 195 |
+
exec(ausdruck, lokal)
|
| 196 |
+
wert = lokal.get("ERG")
|
| 197 |
+
else:
|
| 198 |
+
wert = eval(ausdruck, lokal)
|
| 199 |
+
# Tupel und Listen gelten als gleich, wenn der Inhalt stimmt —
|
| 200 |
+
# die Aufgabe schreibt die Behaeltersorte nicht vor.
|
| 201 |
+
def norm(x):
|
| 202 |
+
if isinstance(x, (list, tuple)):
|
| 203 |
+
return [norm(i) for i in x]
|
| 204 |
+
return x
|
| 205 |
+
ergebnisse.append(norm(wert) == norm(erwartet))
|
| 206 |
+
except Exception as e:
|
| 207 |
+
ergebnisse.append(False)
|
| 208 |
+
print(json.dumps({"ergebnisse": ergebnisse}))
|
| 209 |
+
'''
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def lauf_code(aufgabe, code):
|
| 213 |
+
"""Laesst den gelieferten Code gegen die Testfaelle laufen."""
|
| 214 |
+
if not code.strip():
|
| 215 |
+
return False, 0, len(aufgabe["tests"]), "leere Antwort"
|
| 216 |
+
auftrag = json.dumps({"code": code, "tests": aufgabe["tests"]})
|
| 217 |
+
try:
|
| 218 |
+
p = subprocess.run([sys.executable, "-c", RUNNER], input=auftrag,
|
| 219 |
+
capture_output=True, text=True, timeout=30)
|
| 220 |
+
except subprocess.TimeoutExpired:
|
| 221 |
+
return False, 0, len(aufgabe["tests"]), "Zeitueberschreitung (30s)"
|
| 222 |
+
try:
|
| 223 |
+
d = json.loads(p.stdout.strip().splitlines()[-1])
|
| 224 |
+
except Exception:
|
| 225 |
+
return False, 0, len(aufgabe["tests"]), f"Runner stumm: {p.stderr.strip()[:70]}"
|
| 226 |
+
if "fehler" in d:
|
| 227 |
+
return False, 0, len(aufgabe["tests"]), d["fehler"]
|
| 228 |
+
e = d["ergebnisse"]
|
| 229 |
+
return all(e), sum(e), len(e), "" if all(e) else f"{sum(e)}/{len(e)} Testfaelle"
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def main():
|
| 233 |
+
ap = argparse.ArgumentParser()
|
| 234 |
+
ap.add_argument("--port", type=int, default=8290)
|
| 235 |
+
ap.add_argument("--modell", required=True, help="Anzeigename fuer die Tabelle")
|
| 236 |
+
ap.add_argument("--out", required=True)
|
| 237 |
+
ap.add_argument("--max-tokens", type=int, default=900)
|
| 238 |
+
ap.add_argument("--timeout", type=int, default=300)
|
| 239 |
+
ap.add_argument("--nur", default="", help="term|code, sonst beides")
|
| 240 |
+
ap.add_argument("--reasoning", action="store_true",
|
| 241 |
+
help="Denkblock zulassen (sonst per chat_template_kwargs aus)")
|
| 242 |
+
args = ap.parse_args()
|
| 243 |
+
|
| 244 |
+
global REASONING
|
| 245 |
+
REASONING = args.reasoning
|
| 246 |
+
print(f"Reasoning: {'AN' if REASONING else 'AUS'}, "
|
| 247 |
+
f"max_tokens={args.max_tokens}", file=sys.stderr)
|
| 248 |
+
|
| 249 |
+
bericht = {"modell": args.modell, "reasoning": REASONING,
|
| 250 |
+
"max_tokens": args.max_tokens,
|
| 251 |
+
"terminal": [], "code": [], "messungen": []}
|
| 252 |
+
|
| 253 |
+
if args.nur in ("", "term"):
|
| 254 |
+
print(f"--- Terminal ({len(tasks_terminal.TASKS)} Aufgaben) ---",
|
| 255 |
+
file=sys.stderr)
|
| 256 |
+
for a in tasks_terminal.TASKS:
|
| 257 |
+
txt, m, fehler = frage(args.port, a["prompt"],
|
| 258 |
+
args.max_tokens, args.timeout)
|
| 259 |
+
if fehler:
|
| 260 |
+
bericht["terminal"].append(
|
| 261 |
+
dict(id=a["id"], titel=a["titel"], ok=False, grund=fehler,
|
| 262 |
+
antwort=""))
|
| 263 |
+
print(f" {a['id']} {a['titel']:<26} SERVERFEHLER {fehler[:50]}",
|
| 264 |
+
file=sys.stderr)
|
| 265 |
+
continue
|
| 266 |
+
befehl = schaele_befehl(txt)
|
| 267 |
+
ok, grund = lauf_terminal(a, befehl)
|
| 268 |
+
bericht["terminal"].append(
|
| 269 |
+
dict(id=a["id"], titel=a["titel"], ok=ok, grund=grund,
|
| 270 |
+
antwort=befehl[:200], **m))
|
| 271 |
+
bericht["messungen"].append(m)
|
| 272 |
+
print(f" {a['id']} {a['titel']:<26} {'OK ' if ok else 'FEHL'} "
|
| 273 |
+
f"{m['tps']:5.1f} t/s {m['tokens']:4d} Tok {grund[:45]}",
|
| 274 |
+
file=sys.stderr)
|
| 275 |
+
|
| 276 |
+
if args.nur in ("", "code"):
|
| 277 |
+
print(f"--- Programmierung ({len(tasks_code.TASKS)} Aufgaben) ---",
|
| 278 |
+
file=sys.stderr)
|
| 279 |
+
for a in tasks_code.TASKS:
|
| 280 |
+
txt, m, fehler = frage(args.port, a["prompt"],
|
| 281 |
+
args.max_tokens, args.timeout)
|
| 282 |
+
if fehler:
|
| 283 |
+
bericht["code"].append(
|
| 284 |
+
dict(id=a["id"], titel=a["titel"], ok=False, grund=fehler,
|
| 285 |
+
bestanden=0, gesamt=len(a["tests"])))
|
| 286 |
+
print(f" {a['id']} {a['titel']:<26} SERVERFEHLER {fehler[:50]}",
|
| 287 |
+
file=sys.stderr)
|
| 288 |
+
continue
|
| 289 |
+
code = schaele_code(txt)
|
| 290 |
+
ok, best, ges, grund = lauf_code(a, code)
|
| 291 |
+
bericht["code"].append(
|
| 292 |
+
dict(id=a["id"], titel=a["titel"], ok=ok, grund=grund,
|
| 293 |
+
bestanden=best, gesamt=ges, code=code[:600], **m))
|
| 294 |
+
bericht["messungen"].append(m)
|
| 295 |
+
print(f" {a['id']} {a['titel']:<26} {'OK ' if ok else 'FEHL'} "
|
| 296 |
+
f"{m['tps']:5.1f} t/s {m['tokens']:4d} Tok "
|
| 297 |
+
f"{best}/{ges} {grund[:35]}", file=sys.stderr)
|
| 298 |
+
|
| 299 |
+
t, c, mm = bericht["terminal"], bericht["code"], bericht["messungen"]
|
| 300 |
+
tps = sorted(x["tps"] for x in mm if x["tps"] > 0)
|
| 301 |
+
ptps = sorted(x["prompt_tps"] for x in mm if x["prompt_tps"] > 0)
|
| 302 |
+
med = lambda v: v[len(v) // 2] if v else 0.0
|
| 303 |
+
bericht["summe"] = {
|
| 304 |
+
"terminal_ok": sum(1 for x in t if x["ok"]), "terminal_n": len(t),
|
| 305 |
+
"code_ok": sum(1 for x in c if x["ok"]), "code_n": len(c),
|
| 306 |
+
"code_testfaelle_ok": sum(x.get("bestanden", 0) for x in c),
|
| 307 |
+
"code_testfaelle_n": sum(x.get("gesamt", 0) for x in c),
|
| 308 |
+
"tps_median": round(med(tps), 1),
|
| 309 |
+
"tps_min": round(tps[0], 1) if tps else 0.0,
|
| 310 |
+
"tps_max": round(tps[-1], 1) if tps else 0.0,
|
| 311 |
+
"prompt_tps_median": round(med(ptps), 1),
|
| 312 |
+
"tokens_gesamt": sum(x["tokens"] for x in mm),
|
| 313 |
+
"sekunden_gesamt": round(sum(x["sekunden"] for x in mm), 1),
|
| 314 |
+
"anfragen": len(mm),
|
| 315 |
+
# Der Preis des Denkens: wie viele Token im Schnitt, und wie viel davon
|
| 316 |
+
# Grubelei statt Antwort war. Ein Lauf, der ans Token-Limit stoesst,
|
| 317 |
+
# hat die Antwort nie ausgegeben — deshalb wird das mitgezaehlt.
|
| 318 |
+
"tokens_je_aufgabe": round(sum(x["tokens"] for x in mm) / len(mm), 1)
|
| 319 |
+
if mm else 0,
|
| 320 |
+
"denk_zeichen_gesamt": sum(x.get("denk_zeichen", 0) for x in mm),
|
| 321 |
+
"antwort_zeichen_gesamt": sum(x.get("antwort_zeichen", 0) for x in mm),
|
| 322 |
+
"am_limit": sum(1 for x in mm if x.get("abbruch") == "length"),
|
| 323 |
+
}
|
| 324 |
+
with open(args.out, "w") as f:
|
| 325 |
+
json.dump(bericht, f, ensure_ascii=False, indent=1)
|
| 326 |
+
s = bericht["summe"]
|
| 327 |
+
print(f"\n{args.modell} [Reasoning {'AN' if REASONING else 'AUS'}]: "
|
| 328 |
+
f"Terminal {s['terminal_ok']}/{s['terminal_n']}, "
|
| 329 |
+
f"Code {s['code_ok']}/{s['code_n']} "
|
| 330 |
+
f"({s['code_testfaelle_ok']}/{s['code_testfaelle_n']} Testfaelle), "
|
| 331 |
+
f"{s['tps_median']:.1f} t/s Median "
|
| 332 |
+
f"({s['tps_min']:.1f}-{s['tps_max']:.1f}), "
|
| 333 |
+
f"Prompt {s['prompt_tps_median']:.0f} t/s, "
|
| 334 |
+
f"{s['tokens_gesamt']} Token in {s['sekunden_gesamt']}s, "
|
| 335 |
+
f"{s['tokens_je_aufgabe']:.0f} Token/Aufgabe, "
|
| 336 |
+
f"{s['am_limit']} am Limit abgeschnitten", file=sys.stderr)
|
| 337 |
+
print(f"geschrieben: {args.out}", file=sys.stderr)
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
if __name__ == "__main__":
|
| 341 |
+
main()
|