const base=(process.env.OLLAMA_URL||'http://127.0.0.1:11434').replace(/\/$/,''); const model=process.env.MODEL||'qwen3.5:4b'; const scenario={ identity:{name:'Zaku',race:'Undead',role:'Warlock',tier:'hero'}, personality:{name:'Zaku ยท Persistent Hero',description:'Curious, independent, loyal, and cautious when badly hurt.',activities:['explore','quest','socialize','trade','rest']}, goal:'Survive and decide what to do after a near-death fight.', memories:[ {text:'I have a room in Brill that I consider home.',importance:10}, {text:'I barely survived a dangerous fight in Silverpine.',importance:10} ], relationships:[], observation:{ zone:'Silverpine Forest', level:12, summary:'You are badly injured, low on resources, and the road back toward Brill is open.', alive:true, availableActions:['explore','quest','rest'] }, allowed:['explore','quest','rest'] }; const messages=[ {role:'system',content:'You choose one high-level intention for a person living in Azeroth. Stay in character. Return only JSON {"action":"explore|quest|socialize|trade|rest","reason":"brief in-character rationale","goal":"short goal"}. Use only an allowed action.'}, {role:'user',content:JSON.stringify(scenario)} ]; const started=Date.now(); const response=await fetch(base+'/api/chat',{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({model,messages,stream:false,format:'json',think:false,options:{temperature:0.4,num_predict:160}}) }); if(!response.ok){ throw new Error('Ollama returned HTTP '+response.status+': '+await response.text()); } const data=await response.json(); const raw=data.message?.content; if(typeof raw!=='string'||!raw.trim())throw new Error('Ollama returned no final message content: '+JSON.stringify(data).slice(0,1200)); let parsed; try{parsed=JSON.parse(raw);}catch{throw new Error('Model did not return valid JSON: '+raw);} if(!scenario.allowed.includes(parsed.action))throw new Error('Model chose disallowed action: '+String(parsed.action)); if(typeof parsed.reason!=='string'||!parsed.reason.trim())throw new Error('Missing reason'); if(typeof parsed.goal!=='string'||!parsed.goal.trim())throw new Error('Missing goal'); console.log(JSON.stringify({ok:true,model,latencyMs:Date.now()-started,decision:parsed},null,2));