jaytrainer / server.js
rrg92's picture
Added real modelName
037d3ee
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`)
})