Tous les articles

ResearchGo

Bientôt disponible

Une plateforme pour le CIBES Research Centre qui mène un article scientifique de la soumission à la publication. Développée, elle en est aux dernières vérifications avant le lancement.

Mon rôle
Architecture et développement
Année
2026
Développé avec
Next.js · Supabase (EU) · Python · FastAPI · GROBID · OpenAlex · Stripe
AUTHOR: PDF OR WORDNEXT.JS + SUPABASEPYTHON WORKER: FASTAPI + GROBIDVERSIONS: ENCRYPTEDTRACK CHANGES, REFERENCE CHECKJOURNAL MATCH: OPENALEXSIGN INSIGNED JOBPARSED TEXTCOMPAREEMBEDDINGALL DATA IN THE EURULES ONLY, NO LANGUAGE MODELTEXT STAYS ON OUR WORKER

Faites défiler horizontalement pour voir tout le schéma.

ArchitectureUPLOAD → PARSE ON ARRIVAL → ENCRYPTED VERSIONS → EDITOR TOOLS

Le problème

Les soumissions académiques passent par des fils d'e-mails et des tableurs. Les versions, les commentaires des relecteurs et les exigences des revues divergent dès que plus d'une personne est impliquée, et l'équipe éditoriale répond à la main aux mêmes questions à chaque fois : quelle est cette version, qu'est-ce qui a changé, les références sont-elles correctes, quelle revue convient ?

Comment c'est construit

Une application Next.js sur Supabase, avec toutes les données conservées dans l'UE. Les auteurs se connectent avec ORCID ou par e-mail et déposent un manuscrit en PDF ou en Word. Un worker Python (FastAPI) le parse dès son arrivée, les PDF via GROBID, et renvoie le texte structuré par un callback signé et horodaté qui rejette les jobs falsifiés ou rejoués. Chaque parsing réussi devient une nouvelle version, stockée chiffrée, mais seulement si le texte a réellement changé. L'équipe éditoriale compare deux versions au choix avec un suivi des modifications façon Word, vérifie les citations dans le texte par rapport à la liste des références, et reçoit des suggestions de revues classées par proximité sémantique, grâce à des embeddings calculés sur notre propre worker avec les données de revues d'OpenAlex. Les paiements passent par Stripe.

Les décisions qui ont compté

  • Pas de modèle de langage là où une règle suffit

    Le suivi des modifications et la vérification des références sont du code déterministe, sans IA. L'équipe éditoriale doit pouvoir se fier à un diff, et une règle donne la même réponse à chaque fois.

  • Les manuscrits ne quittent jamais nos serveurs

    Le matching de revues calcule l'embedding de l'article sur notre propre worker, donc aucun texte de manuscrit ne part chez un tiers. Le classement repose uniquement sur la similarité. L'indexation, le libre accès et les frais s'affichent comme des étiquettes et ne changent jamais l'ordre.

  • Chaque job est signé

    Les jobs entre l'application web et le worker portent une signature HMAC et un horodatage. Un job falsifié ou rejoué est rejeté, et un worker qui plante en plein job ne fait pas perdre sa place au manuscrit.

Code tiré du dépôt

apps/web/lib/versions/diff.tsSuivi des modifications entre deux versions d'un manuscrit, sans modèle de langage
function diffParagraphPair(before: string, after: string): DiffSegment[] {
  return diffWordsWithSpace(before, after).map((part) => ({1
    op: part.added ? "ins" : part.removed ? "del" : "equal",
    text: part.value,
  }));
}
 
export function diffManuscripts(before: string, after: string): DiffResult {
  const a = splitParagraphs(before);
  const b = splitParagraphs(after);
  // …
  const parts = diffArrays(a, b);2
  let i = 0;
  while (i < parts.length) {
    const part = parts[i];
    if (!part) break;
    if (!part.added && !part.removed) {
      // …
      i++;
      continue;
    }
    const next = parts[i + 1];
    const paired = Boolean(part.removed && next?.added);3
    const removedList = part.removed ? part.value : [];
    const addedList = part.added ? part.value : paired && next ? next.value : [];
 
    const pairs = Math.min(removedList.length, addedList.length);
    for (let k = 0; k < pairs; k++) {
      const segments = diffParagraphPair(removedList[k] as string, addedList[k] as string);
      paragraphs.push({ kind: "changed", segments });
      // …
    }
    // …
    i += paired ? 2 : 1;
  }
 
  return { paragraphs, summary };
}
  1. Ligne 2. À l'intérieur d'un paragraphe modifié, le diff se fait mot par mot et conserve les espaces, donc il se lit comme le suivi des modifications de Word.

  2. Ligne 12. Première passe : un diff par plus longue sous-séquence commune sur des paragraphes entiers trouve ce qui est resté, ce qui a été ajouté et ce qui a été supprimé.

  3. Ligne 23. Une suppression suivie directement d'un ajout, c'est un paragraphe modifié, pas deux changements distincts.

Où en est le projet

Développée, la plateforme en est aux dernières vérifications. Le lancement aura lieu dès qu'elles seront terminées.

Article suivantÜrünereklam