| | import json |
| | import logging |
| | import os |
| | import asyncio |
| | import tempfile |
| | from typing import List, Dict, Optional, Any |
| | import openai |
| |
|
| | from core.config import settings |
| | from core.prompts import get_report_prompt, get_report_suggestion_prompt |
| | from services.s3_service import s3_service |
| |
|
| | logger = logging.getLogger(__name__) |
| |
|
| | class ReportService: |
| | def __init__(self): |
| | self.openai_client = openai.OpenAI(api_key=settings.OPENAI_API_KEY) |
| |
|
| | async def generate_format_suggestions( |
| | self, |
| | file_key: Optional[str] = None, |
| | text_input: Optional[str] = None, |
| | language: str = "Japanese" |
| | ) -> List[Dict[str, str]]: |
| | """ |
| | Generates 4 AI-suggested report formats based on the content. |
| | Uses asyncio.to_thread for all blocking I/O operations. |
| | """ |
| | try: |
| | system_prompt = get_report_suggestion_prompt(language) |
| |
|
| | if file_key: |
| | |
| | tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") |
| | tmp_path = tmp.name |
| | tmp.close() |
| |
|
| | try: |
| | await asyncio.to_thread( |
| | s3_service.s3_client.download_file, |
| | settings.AWS_S3_BUCKET, |
| | file_key, |
| | tmp_path |
| | ) |
| | |
| | |
| | def upload_to_openai(): |
| | with open(tmp_path, "rb") as f: |
| | return self.openai_client.files.create( |
| | file=f, |
| | purpose="assistants" |
| | ) |
| | |
| | uploaded_file = await asyncio.to_thread(upload_to_openai) |
| | |
| | messages = [ |
| | {"role": "system", "content": system_prompt}, |
| | { |
| | "role": "user", |
| | "content": [ |
| | { |
| | "type": "file", |
| | "file": {"file_id": uploaded_file.id} |
| | } |
| | ] |
| | } |
| | ] |
| | |
| | |
| | response = await asyncio.to_thread( |
| | self.openai_client.chat.completions.create, |
| | model="gpt-4o-mini", |
| | messages=messages, |
| | response_format={"type": "json_object"}, |
| | temperature=0.7 |
| | ) |
| | |
| | |
| | await asyncio.to_thread( |
| | self.openai_client.files.delete, |
| | uploaded_file.id |
| | ) |
| | |
| | raw_content = response.choices[0].message.content |
| | |
| | finally: |
| | if os.path.exists(tmp_path): |
| | await asyncio.to_thread(os.remove, tmp_path) |
| | |
| | elif text_input: |
| | messages = [ |
| | {"role": "system", "content": system_prompt}, |
| | {"role": "user", "content": f"Analyze this content:\n\n{text_input}"} |
| | ] |
| | |
| | |
| | response = await asyncio.to_thread( |
| | self.openai_client.chat.completions.create, |
| | model="gpt-4o-mini", |
| | messages=messages, |
| | response_format={"type": "json_object"}, |
| | temperature=0.7 |
| | ) |
| | raw_content = response.choices[0].message.content |
| | |
| | else: |
| | raise ValueError("Either file_key or text_input must be provided") |
| |
|
| | data = json.loads(raw_content) |
| | return data.get("suggestions", []) |
| |
|
| | except Exception as e: |
| | logger.error(f"Format suggestion failed: {e}") |
| | return [] |
| |
|
| | async def generate_report( |
| | self, |
| | file_key: Optional[str] = None, |
| | text_input: Optional[str] = None, |
| | format_key: str = "briefing_doc", |
| | custom_prompt: Optional[str] = None, |
| | language: str = "Japanese" |
| | ) -> str: |
| | """ |
| | Generates a full report based on the selected format. |
| | Uses asyncio.to_thread for all blocking I/O operations. |
| | """ |
| | try: |
| | base_prompt = get_report_prompt(format_key, custom_prompt or "", language) |
| | |
| | |
| | if language == "Japanese": |
| | system_prompt = ( |
| | "あなたは日本語でレポートを作成するAIアシスタントです。すべての回答は日本語で書いてください。\n\n" |
| | f"{base_prompt}\n\n" |
| | "重要: レポート全体を日本語で書いてください。回答はマークダウン形式で、適切な見出し、箇跨書き、構造を使用して読みやすくフォーマットしてください。" |
| | ) |
| | else: |
| | system_prompt = ( |
| | "You are an AI assistant that creates reports in English. Write all responses in English.\n\n" |
| | f"{base_prompt}\n\n" |
| | "IMPORTANT: Write the entire report in English. Please format your response in markdown with proper headings, bullet points, and structure for easy reading." |
| | ) |
| |
|
| | if file_key: |
| | |
| | tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") |
| | tmp_path = tmp.name |
| | tmp.close() |
| |
|
| | try: |
| | await asyncio.to_thread( |
| | s3_service.s3_client.download_file, |
| | settings.AWS_S3_BUCKET, |
| | file_key, |
| | tmp_path |
| | ) |
| | |
| | |
| | def upload_to_openai(): |
| | with open(tmp_path, "rb") as f: |
| | return self.openai_client.files.create( |
| | file=f, |
| | purpose="assistants" |
| | ) |
| | |
| | uploaded_file = await asyncio.to_thread(upload_to_openai) |
| | |
| | messages = [ |
| | {"role": "system", "content": system_prompt}, |
| | { |
| | "role": "user", |
| | "content": [ |
| | { |
| | "type": "file", |
| | "file": {"file_id": uploaded_file.id} |
| | } |
| | ] |
| | } |
| | ] |
| | |
| | |
| | response = await asyncio.to_thread( |
| | self.openai_client.chat.completions.create, |
| | model="gpt-4o-mini", |
| | messages=messages, |
| | temperature=0.7 |
| | ) |
| | |
| | |
| | await asyncio.to_thread( |
| | self.openai_client.files.delete, |
| | uploaded_file.id |
| | ) |
| | |
| | return response.choices[0].message.content |
| | |
| | finally: |
| | if os.path.exists(tmp_path): |
| | await asyncio.to_thread(os.remove, tmp_path) |
| | |
| | elif text_input: |
| | messages = [ |
| | {"role": "system", "content": system_prompt}, |
| | {"role": "user", "content": f"Please analyze the following content and generate a report based on it:\n\n{text_input}"} |
| | ] |
| | |
| | |
| | response = await asyncio.to_thread( |
| | self.openai_client.chat.completions.create, |
| | model="gpt-4o-mini", |
| | messages=messages, |
| | temperature=0.7 |
| | ) |
| | return response.choices[0].message.content |
| | |
| | else: |
| | raise ValueError("Either file_key or text_input must be provided") |
| |
|
| | except Exception as e: |
| | logger.error(f"Report generation failed: {e}") |
| | raise |
| |
|
| | report_service = ReportService() |
| |
|