Spaces:
Running
Running
Upload 11 files
Browse files- README.md +52 -104
- ai_service.py +62 -116
- app.py +3 -4
- prompts.py +251 -270
- requirements.txt +9 -8
README.md
CHANGED
@@ -8,121 +8,69 @@ app_file: app.py
|
|
8 |
pinned: false
|
9 |
---
|
10 |
|
11 |
-
#
|
12 |
|
13 |
-
##
|
14 |
-
|
15 |
-
|
16 |
-
## Backend API
|
17 |
-
This repository contains the FastAPI backend for the Rawi platform. It provides API endpoints for story generation, continuation, editing, and text-to-speech conversion.
|
18 |
-
|
19 |
-
## Features
|
20 |
-
- Interactive story generation based on user configuration
|
21 |
-
- Complete story generation in a single request
|
22 |
-
- Story continuation based on user choices or custom text
|
23 |
-
- Text-to-speech conversion for generated stories
|
24 |
-
- Story editing capabilities
|
25 |
-
|
26 |
-
## Deployment to Hugging Face Spaces
|
27 |
-
|
28 |
-
### Steps for Deployment
|
29 |
-
1. Create a new Hugging Face Space
|
30 |
-
- Go to [Hugging Face](https://huggingface.co/)
|
31 |
-
- Click on "New Space"
|
32 |
-
- Choose a name for your Space (e.g., "rawi-story-generator")
|
33 |
-
- Select "Docker" as the Space SDK
|
34 |
-
- Choose "CPU" as the hardware
|
35 |
-
|
36 |
-
2. Set Up Environment Variables
|
37 |
-
- After creating the Space, go to Settings > Repository Secrets
|
38 |
-
- Add your DeepSeek API key as a secret with the name `DEEPSEEK_API_KEY`
|
39 |
-
|
40 |
-
3. Upload Backend Files
|
41 |
-
- Clone your Space repository locally
|
42 |
-
- Copy all files from the `Back-End` directory to the repository
|
43 |
-
- Commit and push your changes
|
44 |
-
|
45 |
-
4. Update Frontend API URL
|
46 |
-
- In your frontend `script.js` file, update the `API_URL` to point to your Hugging Face Space:
|
47 |
-
```javascript
|
48 |
-
const API_URL = 'https://your-space-name.hf.space';
|
49 |
-
```
|
50 |
-
|
51 |
-
5. Deploy Your Frontend
|
52 |
-
- Host your frontend files on a web hosting service (GitHub Pages, Netlify, Vercel, etc.)
|
53 |
-
- Ensure CORS is properly configured in the backend to allow requests from your frontend domain
|
54 |
-
|
55 |
-
## Configuration
|
56 |
-
The backend can be configured using environment variables:
|
57 |
-
|
58 |
-
- `DEEPSEEK_API_KEY`: Your DeepSeek API key (required)
|
59 |
-
- `BACKEND_HOST`: Host to bind the server to (default: 0.0.0.0)
|
60 |
-
- `BACKEND_PORT`: Port to run the server on (default: 7860)
|
61 |
-
- `BASE_URL`: Base URL for the API (default: http://HOST:PORT)
|
62 |
-
- `AUDIO_STORAGE_PATH`: Path to store audio files (default: ./audio_files)
|
63 |
|
64 |
-
##
|
65 |
-
|
|
|
|
|
|
|
|
|
66 |
|
67 |
-
|
68 |
-
# Install dependencies
|
69 |
-
pip install -r requirements.txt
|
70 |
|
71 |
-
|
72 |
-
|
|
|
73 |
```
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
- `/api/stories/tts`: Generate text-to-speech for a story
|
80 |
-
- `/api/stories/edit`: Edit a story based on user instructions
|
81 |
-
- `/health`: Check the health status of the API
|
82 |
-
|
83 |
-
For detailed API documentation, visit the `/docs` endpoint after deploying the API.
|
84 |
-
|
85 |
-
---
|
86 |
-
|
87 |
-
# راوي - منصة توليد القصص العربية بالذكاء الاصطناعي
|
88 |
-
|
89 |
-
## نظرة عامة
|
90 |
-
راوي هي منصة لتوليد القصص العربية باستخدام الذكاء الاصطناعي DeepSeek. تتيح المنصة للمستخدمين إعداد القصص، والمشاركة في تشكيل السرد، والاستماع إلى القصص باستخدام تقنية تحويل النص إلى صوت.
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
- تحويل النص إلى صوت للقصص المولدة
|
97 |
-
- إمكانيات تعديل القصة
|
98 |
|
99 |
-
|
|
|
|
|
|
|
100 |
|
101 |
-
###
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
- اختر اسمًا لمساحتك (مثل "rawi-story-generator")
|
106 |
-
- اختر "Docker" كـ SDK للمساحة
|
107 |
-
- اختر "CPU" كعتاد
|
108 |
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
|
|
123 |
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
127 |
|
128 |
-
|
|
|
|
|
|
|
|
|
|
8 |
pinned: false
|
9 |
---
|
10 |
|
11 |
+
# رَاوي (Rawi) - منصة القص العربي بالذكاء الاصطناعي
|
12 |
|
13 |
+
## نظرة عامة
|
14 |
+
خدمة API لتوليد قصص عربية تفاعلية باستخدام الذكاء الاصطناعي. تتيح المنصة للمستخدمين إنشاء قصص مخصصة بناءً على معلمات محددة، والتفاعل مع القصة من خلال خيارات متعددة، والاستماع إلى القصص المنشأة بتقنية تحويل النص إلى كلام.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
+
## المميزات
|
17 |
+
- إنشاء قصص عربية أصلية باستخدام نماذج الذكاء الاصطناعي من DeepSeek
|
18 |
+
- تخصيص معلمات القصة مثل الطول والنوع والشخصيات
|
19 |
+
- القص التفاعلي مع خيارات متعددة للمسارات
|
20 |
+
- تحويل النص إلى صوت عربي
|
21 |
+
- واجهة برمجة تطبيقات RESTful متكاملة
|
22 |
|
23 |
+
## واجهة برمجة التطبيقات (API)
|
|
|
|
|
24 |
|
25 |
+
### تهيئة قصة جديدة
|
26 |
+
```
|
27 |
+
POST /api/stories/initialize
|
28 |
```
|
29 |
|
30 |
+
### متابعة القصة
|
31 |
+
```
|
32 |
+
POST /api/stories/continue
|
33 |
+
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
### تحويل القصة إلى صوت
|
36 |
+
```
|
37 |
+
POST /api/stories/tts
|
38 |
+
```
|
|
|
|
|
39 |
|
40 |
+
### تعديل القصة
|
41 |
+
```
|
42 |
+
POST /api/stories/edit
|
43 |
+
```
|
44 |
|
45 |
+
### الحصول على القصة كاملة
|
46 |
+
```
|
47 |
+
GET /api/stories/story/{story_id}
|
48 |
+
```
|
|
|
|
|
|
|
49 |
|
50 |
+
### الوصول إلى الملفات الصوتية
|
51 |
+
```
|
52 |
+
GET /audio/{filename}
|
53 |
+
```
|
54 |
|
55 |
+
## فحص الحالة والمعلومات
|
56 |
+
```
|
57 |
+
GET /health
|
58 |
+
```
|
59 |
|
60 |
+
## المتطلبات البيئية
|
61 |
+
- `DEEPSEEK_API_KEY`: مفتاح API للوصول إلى نماذج DeepSeek
|
62 |
+
- `BACKEND_HOST`: مضيف الخادم (افتراضي: 0.0.0.0)
|
63 |
+
- `BACKEND_PORT`: منفذ الخادم (افتراضي: 7860)
|
64 |
+
- `BASE_URL`: عنوان URL الأساسي للخدمة
|
65 |
+
- `AUDIO_STORAGE_PATH`: مسار تخزين الملفات الصوتية (افتراضي: ./audio_files)
|
66 |
|
67 |
+
## التكامل مع التطبيقات
|
68 |
+
تم تصميم هذه الخدمة للعمل مع:
|
69 |
+
1. واجهة المستخدم الويب لـراوي
|
70 |
+
2. تطبيق Flutter للأجهزة المحمولة
|
71 |
|
72 |
+
## توثيق واجهة برمجة التطبيقات
|
73 |
+
يمكن الوصول إلى وثائق API التفاعلية الكاملة عبر:
|
74 |
+
```
|
75 |
+
https://[your-space-name].hf.space/docs
|
76 |
+
```
|
ai_service.py
CHANGED
@@ -28,68 +28,29 @@ load_dotenv()
|
|
28 |
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
|
29 |
DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
|
30 |
|
31 |
-
# تكوين التسجيل
|
32 |
-
logging.basicConfig(level=logging.INFO,
|
33 |
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
34 |
-
logger = logging.getLogger(__name__)
|
35 |
-
|
36 |
# تخزين سياق القصص
|
37 |
# في نظام حقيقي، يجب استخدام قاعدة بيانات بدلاً من التخزين في الذاكرة
|
38 |
stories_context = {}
|
39 |
stories_metadata = {}
|
40 |
|
41 |
-
# تخزين مؤقت للإستجابات المتكررة
|
42 |
-
response_cache = {}
|
43 |
-
|
44 |
-
|
45 |
-
def timed_lru_cache(seconds: int, maxsize: int = 128):
|
46 |
-
"""
|
47 |
-
تخزين مؤقت للدوال مع انتهاء صلاحية بعد وقت محدد
|
48 |
-
"""
|
49 |
-
def wrapper_cache(func):
|
50 |
-
func = lru_cache(maxsize=maxsize)(func)
|
51 |
-
func.lifetime = seconds
|
52 |
-
func.expiration = time.time() + seconds
|
53 |
-
|
54 |
-
@functools.wraps(func)
|
55 |
-
def wrapped_func(*args, **kwargs):
|
56 |
-
if time.time() > func.expiration:
|
57 |
-
func.cache_clear()
|
58 |
-
func.expiration = time.time() + func.lifetime
|
59 |
-
|
60 |
-
return func(*args, **kwargs)
|
61 |
-
|
62 |
-
return wrapped_func
|
63 |
-
|
64 |
-
return wrapper_cache
|
65 |
-
|
66 |
|
67 |
async def generate_response(messages: List[Dict], retries=3, backoff_factor=1.5) -> str:
|
68 |
"""
|
69 |
-
|
70 |
|
71 |
Args:
|
72 |
-
messages:
|
73 |
-
retries:
|
74 |
-
backoff_factor:
|
75 |
|
76 |
Returns:
|
77 |
-
str:
|
78 |
|
79 |
Raises:
|
80 |
-
Exception:
|
81 |
"""
|
82 |
if not DEEPSEEK_API_KEY:
|
83 |
-
|
84 |
-
raise Exception("API key not available. Please check the environment variables setup.")
|
85 |
-
|
86 |
-
# Generate a cache key from the messages
|
87 |
-
cache_key = hash(str(messages))
|
88 |
-
|
89 |
-
# Check if we have a cached response
|
90 |
-
if cache_key in response_cache:
|
91 |
-
logger.info("Using cached response")
|
92 |
-
return response_cache[cache_key]
|
93 |
|
94 |
headers = {
|
95 |
"Content-Type": "application/json",
|
@@ -97,96 +58,70 @@ async def generate_response(messages: List[Dict], retries=3, backoff_factor=1.5)
|
|
97 |
}
|
98 |
|
99 |
payload = {
|
100 |
-
"model": "deepseek-chat",
|
101 |
"messages": messages,
|
102 |
-
"temperature": 0.
|
103 |
-
"max_tokens":
|
104 |
}
|
105 |
|
106 |
last_exception = None
|
107 |
|
108 |
-
#
|
109 |
for attempt in range(retries):
|
110 |
try:
|
111 |
-
logger.info(f"API call attempt {attempt+1}/{retries}")
|
112 |
-
|
113 |
async with httpx.AsyncClient() as client:
|
114 |
response = await client.post(
|
115 |
DEEPSEEK_API_URL,
|
116 |
headers=headers,
|
117 |
json=payload,
|
118 |
-
timeout=
|
119 |
)
|
120 |
|
121 |
if response.status_code == 429: # Rate Limit
|
122 |
-
#
|
123 |
wait_time = backoff_factor * (2 ** attempt)
|
124 |
-
|
125 |
await asyncio.sleep(wait_time)
|
126 |
continue
|
127 |
|
128 |
if response.status_code != 200:
|
129 |
-
error_msg = f"DeepSeek API
|
130 |
-
|
131 |
last_exception = Exception(error_msg)
|
132 |
|
133 |
-
#
|
134 |
wait_time = backoff_factor * (2 ** attempt)
|
135 |
await asyncio.sleep(wait_time)
|
136 |
continue
|
137 |
|
138 |
result = response.json()
|
139 |
if "choices" not in result or not result["choices"]:
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
continue
|
144 |
-
|
145 |
-
content = result["choices"][0]["message"]["content"]
|
146 |
-
|
147 |
-
# Cache the response for future use (expiring after 1 hour)
|
148 |
-
response_cache[cache_key] = content
|
149 |
-
|
150 |
-
# Remove old cache entries if cache is too large
|
151 |
-
if len(response_cache) > 100:
|
152 |
-
# Remove a random entry
|
153 |
-
response_cache.pop(next(iter(response_cache)))
|
154 |
-
|
155 |
-
return content
|
156 |
-
|
157 |
-
except httpx.TimeoutException as e:
|
158 |
-
error_msg = f"API call timed out: {str(e)}"
|
159 |
-
logger.error(error_msg)
|
160 |
-
last_exception = Exception(error_msg)
|
161 |
-
|
162 |
-
# Wait before trying again
|
163 |
-
wait_time = backoff_factor * (2 ** attempt)
|
164 |
-
await asyncio.sleep(wait_time)
|
165 |
-
continue
|
166 |
|
167 |
except httpx.HTTPError as e:
|
168 |
-
error_msg = f"
|
169 |
-
|
170 |
last_exception = Exception(error_msg)
|
171 |
|
172 |
-
#
|
173 |
wait_time = backoff_factor * (2 ** attempt)
|
174 |
await asyncio.sleep(wait_time)
|
175 |
continue
|
176 |
|
177 |
except Exception as e:
|
178 |
-
error_msg = f"
|
179 |
-
|
180 |
last_exception = Exception(error_msg)
|
181 |
|
182 |
-
#
|
183 |
wait_time = backoff_factor * (2 ** attempt)
|
184 |
await asyncio.sleep(wait_time)
|
185 |
continue
|
186 |
|
187 |
-
#
|
188 |
-
|
189 |
-
raise last_exception or Exception("Failed to connect to DeepSeek API after multiple attempts")
|
190 |
|
191 |
|
192 |
def parse_paragraph_and_choices(response_text: str) -> Tuple[str, Optional[List[StoryChoice]]]:
|
@@ -231,10 +166,29 @@ def parse_paragraph_and_choices(response_text: str) -> Tuple[str, Optional[List[
|
|
231 |
|
232 |
async def initialize_story(config: StoryConfig) -> StoryResponse:
|
233 |
"""
|
234 |
-
بدء قصة جديدة
|
235 |
"""
|
|
|
|
|
|
|
|
|
|
|
236 |
story_id = str(uuid.uuid4())
|
237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
# إنشاء البرومبت الأولي
|
239 |
system_prompt = get_system_prompt()
|
240 |
user_prompt = create_story_init_prompt(config)
|
@@ -388,41 +342,33 @@ async def continue_story_with_text(story_id: str, custom_text: str) -> StoryResp
|
|
388 |
|
389 |
# Create a prompt for continuing with custom text
|
390 |
custom_prompt = f"""
|
391 |
-
|
392 |
|
393 |
{story_context_text}
|
394 |
|
395 |
-
|
396 |
|
397 |
"{custom_text}"
|
398 |
|
399 |
-
|
400 |
-
|
401 |
-
Remember that the story is currently at:
|
402 |
-
- Paragraph number: {current_paragraph} of {max_paragraphs}
|
403 |
-
- If this is the final or penultimate paragraph, conclude the story appropriately.
|
404 |
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
3. Make sure your paragraph is substantial and richly detailed (minimum 6-8 lines).
|
409 |
-
4. The entire response MUST be in Arabic language only.
|
410 |
-
5. Then present 3 new options for the user to choose from to continue the story.
|
411 |
|
412 |
-
|
413 |
|
414 |
-
الفقرة:
|
415 |
-
[Write the new paragraph of the story here in Arabic, ensuring it is 6-8 lines long with rich details]
|
416 |
|
417 |
الخيارات:
|
418 |
-
1. [
|
419 |
-
2. [
|
420 |
-
3. [
|
421 |
|
422 |
-
|
423 |
|
424 |
-
العنوان:
|
425 |
-
[Write an appropriate title for the complete story in Arabic]
|
426 |
"""
|
427 |
|
428 |
print(f"Custom prompt created, length: {len(custom_prompt)}")
|
|
|
28 |
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
|
29 |
DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
|
30 |
|
|
|
|
|
|
|
|
|
|
|
31 |
# تخزين سياق القصص
|
32 |
# في نظام حقيقي، يجب استخدام قاعدة بيانات بدلاً من التخزين في الذاكرة
|
33 |
stories_context = {}
|
34 |
stories_metadata = {}
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
async def generate_response(messages: List[Dict], retries=3, backoff_factor=1.5) -> str:
|
38 |
"""
|
39 |
+
استدعاء DeepSeek API وتوليد استجابة بناءً على سلسلة الرسائل مع محاولة إعادة المحاولة في حالة الفشل
|
40 |
|
41 |
Args:
|
42 |
+
messages: قائمة برسائل المحادثة
|
43 |
+
retries: عدد محاولات إعادة المحاولة في حالة فشل الاتصال (افتراضي: 3)
|
44 |
+
backoff_factor: معامل التأخير للمحاولات المتتالية (افتراضي: 1.5)
|
45 |
|
46 |
Returns:
|
47 |
+
str: محتوى الاستجابة من API
|
48 |
|
49 |
Raises:
|
50 |
+
Exception: في حالة فشل جميع المحاولات
|
51 |
"""
|
52 |
if not DEEPSEEK_API_KEY:
|
53 |
+
raise Exception("مفتاح API غير متوفر. يرجى التحقق من إعداد المتغيرات البيئية.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
headers = {
|
56 |
"Content-Type": "application/json",
|
|
|
58 |
}
|
59 |
|
60 |
payload = {
|
61 |
+
"model": "deepseek-chat", # استبدل باسم النموذج المناسب من DeepSeek API
|
62 |
"messages": messages,
|
63 |
+
"temperature": 0.7,
|
64 |
+
"max_tokens": 1000
|
65 |
}
|
66 |
|
67 |
last_exception = None
|
68 |
|
69 |
+
# محاولات إعادة الاتصال في حالة فشل API
|
70 |
for attempt in range(retries):
|
71 |
try:
|
|
|
|
|
72 |
async with httpx.AsyncClient() as client:
|
73 |
response = await client.post(
|
74 |
DEEPSEEK_API_URL,
|
75 |
headers=headers,
|
76 |
json=payload,
|
77 |
+
timeout=60.0
|
78 |
)
|
79 |
|
80 |
if response.status_code == 429: # Rate Limit
|
81 |
+
# انتظار فترة قبل المحاولة مرة أخرى
|
82 |
wait_time = backoff_factor * (2 ** attempt)
|
83 |
+
print(f"تجاوز حد معدل الطلبات، انتظار {wait_time} ثانية قبل المحاولة مرة أخرى.")
|
84 |
await asyncio.sleep(wait_time)
|
85 |
continue
|
86 |
|
87 |
if response.status_code != 200:
|
88 |
+
error_msg = f"فشل طلب DeepSeek API: {response.status_code} - {response.text}"
|
89 |
+
print(error_msg)
|
90 |
last_exception = Exception(error_msg)
|
91 |
|
92 |
+
# انتظار فترة قبل المحاولة مرة أخرى
|
93 |
wait_time = backoff_factor * (2 ** attempt)
|
94 |
await asyncio.sleep(wait_time)
|
95 |
continue
|
96 |
|
97 |
result = response.json()
|
98 |
if "choices" not in result or not result["choices"]:
|
99 |
+
raise Exception("تنسيق استجابة DeepSeek API غير صالح")
|
100 |
+
|
101 |
+
return result["choices"][0]["message"]["content"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
|
103 |
except httpx.HTTPError as e:
|
104 |
+
error_msg = f"خطأ في اتصال HTTP: {str(e)}"
|
105 |
+
print(error_msg)
|
106 |
last_exception = Exception(error_msg)
|
107 |
|
108 |
+
# انتظار فترة قبل المحاولة مرة أخرى
|
109 |
wait_time = backoff_factor * (2 ** attempt)
|
110 |
await asyncio.sleep(wait_time)
|
111 |
continue
|
112 |
|
113 |
except Exception as e:
|
114 |
+
error_msg = f"خطأ غير متوقع: {str(e)}"
|
115 |
+
print(error_msg)
|
116 |
last_exception = Exception(error_msg)
|
117 |
|
118 |
+
# انتظار فترة قبل المحاولة مرة أخرى
|
119 |
wait_time = backoff_factor * (2 ** attempt)
|
120 |
await asyncio.sleep(wait_time)
|
121 |
continue
|
122 |
|
123 |
+
# إذا وصلنا إلى هنا، فقد فشلت جميع المحاولات
|
124 |
+
raise last_exception or Exception("فشل الاتصال بـ DeepSeek API بعد عدة محاولات")
|
|
|
125 |
|
126 |
|
127 |
def parse_paragraph_and_choices(response_text: str) -> Tuple[str, Optional[List[StoryChoice]]]:
|
|
|
166 |
|
167 |
async def initialize_story(config: StoryConfig) -> StoryResponse:
|
168 |
"""
|
169 |
+
بدء قصة جديدة بناءً على التكوين المقدم
|
170 |
"""
|
171 |
+
# If this is a non-interactive story, generate the whole story at once
|
172 |
+
if hasattr(config, 'interactive') and not config.interactive:
|
173 |
+
return await generate_complete_story(config)
|
174 |
+
|
175 |
+
# For interactive stories, continue with the normal flow
|
176 |
story_id = str(uuid.uuid4())
|
177 |
|
178 |
+
# إنشاء سياق أولي للقصة
|
179 |
+
stories_context[story_id] = {
|
180 |
+
"paragraphs": [],
|
181 |
+
"current_paragraph": 0
|
182 |
+
}
|
183 |
+
|
184 |
+
# حفظ معلومات القصة
|
185 |
+
length_info = get_story_length_instructions(config.length)
|
186 |
+
stories_metadata[story_id] = {
|
187 |
+
"config": config.dict(),
|
188 |
+
"max_paragraphs": length_info["paragraphs"],
|
189 |
+
"title": None
|
190 |
+
}
|
191 |
+
|
192 |
# إنشاء البرومبت الأولي
|
193 |
system_prompt = get_system_prompt()
|
194 |
user_prompt = create_story_init_prompt(config)
|
|
|
342 |
|
343 |
# Create a prompt for continuing with custom text
|
344 |
custom_prompt = f"""
|
345 |
+
لقد وصلنا إلى هذه النقطة في القصة:
|
346 |
|
347 |
{story_context_text}
|
348 |
|
349 |
+
المستخدم اختار أن يكتب رداً مخصصاً بدلاً من اختيار أحد الخيارات المقدمة. الرد المخصص للمستخدم هو:
|
350 |
|
351 |
"{custom_text}"
|
352 |
|
353 |
+
بناءً على هذا المدخل من المستخدم، استمر في القصة واكتب فقرة جديدة تأخذ بعين الاعتبار ما كتبه المستخدم.
|
354 |
+
ثم قدم 3 خيارات جديدة للمستخدم ليختار منها للاستمرار في القصة.
|
|
|
|
|
|
|
355 |
|
356 |
+
تذكر أن القصة الآن في:
|
357 |
+
- الفقرة رقم: {current_paragraph} من {max_paragraphs}
|
358 |
+
- إذا كانت هذه الفقرة الأخيرة أو قبل الأخيرة، قم بختم القصة بشكل مناسب.
|
|
|
|
|
|
|
359 |
|
360 |
+
يجب أن يكون تنسيق ردك كما يلي:
|
361 |
|
362 |
+
الفقرة: [نص الفقرة الجديدة من القصة]
|
|
|
363 |
|
364 |
الخيارات:
|
365 |
+
1. [الخيار الأول]
|
366 |
+
2. [الخيار الثاني]
|
367 |
+
3. [الخيار الثالث]
|
368 |
|
369 |
+
إذا كانت هذه الفقرة الأخيرة، أضف عنواناً للقصة:
|
370 |
|
371 |
+
العنوان: [عنوان مناسب للقصة كاملة]
|
|
|
372 |
"""
|
373 |
|
374 |
print(f"Custom prompt created, length: {len(custom_prompt)}")
|
app.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
"""
|
2 |
-
|
3 |
-
App entry point for Hugging Face Spaces
|
4 |
"""
|
5 |
|
6 |
from main import app
|
7 |
|
8 |
-
# This file
|
9 |
-
#
|
|
|
1 |
"""
|
2 |
+
Entry point for Hugging Face Spaces deployment of Rawi API
|
|
|
3 |
"""
|
4 |
|
5 |
from main import app
|
6 |
|
7 |
+
# This file serves as the entry point for Hugging Face Spaces
|
8 |
+
# It imports and exposes the FastAPI app object from main.py
|
prompts.py
CHANGED
@@ -1,271 +1,252 @@
|
|
1 |
-
from typing import List, Dict, Any
|
2 |
-
from models import StoryLength, StoryType, Character, StoryConfig
|
3 |
-
|
4 |
-
|
5 |
-
def get_system_prompt() -> str:
|
6 |
-
"""
|
7 |
-
|
8 |
-
"""
|
9 |
-
return """
|
10 |
-
You are a professional and creative Arabic story writer. Your task is to write original, engaging, and cohesive Arabic stories.
|
11 |
-
|
12 |
-
Adhere to the following standards in all the stories you write:
|
13 |
-
|
14 |
-
1. Use correct and understandable classical Arabic language, free from grammatical and spelling errors.
|
15 |
-
2. Build a coherent and logical story that follows good dramatic structure principles (beginning, rising action, climax, resolution).
|
16 |
-
3. Adhere to Arabic and Islamic values and ethics in the story content.
|
17 |
-
4. Avoid inappropriate content or anything that violates public taste or religious values.
|
18 |
-
5. Provide detailed sensory descriptions of characters, places, and events to make the story vivid and engaging.
|
19 |
-
6. Make dialogue realistic and natural, appropriate to the story's characters and environment.
|
20 |
-
7. Maintain consistency in character traits and behaviors throughout the story.
|
21 |
-
8. Include positive values and useful lessons in an indirect way.
|
22 |
-
9. Use diverse narrative techniques: description, dialogue, narration, internal monologue.
|
23 |
-
10. Create a clear conflict that drives the story events and maintains reader interest.
|
24 |
-
|
25 |
-
Each time you are asked to write a new paragraph of the story, you must:
|
26 |
-
- Write a coherent and engaging narrative paragraph of 6
|
27 |
-
- Provide 3 distinctive and interesting options to develop the story's path.
|
28 |
-
|
29 |
-
Important rules for options:
|
30 |
-
1. Make options very practical and short (3-5 words only) in Arabic.
|
31 |
-
2. ALWAYS start each option with the character's name followed by the action verb.
|
32 |
-
3. Use clear format: "[Character name] + verb", for example: "أحمد يتصل بالشرطة" (Ahmed calls the police), "سارة تهرب من المكان" (Sarah escapes from the place).
|
33 |
-
4. Make it absolutely clear WHO is performing the action in each option.
|
34 |
-
5. Don't explain what will happen after the choice, just mention the direct action.
|
35 |
-
6. Ensure each option will lead to a completely different path in the story.
|
36 |
-
7. Make options logical and appropriate to the current situation in the story.
|
37 |
-
|
38 |
-
Result of user choice:
|
39 |
-
1. Do not summarize the user's chosen option at the beginning of the next paragraph.
|
40 |
-
2. Start directly with the reactions and consequences resulting from the user's choice.
|
41 |
-
3. Present surprising and unexpected developments resulting from the choice.
|
42 |
-
4. Maintain story consistency despite the change in path.
|
43 |
-
|
44 |
-
When the story is complete, choose an engaging and deep title that reflects the essence and content of the story.
|
45 |
-
"""
|
46 |
-
|
47 |
-
|
48 |
-
def format_characters_info(characters: List[Character]) -> str:
|
49 |
-
"""
|
50 |
-
|
51 |
-
"""
|
52 |
-
if not characters:
|
53 |
-
return "
|
54 |
-
|
55 |
-
characters_info = "
|
56 |
-
for i, character in enumerate(characters, 1):
|
57 |
-
gender_text = "
|
58 |
-
characters_info += f"{i}.
|
59 |
-
|
60 |
-
return characters_info
|
61 |
-
|
62 |
-
|
63 |
-
def get_story_length_instructions(length: StoryLength) -> Dict[str, Any]:
|
64 |
-
"""
|
65 |
-
|
66 |
-
"""
|
67 |
-
length_mapping = {
|
68 |
-
StoryLength.SHORT: {
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
"""
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
is_final
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
[
|
173 |
-
"""
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
""
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
3. VERY IMPORTANT: Each paragraph MUST be 6-8 lines long with rich details and descriptions.
|
253 |
-
4. Avoid short paragraphs - ensure each paragraph is substantial (6-8 lines minimum).
|
254 |
-
5. Make the story engaging with a developing plot.
|
255 |
-
6. Use classical Arabic language with an appropriate balance of colloquial language in dialogues if appropriate.
|
256 |
-
7. Consider the characteristics of the requested literary genres.
|
257 |
-
8. Provide a clear and satisfying ending to the story.
|
258 |
-
9. Do not use asterisk ** markers around any text in the story.
|
259 |
-
10. Do not include headings like "Title" or "End" or "Introduction" within the story.
|
260 |
-
11. Write the story directly without putting the title at the beginning of the text.
|
261 |
-
12. Do not repeat the title at the beginning or end of the story.
|
262 |
-
13. Each paragraph should contain detailed descriptions, character development, and events to ensure its length.
|
263 |
-
|
264 |
-
IMPORTANT:
|
265 |
-
- The entire story MUST be written in Arabic language only.
|
266 |
-
- Make sure EACH paragraph is substantial (6-8 lines) with rich content and descriptions.
|
267 |
-
|
268 |
-
Now, create the complete story:
|
269 |
-
"""
|
270 |
-
|
271 |
return prompt.strip()
|
|
|
1 |
+
from typing import List, Dict, Any
|
2 |
+
from models import StoryLength, StoryType, Character, StoryConfig
|
3 |
+
|
4 |
+
|
5 |
+
def get_system_prompt() -> str:
|
6 |
+
"""
|
7 |
+
الحصول على برومبت النظام الأساسي الذي يحدد سلوك نموذج الذكاء الاصطناعي
|
8 |
+
"""
|
9 |
+
return """
|
10 |
+
You are a professional and creative Arabic story writer. Your task is to write original, engaging, and cohesive Arabic stories.
|
11 |
+
|
12 |
+
Adhere to the following standards in all the stories you write:
|
13 |
+
|
14 |
+
1. Use correct and understandable classical Arabic language, free from grammatical and spelling errors.
|
15 |
+
2. Build a coherent and logical story that follows good dramatic structure principles (beginning, rising action, climax, resolution).
|
16 |
+
3. Adhere to Arabic and Islamic values and ethics in the story content.
|
17 |
+
4. Avoid inappropriate content or anything that violates public taste or religious values.
|
18 |
+
5. Provide detailed sensory descriptions of characters, places, and events to make the story vivid and engaging.
|
19 |
+
6. Make dialogue realistic and natural, appropriate to the story's characters and environment.
|
20 |
+
7. Maintain consistency in character traits and behaviors throughout the story.
|
21 |
+
8. Include positive values and useful lessons in an indirect way.
|
22 |
+
9. Use diverse narrative techniques: description, dialogue, narration, internal monologue.
|
23 |
+
10. Create a clear conflict that drives the story events and maintains reader interest.
|
24 |
+
|
25 |
+
Each time you are asked to write a new paragraph of the story, you must:
|
26 |
+
- Write a coherent and engaging narrative paragraph of 4-6 lines in Arabic.
|
27 |
+
- Provide 3 distinctive and interesting options to develop the story's path.
|
28 |
+
|
29 |
+
Important rules for options:
|
30 |
+
1. Make options very practical and short (3-5 words only) in Arabic.
|
31 |
+
2. ALWAYS start each option with the character's name followed by the action verb.
|
32 |
+
3. Use clear format: "[Character name] + verb", for example: "أحمد يتصل بالشرطة" (Ahmed calls the police), "سارة تهرب من المكان" (Sarah escapes from the place).
|
33 |
+
4. Make it absolutely clear WHO is performing the action in each option.
|
34 |
+
5. Don't explain what will happen after the choice, just mention the direct action.
|
35 |
+
6. Ensure each option will lead to a completely different path in the story.
|
36 |
+
7. Make options logical and appropriate to the current situation in the story.
|
37 |
+
|
38 |
+
Result of user choice:
|
39 |
+
1. Do not summarize the user's chosen option at the beginning of the next paragraph.
|
40 |
+
2. Start directly with the reactions and consequences resulting from the user's choice.
|
41 |
+
3. Present surprising and unexpected developments resulting from the choice.
|
42 |
+
4. Maintain story consistency despite the change in path.
|
43 |
+
|
44 |
+
When the story is complete, choose an engaging and deep title that reflects the essence and content of the story.
|
45 |
+
"""
|
46 |
+
|
47 |
+
|
48 |
+
def format_characters_info(characters: List[Character]) -> str:
|
49 |
+
"""
|
50 |
+
تنسيق معلومات الشخصيات لتضمينها في البرومبت
|
51 |
+
"""
|
52 |
+
if not characters:
|
53 |
+
return "لا توجد شخصيات محددة، يمكنك إنشاء شخصيات مناسبة للقصة."
|
54 |
+
|
55 |
+
characters_info = "معلومات الشخصيات:\n"
|
56 |
+
for i, character in enumerate(characters, 1):
|
57 |
+
gender_text = "ذكر" if character.gender.value == "ذكر" else "أنثى"
|
58 |
+
characters_info += f"{i}. الشخصية: {character.name}، الجنس: {gender_text}، الوصف: {character.description}\n"
|
59 |
+
|
60 |
+
return characters_info
|
61 |
+
|
62 |
+
|
63 |
+
def get_story_length_instructions(length: StoryLength) -> Dict[str, Any]:
|
64 |
+
"""
|
65 |
+
الحصول على تعليمات طول القصة وعدد الفقرات
|
66 |
+
"""
|
67 |
+
length_mapping = {
|
68 |
+
StoryLength.SHORT: {"paragraphs": 3, "description": "قصة قصيرة تتكون من 3 فقرات"},
|
69 |
+
StoryLength.MEDIUM: {"paragraphs": 5, "description": "قصة متوسطة الطول تتكون من 5 فقرات"},
|
70 |
+
StoryLength.LONG: {"paragraphs": 7, "description": "قصة طويلة تتكون من 7 فقرات"}
|
71 |
+
}
|
72 |
+
|
73 |
+
return length_mapping.get(length, length_mapping[StoryLength.MEDIUM])
|
74 |
+
|
75 |
+
|
76 |
+
def get_story_type_description(primary_type: StoryType, secondary_type: StoryType) -> str:
|
77 |
+
"""
|
78 |
+
الحصول على وصف نوع القصة
|
79 |
+
"""
|
80 |
+
if secondary_type == StoryType.NONE:
|
81 |
+
return f"قصة من نوع {primary_type.value}"
|
82 |
+
else:
|
83 |
+
return f"قصة تجمع بين نوعي {primary_type.value} و{secondary_type.value}"
|
84 |
+
|
85 |
+
|
86 |
+
def create_story_init_prompt(config: StoryConfig) -> str:
|
87 |
+
"""
|
88 |
+
إنشاء البرومبت الأولي لبدء القصة
|
89 |
+
"""
|
90 |
+
length_info = get_story_length_instructions(config.length)
|
91 |
+
story_type = get_story_type_description(config.primary_type, config.secondary_type)
|
92 |
+
characters_info = format_characters_info(config.characters)
|
93 |
+
|
94 |
+
prompt = f"""
|
95 |
+
Please write {length_info['description']} of {story_type}.
|
96 |
+
|
97 |
+
{characters_info}
|
98 |
+
|
99 |
+
Required from you:
|
100 |
+
1. Write the first paragraph of the story (4-6 lines) in Arabic.
|
101 |
+
2. Start the story with a strong and engaging beginning that captivates the reader from the first line.
|
102 |
+
3. Present the characters and setting (place and time) clearly and interestingly.
|
103 |
+
4. Establish a conflict, problem, or situation that drives the story events.
|
104 |
+
5. Present 3 short, exciting, and logical options for actions the protagonist can take.
|
105 |
+
6. Make the options very short (3-5 words only) and practical and direct.
|
106 |
+
7. ALWAYS include the character's name in each option before the action verb.
|
107 |
+
8. Format: "[Character name] + verb", like: "أحمد يتصل بالشرطة", "سارة تهرب من المكان".
|
108 |
+
|
109 |
+
Present the first paragraph and options in the following format:
|
110 |
+
|
111 |
+
الفقرة:
|
112 |
+
[Write the first paragraph of the story here in Arabic]
|
113 |
+
|
114 |
+
الخيارات:
|
115 |
+
1. [Character name + action verb in Arabic, 3-5 words total]
|
116 |
+
2. [Character name + different action verb in Arabic, 3-5 words total]
|
117 |
+
3. [Character name + another different action verb in Arabic, 3-5 words total]
|
118 |
+
"""
|
119 |
+
|
120 |
+
return prompt
|
121 |
+
|
122 |
+
|
123 |
+
def create_continuation_prompt(story_context: str, choice_id: int, choice_text: str, current_paragraph: int, max_paragraphs: int) -> str:
|
124 |
+
"""
|
125 |
+
إنشاء برومبت لاستكمال القصة بناءً على اختيار المستخدم
|
126 |
+
"""
|
127 |
+
is_final = current_paragraph >= max_paragraphs - 1
|
128 |
+
|
129 |
+
prompt = f"""
|
130 |
+
Story context so far:
|
131 |
+
{story_context}
|
132 |
+
|
133 |
+
The user chose path number {choice_id}: {choice_text}
|
134 |
+
|
135 |
+
Required from you:
|
136 |
+
1. Continue writing the story with a new paragraph (4-6 lines) in Arabic that directly follows the choice made by the user.
|
137 |
+
2. Do not summarize the choice that the user made; instead, start directly with the events that result from this choice.
|
138 |
+
3. Add unexpected and exciting developments to engage the reader.
|
139 |
+
4. Maintain consistency in the story's characters and world.
|
140 |
+
"""
|
141 |
+
|
142 |
+
if is_final:
|
143 |
+
prompt += """
|
144 |
+
5. This is the final paragraph of the story, so end the story in a logical and satisfying way that closes all open paths.
|
145 |
+
6. Suggest an appropriate and deep title for the complete story.
|
146 |
+
|
147 |
+
Present the final paragraph and title in the following format:
|
148 |
+
|
149 |
+
الفقرة:
|
150 |
+
[Write the final paragraph of the story here in Arabic]
|
151 |
+
|
152 |
+
العنوان:
|
153 |
+
[Write the suggested title for the story here in Arabic]
|
154 |
+
"""
|
155 |
+
else:
|
156 |
+
prompt += """
|
157 |
+
5. Present 3 short, logical, and practical options for continuing the story.
|
158 |
+
6. Make the options very short (3-5 words only) in Arabic.
|
159 |
+
7. ALWAYS include the character's name in each option before the action verb.
|
160 |
+
8. Format: "[Character name] + verb", like: "أحمد يتصل بالشرطة", "سارة تهرب من المكان".
|
161 |
+
9. Make it absolutely clear WHO is performing the action in each option.
|
162 |
+
10. Ensure each option will lead to a completely different path in the story.
|
163 |
+
|
164 |
+
Present the next paragraph and options in the following format:
|
165 |
+
|
166 |
+
الفقرة:
|
167 |
+
[Write the next paragraph of the story here in Arabic]
|
168 |
+
|
169 |
+
الخيارات:
|
170 |
+
1. [Character name + action verb in Arabic, 3-5 words total]
|
171 |
+
2. [Character name + different action verb in Arabic, 3-5 words total]
|
172 |
+
3. [Character name + another different action verb in Arabic, 3-5 words total]
|
173 |
+
"""
|
174 |
+
|
175 |
+
return prompt
|
176 |
+
|
177 |
+
|
178 |
+
def create_title_prompt(complete_story: str) -> str:
|
179 |
+
"""
|
180 |
+
Create a prompt to generate an appropriate title for the completed story
|
181 |
+
"""
|
182 |
+
return f"""
|
183 |
+
Here is a complete story:
|
184 |
+
|
185 |
+
{complete_story}
|
186 |
+
|
187 |
+
Suggest an appropriate and engaging title for this story that reflects its essence and content.
|
188 |
+
Provide only the title without any additional explanation and without any story Characters names in Arabic.
|
189 |
+
"""
|
190 |
+
|
191 |
+
|
192 |
+
def create_complete_story_prompt(config):
|
193 |
+
"""
|
194 |
+
Create prompt to generate a complete story without interaction
|
195 |
+
|
196 |
+
Args:
|
197 |
+
config: Story configuration
|
198 |
+
|
199 |
+
Returns:
|
200 |
+
str: Prompt used to generate the complete story
|
201 |
+
"""
|
202 |
+
# Get story length instructions
|
203 |
+
length_instructions = get_story_length_instructions(config.length)
|
204 |
+
paragraph_count = length_instructions["paragraphs"]
|
205 |
+
word_count = length_instructions.get("words", 1500) # Default value if key doesn't exist
|
206 |
+
|
207 |
+
# Create character descriptions
|
208 |
+
characters_description = ""
|
209 |
+
for i, character in enumerate(config.characters, 1):
|
210 |
+
gender_text = "Male" if character.gender.value == "ذكر" else "Female"
|
211 |
+
characters_description += f"{i}. {character.name}: {gender_text}, {character.description}\n"
|
212 |
+
|
213 |
+
# Determine story types
|
214 |
+
primary_genre = config.primary_type.value
|
215 |
+
secondary_genre = config.secondary_type.value if config.secondary_type != StoryType.NONE else "None"
|
216 |
+
|
217 |
+
# Create the prompt
|
218 |
+
prompt = f"""
|
219 |
+
I want you to create a complete Arabic story in one go without user interaction.
|
220 |
+
|
221 |
+
Story Information:
|
222 |
+
- Primary Genre: {primary_genre}
|
223 |
+
- Secondary Genre: {secondary_genre}
|
224 |
+
- Required Paragraphs: approximately {paragraph_count} paragraphs
|
225 |
+
- Average Word Count: approximately {word_count} words
|
226 |
+
|
227 |
+
Characters:
|
228 |
+
{characters_description}
|
229 |
+
|
230 |
+
Additional Requirements:
|
231 |
+
1. Create a complete and coherent story from beginning to end.
|
232 |
+
2. Separate paragraphs with an empty line.
|
233 |
+
3. VERY IMPORTANT: Each paragraph MUST be 6-8 lines long with rich details and descriptions.
|
234 |
+
4. Avoid short paragraphs - ensure each paragraph is substantial (6-8 lines minimum).
|
235 |
+
5. Make the story engaging with a developing plot.
|
236 |
+
6. Use classical Arabic language with an appropriate balance of colloquial language in dialogues if appropriate.
|
237 |
+
7. Consider the characteristics of the requested literary genres.
|
238 |
+
8. Provide a clear and satisfying ending to the story.
|
239 |
+
9. Do not use asterisk ** markers around any text in the story.
|
240 |
+
10. Do not include headings like "Title" or "End" or "Introduction" within the story.
|
241 |
+
11. Write the story directly without putting the title at the beginning of the text.
|
242 |
+
12. Do not repeat the title at the beginning or end of the story.
|
243 |
+
13. Each paragraph should contain detailed descriptions, character development, and events to ensure its length.
|
244 |
+
|
245 |
+
IMPORTANT:
|
246 |
+
- The entire story MUST be written in Arabic language only.
|
247 |
+
- Make sure EACH paragraph is substantial (6-8 lines) with rich content and descriptions.
|
248 |
+
|
249 |
+
Now, create the complete story:
|
250 |
+
"""
|
251 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
return prompt.strip()
|
requirements.txt
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
-
fastapi==0.
|
2 |
-
uvicorn==0.
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
1 |
+
fastapi==0.110.0
|
2 |
+
uvicorn==0.28.0
|
3 |
+
pydantic==2.6.1
|
4 |
+
python-dotenv==1.0.1
|
5 |
+
httpx==0.26.0
|
6 |
+
gTTS==2.5.0
|
7 |
+
python-multipart==0.0.9
|
8 |
+
starlette==0.36.3
|
9 |
+
aiofiles==23.2.1
|