Checkpoint: Ajout de la configuration des champs obligatoires pour le score LLM

Nouvelle fonctionnalité permettant de configurer quels champs sont obligatoires ou optionnels pour atteindre un score de reconnaissance de 100%.

Modifications:
- Nouvelle table llmFieldsConfig dans la base de données
- Routes tRPC pour gérer la configuration (getAll, updateField)
- Interface utilisateur dans la page Paramètres avec tableau et cases à cocher
- Modification du code d'extraction pour générer dynamiquement l'instruction de score
- Initialisation automatique des champs par défaut (supplierName, invoiceNumber, invoiceDate, totalAmount obligatoires)
- Tests unitaires pour valider la fonctionnalité

L'utilisateur peut maintenant personnaliser quels champs doivent être détectés pour qu'une facture atteigne 100% de score.
This commit is contained in:
Manus
2026-02-13 03:39:41 -05:00
parent 96f6367094
commit d32359fc10
14 changed files with 1903 additions and 30 deletions

View File

@@ -209,17 +209,32 @@ const normalizeToolChoice = (
return toolChoice;
};
const resolveApiUrl = () =>
ENV.forgeApiUrl && ENV.forgeApiUrl.trim().length > 0
const resolveApiUrl = () => {
// If MISTRAL_API_KEY is set, use Mistral API directly
if (ENV.mistralApiKey && ENV.mistralApiKey.trim().length > 0) {
return "https://api.mistral.ai/v1/chat/completions";
}
// Otherwise use Manus Forge API
return ENV.forgeApiUrl && ENV.forgeApiUrl.trim().length > 0
? `${ENV.forgeApiUrl.replace(/\/$/, "")}/v1/chat/completions`
: "https://forge.manus.im/v1/chat/completions";
};
const assertApiKey = () => {
if (!ENV.forgeApiKey) {
throw new Error("OPENAI_API_KEY is not configured");
if (!ENV.mistralApiKey && !ENV.forgeApiKey) {
throw new Error("MISTRAL_API_KEY or OPENAI_API_KEY is not configured");
}
};
const getApiKey = () => {
// Prioritize MISTRAL_API_KEY if set
if (ENV.mistralApiKey && ENV.mistralApiKey.trim().length > 0) {
return ENV.mistralApiKey;
}
return ENV.forgeApiKey;
};
const normalizeResponseFormat = ({
responseFormat,
response_format,
@@ -279,8 +294,13 @@ export async function invokeLLM(params: InvokeParams): Promise<InvokeResult> {
response_format,
} = params;
// Use mistral-large-latest when MISTRAL_API_KEY is set, otherwise use gemini
const model = ENV.mistralApiKey && ENV.mistralApiKey.trim().length > 0
? "mistral-large-latest"
: "gemini-2.5-flash";
const payload: Record<string, unknown> = {
model: "gemini-2.5-flash",
model,
messages: messages.map(normalizeMessage),
};
@@ -297,8 +317,12 @@ export async function invokeLLM(params: InvokeParams): Promise<InvokeResult> {
}
payload.max_tokens = 32768
payload.thinking = {
"budget_tokens": 128
// Only add thinking parameter for Gemini models
if (!(ENV.mistralApiKey && ENV.mistralApiKey.trim().length > 0)) {
payload.thinking = {
"budget_tokens": 128
}
}
const normalizedResponseFormat = normalizeResponseFormat({
@@ -316,7 +340,7 @@ export async function invokeLLM(params: InvokeParams): Promise<InvokeResult> {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${ENV.forgeApiKey}`,
authorization: `Bearer ${getApiKey()}`,
},
body: JSON.stringify(payload),
});