sachin commited on
Commit
c1d606a
·
1 Parent(s): 39b1c1e
Files changed (1) hide show
  1. src/server/main.py +141 -52
src/server/main.py CHANGED
@@ -119,21 +119,21 @@ class AudioProcessingResponse(BaseModel):
119
 
120
  class ChatRequest(BaseModel):
121
  prompt: str = Field(..., description="Base64-encoded encrypted prompt (max 1000 characters after decryption)")
122
- src_lang: str = Field("kan_Knda", description="Source language code (default: Kannada)")
123
 
124
- @field_validator("prompt")
125
- def prompt_must_be_valid(cls, v):
126
  try:
127
  base64.b64decode(v)
128
  except Exception:
129
- raise ValueError("Prompt must be valid base64-encoded data")
130
  return v
131
 
132
  class Config:
133
  schema_extra = {
134
  "example": {
135
  "prompt": "base64_encoded_encrypted_prompt",
136
- "src_lang": "kan_Knda"
137
  }
138
  }
139
 
@@ -145,24 +145,30 @@ class ChatResponse(BaseModel):
145
 
146
  class TranslationRequest(BaseModel):
147
  sentences: List[str] = Field(..., description="List of base64-encoded encrypted sentences")
148
- src_lang: str = Field(..., description="Source language code")
149
- tgt_lang: str = Field(..., description="Target language code")
150
-
151
- @field_validator("sentences")
152
- def sentences_must_be_valid(cls, v):
153
- for sentence in v:
 
 
 
 
 
 
154
  try:
155
- base64.b64decode(sentence)
156
  except Exception:
157
- raise ValueError("Each sentence must be valid base64-encoded data")
158
  return v
159
 
160
  class Config:
161
  schema_extra = {
162
  "example": {
163
  "sentences": ["base64_encoded_encrypted_hello", "base64_encoded_encrypted_how_are_you"],
164
- "src_lang": "en",
165
- "tgt_lang": "kan_Knda"
166
  }
167
  }
168
 
@@ -173,16 +179,16 @@ class TranslationResponse(BaseModel):
173
  schema_extra = {"example": {"translations": ["ನಮಸ್ಕಾರ", "ನೀವು ಹೇಗಿದ್ದೀರಿ?"]}}
174
 
175
  class VisualQueryRequest(BaseModel):
176
- query: str
177
- src_lang: str = "kan_Knda"
178
- tgt_lang: str = "kan_Knda"
179
 
180
- @field_validator("query")
181
- def query_must_be_valid(cls, v):
182
  try:
183
  base64.b64decode(v)
184
  except Exception:
185
- raise ValueError("Query must be valid base64-encoded data")
186
  return v
187
 
188
  class VisualQueryResponse(BaseModel):
@@ -276,7 +282,7 @@ async def register_user(
276
 
277
  @app.post("/v1/app/register",
278
  response_model=TokenResponse,
279
- summary="Register New App User",
280
  description="Create a new user account for the mobile app in the `app_users` table using an encrypted email and device token. Returns an access token and refresh token. Rate limited to 5 requests per minute per IP. Requires X-Session-Key header.",
281
  tags=["Authentication"],
282
  responses={
@@ -348,11 +354,11 @@ async def generate_audio(
348
  @app.post("/v1/chat",
349
  response_model=ChatResponse,
350
  summary="Chat with AI",
351
- description="Generate a chat response from an encrypted prompt in the specified language. Rate limited to 100 requests per minute per user. Requires authentication and X-Session-Key header.",
352
  tags=["Chat"],
353
  responses={
354
  200: {"description": "Chat response", "model": ChatResponse},
355
- 400: {"description": "Invalid prompt or encrypted data"},
356
  401: {"description": "Unauthorized - Token required"},
357
  429: {"description": "Rate limit exceeded"},
358
  504: {"description": "Chat service timeout"}
@@ -375,19 +381,27 @@ async def chat(
375
  logger.error(f"Prompt decryption failed: {str(e)}")
376
  raise HTTPException(status_code=400, detail="Invalid encrypted prompt")
377
 
 
 
 
 
 
 
 
 
378
  if not decrypted_prompt:
379
  raise HTTPException(status_code=400, detail="Prompt cannot be empty")
380
  if len(decrypted_prompt) > 1000:
381
  raise HTTPException(status_code=400, detail="Decrypted prompt cannot exceed 1000 characters")
382
 
383
- logger.info(f"Received prompt: {decrypted_prompt}, src_lang: {chat_request.src_lang}, user_id: {user_id}")
384
 
385
  try:
386
  external_url = "https://slabstech-dhwani-internal-api-server.hf.space/v1/chat"
387
  payload = {
388
  "prompt": decrypted_prompt,
389
- "src_lang": chat_request.src_lang,
390
- "tgt_lang": chat_request.src_lang
391
  }
392
 
393
  response = requests.post(
@@ -431,13 +445,30 @@ async def chat(
431
  async def process_audio(
432
  request: Request,
433
  file: UploadFile = File(..., description="Audio file to process"),
434
- language: str = Query(..., enum=["kannada", "hindi", "tamil"], description="Language of the audio"),
435
- credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)
 
436
  ):
437
  user_id = await get_current_user(credentials)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  logger.info("Processing audio processing request", extra={
439
  "endpoint": "/v1/process_audio",
440
  "filename": file.filename,
 
441
  "client_ip": get_remote_address(request),
442
  "user_id": user_id
443
  })
@@ -447,7 +478,7 @@ async def process_audio(
447
  file_content = await file.read()
448
  files = {"file": (file.filename, file_content, file.content_type)}
449
 
450
- external_url = f"{settings.external_audio_proc_url}/process_audio/?language={language}"
451
  response = requests.post(
452
  external_url,
453
  files=files,
@@ -469,30 +500,43 @@ async def process_audio(
469
  @app.post("/v1/transcribe/",
470
  response_model=TranscriptionResponse,
471
  summary="Transcribe Audio File",
472
- description="Transcribe an encrypted audio file into text in the specified language. Requires authentication and X-Session-Key header.",
473
  tags=["Audio"],
474
  responses={
475
  200: {"description": "Transcription result", "model": TranscriptionResponse},
476
- 400: {"description": "Invalid encrypted audio"},
477
  401: {"description": "Unauthorized - Token required"},
478
  504: {"description": "Transcription service timeout"}
479
  })
480
  async def transcribe_audio(
481
  file: UploadFile = File(..., description="Encrypted audio file to transcribe"),
482
- language: str = Query(..., enum=["kannada", "hindi", "tamil"], description="Language of the audio"),
483
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
484
  x_session_key: str = Header(..., alias="X-Session-Key")
485
  ):
486
  user_id = await get_current_user(credentials)
487
  session_key = base64.b64decode(x_session_key)
488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  start_time = time()
490
  try:
491
  encrypted_content = await file.read()
492
  file_content = decrypt_data(encrypted_content, session_key)
493
  files = {"file": (file.filename, file_content, file.content_type)}
494
 
495
- external_url = f"{settings.external_asr_url}/transcribe/?language={language}"
496
  response = requests.post(
497
  external_url,
498
  files=files,
@@ -555,11 +599,11 @@ async def chat_v2(
555
  @app.post("/v1/translate",
556
  response_model=TranslationResponse,
557
  summary="Translate Text",
558
- description="Translate a list of base64-encoded encrypted sentences from source to target language. Requires authentication and X-Session-Key header.",
559
  tags=["Translation"],
560
  responses={
561
  200: {"description": "Translation result", "model": TranslationResponse},
562
- 400: {"description": "Invalid encrypted sentences"},
563
  401: {"description": "Unauthorized - Token required"},
564
  500: {"description": "Translation service error"},
565
  504: {"description": "Translation service timeout"}
@@ -583,14 +627,30 @@ async def translate(
583
  logger.error(f"Sentence decryption failed: {str(e)}")
584
  raise HTTPException(status_code=400, detail="Invalid encrypted sentence")
585
 
586
- logger.info(f"Received translation request: {decrypted_sentences}, src_lang: {request.src_lang}, tgt_lang: {request.tgt_lang}, user_id: {user_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
 
588
- external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/translate?src_lang={request.src_lang}&tgt_lang={request.tgt_lang}"
589
 
590
  payload = {
591
  "sentences": decrypted_sentences,
592
- "src_lang": request.src_lang,
593
- "tgt_lang": request.tgt_lang
594
  }
595
 
596
  try:
@@ -628,11 +688,11 @@ async def translate(
628
  @app.post("/v1/visual_query",
629
  response_model=VisualQueryResponse,
630
  summary="Visual Query with Image",
631
- description="Process a visual query with an encrypted text query and encrypted image. Rate limited to 100 requests per minute per user. Requires authentication and X-Session-Key header.",
632
  tags=["Chat"],
633
  responses={
634
  200: {"description": "Query response", "model": VisualQueryResponse},
635
- 400: {"description": "Invalid query or encrypted data"},
636
  401: {"description": "Unauthorized - Token required"},
637
  429: {"description": "Rate limit exceeded"},
638
  504: {"description": "Visual query service timeout"}
@@ -642,8 +702,8 @@ async def visual_query(
642
  request: Request,
643
  query: str = Form(..., description="Base64-encoded encrypted text query"),
644
  file: UploadFile = File(..., description="Encrypted image file to analyze"),
645
- src_lang: str = Query(default="kan_Knda", description="Source language code"),
646
- tgt_lang: str = Query(default="kan_Knda", description="Target language code"),
647
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
648
  x_session_key: str = Header(..., alias="X-Session-Key")
649
  ):
@@ -658,6 +718,22 @@ async def visual_query(
658
  logger.error(f"Query decryption failed: {str(e)}")
659
  raise HTTPException(status_code=400, detail="Invalid encrypted query")
660
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  if not decrypted_query.strip():
662
  raise HTTPException(status_code=400, detail="Query cannot be empty")
663
  if len(decrypted_query) > 1000:
@@ -677,11 +753,11 @@ async def visual_query(
677
  "file_name": file.filename,
678
  "client_ip": get_remote_address(request),
679
  "user_id": user_id,
680
- "src_lang": src_lang,
681
- "tgt_lang": tgt_lang
682
  })
683
 
684
- external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/v1/visual_query/?src_lang={src_lang}&tgt_lang={tgt_lang}"
685
 
686
  try:
687
  files = {"file": (file.filename, decrypted_content, file.content_type)}
@@ -725,11 +801,11 @@ class SupportedLanguage(str, Enum):
725
 
726
  @app.post("/v1/speech_to_speech",
727
  summary="Speech-to-Speech Conversion",
728
- description="Convert input encrypted speech to processed speech by calling an external speech-to-speech API. Rate limited to 5 requests per minute per user. Requires authentication and X-Session-Key header.",
729
  tags=["Audio"],
730
  responses={
731
  200: {"description": "Audio stream", "content": {"audio/mp3": {"example": "Binary audio data"}}},
732
- 400: {"description": "Invalid input or encrypted audio"},
733
  401: {"description": "Unauthorized - Token required"},
734
  429: {"description": "Rate limit exceeded"},
735
  504: {"description": "External API timeout"},
@@ -739,17 +815,30 @@ class SupportedLanguage(str, Enum):
739
  async def speech_to_speech(
740
  request: Request,
741
  file: UploadFile = File(..., description="Encrypted audio file to process"),
742
- language: SupportedLanguage = Query(..., description="Language of the audio (kannada, hindi, tamil)"),
743
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
744
  x_session_key: str = Header(..., alias="X-Session-Key")
745
  ) -> StreamingResponse:
746
  user_id = await get_current_user(credentials)
747
  session_key = base64.b64decode(x_session_key)
748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  logger.info("Processing speech-to-speech request", extra={
750
  "endpoint": "/v1/speech_to_speech",
751
  "audio_filename": file.filename,
752
- "language": language,
753
  "client_ip": get_remote_address(request),
754
  "user_id": user_id
755
  })
@@ -758,7 +847,7 @@ async def speech_to_speech(
758
  encrypted_content = await file.read()
759
  file_content = decrypt_data(encrypted_content, session_key)
760
  files = {"file": (file.filename, file_content, file.content_type)}
761
- external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/v1/speech_to_speech?language={language}"
762
 
763
  response = requests.post(
764
  external_url,
 
119
 
120
  class ChatRequest(BaseModel):
121
  prompt: str = Field(..., description="Base64-encoded encrypted prompt (max 1000 characters after decryption)")
122
+ src_lang: str = Field(..., description="Base64-encoded encrypted source language code (default: kan_Knda after decryption)")
123
 
124
+ @field_validator("prompt", "src_lang")
125
+ def must_be_valid_base64(cls, v):
126
  try:
127
  base64.b64decode(v)
128
  except Exception:
129
+ raise ValueError("Field must be valid base64-encoded data")
130
  return v
131
 
132
  class Config:
133
  schema_extra = {
134
  "example": {
135
  "prompt": "base64_encoded_encrypted_prompt",
136
+ "src_lang": "base64_encoded_encrypted_kan_Knda"
137
  }
138
  }
139
 
 
145
 
146
  class TranslationRequest(BaseModel):
147
  sentences: List[str] = Field(..., description="List of base64-encoded encrypted sentences")
148
+ src_lang: str = Field(..., description="Base64-encoded encrypted source language code")
149
+ tgt_lang: str = Field(..., description="Base64-encoded encrypted target language code")
150
+
151
+ @field_validator("sentences", "src_lang", "tgt_lang")
152
+ def must_be_valid_base64(cls, v):
153
+ if isinstance(v, list):
154
+ for item in v:
155
+ try:
156
+ base64.b64decode(item)
157
+ except Exception:
158
+ raise ValueError("Each sentence must be valid base64-encoded data")
159
+ else:
160
  try:
161
+ base64.b64decode(v)
162
  except Exception:
163
+ raise ValueError("Field must be valid base64-encoded data")
164
  return v
165
 
166
  class Config:
167
  schema_extra = {
168
  "example": {
169
  "sentences": ["base64_encoded_encrypted_hello", "base64_encoded_encrypted_how_are_you"],
170
+ "src_lang": "base64_encoded_encrypted_en",
171
+ "tgt_lang": "base64_encoded_encrypted_kan_Knda"
172
  }
173
  }
174
 
 
179
  schema_extra = {"example": {"translations": ["ನಮಸ್ಕಾರ", "ನೀವು ಹೇಗಿದ್ದೀರಿ?"]}}
180
 
181
  class VisualQueryRequest(BaseModel):
182
+ query: str = Field(..., description="Base64-encoded encrypted text query")
183
+ src_lang: str = Field(..., description="Base64-encoded encrypted source language code")
184
+ tgt_lang: str = Field(..., description="Base64-encoded encrypted target language code")
185
 
186
+ @field_validator("query", "src_lang", "tgt_lang")
187
+ def must_be_valid_base64(cls, v):
188
  try:
189
  base64.b64decode(v)
190
  except Exception:
191
+ raise ValueError("Field must be valid base64-encoded data")
192
  return v
193
 
194
  class VisualQueryResponse(BaseModel):
 
282
 
283
  @app.post("/v1/app/register",
284
  response_model=TokenResponse,
285
+ summary="Register New App User ",
286
  description="Create a new user account for the mobile app in the `app_users` table using an encrypted email and device token. Returns an access token and refresh token. Rate limited to 5 requests per minute per IP. Requires X-Session-Key header.",
287
  tags=["Authentication"],
288
  responses={
 
354
  @app.post("/v1/chat",
355
  response_model=ChatResponse,
356
  summary="Chat with AI",
357
+ description="Generate a chat response from an encrypted prompt and encrypted language code. Rate limited to 100 requests per minute per user. Requires authentication and X-Session-Key header.",
358
  tags=["Chat"],
359
  responses={
360
  200: {"description": "Chat response", "model": ChatResponse},
361
+ 400: {"description": "Invalid prompt, encrypted data, or language code"},
362
  401: {"description": "Unauthorized - Token required"},
363
  429: {"description": "Rate limit exceeded"},
364
  504: {"description": "Chat service timeout"}
 
381
  logger.error(f"Prompt decryption failed: {str(e)}")
382
  raise HTTPException(status_code=400, detail="Invalid encrypted prompt")
383
 
384
+ # Decrypt the source language
385
+ try:
386
+ encrypted_src_lang = base64.b64decode(chat_request.src_lang)
387
+ decrypted_src_lang = decrypt_data(encrypted_src_lang, session_key).decode("utf-8")
388
+ except Exception as e:
389
+ logger.error(f"Source language decryption failed: {str(e)}")
390
+ raise HTTPException(status_code=400, detail="Invalid encrypted source language")
391
+
392
  if not decrypted_prompt:
393
  raise HTTPException(status_code=400, detail="Prompt cannot be empty")
394
  if len(decrypted_prompt) > 1000:
395
  raise HTTPException(status_code=400, detail="Decrypted prompt cannot exceed 1000 characters")
396
 
397
+ logger.info(f"Received prompt: {decrypted_prompt}, src_lang: {decrypted_src_lang}, user_id: {user_id}")
398
 
399
  try:
400
  external_url = "https://slabstech-dhwani-internal-api-server.hf.space/v1/chat"
401
  payload = {
402
  "prompt": decrypted_prompt,
403
+ "src_lang": decrypted_src_lang,
404
+ "tgt_lang": decrypted_src_lang
405
  }
406
 
407
  response = requests.post(
 
445
  async def process_audio(
446
  request: Request,
447
  file: UploadFile = File(..., description="Audio file to process"),
448
+ language: str = Query(..., description="Base64-encoded encrypted language of the audio (kannada, hindi, tamil after decryption)"),
449
+ credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
450
+ x_session_key: str = Header(..., alias="X-Session-Key")
451
  ):
452
  user_id = await get_current_user(credentials)
453
+ session_key = base64.b64decode(x_session_key)
454
+
455
+ # Decrypt the language
456
+ try:
457
+ encrypted_language = base64.b64decode(language)
458
+ decrypted_language = decrypt_data(encrypted_language, session_key).decode("utf-8")
459
+ except Exception as e:
460
+ logger.error(f"Language decryption failed: {str(e)}")
461
+ raise HTTPException(status_code=400, detail="Invalid encrypted language")
462
+
463
+ # Validate language
464
+ allowed_languages = ["kannada", "hindi", "tamil"]
465
+ if decrypted_language not in allowed_languages:
466
+ raise HTTPException(status_code=400, detail=f"Language must be one of {allowed_languages}")
467
+
468
  logger.info("Processing audio processing request", extra={
469
  "endpoint": "/v1/process_audio",
470
  "filename": file.filename,
471
+ "language": decrypted_language,
472
  "client_ip": get_remote_address(request),
473
  "user_id": user_id
474
  })
 
478
  file_content = await file.read()
479
  files = {"file": (file.filename, file_content, file.content_type)}
480
 
481
+ external_url = f"{settings.external_audio_proc_url}/process_audio/?language={decrypted_language}"
482
  response = requests.post(
483
  external_url,
484
  files=files,
 
500
  @app.post("/v1/transcribe/",
501
  response_model=TranscriptionResponse,
502
  summary="Transcribe Audio File",
503
+ description="Transcribe an encrypted audio file into text in the specified encrypted language. Requires authentication and X-Session-Key header.",
504
  tags=["Audio"],
505
  responses={
506
  200: {"description": "Transcription result", "model": TranscriptionResponse},
507
+ 400: {"description": "Invalid encrypted audio or language"},
508
  401: {"description": "Unauthorized - Token required"},
509
  504: {"description": "Transcription service timeout"}
510
  })
511
  async def transcribe_audio(
512
  file: UploadFile = File(..., description="Encrypted audio file to transcribe"),
513
+ language: str = Query(..., description="Base64-encoded encrypted language of the audio (kannada, hindi, tamil after decryption)"),
514
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
515
  x_session_key: str = Header(..., alias="X-Session-Key")
516
  ):
517
  user_id = await get_current_user(credentials)
518
  session_key = base64.b64decode(x_session_key)
519
 
520
+ # Decrypt the language
521
+ try:
522
+ encrypted_language = base64.b64decode(language)
523
+ decrypted_language = decrypt_data(encrypted_language, session_key).decode("utf-8")
524
+ except Exception as e:
525
+ logger.error(f"Language decryption failed: {str(e)}")
526
+ raise HTTPException(status_code=400, detail="Invalid encrypted language")
527
+
528
+ # Validate language
529
+ allowed_languages = ["kannada", "hindi", "tamil"]
530
+ if decrypted_language not in allowed_languages:
531
+ raise HTTPException(status_code=400, detail=f"Language must be one of {allowed_languages}")
532
+
533
  start_time = time()
534
  try:
535
  encrypted_content = await file.read()
536
  file_content = decrypt_data(encrypted_content, session_key)
537
  files = {"file": (file.filename, file_content, file.content_type)}
538
 
539
+ external_url = f"{settings.external_asr_url}/transcribe/?language={decrypted_language}"
540
  response = requests.post(
541
  external_url,
542
  files=files,
 
599
  @app.post("/v1/translate",
600
  response_model=TranslationResponse,
601
  summary="Translate Text",
602
+ description="Translate a list of base64-encoded encrypted sentences from an encrypted source to an encrypted target language. Requires authentication and X-Session-Key header.",
603
  tags=["Translation"],
604
  responses={
605
  200: {"description": "Translation result", "model": TranslationResponse},
606
+ 400: {"description": "Invalid encrypted sentences or languages"},
607
  401: {"description": "Unauthorized - Token required"},
608
  500: {"description": "Translation service error"},
609
  504: {"description": "Translation service timeout"}
 
627
  logger.error(f"Sentence decryption failed: {str(e)}")
628
  raise HTTPException(status_code=400, detail="Invalid encrypted sentence")
629
 
630
+ # Decrypt source language
631
+ try:
632
+ encrypted_src_lang = base64.b64decode(request.src_lang)
633
+ decrypted_src_lang = decrypt_data(encrypted_src_lang, session_key).decode("utf-8")
634
+ except Exception as e:
635
+ logger.error(f"Source language decryption failed: {str(e)}")
636
+ raise HTTPException(status_code=400, detail="Invalid encrypted source language")
637
+
638
+ # Decrypt target language
639
+ try:
640
+ encrypted_tgt_lang = base64.b64decode(request.tgt_lang)
641
+ decrypted_tgt_lang = decrypt_data(encrypted_tgt_lang, session_key).decode("utf-8")
642
+ except Exception as e:
643
+ logger.error(f"Target language decryption failed: {str(e)}")
644
+ raise HTTPException(status_code=400, detail="Invalid encrypted target language")
645
+
646
+ logger.info(f"Received translation request: {decrypted_sentences}, src_lang: {decrypted_src_lang}, tgt_lang: {decrypted_tgt_lang}, user_id: {user_id}")
647
 
648
+ external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/translate?src_lang={decrypted_src_lang}&tgt_lang={decrypted_tgt_lang}"
649
 
650
  payload = {
651
  "sentences": decrypted_sentences,
652
+ "src_lang": decrypted_src_lang,
653
+ "tgt_lang": decrypted_tgt_lang
654
  }
655
 
656
  try:
 
688
  @app.post("/v1/visual_query",
689
  response_model=VisualQueryResponse,
690
  summary="Visual Query with Image",
691
+ description="Process a visual query with an encrypted text query, encrypted image, and encrypted language codes. Rate limited to 100 requests per minute per user. Requires authentication and X-Session-Key header.",
692
  tags=["Chat"],
693
  responses={
694
  200: {"description": "Query response", "model": VisualQueryResponse},
695
+ 400: {"description": "Invalid query, encrypted data, or language codes"},
696
  401: {"description": "Unauthorized - Token required"},
697
  429: {"description": "Rate limit exceeded"},
698
  504: {"description": "Visual query service timeout"}
 
702
  request: Request,
703
  query: str = Form(..., description="Base64-encoded encrypted text query"),
704
  file: UploadFile = File(..., description="Encrypted image file to analyze"),
705
+ src_lang: str = Form(..., description="Base64-encoded encrypted source language code"),
706
+ tgt_lang: str = Form(..., description="Base64-encoded encrypted target language code"),
707
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
708
  x_session_key: str = Header(..., alias="X-Session-Key")
709
  ):
 
718
  logger.error(f"Query decryption failed: {str(e)}")
719
  raise HTTPException(status_code=400, detail="Invalid encrypted query")
720
 
721
+ # Decrypt source language
722
+ try:
723
+ encrypted_src_lang = base64.b64decode(src_lang)
724
+ decrypted_src_lang = decrypt_data(encrypted_src_lang, session_key).decode("utf-8")
725
+ except Exception as e:
726
+ logger.error(f"Source language decryption failed: {str(e)}")
727
+ raise HTTPException(status_code=400, detail="Invalid encrypted source language")
728
+
729
+ # Decrypt target language
730
+ try:
731
+ encrypted_tgt_lang = base64.b64decode(tgt_lang)
732
+ decrypted_tgt_lang = decrypt_data(encrypted_tgt_lang, session_key).decode("utf-8")
733
+ except Exception as e:
734
+ logger.error(f"Target language decryption failed: {str(e)}")
735
+ raise HTTPException(status_code=400, detail="Invalid encrypted target language")
736
+
737
  if not decrypted_query.strip():
738
  raise HTTPException(status_code=400, detail="Query cannot be empty")
739
  if len(decrypted_query) > 1000:
 
753
  "file_name": file.filename,
754
  "client_ip": get_remote_address(request),
755
  "user_id": user_id,
756
+ "src_lang": decrypted_src_lang,
757
+ "tgt_lang": decrypted_tgt_lang
758
  })
759
 
760
+ external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/v1/visual_query/?src_lang={decrypted_src_lang}&tgt_lang={decrypted_tgt_lang}"
761
 
762
  try:
763
  files = {"file": (file.filename, decrypted_content, file.content_type)}
 
801
 
802
  @app.post("/v1/speech_to_speech",
803
  summary="Speech-to-Speech Conversion",
804
+ description="Convert input encrypted speech to processed speech in the specified encrypted language by calling an external speech-to-speech API. Rate limited to 5 requests per minute per user. Requires authentication and X-Session-Key header.",
805
  tags=["Audio"],
806
  responses={
807
  200: {"description": "Audio stream", "content": {"audio/mp3": {"example": "Binary audio data"}}},
808
+ 400: {"description": "Invalid input, encrypted audio, or language"},
809
  401: {"description": "Unauthorized - Token required"},
810
  429: {"description": "Rate limit exceeded"},
811
  504: {"description": "External API timeout"},
 
815
  async def speech_to_speech(
816
  request: Request,
817
  file: UploadFile = File(..., description="Encrypted audio file to process"),
818
+ language: str = Query(..., description="Base64-encoded encrypted language of the audio (kannada, hindi, tamil after decryption)"),
819
  credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
820
  x_session_key: str = Header(..., alias="X-Session-Key")
821
  ) -> StreamingResponse:
822
  user_id = await get_current_user(credentials)
823
  session_key = base64.b64decode(x_session_key)
824
 
825
+ # Decrypt the language
826
+ try:
827
+ encrypted_language = base64.b64decode(language)
828
+ decrypted_language = decrypt_data(encrypted_language, session_key).decode("utf-8")
829
+ except Exception as e:
830
+ logger.error(f"Language decryption failed: {str(e)}")
831
+ raise HTTPException(status_code=400, detail="Invalid encrypted language")
832
+
833
+ # Validate language
834
+ allowed_languages = [lang.value for lang in SupportedLanguage]
835
+ if decrypted_language not in allowed_languages:
836
+ raise HTTPException(status_code=400, detail=f"Language must be one of {allowed_languages}")
837
+
838
  logger.info("Processing speech-to-speech request", extra={
839
  "endpoint": "/v1/speech_to_speech",
840
  "audio_filename": file.filename,
841
+ "language": decrypted_language,
842
  "client_ip": get_remote_address(request),
843
  "user_id": user_id
844
  })
 
847
  encrypted_content = await file.read()
848
  file_content = decrypt_data(encrypted_content, session_key)
849
  files = {"file": (file.filename, file_content, file.content_type)}
850
+ external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/v1/speech_to_speech?language={decrypted_language}"
851
 
852
  response = requests.post(
853
  external_url,