WatNeru's picture
first commit
0447f30
"""
BaseAI - すべてのAIモデルアダプターの基底クラス
"""
from abc import ABC, abstractmethod
from typing import List, Tuple, Optional
class BaseAI(ABC):
"""
すべてのAIモデルアダプターが実装すべき基底クラス
各モデルは以下のメソッドを実装する必要があります:
- get_token_probabilities: トークン確率の取得
- build_chat_prompt: モデル固有のプロンプト形式への変換
"""
@abstractmethod
def get_token_probabilities(self, text: str, k: int = 5) -> List[Tuple[str, float]]:
"""
テキストから次のトークン候補と確率を取得
Args:
text: 入力テキスト(プロンプト)
k: 取得するトークン候補数
Returns:
List[Tuple[str, float]]: (トークン, 確率)のリスト(確率順)
"""
raise NotImplementedError
@abstractmethod
def build_chat_prompt(
self,
user_content: str,
system_content: str = "",
assistant_content: Optional[str] = None
) -> str:
"""
モデル固有のチャットプロンプト形式に変換
注意: モデルによってuser/assistantの分離方法が異なります
- OpenAI, Claude: user/assistantを明確に分離することを推奨
- Gemini: user/assistantを分離しない方が良い場合もある
- Transformers: モデルによって異なる(Llamaは分離推奨)
Args:
user_content: ユーザーのメッセージ
system_content: システムプロンプト(オプション)
assistant_content: アシスタントの既存応答(会話履歴用、オプション)
Returns:
str: モデル固有のプロンプト形式
"""
raise NotImplementedError
def _clean_text(self, text: str) -> str:
"""
制御文字・不可視文字・置換文字を厳密に取り除く(共通処理)
Args:
text: クリーンアップするテキスト
Returns:
str: クリーンアップされたテキスト
"""
if not text:
return ""
# 制御文字(0x00-0x1F、0x7F-0x9F)を除去
# ただし、改行・タブ・復帰は許可
cleaned = []
for ch in text:
code = ord(ch)
# 許可する制御文字: 改行(0x0A), タブ(0x09), 復帰(0x0D)
if code in [0x09, 0x0A, 0x0D]:
cleaned.append(ch)
# 通常の印刷可能文字
elif ch.isprintable():
# 置換文字(U+FFFD)を除去
if ch != "\uFFFD":
cleaned.append(ch)
# その他の制御文字や不可視文字は除去
result = "".join(cleaned)
# ゼロ幅文字を除去
result = result.replace("\u200B", "") # Zero-width space
result = result.replace("\u200C", "") # Zero-width non-joiner
result = result.replace("\u200D", "") # Zero-width joiner
result = result.replace("\uFEFF", "") # Zero-width no-break space
# その他の不可視文字(結合文字など)を除去
result = result.replace("\u200E", "") # Left-to-right mark
result = result.replace("\u200F", "") # Right-to-left mark
result = result.replace("\u202A", "") # Left-to-right embedding
result = result.replace("\u202B", "") # Right-to-left embedding
result = result.replace("\u202C", "") # Pop directional formatting
result = result.replace("\u202D", "") # Left-to-right override
result = result.replace("\u202E", "") # Right-to-left override
return result.strip()