"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Mieux - Une action GitHub de réviseur de code alimentée par l'IA

Mieux - Une action GitHub de réviseur de code alimentée par l'IA

Publié le 2024-11-08
Parcourir:551

Les révisions de code ont toujours été cruciales pour maintenir une norme et mettre l'accent sur les meilleures pratiques de code dans un projet. Il ne s'agit pas d'un article sur la façon dont les développeurs doivent réviser le code, il s'agit plutôt d'en déléguer une partie à l'IA.

Comme Michael Lynch le mentionne dans son article - "Comment effectuer des révisions de code comme un humain" - nous devrions laisser les ordinateurs s'occuper des parties ennuyeuses de la révision de code. Alors que Michael met l'accent sur un outil de formatage, j'aimerais aller plus loin et laisser l'intelligence artificielle le comprendre. Je veux dire, pourquoi ne pas profiter du boom de l’IA dans l’industrie ?

Maintenant, je ne dis pas que l'IA devrait être utilisée à la place des outils de formatage et des linters. Au lieu de cela, il doit être utilisé en plus de cela, pour détecter des éléments insignifiants qui pourraient manquer par un humain.

C'est pourquoi j'ai décidé de créer une action github dont le code examine une différence de demande d'extraction et génère des suggestions à l'aide de l'IA. Laissez-moi vous guider à travers cela.

? Note

  • Cette action GitHub est désormais disponible sur la place de marché GitHub.
  • C'est une action javascript - en savoir plus sur la création d'actions javascript github.

Obtenir la différence

Pour interagir avec l'API github, j'ai utilisé octokit, qui est une sorte de SDK ou de bibliothèque client pour interagir avec l'API github de manière idiomatique.

Pour que vous obteniez la différence de la demande d'extraction générée, vous devez transmettre l'en-tête Accept avec la valeur application/vnd.github.diff ainsi que les paramètres requis.

async function getPullRequestDetails(octokit, { mode }) {
    let AcceptFormat = "application/vnd.github.raw json";

    if (mode === "diff") AcceptFormat = "application/vnd.github.diff";
    if (mode === "json") AcceptFormat = "application/vnd.github.raw json";

    return await octokit.rest.pulls.get({
        owner: github.context.repo.owner,
        repo: github.context.repo.repo,
        pull_number: github.context.payload.pull_request.number,
        headers: {
            accept: AcceptFormat,
        },
    });
}

Si vous n'êtes pas du tout familier avec les actions github, voici une série d'actions github 101 de Victoria Lo et c'est un bon début.

Une fois que j'ai reçu la différence, je l'analyse et supprime les modifications indésirables, puis je la renvoie dans un schéma présenté ci-dessous :

/** using zod */
schema = z.object({
    path: z.string(),
    position: z.number(),
    line: z.number(),
    change: z.object({
        type: z.string(),
        add: z.boolean(),
        ln: z.number(),
        content: z.string(),
        relativePosition: z.number(),
    }),
    previously: z.string().optional(),
    suggestions: z.string().optional(),
})

Ignorer les fichiers

Ignorer les fichiers est assez simple. La liste des entrées utilisateur nécessite une chaîne de modèles globaux séparés par des points-virgules. Il est ensuite analysé, concaténé avec la liste par défaut des fichiers ignorés et dédupé.

**/*.md; **/*.env; **/*.lock;
const filesToIgnoreList = [
    ...new Set(
        filesToIgnore
            .split(";")
            .map(file => file.trim())
            .filter(file => file !== "")
            .concat(FILES_IGNORED_BY_DEFAULT)
    ),
];

La liste des fichiers ignorés est ensuite utilisée pour supprimer les modifications différentielles qui font référence à ces fichiers ignorés. Cela vous donne une charge utile brute contenant uniquement les modifications souhaitées.

Générer des suggestions

Une fois que j'ai obtenu la charge utile brute après avoir analysé la différence, je la transmets à l'API de la plateforme. Voici une implémentation de l'API OpenAI.

async function useOpenAI({ rawComments, openAI, rules, modelName, pullRequestContext }) {
    const result = await openAI.beta.chat.completions.parse({
        model: getModelName(modelName, "openai"),
        messages: [
            {
                role: "system",
                content: COMMON_SYSTEM_PROMPT,
            },
            {
                role: "user",
                content: getUserPrompt(rules, rawComments, pullRequestContext),
            },
        ],
        response_format: zodResponseFormat(diffPayloadSchema, "json_diff_response"),
    });

    const { message } = result.choices[0];

    if (message.refusal) {
        throw new Error(`the model refused to generate suggestions - ${message.refusal}`);
    }

    return message.parsed;
}

Vous remarquerez peut-être l'utilisation du format de réponse dans l'implémentation de l'API. Il s'agit d'une fonctionnalité fournie par de nombreuses plateformes LLM, qui vous permet d'indiquer au modèle de générer la réponse dans un schéma/format spécifique. C'est particulièrement utile dans ce cas car je ne veux pas que le modèle hallucine et génère des suggestions pour des fichiers ou des positions incorrectes dans la demande d'extraction, ou ajoute de nouvelles propriétés à la charge utile de la réponse.

L'invite système est là pour donner au modèle plus de contexte sur la manière dont il doit effectuer la révision du code et sur les éléments à garder à l'esprit. Vous pouvez afficher l'invite du système ici github.com/murtuzaalisurti/better.

L'invite utilisateur contient la différence réelle, les règles et le contexte de la demande d'extraction. C'est ce qui lance la révision du code.

Cette action github prend en charge les modèles OpenAI et Anthropic. Voici comment il implémente l'API Anthropic :

async function useAnthropic({ rawComments, anthropic, rules, modelName, pullRequestContext }) {
    const { definitions } = zodToJsonSchema(diffPayloadSchema, "diffPayloadSchema");
    const result = await anthropic.messages.create({
        max_tokens: 8192,
        model: getModelName(modelName, "anthropic"),
        system: COMMON_SYSTEM_PROMPT,
        tools: [
            {
                name: "structuredOutput",
                description: "Structured Output",
                input_schema: definitions["diffPayloadSchema"],
            },
        ],
        tool_choice: {
            type: "tool",
            name: "structuredOutput",
        },
        messages: [
            {
                role: "user",
                content: getUserPrompt(rules, rawComments, pullRequestContext),
            },
        ],
    });

    let parsed = null;
    for (const block of result.content) {
        if (block.type === "tool_use") {
            parsed = block.input;
            break;
        }
    }

    return parsed;
}

Ajouter des commentaires

Enfin, après avoir récupéré les suggestions, je les nettoie et les transmets à l'API github pour ajouter des commentaires dans le cadre de la révision.

J'ai choisi la méthode ci-dessous pour ajouter des commentaires, car en créant un nouvel avis, vous pouvez ajouter tous les commentaires en une seule fois au lieu d'ajouter un seul commentaire à la fois. L'ajout de commentaires un par un peut également déclencher une limitation du débit, car l'ajout de commentaires déclenche des notifications et vous ne souhaitez pas envoyer de notifications aux utilisateurs.

function filterPositionsNotPresentInRawPayload(rawComments, comments) {
    return comments.filter(comment =>
        rawComments.some(rawComment => rawComment.path === comment.path && rawComment.line === comment.line)
    );
}

async function addReviewComments(suggestions, octokit, rawComments, modelName) {
    const { info } = log({ withTimestamp: true }); // eslint-disable-line no-use-before-define
    const comments = filterPositionsNotPresentInRawPayload(rawComments, extractComments().comments(suggestions));

    try {
        await octokit.rest.pulls.createReview({
            owner: github.context.repo.owner,
            repo: github.context.repo.repo,
            pull_number: github.context.payload.pull_request.number,
            body: `Code Review by ${modelName}`,
            event: "COMMENT",
            comments,
        });
    } catch (error) {
        info(`Failed to add review comments: ${JSON.stringify(comments, null, 2)}`);
        throw error;
    }
}

Conclusion

Je voulais garder l'action github ouverte et ouverte aux intégrations et c'est pourquoi vous pouvez utiliser n'importe quel modèle de votre choix (voir la liste des modèles pris en charge), ou vous pouvez affiner et construire votre propre modèle personnalisé en plus des modèles de base pris en charge et utilisez-le avec cette action github.

Si vous rencontrez des problèmes de jetons ou de limitation de débit, vous souhaiterez peut-être mettre à niveau les limites de votre modèle en vous référant à la documentation de la plate-forme respective.

Alors, qu'est-ce que tu attends ? Si vous avez un référentiel sur github, essayez l'action maintenant - elle se trouve sur le marché des actions github.

Better - An AI powered Code Reviewer GitHub Action murtuzaalisurti / mieux

Une action github de réviseur de code alimentée par l'IA, prête à être utilisée dans votre flux de travail.

mieux

Une action github de révision de code optimisée par l'IA, prête à être utilisée dans votre flux de travail.

Pourquoi l'utiliser ?

  • Standardisez votre processus de révision de code
  • Obtenez des commentaires plus rapidement
  • Reconnaître les modèles qui entraînent un mauvais code
  • Détection des problèmes courants
  • Identifier les vulnérabilités de sécurité
  • Deuxième avis
  • Pour que les humains se concentrent sur des tâches plus complexes

Usage

1. Créer un flux de travail

Créez un fichier de workflow dans le dossier .github/workflows (créez s'il n'existe pas) de votre référentiel avec le contenu suivant :

name: Code Review
on
    pull_request:
        types: [opened, reopened, synchronize, ready_for_review]
        branches:
            - main # change this to your target branch
    workflow_dispatch: # Allows you to run the workflow manually from the Actions tab

permissions: # necessary permissions
    pull-requests: write
    contents: read

jobs:
    your-job-name:
        runs-on: ubuntu-latest
        name: your-job-name
        steps:
            - name: step-name
              id: step-id
              uses: murtuzaalisurti/better@v2 # this is
Passer en mode plein écranQuitter le mode plein écran
Voir sur GitHub
Déclaration de sortie Cet article est reproduit sur : https://dev.to/murtuzaalisurti/better-an-ai-powered-code-reviewer-github-action-2n28?1 En cas de violation, veuillez contacter [email protected] pour supprimer il
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3