👌
マネージド サービスの Redmine に対して Azure OpenAI Service を利用して自動返信する
TL;DR
- マネージド サービスの Redmine (ex. Lychee Redmine) だとプラグインを入れづらい
- API が提供されているので、Azure Functions などから定期的に実行して OpenAI と連携させる
- ChatGPT をふんだんに使ってとりあえずサンプル実装を作ってみた
実装例
OpenAI が動作する条件は以下の感じ。
- 直近 3 日以内に作成された課題のみを対象とする
- 課題にコメントがついていないもののみを対象とする
import requests
from datetime import datetime, timedelta
import os
import json
REDMINE_URL = os.environ.get('REDMINE_URL')
API_KEY = os.environ.get('API_KEY')
PROJECT_ID = os.environ.get('PROJECT_ID')
AZURE_OPENAI_URL = os.environ.get('AZURE_OPENAI_URL') # Azure OpenAI ServiceのURL
AZURE_OPENAI_KEY = os.environ.get('AZURE_OPENAI_KEY') # Azure OpenAI ServiceのAPIキー
AZURE_OPENAI_SYSTEM_PROMPT = os.environ.get('AZURE_OPENAI_SYSTEM_PROMPT')
def get_recent_issues():
one_day_ago = (datetime.now() - timedelta(days=3)).strftime("%Y-%m-%d")
endpoint = f"{REDMINE_URL}/issues.json"
params = {
"key": API_KEY,
"created_on": f">={one_day_ago}T00:00:00Z",
"project_id": PROJECT_ID
}
response = requests.get(endpoint, params=params)
response.raise_for_status()
# デバッグのための出力
print("=== DEBUG: Recent Issues Response JSON ===")
print(json.dumps(response.json(), indent=4))
print("==========================================")
issues = response.json().get('issues', [])
return issues
def get_issue_details(issue_id):
endpoint = f"{REDMINE_URL}/issues/{issue_id}.json"
params = {
"key": API_KEY,
"include": "journals"
}
response = requests.get(endpoint, params=params)
response.raise_for_status()
# デバッグのための出力
print(f"=== DEBUG: Details for Issue {issue_id} Response JSON ===")
print(json.dumps(response.json(), indent=4))
print("=======================================================")
return response.json().get('issue', {})
def add_comment_to_issue(issue_id, comment):
"""
Redmineの課題にコメントを追加する関数
"""
endpoint = f"{REDMINE_URL}/issues/{issue_id}.json"
headers = {
"Content-Type": "application/json",
"X-Redmine-API-Key": API_KEY
}
data = {
"issue": {
"notes": comment
}
}
response = requests.put(endpoint, headers=headers, json=data)
response.raise_for_status()
def ask_openai(question):
headers = {
"Content-Type": "application/json",
"api-key": AZURE_OPENAI_KEY
}
data = {
"messages": [{"role": "system", "content": AZURE_OPENAI_SYSTEM_PROMPT},
{"role": "user", "content": question}],
"max_tokens": 800,
"temperature": 0.7,
"frequency_penalty": 0,
"presence_penalty": 0,
"top_p": 0.95,
"stop": None
}
response = requests.post(f"https://{AZURE_OPENAI_URL}/openai/deployments/gpt-35-turbo/chat/completions?api-version=2023-07-01-preview", headers=headers, json=data)
response.raise_for_status()
return response.json().get("choices", [{}])[0].get("message", {}).get("content", "").strip()
if __name__ == "__main__":
recent_issues = get_recent_issues()
for issue in recent_issues:
issue_details = get_issue_details(issue['id'])
journals = issue_details.get('journals', [])
# コメントが一つもついていない課題のみを表示
if not journals:
print(f"ID: {issue_details['id']}, Subject: {issue_details['subject']}, Created on: {issue_details['created_on']}")
print("No comments or interactions.")
# Azure OpenAI Serviceに問い合わせ
question = f"Can you provide insights on this issue? Think in English and answer in Japanese - 'Subject: {issue_details['subject']}\nDescription: {issue_details.get('description', 'No description provided.')}'"
openai_response = ask_openai(question)
# デバッグ出力
print("=== Azure OpenAI Response ===")
print(openai_response)
print("==============================")
# OpenAIの回答をRedmineの課題にコメントとして追加
add_comment_to_issue(issue_details['id'], openai_response)
print("-" * 50)
参考
- REST API
- Rest Issues - Redmine
- Redmine で課題が起票された際に Azure OpenAI Service を利用して自動返信する
Discussion