import json,subprocess,time,urllib.request,datetime,statistics from pathlib import Path out=Path('/home/mgm/seo-geo-20260912/experiment');out.mkdir(exist_ok=True) def api(path,data=None,timeout=180): req=urllib.request.Request('http://127.0.0.1:11434/api/'+path,data=json.dumps(data).encode() if data is not None else None,headers={'Content-Type':'application/json'}) return json.load(urllib.request.urlopen(req,timeout=timeout)) def cmd(args):return subprocess.check_output(args,text=True).strip() queue=json.load(urllib.request.urlopen('http://127.0.0.1:8188/queue')) assert not queue['queue_running'] and not queue['queue_pending'],'ComfyUI occupied' meta={'date':datetime.datetime.now(datetime.timezone.utc).isoformat(),'gpu':cmd(['nvidia-smi','--query-gpu=name,memory.total,driver_version','--format=csv,noheader']),'cpu':cmd(['lscpu']),'os':cmd(['uname','-a']),'runtime':api('version'),'models':api('tags'),'before':api('ps'),'options':{'num_ctx':4096,'num_predict':256,'temperature':0,'seed':42,'num_gpu':99},'source':'https://docs.ollama.com/api/generate','method':'one warmup + five repetitions per model, unique nonce prevents full prompt cache reuse, single serial request; cache effects retained in raw responses'} (out/'metadata.json').write_text(json.dumps(meta,indent=2)) # Preserve the pre-existing warm model after the experiment. models=['qwen3:8b','hermes3:8b','mistral-nemo:12b'];results=[] try: for loaded in meta['before'].get('models',[]):api('generate',{'model':loaded['name'],'keep_alive':0}) for name in models: details=api('show',{'model':name});(out/(name.replace(':','-')+'-show.json')).write_text(json.dumps(details,indent=2)) for i in range(6): req={'model':name,'stream':False,'think':False,'keep_alive':'5m','options':meta['options'],'prompt':f'Essai {i}. Décris en français une méthode pour vérifier une sauvegarde informatique. Donne les étapes et les limites, sans information personnelle.'} r=api('generate',req);ps=api('ps');row={'model':name,'repeat':i,'warmup':i==0,'request':req,'response':r,'placement':ps,'gpu':cmd(['nvidia-smi','--query-gpu=memory.used,utilization.gpu','--format=csv,noheader'])};results.append(row) (out/'raw.json').write_text(json.dumps(results,ensure_ascii=False,indent=2));print(name,i,r.get('eval_count'),r.get('eval_duration'),flush=True) for task,prompt in [('code','Écris uniquement une fonction Python unique_stable(items) qui supprime les doublons tout en conservant leur ordre. Exemple [3,1,3,2,1] doit donner [3,1,2]. Ne modifie pas la liste en entrée.'),('documents','Document synthétique : Atelier Azur compte 12 membres. La sauvegarde a lieu chaque mardi. Nora vérifie les sauvegardes. Aucune date de création n’est fournie. Réponds en JSON avec effectif, jour, responsable, date_creation. Pour une information absente, utilise null.')]: req={'model':name,'stream':False,'think':False,'keep_alive':'5m','options':meta['options'],'prompt':prompt};r=api('generate',req);(out/(name.replace(':','-')+'-'+task+'.json')).write_text(json.dumps({'request':req,'response':r},ensure_ascii=False,indent=2)) api('generate',{'model':name,'keep_alive':0}) finally: for loaded in api('ps').get('models',[]):api('generate',{'model':loaded['name'],'keep_alive':0}) for loaded in meta['before'].get('models',[]):api('generate',{'model':loaded['name'],'keep_alive':'24h','options':{'num_ctx':loaded.get('context_length',16384)}}) (out/'completed.json').write_text(json.dumps({'completed':True,'date':datetime.datetime.now(datetime.timezone.utc).isoformat()}))