const express = require('express') const app = express() const port = 8080 process.on('uncaughtException', err => log('JayCoach:Exception:', err)) const hfToken = process.env.HF_TOKEN const MODELS = { 'phi3.5': { name: 'microsoft/Phi-3.5-mini-instruct' ,prompt: function(prompt){ return [ "<|user|>" ,prompt +"<|end|>" ,"<|assistant|>" ].join("\n") } } ,'lama3.1': { name: 'meta-llama/Meta-Llama-3.1-8B-Instruct' ,prompt: function(prompt){ return [ "<|start_header_id|>user<|end_header_id|>" ,prompt+"<|eot_id|><|start_header_id|>assistant<|end_header_id|>" +"" ].join("\n") } } ,'lama3.2v': { name: 'meta-llama/Llama-3.2-11B-Vision-Instruct' ,prompt: function(prompt){ return [ "<|start_header_id|>user<|end_header_id|>" ,prompt+"<|eot_id|><|start_header_id|>assistant<|end_header_id|>" +"" ].join("\n") } } ,'mistral-small':{ name: 'mistralai/Mistral-Small-Instruct-2409' ,prompt: function(prompt){ return [ "[INST]"+prompt+"[/INST]" ].join("\n") } } ,'gemma':{ name: 'google/gemma-1.1-7b-it' ,prompt: function(prompt){ return [ "user" ,prompt+"" ,"model" ,"" ].join("\n") } } } function log(m){ let d = new Date(); let diso = d.toISOString(); console.log(`${diso} ${m}`) } if(!hfToken){ throw new Error('NO TOKEN!'); } let LastWorkedModel = ''; const ModelNames = Object.keys(MODELS); const ModelList = [] let BestModel = {} for(let modelId in MODELS){ let model = MODELS[modelId] model.id = modelId model.stats = { total:0 ,erros:0 ,parcela:1 ,get errop() { return this.total === 0 ? 0 : this.erros/this.total } ,get pok() { return 1 - this.errop } } ModelList.push(model); } // Encontrar o modelo que oferece que tem melhor chances do acerto! /* Se você não entendeu o codigo abaixo, parabens. Eu tb não rsrs. brincadeir... Aqui é apenas uma pequena maneira de calcular o melhor modelo rpa ser usado... Cada model tem uma prop que chamei de pok (Percentual de OK = % de sucesso quando o model foi usado!) Então, vamos somar todos os percentuais de ok que temos, e atribuir uma parcela desse total pra cada model. O model que tiver mais % de ok dos demais, tem mais chances de ser escolhido do que um que tem menos... Exemplos: Phi3 Gemini Lama |----|--------|-----------------------------| 10% 20% 70% Phi3 Gemini Lama |--------|----------------|----------------| 20% 39.9% 40.1% Agora, escolhe ai um número entre 0 e 100%, aleatoriamente. Se for até 10%, pega Google... Se for até entre 10 e 20%, vai o Lama...e acma de 20% vai a Microsoft... Sacou a manha? Com isso, quanto mais um model nao da erro, mais ele tem a chance de ser escolhido! Se 2 models tem o mesmo peso, vamos usar um pequeno hack na conta pra nunca dar empate e um deles sempre ter 1 pouco a mais! O reusumo é: Imagine aquele meme da Nazaré fazendo as contas! */ function UpdateProbs(opts){ BestModel.LastRandom = Math.random(); let AllModels = []; // total de "oks" let Total = ModelList.reduce( (acc,m) => acc + m.stats.pok , 0 ) // calcula parcela de ok desse model! ModelList.forEach( m => m.stats.parcela = m.stats.pok/Total ) // Organiza pela ordem... let SortedModels = ModelList.sort( (a,b) => { let diff = parseFloat(a.stats.parcela.toPrecision(2)) - parseFloat(b.stats.parcela.toPrecision(2)); if(diff == 0) diff = a.stats.parcela*Math.random() - b.stats.parcela*Math.random() return diff; }) BestModel.LastSorted = SortedModels; let parcAcc = 0; for(let [idx,model] of SortedModels.entries()){ let stats = model.stats; parcAcc += stats.parcela; if(BestModel.LastRandom <= parcAcc){ BestModel.model = model.id; return; } } return; } async function GetModelAnswer(model, prompt){ let StartIndex; if(!model){ UpdateProbs(); model = BestModel.model; } let i = ModelList.length; while(i--){ // pra evitar um loop infinito, vai girar no maximo o numero de models... let ModelConfig = MODELS[model]; let MyStats = ModelConfig.stats; log("Stats:"); console.log(MyStats); let InferenceApi = 'https://api-inference.huggingface.co/models/' + ModelConfig.name; let data ={ inputs: ModelConfig.prompt(prompt) ,parameters:{ max_new_tokens: 70 ,return_full_text: false ,temperature: 0.5 } ,options:{ use_cache: false ,wait_for_model: false } } log("Falando com a IA 🤖") console.log(model, ModelConfig.name) MyStats.total++; let StartTime = new Date(); const response = await fetch( InferenceApi, { headers: { Authorization: "Bearer "+hfToken, "content-type":"application/json" }, method: "POST", body: JSON.stringify(data), } ); let EndTime = new Date(); let ElapsedTime = EndTime.getTime() - StartTime.getTime(); log("Total", ElapsedTime); if(response.status != 200){ MyStats.erros++; log('FAILED: Escolhendo outro...'+response.status) if(StartIndex == null) StartIndex = ModelList.map(m => m.id).indexOf(model); let NextIndex = StartIndex+1; if(NextIndex >= ModelList.length) NextIndex = 0; if(NextIndex == StartIndex){ log("Fiz de tudo, mas não deu bom :("); throw new Error('SOME_SHIT_HAPPENS'); } model = ModelList[NextIndex].id; log("Tentando com o ",model); continue; } // Validacoes adicionais de erros! // Tempo de resposta maior que que 2s? // Penaliza if(ElapsedTime >= 2500) MyStats.erros += 0.100; if(ElapsedTime < 900){ MyStats.erros -= 0.100; if(MyStats.erros < 0) MyStats.erros = 0; } log("Ok, lendo o json..."+response.status); const result = await response.json(); LastWorkedModel = model; return { result ,model } } // se chegou aqui é pq todo mundo falhou! throw new Error('Nenhum model respondeu! O trem tá feio ou o dev cagou em algo...') } async function Prompt(opts){ let error = opts.error let tentativas = opts.tentativas let max = opts.max let model = opts.model if(!max) max = 20 if(tentativas){ tentativas = 'últimas tentativas:'+tentativas } else tentativas = "" let tom = ""; let statusErro = "" if(error <= 450){ tom = `Gere mensagens com bastantes elogios e tom de dúvida (e sarcasmo) se foi realmente um humano que fez isso... EXEMPLOS: - Rapaz, acho que isso foi humanamente impossível...|fim| - Você não está usando um script não né?|fim| - Não é possível, tá muito baixo pra ter sido um ser humano...|fim| ` statusErro = "Objetivo atingido! Menor que 450" } else if(error <= 500){ tom = `Gere mensagens que parabenizem e elogiem o desempenho. EXEMPLOS: - Muito, muito, mas muito bom!|fim| - ora, ora ora, temos um Vingador da IA aqui|fim| - Você é o pica das galáxias da IA hein!|fim| ` statusErro = "Objetivo atingido! Menor que 500" }else if(error <= 2000){ tom = `Gere mensagens inspiradoras, no sentido em que está indo bem! EXEMPLOS: - Vamos lá, dá pra melhorar, você consegue|fim| - Não desista, continue tentando|fim| ` statusErro = "Objetivo não atingido! Maior que 500" } else { tom = `Gere mensagens sarcástias e engraçadas brincando com a situação. Faça piadas e zoeiras. EXEMPLOS: - Ei, psiu, volta aqui|fim| - Ou, não é pra aí não, volta aqui|fim| - Você ainda tá tentando ou tá só de brincadeira mesmo?|fim| - Ainda bem que eu não sou você hein...|fim| - Nossa, mas esse erro tá sensacionalmente errado!|fim| - Muito bom continue assim #sqn|fim| ` statusErro = "Objetivo não atingindo! Muito longe!" } let prompt = ` Um usuário está estudando Redes Neurais e IA e está aprendendo o conceito de Erro (erro quadrático médio). Ele está fazendo um exercício onde deve conseguir gerar um erro < 499 (menor que 499). ${tom} --- Informações das tentativas: Erro atual: ${error} ${tentativas} Status: ${statusErro} Gere uma mensagem para ser exibida ao usuário com base nas informacoes das tentativas fornecidas. Use emojis nas respostas, quando apropriado! REGRAS: - máx ${max} palavras - Responda como se estivesse falando diretamente com o usuário (use a segunda pessoa "você"). - encerrar com |fim| --- ` log("Prompt Info: ") console.log(prompt.length, prompt); let answer = await GetModelAnswer(model, prompt); return answer; } app.get('/error', async (req, res) => { let tentativas = req.query.tentativas; let max = 20; if(tentativas && tentativas.length >= 100){ res.json({error:"Tentando atacar né?"}) return; } let error = req.query.error; if(/^[^0-9]+$/g.test(error)){ res.json({error:"Tentando atacar né?"}) return; } if(tentativas) tentativas = tentativas.split(",").map(Number).join(","); let StartTime = new Date(); let result = await Prompt({ error:req.query.error ,tentativas ,model:req.query.model ,max }); let EndTime = new Date(); let TotalMs = EndTime.getTime() - StartTime.getTime(); let ModelInfo = MODELS[result.model] log("Respondido:") console.log(TotalMs, result); let resp = result.result; if(!resp || !Array.isArray(resp)){ res.json({text:":("}); ModelInfo.stats.erros += 0.2; return; } let gentext = resp[0].generated_text let textParts = gentext.split('|fim|'); if(textParts.length < 2){ ModelInfo.stats.erros += 0.1; } let txtFinal = textParts[0].trim(); let estimatedChars = max*8; if(txtFinal.length >= estimatedChars){ txtFinal = txtFinal.slice(0,estimatedChars); ModelInfo.stats.erros += 0.05; } log("FullResp:"+gentext); log(`Final:${txtFinal}`); res.json({text:txtFinal, model:result.model, hfName:ModelInfo.name, TotalMs}) }) app.get('/test', async (req, res) => { res.send("Working!") }) app.get('/models', async (req, res) => { //UpdateProbs() res.json({ BestModel }) }) app.get('/', async (req, res) => { res.send('JayCoach ON! Veja mais no blog IA Talking: https://iatalk.ing') }) app.use(function(err, req, res, next) { console.error(err.stack); res.json({error:'Server error, admin must check logs',status:res.status}) }); app.listen(port, () => { log(`JayCoach running`) })