| | 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" |
| | language = language.strip().lower() |
| | code = match.group(2) |
| | if code.strip(): |
| | code_blocks.append({ |
| | "language": language, |
| | "code": code, |
| | "block_index": i |
| | }) |
| | |
| | else: |
| | |
| | python_patterns = [ |
| | r'import\s+\w+', |
| | r'from\s+\w+\s+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: |
| | |
| | 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): |
| | |
| | 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 |