walker11 commited on
Commit
503a72d
·
verified ·
1 Parent(s): 4000ef6

Upload 11 files

Browse files
Files changed (5) hide show
  1. README.md +52 -104
  2. ai_service.py +62 -116
  3. app.py +3 -4
  4. prompts.py +251 -270
  5. requirements.txt +9 -8
README.md CHANGED
@@ -8,121 +8,69 @@ app_file: app.py
8
  pinned: false
9
  ---
10
 
11
- # راوي (Rawi) - Arabic AI Storytelling Platform
12
 
13
- ## Overview
14
- Rawi is an Arabic AI storytelling platform that leverages DeepSeek AI to generate interactive and immersive stories in Arabic. The platform allows users to configure stories, participate in shaping the narrative, and listen to the stories using text-to-speech capabilities.
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
- ## Development
65
- To run the backend locally:
 
 
 
 
66
 
67
- ```bash
68
- # Install dependencies
69
- pip install -r requirements.txt
70
 
71
- # Run the server
72
- uvicorn main:app --reload --host 0.0.0.0 --port 7860
 
73
  ```
74
 
75
- ## API Endpoints
76
- - `/api/stories/initialize`: Initialize a new story
77
- - `/api/stories/complete`: Generate a complete story
78
- - `/api/stories/continue`: Continue a story based on user choice or custom text
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
- ## التوزيع على Hugging Face Spaces
 
 
 
100
 
101
- ### خطوات النشر
102
- 1. إنشاء مساحة جديدة في Hugging Face
103
- - انتقل إلى [Hugging Face](https://huggingface.co/)
104
- - انقر على "New Space"
105
- - اختر اسمًا لمساحتك (مثل "rawi-story-generator")
106
- - اختر "Docker" كـ SDK للمساحة
107
- - اختر "CPU" كعتاد
108
 
109
- 2. إعداد متغيرات البيئة
110
- - بعد إنشاء المساحة، انتقل إلى Settings > Repository Secrets
111
- - أضف مفتاح DeepSeek API الخاص بك كسر باسم `DEEPSEEK_API_KEY`
 
112
 
113
- 3. رفع ملفات الواجهة الخلفية
114
- - استنسخ مستودع المساحة محليًا
115
- - انسخ جميع الملفات من دليل `Back-End` إلى المستودع
116
- - قم بتنفيذ الالتزام والدفع للتغييرات
117
 
118
- 4. تحديث عنوان URL للواجهة الأمامية
119
- - في ملف `script.js` الخاص بالواجهة الأمامية، قم بتحديث `API_URL` ليشير إلى مساحة Hugging Face الخاصة بك:
120
- ```javascript
121
- const API_URL = 'https://your-space-name.hf.space';
122
- ```
 
123
 
124
- 5. نشر الواجهة الأمامية الخاصة بك
125
- - استضف ملفات الواجهة الأمامية على خدمة استضافة ويب (GitHub Pages, Netlify, Vercel, إلخ)
126
- - تأكد من تكوين CORS بشكل صحيح في الواجهة الخلفية للسماح بالطلبات من نطاق الواجهة الأمامية
 
127
 
128
- للحصول على وثائق API مفصلة، قم بزيارة نقطة النهاية `/docs` بعد نشر API.
 
 
 
 
 
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
- Call DeepSeek API and generate a response based on message chain with retry attempts in case of failure
70
 
71
  Args:
72
- messages: List of conversation messages
73
- retries: Number of retry attempts in case of connection failure (default: 3)
74
- backoff_factor: Delay factor for consecutive attempts (default: 1.5)
75
 
76
  Returns:
77
- str: Response content from API
78
 
79
  Raises:
80
- Exception: In case all attempts fail
81
  """
82
  if not DEEPSEEK_API_KEY:
83
- logger.error("API key not available. Please check the environment variables setup.")
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.75,
103
- "max_tokens": 4000
104
  }
105
 
106
  last_exception = None
107
 
108
- # Retry connection attempts in case of API failure
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=120.0 # Increased timeout for longer responses
119
  )
120
 
121
  if response.status_code == 429: # Rate Limit
122
- # Wait before trying again
123
  wait_time = backoff_factor * (2 ** attempt)
124
- logger.warning(f"Rate limit exceeded, waiting {wait_time} seconds before retrying.")
125
  await asyncio.sleep(wait_time)
126
  continue
127
 
128
  if response.status_code != 200:
129
- error_msg = f"DeepSeek API request failed: {response.status_code} - {response.text}"
130
- logger.error(error_msg)
131
  last_exception = Exception(error_msg)
132
 
133
- # Wait before trying again
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
- error_msg = "Invalid DeepSeek API response format"
141
- logger.error(error_msg)
142
- last_exception = Exception(error_msg)
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"HTTP connection error: {str(e)}"
169
- logger.error(error_msg)
170
  last_exception = Exception(error_msg)
171
 
172
- # Wait before trying again
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"Unexpected error: {str(e)}"
179
- logger.error(error_msg)
180
  last_exception = Exception(error_msg)
181
 
182
- # Wait before trying again
183
  wait_time = backoff_factor * (2 ** attempt)
184
  await asyncio.sleep(wait_time)
185
  continue
186
 
187
- # If we got here, all attempts have failed
188
- logger.critical(f"API call failed after {retries} attempts")
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
- بدء قصة جديدة باستخدام DeepSeek API
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
- We have reached this point in the story:
392
 
393
  {story_context_text}
394
 
395
- The user chose to write a custom response instead of selecting one of the provided options. The user's custom input is:
396
 
397
  "{custom_text}"
398
 
399
- Based on this user input, continue the story and write a new paragraph that takes into account what the user wrote.
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
- VERY IMPORTANT REQUIREMENTS:
406
- 1. The paragraph MUST be 6-8 lines long with rich details and descriptive content.
407
- 2. Include detailed descriptions of characters, settings, emotions, and events.
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
- Your response format must be as follows:
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. [First option - character name + action verb in Arabic, 3-5 words total]
419
- 2. [Second option - character name + action verb in Arabic, 3-5 words total]
420
- 3. [Third option - character name + action verb in Arabic, 3-5 words total]
421
 
422
- If this is the final paragraph, add a title for the story:
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
- راوي (Rawi) - Arabic AI Storytelling Platform
3
- App entry point for Hugging Face Spaces
4
  """
5
 
6
  from main import app
7
 
8
- # This file is used by Hugging Face Spaces to serve the application
9
- # The FastAPI app is imported from main.py
 
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
- Get the base system prompt that defines the AI model's behavior
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-8 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
- Format character information to include in the prompt
51
- """
52
- if not characters:
53
- return "No characters specified. You can create appropriate characters for the story."
54
-
55
- characters_info = "Character information:\n"
56
- for i, character in enumerate(characters, 1):
57
- gender_text = "Male" if character.gender.value == "ذكر" else "Female"
58
- characters_info += f"{i}. Character: {character.name}, Gender: {gender_text}, Description: {character.description}\n"
59
-
60
- return characters_info
61
-
62
-
63
- def get_story_length_instructions(length: StoryLength) -> Dict[str, Any]:
64
- """
65
- Get story length instructions and number of paragraphs
66
- """
67
- length_mapping = {
68
- StoryLength.SHORT: {
69
- "paragraphs": 5,
70
- "description": "a short story consisting of 5 paragraphs",
71
- "words": 1500
72
- },
73
- StoryLength.MEDIUM: {
74
- "paragraphs": 7,
75
- "description": "a medium-length story consisting of 7 paragraphs",
76
- "words": 3000
77
- },
78
- StoryLength.LONG: {
79
- "paragraphs": 9,
80
- "description": "a long story consisting of 9 paragraphs",
81
- "words": 5000
82
- }
83
- }
84
-
85
- return length_mapping.get(length, length_mapping[StoryLength.MEDIUM])
86
-
87
-
88
- def get_story_type_description(primary_type: StoryType, secondary_type: StoryType) -> str:
89
- """
90
- Get a description of the story type
91
- """
92
- if secondary_type == StoryType.NONE:
93
- return f"a story of type {primary_type.value}"
94
- else:
95
- return f"a story that combines {primary_type.value} and {secondary_type.value} genres"
96
-
97
-
98
- def create_story_init_prompt(config: StoryConfig) -> str:
99
- """
100
- Create the initial prompt to start the story
101
- """
102
- length_info = get_story_length_instructions(config.length)
103
- story_type = get_story_type_description(config.primary_type, config.secondary_type)
104
- characters_info = format_characters_info(config.characters)
105
-
106
- prompt = f"""
107
- Please write {length_info['description']} of {story_type}.
108
-
109
- {characters_info}
110
-
111
- Required from you:
112
- 1. Write the first paragraph of the story IN ARABIC LANGUAGE.
113
- 2. VERY IMPORTANT: The paragraph MUST be 6-8 lines long with rich details and descriptive content.
114
- 3. Start the story with a strong and engaging beginning that captivates the reader from the first line.
115
- 4. Present the characters and setting (place and time) clearly and interestingly.
116
- 5. Establish a conflict, problem, or situation that drives the story events.
117
- 6. Include detailed descriptions of the environment, character emotions, and sensory details.
118
- 7. Make sure your paragraph is substantial and richly detailed (minimum 6-8 lines).
119
- 8. Present 3 short, exciting, and logical options for actions the protagonist can take.
120
- 9. Make the options very short (3-5 words only) and practical and direct.
121
- 10. ALWAYS include the character's name in each option before the action verb.
122
- 11. Format: "[Character name] + verb", like: "أحمد يتصل بالشرطة", "سارة تهرب من المكان".
123
-
124
- Present the first paragraph and options in the following format:
125
-
126
- الفقرة:
127
- [Write the first paragraph of the story here in Arabic, ensuring it is 6-8 lines long with rich details]
128
-
129
- الخيارات:
130
- 1. [Character name + action verb in Arabic, 3-5 words total]
131
- 2. [Character name + different action verb in Arabic, 3-5 words total]
132
- 3. [Character name + another different action verb in Arabic, 3-5 words total]
133
- """
134
-
135
- return prompt
136
-
137
-
138
- def create_continuation_prompt(story_context: str, choice_id: int, choice_text: str, current_paragraph: int, max_paragraphs: int) -> str:
139
- """
140
- Create a prompt to continue the story based on the user's choice
141
- """
142
- is_final = current_paragraph >= max_paragraphs - 1
143
-
144
- prompt = f"""
145
- Story context so far:
146
- {story_context}
147
-
148
- The user chose path number {choice_id}: {choice_text}
149
-
150
- Required from you:
151
- 1. Continue writing the story with a new paragraph IN ARABIC LANGUAGE that directly follows the choice made by the user.
152
- 2. VERY IMPORTANT: The paragraph MUST be 6-8 lines long with rich details and descriptive content.
153
- 3. Include detailed descriptions of characters, settings, emotions, and events.
154
- 4. Do not summarize the choice that the user made; instead, start directly with the events that result from this choice.
155
- 5. Add unexpected and exciting developments to engage the reader.
156
- 6. Maintain consistency in the story's characters and world.
157
- 7. Make sure your paragraph is substantial and richly detailed (minimum 6-8 lines).
158
- """
159
-
160
- if is_final:
161
- prompt += """
162
- 8. This is the final paragraph of the story, so end the story in a logical and satisfying way that closes all open paths.
163
- 9. Make this final paragraph detailed and substantial (6-8 lines) with a proper conclusion.
164
- 10. Suggest an appropriate and deep title for the complete story.
165
-
166
- Present the final paragraph and title in the following format:
167
-
168
- الفقرة:
169
- [Write the final paragraph of the story here in Arabic, ensuring it is 6-8 lines long]
170
-
171
- العنوان:
172
- [Write the suggested title for the story here in Arabic]
173
- """
174
- else:
175
- prompt += """
176
- 8. Present 3 short, logical, and practical options for continuing the story.
177
- 9. Make the options very short (3-5 words only) in Arabic.
178
- 10. ALWAYS include the character's name in each option before the action verb.
179
- 11. Format: "[Character name] + verb", like: "أحمد يتصل بالشرطة", "سارة تهرب من المكان".
180
- 12. Make it absolutely clear WHO is performing the action in each option.
181
- 13. Ensure each option will lead to a completely different path in the story.
182
-
183
- Present the next paragraph and options in the following format:
184
-
185
- الفقرة:
186
- [Write the next paragraph of the story here in Arabic, ensuring it is 6-8 lines long]
187
-
188
- الخيارات:
189
- 1. [Character name + action verb in Arabic, 3-5 words total]
190
- 2. [Character name + different action verb in Arabic, 3-5 words total]
191
- 3. [Character name + another different action verb in Arabic, 3-5 words total]
192
- """
193
-
194
- return prompt
195
-
196
-
197
- def create_title_prompt(complete_story: str) -> str:
198
- """
199
- Create a prompt to generate an appropriate title for the completed story
200
- """
201
- return f"""
202
- Here is a complete story:
203
-
204
- {complete_story}
205
-
206
- Suggest an appropriate and engaging title for this story that reflects its essence and content.
207
- Provide only the title without any additional explanation and without any story Characters names in Arabic.
208
- """
209
-
210
-
211
- def create_complete_story_prompt(config):
212
- """
213
- Create prompt to generate a complete story without interaction
214
-
215
- Args:
216
- config: Story configuration
217
-
218
- Returns:
219
- str: Prompt used to generate the complete story
220
- """
221
- # Get story length instructions
222
- length_instructions = get_story_length_instructions(config.length)
223
- paragraph_count = length_instructions["paragraphs"]
224
- word_count = length_instructions.get("words", 1500) # Default value if key doesn't exist
225
-
226
- # Create character descriptions
227
- characters_description = ""
228
- for i, character in enumerate(config.characters, 1):
229
- gender_text = "Male" if character.gender.value == "ذكر" else "Female"
230
- characters_description += f"{i}. {character.name}: {gender_text}, {character.description}\n"
231
-
232
- # Determine story types
233
- primary_genre = config.primary_type.value
234
- secondary_genre = config.secondary_type.value if config.secondary_type != StoryType.NONE else "None"
235
-
236
- # Create the prompt
237
- prompt = f"""
238
- I want you to create a complete Arabic story in one go without user interaction.
239
-
240
- Story Information:
241
- - Primary Genre: {primary_genre}
242
- - Secondary Genre: {secondary_genre}
243
- - Required Paragraphs: approximately {paragraph_count} paragraphs
244
- - Average Word Count: approximately {word_count} words
245
-
246
- Characters:
247
- {characters_description}
248
-
249
- Additional Requirements:
250
- 1. Create a complete and coherent story from beginning to end.
251
- 2. Separate paragraphs with an empty line.
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.104.1
2
- uvicorn==0.24.0
3
- python-dotenv==1.0.0
4
- httpx==0.25.1
5
- pydantic==2.4.2
6
- python-multipart==0.0.6
7
- gtts==2.3.2
8
- gradio==3.50.2
 
 
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