import re from abc import ABC, abstractmethod from typing import Dict, Any, List, Optional, Union, Tuple, Set class BaseValidator: """すべての検証クラスの基底クラス""" def __init__(self, client=None): """検証クラスの初期化""" self.client = client async def _make_async_api_call(self, model: str, messages: List[Dict[str, str]], **kwargs) -> Any: """非同期APIコールを行う共通メソッド""" try: response = self.client.chat.completions.create( model=model, messages=messages, **kwargs ) return response except Exception as e: print(f"[Error] API call failed: {str(e)}") raise def _extract_code_blocks(self, content: str) -> List[Dict[str, Any]]: """マークダウン形式のコードブロックを抽出する共通メソッド""" code_blocks = [] # 通常のマークダウンコードブロックを検出 pattern = r'```(\w+)?\s*\n([\s\S]*?)\n```' matches = list(re.finditer(pattern, content)) # マークダウンコードブロックが見つかった場合の処理 if matches: for i, match in enumerate(matches): language = match.group(1) or "python" # デフォルトはpython language = language.strip().lower() code = match.group(2) if code.strip(): # 空のコードブロックを除外 code_blocks.append({ "language": language, "code": code, "block_index": i }) # マークダウンコードブロックが見つからない場合は、インデントされたコードブロックやクラス定義を探す else: # Pythonのクラス定義やインポート文を探す python_patterns = [ r'import\s+\w+', # import文 r'from\s+\w+\s+import', # from import文 r'class\s+\w+\s*\(?\w*\)?:', # クラス定義 r'def\s+\w+\s*\(.*\):', # 関数定義 ] python_code = False for pattern in python_patterns: if re.search(pattern, content): python_code = True break if python_code: # コンテンツ全体をPythonコードとして扱う code_blocks.append({ "language": "python", "code": content, "block_index": 0 }) return code_blocks def _update_code_in_content(self, content: str, block_index: int, new_code: str) -> str: """マークダウン形式のコンテンツ内のコードブロックを更新する""" pattern = r'```(\w+)?\s*\n([\s\S]*?)\n```' matches = list(re.finditer(pattern, content)) if 0 <= block_index < len(matches): match = matches[block_index] language = match.group(1) or "unknown" start, end = match.span() # 元のコードブロックを新しいものに置き換え new_block = f"```{language}\n{new_code}\n```" content = content[:start] + new_block + content[end:] return content def _extract_nested_code_blocks(self, data: Dict[str, Any]) -> List[Dict[str, Any]]: """辞書/JSONから入れ子になったコードブロックを抽出する""" code_blocks = [] # 再帰的に辞書内のコードブロックを探索する関数 def extract_from_dict(data, current_path=[]): if isinstance(data, dict): # 'code', 'language' キーがあればコードブロックとして抽出 if "code" in data and isinstance(data["code"], str) and len(data["code"].strip()) > 0: language = data.get("language", "unknown") code_blocks.append({ "language": language, "code": data["code"], "location": current_path + ["code"] }) # 再帰的に全てのキーを探索 for key, value in data.items(): extract_from_dict(value, current_path + [key]) elif isinstance(data, list): for i, item in enumerate(data): extract_from_dict(item, current_path + [i]) # 抽出を開始 extract_from_dict(data) return code_blocks class Validator(ABC): """検証クラスのインターフェース""" @abstractmethod async def validate(self, content, context=None): """検証を実行する抽象メソッド""" pass @abstractmethod def get_result_summary(self): """検証結果の要約を返す抽象メソッド""" pass