Spaces:
Running
Running
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 [ | |
"<start_of_turn>user" | |
,prompt+"<end_of_turn>" | |
,"<start_of_turn>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: <a href="https://iatalk.ing/tag/jay-trainer">https://iatalk.ing</a>') | |
}) | |
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`) | |
}) |