メモ：langchainへのプルリクの出し方

# はじめに
langchainにプルリクエストを出してみたので、自分用にメモを残します。

# 経緯
EnsembleRetrieverの仕様で気になるところがあったので、後方互換性をもちつつユーザから指定できる選択肢を上げるためのプルリクを出してみました。

今回出したプルリクは以下です。

[langchain: add id_key option to EnsembleRetriever for metadata-based document merging #22950](https://github.com/langchain-ai/langchain/pull/22950)

# 基本方針
langchainのgithubから辿れる場所に[contribution guideline](https://python.langchain.com/v0.1/docs/contributing/)があるので基本的にはこれに従います。
判断に迷ったところを中心にメモします。

# masterブランチのfork
[langchain](https://github.com/langchain-ai/langchain)のmasterブランチを自分のアカウントにforkします。自分のgithubアカウントを持っていなければ作ってください。fork元はmasterブランチでよいのか少し迷いましたが、他のPRもmasterに対して出されていたので、それで良いと判断しました。

# clone
forkしたリポジトリをローカルにcloneします。
その後必要な編集を行います。
# poetry
テスト等の環境を構築するためにpoetryをインストールします。
筆者はM1 Macを使っており、このために初めてpoetryのインストールを試みましたが、以下の方法でうまくできました。
[M1 MacでpyenvとpoetryをインストールしてPython3.10.10環境を構築](https://qiita.com/nokonoko_1203/items/a694be4e76da0872f51a)

poetry公式のpipxを使う方法は試していません。
brewでinstallする方法は失敗しました。
# 環境構築
poetryをインストールしたら、poetryを使ってテスト等を実行するのに必要な環境構築を行います。
langchainリポジトリは複数のパッケージ（langhchain、core、community、experimental、など）で構成されています。
参考: [Repository Structure | langchain](https://python.langchain.com/v0.1/docs/contributing/repo_structure/)

環境構築はそれぞれのパッケージディクレクトリの配下で環境を構築できます。
今回回収の対象とするEnsembleRetrieverはlangchainの一部ですので、libs/langchainに移動してからコマンドを実行します（以降のコマンドも基本的にlibs/langchainの下で実行しています）

```sh
poetry install --with lint,typing,test,test_integration
```

参考: [Contribute Code](https://python.langchain.com/v0.1/docs/contributing/code/)

なおpoetry以前にpyenvやcondaを環境/パッケージマネジャーとして登録していた場合には、poetryを仮想環境として使うようにコマンドで指定する必要があるようです。

```
❗Note: If you use Conda or Pyenv as your environment/package manager, after installing Poetry, tell Poetry to use the virtualenv python environment (poetry config virtualenvs.prefer-active-python true)
```

筆者はcondaは使っておらずpyenvは使っていましたが、仮想環境を作る用途には使っていなかったので、特にpoetry config...のコマンドは実行しませんでした。実行しなくても、少なくとも今回はうまくいきました。

cloneと環境構築直後の状態でテストが通ることもとりあえず確かめておきました。

```sh
make test
```

# コード改修

コードに対して必要な改修を実施します。
今回はlibs/langchain/retrievers/ensemble.pyを編集しました。

またmake testを実行したところ、ensemble.pyのテストが存在しないようでした。テストしないままにプルリクするのは不安だったので、tests/unit_tests/retrievers/test_ensemble.pyというファイルを新規作成して、その中にtestを記述しました。

`make test`コマンドはMakefileを読んだ感じ、pytestを実行しているようでしたので、pytestで実行されることを前提に、記述しました。テストに必要な既存のlangchainクラスのインポート方法などは、同じ階層のtestスクリプト（test_multi_vector.pyなど）を参考にしました。
EnsembleRetrieverは複数のretrieverを引数に与えなければテストできないので、初期化時に与えられたDocumentのリストを返すだけのクラスをBaseRetrieverを継承して作って、テストに使いました。

参考: [langchain_core.retrievers.BaseRetriever](https://api.python.langchain.com/en/latest/retrievers/langchain_core.retrievers.BaseRetriever.html)

# テスト
`make test`を実行すると、新規に作成したtests/unit_tests/retrievers/test_ensemble.pyも実行してくれます。
pytestの仕様として`test_*.py`の形式のファイルをすべて実行するからです。
ただし他の関係ないテストも実行されて時間がかかるので、

```
poetry run pytest {filepath e.g. tests/unit_tests/retrievers/test_ensemble.py}
```

とすれば、１つだけ実行できて便利です。

# フォーマット
テストが無事通ったら

```sh
make format
```

を実行します。
１ファイルにだけ実行する方法があるのかよくわかりません。

# lint

```sh
make lint
```

で静的解析を実行します。
１ファイルにだけ実行する方法があるのかよくわかりません。

エラーが出たら修正します。
今回のプルリクでは2、3回エラーが出ました。
修正のたびに`make test`と`make format`も実行します。

# プルリク
test, format, lintがすべて通ったら、変更をコミットしてpushし、プルリクを発行します。
プルリクのタイトルやメッセージはプルリクを書く欄のプレースホルダーにガイドラインが書かれていたので、それに可能な範囲で従いました。プルリクメッセージについては、他の既存のものを眺めた感じ、そこまでプレースホルダーに厳密に沿っている感じでもなさそうでした。

プルリクを提出したら、おそらくllmによって自動でタグなどが付与されるようです。
github actionsによるテストはmaintainerがapproveするまでは実行されないようです。
プルリクが渋滞しているので気長に待ちましょう。

# おわりに
自分用のメモですが、同じことをやりたい方の参考になりましたら幸いです。