groxy / validators /base.py
Yasu777's picture
Update validators/base.py
d18d4fc verified
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