from typing import Optional, List, Dict, Any from openai import OpenAI import anthropic from dataclasses import dataclass from config.llm_settings import LLMSettings from core.file_scanner import FileInfo @dataclass class Message: role: str content: str class LLMService: MAX_TURNS = 5 def __init__(self): """LLMサービスの初期化""" self.settings = LLMSettings() self.current_model = self.settings.default_llm # API クライアントの初期化 if self.settings.anthropic_api_key: self.claude_client = anthropic.Anthropic(api_key=self.settings.anthropic_api_key) if self.settings.openai_api_key: self.openai_client = OpenAI(api_key=self.settings.openai_api_key) self.conversation_history: List[Message] = [] def switch_model(self, model: str): """使用するモデルを切り替え""" model_name = model.lower() if model_name == "claude" and self.settings.anthropic_api_key: self.current_model = "claude" elif model_name == "openai" and self.settings.openai_api_key: self.current_model = "openai" else: available = [] if self.settings.anthropic_api_key: available.append("claude") if self.settings.openai_api_key: available.append("openai") raise ValueError(f"利用可能なモデル: {', '.join(available)}") def create_prompt(self, content: str, query: str) -> str: """プロンプトを生成""" return f"""以下はGitHubリポジトリのコード解析結果です。このコードについて質問に答えてください。 コード解析結果: {content} 質問: {query} できるだけ具体的に、コードの内容を参照しながら回答してください。""" def _add_to_history(self, role: str, content: str): """会話履歴に追加(最大5ターン)""" self.conversation_history.append(Message(role=role, content=content)) # 最大ターン数を超えた場合、古い会話を削除 if len(self.conversation_history) > self.MAX_TURNS * 2: # 各ターンは質問と回答で2メッセージ self.conversation_history = self.conversation_history[-self.MAX_TURNS * 2:] def _format_messages_for_claude(self) -> List[Dict[str, str]]: """Claude用にメッセージをフォーマット""" return [{"role": msg.role, "content": msg.content} for msg in self.conversation_history] def _format_messages_for_gpt(self) -> List[Dict[str, str]]: """GPT用にメッセージをフォーマット""" return [ {"role": "system", "content": "あなたはコードアナリストとして、リポジトリの解析と質問への回答を行います。"}, *[{"role": msg.role, "content": msg.content} for msg in self.conversation_history] ] def get_conversation_history(self) -> List[Dict[str, str]]: """会話履歴を取得""" return [{"role": msg.role, "content": msg.content} for msg in self.conversation_history] def clear_history(self): """会話履歴をクリア""" self.conversation_history = [] def get_response(self, content: str, query: str) -> tuple[Optional[str], Optional[str]]: """LLMを使用して回答を生成""" try: prompt = self.create_prompt(content, query) self._add_to_history("user", prompt) print(f"Current model: {self.current_model}") # デバッグ用出力 if self.current_model.lower() == 'claude': print("Using Claude API") # デバッグ用出力 response = self.claude_client.messages.create( model="claude-3-5-sonnet-latest", messages=self._format_messages_for_claude() ) answer = response.content[0].text else: # gpt print("Using GPT API") # デバッグ用出力 response = openai.chat.completions.create( model="gpt-4", messages=self._format_messages_for_gpt() ) answer = response.choices[0].message.content self._add_to_history("assistant", answer) return answer, None except Exception as e: return None, f"エラーが発生しました: {str(e)}"