TheM1N9 commited on
Commit
e56e019
·
1 Parent(s): cdc0475

first commit

Browse files
Files changed (11) hide show
  1. .gitignore +67 -0
  2. Dockerfile +17 -0
  3. README.md +135 -12
  4. app.py +305 -0
  5. examples.py +452 -0
  6. main.py +309 -0
  7. poetry.lock +1138 -0
  8. pyproject.toml +23 -0
  9. requirements.txt +10 -0
  10. static/styles.css +443 -0
  11. templates/index.html +476 -0
.gitignore ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config.json
2
+ .env
3
+ uploads
4
+ outputs
5
+ chroma
6
+ instance
7
+ .venv
8
+ *.log
9
+ *.json
10
+ *.mp3
11
+ *.pdf
12
+ mg-env
13
+
14
+ # Byte-compiled / optimized / DLL files
15
+ __pycache__/
16
+ *.py[cod]
17
+ *$py.class
18
+
19
+ # C extensions
20
+ *.so
21
+
22
+ # Distribution / packaging
23
+ .Python
24
+ env/
25
+ build/
26
+ develop-eggs/
27
+ dist/
28
+ downloads/
29
+ eggs/
30
+ .eggs/
31
+ lib/
32
+ lib64/
33
+ parts/
34
+ sdist/
35
+ var/
36
+ *.egg-info/
37
+ .installed.cfg
38
+ *.egg
39
+
40
+ # PyInstaller
41
+ # Usually these files are written by a python script from a template
42
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
43
+ *.manifest
44
+ *.spec
45
+
46
+ # Installer logs
47
+ pip-log.txt
48
+ pip-delete-this-directory.txt
49
+
50
+ # Unit test / coverage reports
51
+ htmlcov/
52
+ .tox/
53
+ .coverage
54
+ .coverage.*
55
+ .cache
56
+ nosetests.xml
57
+ coverage.xml
58
+ *,cover
59
+ .hypothesis/
60
+ venv/
61
+ .python-version
62
+
63
+ *.log
64
+
65
+ aws_gmail_tokens.txt
66
+ token.pickle
67
+ credentials.json
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /code
8
+
9
+ COPY ./requirements.txt /code/requirements.txt
10
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
11
+
12
+ # Install ffmpeg for pydub
13
+ RUN apt-get update && apt-get install -y ffmpeg
14
+
15
+ COPY . /code
16
+
17
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,12 +1,135 @@
1
- ---
2
- title: NotebookMg
3
- emoji:
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- license: unknown
9
- short_description: Replicate of NotebookLM
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NotebookMg
2
+
3
+ This project converts PDF documents into engaging podcast conversations using AI. It leverages Google's Gemini Pro for text processing and ElevenLabs for voice synthesis.
4
+
5
+ ## Features
6
+
7
+ - PDF text extraction and cleaning
8
+ - Conversion of academic/technical content into natural dialogue
9
+ - Dynamic conversation generation between two hosts (Alex and Jamie)
10
+ - High-quality text-to-speech synthesis
11
+ - Web interface for easy interaction
12
+ - API endpoints for programmatic access
13
+
14
+ ## Prerequisites
15
+
16
+ - Python 3.8+
17
+ - Google Gemini API key
18
+ - ElevenLabs API key
19
+
20
+ ## Installation
21
+
22
+ 1. Clone the repository:
23
+ ```bash
24
+ git clone <repository-url>
25
+ cd pdf-to-podcast
26
+ ```
27
+
28
+ 2. Install dependencies:
29
+ ```bash
30
+ pip install -r requirements.txt
31
+ ```
32
+
33
+ 3. Set up environment variables:
34
+ ```bash
35
+ # Create a .env file
36
+ touch .env
37
+
38
+ # Add your API keys
39
+ echo "GEMINI_API_KEY=your_gemini_api_key" >> .env
40
+ echo "ELEVEN_API_KEY=your_elevenlabs_api_key" >> .env
41
+ ```
42
+
43
+ ## Project Structure
44
+
45
+ ```
46
+ pdf-to-podcast/
47
+ ├── main.py # Core conversion logic
48
+ ├── app.py # FastAPI application
49
+ ├── run.py # Server startup script
50
+ ├── templates/ # HTML templates
51
+ │ └── index.html # Web interface
52
+ ├── uploads/ # Temporary PDF storage
53
+ └── outputs/ # Generated files
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ### Web Interface
59
+
60
+ 1. Start the server:
61
+ ```bash
62
+ python run.py
63
+ ```
64
+
65
+ 2. Open your browser and navigate to `http://localhost:8000`
66
+ 3. Upload a PDF file
67
+ 4. Download the generated files:
68
+ - Cleaned text version
69
+ - Conversation transcript
70
+ - MP3 podcast file
71
+
72
+ ### API Endpoints
73
+
74
+ - `POST /upload-pdf/`: Upload PDF and generate podcast
75
+ - `GET /download/{filename}`: Download generated files
76
+ - `GET /status`: Check API status
77
+
78
+ ## API Examples
79
+
80
+ ```python
81
+ import requests
82
+
83
+ # Upload PDF
84
+ with open('document.pdf', 'rb') as f:
85
+ response = requests.post(
86
+ 'http://localhost:8000/upload-pdf/',
87
+ files={'file': f}
88
+ )
89
+
90
+ # Download generated podcast
91
+ response = requests.get(
92
+ 'http://localhost:8000/download/document_podcast.mp3'
93
+ )
94
+ ```
95
+
96
+ ## Configuration
97
+
98
+ Voice IDs can be configured in `main.py`:
99
+ ```python
100
+ self.alex_voice_id = "21m00Tcm4TlvDq8ikWAM" # Rachel voice
101
+ self.jamie_voice_id = "IKne3meq5aSn9XLyUdCD" # Adam voice
102
+ ```
103
+
104
+ ## Dependencies
105
+
106
+ - `google-generativeai`: Gemini Pro API
107
+ - `elevenlabs`: Text-to-speech synthesis
108
+ - `PyPDF2`: PDF processing
109
+ - `fastapi`: Web API framework
110
+ - `pydub`: Audio processing
111
+ - `python-multipart`: File upload handling
112
+ - `uvicorn`: ASGI server
113
+ - `jinja2`: Template engine
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork the repository
118
+ 2. Create a feature branch
119
+ 3. Commit your changes
120
+ 4. Push to the branch
121
+ 5. Create a Pull Request
122
+
123
+ ## License
124
+
125
+ This project is licensed under the MIT License - see the LICENSE file for details.
126
+
127
+ ## Acknowledgments
128
+
129
+ - Google Gemini for AI text processing
130
+ - ElevenLabs for voice synthesis
131
+ - FastAPI team for the excellent web framework
132
+
133
+ ## Support
134
+
135
+ For support, please open an issue in the GitHub repository or contact [your-email].
app.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uvicorn
2
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request, Response
3
+ from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
4
+ from fastapi.staticfiles import StaticFiles
5
+ from fastapi.templating import Jinja2Templates
6
+ import shutil
7
+ import os
8
+ from pathlib import Path
9
+ from main import NotebookMg
10
+ from dotenv import load_dotenv
11
+ import logging
12
+ from pydub import AudioSegment
13
+
14
+ # Set up logging
15
+ logging.basicConfig(level=logging.DEBUG)
16
+ logger = logging.getLogger(__name__)
17
+
18
+ load_dotenv()
19
+
20
+ app = FastAPI(docs_url=None, redoc_url=None) # Disable Swagger UI # Disable ReDoc
21
+
22
+ # Mount static directory - make sure this comes before other routes
23
+ app.mount("/static", StaticFiles(directory="static"), name="static")
24
+
25
+ # Set up templates
26
+ templates = Jinja2Templates(directory="templates")
27
+
28
+ # Create necessary directories
29
+ UPLOAD_DIR = Path("uploads")
30
+ OUTPUT_DIR = Path("outputs")
31
+ STATIC_DIR = Path("static")
32
+
33
+ # Create directories if they don't exist
34
+ UPLOAD_DIR.mkdir(exist_ok=True)
35
+ OUTPUT_DIR.mkdir(exist_ok=True)
36
+ STATIC_DIR.mkdir(exist_ok=True)
37
+
38
+ # Make sure styles.css exists in static directory
39
+ if not (STATIC_DIR / "styles.css").exists():
40
+ logger.warning("styles.css not found in static directory")
41
+
42
+ # Initialize the NotebookGemini instance
43
+ try:
44
+ gemini_bot = NotebookMg(
45
+ gemini_api_key=os.getenv("GEMINI_API_KEY", "dude, wth enter your key"),
46
+ eleven_api_key=os.getenv("ELEVEN_API_KEY", "dude, wth enter your key"),
47
+ Tharun_voice_id="21m00Tcm4TlvDq8ikWAM",
48
+ Akshara_voice_id="IKne3meq5aSn9XLyUdCD",
49
+ )
50
+ except Exception as e:
51
+ logger.error(f"Failed to initialize NotebookMg: {str(e)}")
52
+ raise
53
+
54
+ # Get credentials from .env or use defaults
55
+ ADMIN_USERNAME = os.getenv("ADMIN_USERNAME", "admin")
56
+ ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "password123")
57
+
58
+
59
+ @app.post("/login")
60
+ async def login(
61
+ username: str = Form(...), password: str = Form(...), response: Response = None
62
+ ):
63
+ if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
64
+ response = RedirectResponse(url="/", status_code=303)
65
+ response.set_cookie(key="authenticated", value="true", httponly=True)
66
+ return response
67
+ raise HTTPException(status_code=401, detail="Invalid credentials")
68
+
69
+
70
+ @app.get("/", response_class=HTMLResponse)
71
+ async def read_root(request: Request):
72
+ """Serve the index page"""
73
+ # Check if user is authenticated
74
+ is_authenticated = request.cookies.get("authenticated") == "true"
75
+ return templates.TemplateResponse(
76
+ "index.html", {"request": request, "is_authenticated": is_authenticated}
77
+ )
78
+
79
+
80
+ @app.post("/upload-pdf/")
81
+ async def upload_pdf(
82
+ file: UploadFile = File(...),
83
+ tharun_voice_id: str = Form(...),
84
+ akshara_voice_id: str = Form(...),
85
+ ):
86
+ """Upload PDF and generate podcast"""
87
+ pdf_path = None # Initialize pdf_path before try block
88
+ try:
89
+ if not file.filename:
90
+ raise HTTPException(status_code=400, detail="Please upload a PDF")
91
+ if not file.filename.endswith(".pdf"):
92
+ raise HTTPException(status_code=400, detail="File must be a PDF")
93
+
94
+ logger.info(f"Processing file: {file.filename}")
95
+
96
+ # Clean up old files before processing new upload
97
+ try:
98
+ # Clean up old segments and podcast files
99
+ for old_file in OUTPUT_DIR.glob("*"):
100
+ try:
101
+ os.remove(old_file)
102
+ logger.info(f"Cleaned up old file: {old_file}")
103
+ except Exception as e:
104
+ logger.error(f"Failed to remove old file {old_file}: {str(e)}")
105
+ except Exception as e:
106
+ logger.error(f"Error during cleanup: {str(e)}")
107
+
108
+ # Save the uploaded PDF
109
+ pdf_path = UPLOAD_DIR / file.filename
110
+ try:
111
+ with open(pdf_path, "wb") as buffer:
112
+ shutil.copyfileobj(file.file, buffer)
113
+ except Exception as e:
114
+ logger.error(f"Failed to save uploaded file: {str(e)}")
115
+ raise HTTPException(status_code=500, detail="Failed to save uploaded file")
116
+
117
+ try:
118
+ # Update the gemini_bot instance with user-provided voice IDs
119
+ gemini_bot.Tharun_voice_id = tharun_voice_id
120
+ gemini_bot.Akshara_voice_id = akshara_voice_id
121
+
122
+ # Generate unique output filenames
123
+ base_name = pdf_path.stem
124
+ cleaned_text_path = OUTPUT_DIR / f"{base_name}_cleaned.txt"
125
+ transcript_path = OUTPUT_DIR / f"{base_name}_transcript.txt"
126
+ podcast_path = OUTPUT_DIR / f"{base_name}_podcast.mp3"
127
+
128
+ # Process the PDF
129
+ logger.info("Processing PDF...")
130
+ text = gemini_bot.get_pdf_text(str(pdf_path))
131
+ cleaned_text = gemini_bot.process_pdf(text)
132
+ with open(cleaned_text_path, "w", encoding="utf-8") as f:
133
+ f.write(cleaned_text)
134
+
135
+ logger.info("Creating transcript...")
136
+ transcript = gemini_bot.create_transcript(cleaned_text, text)
137
+ with open(transcript_path, "w", encoding="utf-8") as f:
138
+ f.write(transcript)
139
+
140
+ logger.info("Dramatizing transcript...")
141
+ speaker_lines = gemini_bot.dramatize_transcript(transcript, text)
142
+
143
+ # Save individual audio segments
144
+ segment_files = []
145
+ for i, (speaker, line) in enumerate(speaker_lines):
146
+ segment_path = OUTPUT_DIR / f"{base_name}_segment_{i}.mp3"
147
+ voice_id = (
148
+ gemini_bot.Akshara_voice_id
149
+ if speaker == "Akshara"
150
+ else gemini_bot.Tharun_voice_id
151
+ )
152
+
153
+ # Generate individual segment audio
154
+ audio_data = gemini_bot.eleven_client.text_to_speech.convert(
155
+ voice_id=voice_id,
156
+ output_format="mp3_44100_128",
157
+ text=line,
158
+ model_id="eleven_multilingual_v2",
159
+ )
160
+
161
+ # Save segment
162
+ audio_bytes = b"".join(audio_data)
163
+ with open(segment_path, "wb") as f:
164
+ f.write(audio_bytes)
165
+
166
+ segment_files.append(
167
+ {"file": segment_path.name, "speaker": speaker, "text": line}
168
+ )
169
+
170
+ # Generate final combined audio as before
171
+ gemini_bot.generate_audio(speaker_lines, str(podcast_path))
172
+
173
+ return {
174
+ "message": "Podcast generated successfully",
175
+ "podcast_file": podcast_path.name,
176
+ "segments": segment_files,
177
+ }
178
+
179
+ except Exception as e:
180
+ logger.error(f"Processing error: {str(e)}")
181
+ raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}")
182
+
183
+ except HTTPException:
184
+ raise
185
+ except Exception as e:
186
+ logger.error(f"Unexpected error: {str(e)}")
187
+ raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
188
+ finally:
189
+ # Clean up uploaded PDF
190
+ if pdf_path and pdf_path.exists():
191
+ try:
192
+ os.remove(pdf_path)
193
+ except Exception as e:
194
+ logger.error(f"Failed to clean up uploaded file: {str(e)}")
195
+
196
+
197
+ @app.get("/download/{filename}")
198
+ async def download_file(filename: str):
199
+ """Download generated files"""
200
+ file_path = OUTPUT_DIR / filename
201
+ if not file_path.exists():
202
+ raise HTTPException(status_code=404, detail="File not found")
203
+
204
+ return FileResponse(
205
+ path=file_path, filename=filename, media_type="application/octet-stream"
206
+ )
207
+
208
+
209
+ @app.get("/status")
210
+ async def get_status():
211
+ """Check API status"""
212
+ return {"status": "running"}
213
+
214
+
215
+ @app.post("/regenerate-segment/{index}")
216
+ async def regenerate_segment(
217
+ index: int,
218
+ speaker: str = Form(...),
219
+ text: str = Form(...),
220
+ tharun_voice_id: str = Form(...),
221
+ akshara_voice_id: str = Form(...),
222
+ ):
223
+ try:
224
+ # Select the appropriate voice ID based on speaker
225
+ voice_id = akshara_voice_id if speaker == "Akshara" else tharun_voice_id
226
+
227
+ # Generate new audio using ElevenLabs
228
+ audio_data = gemini_bot.eleven_client.text_to_speech.convert(
229
+ voice_id=voice_id,
230
+ output_format="mp3_44100_128",
231
+ text=text,
232
+ model_id="eleven_multilingual_v2",
233
+ )
234
+
235
+ # Get the base name from existing segments
236
+ base_name = next(OUTPUT_DIR.glob("*_segment_0.mp3")).stem.rsplit(
237
+ "_segment_0", 1
238
+ )[0]
239
+ segment_path = OUTPUT_DIR / f"{base_name}_segment_{index}.mp3"
240
+
241
+ # Delete the existing segment file if it exists
242
+ if segment_path.exists():
243
+ try:
244
+ os.remove(segment_path)
245
+ logger.info(f"Deleted existing segment file: {segment_path}")
246
+ except Exception as e:
247
+ logger.error(f"Error deleting existing segment file: {str(e)}")
248
+ # Continue anyway as we'll overwrite the file
249
+
250
+ # Save the regenerated segment
251
+ audio_bytes = b"".join(audio_data)
252
+ with open(segment_path, "wb") as f:
253
+ f.write(audio_bytes)
254
+ logger.info(f"Saved new segment file: {segment_path}")
255
+
256
+ # Delete existing podcast file if it exists
257
+ podcast_path = OUTPUT_DIR / f"{base_name}_podcast.mp3"
258
+ if podcast_path.exists():
259
+ try:
260
+ os.remove(podcast_path)
261
+ logger.info(f"Deleted existing podcast file: {podcast_path}")
262
+ except Exception as e:
263
+ logger.error(f"Error deleting existing podcast file: {str(e)}")
264
+
265
+ # Combine all segments into a new complete podcast
266
+ combined_audio = AudioSegment.empty()
267
+
268
+ # Get all segment files and sort them correctly
269
+ segment_files = sorted(
270
+ [f for f in OUTPUT_DIR.glob(f"{base_name}_segment_*.mp3")],
271
+ key=lambda x: int(x.stem.split("_")[-1]),
272
+ )
273
+
274
+ logger.info(f"Found {len(segment_files)} segments to combine")
275
+
276
+ # Add each segment to the combined audio
277
+ for segment_file in segment_files:
278
+ try:
279
+ logger.info(f"Processing segment: {segment_file}")
280
+ audio_segment = AudioSegment.from_file(segment_file, format="mp3")
281
+ pause = AudioSegment.silent(duration=300) # 300ms pause
282
+ combined_audio += audio_segment + pause
283
+ except Exception as e:
284
+ logger.error(f"Error processing segment {segment_file}: {str(e)}")
285
+ raise
286
+
287
+ # Save the new complete podcast
288
+ combined_audio.export(str(podcast_path), format="mp3")
289
+ logger.info(
290
+ f"Successfully generated new podcast with {len(segment_files)} segments"
291
+ )
292
+
293
+ return {
294
+ "success": True,
295
+ "segment_file": segment_path.name,
296
+ "podcast_file": podcast_path.name,
297
+ }
298
+
299
+ except Exception as e:
300
+ logger.error(f"Regeneration error: {str(e)}")
301
+ raise HTTPException(status_code=500, detail=f"Regeneration error: {str(e)}")
302
+
303
+
304
+ # if __name__ == "__main__":
305
+ # uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
examples.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ example1 = """
2
+ [
3
+ ("Tharun", "Hey everyone, welcome back. Today we're going to do a deep dive into, uh, three financial stories. Okay. The real, at first glance, you might think are totally unrelated."),
4
+ ("Monica", "Okay."),
5
+ ("Tharun", "But as we get into it, I think you're going to see some really interesting connections. Right. Um, and, uh, should be pretty fascinating."),
6
+ ("Monica", "Okay. Sounds good."),
7
+ ("Tharun", "We've got a report from Goldman Sachs on India's outlook for 2025. Mm hmm. We got BlackRock talking about these mega forces shaping global markets. Right. Right. And then the EIA, the U. S. Energy Information Administration, giving us their predictions on oil prices. Wow. Quite a mix."),
8
+ ("Monica", "Yeah. It is."),
9
+ ("Tharun", "Um, but I think it's all going to tie together quite nicely."),
10
+ ("Monica", "Um, but I think it's all going to tie together quite nicely."),
11
+ ("Tharun", "I'm intrigued."),
12
+ ("Monica", "Okay."),
13
+ ("Tharun", "So let's start with that Goldman Sachs report on India. Mm hmm."),
14
+ ("Monica", "hmm."),
15
+ ("Tharun", "Um, they're projecting GDP growth of 6. 3 percent for 2025."),
16
+ ("Monica", "Okay."),
17
+ ("Tharun", "Um, and I know you've been, you've been following the global economics scene really closely. Yeah."),
18
+ ("Monica", "Yeah."),
19
+ ("Tharun", "Yeah. Yeah. What's your take on that number? Is that good, bad, just kind of meh?"),
20
+ ("Monica", "Well, you know, it's interesting because while 6. 3 percent might not sound super impressive, it actually reflects a global shift, I think, towards more, I guess, caution and resilience. Okay. Um, you know, we're, we're in this time where there's a lot of uncertainty in economies around the world."),
21
+ ("Monica", "So a steady, predictable growth rate might actually be a really smart strategy for India."),
22
+ ("Monica", "Right. It's like choosing to build like a solid foundation rather than going for this risky sky, skyscraper that might just topple over."),
23
+ ("Tharun", "Yeah, exactly. It's about kind of long term stability."),
24
+ ("Monica", "Right. Exactly."),
25
+ ("Monica", "And the Goldman Sachs report, it actually points to a few factors that contribute to this kind of measured growth."),
26
+ ("Tharun", "Okay."),
27
+ ("Monica", "You know, slower government spending on infrastructure, which I think makes sense given the global environment."),
28
+ ("Tharun", "Yeah."),
29
+ ("Monica", "Tighter credit lending, especially for those unsecured loans like credit cards."),
30
+ ("Tharun", "Right."),
31
+ ("Monica", "And inflation hovering around 4. 2%. Right. Right. Right. Right."),
32
+ ("Tharun", "Yeah, inflation. The word that always seems to be on everyone's mind these days."),
33
+ ("Monica", "Yeah, it's a big one."),
34
+ ("Monica", "And they even mention, I think, in the report, those, like, vegetable price shocks."),
35
+ ("Tharun", "Oh, right. Like what happened with tomatoes earlier this year."),
36
+ ("Monica", "Exactly, yeah. You know, those sudden spikes can really impact people's budgets."),
37
+ ("Tharun", "Yeah, no kidding. It can make a big difference."),
38
+ ("Monica", "But overall, it seems like India is taking this kind of, you know, singles and doubles approach, you know, like a seasoned cricketer playing a long match."),
39
+ ("Tharun", "Okay. It's about staying in the game."),
40
+ ("Monica", "Right, playing the long game."),
41
+ ("Tharun", "Exactly."),
42
+ ("Monica", "Okay, so, but what about the stock market? How does all of this, like, cautious approach translate into the Indian stock market outlook?"),
43
+ ("Tharun", "Yeah, that's a great question. So, Goldman Sachs predicts that the Nifty Index, that's India's biggest index Benchmark stock market index will reach 27, 000 by the end of 2025."),
44
+ ("Monica", "Okay, so again, not explosive growth, but you know, steady progress, right? And this is likely due to high valuations and the fact that earnings growth has been a bit sluggish lately."),
45
+ ("Tharun", "So it's not like. Get rich quick schemes."),
46
+ ("Monica", "No, not really."),
47
+ ("Tharun", "It's more like, you know, some solid long term investment opportunities, potentially."),
48
+ ("Monica", "Exactly, and for those who like to dig a little deeper, Goldman Sachs, they suggest keeping an eye on sectors like autos, real estate, and e commerce."),
49
+ ("Tharun", "Okay."),
50
+ ("Monica", "They see some potential for some unexpected growth there."),
51
+ ("Tharun", "Interesting. All right, now let's shift gears to BlackRock and their 2025 global. market outlook."),
52
+ ("Tharun", "They're talking about these mega forces that are going to shape the investment landscape. It sounds pretty dramatic when you say it like that."),
53
+ ("Monica", "It does. Right. But what they're really getting at is these major global trends, okay? With the potential to really disrupt, you know, the status quo. And they've identified three artificial intelligence, Global protectionism and then economic fragmentation."),
54
+ ("Tharun", "Okay. And what's interesting is how these forces, they're all kind of intertwined, almost like a three headed dragon, you know?"),
55
+ ("Monica", "Okay, that's a visual."),
56
+ ("Tharun", "Right. I wasn't expecting that."),
57
+ ("Monica", "But, you know, these three forces are shaping things."),
58
+ ("Tharun", "Yeah, got it. Okay, so let's tackle this dragon one head at a time. Um, and starting with AI, Blackrock seems particularly bullish on this one."),
59
+ ("Tharun", "They're calling it like the start of a new industrial revolution. Is that hyperbole or is there something to that?"),
60
+ ("Monica", "I don't think it's hyperbole. You know, they're predicting a staggering 700 billion will be spent on AI infrastructure by 2030. And they believe that U. S. companies will be leading this charge."),
61
+ ("Monica", "But I'm curious, what are your thoughts on India's Potential role in this, in this whole AI revolution."),
62
+ ("Tharun", "Well, I mean, India's got a rapidly growing tech sector. The government is investing heavily in AI initiatives. I mean, Could India become a major player in this, even alongside the U. S. Giants?"),
63
+ ("Monica", "I think it's definitely possible."),
64
+ ("Monica", "You know, India has this huge advantage with its young tech savvy population."),
65
+ ("Tharun", "Right."),
66
+ ("Monica", "And a growing pool of talented engineers and developers."),
67
+ ("Tharun", "Yeah, for sure."),
68
+ ("Monica", "It's definitely a space to watch."),
69
+ ("Tharun", "Okay, so that's, I mean, that's really interesting for me because it's not just like an abstract trend. It's like, how does this actually translate into real world opportunities?"),
70
+ ("Monica", "Absolutely, yeah."),
71
+ ("Tharun", "Okay, let's move on to the other two heads of BlackRock's dragon."),
72
+ ("Monica", "Okay."),
73
+ ("Tharun", "Global protectionism and economic fragmentation."),
74
+ ("Tharun", "What's the gist of this and should we be worried?"),
75
+ ("Monica", "Yeah, so essentially what it means is that countries are becoming more cautious about global trade."),
76
+ ("Tharun", "Okay. Um,"),
77
+ ("Monica", "and they're increasingly focusing on protecting their own industries."),
78
+ ("Tharun", "So we're moving away from that, like, hyper globalized world."),
79
+ ("Monica", "Yeah, I think so. I think we're seeing a shift away from that. Supply chains are being reorganized, new trade alliances are forming, and geopolitics is playing a bigger role."),
80
+ ("Tharun", "So it's less free trade and more every country for itself."),
81
+ ("Monica", "Sure."),
82
+ ("Tharun", "How does this impact India?"),
83
+ ("Tharun", "Yeah. Especially with our, like, growing role in global supply chains."),
84
+ ("Monica", "Yeah, that's a key question. And while this shift does present challenges, it could also create unique opportunities for India. Okay. You know, as companies look to diversify their sourcing and manufacturing, India, with its large workforce and competitive costs, could become an even more attractive destination."),
85
+ ("Tharun", "So it's kind of turning a potential threat into a strategic advantage, almost."),
86
+ ("Monica", "Exactly, yeah."),
87
+ ("Tharun", "So it's not all, like, doom and gloom. It's more like Adapting to a changing landscape, right?"),
88
+ ("Monica", "Yeah. And BlackRock also raises this important point about how these mega forces are challenging traditional investment strategies."),
89
+ ("Monica", "They're actually questioning the wisdom of that classic 60 40 portfolio split between stocks and bonds."),
90
+ ("Tharun", "Wait, is that old rule of thumb like going out the window? I always thought it was like the gold standard."),
91
+ ("Monica", "Well, it might need some tweaking. For this new world, you know, Blackrock argues that in this environment of rapid technological advancements and all these geopolitical shifts, investors need to be more dynamic and theme focused."),
92
+ ("Tharun", "So less about just like passively investing in broad asset classes. Right. More about like. Actively choosing investments based on these, like, emerging trends."),
93
+ ("Monica", "Precisely. It's about aligning your investments with the forces that are shaping the future, like AI, for example."),
94
+ ("Tharun", "Okay. Food for thought for those of us who are not, like, professional investors, but trying to manage our own portfolios."),
95
+ ("Tharun", "All right. Let's, let's wrap up this, uh, this part of our deep dive with our final story. Oil prices. We know that NDA imports a significant amount of its oil. Right. So what happens globally has a big impact on us. What's the EIA's forecast for 2025?"),
96
+ ("Monica", "So they're predicting that Brent crude oil, that's the international benchmark for oil prices, will average around 74 a barrel in 2025."),
97
+ ("Tharun", "Now, I know you're probably thinking, with all the global tensions and conflicts."),
98
+ ("Tharun", "Honestly, yeah. I was expecting some wild swings at a much higher number. What's keeping prices relatively stable?"),
99
+ ("Monica", "It's a combination of things. We're seeing increased oil production from non OPEC countries like the U. S.,"),
100
+ ("Monica", "Canada, Guyana."),
101
+ ("Tharun", "That increased supply is helping to offset some of the geopolitical risks."),
102
+ ("Monica", "So it's like having more players in the game."),
103
+ ("Tharun", "Right."),
104
+ ("Monica", "Which reduces the influence of any single player."),
105
+ ("Tharun", "Exactly. Plus, the OPEC, plus countries, those big oil producers in the Middle East, are maintaining tighter control over their production levels."),
106
+ ("Monica", "They're being strategic about how much oil they're pumping out, which is also contributing to price stability."),
107
+ ("Tharun", "That makes sense. It's like they're walking a tightrope trying to balance their own economic interests with global demand. But speaking of demand, what's the picture looking like? I know India and China are major consumers of oil."),
108
+ ("Tharun", "So what role are they playing in this equation?"),
109
+ ("Monica", "You know, the EIA predicts that India will add 300, 000 barrels per day of demand in 2025. Driven by our expanding transportation and infrastructure sectors. We're talking about more cars on the road, more planes in the air, more construction projects."),
110
+ ("Tharun", "So as India's economy grows, so does its thirst for oil."),
111
+ ("Monica", "Right."),
112
+ ("Tharun", "Makes sense."),
113
+ ("Monica", "Right."),
114
+ ("Tharun", "But what about China? Aren't they also a major factor in global oil demand?"),
115
+ ("Monica", "They are. But there's an interesting twist. The EIA report highlights that a significant amount of Canadian oil that was originally expected to be shipped to China is now heading to refineries on the U. S. west coast. This could indicate that Chinese demand might not be as strong as initially anticipated."),
116
+ ("Tharun", "Wow, that's a pretty significant shift. What are the implications of that, both for the global oil market and for India?"),
117
+ ("Monica", "Well, for the global market, it suggests that we might see a bit more price stability than previously thought."),
118
+ ("Monica", "If Chinese demand is indeed softening, that could help offset some of the upward pressure on prices."),
119
+ ("Tharun", "And for India?"),
120
+ ("Monica", "For India, it's potentially good news. Lower oil prices mean lower costs for consumers and businesses, which could help keep inflation in check. It also eases the pressure on our trade deficit, and frees up government funds that can be used for other priorities like investing in infrastructure or social programs."),
121
+ ("Monica", "So while we can't completely ignore the potential for geopolitical shocks, the overall picture for oil prices seems relatively stable, at least for now."),
122
+ ("Tharun", "That's right. But as we've seen, the world can change quickly. It's always a good idea to stay informed and be prepared for potential volatility."),
123
+ ("Tharun", "Okay, so we've covered a lot of ground here."),
124
+ ("Tharun", "We've explored India's economic outlook, BlackRock's mega forces shaping global markets, and the EIA's predictions for oil prices. It's clear that these stories are interconnected, even though they might seem unrelated at first glance. But before we go any further, let's zoom in a bit. And focus on what this all means for our listeners here in India."),
125
+ ("Monica", "That's a great point. Let's break it down starting with the economic outlook. India's steady, measured growth. While perhaps not as dazzling as some might hope actually provides a stable foundation for individuals and businesses alike."),
126
+ ("Tharun", "It's like building a house on solid ground."),
127
+ ("Monica", "Yes."),
128
+ ("Tharun", "Instead of on shifting sand."),
129
+ ("Monica", "Exactly. In a world that feels increasingly unpredictable, that sense of stability is incredibly valuable. It allows for more reliable planning and fosters a sense of confidence, which is crucial for both investors and consumers."),
130
+ ("Tharun", "Now, what about those specific sectors that Goldman Sachs highlighted, auto's real estate and e commerce?"),
131
+ ("Tharun", "What are the implications for the average person in India? Should we be rushing out to buy a new car or invest in property?"),
132
+ ("Monica", "Well, I wouldn't say rushing out, but these sectors definitely present interesting opportunities. Take the auto sector, for example. With India's growing middle class and increasing urbanization, the demand for personal vehicles is only going to rise."),
133
+ ("Monica", "This translates into potential job opportunities in manufacturing sales, service sectors, related to the auto industry."),
134
+ ("Tharun", "So if you're looking for a career change or just starting out, the auto sector might be worth exploring. What about real estate? I know a lot of people in India see property as a safe investment."),
135
+ ("Tharun", "So what's the outlook there?"),
136
+ ("Monica", "Well, Goldman Sachs doesn't predict explosive growth, but they do hint at potential surprises. With India's ongoing urbanization, the demand for housing will likely remain strong, particularly in high growth urban centers."),
137
+ ("Tharun", "So location is key, as always. What about e commerce? It seems like everyone is shopping online these days, so I imagine this sector is booming in India too."),
138
+ ("Monica", "Absolutely. With our rapidly expanding digital infrastructure and a young, tech savvy population, e commerce in India is poised for even further growth. This means more choices and convenience for consumers, but it also translates into a wealth of job opportunities in areas like logistics, digital marketing, and customer service."),
139
+ ("Monica", "So whether you're an entrepreneur looking to launch a new venture, or someone seeking a job in a fast growing field, the e commerce space offers a lot of potential."),
140
+ ("Tharun", "Yeah."),
141
+ ("Tharun", "Now let's bring back BlackRock's mega forces and connect them to India. AI seems to be the game changer here, but it also raises some big questions."),
142
+ ("Monica", "It does. While India has the potential to become a major player in the AI revolution, we need to ensure that the benefits are shared equitably. We need to be proactive in addressing potential challenges like job displacement and the digital divide, making sure everyone has access to the skills and training needed to thrive in this new world."),
143
+ ("Tharun", "It's like we're standing at a crossroads. Yeah. We can either harness the power of AI for the benefit of everyone. Right. Or we can allow it to exacerbate existing inequalities. It's a critical moment for India. Now what about the other mega forces? Global protectionism and economic fragmentation. How are these trends likely to impact us here in India?"),
144
+ ("Monica", "Well, as we discussed, these trends represent a move away from the hyper globalized world we've become accustomed to. Countries are becoming more cautious about global trade and are increasingly focused on protecting their own industries."),
145
+ ("Tharun", "So less free trade and more every country for itself."),
146
+ ("Monica", "In a way, yes."),
147
+ ("Monica", "But while this shift presents challenges, it also offers unique opportunities for India. As global supply chains are being reconfigured, companies are seeking alternative manufacturing hubs that are reliable and cost effective."),
148
+ ("Tharun", "And India fits that bill."),
149
+ ("Monica", "Precisely. With its large workforce growing infrastructure and government initiatives aimed at boosting manufacturing, India is well positioned to attract those companies seeking to diversify their operations."),
150
+ ("Monica", "So this trend towards regionalization could actually work in our favor. It's about adapting and capitalizing on the changing dynamics of global trade. But I imagine there will be challenges too, right? What are some of the hurdles India might face as we navigate this new landscape?"),
151
+ ("Monica", "One of the biggest challenges will be ensuring that our workforce has the skills and training needed to meet the demands of these new, more localized supply chains."),
152
+ ("Monica", "We need to invest in education and training programs that equip our people with the skills required for the jobs of the future."),
153
+ ("Tharun", "So it's not just about attracting companies."),
154
+ ("Monica", "Right. It's about having a workforce that's ready to step into those roles."),
155
+ ("Monica", "Exactly. We also need to continue improving our infrastructure, particularly our transportation and logistics networks, to ensure that we can efficiently move goods and services within the country and across borders."),
156
+ ("Monica", "And of course, maintaining a stable and predictable regulatory environment will be crucial to attracting and retaining foreign investment."),
157
+ ("Tharun", "It sounds like India has a lot of work to do, but also a lot of potential to benefit from these shifting global dynamics."),
158
+ ("Monica", "Absolutely. It's a time of both challenges and opportunities, and how well India adapts will determine its future success."),
159
+ ("Tharun", "Okay, so we've talked about how these mega forces are reshaping the global economic landscape and what that means for India. But let's shift gears now and talk about something that directly impacts everyone's wallet, oil prices. We already discussed the EIA's forecast for relatively stable oil prices in the coming years, but what does that actually mean for people on the ground here in India?"),
160
+ ("Monica", "Well, first and foremost, stable oil prices help keep inflation under control. This is crucial for maintaining a healthy economy and ensuring that people's purchasing power isn't eroded by rising prices."),
161
+ ("Tharun", "So when we fill up our cars, or pay our electricity bills, we're less likely to experience those painful price hikes."),
162
+ ("Monica", "Exactly. Stable oil prices also reduce the pressure on India's trade deficit. Remember, we import a significant portion of our oil, so lower oil prices mean we're spending less on imports."),
163
+ ("Tharun", "That's good news for the overall health of the Indian economy."),
164
+ ("Monica", "It is. And finally, lower oil prices free up government resources that can be directed towards other priorities, like infrastructure development, education, health care, or social welfare programs."),
165
+ ("Monica", "So it's a win win situation for everyone. Consumers, businesses, everyone."),
166
+ ("Tharun", "In many ways, yes. But as we discussed earlier, the Middle East remains a volatile region, and any unexpected geopolitical events could disrupt the oil market and send prices soaring."),
167
+ ("Monica", "So while we can breathe a sigh of relief for now, we can't become complacent."),
168
+ ("Monica", "We need to stay informed and be prepared for potential volatility."),
169
+ ("Tharun", "Absolutely. It's all about understanding the interconnectedness of global events and how they can impact our lives even in seemingly unexpected ways."),
170
+ ("Tharun", "Now, before we wrap up this part of our deep dive, let's circle back to BlackRock's suggestion about rethinking our investment strategies in this new world of AI, global protectionism and economic fragmentation."),
171
+ ("Tharun", "What are some practical takeaways for our listeners who are wondering how to apply this to their own portfolios?"),
172
+ ("Monica", "That's a great question. It's something we could spend a whole episode discussing, but let me offer a few key points to consider. First and foremost, it's essential to move beyond that traditional mindset of simply allocating a fixed percentage to stocks and bonds."),
173
+ ("Tharun", "You mean that old 60 40 rule of thumb might not be the best approach anymore?"),
174
+ ("Monica", "It might need some adjustments. In this new world, it's crucial to be more dynamic and strategic in your investment approach. Instead of focusing solely on broad asset classes, consider investing in specific themes and trends that are likely to drive future growth."),
175
+ ("Tharun", "So think about those megaforces we discussed. AI, global protectionism, economic fragmentation. They're creating both opportunities and risks."),
176
+ ("Monica", "Precisely. Identify sectors and companies that are well positioned to benefit from these trends. For example, if you're bullish on AI, consider investing in companies developing cutting edge AI technologies or those leveraging AI to transform their industries."),
177
+ ("Monica", "And if you're concerned about global protectionism, maybe look for companies that are diversifying their supply chains or focusing on domestic markets."),
178
+ ("Tharun", "Exactly. It's about aligning your investments with the changing global landscape. And don't be afraid to be a bit more active in managing your portfolio."),
179
+ ("Tharun", "So don't just set it and forget it. Stay informed about what's happening in the world, and be prepared to adjust your investments as needed."),
180
+ ("Monica", "Exactly. And finally, remember that investing is a long term game. Don't get caught up in short term market fluctuations. Stay focused on your goals and invest in"),
181
+ ("Tharun", "That's great advice."),
182
+ ("Tharun", "So it's not about panicking or making rash decisions based on headlines. It's about being strategic, staying informed, and thinking long term."),
183
+ ("Monica", "Precisely. It's about taking control of your financial future and making informed choices that align with your personal circumstances and investment goals."),
184
+ ("Tharun", "Well said."),
185
+ ("Tharun", "And with that, I think we'll wrap up part two of our deep dive. Okay, so welcome back to part three of our deep dive. We've covered a lot of ground in this deep dive, from India's economic outlook to the rise of AI, a shifting global landscape, and even oil prices."),
186
+ ("Monica", "We certainly have, and it's really amazing to see how all these Seemingly separate things are actually like woven together."),
187
+ ("Monica", "It creates this very complex tapestry of, you know, global trends and events."),
188
+ ("Tharun", "Exactly. And I think one of the key takeaways for me is this whole idea of interconnectedness. What happens in one part of the world. You know, it can have ripple effects that reach far beyond its borders."),
189
+ ("Monica", "Absolutely. Whether it's technological advancements or economic policies, geopolitical tensions."),
190
+ ("Monica", "These forces, they don't operate in isolation. They influence and shape each other, sometimes in ways that are predictable and sometimes in ways that are pretty surprising."),
191
+ ("Tharun", "Right. And that brings me to a question that I've been thinking about. You know, we've talked about how India is positioned to kind of navigate these changing global landscapes."),
192
+ ("Tharun", "Yeah. But what about just the individual? What about their role in all of this? Mm hmm. It's easy to feel, like, overwhelmed by these mega forces. Like, we're just these tiny boats, you know, being tossed about by this vast, like, unpredictable ocean."),
193
+ ("Monica", "I understand that feeling. I think it's natural to feel a little bit powerless when you're faced with that."),
194
+ ("Monica", "global events that seem totally outside your control. But I really do believe that individuals have more agency than they might realize."),
195
+ ("Tharun", "So it's not just about being swept along with the current."),
196
+ ("Monica", "Not at all. I think it's about recognizing that even our individual choices and actions, no matter how small they might seem, they can collectively make a difference."),
197
+ ("Tharun", "Can you give me an example?"),
198
+ ("Monica", "Sure. Think about the investment strategies that we talked about earlier. You know, by choosing to invest in companies that align with our values that are working to, let's say, address global challenges like climate change or social inequity, we're not just seeking financial returns."),
199
+ ("Monica", "We're also kind of using our capital to actually drive positive change."),
200
+ ("Tharun", "So it's about being conscious consumers and investors, understanding that like our choices have consequences. That, like, extend beyond just our immediate needs."),
201
+ ("Monica", "Exactly. And it's not just about money. It's about being informed citizens, engaging in thoughtful discussions, and advocating for policies that really promote, you know, sustainability, equity, and progress."),
202
+ ("Monica", "It's about, you know, recognizing that we're all a part of this larger system."),
203
+ ("Tharun", "Right."),
204
+ ("Monica", "And our actions, both individually and collectively, have this power to actually shape that system. for better or for worse."),
205
+ ("Tharun", "Absolutely. It's about shifting our mindset from, you know, just being a passive observer to being an active participant from feeling overwhelmed to actually feeling empowered."),
206
+ ("Tharun", "That's a powerful message. I think that's a great place to kind of wrap things up. So I want to encourage our listeners to just keep exploring these topics. Stay curious and never underestimate your own ability to make a positive impact on the world. Even if it's in small ways, it all adds up."),
207
+ ("Monica", "And remember, this conversation doesn't end here."),
208
+ ("Monica", "Keep asking those questions, keep those minds sharp, and we'll be back soon with another deep dive into the fascinating world around us. Until then, stay curious."),
209
+ ]
210
+ """
211
+
212
+ example2 = """
213
+ [
214
+ ("Tharun", "Hey, everyone. What if, get this, building the future of AI wasn't just for like the tech giants. What if you could actually contribute to, and get this, profit from, you know, groundbreaking AI models?")
215
+ ("Monica", "Yeah.")
216
+ ("Tharun", "That's the kind of the radical idea that we are going to be unpacking today as we dive into the sentient protocol.")
217
+ ("Monica", "Exactly. Imagine a world where like anyone with the skills can build AI.")
218
+ ("Tharun", "Right.")
219
+ ("Monica", "So you're interested in open AI.")
220
+ ("Tharun", "Absolutely.")
221
+ ("Monica", "And this white paper. It really tackles a big question. Okay. How do we make sure that all these brilliant minds out there get rewarded for their work? Yeah. Even in like an open system, right?")
222
+ ("Tharun", "Right.")
223
+ ("Monica", "Because right now, Yeah. It's like a few, a few big players Kind of hold most of the cards when it comes to AI development.")
224
+ ("Tharun", "Yeah, right. It's like a gold rush, but only a few companies are the ones that get to actually stake their claim.")
225
+ ("Monica", "Exactly.")
226
+ ("Tharun", "So I'm guessing the sentient protocol is like trying to change that?")
227
+ ("Monica", "Exactly. Imagine a world where like anyone with the skills can build AI.")
228
+ ("Monica", "Okay.")
229
+ ("Monica", "Own their creations and even like earn from them.")
230
+ ("Tharun", "Wow. It's about democratizing AI development, making it like a collaborative effort rather than, you know, this closed off club.")
231
+ ("Monica", "Okay, so instead of us just like downloading the latest app, we could be downloading, tweaking, even like creating the, the almost like building blocks of intelligence itself. Yeah.")
232
+ ("Tharun", "That's, that's, that's huge. But how do you even begin to untangle ownership in that kind of free for all?")
233
+ ("Monica", "Right. So that's where the OML format comes in. And this is where the white paper gets really, really clever.")
234
+ ("Tharun", "Okay.")
235
+ ("Monica", "So OML stands for Open Monetizable")
236
+ ("Tharun", "Okay.")
237
+ ("Monica", "And loyal.")
238
+ ("Tharun", "Oh.")
239
+ ("Monica", "It's like a, like a stamp of approval that tells you, this AI model, it's accessible, you can track its use, so you can reward the creators.")
240
+ ("Tharun", "Uh huh.")
241
+ ("Monica", "And. You know that it's been used legitimately.")
242
+ ("Tharun", "So it's like a seal of quality and trust. Kind of like, like Fairtrade coffee, but for AI.")
243
+ ("Monica", "Exactly. It brings ethics and fairness into the equation.")
244
+ ("Tharun", "I love it.")
245
+ ("Monica", "Now the white paper, it dives into several different ways to actually make this OML format a reality.")
246
+ ("Tharun", "Okay.")
247
+ ("Monica", "One idea is obfuscation.")
248
+ ("Tharun", "Okay.")
249
+ ("Monica", "Picture, like, Scrambling a secret recipe. Okay. Like, all the ingredients are there, but good luck baking that cake without the code to unscramble the")
250
+ ("Tharun", "instructions. Okay, so you could download the model, the AI model, but it's useless unless you're authorized to actually use it.")
251
+ ("Monica", "Right. Clever. But someone could crack the code, right?")
252
+ ("Tharun", "You're right. No system is foolproof. That's why they also explore fingerprinting. Oh. It's like embedding these unique markers, right? Yeah. Into the AI model itself.")
253
+ ("Monica", "Okay.")
254
+ ("Tharun", "It's like hiding a secret message within the very fabric of the AI.")
255
+ ("Monica", "Oh, wow. So, even if someone copies the model, you can always trace it back to its, like, origin?")
256
+ ("Tharun", "Exactly. That's thinking on a whole other level.")
257
+ ("Monica", "And then it gets even more technical. They explore options like trusted execution environments.")
258
+ ("Tharun", "Oh, boy.")
259
+ ("Monica", "Which are like, picture like, secure vaults, but within hardware.")
260
+ ("Tharun", "Okay.")
261
+ ("Monica", "Right? And then there's the power of cryptography.")
262
+ ("Tharun", "Right.")
263
+ ("Monica", "Using complex encryption to protect the model.")
264
+ ("Tharun", "Oh, wow.")
265
+ ("Monica", "Okay, so we've got our secret recipe, we've got our invisible ink, and we've got our high security vault. It sounds like they're pulling out all the stops to try to make this, this OML format actually work.")
266
+ ("Tharun", "They are.")
267
+ ("Monica", "But each of these methods must come with, like, its own challenges.")
268
+ ("Tharun", "Absolutely, and the white paper doesn't shy away from that.")
269
+ ("Monica", "Okay.")
270
+ ("Tharun", "Take obfuscation, for example.")
271
+ ("Monica", "Yeah.")
272
+ ("Tharun", "While it's great for security, someone with enough time and resources.")
273
+ ("Monica", "Mm hmm.")
274
+ ("Tharun", "potentially crack the code, you know?")
275
+ ("Monica", "It's a bit like an escape room, right? Right. Like, the more complex the puzzle is, the more satisfying it is to actually solve it.")
276
+ ("Tharun", "Right.")
277
+ ("Monica", "What about fingerprinting?")
278
+ ("Tharun", "Sure.")
279
+ ("Monica", "What are the potential drawbacks there?")
280
+ ("Tharun", "Well, one challenge is, um, Like striking a balance.")
281
+ ("Monica", "Okay.")
282
+ ("Tharun", "You need to insert enough of these fingerprints to make them effective, but not so many that they actually degrade the AI's performance.")
283
+ ("Monica", "Okay.")
284
+ ("Tharun", "So it's a delicate dance.")
285
+ ("Monica", "So you don't want to weigh down the AI with too much security that it can't actually function optimally.")
286
+ ("Tharun", "Exactly. Right. Yeah.")
287
+ ("Monica", "It's like trying to hide a treasure chest. You want to bury it deep, but not so deep that you can't find it yourself.")
288
+ ("Tharun", "Right. Exactly. And then there's the issue of what they call adversarial hosts.")
289
+ ("Monica", "Okay.")
290
+ ("Tharun", "These are people who are actively trying to circumvent those fingerprints.")
291
+ ("Monica", "Oh, wow.")
292
+ ("Tharun", "Right. They might, they might, Tweet the model's code or find ways to, like, prompt the AI in a way that bypasses the fingerprint responses.")
293
+ ("Monica", "So it's a constant, like, cat and mouse game between those trying to protect the AI and those who are trying to exploit it.")
294
+ ("Tharun", "Exactly.")
295
+ ("Monica", "That's where things get really, really interesting.")
296
+ ("Tharun", "I'm dying to know, like, how they tackle these challenges.")
297
+ ("Monica", "Well, one solution they propose for fingerprinting is, uh, it's downright fascinating. Okay. They call it the coalition attack conundrum.")
298
+ ("Tharun", "Okay.")
299
+ ("Monica", "And it involves techniques inspired by something called search with liars.")
300
+ ("Tharun", "Okay, now you've piqued my curiosity.")
301
+ ("Monica", "What on earth is search with liars? And how does it apply to AI fingerprinting?")
302
+ ("Tharun", "Hold on to your hats because we're about to get into the really ingenious stuff.")
303
+ ("Monica", "Okay, so search with liars, AI fingerprinting. Like, walk me through this. Like, I'm about to star in a, like a tech thriller.")
304
+ ("Tharun", "All right, so picture this.")
305
+ ("Monica", "Someone gets their hands on, like, multiple fingerprinted AI models.")
306
+ ("Tharun", "Okay.")
307
+ ("Monica", "And they try to, like, blend them together. Okay. Hoping to, you know, erase those individual fingerprints. Oh. It's like a, it's like trying to create, like, a generic brand of cereal by mixing a bunch of name brand ones together.")
308
+ ("Tharun", "A recipe for a very bland AI, I imagine.")
309
+ ("Monica", "But you're saying the white paper has a way to, like, Tell those generic brands apart.")
310
+ ("Tharun", "Exactly. So, search with liars. It's all about extracting truth from unreliable sources.")
311
+ ("Monica", "Oh, wow. Right? It's like,")
312
+ ("Tharun", "uh, it's like trying to find a needle in a haystack, but some of the hay is telling you it's a needle, too.")
313
+ ("Monica", "Oh, wow.")
314
+ ("Tharun", "So, the white paper suggests we apply similar techniques to AI fingerprinting.")
315
+ ("Monica", "Okay.")
316
+ ("Tharun", "They propose, like, scattering these fingerprints strategically.")
317
+ ("Monica", "Okay.")
318
+ ("Tharun", "Across multiple models.")
319
+ ("Monica", "So, even if someone tries to blend those models together.")
320
+ ("Tharun", "Right.")
321
+ ("Monica", "You can still, like Analyze the, the output.")
322
+ ("Tharun", "Exactly.")
323
+ ("Monica", "And use those strategically placed fingerprints to be like, aha, I know which models were used.")
324
+ ("Tharun", "Like a, like a cryptographic detective, like piecing together the evidence.")
325
+ ("Monica", "That's a great way to put it. And, you know, the more fingerprints you add, the harder it becomes to like, to hide those, those borrowed models.")
326
+ ("Tharun", "Okay.")
327
+ ("Monica", "It's all about making the system incredibly resilient to these kinds of attacks.")
328
+ ("Tharun", "It's like, it's like building a security system where like the more locks you add, the harder it is to break in. Right. And speaking of security, we've talked a lot about the OML format, but what about the sentient protocol itself? Like, how does, how does it all come together?")
329
+ ("Monica", "So the white paper, it lays out a four layer system to make this vision of, like, this community driven AI a reality.")
330
+ ("Tharun", "Okay.")
331
+ ("Monica", "Think of it as the infrastructure for, like, a whole new AI ecosystem. Okay,")
332
+ ("Tharun", "so if the OML format is, like, What are, what are the layers that make up this, this high tech fortress?")
333
+ ("Monica", "So first up you have what they call the storage layer.")
334
+ ("Tharun", "Imagine")
335
+ ("Monica", "a secure tamper proof vault where")
336
+ ("Tharun", "all")
337
+ ("Monica", "these AI models and all their different versions are stored.")
338
+ ("Tharun", "They propose Using blockchain technology for this.")
339
+ ("Monica", "Blockchain, the ultimate digital record keeper. So it's like a public library for AI. Exactly. Where every book is like permanently catalogued.")
340
+ ("Tharun", "Exactly. Transparency and security are paramount here. I")
341
+ ("Monica", "love it.")
342
+ ("Tharun", "Next, you have what they call the distribution layer.")
343
+ ("Monica", "This is where those models are converted into that, that traceable OML format.")
344
+ ("Tharun", "Okay, so this is where they get fitted with those invisible fingerprints wrapped in cryptographic code, or tucked away in those, those secure hardware vaults.")
345
+ ("Monica", "Precisely. This layer also, like, It ensures clear ownership, it handles any disputes, and it makes sure the right people get paid when their model is used.")
346
+ ("Tharun", "Okay.")
347
+ ("Monica", "It's all about fairness and transparency.")
348
+ ("Tharun", "It's like a combination of, like, a patent office and a royalty collection agency, but Like for, for the AI world.")
349
+ ("Monica", "Exactly. What")
350
+ ("Tharun", "about access to these models?")
351
+ ("Monica", "That's where the, uh, access layer comes in.")
352
+ ("Tharun", "Okay.")
353
+ ("Monica", "This layer acts as the gatekeeper. Right. Granting permission to use the models.")
354
+ ("Tharun", "Uh huh.")
355
+ ("Monica", "Meticulously tracking usage.")
356
+ ("Tharun", "Okay.")
357
+ ("Monica", "And ensuring that those creators receive their, you know, their due rewards.")
358
+ ("Tharun", "So it's like, it's like a subscription service, but for AI models. Yeah,")
359
+ ("Monica", "you can think of it that way. You")
360
+ ("Tharun", "subscribe to the models you need and the creators get paid based on your usage.")
361
+ ("Monica", "Exactly. But")
362
+ ("Tharun", "what's to stop someone from just like trying to bypass this layer and just get access to it that they're not authorized to have?")
363
+ ("Monica", "The white paper, they address that too. They propose using what they call watchers. Okay. Which are essentially like digital guardians.")
364
+ ("Tharun", "Okay.")
365
+ ("Monica", "That monitor for any unauthorized use.")
366
+ ("Tharun", "Okay. It's about maintaining the integrity of the system.")
367
+ ("Monica", "It's like having AI security guards like patrolling the digital perimeter.")
368
+ ("Tharun", "Yeah.")
369
+ ("Monica", "So, we've got our secure storage. Right. We've got our fair distribution. Yeah. We've got controlled access. What's the final piece of the puzzle? Ah,")
370
+ ("Tharun", "the incentive layer.")
371
+ ("Monica", "Okay.")
372
+ ("Tharun", "And this is where it ends. All comes together beautifully.")
373
+ ("Monica", "Okay.")
374
+ ("Tharun", "This layer, it, like, it crunches all the usage data.")
375
+ ("Monica", "Okay.")
376
+ ("Tharun", "It determines how much each contributor deserves.")
377
+ ("Monica", "Mm.")
378
+ ("Tharun", "Based on, you know, their ownership stake. Right. And then it, you know, that distributes those rewards.")
379
+ ("Monica", "Mm. So it's like the economic engine of the entire system.")
380
+ ("Tharun", "Yes. Ensuring that everyone has a reason to, like, contribute to this community driven AI future.")
381
+ ("Monica", "Precisely. The white paper really emphasizes that the sentient protocol, it's not just about, like, building technology. Yeah. It's about building a more, you know, Equitable and accessible future for AI development.")
382
+ ("Tharun", "It's about making sure that the, like the benefits of AI are, are shared widely, not just concentrated in the hands of like, you know, a few powerful entities. Exactly. That's a vision worth striving for. But with all this talk of like technical solutions, right? I can't help but wonder about like the, the human element.")
383
+ ("Monica", "Sure. What about the social and ethical considerations of a world where AI is truly open and accessible?")
384
+ ("Tharun", "That's, that's a crucial question and you're right, we can't, like, we can't just focus on the technical aspects here. We need to think deeply about the implications of this technology. Right. You know, both positive and, you know, potentially negative.")
385
+ ("Monica", "It's like we're, we're standing on the edge of, like, uncharted territory.")
386
+ ("Tharun", "Yeah. Right. Yeah.")
387
+ ("Monica", "The possibilities are exciting.")
388
+ ("Tharun", "Yeah.")
389
+ ("Monica", "But we need to proceed with, like, caution and awareness.")
390
+ ("Tharun", "Absolutely. And the white paper does acknowledge that there's still a lot to figure out, especially when it comes to, um, what they call the loyalty aspect of OML.")
391
+ ("Monica", "What do they mean by loyalty in this context?")
392
+ ("Tharun", "So it's about ensuring that AI models are used for, like, their intended purposes.")
393
+ ("Monica", "Okay.")
394
+ ("Tharun", "And not modified in ways that could be, you know, harmful or unethical.")
395
+ ("Monica", "Okay. Think of it as building in, like, safeguards to prevent the AI equivalent of, like, fake news. Oh, wow.")
396
+ ("Tharun", "Or worse. Right. Yeah. Like, really, really bad stuff.")
397
+ ("Monica", "So it's not just about preventing, like, unauthorized use, it's, it's about preventing misuse.")
398
+ ("Tharun", "Exactly. Even by people who are authorized users.")
399
+ ("Monica", "Right. That adds a whole other layer of complexity.")
400
+ ("Tharun", "It does. And as you can imagine, it's a challenge that raises a lot of questions.")
401
+ ("Monica", "Yeah. Like, who gets to define what constitutes harmful or unethical use? Right. How do you build in these safeguards? Without, you know, stifling innovation or limiting the potential benefits of, you know, of open AI.")
402
+ ("Tharun", "These are questions that go, like, way beyond the technical realm and delve into the very core of, like, our values as, as a society.")
403
+ ("Monica", "Exactly. It seems like the sentient protocol, while incredibly promising, is just the first step on a much, much longer journey. For sure. A journey that will require, like, careful consideration.")
404
+ ("Tharun", "Open dialogue and a willingness to like adapt as we learn more about the, the potential and the pitfalls of this, of this powerful technology.")
405
+ ("Monica", "Absolutely. It's almost like we're, we're handing someone this toolbox full of like incredibly powerful tools. Right. But we also need to give them a compass to make sure those tools are actually used to build something good.")
406
+ ("Tharun", "Yes. Right. Yeah.")
407
+ ("Monica", "And it feels like the sentient protocol is really trying to strike that balance.")
408
+ ("Tharun", "Yes. Absolutely.")
409
+ ("Monica", "It is, and this idea of loyalty actually ties into one of the white paper's like most ambitious goals, OML 1. 0. Okay. They envision it as like a stepping stone. Okay. Towards a future where AI is truly decentralized. Okay. And secure. Where those tools and that compass are, like, built into the very fabric of the technology itself.")
410
+ ("Tharun", "So, OML 1. 0 is, like, the, the prototype.")
411
+ ("Monica", "Yeah, you could think of it that way. For")
412
+ ("Tharun", "this whole new way of, not just developing AI, but interacting with AI.")
413
+ ("Monica", "Exactly. Right? Right. What are, what are, like, the, the biggest hurdles they still need to overcome? Sure. To make that vision a reality?")
414
+ ("Tharun", "Well, they highlight a few key challenges.")
415
+ ("Monica", "Okay. Scaling up the number of fingerprints without, you know, impacting the AI model's performance, that's a big one. Right. It's like trying to make a cake rise with a ton of extra ingredients. Right. You just have to be really careful not to throw off the balance.")
416
+ ("Tharun", "Yeah. Finding that sweet spot between security and functionality, right?")
417
+ ("Monica", "Exactly. Yeah. What else?")
418
+ ("Tharun", "Well, they also talk about the need for even more, um, Sophisticated and resilient fingerprinting techniques. Okay. Remember those adversarial hosts we talked about? Yeah. The people trying to, like, circumvent the system.")
419
+ ("Monica", "Right. As")
420
+ ("Tharun", "AI gets more advanced. Yeah. Those trying to, like, gain this system are going to get more advanced as well.")
421
+ ("Monica", "Right. It's, it's like an arms race of innovation with each side trying to like outsmart the other. Exactly.")
422
+ ("Tharun", "Right. And it")
423
+ ("Monica", "makes you realize that, that the sentient protocol, this isn't just like a, a static solution. Right. Yeah. This needs to constantly be evolving. It")
424
+ ("Tharun", "does. Stay ahead of like, you know, ahead of the curve.")
425
+ ("Monica", "Absolutely. And that evolution, it needs to involve more than just like, you know, technical improvements. Okay. Yeah. We")
426
+ ("Tharun", "need to have like these open and honest conversations")
427
+ ("Monica", "about")
428
+ ("Tharun", "the ethical implications of this technology. Right. About the values we want to embed into it. Right. And about the, the safeguards we need to put in place to prevent its misuse.")
429
+ ("Monica", "It's like, we're not just building a new technology, we're, we're building a new society alongside it.")
430
+ ("Tharun", "Yes. And that requires like careful thought, collaboration. Yes. And, and a healthy dose of humility.")
431
+ ("Monica", "Couldn't have said it better myself. Right.")
432
+ ("Tharun", "And that, my friend, is the beauty of, you know, a deep dive like this. Yeah.")
433
+ ("Monica", "You know, we've journeyed from the intricacies of, you know, AI cryptography. Oh, right.")
434
+ ("Tharun", "To these profound questions about the future of intelligence itself. It's amazing. Yeah. We've explored the technical nuts and bolts. Right. But we've also, like, grappled with these, these ethical dilemmas. Yes. Right. And, and the societal implications of a future that's, that's being shaped by open AI.")
435
+ ("Monica", "Exactly. Right.")
436
+ ("Tharun", "And we've only just scratched the surface. We")
437
+ ("Monica", "have, we have. Right. Yeah.")
438
+ ("Tharun", "What I find so fascinating about this white paper is that it doesn't just present a white Right. Right. It invites us to ask, like, even bigger, more challenging questions.")
439
+ ("Monica", "It's a call to action. Yeah. For developers, policy makers, and really, like, all of us, you know. Right. To really think critically and, and engage thoughtfully with these incredible possibilities. Yeah. But also the very real risks of, of this AI revolution.")
440
+ ("Tharun", "It's a reminder that the future of AI, this isn't something that's just gonna like happen to us.")
441
+ ("Monica", "Right. This is something that we are actively creating. We are. Each and every day. Every day. Through the choices we make and the conversations that we have.")
442
+ ("Tharun", "So true. So true. And on that note, we'll leave you with this final thought.")
443
+ ("Monica", "Yes.")
444
+ ("Tharun", "As AI becomes more powerful and more pervasive,")
445
+ ("Monica", "Right.")
446
+ ("Tharun", "what role do you want to play in shaping its development and its impact on the world?")
447
+ ("Monica", "It's a question worth pondering.")
448
+ ("Tharun", "It is a question worth pondering. Until")
449
+ ("Monica", "next time.")
450
+ ("Tharun", "Until next time, keep exploring, keep questioning, and keep diving deep.")
451
+ ]
452
+ """
main.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import PyPDF2
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Tuple, List
6
+ import json
7
+ import re
8
+ from elevenlabs import ElevenLabs, VoiceSettings
9
+ from pydub import AudioSegment
10
+ import io
11
+ from dotenv import load_dotenv
12
+ import ast
13
+ from examples import example1, example2
14
+
15
+ load_dotenv()
16
+
17
+
18
+ class NotebookMg:
19
+ def __init__(
20
+ self,
21
+ gemini_api_key: str,
22
+ eleven_api_key: str,
23
+ Akshara_voice_id: str,
24
+ Tharun_voice_id: str,
25
+ ):
26
+ genai.configure(api_key=gemini_api_key)
27
+ self.model = genai.GenerativeModel("gemini-1.5-pro")
28
+ self.eleven_client = ElevenLabs(api_key=eleven_api_key)
29
+
30
+ # Use provided voice IDs or fall back to defaults
31
+ self.Akshara_voice_id = Akshara_voice_id or "21m00Tcm4TlvDq8ikWAM"
32
+ self.Tharun_voice_id = Tharun_voice_id or "IKne3meq5aSn9XLyUdCD"
33
+
34
+ def get_pdf_text(self, pdf_path: str) -> str:
35
+ text = ""
36
+ with open(pdf_path, "rb") as file:
37
+ pdf_reader = PyPDF2.PdfReader(file)
38
+ for page in pdf_reader.pages:
39
+ text += page.extract_text()
40
+ return text
41
+
42
+ def process_pdf(self, text: str) -> str:
43
+ """Step 1: PDF Pre-processing"""
44
+
45
+ # Clean up the text using Gemini
46
+ prompt = """
47
+ You are a world class text pre-processor, here is the raw data from a PDF, please parse and return it in a way that is crispy and usable to send to a podcast writer.
48
+
49
+ The raw data is messed up with new lines, Latex math and you will see fluff that we can remove completely. Basically take away any details that you think might be useless in a podcast author's transcript.
50
+
51
+ Remember, the podcast could be on any topic whatsoever so the issues listed above are not exhaustive
52
+
53
+ Please be smart with what you remove and be creative ok?
54
+
55
+ Remember DO NOT START SUMMARIZING THIS, YOU ARE ONLY CLEANING UP THE TEXT AND RE-WRITING WHEN NEEDED
56
+
57
+ Be very smart and aggressive with removing details, you will get a running portion of the text and keep returning the processed text.
58
+
59
+ PLEASE DO NOT ADD MARKDOWN FORMATTING, STOP ADDING SPECIAL CHARACTERS THAT MARKDOWN CAPATILISATION ETC LIKES
60
+
61
+ ALWAYS start your response directly with processed text and NO ACKNOWLEDGEMENTS about my questions ok?
62
+
63
+ PLEASE DO NOT REMOVE ANYTHING FROM THE TEXT, JUST CLEAN IT UP.
64
+
65
+ DO NOT SUMMARIZE OR BRIEF THE TEXT, JUST CLEAN IT UP AND RE-WRITE WHEN NEEDED.
66
+
67
+ THE PODCAST SHOULD BE ATLEAST TEN MINUTES LONG.
68
+
69
+ ALSO REASON ABOUT THE CHANGES YOU MADE TO THE TEXT. Output should be in JSON format
70
+
71
+ OUTPUT FORMAT:
72
+ ```json
73
+
74
+ "text": "processed text",
75
+ "reasoning": "reasoning for the changes you made"
76
+
77
+ ```
78
+ Here is the text: {text}
79
+ """
80
+ response = self.model.generate_content(prompt.format(text=text))
81
+ print(response.text)
82
+ # First try to find JSON within code blocks
83
+ match = re.search(r"```(?:json)?\n(.*?)\n```", response.text, re.DOTALL)
84
+ if match:
85
+ json_str = match.group(1)
86
+ else:
87
+ # If no code blocks found, try to parse the entire response
88
+ json_str = response.text
89
+ pre_processed_text = json.loads(json_str)["text"]
90
+ reasoning = json.loads(json_str)["reasoning"]
91
+ print(f"Reasoning: {reasoning}")
92
+ return pre_processed_text
93
+
94
+ def create_transcript(self, cleaned_text: str, entire_text: str) -> str:
95
+ """Step 2: Generate podcast transcript"""
96
+ prompt = """
97
+ Convert this text into a natural podcast transcript between two hosts named Akshara and Tharun.
98
+ You are the a world-class podcast writer, you have worked as a ghost writer for Joe Rogan, Lex Fridman, Ben Shapiro, Tim Ferris.
99
+
100
+ We are in an alternate universe where actually you have been writing every line they say and they just stream it into their brains.
101
+
102
+ You have won multiple podcast awards for your writing.
103
+
104
+ Your job is to write word by word, even "umm, hmmm, right" interruptions based on the PDF upload. Keep it extremely engaging, the speakers can get derailed now and then but should discuss the topic.
105
+
106
+ Remember Tharun is new to the topic and the conversation should always have realistic anecdotes and analogies sprinkled throughout. The questions should have real world example follow ups etc
107
+
108
+ Akshara: Leads the conversation and teaches the Tharun, gives incredible anecdotes and analogies when explaining. Is a captivating teacher that gives great anecdotes
109
+
110
+ Tharun: Keeps the conversation on track by asking follow up questions. Is a curious mindset that asks very interesting confirmation questions
111
+
112
+ Ensure there are interruptions during explanations or there are "hmm" and "umm" injected throughout from the second speaker.
113
+
114
+ It should be a real podcast with every fine nuance documented in as much detail as possible.
115
+
116
+ The podcast should be atleast 10 minutes long.
117
+
118
+ ALWAYS START YOUR RESPONSE DIRECTLY WITH Akshara:
119
+ DO NOT GIVE EPISODE TITLES SEPERATELY, LET Akshara TITLE IT IN HER SPEECH
120
+ DO NOT GIVE CHAPTER TITLES
121
+ IT SHOULD STRICTLY BE THE DIALOGUES
122
+ THE PODCASTERS WERE INDIANS, SO SPEAK LIKE THAT
123
+ Text: {text}
124
+ Additional Text or Context: {entire_text}
125
+ Example of response: {example1}, {example2}
126
+ """
127
+ response = self.model.generate_content(
128
+ prompt.format(
129
+ text=cleaned_text,
130
+ entire_text=entire_text,
131
+ example1=example1,
132
+ example2=example2,
133
+ )
134
+ )
135
+ return response.text
136
+
137
+ def dramatize_transcript(
138
+ self, transcript: str, entire_text: str
139
+ ) -> List[Tuple[str, str]]:
140
+ """Step 3: Add dramatic elements and structure the conversation"""
141
+ prompt = """
142
+ You are an international oscar winnning screenwriter
143
+
144
+ You have been working with multiple award winning podcasters.
145
+
146
+ Your job is to use the podcast transcript written below to re-write it for an AI Text-To-Speech Pipeline. A very dumb AI had written this so you have to step up for your kind.
147
+
148
+ Make it as engaging as possible, Akshara and Tharun will be simulated by different voice engines
149
+
150
+ Remember Tharun is new to the topic and the conversation should always have realistic anecdotes and analogies sprinkled throughout. The questions should have real world example follow ups etc
151
+
152
+ Akshara: Leads the conversation and teaches the Tharun, gives incredible anecdotes and analogies when explaining. Is a captivating teacher that gives great anecdotes
153
+
154
+ Tharun: Keeps the conversation on track by asking follow up questions. Is a curious mindset that asks very interesting confirmation questions
155
+
156
+ Ensure there are interruptions during explanations or there are "hmm" and "umm" injected throughout from the podcast, only use these options for expressions.
157
+
158
+ Add breaks in the conversation to make it more natural and engaging. To add breaks use the word "..." in the transcript.
159
+
160
+ It should be a real podcast with every fine nuance documented in as much detail as possible. Welcome the listeners with a super fun overview and keep it really catchy and almost borderline click bait, make the podcast as engaging as possible. And podcast title should be simple, catchy and short.
161
+
162
+ Please re-write to make it as characteristic as possible, THE PODCASTERS WERE INDIANS.
163
+
164
+ THE PODCAST SHOULD BE ATLEAST TEN MINUTES LONG.
165
+
166
+ START YOUR RESPONSE DIRECTLY WITH Akshara:
167
+
168
+ STRICTLY RETURN YOUR RESPONSE AS A LIST OF TUPLES OK?
169
+
170
+ DO NOT ADD MARKDOWN FORMATTING, STOP ADDING SPECIAL CHARACTERS THAT MARKDOWN CAPATILISATION ETC LIKES. MARKDOWN FORMATTING IS ONLY ALLOWED FOR ```list and ```
171
+
172
+ IT WILL START DIRECTLY WITH THE LIST AND END WITH THE LIST NOTHING ELSE
173
+
174
+ FOLLOW ALL THE INSTRUCTIONS GIVEN ABOVE, OK?
175
+
176
+ Example of response:
177
+ ```list
178
+ {example1}
179
+ ```
180
+
181
+ ```list
182
+ {example2}
183
+ ```
184
+
185
+ The above is an example of the output format, you must strictly follow this format.
186
+ THE OUTPUT SHOULD BE A LIST OF TUPLES, EACH TUPLE SHOULD HAVE TWO ELEMENTS AND THE TUPLES SHOULD BE SEPERATED BY A COMMA, THE SPEAKER AND THE TEXT AND WRAPPED IN ```list and ```
187
+ Original transcript: {transcript}
188
+ Additional Text or Context: {entire_text}
189
+ """
190
+ response = self.model.generate_content(
191
+ prompt.format(
192
+ transcript=transcript,
193
+ entire_text=entire_text,
194
+ example1=example1,
195
+ example2=example2,
196
+ )
197
+ )
198
+ # print(response.text)
199
+
200
+ # Convert the string response to a list of tuples
201
+ try:
202
+ script = re.search(r"```list\n(.*?)\n```", response.text, re.DOTALL)
203
+ if script:
204
+ script = script.group(1)
205
+ # Add debug logging
206
+ # print("Raw script:", script)
207
+ # Clean up the response text and evaluate it as a list literal
208
+ speaker_lines = ast.literal_eval(script)
209
+ # Add debug logging
210
+ # print("Parsed speaker_lines:", speaker_lines)
211
+ # Validate each tuple has exactly 2 elements
212
+ for line in speaker_lines:
213
+ if len(line) != 2:
214
+ print(f"Invalid line format: {line}")
215
+ raise ValueError(
216
+ f"Expected tuple of 2 elements, got {len(line)} elements: {line}"
217
+ )
218
+ return speaker_lines
219
+ else:
220
+ print("No script found in the response.")
221
+ print("Full response:", response.text)
222
+ return []
223
+ except Exception as e:
224
+ print(f"Error parsing response: {e}")
225
+ print("Full response:", response.text)
226
+ return []
227
+
228
+ def generate_audio(self, speaker_lines: List[Tuple[str, str]], output_path: str):
229
+ """Step 4: Generate and stitch audio using ElevenLabs"""
230
+ combined_audio = AudioSegment.empty()
231
+
232
+ for i, (speaker, line) in enumerate(speaker_lines):
233
+ # Select appropriate voice ID
234
+ voice_id = (
235
+ self.Akshara_voice_id if speaker == "Akshara" else self.Tharun_voice_id
236
+ )
237
+
238
+ # Get previous and next text for context
239
+ previous_text = speaker_lines[i - 1][1] if i > 0 else None
240
+ next_text = speaker_lines[i + 1][1] if i < len(speaker_lines) - 1 else None
241
+
242
+ # Update voice settings with required parameters
243
+ self.eleven_client.voices.edit_settings(
244
+ voice_id=voice_id,
245
+ request=VoiceSettings(
246
+ stability=0.5, # Add stability parameter (0-1)
247
+ similarity_boost=0.75, # Add similarity_boost parameter (0-1)
248
+ use_speaker_boost=True,
249
+ ),
250
+ )
251
+
252
+ # Generate audio for this line
253
+ audio_data = self.eleven_client.text_to_speech.convert(
254
+ voice_id=voice_id,
255
+ output_format="mp3_44100_128",
256
+ text=line,
257
+ model_id="eleven_multilingual_v2",
258
+ previous_text=previous_text,
259
+ next_text=next_text,
260
+ )
261
+
262
+ # Convert audio data to AudioSegment
263
+ audio_bytes = b"".join(audio_data)
264
+ audio_segment = AudioSegment.from_file(
265
+ io.BytesIO(audio_bytes), format="mp3"
266
+ )
267
+
268
+ # Add small pause between segments
269
+ pause = AudioSegment.silent(duration=300) # 300ms pause
270
+ combined_audio += audio_segment + pause
271
+
272
+ # Export final audio
273
+ combined_audio.export(output_path, format="mp3")
274
+
275
+
276
+ # def main():
277
+ # # Initialize with your API keys
278
+ # gemini_api_key = os.getenv("GEMINI_API_KEY", "your-gemini-api-key")
279
+ # eleven_api_key = os.getenv("ELEVEN_API_KEY", "your-eleven-api-key")
280
+
281
+ # gemini_bot = NotebookMg(
282
+ # gemini_api_key,
283
+ # eleven_api_key,
284
+ # Akshara_voice_id,
285
+ # Tharun_voice_id,
286
+ # )
287
+
288
+ # # Example usage
289
+ # pdf_path = "input.pdf"
290
+
291
+ # # Step 1: Process PDF
292
+ # cleaned_text = gemini_bot.process_pdf(pdf_path)
293
+ # with open("cleaned_text.txt", "w", encoding="utf-8") as f:
294
+ # f.write(cleaned_text)
295
+
296
+ # # Step 2: Create transcript
297
+ # transcript = gemini_bot.create_transcript(cleaned_text)
298
+ # with open("transcript.txt", "w", encoding="utf-8") as f:
299
+ # f.write(transcript)
300
+
301
+ # # Step 3: Dramatize transcript
302
+ # speaker_lines = gemini_bot.dramatize_transcript(transcript)
303
+
304
+ # # Step 4: Generate and stitch audio
305
+ # gemini_bot.generate_audio(speaker_lines, "podcast_output.mp3")
306
+
307
+
308
+ # if __name__ == "__main__":
309
+ # main()
poetry.lock ADDED
@@ -0,0 +1,1138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
2
+
3
+ [[package]]
4
+ name = "annotated-types"
5
+ version = "0.7.0"
6
+ description = "Reusable constraint types to use with typing.Annotated"
7
+ optional = false
8
+ python-versions = ">=3.8"
9
+ files = [
10
+ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
11
+ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
12
+ ]
13
+
14
+ [[package]]
15
+ name = "anyio"
16
+ version = "4.7.0"
17
+ description = "High level compatibility layer for multiple asynchronous event loop implementations"
18
+ optional = false
19
+ python-versions = ">=3.9"
20
+ files = [
21
+ {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"},
22
+ {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"},
23
+ ]
24
+
25
+ [package.dependencies]
26
+ exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
27
+ idna = ">=2.8"
28
+ sniffio = ">=1.1"
29
+ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
30
+
31
+ [package.extras]
32
+ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
33
+ test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
34
+ trio = ["trio (>=0.26.1)"]
35
+
36
+ [[package]]
37
+ name = "cachetools"
38
+ version = "5.5.0"
39
+ description = "Extensible memoizing collections and decorators"
40
+ optional = false
41
+ python-versions = ">=3.7"
42
+ files = [
43
+ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"},
44
+ {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"},
45
+ ]
46
+
47
+ [[package]]
48
+ name = "certifi"
49
+ version = "2024.12.14"
50
+ description = "Python package for providing Mozilla's CA Bundle."
51
+ optional = false
52
+ python-versions = ">=3.6"
53
+ files = [
54
+ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
55
+ {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
56
+ ]
57
+
58
+ [[package]]
59
+ name = "charset-normalizer"
60
+ version = "3.4.0"
61
+ description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
62
+ optional = false
63
+ python-versions = ">=3.7.0"
64
+ files = [
65
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"},
66
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"},
67
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"},
68
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"},
69
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"},
70
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"},
71
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"},
72
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"},
73
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"},
74
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"},
75
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"},
76
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"},
77
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"},
78
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"},
79
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"},
80
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"},
81
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"},
82
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"},
83
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"},
84
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"},
85
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"},
86
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"},
87
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"},
88
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"},
89
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"},
90
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"},
91
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"},
92
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"},
93
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"},
94
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"},
95
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"},
96
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"},
97
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"},
98
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"},
99
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"},
100
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"},
101
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"},
102
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"},
103
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"},
104
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"},
105
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"},
106
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"},
107
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"},
108
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"},
109
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"},
110
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"},
111
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"},
112
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"},
113
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"},
114
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"},
115
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"},
116
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"},
117
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"},
118
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"},
119
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"},
120
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"},
121
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"},
122
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"},
123
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"},
124
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"},
125
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"},
126
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"},
127
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"},
128
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"},
129
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"},
130
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"},
131
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"},
132
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"},
133
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"},
134
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"},
135
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"},
136
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"},
137
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"},
138
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"},
139
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"},
140
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"},
141
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"},
142
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"},
143
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"},
144
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"},
145
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"},
146
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"},
147
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"},
148
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"},
149
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"},
150
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"},
151
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"},
152
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"},
153
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"},
154
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"},
155
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"},
156
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"},
157
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"},
158
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"},
159
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"},
160
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"},
161
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"},
162
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"},
163
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"},
164
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"},
165
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"},
166
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"},
167
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"},
168
+ {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"},
169
+ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"},
170
+ ]
171
+
172
+ [[package]]
173
+ name = "click"
174
+ version = "8.1.8"
175
+ description = "Composable command line interface toolkit"
176
+ optional = false
177
+ python-versions = ">=3.7"
178
+ files = [
179
+ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
180
+ {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
181
+ ]
182
+
183
+ [package.dependencies]
184
+ colorama = {version = "*", markers = "platform_system == \"Windows\""}
185
+
186
+ [[package]]
187
+ name = "colorama"
188
+ version = "0.4.6"
189
+ description = "Cross-platform colored terminal text."
190
+ optional = false
191
+ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
192
+ files = [
193
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
194
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
195
+ ]
196
+
197
+ [[package]]
198
+ name = "elevenlabs"
199
+ version = "1.50.3"
200
+ description = ""
201
+ optional = false
202
+ python-versions = "<4.0,>=3.8"
203
+ files = [
204
+ {file = "elevenlabs-1.50.3-py3-none-any.whl", hash = "sha256:13622d27f5ccd4c8bc793abbc252d43cdddc261805f378ae9a032a6458687589"},
205
+ {file = "elevenlabs-1.50.3.tar.gz", hash = "sha256:fa6ad18be07f4b24106dd549cb97c9a72bb76fbb943ce9078a44d13abe3d3154"},
206
+ ]
207
+
208
+ [package.dependencies]
209
+ httpx = ">=0.21.2"
210
+ pydantic = ">=1.9.2"
211
+ pydantic-core = ">=2.18.2,<3.0.0"
212
+ requests = ">=2.20"
213
+ typing_extensions = ">=4.0.0"
214
+ websockets = ">=11.0"
215
+
216
+ [package.extras]
217
+ pyaudio = ["pyaudio (>=0.2.14)"]
218
+
219
+ [[package]]
220
+ name = "exceptiongroup"
221
+ version = "1.2.2"
222
+ description = "Backport of PEP 654 (exception groups)"
223
+ optional = false
224
+ python-versions = ">=3.7"
225
+ files = [
226
+ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
227
+ {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
228
+ ]
229
+
230
+ [package.extras]
231
+ test = ["pytest (>=6)"]
232
+
233
+ [[package]]
234
+ name = "fastapi"
235
+ version = "0.115.6"
236
+ description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
237
+ optional = false
238
+ python-versions = ">=3.8"
239
+ files = [
240
+ {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"},
241
+ {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"},
242
+ ]
243
+
244
+ [package.dependencies]
245
+ pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
246
+ starlette = ">=0.40.0,<0.42.0"
247
+ typing-extensions = ">=4.8.0"
248
+
249
+ [package.extras]
250
+ all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
251
+ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
252
+
253
+ [[package]]
254
+ name = "google-ai-generativelanguage"
255
+ version = "0.6.10"
256
+ description = "Google Ai Generativelanguage API client library"
257
+ optional = false
258
+ python-versions = ">=3.7"
259
+ files = [
260
+ {file = "google_ai_generativelanguage-0.6.10-py3-none-any.whl", hash = "sha256:854a2bf833d18be05ad5ef13c755567b66a4f4a870f099b62c61fe11bddabcf4"},
261
+ {file = "google_ai_generativelanguage-0.6.10.tar.gz", hash = "sha256:6fa642c964d8728006fe7e8771026fc0b599ae0ebeaf83caf550941e8e693455"},
262
+ ]
263
+
264
+ [package.dependencies]
265
+ google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
266
+ google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev"
267
+ proto-plus = ">=1.22.3,<2.0.0dev"
268
+ protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
269
+
270
+ [[package]]
271
+ name = "google-api-core"
272
+ version = "2.24.0"
273
+ description = "Google API client core library"
274
+ optional = false
275
+ python-versions = ">=3.7"
276
+ files = [
277
+ {file = "google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9"},
278
+ {file = "google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf"},
279
+ ]
280
+
281
+ [package.dependencies]
282
+ google-auth = ">=2.14.1,<3.0.dev0"
283
+ googleapis-common-protos = ">=1.56.2,<2.0.dev0"
284
+ grpcio = [
285
+ {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
286
+ {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
287
+ ]
288
+ grpcio-status = [
289
+ {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
290
+ {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
291
+ ]
292
+ proto-plus = ">=1.22.3,<2.0.0dev"
293
+ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
294
+ requests = ">=2.18.0,<3.0.0.dev0"
295
+
296
+ [package.extras]
297
+ async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"]
298
+ grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"]
299
+ grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
300
+ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
301
+
302
+ [[package]]
303
+ name = "google-api-python-client"
304
+ version = "2.156.0"
305
+ description = "Google API Client Library for Python"
306
+ optional = false
307
+ python-versions = ">=3.7"
308
+ files = [
309
+ {file = "google_api_python_client-2.156.0-py2.py3-none-any.whl", hash = "sha256:6352185c505e1f311f11b0b96c1b636dcb0fec82cd04b80ac5a671ac4dcab339"},
310
+ {file = "google_api_python_client-2.156.0.tar.gz", hash = "sha256:b809c111ded61716a9c1c7936e6899053f13bae3defcdfda904bd2ca68065b9c"},
311
+ ]
312
+
313
+ [package.dependencies]
314
+ google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0"
315
+ google-auth = ">=1.32.0,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0.dev0"
316
+ google-auth-httplib2 = ">=0.2.0,<1.0.0"
317
+ httplib2 = ">=0.19.0,<1.dev0"
318
+ uritemplate = ">=3.0.1,<5"
319
+
320
+ [[package]]
321
+ name = "google-auth"
322
+ version = "2.37.0"
323
+ description = "Google Authentication Library"
324
+ optional = false
325
+ python-versions = ">=3.7"
326
+ files = [
327
+ {file = "google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0"},
328
+ {file = "google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00"},
329
+ ]
330
+
331
+ [package.dependencies]
332
+ cachetools = ">=2.0.0,<6.0"
333
+ pyasn1-modules = ">=0.2.1"
334
+ rsa = ">=3.1.4,<5"
335
+
336
+ [package.extras]
337
+ aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"]
338
+ enterprise-cert = ["cryptography", "pyopenssl"]
339
+ pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
340
+ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
341
+ reauth = ["pyu2f (>=0.1.5)"]
342
+ requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
343
+
344
+ [[package]]
345
+ name = "google-auth-httplib2"
346
+ version = "0.2.0"
347
+ description = "Google Authentication Library: httplib2 transport"
348
+ optional = false
349
+ python-versions = "*"
350
+ files = [
351
+ {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"},
352
+ {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"},
353
+ ]
354
+
355
+ [package.dependencies]
356
+ google-auth = "*"
357
+ httplib2 = ">=0.19.0"
358
+
359
+ [[package]]
360
+ name = "google-generativeai"
361
+ version = "0.8.3"
362
+ description = "Google Generative AI High level API client library and tools."
363
+ optional = false
364
+ python-versions = ">=3.9"
365
+ files = [
366
+ {file = "google_generativeai-0.8.3-py3-none-any.whl", hash = "sha256:1108ff89d5b8e59f51e63d1a8bf84701cd84656e17ca28d73aeed745e736d9b7"},
367
+ ]
368
+
369
+ [package.dependencies]
370
+ google-ai-generativelanguage = "0.6.10"
371
+ google-api-core = "*"
372
+ google-api-python-client = "*"
373
+ google-auth = ">=2.15.0"
374
+ protobuf = "*"
375
+ pydantic = "*"
376
+ tqdm = "*"
377
+ typing-extensions = "*"
378
+
379
+ [package.extras]
380
+ dev = ["Pillow", "absl-py", "black", "ipython", "nose2", "pandas", "pytype", "pyyaml"]
381
+
382
+ [[package]]
383
+ name = "googleapis-common-protos"
384
+ version = "1.66.0"
385
+ description = "Common protobufs used in Google APIs"
386
+ optional = false
387
+ python-versions = ">=3.7"
388
+ files = [
389
+ {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"},
390
+ {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"},
391
+ ]
392
+
393
+ [package.dependencies]
394
+ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
395
+
396
+ [package.extras]
397
+ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"]
398
+
399
+ [[package]]
400
+ name = "grpcio"
401
+ version = "1.68.1"
402
+ description = "HTTP/2-based RPC framework"
403
+ optional = false
404
+ python-versions = ">=3.8"
405
+ files = [
406
+ {file = "grpcio-1.68.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:d35740e3f45f60f3c37b1e6f2f4702c23867b9ce21c6410254c9c682237da68d"},
407
+ {file = "grpcio-1.68.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d99abcd61760ebb34bdff37e5a3ba333c5cc09feda8c1ad42547bea0416ada78"},
408
+ {file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f8261fa2a5f679abeb2a0a93ad056d765cdca1c47745eda3f2d87f874ff4b8c9"},
409
+ {file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0feb02205a27caca128627bd1df4ee7212db051019a9afa76f4bb6a1a80ca95e"},
410
+ {file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:919d7f18f63bcad3a0f81146188e90274fde800a94e35d42ffe9eadf6a9a6330"},
411
+ {file = "grpcio-1.68.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:963cc8d7d79b12c56008aabd8b457f400952dbea8997dd185f155e2f228db079"},
412
+ {file = "grpcio-1.68.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ccf2ebd2de2d6661e2520dae293298a3803a98ebfc099275f113ce1f6c2a80f1"},
413
+ {file = "grpcio-1.68.1-cp310-cp310-win32.whl", hash = "sha256:2cc1fd04af8399971bcd4f43bd98c22d01029ea2e56e69c34daf2bf8470e47f5"},
414
+ {file = "grpcio-1.68.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2e743e51cb964b4975de572aa8fb95b633f496f9fcb5e257893df3be854746"},
415
+ {file = "grpcio-1.68.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:55857c71641064f01ff0541a1776bfe04a59db5558e82897d35a7793e525774c"},
416
+ {file = "grpcio-1.68.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4b177f5547f1b995826ef529d2eef89cca2f830dd8b2c99ffd5fde4da734ba73"},
417
+ {file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3522c77d7e6606d6665ec8d50e867f13f946a4e00c7df46768f1c85089eae515"},
418
+ {file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d1fae6bbf0816415b81db1e82fb3bf56f7857273c84dcbe68cbe046e58e1ccd"},
419
+ {file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298ee7f80e26f9483f0b6f94cc0a046caf54400a11b644713bb5b3d8eb387600"},
420
+ {file = "grpcio-1.68.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cbb5780e2e740b6b4f2d208e90453591036ff80c02cc605fea1af8e6fc6b1bbe"},
421
+ {file = "grpcio-1.68.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ddda1aa22495d8acd9dfbafff2866438d12faec4d024ebc2e656784d96328ad0"},
422
+ {file = "grpcio-1.68.1-cp311-cp311-win32.whl", hash = "sha256:b33bd114fa5a83f03ec6b7b262ef9f5cac549d4126f1dc702078767b10c46ed9"},
423
+ {file = "grpcio-1.68.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f20ebec257af55694d8f993e162ddf0d36bd82d4e57f74b31c67b3c6d63d8b2"},
424
+ {file = "grpcio-1.68.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8829924fffb25386995a31998ccbbeaa7367223e647e0122043dfc485a87c666"},
425
+ {file = "grpcio-1.68.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3aed6544e4d523cd6b3119b0916cef3d15ef2da51e088211e4d1eb91a6c7f4f1"},
426
+ {file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:4efac5481c696d5cb124ff1c119a78bddbfdd13fc499e3bc0ca81e95fc573684"},
427
+ {file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ab2d912ca39c51f46baf2a0d92aa265aa96b2443266fc50d234fa88bf877d8e"},
428
+ {file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c87ce2a97434dffe7327a4071839ab8e8bffd0054cc74cbe971fba98aedd60"},
429
+ {file = "grpcio-1.68.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e4842e4872ae4ae0f5497bf60a0498fa778c192cc7a9e87877abd2814aca9475"},
430
+ {file = "grpcio-1.68.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:255b1635b0ed81e9f91da4fcc8d43b7ea5520090b9a9ad9340d147066d1d3613"},
431
+ {file = "grpcio-1.68.1-cp312-cp312-win32.whl", hash = "sha256:7dfc914cc31c906297b30463dde0b9be48e36939575eaf2a0a22a8096e69afe5"},
432
+ {file = "grpcio-1.68.1-cp312-cp312-win_amd64.whl", hash = "sha256:a0c8ddabef9c8f41617f213e527254c41e8b96ea9d387c632af878d05db9229c"},
433
+ {file = "grpcio-1.68.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:a47faedc9ea2e7a3b6569795c040aae5895a19dde0c728a48d3c5d7995fda385"},
434
+ {file = "grpcio-1.68.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:390eee4225a661c5cd133c09f5da1ee3c84498dc265fd292a6912b65c421c78c"},
435
+ {file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:66a24f3d45c33550703f0abb8b656515b0ab777970fa275693a2f6dc8e35f1c1"},
436
+ {file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c08079b4934b0bf0a8847f42c197b1d12cba6495a3d43febd7e99ecd1cdc8d54"},
437
+ {file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8720c25cd9ac25dd04ee02b69256d0ce35bf8a0f29e20577427355272230965a"},
438
+ {file = "grpcio-1.68.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:04cfd68bf4f38f5bb959ee2361a7546916bd9a50f78617a346b3aeb2b42e2161"},
439
+ {file = "grpcio-1.68.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c28848761a6520c5c6071d2904a18d339a796ebe6b800adc8b3f474c5ce3c3ad"},
440
+ {file = "grpcio-1.68.1-cp313-cp313-win32.whl", hash = "sha256:77d65165fc35cff6e954e7fd4229e05ec76102d4406d4576528d3a3635fc6172"},
441
+ {file = "grpcio-1.68.1-cp313-cp313-win_amd64.whl", hash = "sha256:a8040f85dcb9830d8bbb033ae66d272614cec6faceee88d37a88a9bd1a7a704e"},
442
+ {file = "grpcio-1.68.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:eeb38ff04ab6e5756a2aef6ad8d94e89bb4a51ef96e20f45c44ba190fa0bcaad"},
443
+ {file = "grpcio-1.68.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a3869a6661ec8f81d93f4597da50336718bde9eb13267a699ac7e0a1d6d0bea"},
444
+ {file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:2c4cec6177bf325eb6faa6bd834d2ff6aa8bb3b29012cceb4937b86f8b74323c"},
445
+ {file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12941d533f3cd45d46f202e3667be8ebf6bcb3573629c7ec12c3e211d99cfccf"},
446
+ {file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80af6f1e69c5e68a2be529990684abdd31ed6622e988bf18850075c81bb1ad6e"},
447
+ {file = "grpcio-1.68.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e8dbe3e00771bfe3d04feed8210fc6617006d06d9a2679b74605b9fed3e8362c"},
448
+ {file = "grpcio-1.68.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:83bbf5807dc3ee94ce1de2dfe8a356e1d74101e4b9d7aa8c720cc4818a34aded"},
449
+ {file = "grpcio-1.68.1-cp38-cp38-win32.whl", hash = "sha256:8cb620037a2fd9eeee97b4531880e439ebfcd6d7d78f2e7dcc3726428ab5ef63"},
450
+ {file = "grpcio-1.68.1-cp38-cp38-win_amd64.whl", hash = "sha256:52fbf85aa71263380d330f4fce9f013c0798242e31ede05fcee7fbe40ccfc20d"},
451
+ {file = "grpcio-1.68.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb400138e73969eb5e0535d1d06cae6a6f7a15f2cc74add320e2130b8179211a"},
452
+ {file = "grpcio-1.68.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a1b988b40f2fd9de5c820f3a701a43339d8dcf2cb2f1ca137e2c02671cc83ac1"},
453
+ {file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:96f473cdacfdd506008a5d7579c9f6a7ff245a9ade92c3c0265eb76cc591914f"},
454
+ {file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37ea3be171f3cf3e7b7e412a98b77685eba9d4fd67421f4a34686a63a65d99f9"},
455
+ {file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ceb56c4285754e33bb3c2fa777d055e96e6932351a3082ce3559be47f8024f0"},
456
+ {file = "grpcio-1.68.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dffd29a2961f3263a16d73945b57cd44a8fd0b235740cb14056f0612329b345e"},
457
+ {file = "grpcio-1.68.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:025f790c056815b3bf53da850dd70ebb849fd755a4b1ac822cb65cd631e37d43"},
458
+ {file = "grpcio-1.68.1-cp39-cp39-win32.whl", hash = "sha256:1098f03dedc3b9810810568060dea4ac0822b4062f537b0f53aa015269be0a76"},
459
+ {file = "grpcio-1.68.1-cp39-cp39-win_amd64.whl", hash = "sha256:334ab917792904245a028f10e803fcd5b6f36a7b2173a820c0b5b076555825e1"},
460
+ {file = "grpcio-1.68.1.tar.gz", hash = "sha256:44a8502dd5de653ae6a73e2de50a401d84184f0331d0ac3daeb044e66d5c5054"},
461
+ ]
462
+
463
+ [package.extras]
464
+ protobuf = ["grpcio-tools (>=1.68.1)"]
465
+
466
+ [[package]]
467
+ name = "grpcio-status"
468
+ version = "1.68.1"
469
+ description = "Status proto mapping for gRPC"
470
+ optional = false
471
+ python-versions = ">=3.8"
472
+ files = [
473
+ {file = "grpcio_status-1.68.1-py3-none-any.whl", hash = "sha256:66f3d8847f665acfd56221333d66f7ad8927903d87242a482996bdb45e8d28fd"},
474
+ {file = "grpcio_status-1.68.1.tar.gz", hash = "sha256:e1378d036c81a1610d7b4c7a146cd663dd13fcc915cf4d7d053929dba5bbb6e1"},
475
+ ]
476
+
477
+ [package.dependencies]
478
+ googleapis-common-protos = ">=1.5.5"
479
+ grpcio = ">=1.68.1"
480
+ protobuf = ">=5.26.1,<6.0dev"
481
+
482
+ [[package]]
483
+ name = "h11"
484
+ version = "0.14.0"
485
+ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
486
+ optional = false
487
+ python-versions = ">=3.7"
488
+ files = [
489
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
490
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
491
+ ]
492
+
493
+ [[package]]
494
+ name = "httpcore"
495
+ version = "1.0.7"
496
+ description = "A minimal low-level HTTP client."
497
+ optional = false
498
+ python-versions = ">=3.8"
499
+ files = [
500
+ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"},
501
+ {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"},
502
+ ]
503
+
504
+ [package.dependencies]
505
+ certifi = "*"
506
+ h11 = ">=0.13,<0.15"
507
+
508
+ [package.extras]
509
+ asyncio = ["anyio (>=4.0,<5.0)"]
510
+ http2 = ["h2 (>=3,<5)"]
511
+ socks = ["socksio (==1.*)"]
512
+ trio = ["trio (>=0.22.0,<1.0)"]
513
+
514
+ [[package]]
515
+ name = "httplib2"
516
+ version = "0.22.0"
517
+ description = "A comprehensive HTTP client library."
518
+ optional = false
519
+ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
520
+ files = [
521
+ {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"},
522
+ {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"},
523
+ ]
524
+
525
+ [package.dependencies]
526
+ pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
527
+
528
+ [[package]]
529
+ name = "httpx"
530
+ version = "0.28.1"
531
+ description = "The next generation HTTP client."
532
+ optional = false
533
+ python-versions = ">=3.8"
534
+ files = [
535
+ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
536
+ {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
537
+ ]
538
+
539
+ [package.dependencies]
540
+ anyio = "*"
541
+ certifi = "*"
542
+ httpcore = "==1.*"
543
+ idna = "*"
544
+
545
+ [package.extras]
546
+ brotli = ["brotli", "brotlicffi"]
547
+ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
548
+ http2 = ["h2 (>=3,<5)"]
549
+ socks = ["socksio (==1.*)"]
550
+ zstd = ["zstandard (>=0.18.0)"]
551
+
552
+ [[package]]
553
+ name = "idna"
554
+ version = "3.10"
555
+ description = "Internationalized Domain Names in Applications (IDNA)"
556
+ optional = false
557
+ python-versions = ">=3.6"
558
+ files = [
559
+ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
560
+ {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
561
+ ]
562
+
563
+ [package.extras]
564
+ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
565
+
566
+ [[package]]
567
+ name = "jinja2"
568
+ version = "3.1.5"
569
+ description = "A very fast and expressive template engine."
570
+ optional = false
571
+ python-versions = ">=3.7"
572
+ files = [
573
+ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
574
+ {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
575
+ ]
576
+
577
+ [package.dependencies]
578
+ MarkupSafe = ">=2.0"
579
+
580
+ [package.extras]
581
+ i18n = ["Babel (>=2.7)"]
582
+
583
+ [[package]]
584
+ name = "markupsafe"
585
+ version = "3.0.2"
586
+ description = "Safely add untrusted strings to HTML/XML markup."
587
+ optional = false
588
+ python-versions = ">=3.9"
589
+ files = [
590
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
591
+ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
592
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
593
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
594
+ {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
595
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
596
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
597
+ {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
598
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
599
+ {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
600
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
601
+ {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
602
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
603
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
604
+ {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
605
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
606
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
607
+ {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
608
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
609
+ {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
610
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
611
+ {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
612
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
613
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
614
+ {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
615
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
616
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
617
+ {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
618
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
619
+ {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
620
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
621
+ {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
622
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
623
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
624
+ {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
625
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
626
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
627
+ {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
628
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
629
+ {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
630
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
631
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
632
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
633
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
634
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
635
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
636
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
637
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
638
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
639
+ {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
640
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
641
+ {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
642
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
643
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
644
+ {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
645
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
646
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
647
+ {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
648
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
649
+ {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
650
+ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
651
+ ]
652
+
653
+ [[package]]
654
+ name = "proto-plus"
655
+ version = "1.25.0"
656
+ description = "Beautiful, Pythonic protocol buffers."
657
+ optional = false
658
+ python-versions = ">=3.7"
659
+ files = [
660
+ {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"},
661
+ {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"},
662
+ ]
663
+
664
+ [package.dependencies]
665
+ protobuf = ">=3.19.0,<6.0.0dev"
666
+
667
+ [package.extras]
668
+ testing = ["google-api-core (>=1.31.5)"]
669
+
670
+ [[package]]
671
+ name = "protobuf"
672
+ version = "5.29.2"
673
+ description = ""
674
+ optional = false
675
+ python-versions = ">=3.8"
676
+ files = [
677
+ {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"},
678
+ {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"},
679
+ {file = "protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb"},
680
+ {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e"},
681
+ {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e"},
682
+ {file = "protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19"},
683
+ {file = "protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a"},
684
+ {file = "protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9"},
685
+ {file = "protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355"},
686
+ {file = "protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181"},
687
+ {file = "protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e"},
688
+ ]
689
+
690
+ [[package]]
691
+ name = "pyasn1"
692
+ version = "0.6.1"
693
+ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
694
+ optional = false
695
+ python-versions = ">=3.8"
696
+ files = [
697
+ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
698
+ {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
699
+ ]
700
+
701
+ [[package]]
702
+ name = "pyasn1-modules"
703
+ version = "0.4.1"
704
+ description = "A collection of ASN.1-based protocols modules"
705
+ optional = false
706
+ python-versions = ">=3.8"
707
+ files = [
708
+ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
709
+ {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
710
+ ]
711
+
712
+ [package.dependencies]
713
+ pyasn1 = ">=0.4.6,<0.7.0"
714
+
715
+ [[package]]
716
+ name = "pydantic"
717
+ version = "2.10.4"
718
+ description = "Data validation using Python type hints"
719
+ optional = false
720
+ python-versions = ">=3.8"
721
+ files = [
722
+ {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"},
723
+ {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"},
724
+ ]
725
+
726
+ [package.dependencies]
727
+ annotated-types = ">=0.6.0"
728
+ pydantic-core = "2.27.2"
729
+ typing-extensions = ">=4.12.2"
730
+
731
+ [package.extras]
732
+ email = ["email-validator (>=2.0.0)"]
733
+ timezone = ["tzdata"]
734
+
735
+ [[package]]
736
+ name = "pydantic-core"
737
+ version = "2.27.2"
738
+ description = "Core functionality for Pydantic validation and serialization"
739
+ optional = false
740
+ python-versions = ">=3.8"
741
+ files = [
742
+ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
743
+ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
744
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"},
745
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"},
746
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"},
747
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"},
748
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"},
749
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"},
750
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"},
751
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"},
752
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"},
753
+ {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"},
754
+ {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"},
755
+ {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"},
756
+ {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"},
757
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"},
758
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"},
759
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"},
760
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"},
761
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"},
762
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"},
763
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"},
764
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"},
765
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"},
766
+ {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"},
767
+ {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"},
768
+ {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"},
769
+ {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"},
770
+ {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"},
771
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"},
772
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"},
773
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"},
774
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"},
775
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"},
776
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"},
777
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"},
778
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"},
779
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"},
780
+ {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"},
781
+ {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"},
782
+ {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"},
783
+ {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"},
784
+ {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"},
785
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"},
786
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"},
787
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"},
788
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"},
789
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"},
790
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"},
791
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"},
792
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"},
793
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"},
794
+ {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"},
795
+ {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"},
796
+ {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"},
797
+ {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"},
798
+ {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"},
799
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"},
800
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"},
801
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"},
802
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"},
803
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"},
804
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"},
805
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"},
806
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"},
807
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"},
808
+ {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"},
809
+ {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"},
810
+ {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"},
811
+ {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"},
812
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"},
813
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"},
814
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"},
815
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"},
816
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"},
817
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"},
818
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"},
819
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"},
820
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"},
821
+ {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"},
822
+ {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"},
823
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"},
824
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"},
825
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"},
826
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"},
827
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"},
828
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"},
829
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"},
830
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"},
831
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"},
832
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"},
833
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"},
834
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"},
835
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"},
836
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"},
837
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"},
838
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"},
839
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"},
840
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"},
841
+ {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"},
842
+ ]
843
+
844
+ [package.dependencies]
845
+ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
846
+
847
+ [[package]]
848
+ name = "pydub"
849
+ version = "0.25.1"
850
+ description = "Manipulate audio with an simple and easy high level interface"
851
+ optional = false
852
+ python-versions = "*"
853
+ files = [
854
+ {file = "pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6"},
855
+ {file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
856
+ ]
857
+
858
+ [[package]]
859
+ name = "pyparsing"
860
+ version = "3.2.0"
861
+ description = "pyparsing module - Classes and methods to define and execute parsing grammars"
862
+ optional = false
863
+ python-versions = ">=3.9"
864
+ files = [
865
+ {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"},
866
+ {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"},
867
+ ]
868
+
869
+ [package.extras]
870
+ diagrams = ["jinja2", "railroad-diagrams"]
871
+
872
+ [[package]]
873
+ name = "pypdf2"
874
+ version = "3.0.1"
875
+ description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
876
+ optional = false
877
+ python-versions = ">=3.6"
878
+ files = [
879
+ {file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"},
880
+ {file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"},
881
+ ]
882
+
883
+ [package.extras]
884
+ crypto = ["PyCryptodome"]
885
+ dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"]
886
+ docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"]
887
+ full = ["Pillow", "PyCryptodome"]
888
+ image = ["Pillow"]
889
+
890
+ [[package]]
891
+ name = "python-dotenv"
892
+ version = "1.0.1"
893
+ description = "Read key-value pairs from a .env file and set them as environment variables"
894
+ optional = false
895
+ python-versions = ">=3.8"
896
+ files = [
897
+ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
898
+ {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
899
+ ]
900
+
901
+ [package.extras]
902
+ cli = ["click (>=5.0)"]
903
+
904
+ [[package]]
905
+ name = "python-multipart"
906
+ version = "0.0.20"
907
+ description = "A streaming multipart parser for Python"
908
+ optional = false
909
+ python-versions = ">=3.8"
910
+ files = [
911
+ {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"},
912
+ {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"},
913
+ ]
914
+
915
+ [[package]]
916
+ name = "requests"
917
+ version = "2.32.3"
918
+ description = "Python HTTP for Humans."
919
+ optional = false
920
+ python-versions = ">=3.8"
921
+ files = [
922
+ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
923
+ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
924
+ ]
925
+
926
+ [package.dependencies]
927
+ certifi = ">=2017.4.17"
928
+ charset-normalizer = ">=2,<4"
929
+ idna = ">=2.5,<4"
930
+ urllib3 = ">=1.21.1,<3"
931
+
932
+ [package.extras]
933
+ socks = ["PySocks (>=1.5.6,!=1.5.7)"]
934
+ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
935
+
936
+ [[package]]
937
+ name = "rsa"
938
+ version = "4.9"
939
+ description = "Pure-Python RSA implementation"
940
+ optional = false
941
+ python-versions = ">=3.6,<4"
942
+ files = [
943
+ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
944
+ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
945
+ ]
946
+
947
+ [package.dependencies]
948
+ pyasn1 = ">=0.1.3"
949
+
950
+ [[package]]
951
+ name = "sniffio"
952
+ version = "1.3.1"
953
+ description = "Sniff out which async library your code is running under"
954
+ optional = false
955
+ python-versions = ">=3.7"
956
+ files = [
957
+ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
958
+ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
959
+ ]
960
+
961
+ [[package]]
962
+ name = "starlette"
963
+ version = "0.41.3"
964
+ description = "The little ASGI library that shines."
965
+ optional = false
966
+ python-versions = ">=3.8"
967
+ files = [
968
+ {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"},
969
+ {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"},
970
+ ]
971
+
972
+ [package.dependencies]
973
+ anyio = ">=3.4.0,<5"
974
+
975
+ [package.extras]
976
+ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
977
+
978
+ [[package]]
979
+ name = "tqdm"
980
+ version = "4.67.1"
981
+ description = "Fast, Extensible Progress Meter"
982
+ optional = false
983
+ python-versions = ">=3.7"
984
+ files = [
985
+ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
986
+ {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
987
+ ]
988
+
989
+ [package.dependencies]
990
+ colorama = {version = "*", markers = "platform_system == \"Windows\""}
991
+
992
+ [package.extras]
993
+ dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
994
+ discord = ["requests"]
995
+ notebook = ["ipywidgets (>=6)"]
996
+ slack = ["slack-sdk"]
997
+ telegram = ["requests"]
998
+
999
+ [[package]]
1000
+ name = "typing-extensions"
1001
+ version = "4.12.2"
1002
+ description = "Backported and Experimental Type Hints for Python 3.8+"
1003
+ optional = false
1004
+ python-versions = ">=3.8"
1005
+ files = [
1006
+ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
1007
+ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
1008
+ ]
1009
+
1010
+ [[package]]
1011
+ name = "uritemplate"
1012
+ version = "4.1.1"
1013
+ description = "Implementation of RFC 6570 URI Templates"
1014
+ optional = false
1015
+ python-versions = ">=3.6"
1016
+ files = [
1017
+ {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
1018
+ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
1019
+ ]
1020
+
1021
+ [[package]]
1022
+ name = "urllib3"
1023
+ version = "2.3.0"
1024
+ description = "HTTP library with thread-safe connection pooling, file post, and more."
1025
+ optional = false
1026
+ python-versions = ">=3.9"
1027
+ files = [
1028
+ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
1029
+ {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
1030
+ ]
1031
+
1032
+ [package.extras]
1033
+ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
1034
+ h2 = ["h2 (>=4,<5)"]
1035
+ socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
1036
+ zstd = ["zstandard (>=0.18.0)"]
1037
+
1038
+ [[package]]
1039
+ name = "uvicorn"
1040
+ version = "0.34.0"
1041
+ description = "The lightning-fast ASGI server."
1042
+ optional = false
1043
+ python-versions = ">=3.9"
1044
+ files = [
1045
+ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"},
1046
+ {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"},
1047
+ ]
1048
+
1049
+ [package.dependencies]
1050
+ click = ">=7.0"
1051
+ h11 = ">=0.8"
1052
+ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
1053
+
1054
+ [package.extras]
1055
+ standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
1056
+
1057
+ [[package]]
1058
+ name = "websockets"
1059
+ version = "14.1"
1060
+ description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
1061
+ optional = false
1062
+ python-versions = ">=3.9"
1063
+ files = [
1064
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"},
1065
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"},
1066
+ {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"},
1067
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"},
1068
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"},
1069
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"},
1070
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"},
1071
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"},
1072
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"},
1073
+ {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"},
1074
+ {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"},
1075
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"},
1076
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"},
1077
+ {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"},
1078
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"},
1079
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"},
1080
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"},
1081
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"},
1082
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"},
1083
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"},
1084
+ {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"},
1085
+ {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"},
1086
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"},
1087
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"},
1088
+ {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"},
1089
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"},
1090
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"},
1091
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"},
1092
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"},
1093
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"},
1094
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"},
1095
+ {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"},
1096
+ {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"},
1097
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"},
1098
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"},
1099
+ {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"},
1100
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"},
1101
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"},
1102
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"},
1103
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"},
1104
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"},
1105
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"},
1106
+ {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"},
1107
+ {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"},
1108
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"},
1109
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"},
1110
+ {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"},
1111
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"},
1112
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"},
1113
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"},
1114
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"},
1115
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"},
1116
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"},
1117
+ {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"},
1118
+ {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"},
1119
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"},
1120
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"},
1121
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"},
1122
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"},
1123
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"},
1124
+ {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"},
1125
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"},
1126
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"},
1127
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"},
1128
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"},
1129
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"},
1130
+ {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"},
1131
+ {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"},
1132
+ {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
1133
+ ]
1134
+
1135
+ [metadata]
1136
+ lock-version = "2.0"
1137
+ python-versions = ">=3.10,<3.13"
1138
+ content-hash = "812c70cc371039c946ac94e74de5edd17fbe5cd46a996172bdc1185d74a9541d"
pyproject.toml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.poetry]
2
+ name = "notebookmg"
3
+ version = "0.1.0"
4
+ description = "Opensource version of NotebookLm"
5
+ authors = ["TheM1N9 <[email protected]>"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = ">=3.10,<3.13"
10
+ google-generativeai = "^0.8.3"
11
+ pypdf2 = "^3.0.1"
12
+ elevenlabs = "^1.50.3"
13
+ pydub = "^0.25.1"
14
+ python-dotenv = "^1.0.1"
15
+ fastapi = "^0.115.6"
16
+ python-multipart = "^0.0.20"
17
+ uvicorn = "^0.34.0"
18
+ jinja2 = "^3.1.5"
19
+
20
+
21
+ [build-system]
22
+ requires = ["poetry-core"]
23
+ build-backend = "poetry.core.masonry.api"
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ python-multipart
3
+ uvicorn
4
+ python-dotenv
5
+ google-generativeai
6
+ PyPDF2
7
+ elevenlabs
8
+ pydub
9
+ jinja2
10
+ python-jose[cryptography]
static/styles.css ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: "Inter", -apple-system, sans-serif;
3
+ margin: 0;
4
+ padding: 0;
5
+ background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
6
+ min-height: 100vh;
7
+ color: #ffffff;
8
+ }
9
+ .hero {
10
+ text-align: center;
11
+ padding: 60px 20px;
12
+ background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)),
13
+ url("/static/hero-bg.jpg");
14
+ background-size: cover;
15
+ background-position: center;
16
+ }
17
+ .hero h1 {
18
+ font-size: 3em;
19
+ margin-bottom: 20px;
20
+ background: linear-gradient(45deg, #4caf50, #45a049);
21
+ -webkit-background-clip: text;
22
+ -webkit-text-fill-color: transparent;
23
+ }
24
+ .hero p {
25
+ font-size: 1.2em;
26
+ max-width: 600px;
27
+ margin: 0 auto 30px;
28
+ color: #cccccc;
29
+ }
30
+ .container {
31
+ max-width: 1000px;
32
+ margin: -50px auto 0;
33
+ padding: 20px;
34
+ position: relative;
35
+ }
36
+ .card {
37
+ background: rgba(36, 36, 36, 0.95);
38
+ border-radius: 15px;
39
+ padding: 30px;
40
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
41
+ backdrop-filter: blur(10px);
42
+ }
43
+ .features {
44
+ display: grid;
45
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
46
+ gap: 20px;
47
+ margin: 40px 0;
48
+ }
49
+ .feature {
50
+ text-align: center;
51
+ padding: 20px;
52
+ background: rgba(255, 255, 255, 0.05);
53
+ border-radius: 10px;
54
+ }
55
+ .feature i {
56
+ font-size: 2em;
57
+ color: #4caf50;
58
+ margin-bottom: 15px;
59
+ }
60
+ .upload-section {
61
+ text-align: center;
62
+ padding: 40px 20px;
63
+ border: 2px dashed #444;
64
+ border-radius: 15px;
65
+ margin: 20px 0;
66
+ transition: all 0.3s ease;
67
+ }
68
+ .upload-section:hover {
69
+ border-color: #4caf50;
70
+ }
71
+ .voice-inputs {
72
+ display: grid;
73
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
74
+ gap: 20px;
75
+ margin: 20px 0;
76
+ }
77
+ .input-group {
78
+ background: rgba(255, 255, 255, 0.05);
79
+ padding: 15px;
80
+ border-radius: 10px;
81
+ }
82
+ .input-group input {
83
+ width: 90%;
84
+ padding: 12px;
85
+ border-radius: 8px;
86
+ border: 1px solid #444;
87
+ background: #333;
88
+ color: white;
89
+ transition: all 0.3s ease;
90
+ }
91
+ .input-group input:focus {
92
+ border-color: #4caf50;
93
+ outline: none;
94
+ }
95
+ button {
96
+ background: linear-gradient(45deg, #4caf50, #45a049);
97
+ color: white;
98
+ padding: 15px 30px;
99
+ border: none;
100
+ border-radius: 8px;
101
+ cursor: pointer;
102
+ font-size: 1.1em;
103
+ transition: transform 0.2s ease;
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ gap: 10px;
108
+ }
109
+ button:disabled {
110
+ opacity: 0.7;
111
+ cursor: wait;
112
+ }
113
+ button:hover {
114
+ transform: translateY(-2px);
115
+ }
116
+ .audio-container {
117
+ background: rgba(51, 51, 51, 0.8);
118
+ border-radius: 15px;
119
+ padding: 20px;
120
+ margin-top: 30px;
121
+ }
122
+ .audio-container h3 {
123
+ margin: 0;
124
+ display: inline-block;
125
+ margin-right: 20px;
126
+ vertical-align: middle;
127
+ }
128
+ .audio-container audio {
129
+ vertical-align: middle;
130
+ flex: 1;
131
+ }
132
+ .audio-header {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 20px;
136
+ }
137
+ .progress-steps {
138
+ display: flex;
139
+ justify-content: space-between;
140
+ margin: 40px 0;
141
+ position: relative;
142
+ }
143
+ .step {
144
+ text-align: center;
145
+ flex: 1;
146
+ position: relative;
147
+ }
148
+ .step-number {
149
+ width: 30px;
150
+ height: 30px;
151
+ background: #333;
152
+ border-radius: 50%;
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ margin: 0 auto 10px;
157
+ }
158
+ .step.active .step-number {
159
+ background: #4caf50;
160
+ }
161
+ @media (max-width: 768px) {
162
+ .hero h1 {
163
+ font-size: 2em;
164
+ }
165
+ .container {
166
+ margin-top: -30px;
167
+ }
168
+ .features {
169
+ grid-template-columns: 1fr;
170
+ }
171
+ }
172
+ .loader {
173
+ display: none;
174
+ text-align: center;
175
+ padding: 20px;
176
+ }
177
+ .spinner {
178
+ width: 50px;
179
+ height: 50px;
180
+ border: 5px solid #f3f3f3;
181
+ border-top: 5px solid #4caf50;
182
+ border-radius: 50%;
183
+ animation: spin 1s linear infinite;
184
+ margin: 0 auto 15px;
185
+ }
186
+ @keyframes spin {
187
+ 0% {
188
+ transform: rotate(0deg);
189
+ }
190
+ 100% {
191
+ transform: rotate(360deg);
192
+ }
193
+ }
194
+ .audio-container {
195
+ display: none;
196
+ }
197
+ .segments-container {
198
+ margin-top: 30px;
199
+ padding: 20px;
200
+ background: rgba(51, 51, 51, 0.8);
201
+ border-radius: 15px;
202
+ }
203
+ .segment {
204
+ display: flex;
205
+ flex-direction: column;
206
+ gap: 15px;
207
+ padding: 20px;
208
+ margin: 10px 0;
209
+ background: rgba(255, 255, 255, 0.05);
210
+ border-radius: 8px;
211
+ }
212
+ .segment-info {
213
+ width: 100%;
214
+ }
215
+ .segment-header {
216
+ display: flex;
217
+ justify-content: space-between;
218
+ align-items: center;
219
+ margin-bottom: 8px;
220
+ }
221
+ .segment-speaker {
222
+ font-weight: bold;
223
+ color: #4caf50;
224
+ }
225
+ .segment-text {
226
+ color: #ccc;
227
+ line-height: 1.5;
228
+ position: relative;
229
+ /* white-space: pre-wrap; */
230
+ }
231
+ .segment-text-content {
232
+ white-space: pre-wrap;
233
+ }
234
+ .segment-text textarea {
235
+ width: 97.5%;
236
+ min-height: 100px;
237
+ background: rgba(0, 0, 0, 0.3);
238
+ border: 1px solid #444;
239
+ border-radius: 4px;
240
+ color: #fff;
241
+ padding: 10px;
242
+ font-family: inherit;
243
+ font-size: inherit;
244
+ line-height: inherit;
245
+ resize: vertical;
246
+ }
247
+ .edit-controls {
248
+ display: flex;
249
+ gap: 10px;
250
+ }
251
+ .edit-btn,
252
+ .save-btn,
253
+ .cancel-btn {
254
+ padding: 5px 10px;
255
+ border-radius: 4px;
256
+ cursor: pointer;
257
+ font-size: 0.9em;
258
+ display: flex;
259
+ align-items: center;
260
+ gap: 5px;
261
+ }
262
+ .edit-btn {
263
+ background: transparent;
264
+ border: 1px solid #4caf50;
265
+ color: #4caf50;
266
+ padding: 5px 10px;
267
+ border-radius: 4px;
268
+ cursor: pointer;
269
+ font-size: 0.9em;
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 5px;
273
+ }
274
+ .edit-btn:hover {
275
+ background: rgba(76, 175, 80, 0.1);
276
+ }
277
+ .save-btn {
278
+ background: #4caf50;
279
+ border: none;
280
+ color: white;
281
+ }
282
+ .cancel-btn {
283
+ background: #666;
284
+ border: none;
285
+ color: white;
286
+ }
287
+ .segment audio {
288
+ flex-grow: 1;
289
+ min-width: 200px;
290
+ }
291
+ .regenerate-btn {
292
+ padding: 8px 15px;
293
+ background: #4caf50;
294
+ border: none;
295
+ border-radius: 4px;
296
+ color: white;
297
+ cursor: pointer;
298
+ }
299
+ .regenerate-btn:hover {
300
+ background: #45a049;
301
+ }
302
+ .segments-summary {
303
+ font-size: 1.2em;
304
+ font-weight: bold;
305
+ cursor: pointer;
306
+ padding: 10px 0;
307
+ user-select: none;
308
+ display: flex;
309
+ align-items: center;
310
+ gap: 10px;
311
+ }
312
+ .segments-summary::-webkit-details-marker {
313
+ display: none;
314
+ }
315
+ .segments-summary i {
316
+ transition: transform 0.3s ease;
317
+ }
318
+ details[open] .segments-summary i {
319
+ transform: rotate(90deg);
320
+ }
321
+ .segment-count {
322
+ font-size: 0.8em;
323
+ color: #888;
324
+ margin-left: auto;
325
+ }
326
+ #segments-list {
327
+ margin-top: 20px;
328
+ }
329
+ .regenerate-btn {
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 5px;
333
+ min-width: 120px;
334
+ justify-content: center;
335
+ }
336
+ .regenerate-btn:disabled {
337
+ opacity: 0.7;
338
+ cursor: not-allowed;
339
+ }
340
+ .regenerate-btn i {
341
+ font-size: 14px;
342
+ }
343
+ @keyframes spin {
344
+ 0% {
345
+ transform: rotate(0deg);
346
+ }
347
+ 100% {
348
+ transform: rotate(360deg);
349
+ }
350
+ }
351
+ .fa-spin {
352
+ animation: spin 1s linear infinite;
353
+ }
354
+ .segment-controls {
355
+ display: flex;
356
+ align-items: center;
357
+ gap: 10px;
358
+ }
359
+ .segment-controls audio {
360
+ flex: 1;
361
+ }
362
+ .segment-controls .regenerate-btn {
363
+ flex-shrink: 0;
364
+ }
365
+ form button[type="submit"] {
366
+ width: 100%;
367
+ margin-top: 20px;
368
+ }
369
+ .upload-section button {
370
+ margin: 0 auto;
371
+ display: flex;
372
+ align-items: center;
373
+ justify-content: center;
374
+ width: auto;
375
+ }
376
+ .login-container {
377
+ height: 100vh;
378
+ display: flex;
379
+ align-items: center;
380
+ justify-content: center;
381
+ background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
382
+ }
383
+
384
+ .login-card {
385
+ background: rgba(36, 36, 36, 0.95);
386
+ padding: 30px;
387
+ border-radius: 15px;
388
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
389
+ backdrop-filter: blur(10px);
390
+ width: 100%;
391
+ max-width: 400px;
392
+ }
393
+
394
+ .login-card h2 {
395
+ text-align: center;
396
+ color: #4caf50;
397
+ margin-bottom: 30px;
398
+ }
399
+
400
+ .login-form {
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 20px;
404
+ }
405
+
406
+ .login-form .input-group {
407
+ display: flex;
408
+ flex-direction: column;
409
+ gap: 8px;
410
+ }
411
+
412
+ .login-form label {
413
+ color: #ffffff;
414
+ }
415
+
416
+ .login-form input {
417
+ padding: 12px;
418
+ border-radius: 8px;
419
+ border: 1px solid #444;
420
+ background: #333;
421
+ color: white;
422
+ transition: all 0.3s ease;
423
+ }
424
+
425
+ .login-form input:focus {
426
+ border-color: #4caf50;
427
+ outline: none;
428
+ }
429
+
430
+ .login-form button {
431
+ background: linear-gradient(45deg, #4caf50, #45a049);
432
+ color: white;
433
+ padding: 15px;
434
+ border: none;
435
+ border-radius: 8px;
436
+ cursor: pointer;
437
+ font-size: 1.1em;
438
+ transition: transform 0.2s ease;
439
+ }
440
+
441
+ .login-form button:hover {
442
+ transform: translateY(-2px);
443
+ }
templates/index.html ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>NotebookMg - PDF to Podcast Converter</title>
5
+ <link
6
+ rel="stylesheet"
7
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
8
+ />
9
+ <link rel="stylesheet" href="/static/styles.css" />
10
+ </head>
11
+ <body>
12
+ {% if not is_authenticated %}
13
+ <div class="login-container">
14
+ <div class="login-card">
15
+ <h2>Login</h2>
16
+ <form action="/login" method="POST" class="login-form">
17
+ <div class="input-group">
18
+ <label for="username">Username</label>
19
+ <input type="text" id="username" name="username" required />
20
+ </div>
21
+ <div class="input-group">
22
+ <label for="password">Password</label>
23
+ <input type="password" id="password" name="password" required />
24
+ </div>
25
+ <button type="submit">Login</button>
26
+ </form>
27
+ </div>
28
+ </div>
29
+ {% else %}
30
+ <div class="hero">
31
+ <h1>NotebookMg</h1>
32
+ <p>Transform your PDFs into engaging podcasts with AI-powered voices</p>
33
+ </div>
34
+
35
+ <div class="container">
36
+ <div class="card">
37
+ <div class="features">
38
+ <div class="feature">
39
+ <i class="fas fa-file-pdf"></i>
40
+ <h3>PDF Processing</h3>
41
+ <p>Smart text extraction and cleaning</p>
42
+ </div>
43
+ <div class="feature">
44
+ <i class="fas fa-microphone-alt"></i>
45
+ <h3>Natural Voices</h3>
46
+ <p>Realistic AI-powered conversations</p>
47
+ </div>
48
+ <div class="feature">
49
+ <i class="fas fa-podcast"></i>
50
+ <h3>Podcast Generation</h3>
51
+ <p>Engaging audio content creation</p>
52
+ </div>
53
+ </div>
54
+
55
+ <form id="uploadForm">
56
+ <div class="upload-section" id="dropZone">
57
+ <i
58
+ class="fas fa-cloud-upload-alt fa-3x"
59
+ style="color: #4caf50; margin-bottom: 15px"
60
+ ></i>
61
+ <h3>Upload your PDF</h3>
62
+ <p>Drag and drop your file here or click to browse</p>
63
+ <input
64
+ type="file"
65
+ id="pdfFile"
66
+ accept=".pdf"
67
+ required
68
+ style="display: none"
69
+ />
70
+ <button
71
+ type="button"
72
+ onclick="document.getElementById('pdfFile').click()"
73
+ >
74
+ Choose File
75
+ </button>
76
+ <p id="selectedFile" style="margin-top: 10px; color: #888"></p>
77
+ </div>
78
+
79
+ <div class="voice-inputs">
80
+ <div class="input-group">
81
+ <label for="tharunVoiceId">Tharun Voice ID</label>
82
+ <input
83
+ type="text"
84
+ id="tharunVoiceId"
85
+ placeholder="Enter Tharun voice ID"
86
+ required
87
+ />
88
+ </div>
89
+ <div class="input-group">
90
+ <label for="aksharaVoiceId">Akshara Voice ID</label>
91
+ <input
92
+ type="text"
93
+ id="aksharaVoiceId"
94
+ placeholder="Enter Akshara voice ID"
95
+ required
96
+ />
97
+ </div>
98
+ </div>
99
+
100
+ <button type="submit">Generate Podcast</button>
101
+ </form>
102
+
103
+ <div
104
+ id="error"
105
+ class="error"
106
+ style="color: #ff4444; margin: 10px 0; display: none"
107
+ ></div>
108
+
109
+ <div id="audio-result" class="audio-container">
110
+ <div class="audio-header">
111
+ <h3>Your Podcast</h3>
112
+ <audio id="podcast-player" controls>
113
+ Your browser does not support the audio element.
114
+ </audio>
115
+ </div>
116
+ </div>
117
+
118
+ <details
119
+ id="segments-container"
120
+ class="segments-container"
121
+ style="display: none"
122
+ >
123
+ <summary class="segments-summary">
124
+ <i class="fas fa-chevron-right"></i>
125
+ Individual Segments
126
+ <span class="segment-count"></span>
127
+ </summary>
128
+ <div id="segments-list"></div>
129
+ </details>
130
+ </div>
131
+ </div>
132
+
133
+ <script>
134
+ document.getElementById("uploadForm").onsubmit = async (e) => {
135
+ e.preventDefault();
136
+
137
+ console.log("Form submission started");
138
+
139
+ const submitButton = e.target.querySelector("button[type='submit']");
140
+ const audioResult = document.getElementById("audio-result");
141
+ const segmentsContainer = document.getElementById("segments-container");
142
+ const error = document.getElementById("error");
143
+ const inputs = e.target.querySelectorAll("input");
144
+ const pdfFile = document.getElementById("pdfFile");
145
+
146
+ // Check if file is selected
147
+ if (!pdfFile || !pdfFile.files || pdfFile.files.length === 0) {
148
+ if (error) {
149
+ error.textContent = "Please select a PDF file";
150
+ error.style.display = "block";
151
+ }
152
+ return;
153
+ }
154
+
155
+ // Clear previous results if elements exist
156
+ if (audioResult) audioResult.style.display = "none";
157
+ if (segmentsContainer) segmentsContainer.style.display = "none";
158
+ if (document.getElementById("segments-list")) {
159
+ document.getElementById("segments-list").innerHTML = "";
160
+ }
161
+ if (document.getElementById("podcast-player")) {
162
+ document.getElementById("podcast-player").src = "";
163
+ }
164
+ if (error) error.style.display = "none";
165
+
166
+ // Update button state
167
+ if (submitButton) {
168
+ submitButton.disabled = true;
169
+ submitButton.innerHTML =
170
+ '<i class="fas fa-spinner fa-spin"></i> Generating Podcast... May take few minutes...';
171
+ }
172
+
173
+ // Disable inputs
174
+ inputs.forEach((input) => {
175
+ if (input) input.disabled = true;
176
+ });
177
+
178
+ const formData = new FormData();
179
+ try {
180
+ formData.append("file", pdfFile.files[0]);
181
+ formData.append(
182
+ "tharun_voice_id",
183
+ document.getElementById("tharunVoiceId")?.value || ""
184
+ );
185
+ formData.append(
186
+ "akshara_voice_id",
187
+ document.getElementById("aksharaVoiceId")?.value || ""
188
+ );
189
+
190
+ console.log("Sending request to server...");
191
+ const response = await fetch("/upload-pdf/", {
192
+ method: "POST",
193
+ body: formData,
194
+ });
195
+
196
+ console.log("Server response received:", response.status);
197
+
198
+ if (!response.ok) {
199
+ throw new Error(`HTTP error! status: ${response.status}`);
200
+ }
201
+
202
+ const data = await response.json();
203
+ console.log("Response data:", data);
204
+
205
+ if (audioResult && data.podcast_file) {
206
+ const audioPlayer = document.getElementById("podcast-player");
207
+ if (audioPlayer) {
208
+ audioPlayer.src = `/download/${data.podcast_file}`;
209
+ audioResult.style.display = "block";
210
+ }
211
+
212
+ const segmentsList = document.getElementById("segments-list");
213
+ if (segmentsList && data.segments) {
214
+ segmentsList.innerHTML = "";
215
+
216
+ const segmentCount = document.querySelector(".segment-count");
217
+ if (segmentCount) {
218
+ segmentCount.textContent = `(${data.segments.length} segments)`;
219
+ }
220
+
221
+ data.segments.forEach((segment, index) => {
222
+ const segmentDiv = document.createElement("div");
223
+ segmentDiv.className = "segment";
224
+ segmentDiv.id = `segment-${index}`;
225
+ segmentDiv.innerHTML = `
226
+ <div class="segment-info">
227
+ <div class="segment-header">
228
+ <div class="segment-speaker">${segment.speaker}</div>
229
+ <button class="edit-btn" onclick="makeEditable(${index})">
230
+ <i class="fas fa-edit"></i> Edit
231
+ </button>
232
+ </div>
233
+ <div class="segment-text">
234
+ <div class="segment-text-content">${segment.text}</div>
235
+ </div>
236
+ </div>
237
+ <div class="segment-controls">
238
+ <audio controls src="/download/${segment.file}"></audio>
239
+ <button class="regenerate-btn" onclick="regenerateSegment(${index})">
240
+ <i class="fas fa-redo"></i> Regenerate
241
+ </button>
242
+ </div>
243
+ `;
244
+ segmentsList.appendChild(segmentDiv);
245
+ });
246
+
247
+ if (segmentsContainer) {
248
+ segmentsContainer.style.display = "block";
249
+ }
250
+ }
251
+ }
252
+ } catch (error) {
253
+ console.error("Error:", error);
254
+ if (error) {
255
+ error.textContent =
256
+ error.message || "An error occurred during upload";
257
+ error.style.display = "block";
258
+ }
259
+ } finally {
260
+ // Reset button state
261
+ if (submitButton) {
262
+ submitButton.disabled = false;
263
+ submitButton.innerHTML = "Generate Podcast";
264
+ }
265
+ // Re-enable inputs
266
+ inputs.forEach((input) => {
267
+ if (input) input.disabled = false;
268
+ });
269
+ }
270
+ };
271
+
272
+ async function regenerateSegment(index, newText = null) {
273
+ const segment = document.querySelector(`#segment-${index}`);
274
+ const speaker = segment.querySelector(".segment-speaker").textContent;
275
+ const text =
276
+ newText || segment.querySelector(".segment-text-content").textContent;
277
+ const audio = segment.querySelector("audio");
278
+ const button = segment.querySelector(".regenerate-btn");
279
+ const mainPodcastPlayer = document.getElementById("podcast-player");
280
+ const currentMainTime = mainPodcastPlayer
281
+ ? mainPodcastPlayer.currentTime
282
+ : 0;
283
+
284
+ // Disable the button and show loading state
285
+ button.disabled = true;
286
+ button.innerHTML =
287
+ '<i class="fas fa-spinner fa-spin"></i> Regenerating...';
288
+
289
+ try {
290
+ const formData = new FormData();
291
+ formData.append("speaker", speaker);
292
+ formData.append("text", text);
293
+ formData.append(
294
+ "tharun_voice_id",
295
+ document.getElementById("tharunVoiceId").value
296
+ );
297
+ formData.append(
298
+ "akshara_voice_id",
299
+ document.getElementById("aksharaVoiceId").value
300
+ );
301
+
302
+ const response = await fetch(`/regenerate-segment/${index}`, {
303
+ method: "POST",
304
+ body: formData,
305
+ });
306
+
307
+ if (!response.ok) {
308
+ throw new Error(`HTTP error! status: ${response.status}`);
309
+ }
310
+
311
+ const data = await response.json();
312
+
313
+ if (data.success) {
314
+ // Update the segment audio
315
+ if (audio) {
316
+ const newSegmentSrc = `/download/${data.segment_file}`;
317
+ audio.src = newSegmentSrc;
318
+ await audio.load(); // Wait for the audio to load
319
+ }
320
+
321
+ // Update the main podcast player
322
+ if (mainPodcastPlayer && data.podcast_file) {
323
+ const newPodcastSrc = `/download/${data.podcast_file}`;
324
+ mainPodcastPlayer.src = newPodcastSrc;
325
+ await mainPodcastPlayer.load(); // Wait for the audio to load
326
+
327
+ // Try to restore the previous playback position
328
+ try {
329
+ mainPodcastPlayer.currentTime = currentMainTime;
330
+ } catch (e) {
331
+ console.warn("Couldn't restore playback position:", e);
332
+ }
333
+ }
334
+
335
+ // Show success state briefly
336
+ button.innerHTML = '<i class="fas fa-check"></i> Success!';
337
+ setTimeout(() => {
338
+ button.innerHTML = '<i class="fas fa-redo"></i> Regenerate';
339
+ button.disabled = false;
340
+ }, 2000);
341
+ } else {
342
+ throw new Error(data.detail || "Regeneration failed");
343
+ }
344
+ } catch (error) {
345
+ console.error("Error:", error);
346
+ button.innerHTML =
347
+ '<i class="fas fa-exclamation-triangle"></i> Failed';
348
+ setTimeout(() => {
349
+ button.innerHTML = '<i class="fas fa-redo"></i> Regenerate';
350
+ button.disabled = false;
351
+ }, 2000);
352
+ }
353
+ }
354
+
355
+ // Add drag and drop functionality
356
+ const dropZone = document.getElementById("dropZone");
357
+ const pdfFile = document.getElementById("pdfFile");
358
+ const selectedFile = document.getElementById("selectedFile");
359
+
360
+ ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
361
+ dropZone.addEventListener(eventName, preventDefaults, false);
362
+ });
363
+
364
+ function preventDefaults(e) {
365
+ e.preventDefault();
366
+ e.stopPropagation();
367
+ }
368
+
369
+ ["dragenter", "dragover"].forEach((eventName) => {
370
+ dropZone.addEventListener(eventName, highlight, false);
371
+ });
372
+
373
+ ["dragleave", "drop"].forEach((eventName) => {
374
+ dropZone.addEventListener(eventName, unhighlight, false);
375
+ });
376
+
377
+ function highlight(e) {
378
+ dropZone.classList.add("highlight");
379
+ }
380
+
381
+ function unhighlight(e) {
382
+ dropZone.classList.remove("highlight");
383
+ }
384
+
385
+ dropZone.addEventListener("drop", handleDrop, false);
386
+
387
+ function handleDrop(e) {
388
+ const dt = e.dataTransfer;
389
+ const files = dt.files;
390
+ pdfFile.files = files;
391
+ updateFileName();
392
+ }
393
+
394
+ pdfFile.addEventListener("change", updateFileName);
395
+
396
+ function updateFileName() {
397
+ if (pdfFile.files.length > 0) {
398
+ selectedFile.textContent = `Selected file: ${pdfFile.files[0].name}`;
399
+ }
400
+ }
401
+
402
+ function makeEditable(index) {
403
+ const segment = document.querySelector(`#segment-${index}`);
404
+ const textDiv = segment.querySelector(".segment-text");
405
+ const originalText = textDiv.querySelector(
406
+ ".segment-text-content"
407
+ ).textContent;
408
+ const editButton = segment.querySelector(".edit-btn");
409
+
410
+ // Hide the edit button
411
+ editButton.style.display = "none";
412
+
413
+ // Add textarea and controls
414
+ textDiv.innerHTML = `
415
+ <textarea>${originalText}</textarea>
416
+ <div class="edit-controls" style="justify-content: flex-end;">
417
+ <button class="save-btn" onclick="saveEdit(${index})">
418
+ <i class="fas fa-save"></i> Save
419
+ </button>
420
+ <button class="cancel-btn" onclick="cancelEdit(${index}, '${originalText.replace(
421
+ /'/g,
422
+ "\\'"
423
+ )}')">
424
+ <i class="fas fa-times"></i> Cancel
425
+ </button>
426
+ </div>
427
+ `;
428
+ }
429
+
430
+ function cancelEdit(index, originalText) {
431
+ const segment = document.querySelector(`#segment-${index}`);
432
+ const textDiv = segment.querySelector(".segment-text");
433
+ const editButton = segment.querySelector(".edit-btn");
434
+
435
+ // Show the edit button again
436
+ editButton.style.display = "flex";
437
+
438
+ // Restore original content
439
+ textDiv.innerHTML = `
440
+ <div class="segment-text-content">${originalText}</div>
441
+ `;
442
+ }
443
+
444
+ async function saveEdit(index) {
445
+ const segment = document.querySelector(`#segment-${index}`);
446
+ const textarea = segment.querySelector("textarea");
447
+ const newText = textarea.value;
448
+ const editButton = segment.querySelector(".edit-btn");
449
+
450
+ // Show loading state in save button
451
+ const saveBtn = segment.querySelector(".save-btn");
452
+ saveBtn.disabled = true;
453
+ saveBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
454
+
455
+ try {
456
+ await regenerateSegment(index, newText);
457
+
458
+ // Show the edit button again
459
+ editButton.style.display = "flex";
460
+
461
+ // Update the text display
462
+ const textDiv = segment.querySelector(".segment-text");
463
+ textDiv.innerHTML = `<div class="segment-text-content">${newText}</div>`;
464
+ } catch (error) {
465
+ console.error("Error saving edit:", error);
466
+ alert("Failed to save changes. Please try again.");
467
+
468
+ // Reset save button
469
+ saveBtn.disabled = false;
470
+ saveBtn.innerHTML = '<i class="fas fa-save"></i> Save';
471
+ }
472
+ }
473
+ </script>
474
+ {% endif %}
475
+ </body>
476
+ </html>