diff --git a/app/models/dtos.py b/app/models/dtos.py index d52d0487..b1ffc8a2 100644 --- a/app/models/dtos.py +++ b/app/models/dtos.py @@ -13,6 +13,12 @@ class ContentType(str, Enum): TEXT = "text" +# V1 API only +class Content(BaseModel): + text_content: str = Field(..., alias="textContent") + type: ContentType + + class SendMessageRequest(BaseModel): class Template(BaseModel): id: int @@ -23,7 +29,19 @@ class Template(BaseModel): parameters: dict +# V1 API only class SendMessageResponse(BaseModel): + class Message(BaseModel): + sent_at: datetime = Field( + alias="sentAt", default_factory=datetime.utcnow + ) + content: list[Content] + + used_model: str = Field(..., alias="usedModel") + message: Message + + +class SendMessageResponseV2(BaseModel): used_model: str = Field(..., alias="usedModel") sent_at: datetime = Field(alias="sentAt", default_factory=datetime.utcnow) content: dict diff --git a/app/routes/messages.py b/app/routes/messages.py index 4dc873d1..383091a3 100644 --- a/app/routes/messages.py +++ b/app/routes/messages.py @@ -11,7 +11,13 @@ InvalidModelException, ) from app.dependencies import TokenPermissionsValidator -from app.models.dtos import SendMessageRequest, SendMessageResponse +from app.models.dtos import ( + SendMessageRequest, + SendMessageResponse, + Content, + ContentType, + SendMessageResponseV2, +) from app.services.circuit_breaker import CircuitBreaker from app.services.guidance_wrapper import GuidanceWrapper from app.config import settings @@ -19,10 +25,7 @@ router = APIRouter(tags=["messages"]) -@router.post( - "/api/v1/messages", dependencies=[Depends(TokenPermissionsValidator())] -) -def send_message(body: SendMessageRequest) -> SendMessageResponse: +def execute_call(body: SendMessageRequest) -> dict: try: model = settings.pyris.llms[body.preferred_model] except ValueError as e: @@ -35,7 +38,7 @@ def send_message(body: SendMessageRequest) -> SendMessageResponse: ) try: - content = CircuitBreaker.protected_call( + return CircuitBreaker.protected_call( func=guidance.query, cache_key=body.preferred_model, accepted_exceptions=( @@ -52,8 +55,41 @@ def send_message(body: SendMessageRequest) -> SendMessageResponse: except Exception as e: raise InternalServerException(str(e)) + +@router.post( + "/api/v1/messages", dependencies=[Depends(TokenPermissionsValidator())] +) +def send_message(body: SendMessageRequest) -> SendMessageResponse: + generated_vars = execute_call(body) + + # Restore the old behavior of only returning the 'response' variable for the v1 API + if "response" not in generated_vars: + raise InternalServerException( + str(ValueError("The handlebars do not generate 'response'")) + ) + return SendMessageResponse( + usedModel=body.preferred_model, + message=SendMessageResponse.Message( + sentAt=datetime.now(timezone.utc), + content=[ + Content( + type=ContentType.TEXT, + textContent=generated_vars["response"], + ) + ], + ), + ) + + +@router.post( + "/api/v2/messages", dependencies=[Depends(TokenPermissionsValidator())] +) +def send_message_v2(body: SendMessageRequest) -> SendMessageResponseV2: + generated_vars = execute_call(body) + + return SendMessageResponseV2( usedModel=body.preferred_model, sentAt=datetime.now(timezone.utc), - content=content, + content=generated_vars, )