mari-chat-3 / letter_generator.py
sirochild's picture
Upload 57 files
a73fa4e verified
raw
history blame
8.41 kB
"""
Letter generator that combines Groq and Gemini APIs for high-quality letter generation.
"""
import asyncio
from datetime import datetime
from typing import Dict, List, Optional, Any
import logging
from groq_client import GroqClient
from together_client import TogetherClient
logger = logging.getLogger(__name__)
class LetterGenerator:
"""Groq + Together AIの組み合わせによる高品質な手紙生成クラス"""
def __init__(self):
"""GroqとTogether AIクライアントを初期化"""
self.groq_client = GroqClient()
self.together_client = TogetherClient()
async def generate_letter(self, user_id: str, theme: str, user_history: Dict[str, Any]) -> Dict[str, Any]:
"""
テーマとユーザー履歴を基に完成した手紙を生成
Args:
user_id: ユーザーID
theme: 手紙のテーマ
user_history: ユーザーの履歴情報
Returns:
生成された手紙の情報を含む辞書
{
'content': '手紙の内容',
'metadata': {
'theme': 'テーマ',
'generated_at': '生成日時',
'groq_model': 'モデル名',
'together_model': 'モデル名',
'generation_time': 生成時間(秒),
'user_id': 'ユーザーID'
}
}
Raises:
Exception: 手紙生成に失敗した場合
"""
start_time = datetime.now()
try:
logger.info(f"ユーザー {user_id} のテーマ '{theme}' で手紙生成開始")
# ユーザーコンテキストを構築
context = self._build_context(theme, user_history)
# ステップ1: Groqで論理構造を生成
logger.info("Groqで論理構造を生成中...")
structure = await self.groq_client.generate_structure(theme, context)
# ステップ2: Together AIで感情表現を補完
logger.info("Together AIで感情表現を補完中...")
enhanced_context = {**context, 'theme': theme}
final_letter = await self.together_client.enhance_emotion(structure, enhanced_context)
# 生成時間を計算
generation_time = (datetime.now() - start_time).total_seconds()
# メタデータを構築
metadata = {
'theme': theme,
'generated_at': datetime.now().isoformat(),
'groq_model': self.groq_client.model,
'together_model': self.together_client.model,
'generation_time': generation_time,
'user_id': user_id,
'structure_length': len(structure),
'final_length': len(final_letter)
}
logger.info(f"手紙生成完了 (所要時間: {generation_time:.2f}秒)")
return {
'content': final_letter,
'metadata': metadata
}
except Exception as e:
generation_time = (datetime.now() - start_time).total_seconds()
logger.error(f"手紙生成失敗 (所要時間: {generation_time:.2f}秒): {str(e)}")
raise Exception(f"手紙生成に失敗しました: {str(e)}")
def _build_context(self, theme: str, user_history: Dict[str, Any]) -> Dict[str, Any]:
"""
ユーザー履歴を考慮したコンテキストを生成
Args:
theme: 手紙のテーマ
user_history: ユーザーの履歴情報
Returns:
生成用のコンテキスト辞書
"""
context = {
'theme': theme,
'user_history': user_history,
'previous_letters': [],
'interaction_count': 0
}
# 過去の手紙情報を抽出
if 'letters' in user_history:
letters = user_history['letters']
previous_letters = []
for date, letter_data in letters.items():
if isinstance(letter_data, dict) and letter_data.get('status') == 'completed':
previous_letters.append({
'date': date,
'theme': letter_data.get('theme', ''),
'content_preview': letter_data.get('content', '')[:100] + '...' if letter_data.get('content') else ''
})
# 日付順にソート(新しい順)
previous_letters.sort(key=lambda x: x['date'], reverse=True)
context['previous_letters'] = previous_letters[:5] # 最新5通まで
# ユーザープロファイル情報を抽出
if 'profile' in user_history:
profile = user_history['profile']
context['interaction_count'] = profile.get('total_letters', 0)
# 季節情報を追加
current_month = datetime.now().month
if current_month in [12, 1, 2]:
context['season'] = '冬'
elif current_month in [3, 4, 5]:
context['season'] = '春'
elif current_month in [6, 7, 8]:
context['season'] = '夏'
else:
context['season'] = '秋'
# 時間帯情報を追加
current_hour = datetime.now().hour
if 5 <= current_hour < 12:
context['time_of_day'] = '朝'
elif 12 <= current_hour < 17:
context['time_of_day'] = '昼'
elif 17 <= current_hour < 21:
context['time_of_day'] = '夕方'
else:
context['time_of_day'] = '夜'
return context
async def test_generation_pipeline(self, test_theme: str = "テスト") -> Dict[str, Any]:
"""
手紙生成パイプラインのテスト
Args:
test_theme: テスト用のテーマ
Returns:
テスト結果の辞書
"""
try:
# テスト用のユーザー履歴
test_user_history = {
'profile': {
'created_at': datetime.now().isoformat(),
'total_letters': 0
},
'letters': {},
'requests': {}
}
# テスト生成を実行
result = await self.generate_letter("test_user", test_theme, test_user_history)
return {
'success': True,
'result': result,
'message': 'テスト生成成功'
}
except Exception as e:
return {
'success': False,
'error': str(e),
'message': 'テスト生成失敗'
}
async def check_api_connections(self) -> Dict[str, bool]:
"""
両方のAPIクライアントの接続状態をチェック
Returns:
各APIの接続状態を示す辞書
"""
try:
groq_status = await self.groq_client.test_connection()
together_status = await self.together_client.test_connection()
return {
'groq': groq_status,
'together': together_status,
'overall': groq_status and together_status
}
except Exception as e:
logger.error(f"API接続チェック失敗: {str(e)}")
return {
'groq': False,
'together': False,
'overall': False,
'error': str(e)
}
def get_generation_stats(self) -> Dict[str, Any]:
"""
生成統計情報を取得(将来の拡張用)
Returns:
統計情報の辞書
"""
return {
'groq_model': self.groq_client.model,
'together_model': self.together_client.model,
'max_retries': max(self.groq_client.max_retries, self.together_client.max_retries),
'available': True
}