GitHub Copilotのチャットで非対応LLMを使う方法

GitHub Copilotのチャット機能ですが、複数のモデルから選択が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/78ca580b-d0a7-4a4b-8773-6c8e1ac576f9.png)

GitHub Copilotでは、「Bring your own language model key」という機能が用意されており、APIキーを持ち込んで利用することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/bb7f6d06-fae5-4409-96bb-f7b954f94402.png)

https://code.visualstudio.com/docs/copilot/language-models#_bring-your-own-language-model-key

ここにあるモデルであればキーをセットするだけで使えるのですが、ここにないモデルも使いたいんです。

一筋縄ではいかなかったのですが、ひねりにひねって非対応モデルでも動作させられましたので紹介します。

もちろん、自己責任でご利用ください。

:::note
チャットのモデルとコード補完のモデルは別々の設定があるようです。今回扱うのはチャットで使うモデルです。
:::


## 作戦

Bring your own language model keyで選べるOllamaを選択したうえで、テキスト生成の`/chat/completions`APIだけをLiteLLMを使って別のモデルにルーティングします。

設定のタイミングで`/chat/completions`以外のAPIが呼び出され、ここを突破しないと使えなかったのでこんな事になりました。

```
+-------------------------------------------------------+
|                  docker-compose.yml                    |
+-------------------------------------------------------+
|                                                       |
|  +---------------------+                              |
|  |       Nginx         |                              |
|  | +---------------+   |                              |
|  | |   Container   |   |                              |
|  | +---------------+   |                              |
|  |                     |                              |
|  | - image: nginx      |                              |
|  | - ports: "11434:80" |                              |
|  | - volumes:          |                              |
|  |   - ./nginx.conf    |                              |
|  +---------------------+                              |
|            |                                          |
|            |                                          |
|            v                                          |
|  +---------------------+     +---------------------+  |
|  |  nginx.conf         |     |       LiteLLM       |  |
|  |                     |     | +---------------+   |  |
|  | location /v1/chat/  |---->| |   Container   |   |  |
|  |   completions {     |     | +---------------+   |  |
|  |   proxy_pass        |     |                     |  |
|  |   litellm:4000;     |     | - image: litellm    |  |
|  |   }                 |     | - port: 4000        |  |
|  |                     |     | - config.yaml       |  |
|  | location / {        |     +---------------------+  |
|  |   proxy_pass        |                              |
|  |   ollama:11434;     |                              |
|  |   }                 |                              |
|  +---------------------+                              |
|            |                                          |
|            v                                          |
|  +---------------------+                              |
|  |       Ollama        |                              |
|  | +---------------+   |                              |
|  | |   Container   |   |                              |
|  | +---------------+   |                              |
|  |                     |                              |
|  | - image: ollama     |                              |
|  | - volumes: ./ollama |                              |
|  +---------------------+                              |
|                                                       |
+-------------------------------------------------------+
```

##　手順

以下のコードを準備します。

- docker-compose.yml

    <details><summary>こちら</summary>
    
    ```yaml:docker-compose.yml
    services:
      ollama:
        image: ollama/ollama:latest
        container_name: ollama
        volumes:
          - ./ollama:/root/.ollama
        networks:
          - app_network
        restart: unless-stopped
    
      litellm:
        image: ghcr.io/berriai/litellm:main-latest
        container_name: litellm
        volumes:
          - ./litellm_config.yaml:/app/config.yaml
          - ~/.aws:/root/.aws
        networks:
          - app_network
        restart: unless-stopped
        depends_on:
          - ollama
        command: [ "--config", "/app/config.yaml", "--port", "4000", "--num_workers", "8" ]
    
      nginx:
        image: nginx:alpine
        container_name: nginx_proxy
        ports:
          - "11434:80"
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf:ro
        networks:
          - app_network
        restart: unless-stopped
        depends_on:
          - ollama
          - litellm
    
    networks:
      app_network:
        driver: bridge
    ```
    
    </details>

- nginx.conf

    <details><summary>こちら</summary>
    
    ```nginx:nginx.conf
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    
    events {
        worker_connections 1024;
    }
    
    http {
        include /etc/nginx/mime.types;
        default_type application/json;
    
        log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log /var/log/nginx/access.log main;
        sendfile on;
        keepalive_timeout 65;
    
        server {
            listen 80;
            
            # /v1/chat/completions のみ litellm にルーティング
            location /v1/chat/completions {
                proxy_pass http://litellm:4000;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
            }
            
            # その他のAPIは ollama にルーティング
            location / {
                proxy_pass http://ollama:11434;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
            }
        }
    }
    ```

    </details>

- litellm_config.yaml

    <details><summary>こちら</summary>
    
    ```yaml:litellm_config.yaml
    model_list:
      - model_name: nova-premier
        litellm_params:
          model: bedrock/us.amazon.nova-premier-v1:0
          aws_region_name: us-east-1
    ```
    
    </details>

準備ができたら起動します。

```shell
docker compose up
```

それでは設定を行っていきます。

1. チャット欄にあるモデル名（この図だと`Claude 3.5 Sonnet`）をクリックし、「Manage models...」をクリックします

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/dee57b5d-8654-446f-9ae9-bffa90870873.png)

1. 「Ollama」を選択します

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/faf962d9-ea56-47b7-83a3-63250254b815.png)

1. モデル名を入力します（`nova-premier`の部分）。ここは`litellm_config.yaml`の`model_name`にセットした値です

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/44ec5253-4662-4a25-b562-0dd0287a6499.png)

1. `Configure advances settngs`は、「Yes」を選択します。（Noだと設定が失敗しちゃいます）

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/10b38a65-acf7-4c44-bcba-cda418796915.png)

1. `Enter a friendly name ...`は空欄のままでOKです
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/4ada0ea6-131a-495e-84a2-4419c122ebfc.png)

1. `maximum input tokens`や`maximum output tokens`もデフォルトのまま行ってみましょう
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/f4e3d78f-0716-425c-9da6-0b90a4b85265.png)

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/5aa08394-7355-4516-af9a-e83dd5bf502d.png)

1. `Does this model support vision`は画像のインプットに対応しているかどうかです。Amazon Nova Proで有効にしてみましたが、うまく動きませんでしたので「No」でいきましょう
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/72000d4a-a292-4985-851e-ffd91f870d66.png)

これで設定完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/244d341a-8c33-4192-b6c3-203d1daa1ed0.png)

モデルの一覧に追加されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b137eab2-0fec-491c-b4c9-92f94d1d3e65.png)

## チャットしてみる

チャットができました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d13a9c74-9823-4a70-a2c7-0a2d964b314a.png)

:::note
設定が終わったあとはOllamaは不要でLiteLLMだけでOKのようです。
:::


以上、誰得情報でした。