Update router with mistral endpoint
Browse files- routers/powerpoint.py +80 -2
routers/powerpoint.py
CHANGED
|
@@ -4,6 +4,7 @@ from pydantic import BaseModel
|
|
| 4 |
from typing import Annotated, List, Literal, Optional, Union, Dict
|
| 5 |
from google import genai
|
| 6 |
from google.genai import types
|
|
|
|
| 7 |
from auth import verify_token
|
| 8 |
import os
|
| 9 |
import tempfile
|
|
@@ -20,6 +21,7 @@ from collections import deque
|
|
| 20 |
from threading import Lock
|
| 21 |
import json
|
| 22 |
from abc import ABC, abstractmethod
|
|
|
|
| 23 |
|
| 24 |
router = APIRouter(prefix="/powerpoint", tags=["powerpoint"])
|
| 25 |
|
|
@@ -27,6 +29,10 @@ router = APIRouter(prefix="/powerpoint", tags=["powerpoint"])
|
|
| 27 |
gemini_key = os.environ.get('GEMINI_KEY', '')
|
| 28 |
client = genai.Client(api_key=gemini_key)
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
class APIResponse(BaseModel):
|
| 31 |
success: bool
|
| 32 |
data: Optional[Union[Dict, List[Dict], str]] = None
|
|
@@ -42,8 +48,10 @@ class MultimodalModel(str, Enum):
|
|
| 42 |
gemini_20_flash = "gemini-2.0-flash"
|
| 43 |
gemini_20_flash_lite = "gemini-2.0-flash-lite"
|
| 44 |
gemini_25_pro = "gemini-2.5-pro"
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
| 47 |
|
| 48 |
class TemplateType(str, Enum):
|
| 49 |
single_graph = "single_graph"
|
|
@@ -554,6 +562,76 @@ async def image_to_pptx(
|
|
| 554 |
except Exception as e:
|
| 555 |
return APIResponse(success=False, error=f"Failed to generate presentation: {str(e)}")
|
| 556 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
@router.get("/templates")
|
| 558 |
async def get_available_templates(token: Annotated[str, Depends(verify_token)]):
|
| 559 |
"""Get list of available presentation templates"""
|
|
|
|
| 4 |
from typing import Annotated, List, Literal, Optional, Union, Dict
|
| 5 |
from google import genai
|
| 6 |
from google.genai import types
|
| 7 |
+
from mistralai import Mistral
|
| 8 |
from auth import verify_token
|
| 9 |
import os
|
| 10 |
import tempfile
|
|
|
|
| 21 |
from threading import Lock
|
| 22 |
import json
|
| 23 |
from abc import ABC, abstractmethod
|
| 24 |
+
import base64
|
| 25 |
|
| 26 |
router = APIRouter(prefix="/powerpoint", tags=["powerpoint"])
|
| 27 |
|
|
|
|
| 29 |
gemini_key = os.environ.get('GEMINI_KEY', '')
|
| 30 |
client = genai.Client(api_key=gemini_key)
|
| 31 |
|
| 32 |
+
# Get Mistral client
|
| 33 |
+
mistral_key = os.environ.get('MISTRAL_KEY', '')
|
| 34 |
+
mistral_client = Mistral(api_key=mistral_key)
|
| 35 |
+
|
| 36 |
class APIResponse(BaseModel):
|
| 37 |
success: bool
|
| 38 |
data: Optional[Union[Dict, List[Dict], str]] = None
|
|
|
|
| 48 |
gemini_20_flash = "gemini-2.0-flash"
|
| 49 |
gemini_20_flash_lite = "gemini-2.0-flash-lite"
|
| 50 |
gemini_25_pro = "gemini-2.5-pro"
|
| 51 |
+
|
| 52 |
+
class MistralModel(str, Enum):
|
| 53 |
+
mistral_small_latest = "mistral-small-latest"
|
| 54 |
+
mistral_medium_latest = "mistral-medium-latest"
|
| 55 |
|
| 56 |
class TemplateType(str, Enum):
|
| 57 |
single_graph = "single_graph"
|
|
|
|
| 562 |
except Exception as e:
|
| 563 |
return APIResponse(success=False, error=f"Failed to generate presentation: {str(e)}")
|
| 564 |
|
| 565 |
+
@router.post("/mistral-image-to-pptx")
|
| 566 |
+
async def mistral_image_to_pptx(
|
| 567 |
+
token: Annotated[str, Depends(verify_token)],
|
| 568 |
+
file: UploadFile = File(..., description="Image file to convert to PowerPoint"),
|
| 569 |
+
template: TemplateType = Form(..., description="Template type to use for the presentation"),
|
| 570 |
+
model: MistralModel = Form(..., description="Mistral model (mistral-small-latest or mistral-medium-latest)"),
|
| 571 |
+
):
|
| 572 |
+
"""
|
| 573 |
+
Analyze an image with Mistral AI and generate a PowerPoint presentation based on the analysis using the selected template
|
| 574 |
+
"""
|
| 575 |
+
try:
|
| 576 |
+
# Get template generator
|
| 577 |
+
template_generator = TEMPLATE_GENERATORS.get(template)
|
| 578 |
+
if not template_generator:
|
| 579 |
+
raise HTTPException(status_code=400, detail=f"Unsupported template: {template}")
|
| 580 |
+
|
| 581 |
+
# Read uploaded file
|
| 582 |
+
file_content = await file.read()
|
| 583 |
+
|
| 584 |
+
# Convert image to base64 for Mistral
|
| 585 |
+
image_base64 = base64.b64encode(file_content).decode('utf-8')
|
| 586 |
+
image_url = f"data:{file.content_type};base64,{image_base64}"
|
| 587 |
+
|
| 588 |
+
# Prepare messages for Mistral
|
| 589 |
+
messages = [
|
| 590 |
+
{
|
| 591 |
+
"role": "user",
|
| 592 |
+
"content": [
|
| 593 |
+
{
|
| 594 |
+
"type": "text",
|
| 595 |
+
"text": template_generator.get_prompt()
|
| 596 |
+
},
|
| 597 |
+
{
|
| 598 |
+
"type": "image_url",
|
| 599 |
+
"image_url": image_url
|
| 600 |
+
}
|
| 601 |
+
]
|
| 602 |
+
}
|
| 603 |
+
]
|
| 604 |
+
|
| 605 |
+
# Send to Mistral
|
| 606 |
+
response = mistral_client.chat.complete(
|
| 607 |
+
model=model.value,
|
| 608 |
+
messages=messages,
|
| 609 |
+
response_format=template_generator.get_response_schema()
|
| 610 |
+
)
|
| 611 |
+
|
| 612 |
+
# Display usage information
|
| 613 |
+
if hasattr(response, 'usage'):
|
| 614 |
+
print(f"Mistral usage: {response.usage}")
|
| 615 |
+
|
| 616 |
+
# Parse the response
|
| 617 |
+
response_text = response.choices[0].message.content
|
| 618 |
+
presentation_data = template_generator.get_response_schema().model_validate(json.loads(response_text))
|
| 619 |
+
|
| 620 |
+
# Generate PPTX file
|
| 621 |
+
pptx_path = template_generator.generate_pptx(presentation_data)
|
| 622 |
+
|
| 623 |
+
# Return file as download
|
| 624 |
+
return FileResponse(
|
| 625 |
+
path=pptx_path,
|
| 626 |
+
filename=f"mistral_presentation_{template.value}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pptx",
|
| 627 |
+
media_type="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
| 628 |
+
)
|
| 629 |
+
|
| 630 |
+
except HTTPException:
|
| 631 |
+
raise
|
| 632 |
+
except Exception as e:
|
| 633 |
+
return APIResponse(success=False, error=f"Failed to generate presentation with Mistral: {str(e)}")
|
| 634 |
+
|
| 635 |
@router.get("/templates")
|
| 636 |
async def get_available_templates(token: Annotated[str, Depends(verify_token)]):
|
| 637 |
"""Get list of available presentation templates"""
|