Spaces:
Runtime error
Runtime error
| """ | |
| 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 | |
| } |