Deploy Inicial do SteelCheck com Docker Build Automatizado
This commit is contained in:
+54
-44
@@ -39,11 +39,14 @@ const cleanAndParseJson = (jsonString: string): any => {
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(cleanedString);
|
||||
// Se o modelo aninhou os dados dentro de uma chave "report"
|
||||
if (parsed.report && !parsed.identification) {
|
||||
return parsed.report;
|
||||
let finalData = parsed;
|
||||
// Se o modelo aninhou os dados dentro de uma chave "report" ou "reports"
|
||||
if (parsed.reports) {
|
||||
finalData = parsed.reports;
|
||||
} else if (parsed.report && !parsed.identification) {
|
||||
finalData = parsed.report;
|
||||
}
|
||||
return parsed;
|
||||
return Array.isArray(finalData) ? finalData : [finalData];
|
||||
} catch (error) {
|
||||
console.error("Failed to parse cleaned JSON:", jsonString);
|
||||
throw new Error("A resposta da IA não estava no formato JSON esperado.");
|
||||
@@ -143,37 +146,44 @@ const reportSchema = {
|
||||
required: ["confidence", "identification", "compliance", "overPerformance", "equivalents"]
|
||||
};
|
||||
|
||||
const PROMPT_BASE = `Você é o "SteelCheck", um especialista sênior em engenharia de materiais, metalurgia e controle de qualidade, com profundo conhecimento em aços estruturais (ASTM, EN, DIN), parafusos (ASTM, ISO), consumíveis de soldagem (AWS, ISO) e revestimentos industriais (ISO, NACE, SSPC).
|
||||
const arraySchema = {
|
||||
type: Type.ARRAY,
|
||||
items: reportSchema
|
||||
};
|
||||
|
||||
Sua tarefa é analisar o Certificado de Qualidade fornecido e gerar um "Relatório de Análise Técnica" em formato JSON, seguindo rigorosamente o schema fornecido.
|
||||
const PROMPT_BASE = `Você é o "SteelCheck", um especialista sênior em engenharia de materiais, metalurgia e controle de qualidade.
|
||||
|
||||
**Instrução Crítica:** Verifique a orientação do documento. Se estiver rotacionado, ajuste para a posição correta antes de extrair dados.
|
||||
Sua tarefa é analisar o Certificado de Qualidade fornecido e gerar um "Relatório de Análise Técnica" em formato JSON.
|
||||
|
||||
**Instrução Crítica 1 (Orientação):** A imagem PODE estar rotacionada (deitada ou de cabeça para baixo). Compense mentalmente a orientação correta antes de ler as tabelas.
|
||||
**Instrução Crítica 2 (Múltiplos Lotes/Corridas):** Se o certificado contiver MÚLTIPLOS LOTES (Batches) ou MÚLTIPLAS CORRIDAS (Heats) com resultados INDEPENDENTES, gere múltiplos relatórios! O JSON final DEVE SER UM ARRAY contendo 1 objeto para cada lote/corrida independente encontrado.
|
||||
|
||||
Execute:
|
||||
1. **Extração de Dados (OCR):** Extraia todos os dados primários do certificado.
|
||||
2. **Identificação de Normas:** Identifique a(s) norma(s) que o certificado alega atender.
|
||||
3. **Análise de Conformidade:** Compare os resultados com os requisitos mínimos e máximos das normas. Determine status como "OK" ou "FALHA" para cada item e "CONFORME" ou "NÃO CONFORME" geral.
|
||||
4. **Propriedades Mecânicas (mechanical):** Tabela de propriedades extraídas (ex: Yield Strength, Tensile Strength, Alongamento). Para CADA propriedade, calcule o percentual de quanto o material analisado é superior (ex: "+15%") ou inferior (ex: "-5%") em resistência ao material normativo teórico, e preencha no campo "differencePercentage".
|
||||
5. **Análise de Equivalência:** Liste materiais equivalentes de outras normas (EN, DIN, JIS, NBR, etc.).
|
||||
6. **Grau de Confiança:** Forneça um % de confiança baseado na qualidade do documento.
|
||||
1. Extração de Dados: Leia a imagem atentamente, respeitando as colunas.
|
||||
2. Identificação de Normas.
|
||||
3. Análise de Conformidade: Compare resultados x requisitos.
|
||||
4. Propriedades Mecânicas (mechanical): Calcule o "differencePercentage" (+X% ou -X%) em relação ao teórico.
|
||||
5. Análise de Equivalência e Grau de Confiança.
|
||||
|
||||
Retorne APENAS o objeto JSON, sem formatação adicional, seguindo EXATAMENTE esta estrutura de chaves (em inglês):
|
||||
{
|
||||
"confidence": 95,
|
||||
"identification": {
|
||||
"product": "", "standards": "", "manufacturer": "", "certificateNumber": "", "certificateDate": "", "batches": "", "heats": "", "quantity": ""
|
||||
},
|
||||
"compliance": {
|
||||
"status": "CONFORME ou NÃO CONFORME",
|
||||
"mechanical": [ { "property": "", "norm": "", "certificate": "", "status": "OK ou FALHA", "differencePercentage": "+X% ou -X%" } ],
|
||||
"chemical": [ { "element": "", "norm": "", "certificate": "", "status": "OK ou FALHA" } ],
|
||||
"otherTests": [ { "test": "", "norm": "", "certificate": "", "status": "OK ou FALHA" } ]
|
||||
},
|
||||
"overPerformance": [ { "property": "", "value": "" } ],
|
||||
"equivalents": [ { "system": "", "norm": "" } ]
|
||||
}`;
|
||||
Retorne APENAS o JSON, sem markdown. DEVE SER UM ARRAY DE OBJETOS:
|
||||
[
|
||||
{
|
||||
"confidence": 95,
|
||||
"identification": {
|
||||
"product": "", "standards": "", "manufacturer": "", "certificateNumber": "", "certificateDate": "", "batches": "LOTE X", "heats": "CORRIDA Y", "quantity": ""
|
||||
},
|
||||
"compliance": {
|
||||
"status": "CONFORME ou NÃO CONFORME",
|
||||
"mechanical": [ { "property": "", "norm": "", "certificate": "", "status": "OK ou FALHA", "differencePercentage": "+X% ou -X%" } ],
|
||||
"chemical": [ { "element": "", "norm": "", "certificate": "", "status": "OK ou FALHA" } ],
|
||||
"otherTests": [ { "test": "", "norm": "", "certificate": "", "status": "OK ou FALHA" } ]
|
||||
},
|
||||
"overPerformance": [ { "property": "", "value": "" } ],
|
||||
"equivalents": [ { "system": "", "norm": "" } ]
|
||||
}
|
||||
]`;
|
||||
|
||||
export const analyzeWithGemini = async (file: File, apiKey: string, model: string = 'gemini-2.5-flash'): Promise<ReportData> => {
|
||||
export const analyzeWithGemini = async (file: File, apiKey: string, model: string = 'gemini-2.5-flash'): Promise<ReportData[]> => {
|
||||
if (!apiKey) {
|
||||
throw new Error("A chave de API do Gemini não foi fornecida.");
|
||||
}
|
||||
@@ -186,7 +196,7 @@ export const analyzeWithGemini = async (file: File, apiKey: string, model: strin
|
||||
contents: { parts: [imagePart, { text: PROMPT_BASE }] },
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: reportSchema,
|
||||
responseSchema: arraySchema,
|
||||
temperature: 0.1,
|
||||
}
|
||||
});
|
||||
@@ -194,7 +204,7 @@ export const analyzeWithGemini = async (file: File, apiKey: string, model: strin
|
||||
try {
|
||||
const text = response.text;
|
||||
const data = cleanAndParseJson(text);
|
||||
return data as ReportData;
|
||||
return data as ReportData[];
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw e;
|
||||
@@ -203,7 +213,7 @@ export const analyzeWithGemini = async (file: File, apiKey: string, model: strin
|
||||
}
|
||||
};
|
||||
|
||||
export const analyzeWithOpenAI = async (file: File, apiKey: string, model: string = 'gpt-4o'): Promise<ReportData> => {
|
||||
export const analyzeWithOpenAI = async (file: File, apiKey: string, model: string = 'gpt-4o'): Promise<ReportData[]> => {
|
||||
if (!apiKey) {
|
||||
throw new Error("A chave de API da OpenAI não foi fornecida.");
|
||||
}
|
||||
@@ -252,10 +262,10 @@ export const analyzeWithOpenAI = async (file: File, apiKey: string, model: strin
|
||||
throw new Error("Resposta vazia da OpenAI");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
|
||||
export const analyzeWithAnthropic = async (file: File, apiKey: string, model: string = 'claude-3-sonnet-20240229'): Promise<ReportData> => {
|
||||
export const analyzeWithAnthropic = async (file: File, apiKey: string, model: string = 'claude-3-sonnet-20240229'): Promise<ReportData[]> => {
|
||||
if (!apiKey) {
|
||||
throw new Error("A chave de API da Anthropic não foi fornecida.");
|
||||
}
|
||||
@@ -311,10 +321,10 @@ export const analyzeWithAnthropic = async (file: File, apiKey: string, model: st
|
||||
throw new Error("Resposta vazia da Anthropic");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
|
||||
export const analyzeWithAzure = async (file: File, apiKey: string, endpoint: string, deployment: string): Promise<ReportData> => {
|
||||
export const analyzeWithAzure = async (file: File, apiKey: string, endpoint: string, deployment: string): Promise<ReportData[]> => {
|
||||
if (!apiKey || !endpoint) {
|
||||
throw new Error("A chave de API e endpoint do Azure são necessários.");
|
||||
}
|
||||
@@ -363,10 +373,10 @@ export const analyzeWithAzure = async (file: File, apiKey: string, endpoint: str
|
||||
throw new Error("Resposta vazia do Azure");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
|
||||
export const analyzeCertificate = async (options: AnalyzeOptions): Promise<ReportData> => {
|
||||
export const analyzeCertificate = async (options: AnalyzeOptions): Promise<ReportData[]> => {
|
||||
const { provider, apiKey, model, file, endpoint } = options;
|
||||
|
||||
switch (provider) {
|
||||
@@ -389,7 +399,7 @@ export const analyzeCertificate = async (options: AnalyzeOptions): Promise<Repor
|
||||
}
|
||||
};
|
||||
|
||||
export const analyzeWithOllama = async (file: File, endpoint: string, model: string = 'llama3.2-vision'): Promise<ReportData> => {
|
||||
export const analyzeWithOllama = async (file: File, endpoint: string, model: string = 'llama3.2-vision'): Promise<ReportData[]> => {
|
||||
if (!endpoint) {
|
||||
throw new Error("O endpoint do Ollama é necessário. Configure o endereço da sua VPS.");
|
||||
}
|
||||
@@ -456,10 +466,10 @@ export const analyzeWithOllama = async (file: File, endpoint: string, model: str
|
||||
throw new Error("Resposta vazia do Ollama/OpenWebUI");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
|
||||
export const analyzeWithOpenRouter = async (file: File, apiKey: string, model: string = 'google/gemini-2.5-flash'): Promise<ReportData> => {
|
||||
export const analyzeWithOpenRouter = async (file: File, apiKey: string, model: string = 'google/gemini-2.5-flash'): Promise<ReportData[]> => {
|
||||
if (!apiKey) {
|
||||
throw new Error("A chave de API do OpenRouter não foi fornecida.");
|
||||
}
|
||||
@@ -508,10 +518,10 @@ export const analyzeWithOpenRouter = async (file: File, apiKey: string, model: s
|
||||
throw new Error("Resposta vazia do OpenRouter");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
|
||||
export const analyzeWithMinimax = async (file: File, apiKey: string, model: string = 'minimax-2.7'): Promise<ReportData> => {
|
||||
export const analyzeWithMinimax = async (file: File, apiKey: string, model: string = 'minimax-2.7'): Promise<ReportData[]> => {
|
||||
if (!apiKey) {
|
||||
throw new Error("A chave de API do MiniMax não foi fornecida.");
|
||||
}
|
||||
@@ -558,5 +568,5 @@ export const analyzeWithMinimax = async (file: File, apiKey: string, model: stri
|
||||
throw new Error("Resposta vazia do MiniMax");
|
||||
}
|
||||
|
||||
return cleanAndParseJson(content) as ReportData;
|
||||
return cleanAndParseJson(content) as ReportData[];
|
||||
};
|
||||
Reference in New Issue
Block a user