sachin
commited on
Commit
·
c1d606a
1
Parent(s):
39b1c1e
fix
Browse files- 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(
|
123 |
|
124 |
-
@field_validator("prompt")
|
125 |
-
def
|
126 |
try:
|
127 |
base64.b64decode(v)
|
128 |
except Exception:
|
129 |
-
raise ValueError("
|
130 |
return v
|
131 |
|
132 |
class Config:
|
133 |
schema_extra = {
|
134 |
"example": {
|
135 |
"prompt": "base64_encoded_encrypted_prompt",
|
136 |
-
"src_lang": "
|
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="
|
149 |
-
tgt_lang: str = Field(..., description="
|
150 |
-
|
151 |
-
@field_validator("sentences")
|
152 |
-
def
|
153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
try:
|
155 |
-
base64.b64decode(
|
156 |
except Exception:
|
157 |
-
raise ValueError("
|
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": "
|
165 |
-
"tgt_lang": "
|
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 = "
|
178 |
-
tgt_lang: str = "
|
179 |
|
180 |
-
@field_validator("query")
|
181 |
-
def
|
182 |
try:
|
183 |
base64.b64decode(v)
|
184 |
except Exception:
|
185 |
-
raise ValueError("
|
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
|
352 |
tags=["Chat"],
|
353 |
responses={
|
354 |
200: {"description": "Chat response", "model": ChatResponse},
|
355 |
-
400: {"description": "Invalid prompt or
|
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: {
|
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":
|
390 |
-
"tgt_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(...,
|
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={
|
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(...,
|
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={
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
587 |
|
588 |
-
external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/translate?src_lang={
|
589 |
|
590 |
payload = {
|
591 |
"sentences": decrypted_sentences,
|
592 |
-
"src_lang":
|
593 |
-
"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
|
632 |
tags=["Chat"],
|
633 |
responses={
|
634 |
200: {"description": "Query response", "model": VisualQueryResponse},
|
635 |
-
400: {"description": "Invalid query or
|
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 =
|
646 |
-
tgt_lang: str =
|
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":
|
681 |
-
"tgt_lang":
|
682 |
})
|
683 |
|
684 |
-
external_url = f"https://slabstech-dhwani-internal-api-server.hf.space/v1/visual_query/?src_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
|
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:
|
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":
|
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={
|
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,
|