GitHubのコードリーディングをするAIWebアプリ作ってみた。

こんにちは、学生でエンジニアの勉強をしているあまみやです。

普段はWeb3関係の開発の勉強やWebアプリの開発を行っているのですが、趣味でほかの人のソースコードを読んで開発手法勉強しています。

ただコードを読むにあたっての悩みがあり、それは大規模なプロジェクトであればあるほどコードの量が膨大で追いきれないことです...(汗)

そこで複数のAPIとNextjsを用いてコードリーディングを行うWebアプリを開発しました。

:::note alert
注意
今回のようなケースでは頻繁にAPIにアクセスしてしまうとbot判定を食らってしまう可能性があり、迷惑にならない範囲で利用してください。
:::

また完全な解説記事ではないので、いろいろggりながら見てもらえれば💦


# 実行環境
### フレームワーク
- nodejs 22.14.0 LTS
- Nextjs 15.2
- React 19
- PandaCSS

### エディター他
- vscode 
- GitHub Copilot
- Perplexity (API)
- llama-3.1-sonar-large-128k-online(Perplexity向けLLM)

nodejsのパッケージマネージャーはお好みで。
なお今回は`yarn`を使用します
### (補足)なぜPerplexityなのか?

もともと私がYmobileユーザーで、昨年SotbankとYmobileユーザー向けにPerplexityが一年無料になるキャンペーンを行っていました。

そこで有料プランに加入すると月当たり5ドルまでAPIが使用かのうになっており、コスパなどの面から使用しています。

Perplexityとsoftbankの関係については話が長くなるので省略。(すでに詳細がニュースや公式リリースにあがってます。)

# 開発手順
### ライブラリインストール

```bash
yarn add @pandacss/dev @types/next @types/next @types/react @types/react-dom axios next react react-dom typescript
```

今回はCSSライブラリにPandaCSSを使います。

Nextjsの環境構築はすでに情報があるので割愛。

### PandaCSS 初期設定
```bash
yarn add -D @pandacss/dev
```

```bash
yarn panda init --postcss
```
Nextjsのglobal.cssに以下を記載
```global.css
@layer reset, base, tokens, recipes, utilities;
```

### PerplexityとGitHubAPIキーの取得
- PerplexityのAPI

https://www.perplexity.ai/settings/api

- GitHubのAPI

https://github.com/settings/personal-access-tokens

GithubのAPIに関してはレポジトリの読み取り権限くらいでいいと思います(私もググりながら設定したのでくわしくないです...w)

一応GitHubのREST APIについては公式ドキュメントを見るのが確実だと思います。

https://docs.github.com/ja/rest/using-the-rest-api/getting-started-with-the-rest-api?apiVersion=2022-11-28

GitHubのAPIに関してはバージョン制なので注意です
2025年3月時点では`2022-11-28`が最新です。

# Nextjsのミドルウェア作成
perplexityのAPIへのPOST要求はaxiosを使っています。
当初は`fetch()`を使っていたのですが、なぜかうまくいかず...以前作った別のAIチャットアプリでもaxiosをつかっていたので採用しました。

```ai.ts
import axios from 'axios';

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { repourl, message } = req.body;
    const msg = repourl + " " + message;
    try {
      const response = await axios.post('https://api.perplexity.ai/chat/completions', {
        model: 'llama-3.1-sonar-large-128k-online',
        messages: [{ role: 'user', content: msg }],
      }, {
        headers: {
          'Authorization': `Bearer ${process.env.PERPLEXITY_API_KEY}`,
          'Content-Type': 'application/json',
        },
      });

      res.status(200).json({ answer: response.data.choices[0].message.content });
    } catch (error) {
      console.error('Error:', error);
      res.status(500).json({ error: 'An error occurred while processing your request.' });
    }
  } else {
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

```
`model`の部分に関しては一期一句間違えずに記載しないとエラーになります。
ただ公式ドキュメントのモデル一覧の説明がイマイチわかりにくいです(前はもっとわかりやすかった)

今回は`llama-3.1-sonar-large-128k-online`を使用しています。これはllamaをベースに改良されたものっぽいです。

また別途GitHubのAPIにアクセスするためのミドルウェアを作成しています。

```repo.ts
import { NextApiHandler } from "next"

const handler: NextApiHandler = async (req, res) => {
    const { user ,repo } = req.query
    const data = await fetch(`https://api.github.com/repos/${user}/${repo}`, {
        headers: {
            "Accept": "application/vnd.github.v3+json",
            // APIバージョン指定
            "X-Github-Api-Version": "2022-11-28",
            "Authorization": `token ${process.env.GITHUB_TOKEN}`,
        }
    })
    const data_json = await data.json()
    if (data_json.message === "Not Found") {
        res.status(404).json({ status: 66 })
        return
    } else {
        res.status(200).json({ data: data_json })
    }
    res.end()
}

export default handler
```


URLの形式を/user/_GITHUB_USER_ID_?repo=_REPOSITORY_NAME_ としそこから得られたユーザーIDとリポジトリ名を取得、それをミドルウェアを介してGitHubからデータを取得する流れです。


フロントエンド側では`getServerSideProps`関数を用いて上記のミドルウェアに対してPOST要求を行っています。
```[user].tsx
export async function getServerSideProps(context) {
    const { user, repo } = context.query
    try {
    //ミドルウェアPOST要求
        const response = await fetch(`http://<YOUR_SERVER_ADDRES>/api/repo?user=${user}&repo=${repo}`)
        if (!response.ok) {
            return {
                redirect: {
                //見つからなかった場合はリダイレクト
                    destination: '/404?status=not-found-repo',
                    permanent: false,
                }
            }
        }
        const github_code_json = await response.json()
        // 扱いやすいように形式を指定
        return {
            props: {
                data: {
                    status: "success",
                    user: user,
                    repo: repo,
                    data: github_code_json.data
                }
            },
        }
    } catch (error) {
        return {
            props: {
                data: {
                    status: "error",
                    error: error.message || "An unknown error occurred"
                }
            },
        }
    }
}
```

GitHubのAPI情報とフロントエンドから入力されたプロンプトをもとに先述の通りPerplexityのAPIにミドルウェア経由で要求を送信します。

### 最終的な動作の流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2081812/9fce5f62-804c-45ea-abc8-9a7716e77722.png)

# 実際に稼働させたものがこちら

これはGitHub上のトーバルズ氏本人(?)のLinuxリポジトリです。
![スクリーンショット 2025-03-20 165757.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2081812/e6be303e-2460-485d-894d-496e321ace82.png)


# まとめ
今回はAPIなどの関係でGitHubのみの対応になりましたが、今後はいろんなリポジトリも読み取れるようにしたいです。

またセキュリティ上の懸念はMarkDown形式表示への対応などまだまだ改良点はあると思います。

今回はPerplexityのAPIを使いましたが、理論上はGeminiやGPT4のAPIでも可能です。

# 今回の記事のGitHubリポジトリ

https://github.com/nknighta/codash


# 参考リンク
- Panda CSS

https://panda-css.com/docs/installation/nextjs

- Perplexity

https://www.perplexity.ai/

https://docs.perplexity.ai/