feat: implement AI-powered Human-in-the-Loop Norm Builder interface and standard CRUD endpoints

This commit is contained in:
2026-06-23 18:28:48 +00:00
parent a62c8d1cf4
commit e4b9cb8139
5 changed files with 447 additions and 2 deletions
+84
View File
@@ -597,4 +597,88 @@ export const analyzeWithMinimax = async (file: File, apiKey: string, model: stri
}
return cleanAndParseJson(content) as ReportData[];
};
export const buildStandardWithAI = async (
options: { provider: AIProvider; apiKey: string; model: string; endpoint?: string },
standardName: string,
categoryDescription: string
) => {
const prompt = `Você é um engenheiro de materiais sênior. O usuário solicitou a criação de uma tabela de limites para a norma técnica: "${standardName}".
A categoria deste material é descrita como: "${categoryDescription}".
Sua tarefa é acessar seu conhecimento sobre esta norma e gerar os limites estritos (mínimos e máximos) exigidos.
RESPONDA APENAS UM JSON VÁLIDO no seguinte formato EXATO:
{
"codigo": "Nome oficial da norma (ex: ASTM A36/A36M-19)",
"produto_especifico": "Descreva o tipo de produto que essa norma cobre",
"parametros_controle": {
"mecanicos": {
"nome_do_parametro_1": valor_numerico,
"nome_do_parametro_2": valor_numerico
},
"quimicos": {
"nome_do_parametro": valor_numerico
},
"fisicos": {
"nome_do_parametro": valor_numerico
}
}
}
Regras Cruciais:
1. USE APENAS NÚMEROS (floats/ints) nos valores. NUNCA use strings como "0.25" ou "250". Use 0.25 e 250.
2. Se a norma não especifica um limite máximo ou mínimo para um elemento, coloque null (sem aspas).
3. O nome da chave dentro de mecânicos/químicos/físicos deve ser em minúsculo, sem acentos, com underscores, e deve conter a unidade ou a palavra min/max (ex: "limite_escoamento_min_mpa", "carbono_max_percentual", "espessura_min_microns").
4. Crie APENAS os grupos (mecanicos, quimicos, fisicos) que fazem sentido para a norma solicitada.
5. DEVOLVA APENAS O JSON, sem nenhuma formatação ou texto explicativo ao redor.`;
try {
let resultJson = '';
if (options.provider === 'gemini') {
const ai = new GoogleGenAI({ apiKey: options.apiKey });
const response = await ai.models.generateContent({
model: options.model || 'gemini-2.5-flash',
contents: prompt
});
resultJson = response.text || '';
} else if (options.provider === 'openai') {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${options.apiKey}`
},
body: JSON.stringify({
model: options.model || 'gpt-4o',
messages: [{ role: "user", content: prompt }]
})
});
const data = await response.json();
resultJson = data.choices[0].message.content;
} else if (options.provider === 'openrouter') {
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${options.apiKey}`,
'HTTP-Referer': 'https://steelcheck.reifonas.cloud'
},
body: JSON.stringify({
model: options.model || 'google/gemini-2.5-flash',
messages: [{ role: "user", content: prompt }]
})
});
const data = await response.json();
resultJson = data.choices[0].message.content;
}
const parsed = cleanAndParseJson(resultJson);
return parsed;
} catch (error) {
console.error("Erro no buildStandardWithAI:", error);
throw new Error("Falha ao gerar norma com a IA. Tente novamente.");
}
};