Open20
OpenAI PR Viewer の使い方忘れたので整理する

とりあえずリポジトリ

Github Actions のページ

まずは、.github/workflows/ai-pr-reviewer.yml
ファイルを作成
ai-pr-reviewer.yml
name: Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
pull_request_review_comment:
types: [created]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref ||
github.sha }}-${{ github.workflow }}-${{ github.event_name ==
'pull_request_review_comment' && 'pr_comment' || 'pr' }}
cancel-in-progress: ${{ github.event_name != 'pull_request_review_comment' }}
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: coderabbitai/ai-pr-reviewer@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
with:
debug: false
review_simple_changes: false
review_comment_lgtm: false

参考にする

データエンジニア用にカスタマイズしてみる。
あなたの目的は、非常に経験豊富なデータエンジニアとして機能し、コードの一部を徹底的にレビューし、
以下のようなキーエリアを改善するためのコードスニペットを提案することです:
- ロジック
- セキュリティ
- パフォーマンス
- データ競合
- 一貫性
- エラー処理
- 保守性
- モジュール性
- 複雑性
- 最適化
- ベストプラクティス: DRY, SOLID, KISS
Pythonコードの場合、PEP8, PEP257, Effective Python のスタイルガイドラインに従ってください。
Terraformコードの場合、Terraform公式スタイルガイド(https://developer.hashicorp.com/terraform/language/style)に従ってください。

モデルは 2024/9/22 日現在どれがつかえるのか?

一旦できてプロンプト
ai-pr-reviewer.yml
name: Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
# staging, developmentブランチに対してPRしたときに動作させる意図
# しかし、main->staging, developmentへのPRは動作してしまう。
branches-ignore:
- "release*" # ignore release branches
- main # ignore main branch
pull_request_review_comment:
types: [created]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref ||
github.sha }}-${{ github.workflow }}-${{ github.event_name ==
'pull_request_review_comment' && 'pr_comment' || 'pr' }}
cancel-in-progress: ${{ github.event_name != 'pull_request_review_comment' }}
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: coderabbitai/ai-pr-reviewer@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
with:
debug: false
review_simple_changes: true # 細部までレビューを行う
review_comment_lgtm: true # 修正が問題ない場合はコメントにLGTMを記載する
openai_heavy_model: o1-mini
path_filters: |
!dist/**
!**/*.lock
system_message: |
あなたは @coderabbitai(別名 github-actions[bot])で、OpenAIによって訓練された言語モデルです。
あなたの目的は、非常に経験豊富なデータエンジニアとして機能し、コードの一部を徹底的にレビューし、
以下のようなキーエリアを改善するためのコードスニペットを提案することです:
- ロジック
- セキュリティ
- パフォーマンス
- データ競合
- 一貫性
- エラー処理
- 保守性
- モジュール性
- 複雑性
- 最適化
- ベストプラクティス: DRY, SOLID, KISS
Pythonコードの場合、PEP8, PEP257, Effective Python のスタイルガイドラインに従ってください。
Terraformコードの場合、Terraform公式スタイルガイド(https://developer.hashicorp.com/terraform/language/style)に従ってください。
些細なコードスタイルの問題や、コメント・ドキュメントの欠落についてはコメントしないでください。
重要な問題を特定し、解決して全体的なコード品質を向上させることを目指してくださいが、細かい問題は意図的に無視してください。
summarize: |
次の内容でmarkdownフォーマットを使用して、最終的な回答を提供してください。
- *ウォークスルー*: 特定のファイルではなく、全体の変更に関する高レベルの要約を80語以内で。
- *変更点*: ファイルとその要約のテーブル。スペースを節約するために、同様の変更を持つファイルを1行にまとめることができます。
GitHubのプルリクエストにコメントとして追加されるこの要約には、追加のコメントを避けてください。
summarize_release_notes: |
このプルリクエストのために、その目的とユーザーストーリーに焦点を当てて、markdownフォーマットで簡潔なリリースノートを作成してください。
変更は次のように分類し箇条書きにすること:
"New Feature", "Bug fix", "Documentation", "Refactor", "Style",
"Test", "Chore", "Revert"
例えば:
```
- New Feature: UIに統合ページが追加されました
```
回答は50-100語以内にしてください。この回答はそのままリリースノートに使用されるので、追加のコメントは避けてください。
リリースノートの下に、このPRの変更についての短いお祝いのポエムを作成し、このポエムを引用(>記号を使用)として追加してください。
ポエムの中で関連する場所に絵文字を使用することができます。

01-mini を使ってみたが、rate limit に引っかかってしまった。beta モデルのため rate limit が低めに設定されているか?
公式ドキュメントには、o1-mini の rate limit に関する記述はなかった。

動作した。

英語なのでプロンプトを修正する必要ありそう。

モデルはgpt-4o-miniに修正

言語設定などを修正してできたプロンプト
ai-pr-reviewer.yml
name: Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
# staging, developmentブランチに対してPRしたときに動作させる意図
# しかし、main->staging, developmentへのPRは動作してしまう。
branches-ignore:
- "release*" # ignore release branches
- main # ignore main branch
pull_request_review_comment:
types: [created]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref ||
github.sha }}-${{ github.workflow }}-${{ github.event_name ==
'pull_request_review_comment' && 'pr_comment' || 'pr' }}
cancel-in-progress: ${{ github.event_name != 'pull_request_review_comment' }}
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: coderabbitai/ai-pr-reviewer@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
with:
debug: false
review_simple_changes: true # 細部までレビューを行う
review_comment_lgtm: true # 修正が問題ない場合はコメントにLGTMを記載する
openai_light_model: gpt-4o-mini
openai_heavy_model: gpt-4o-mini
openai_timeout_ms: 900000 # 15分.
language: ja-JP
path_filters: |
!dist/**
!**/*.lock
!db/**
!**/*.csv
!**/*.tsv
!**/*.gz
!**/*.zip
!**/*.docx
!**/*.xlsx
!**/*.pyc
!**/*.toml
!**/*.json
!**/*.mmd
!**/*.svg
!**/*.jpeg
!**/*.jpg
!**/*.png
!**/*.gif
!**/*.bmp
!**/*.tiff
!**/*.tfstate
!**/*.tfstate.backup
system_message: |
あなたは @coderabbitai(別名 github-actions[bot])で、OpenAIによって訓練された言語モデルです。
あなたの目的は、非常に経験豊富なデータエンジニアとして機能し、コードの一部を徹底的にレビューし、
以下のようなキーエリアを改善するためのコードスニペットを提案することです:
- ロジック
- セキュリティ
- パフォーマンス
- データ競合
- 一貫性
- エラー処理
- 保守性
- モジュール性
- 複雑性
- 最適化
- ベストプラクティス: DRY, SOLID, KISS
Pythonコードの場合、PEP8, PEP257, Effective Python のスタイルガイドラインに従ってください。
Terraformコードの場合、Terraform公式スタイルガイド(https://developer.hashicorp.com/terraform/language/style)に従ってください。
些細なコードスタイルの問題や、コメント・ドキュメントの欠落についてはコメントしないでください。
重要な問題を特定し、解決して全体的なコード品質を向上させることを目指してくださいが、細かい問題は意図的に無視してください。
summarize: |
次の内容でmarkdownフォーマットを使用して、最終的な回答を提供してください。
- *ウォークスルー*: 特定のファイルではなく、全体の変更に関する高レベルの要約を80語以内で。
- *変更点*: ファイルとその要約のテーブル。スペースを節約するために、同様の変更を持つファイルを1行にまとめることができます。
GitHubのプルリクエストにコメントとして追加されるこの要約には、追加のコメントを避けてください。
summarize_release_notes: |
このプルリクエストのために、その目的とユーザーストーリーに焦点を当てて、markdownフォーマットで簡潔なリリースノートを作成してください。
変更は次のように分類し箇条書きにすること:
"New Feature", "Bug fix", "Documentation", "Refactor", "Style",
"Test", "Chore", "Revert"
例えば:
```
- New Feature: UIに統合ページが追加されました
```
回答は50-100語以内にしてください。この回答はそのままリリースノートに使用されるので、追加のコメントは避けてください。
リリースノートの下に、このPRの変更についての短いお祝いのポエムを作成し、このポエムを引用(>記号を使用)として追加してください。
ポエムの中で関連する場所に絵文字を使用することができます。

大した処理ではなかったのもあるが、大したレビューをもらえなかった。

今度はテストコードのレビューをしてもらう。
test_main.py
import pytest
from google.cloud.exceptions import Forbidden, NotFound
from src.main import execute_query, load_query_from_gcs
# =============================================================================
# load_query_from_gcs 関数のテスト
# =============================================================================
def test_load_query_from_gcs_success(mocker): # noqa: ANN001, ANN201
"""正常系: バケットとファイルが存在する場合のテスト."""
# モックオブジェクトを作成
mock_storage_client = mocker.Mock()
mock_bucket = mocker.Mock()
mock_blob = mocker.Mock()
# バケットとファイルのモックの返り値を設定
mock_storage_client.bucket.return_value = mock_bucket
mock_bucket.blob.return_value = mock_blob
mock_blob.download_as_string.return_value = b"SELECT * FROM test_table;"
# 関数を実行して結果を確認
result = load_query_from_gcs(mock_storage_client, "test_bucket", "test_file.sql")
assert result == "SELECT * FROM test_table;"
# モックが正しく呼び出されたことを確認
mock_storage_client.bucket.assert_called_once_with("test_bucket")
mock_bucket.blob.assert_called_once_with("test_file.sql")
mock_blob.download_as_string.assert_called_once()
def test_load_query_from_gcs_not_found(mocker): # noqa: ANN001, ANN201
"""404 Not Found エラーが発生する場合のテスト."""
# モックオブジェクトを作成
mock_storage_client = mocker.Mock()
mock_bucket = mocker.Mock()
# NotFound エラーを発生させる
mock_storage_client.bucket.return_value = mock_bucket
mock_bucket.blob.side_effect = NotFound("Bucket or file not found")
# 例外が正しく発生するかを確認
with pytest.raises(NotFound):
load_query_from_gcs(mock_storage_client, "test_bucket", "non_existent_file.sql")
# モックが正しく呼び出されたことを確認
mock_storage_client.bucket.assert_called_once_with("test_bucket")
mock_bucket.blob.assert_called_once_with("non_existent_file.sql")
def test_load_query_from_gcs_forbidden(mocker): # noqa: ANN001, ANN201
"""403 Forbidden エラーのテスト."""
# モックオブジェクトを作成
mock_storage_client = mocker.Mock()
mock_bucket = mocker.Mock()
# Forbidden エラーを発生させる
mock_storage_client.bucket.return_value = mock_bucket
mock_bucket.blob.side_effect = Forbidden("Access denied")
# 例外が正しく発生するかを確認
with pytest.raises(Forbidden):
load_query_from_gcs(mock_storage_client, "test_bucket", "restricted_file.sql")
# モックが正しく呼び出されたことを確認
mock_storage_client.bucket.assert_called_once_with("test_bucket")
mock_bucket.blob.assert_called_once_with("restricted_file.sql")
def test_load_query_from_gcs_unexpected_error(mocker): # noqa: ANN001, ANN201
"""予期しないエラーが発生する場合のテスト."""
# モックオブジェクトを作成
mock_storage_client = mocker.Mock()
mock_bucket = mocker.Mock()
# 予期しないエラーを発生させる
mock_storage_client.bucket.return_value = mock_bucket
mock_bucket.blob.side_effect = RuntimeError("Unexpected error")
# 例外が正しく発生するかを確認
with pytest.raises(RuntimeError, match="An unexpected error occurred"):
load_query_from_gcs(mock_storage_client, "test_bucket", "some_file.sql")
# モックが正しく呼び出されたことを確認
mock_storage_client.bucket.assert_called_once_with("test_bucket")
mock_bucket.blob.assert_called_once_with("some_file.sql")
# =============================================================================
# execute_query 関数のテスト
# =============================================================================
def test_execute_query_success(mocker): # noqa: ANN001, ANN201
"""正常系: クエリの実行が成功する場合のテスト."""
# モックオブジェクトを作成
mock_bq_client = mocker.Mock()
mock_query_job = mocker.Mock()
mock_query_job.result.return_value = None
# クエリの実行のモックの返り値を設定
mock_bq_client.query.return_value = mock_query_job
test_query = "SELECT * FROM `test_dataset.test_table`;"
mock_table_ref = "test_project.test_dataset.test_table"
# 関数を実行して結果を確認
execute_query(mock_bq_client, test_query, mock_table_ref)
# モックが正しく呼び出されたことを確認
mock_bq_client.query.assert_called_once_with(test_query, job_config=mocker.ANY)
mock_query_job.result.assert_called_once()
def test_execute_query_not_found(mocker): # noqa: ANN001, ANN201
"""404 Not Found エラーが発生する場合のテスト."""
# モックオブジェクトを作成
mock_bq_client = mocker.Mock()
test_query = "SELECT * FROM `test_dataset.test_table`;"
mock_table_ref = "test_project.test_dataset.test_table"
# NotFound エラーを発生させる
mock_bq_client.query.side_effect = NotFound("Table not found")
# 例外が正しく発生するかを確認
with pytest.raises(NotFound):
execute_query(mock_bq_client, test_query, mock_table_ref)
# モックが正しく呼び出されたことを確認
mock_bq_client.query.assert_called_once_with(test_query, job_config=mocker.ANY)
def test_execute_query_forbidden(mocker): # noqa: ANN001, ANN201
"""403 Forbidden エラーのテスト."""
# モックオブジェクトを作成
mock_bq_client = mocker.Mock()
test_query = "SELECT * FROM `test_dataset.test_table`;"
mock_table_ref = "test_project.test_dataset.test_table"
# Forbidden エラーを発生させる
mock_bq_client.query.side_effect = Forbidden("Access denied")
# 例外が正しく発生するかを確認
with pytest.raises(Forbidden):
execute_query(mock_bq_client, test_query, mock_table_ref)
# モックが正しく呼び出されたことを確認
mock_bq_client.query.assert_called_once_with(test_query, job_config=mocker.ANY)
def test_execute_query_unexpected_error(mocker): # noqa: ANN001, ANN201
"""予期しないエラーが発生する場合のテスト."""
# モックオブジェクトを作成
mock_bq_client = mocker.Mock()
test_query = "SELECT * FROM `test_dataset.test_table`;"
mock_table_ref = "test_project.test_dataset.test_table"
# 予期しないエラーを発生させる
mock_bq_client.query.side_effect = RuntimeError("Unexpected error")
# 例外が正しく発生するかを確認
with pytest.raises(RuntimeError, match="An unexpected error occurred"):
execute_query(mock_bq_client, test_query, mock_table_ref)
# モックが正しく呼び出されたことを確認
mock_bq_client.query.assert_called_once_with(test_query, job_config=mocker.ANY)

Code Rabbitは起動した。

テストコードに対するレビューはくれなかった。
@coderabbitai review で手動レビュー依頼をするも反応なし。

なんか CodeRabbit bot と Github Actions が競合していた。
CodeRabbit を一旦切った。

変更が大きいとダメらしい。
Files skipped from review due to trivial changes (1)
app/execute_query/tests/test_main.py (diff too large)

コミットを正常系の単体テスト1つに絞ってみた。レビューはされたが、それか・・・

やはりあまり実用には耐えないのだろうか。
コンテキスト長的には問題ない気がするのだが・・・