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} |