Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| import random | |
| import logging | |
| import json | |
| try: | |
| from PyPDF2 import PdfReader | |
| except ImportError: | |
| # Fallback for Docker environment | |
| from pypdf import PdfReader | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Check for environment variable or use a default value for testing | |
| def get_api_key(): | |
| api_key = os.environ.get('DEEPSEEK_API_KEY') | |
| if not api_key: | |
| logger.warning("No DEEPSEEK_API_KEY environment variable found. Using mock responses.") | |
| return api_key | |
| # Mock evaluation result for testing | |
| def get_mock_evaluation(story_excerpt): | |
| # Use a short excerpt of the story in the evaluation to make it look personalized | |
| story_start = story_excerpt[:100].replace("\n", " ").strip() | |
| if len(story_start) > 50: | |
| story_start = story_start[:50] + "..." | |
| # Generate random scores but keep them reasonable | |
| unity_score = random.randint(6, 9) | |
| characters_score = random.randint(6, 9) | |
| decisive_moment_score = random.randint(7, 10) | |
| language_score = random.randint(5, 9) | |
| time_place_score = random.randint(6, 9) | |
| plot_score = random.randint(7, 10) | |
| ending_score = random.randint(6, 10) | |
| message_score = random.randint(6, 9) | |
| total_score = unity_score + characters_score + decisive_moment_score + language_score + \ | |
| time_place_score + plot_score + ending_score + message_score | |
| # Mock evaluation text | |
| return { | |
| "evaluation": f"""📋 التقييم: | |
| وحدة الحدث: {unity_score}/10 | |
| القصة تدور حول حدث رئيسي بشكل جيد، وتحافظ على تماسك الأحداث. | |
| الشخصيات المحدودة والمعرّفة: {characters_score}/10 | |
| الشخصيات محددة بوضوح ومتميزة، مع تطوير مناسب للشخصيات الرئيسية. | |
| التركيز على لحظة حاسمة: {decisive_moment_score}/10 | |
| القصة تبني بمهارة نحو لحظة التحول الرئيسية التي تغير مسار الأحداث. | |
| الإيجاز واقتصاد اللغة: {language_score}/10 | |
| اللغة موجزة ومعبرة، تنقل المعاني بفعالية دون إطناب غير ضروري. | |
| وحدة الزمان والمكان: {time_place_score}/10 | |
| هناك استخدام متناسق للإطار الزماني والمكاني، مما يعزز تماسك القصة. | |
| حبكة جيدة البناء: {plot_score}/10 | |
| الحبكة متطورة بشكل منطقي ومتماسك، مع تسلسل واضح للأحداث. | |
| نهاية مؤثرة: {ending_score}/10 | |
| النهاية تترك انطباعًا قويًا وتوفر إغلاقًا مناسبًا للأحداث. | |
| رسالة أو موضوع واضح: {message_score}/10 | |
| تُظهر القصة رسالة واضحة تتطور بشكل طبيعي من خلال الأحداث. | |
| النتيجة النهائية: {total_score}/80 | |
| {"هذا عمل أدبي متميز يتسم بقوة البناء والتماسك. القصة تحقق توازنًا جيدًا بين تطوير الشخصيات وتقدم الحبكة." if total_score > 65 else "رغم وجود عناصر قوية في القصة، هناك مجال للتحسين في بعض الجوانب. ننصح بالتركيز على تعزيز تماسك الأحداث وتطوير الشخصيات بشكل أعمق."} | |
| ملاحظة: بداية القصة "{story_start}" تظهر إمكانات جيدة وتجذب اهتمام القارئ.""", | |
| "fixed_story": None | |
| } | |
| def review_story(pdf_path): | |
| """ | |
| Review a story from a PDF file | |
| """ | |
| try: | |
| # Load text from PDF | |
| text = "" | |
| try: | |
| with open(pdf_path, "rb") as f: | |
| reader = PdfReader(f) | |
| text = "\n".join([p.extract_text() for p in reader.pages if p.extract_text()]) | |
| text = " ".join(text.split()) | |
| except Exception as e: | |
| logger.error(f"PDF loading failed: {e}") | |
| return {"evaluation": f"Error loading PDF: {e}", "fixed_story": None} | |
| # Get evaluation | |
| api_key = get_api_key() | |
| if not api_key: | |
| # Return mock evaluation | |
| logger.info("Using mock evaluation for PDF") | |
| return get_mock_evaluation(text) | |
| # Use API for evaluation | |
| return call_api_for_evaluation(text) | |
| except Exception as e: | |
| logger.error(f"Error in review_story: {e}") | |
| return {"evaluation": f"Error processing story: {e}", "fixed_story": None} | |
| def review_story_text(story_text): | |
| """ | |
| Review a story provided as text directly | |
| """ | |
| try: | |
| # Get evaluation | |
| api_key = get_api_key() | |
| if not api_key: | |
| # Return mock evaluation | |
| logger.info("Using mock evaluation for text") | |
| return get_mock_evaluation(story_text) | |
| # Use API for evaluation | |
| return call_api_for_evaluation(story_text) | |
| except Exception as e: | |
| logger.error(f"Error in review_story_text: {e}") | |
| return {"evaluation": f"Error processing story text: {e}", "fixed_story": None} | |
| def call_api_for_evaluation(story): | |
| """ | |
| Call the DeepSeek API for story evaluation | |
| """ | |
| api_key = get_api_key() | |
| if not api_key: | |
| return get_mock_evaluation(story) | |
| evaluation_prompt = f""" | |
| You are a professional literary critic specializing in the art of the short story. Your task is to evaluate the following story according to 8 essential criteria used in literary criticism. You must write the full evaluation in Modern Standard Arabic, using a clear and organized style. | |
| 🔹 For each criterion: | |
| Give a score out of 10. | |
| Write a brief explanation (one or two lines) that justifies the score. | |
| 🔹 At the end of the evaluation: | |
| Add up the scores to get a final result out of 80. | |
| If the score is above 65: Praise the story as a successful and well-crafted literary work. | |
| If the score is 65 or lower: Provide constructive criticism that highlights the main weaknesses and suggests how to improve them. | |
| Start the evaluation with a title that includes an emoji, such as: 📋 التقييم: | |
| 🔹 The evaluation criteria are: | |
| 1. Unity of event: Does the story revolve around a single main incident or situation? | |
| 2. Limited and defined characters: Does the story include a small number of clear and distinctive characters? | |
| 3. Focus on a decisive moment: Does the story highlight a turning point or critical decision? | |
| 4. Conciseness and economy of language: Is the language focused and free of unnecessary details? | |
| 5. Unity of time and place: Does the story take place in a specific time and setting? | |
| 6. Well-structured plot: Is there a clear logical sequence (beginning, middle, end)? | |
| 7. Impactful ending: Does the ending leave an emotional or intellectual impact? | |
| 8. Clear message or theme: Does the story convey a specific idea or feeling clearly? | |
| Evaluate the following story based on these criteria: | |
| {story} | |
| """ | |
| url = "https://api.deepseek.com/v1/chat/completions" | |
| headers = { | |
| "Authorization": f"Bearer {api_key}", | |
| "Content-Type": "application/json" | |
| } | |
| payload_eval = { | |
| "model": "deepseek-chat", | |
| "messages": [ | |
| {"role": "system", "content": "You are a formal short story evaluator."}, | |
| {"role": "user", "content": evaluation_prompt.strip()} | |
| ], | |
| "temperature": random.uniform(0.9, 1.0), | |
| "max_tokens": 2500 | |
| } | |
| try: | |
| logger.info("Sending request to DeepSeek API") | |
| response_eval = requests.post(url, headers=headers, json=payload_eval) | |
| if response_eval.status_code != 200: | |
| logger.error(f"API Error: {response_eval.status_code} - {response_eval.text}") | |
| return {"evaluation": f"Error from API: {response_eval.text}", "fixed_story": None} | |
| evaluation_result = response_eval.json()["choices"][0]["message"]["content"] | |
| logger.info("Successfully received evaluation from API") | |
| return { | |
| "evaluation": evaluation_result.strip(), | |
| "fixed_story": None | |
| } | |
| except Exception as e: | |
| logger.error(f"API call failed: {e}") | |
| return {"evaluation": f"Error calling API: {e}", "fixed_story": None} |