patdev commited on
Commit
5896cf6
·
verified ·
1 Parent(s): de7ff79

epreuve de lecture de captures

Browse files
Files changed (1) hide show
  1. aides/_epreuve_texte.py +158 -0
aides/_epreuve_texte.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Le vrai regime d'usage : lire une CAPTURE D'ECRAN.
2
+ #
3
+ # Les 7/7 precedents portaient sur de la geometrie -- couleurs, comptage,
4
+ # lateralite. Ils prouvent que le pipeline d'image est correctement cable, PAS
5
+ # que le modele sait lire un terminal a 14 px. Ce sont deux regimes differents,
6
+ # et c'est le second que Claude Code lui envoie.
7
+ #
8
+ # Tailles volontairement realistes : 1200x400 en monospace 15-16 px, soit la
9
+ # densite d'une vraie fenetre. A patch 16, ca fait 75x25 = 1875 patchs, sous
10
+ # les 2304 positions du config -- l'image n'est donc pas redimensionnee a la
11
+ # baisse, ce qui fausserait le test dans le bon sens.
12
+ import base64
13
+ import io
14
+ import json
15
+ import os
16
+ import subprocess
17
+
18
+ from PIL import Image, ImageDraw, ImageFont
19
+
20
+ PONT = "http://127.0.0.1:8080/v1/messages"
21
+
22
+ PISTES = [
23
+ "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
24
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
25
+ "/usr/share/fonts/dejavu/DejaVuSansMono.ttf",
26
+ "/opt/conda/lib/python3.12/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono.ttf",
27
+ ]
28
+
29
+
30
+ def police(taille):
31
+ for p in PISTES:
32
+ if os.path.exists(p):
33
+ return ImageFont.truetype(p, taille)
34
+ for racine, _, fichiers in os.walk("/usr/share/fonts"):
35
+ for f in fichiers:
36
+ if f.endswith((".ttf", ".otf")):
37
+ return ImageFont.truetype(os.path.join(racine, f), taille)
38
+ return ImageFont.load_default()
39
+
40
+
41
+ def rendre(lignes, larg=1200, haut=400, fond=(30, 30, 35), encre=(220, 220, 220),
42
+ taille=16, x0=24, y0=20, interligne=26):
43
+ img = Image.new("RGB", (larg, haut), fond)
44
+ d = ImageDraw.Draw(img)
45
+ f = police(taille)
46
+ for i, (txt, couleur) in enumerate(lignes):
47
+ d.text((x0, y0 + i * interligne), txt, font=f, fill=couleur or encre)
48
+ tampon = io.BytesIO()
49
+ img.save(tampon, format="PNG")
50
+ return base64.b64encode(tampon.getvalue()).decode()
51
+
52
+
53
+ def demande(b64, texte, maxtok=200):
54
+ corps = {"model": "flashnext", "max_tokens": maxtok,
55
+ "thinking": {"type": "disabled"},
56
+ "messages": [{"role": "user", "content": [
57
+ {"type": "image", "source": {"type": "base64",
58
+ "media_type": "image/png", "data": b64}},
59
+ {"type": "text", "text": texte}]}]}
60
+ p = subprocess.run(["curl", "-s", "-m", "180",
61
+ "-H", "content-type: application/json",
62
+ "-H", "anthropic-version: 2023-06-01",
63
+ "-H", "x-api-key: x",
64
+ "-d", json.dumps(corps), PONT],
65
+ capture_output=True, text=True)
66
+ try:
67
+ d = json.loads(p.stdout)
68
+ if d.get("type") != "message":
69
+ return "ERREUR " + json.dumps(d)[:140], 0
70
+ t = "".join(b.get("text", "") for b in d.get("content", []))
71
+ return t.strip().replace("\n", " ⏎ "), d["usage"]["input_tokens"]
72
+ except Exception:
73
+ return "ILLISIBLE " + (p.stdout or "")[:120], 0
74
+
75
+
76
+ score = 0
77
+ total = 0
78
+ print(" police :", police(16).path if hasattr(police(16), "path") else "defaut bitmap")
79
+ print()
80
+
81
+ # --- 1. erreur de compilation dans un terminal sombre
82
+ b64 = rendre([
83
+ ("$ npm run build", (120, 220, 120)),
84
+ ("", None),
85
+ ("ERROR in src/services/auth.ts:142:18", (240, 90, 90)),
86
+ ("TS2345: Argument of type 'string | undefined' is not", None),
87
+ ("assignable to parameter of type 'string'.", None),
88
+ ("", None),
89
+ (" 140 | const token = readToken();", (150, 150, 150)),
90
+ (" 141 | if (!token) return null;", (150, 150, 150)),
91
+ ("> 142 | return verify(token, secret);", None),
92
+ (" | ^^^^^", (240, 90, 90)),
93
+ ])
94
+ r, n = demande(b64, "Donne exactement : le code d'erreur TypeScript, le fichier, et le numero de ligne. Format : CODE | FICHIER | LIGNE")
95
+ ok = ("TS2345" in r) and ("auth.ts" in r) and ("142" in r)
96
+ score += ok; total += 1
97
+ print(" 1. erreur de build (%s jetons)" % n)
98
+ print(" -> %s" % r[:170])
99
+ print(" %s (attendu TS2345 | src/services/auth.ts | 142)" % ("OK" if ok else "ECHEC"))
100
+ print()
101
+
102
+ # --- 2. extrait de code sur fond clair
103
+ b64 = rendre([
104
+ ("def fibonacci(n: int) -> int:", (20, 20, 20)),
105
+ (" if n <= 1:", (20, 20, 20)),
106
+ (" return n", (20, 20, 20)),
107
+ (" return fibonacci(n - 1) + fibonacci(n - 2)", (20, 20, 20)),
108
+ ("", None),
109
+ ("MAX_DEPTH = 4096", (20, 20, 20)),
110
+ ], fond=(250, 250, 250), encre=(20, 20, 20))
111
+ r, n = demande(b64, "Quel est le nom de la fonction, et quelle est la valeur de la constante ? Reponds : NOM = ..., CONSTANTE = ...")
112
+ ok = ("fibonacci" in r.lower()) and ("4096" in r)
113
+ score += ok; total += 1
114
+ print(" 2. extrait de code (%s jetons)" % n)
115
+ print(" -> %s" % r[:170])
116
+ print(" %s (attendu fibonacci / 4096)" % ("OK" if ok else "ECHEC"))
117
+ print()
118
+
119
+ # --- 3. transcription exacte, petite taille (13 px : densite reelle)
120
+ b64 = rendre([
121
+ ("Traceback (most recent call last):", (240, 120, 120)),
122
+ (' File "/travail/pont.py", line 389, in _relayer', None),
123
+ (" raise EngineDeadError(queue.Full)", None),
124
+ ("EngineDeadError: ple_offload/connector.py queue full", (240, 90, 90)),
125
+ ], taille=13, interligne=20, haut=200)
126
+ r, n = demande(b64, "Transcris la DERNIERE ligne du message, mot pour mot.")
127
+ ok = ("EngineDeadError" in r) and ("queue" in r.lower())
128
+ score += ok; total += 1
129
+ print(" 3. transcription a 13 px (%s jetons)" % n)
130
+ print(" -> %s" % r[:170])
131
+ print(" %s (attendu 'EngineDeadError: ple_offload/connector.py queue full')" % ("OK" if ok else "ECHEC"))
132
+ print()
133
+
134
+ # --- 4. disposition d'interface
135
+ img = Image.new("RGB", (900, 300), (245, 245, 248))
136
+ d = ImageDraw.Draw(img)
137
+ f = police(20)
138
+ d.rectangle([80, 110, 300, 175], fill=(210, 60, 60))
139
+ d.text((130, 132), "Supprimer", font=f, fill=(255, 255, 255))
140
+ d.rectangle([600, 110, 820, 175], fill=(60, 140, 220))
141
+ d.text((650, 132), "Enregistrer", font=f, fill=(255, 255, 255))
142
+ tampon = io.BytesIO(); img.save(tampon, format="PNG")
143
+ b64 = base64.b64encode(tampon.getvalue()).decode()
144
+ r, n = demande(b64, "Il y a deux boutons. Lequel est a DROITE, et de quelle couleur est-il ?")
145
+ ok = ("enregistrer" in r.lower()) and ("bleu" in r.lower())
146
+ score += ok; total += 1
147
+ print(" 4. disposition d'interface (%s jetons)" % n)
148
+ print(" -> %s" % r[:170])
149
+ print(" %s (attendu 'Enregistrer', bleu)" % ("OK" if ok else "ECHEC"))
150
+ print()
151
+
152
+ print(" SCORE LECTURE DE CAPTURE : %d/%d" % (score, total))
153
+ if score == total:
154
+ print(" -> exploitable sur des captures reelles")
155
+ elif score >= 2:
156
+ print(" -> partiel : lit les gros elements, echoue sur le detail")
157
+ else:
158
+ print(" -> NE SAIT PAS lire une capture, malgre 7/7 en geometrie")