From 7e4617f7e0e8c4ad4a65027fd686c67d1ecb7d2d Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Thu, 13 Mar 2025 11:05:56 +0530 Subject: [PATCH 01/14] getting threads up and running --- backend/app/api/main.py | 3 +- backend/app/api/routes/threads.py | 172 ++++++++++++++++++++++++++++++ backend/app/core/config.py | 1 + backend/app/core/logger.py | 20 ++++ backend/app/models/__init__.py | 3 +- backend/app/models/thread.py | 27 +++++ 6 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 backend/app/api/routes/threads.py create mode 100644 backend/app/core/logger.py create mode 100644 backend/app/models/thread.py diff --git a/backend/app/api/main.py b/backend/app/api/main.py index eac18c8e8..5a303b516 100644 --- a/backend/app/api/main.py +++ b/backend/app/api/main.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from app.api.routes import items, login, private, users, utils +from app.api.routes import items, login, private, users, utils, threads from app.core.config import settings api_router = APIRouter() @@ -8,6 +8,7 @@ api_router.include_router(users.router) api_router.include_router(utils.router) api_router.include_router(items.router) +api_router.include_router(threads.router) if settings.ENVIRONMENT == "local": diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py new file mode 100644 index 000000000..7e6fb27fd --- /dev/null +++ b/backend/app/api/routes/threads.py @@ -0,0 +1,172 @@ +import requests +import openai +from openai import OpenAI +from fastapi import APIRouter, BackgroundTasks +from app.models import ( MessageRequest, AckPayload, CallbackPayload) +from ...core.config import settings +from ...core.logger import logging + +logger = logging.getLogger(__name__) +router = APIRouter(prefix="/threads", tags=["threads"]) + + +def send_callback(callback_url: str, data: dict): + """Send results to the callback URL (synchronously).""" + try: + print("completed") + session = requests.Session() + session.verify = False + print(data) + response = session.post(callback_url, json=data) + response.raise_for_status() + return True + except requests.RequestException as e: + logger.error(f"Callback failed: {str(e)}") + return False + + +def build_callback_payload(request: MessageRequest, status: str, message: str) -> CallbackPayload: + """ + Helper function to build the CallbackPayload from a MessageRequest. + """ + data = { + "status": status, + "message": message, + "thread_id": request.thread_id, + "endpoint": getattr(request, "endpoint", "some-default-endpoint"), + } + + # Update with any additional fields from request that we haven't excluded + data.update( + request.model_dump(exclude={"question", "assistant_id", "callback_url", "thread_id"}) + ) + + return CallbackPayload(**data) + + +def process_run(request: MessageRequest, client: OpenAI): + """ + Background task to run create_and_poll, then send the callback with the result. + This function is run in the background after we have already returned an initial response. + """ + try: + print("Thread run started") + # Start the run + run = client.beta.threads.runs.create_and_poll( + thread_id=request.thread_id, + assistant_id=request.assistant_id, + ) + + if run.status == "completed": + print("Thread run completed") + messages = client.beta.threads.messages.list(thread_id=request.thread_id) + latest_message = messages.data[0] + message_content = latest_message.content[0].text.value + + callback_response = build_callback_payload( + request=request, status="success", message=message_content + ) + else: + callback_response = build_callback_payload( + request=request, status="error", message=f"Run failed with status: {run.status}" + ) + + # Send callback with results + send_callback(request.callback_url, callback_response.model_dump()) + + except openai.OpenAIError as e: + # Handle any other OpenAI API errors + if isinstance(e.body, dict) and "message" in e.body: + error_message = e.body["message"] + else: + error_message = str(e) + + callback_response = build_callback_payload( + request=request, status="error", message=error_message + ) + + send_callback(request.callback_url, callback_response.model_dump()) + + +def validate_assistant_id(assistant_id: str, client: OpenAI): + try: + client.beta.assistants.retrieve(assistant_id=assistant_id) + except openai.NotFoundError: + return AckPayload( + status="error", + message=f"Invalid assistant ID provided {assistant_id}", + success=False, + ) + return None + + +@router.post("/") +async def threads(request: MessageRequest, background_tasks: BackgroundTasks): + """ + Accepts a question, assistant_id, callback_url, and optional thread_id from the request body. + Returns an immediate "processing" response, then continues to run create_and_poll in background. + Once completed, calls send_callback with the final result. + """ + client = OpenAI(api_key=settings.OPENAI_API_KEY) + + assistant_error = validate_assistant_id(request.assistant_id, client) + if assistant_error: + return assistant_error + + # 1. Validate or check if there's an existing thread with an in-progress run + if request.thread_id: + try: + runs = client.beta.threads.runs.list(thread_id=request.thread_id) + # Get the most recent run (first in the list) if any + if runs.data and len(runs.data) > 0: + latest_run = runs.data[0] + if latest_run.status in ["queued", "in_progress", "requires_action"]: + return { + "status": "error", + "message": f"There is an active run on this thread (status: {latest_run.status}). Please wait for it to complete.", + } + except openai.NotFoundError: + # Handle invalid thread ID + return AckPayload( + status="error", + message=f"Invalid thread ID provided {request.thread_id}", + success=False, + ) + + # Use existing thread + client.beta.threads.messages.create( + thread_id=request.thread_id, role="user", content=request.question + ) + else: + try: + # Create new thread + thread = client.beta.threads.create() + client.beta.threads.messages.create( + thread_id=thread.id, role="user", content=request.question + ) + request.thread_id = thread.id + except openai.OpenAIError as e: + # Handle any other OpenAI API errors + if isinstance(e.body, dict) and "message" in e.body: + error_message = e.body["message"] + else: + error_message = str(e) + return AckPayload( + status="error", + message=error_message, + success=False, + ) + + # 2. Send immediate response to complete the API call + initial_response = AckPayload( + status="processing", + message="Run started", + thread_id=request.thread_id, + success=True, + ) + + # 3. Schedule the background task to run create_and_poll and send callback + background_tasks.add_task(process_run, request, client) + + # 4. Return immediately so the client knows we've accepted the request + return initial_response \ No newline at end of file diff --git a/backend/app/core/config.py b/backend/app/core/config.py index d58e03c87..1196a98cf 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -31,6 +31,7 @@ class Settings(BaseSettings): env_ignore_empty=True, extra="ignore", ) + OPENAI_API_KEY: str API_V1_STR: str = "/api/v1" SECRET_KEY: str = secrets.token_urlsafe(32) # 60 minutes * 24 hours * 8 days = 8 days diff --git a/backend/app/core/logger.py b/backend/app/core/logger.py new file mode 100644 index 000000000..c2aba337f --- /dev/null +++ b/backend/app/core/logger.py @@ -0,0 +1,20 @@ +import logging +import os +from logging.handlers import RotatingFileHandler + +LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "logs") +if not os.path.exists(LOG_DIR): + os.makedirs(LOG_DIR) + +LOG_FILE_PATH = os.path.join(LOG_DIR, "app.log") + +LOGGING_LEVEL = logging.INFO +LOGGING_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + +logging.basicConfig(level=LOGGING_LEVEL, format=LOGGING_FORMAT) + +file_handler = RotatingFileHandler(LOG_FILE_PATH, maxBytes=10485760, backupCount=5) +file_handler.setLevel(LOGGING_LEVEL) +file_handler.setFormatter(logging.Formatter(LOGGING_FORMAT)) + +logging.getLogger("").addHandler(file_handler) \ No newline at end of file diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 5a03fa56c..cc10ae3c5 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -11,4 +11,5 @@ UserUpdateMe, NewPassword, UpdatePassword, -) \ No newline at end of file +) +from .thread import MessageRequest, AckPayload, CallbackPayload \ No newline at end of file diff --git a/backend/app/models/thread.py b/backend/app/models/thread.py new file mode 100644 index 000000000..b200984f5 --- /dev/null +++ b/backend/app/models/thread.py @@ -0,0 +1,27 @@ +from pydantic import BaseModel, ConfigDict +from typing import Optional + + +class MessageRequest(BaseModel): + model_config = ConfigDict(extra="allow") + + question: str + assistant_id: str + callback_url: str + thread_id: Optional[str] = None + + +class AckPayload(BaseModel): + status: str + message: str + success: bool + thread_id: Optional[str] = None + + +class CallbackPayload(BaseModel): + model_config = ConfigDict(extra="allow") + + status: str + message: str + thread_id: str + endpoint: str \ No newline at end of file From 743e5d000c2cdbe811d4769cd62b2546c6ab7821 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Thu, 13 Mar 2025 11:18:03 +0530 Subject: [PATCH 02/14] added testcases and citation --- backend/app/api/routes/threads.py | 17 +-- backend/app/models/thread.py | 1 + backend/app/tests/api/routes/test_threads.py | 132 +++++++++++++++++++ 3 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 backend/app/tests/api/routes/test_threads.py diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 7e6fb27fd..fb67a47f7 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -1,5 +1,6 @@ -import requests import openai +import re +import requests from openai import OpenAI from fastapi import APIRouter, BackgroundTasks from app.models import ( MessageRequest, AckPayload, CallbackPayload) @@ -7,16 +8,14 @@ from ...core.logger import logging logger = logging.getLogger(__name__) -router = APIRouter(prefix="/threads", tags=["threads"]) +router = APIRouter(tags=["threads"]) def send_callback(callback_url: str, data: dict): """Send results to the callback URL (synchronously).""" try: - print("completed") session = requests.Session() session.verify = False - print(data) response = session.post(callback_url, json=data) response.raise_for_status() return True @@ -50,7 +49,6 @@ def process_run(request: MessageRequest, client: OpenAI): This function is run in the background after we have already returned an initial response. """ try: - print("Thread run started") # Start the run run = client.beta.threads.runs.create_and_poll( thread_id=request.thread_id, @@ -58,13 +56,16 @@ def process_run(request: MessageRequest, client: OpenAI): ) if run.status == "completed": - print("Thread run completed") messages = client.beta.threads.messages.list(thread_id=request.thread_id) latest_message = messages.data[0] message_content = latest_message.content[0].text.value + if request.remove_citation: + message = re.sub(r"【\d+(?::\d+)?†[^】]*】", "", message_content) + else: + message = message_content callback_response = build_callback_payload( - request=request, status="success", message=message_content + request=request, status="success", message=message ) else: callback_response = build_callback_payload( @@ -100,7 +101,7 @@ def validate_assistant_id(assistant_id: str, client: OpenAI): return None -@router.post("/") +@router.post("/threads") async def threads(request: MessageRequest, background_tasks: BackgroundTasks): """ Accepts a question, assistant_id, callback_url, and optional thread_id from the request body. diff --git a/backend/app/models/thread.py b/backend/app/models/thread.py index b200984f5..2244eb846 100644 --- a/backend/app/models/thread.py +++ b/backend/app/models/thread.py @@ -9,6 +9,7 @@ class MessageRequest(BaseModel): assistant_id: str callback_url: str thread_id: Optional[str] = None + remove_citation: Optional[bool] = False class AckPayload(BaseModel): diff --git a/backend/app/tests/api/routes/test_threads.py b/backend/app/tests/api/routes/test_threads.py new file mode 100644 index 000000000..bf3ecb595 --- /dev/null +++ b/backend/app/tests/api/routes/test_threads.py @@ -0,0 +1,132 @@ +import pytest +import openai +from unittest.mock import MagicMock, patch +from fastapi import FastAPI +from fastapi.testclient import TestClient +from app.api.routes.threads import router, process_run, validate_assistant_id +from models.thread import MessageRequest, AckPayload + +# Wrap the router in a FastAPI app instance. +app = FastAPI() +app.include_router(router) +client = TestClient(app) + + +@patch("src.app.api.v1.threads.OpenAI") +def test_threads_endpoint(mock_openai): + """ + Test the /threads endpoint when creating a new thread. + The patched OpenAI client simulates: + - A successful assistant ID validation. + - New thread creation with a dummy thread id. + - No existing runs. + The expected response should have status "processing" and include a thread_id. + """ + # Create a dummy client to simulate OpenAI API behavior. + dummy_client = MagicMock() + # Simulate a valid assistant ID by ensuring retrieve doesn't raise an error. + dummy_client.beta.assistants.retrieve.return_value = None + # Simulate thread creation. + dummy_thread = MagicMock() + dummy_thread.id = "dummy_thread_id" + dummy_client.beta.threads.create.return_value = dummy_thread + # Simulate message creation. + dummy_client.beta.threads.messages.create.return_value = None + # Simulate that no active run exists. + dummy_client.beta.threads.runs.list.return_value = MagicMock(data=[]) + + mock_openai.return_value = dummy_client + + request_data = { + "question": "What is Glific?", + "assistant_id": "assistant_123", + "callback_url": "http://example.com/callback", + } + response = client.post("/threads", json=request_data) + assert response.status_code == 200 + response_json = response.json() + assert response_json["status"] == "processing" + assert response_json["message"] == "Run started" + assert response_json["success"] == True + assert response_json["thread_id"] == "dummy_thread_id" + + +@patch("src.app.api.v1.threads.OpenAI") +@pytest.mark.parametrize( + "remove_citation, expected_message", + [ + ( + True, + "Glific is an open-source, two-way messaging platform designed for nonprofits to scale their outreach via WhatsApp", + ), + ( + False, + "Glific is an open-source, two-way messaging platform designed for nonprofits to scale their outreach via WhatsApp【1:2†citation】", + ), + ], +) +def test_process_run_variants(mock_openai, remove_citation, expected_message): + """ + Test process_run for both remove_citation variants: + - Mocks the OpenAI client to simulate a completed run. + - Verifies that send_callback is called with the expected message based on the remove_citation flag. + """ + # Setup the mock client. + mock_client = MagicMock() + mock_openai.return_value = mock_client + + # Create the request with the variable remove_citation flag. + request = MessageRequest( + question="What is Glific?", + assistant_id="assistant_123", + callback_url="http://example.com/callback", + thread_id="thread_123", + remove_citation=remove_citation, + ) + + # Simulate a completed run. + mock_run = MagicMock() + mock_run.status = "completed" + mock_client.beta.threads.runs.create_and_poll.return_value = mock_run + + # Set up the dummy message based on the remove_citation flag. + base_message = "Glific is an open-source, two-way messaging platform designed for nonprofits to scale their outreach via WhatsApp" + citation_message = base_message if remove_citation else f"{base_message}【1:2†citation】" + dummy_message = MagicMock() + dummy_message.content = [MagicMock(text=MagicMock(value=citation_message))] + mock_client.beta.threads.messages.list.return_value.data = [dummy_message] + + # Patch send_callback and invoke process_run. + with patch("src.app.api.v1.threads.send_callback") as mock_send_callback: + process_run(request, mock_client) + mock_send_callback.assert_called_once() + callback_url, payload = mock_send_callback.call_args[0] + print(payload) + assert callback_url == request.callback_url + assert payload.get("message", "") == expected_message + assert payload.get("status", "") == "success" + assert payload.get("thread_id", "") == "thread_123" + + +@patch("src.app.api.v1.threads.OpenAI") +def test_validate_assistant_id(mock_openai): + """ + Test validate_assistant_id: + - For a valid assistant ID, it should return None. + - For an invalid assistant ID, it should return an AckPayload with an error. + """ + mock_client = MagicMock() + mock_openai.return_value = mock_client + + # Simulate a valid assistant ID. + result_valid = validate_assistant_id("valid_assistant_id", mock_client) + assert result_valid is None + + # Simulate an invalid assistant ID by raising NotFoundError with required kwargs. + mock_client.beta.assistants.retrieve.side_effect = openai.NotFoundError( + "Not found", response=MagicMock(), body={"message": "Not found"} + ) + ack_payload = validate_assistant_id("invalid_assistant_id", mock_client) + assert isinstance(ack_payload, AckPayload) + assert ack_payload.status == "error" + assert "invalid_assistant_id" in ack_payload.message \ No newline at end of file From 00ebe6fd4778a6bf1c65d139cd9142e4cdf22251 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Thu, 13 Mar 2025 11:18:45 +0530 Subject: [PATCH 03/14] removing ssl verify --- backend/app/api/routes/threads.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index fb67a47f7..2407ddd30 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -15,7 +15,6 @@ def send_callback(callback_url: str, data: dict): """Send results to the callback URL (synchronously).""" try: session = requests.Session() - session.verify = False response = session.post(callback_url, json=data) response.raise_for_status() return True From 15aa41ce4c23f9c3be7d093194bf59392128f273 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 19 Mar 2025 19:16:18 +0530 Subject: [PATCH 04/14] using standardized APIResponse --- backend/app/api/routes/threads.py | 101 +++++++----------- backend/app/logs/app.log | 167 ++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 65 deletions(-) create mode 100644 backend/app/logs/app.log diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 2407ddd30..1d99c3e66 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -3,7 +3,7 @@ import requests from openai import OpenAI from fastapi import APIRouter, BackgroundTasks -from app.models import ( MessageRequest, AckPayload, CallbackPayload) +from app.utils import APIResponse from ...core.config import settings from ...core.logger import logging @@ -23,26 +23,7 @@ def send_callback(callback_url: str, data: dict): return False -def build_callback_payload(request: MessageRequest, status: str, message: str) -> CallbackPayload: - """ - Helper function to build the CallbackPayload from a MessageRequest. - """ - data = { - "status": status, - "message": message, - "thread_id": request.thread_id, - "endpoint": getattr(request, "endpoint", "some-default-endpoint"), - } - - # Update with any additional fields from request that we haven't excluded - data.update( - request.model_dump(exclude={"question", "assistant_id", "callback_url", "thread_id"}) - ) - - return CallbackPayload(**data) - - -def process_run(request: MessageRequest, client: OpenAI): +def process_run(request: dict, client: OpenAI): """ Background task to run create_and_poll, then send the callback with the result. This function is run in the background after we have already returned an initial response. @@ -50,29 +31,32 @@ def process_run(request: MessageRequest, client: OpenAI): try: # Start the run run = client.beta.threads.runs.create_and_poll( - thread_id=request.thread_id, - assistant_id=request.assistant_id, + thread_id=request["thread_id"], + assistant_id=request["assistant_id"], ) if run.status == "completed": - messages = client.beta.threads.messages.list(thread_id=request.thread_id) + messages = client.beta.threads.messages.list( + thread_id=request["thread_id"]) latest_message = messages.data[0] message_content = latest_message.content[0].text.value - if request.remove_citation: + if request["remove_citation"]: message = re.sub(r"【\d+(?::\d+)?†[^】]*】", "", message_content) else: message = message_content - callback_response = build_callback_payload( - request=request, status="success", message=message - ) + callback_response = APIResponse.success_response(data={ + "status": "success", + "message": message, + "thread_id": request["thread_id"], + "endpoint": getattr(request, "endpoint", "some-default-endpoint"), + }) else: - callback_response = build_callback_payload( - request=request, status="error", message=f"Run failed with status: {run.status}" - ) + callback_response = APIResponse.failure_response( + error=f"Run failed with status: {run.status}") # Send callback with results - send_callback(request.callback_url, callback_response.model_dump()) + send_callback(request["callback_url"], callback_response.model_dump()) except openai.OpenAIError as e: # Handle any other OpenAI API errors @@ -81,27 +65,21 @@ def process_run(request: MessageRequest, client: OpenAI): else: error_message = str(e) - callback_response = build_callback_payload( - request=request, status="error", message=error_message - ) + callback_response = APIResponse.failure_response(error=error_message) - send_callback(request.callback_url, callback_response.model_dump()) + send_callback(request["callback_url"], callback_response.model_dump()) def validate_assistant_id(assistant_id: str, client: OpenAI): try: client.beta.assistants.retrieve(assistant_id=assistant_id) except openai.NotFoundError: - return AckPayload( - status="error", - message=f"Invalid assistant ID provided {assistant_id}", - success=False, - ) + return APIResponse.failure_response(error=f"Invalid assistant ID provided {assistant_id}") return None @router.post("/threads") -async def threads(request: MessageRequest, background_tasks: BackgroundTasks): +async def threads(request: dict, background_tasks: BackgroundTasks): """ Accepts a question, assistant_id, callback_url, and optional thread_id from the request body. Returns an immediate "processing" response, then continues to run create_and_poll in background. @@ -109,14 +87,15 @@ async def threads(request: MessageRequest, background_tasks: BackgroundTasks): """ client = OpenAI(api_key=settings.OPENAI_API_KEY) - assistant_error = validate_assistant_id(request.assistant_id, client) + assistant_error = validate_assistant_id(request["assistant_id"], client) if assistant_error: return assistant_error # 1. Validate or check if there's an existing thread with an in-progress run - if request.thread_id: + if request["thread_id"]: try: - runs = client.beta.threads.runs.list(thread_id=request.thread_id) + runs = client.beta.threads.runs.list( + thread_id=request["thread_id"]) # Get the most recent run (first in the list) if any if runs.data and len(runs.data) > 0: latest_run = runs.data[0] @@ -127,46 +106,38 @@ async def threads(request: MessageRequest, background_tasks: BackgroundTasks): } except openai.NotFoundError: # Handle invalid thread ID - return AckPayload( - status="error", - message=f"Invalid thread ID provided {request.thread_id}", - success=False, - ) + return APIResponse.failure_response(error=f"Invalid thread ID provided {request['thread_id']}") # Use existing thread client.beta.threads.messages.create( - thread_id=request.thread_id, role="user", content=request.question + thread_id=request["thread_id"], role="user", content=request["question"] ) else: try: # Create new thread thread = client.beta.threads.create() client.beta.threads.messages.create( - thread_id=thread.id, role="user", content=request.question + thread_id=thread.id, role="user", content=request["question"] ) - request.thread_id = thread.id + request["thread_id"] = thread.id except openai.OpenAIError as e: # Handle any other OpenAI API errors if isinstance(e.body, dict) and "message" in e.body: error_message = e.body["message"] else: error_message = str(e) - return AckPayload( - status="error", - message=error_message, - success=False, - ) + return APIResponse.failure_response(error=error_message) # 2. Send immediate response to complete the API call - initial_response = AckPayload( - status="processing", - message="Run started", - thread_id=request.thread_id, - success=True, - ) + initial_response = APIResponse.success_response(data={ + "status": "processing", + "message": "Run started", + "thread_id": request["thread_id"], + "success": True, + }) # 3. Schedule the background task to run create_and_poll and send callback background_tasks.add_task(process_run, request, client) # 4. Return immediately so the client knows we've accepted the request - return initial_response \ No newline at end of file + return initial_response diff --git a/backend/app/logs/app.log b/backend/app/logs/app.log new file mode 100644 index 000000000..97a9ea871 --- /dev/null +++ b/backend/app/logs/app.log @@ -0,0 +1,167 @@ +2025-03-18 10:25:26,353 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-18 10:25:26,865 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-18 10:25:27,351 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/messages "HTTP/1.1 200 OK" +2025-03-18 10:25:28,268 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs "HTTP/1.1 200 OK" +2025-03-18 10:25:28,598 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-18 10:25:29,047 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-18 10:25:29,322 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:30,055 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/messages "HTTP/1.1 200 OK" +2025-03-18 10:25:30,792 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:31,034 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs "HTTP/1.1 200 OK" +2025-03-18 10:25:31,539 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" +2025-03-18 10:25:32,253 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:33,110 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" +2025-03-18 10:25:33,725 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:34,607 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" +2025-03-18 10:25:35,195 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:36,067 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" +2025-03-18 10:25:36,695 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" +2025-03-18 10:25:37,061 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/messages "HTTP/1.1 200 OK" +2025-03-18 10:25:37,088 - app.api.routes.threads - ERROR - Callback failed: HTTPSConnectionPool(host='api.glific.test', port=4001): Max retries exceeded with url: /webhook/flow_resume (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)'))) +2025-03-18 10:25:37,721 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" +2025-03-18 10:25:38,129 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/messages "HTTP/1.1 200 OK" +2025-03-18 10:25:38,153 - app.api.routes.threads - ERROR - Callback failed: HTTPSConnectionPool(host='api.glific.test', port=4001): Max retries exceeded with url: /webhook/flow_resume (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)'))) +2025-03-19 10:11:34,457 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:11:34,929 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:11:35,391 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/messages "HTTP/1.1 200 OK" +2025-03-19 10:11:36,502 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs "HTTP/1.1 200 OK" +2025-03-19 10:11:37,031 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" +2025-03-19 10:11:38,499 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" +2025-03-19 10:11:39,972 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" +2025-03-19 10:11:41,640 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" +2025-03-19 10:11:42,002 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/messages "HTTP/1.1 200 OK" +2025-03-19 10:13:10,066 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:13:10,527 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:13:10,988 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/messages "HTTP/1.1 200 OK" +2025-03-19 10:13:12,480 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs "HTTP/1.1 200 OK" +2025-03-19 10:13:12,908 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:14,358 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:15,845 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:17,335 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:18,823 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:20,357 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:21,801 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:23,429 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:24,989 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:26,463 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" +2025-03-19 10:13:26,852 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/messages "HTTP/1.1 200 OK" +2025-03-19 10:13:46,783 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:13:47,255 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:13:47,723 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/messages "HTTP/1.1 200 OK" +2025-03-19 10:13:48,630 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs "HTTP/1.1 200 OK" +2025-03-19 10:13:49,110 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" +2025-03-19 10:13:50,611 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" +2025-03-19 10:13:52,205 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" +2025-03-19 10:13:53,723 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" +2025-03-19 10:13:54,149 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/messages "HTTP/1.1 200 OK" +2025-03-19 10:15:27,642 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:15:28,091 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:15:28,668 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/messages "HTTP/1.1 200 OK" +2025-03-19 10:15:33,547 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs "HTTP/1.1 200 OK" +2025-03-19 10:15:34,123 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" +2025-03-19 10:15:35,630 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" +2025-03-19 10:15:37,122 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" +2025-03-19 10:15:38,595 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" +2025-03-19 10:15:39,008 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/messages "HTTP/1.1 200 OK" +2025-03-19 10:15:40,362 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:15:41,467 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:15:41,952 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/messages "HTTP/1.1 200 OK" +2025-03-19 10:15:42,841 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs "HTTP/1.1 200 OK" +2025-03-19 10:15:43,261 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs/run_z0xhauhIjbRE4pm3qPkT5DLJ "HTTP/1.1 200 OK" +2025-03-19 10:15:44,848 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs/run_z0xhauhIjbRE4pm3qPkT5DLJ "HTTP/1.1 200 OK" +2025-03-19 10:15:45,236 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/messages "HTTP/1.1 200 OK" +2025-03-19 10:19:32,793 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:19:33,213 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:19:33,679 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/messages "HTTP/1.1 200 OK" +2025-03-19 10:19:35,150 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs "HTTP/1.1 200 OK" +2025-03-19 10:19:35,571 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:37,173 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:38,706 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:40,168 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:41,703 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:43,240 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:44,776 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" +2025-03-19 10:19:45,285 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/messages "HTTP/1.1 200 OK" +2025-03-19 10:19:46,663 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:19:47,658 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:19:48,359 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/messages "HTTP/1.1 200 OK" +2025-03-19 10:19:49,149 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs "HTTP/1.1 200 OK" +2025-03-19 10:19:49,606 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" +2025-03-19 10:19:51,123 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" +2025-03-19 10:19:52,589 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" +2025-03-19 10:19:53,107 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/messages "HTTP/1.1 200 OK" +2025-03-19 10:24:16,700 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:24:17,117 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:24:17,628 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/messages "HTTP/1.1 200 OK" +2025-03-19 10:24:19,052 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs "HTTP/1.1 200 OK" +2025-03-19 10:24:19,564 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:21,333 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:22,840 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:24,320 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:25,868 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:28,017 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" +2025-03-19 10:24:28,435 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/messages "HTTP/1.1 200 OK" +2025-03-19 10:24:29,146 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:24:29,655 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:24:30,111 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/messages "HTTP/1.1 200 OK" +2025-03-19 10:24:31,346 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs "HTTP/1.1 200 OK" +2025-03-19 10:24:31,900 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" +2025-03-19 10:24:34,481 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" +2025-03-19 10:24:35,992 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" +2025-03-19 10:24:36,375 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/messages "HTTP/1.1 200 OK" +2025-03-19 10:28:04,292 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:28:05,261 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:28:05,713 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/messages "HTTP/1.1 200 OK" +2025-03-19 10:28:06,604 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs "HTTP/1.1 200 OK" +2025-03-19 10:28:07,158 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:08,693 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:10,204 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:11,695 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:13,200 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:14,735 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" +2025-03-19 10:28:17,395 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/messages "HTTP/1.1 200 OK" +2025-03-19 10:28:18,831 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:28:19,874 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:28:20,393 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/messages "HTTP/1.1 200 OK" +2025-03-19 10:28:21,596 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs "HTTP/1.1 200 OK" +2025-03-19 10:28:22,107 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs/run_J7ATFRXWfPXl7ir2R00UuJD7 "HTTP/1.1 200 OK" +2025-03-19 10:28:47,093 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs/run_J7ATFRXWfPXl7ir2R00UuJD7 "HTTP/1.1 200 OK" +2025-03-19 10:28:47,603 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/messages "HTTP/1.1 200 OK" +2025-03-19 10:31:43,058 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:31:43,471 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:31:44,044 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/messages "HTTP/1.1 200 OK" +2025-03-19 10:31:46,179 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs "HTTP/1.1 200 OK" +2025-03-19 10:31:46,665 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:48,220 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:49,843 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:51,519 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:53,057 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:54,496 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" +2025-03-19 10:31:54,864 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/messages "HTTP/1.1 200 OK" +2025-03-19 10:31:55,602 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:31:56,054 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:31:56,538 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/messages "HTTP/1.1 200 OK" +2025-03-19 10:31:58,483 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs "HTTP/1.1 200 OK" +2025-03-19 10:31:59,010 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" +2025-03-19 10:32:00,633 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" +2025-03-19 10:32:02,476 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" +2025-03-19 10:32:02,837 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/messages "HTTP/1.1 200 OK" +2025-03-19 10:33:27,777 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-19 10:33:28,494 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:33:28,939 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/messages "HTTP/1.1 200 OK" +2025-03-19 10:33:29,835 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs "HTTP/1.1 200 OK" +2025-03-19 10:33:30,296 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:31,872 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:33,363 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:36,358 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:37,843 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:39,285 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" +2025-03-19 10:33:39,665 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/messages "HTTP/1.1 200 OK" +2025-03-19 10:33:40,372 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" +2025-03-19 10:33:40,883 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" +2025-03-19 10:33:41,976 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/messages "HTTP/1.1 200 OK" +2025-03-19 10:33:43,240 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs "HTTP/1.1 200 OK" +2025-03-19 10:33:43,694 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" +2025-03-19 10:33:45,261 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" +2025-03-19 10:33:46,775 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" +2025-03-19 10:33:47,233 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/messages "HTTP/1.1 200 OK" From 71bc5a8745dec4ffb85bac42a8cee4e90542e51e Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Thu, 20 Mar 2025 19:40:47 +0530 Subject: [PATCH 05/14] getting rid of redundant files --- backend/app/api/routes/threads.py | 22 +++++++++++++++------- backend/app/models/__init__.py | 3 +-- backend/app/models/thread.py | 28 ---------------------------- 3 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 backend/app/models/thread.py diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 1d99c3e66..7c20f13f8 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -15,6 +15,8 @@ def send_callback(callback_url: str, data: dict): """Send results to the callback URL (synchronously).""" try: session = requests.Session() + # uncomment this to run locally without SSL + # session.verify = False response = session.post(callback_url, json=data) response.raise_for_status() return True @@ -45,11 +47,16 @@ def process_run(request: dict, client: OpenAI): message = re.sub(r"【\d+(?::\d+)?†[^】]*】", "", message_content) else: message = message_content + + # Update the data dictionary with additional fields from the request, excluding specific keys + additional_data = {k: v for k, v in request.items( + ) if k not in {"question", "assistant_id", "callback_url", "thread_id"}} callback_response = APIResponse.success_response(data={ "status": "success", "message": message, "thread_id": request["thread_id"], "endpoint": getattr(request, "endpoint", "some-default-endpoint"), + **additional_data }) else: callback_response = APIResponse.failure_response( @@ -86,16 +93,17 @@ async def threads(request: dict, background_tasks: BackgroundTasks): Once completed, calls send_callback with the final result. """ client = OpenAI(api_key=settings.OPENAI_API_KEY) - assistant_error = validate_assistant_id(request["assistant_id"], client) if assistant_error: return assistant_error + # Use get method to safely access thread_id + thread_id = request.get("thread_id") + # 1. Validate or check if there's an existing thread with an in-progress run - if request["thread_id"]: + if thread_id: try: - runs = client.beta.threads.runs.list( - thread_id=request["thread_id"]) + runs = client.beta.threads.runs.list(thread_id=thread_id) # Get the most recent run (first in the list) if any if runs.data and len(runs.data) > 0: latest_run = runs.data[0] @@ -106,11 +114,11 @@ async def threads(request: dict, background_tasks: BackgroundTasks): } except openai.NotFoundError: # Handle invalid thread ID - return APIResponse.failure_response(error=f"Invalid thread ID provided {request['thread_id']}") + return APIResponse.failure_response(error=f"Invalid thread ID provided {thread_id}") # Use existing thread client.beta.threads.messages.create( - thread_id=request["thread_id"], role="user", content=request["question"] + thread_id=thread_id, role="user", content=request["question"] ) else: try: @@ -132,7 +140,7 @@ async def threads(request: dict, background_tasks: BackgroundTasks): initial_response = APIResponse.success_response(data={ "status": "processing", "message": "Run started", - "thread_id": request["thread_id"], + "thread_id": request.get("thread_id"), "success": True, }) diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index cc10ae3c5..5a03fa56c 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -11,5 +11,4 @@ UserUpdateMe, NewPassword, UpdatePassword, -) -from .thread import MessageRequest, AckPayload, CallbackPayload \ No newline at end of file +) \ No newline at end of file diff --git a/backend/app/models/thread.py b/backend/app/models/thread.py deleted file mode 100644 index 2244eb846..000000000 --- a/backend/app/models/thread.py +++ /dev/null @@ -1,28 +0,0 @@ -from pydantic import BaseModel, ConfigDict -from typing import Optional - - -class MessageRequest(BaseModel): - model_config = ConfigDict(extra="allow") - - question: str - assistant_id: str - callback_url: str - thread_id: Optional[str] = None - remove_citation: Optional[bool] = False - - -class AckPayload(BaseModel): - status: str - message: str - success: bool - thread_id: Optional[str] = None - - -class CallbackPayload(BaseModel): - model_config = ConfigDict(extra="allow") - - status: str - message: str - thread_id: str - endpoint: str \ No newline at end of file From 8f11fa591acde009fbb586a3969ebdff05c66438 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Fri, 21 Mar 2025 10:27:17 +0530 Subject: [PATCH 06/14] refactor code after testing --- backend/app/api/routes/threads.py | 9 +- backend/app/logs/app.log | 167 ------------------------------ backend/pyproject.toml | 1 + backend/uv.lock | 104 ++++++++++++++++++- 4 files changed, 107 insertions(+), 174 deletions(-) delete mode 100644 backend/app/logs/app.log diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 7c20f13f8..5b2aba744 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -15,7 +15,7 @@ def send_callback(callback_url: str, data: dict): """Send results to the callback URL (synchronously).""" try: session = requests.Session() - # uncomment this to run locally without SSL + # uncomment this to run locally without SSL # session.verify = False response = session.post(callback_url, json=data) response.raise_for_status() @@ -108,11 +108,8 @@ async def threads(request: dict, background_tasks: BackgroundTasks): if runs.data and len(runs.data) > 0: latest_run = runs.data[0] if latest_run.status in ["queued", "in_progress", "requires_action"]: - return { - "status": "error", - "message": f"There is an active run on this thread (status: {latest_run.status}). Please wait for it to complete.", - } - except openai.NotFoundError: + return APIResponse.failure_response(error=f"There is an active run on this thread (status: {latest_run.status}). Please wait for it to complete.") + except openai.OpenAIError: # Handle invalid thread ID return APIResponse.failure_response(error=f"Invalid thread ID provided {thread_id}") diff --git a/backend/app/logs/app.log b/backend/app/logs/app.log deleted file mode 100644 index 97a9ea871..000000000 --- a/backend/app/logs/app.log +++ /dev/null @@ -1,167 +0,0 @@ -2025-03-18 10:25:26,353 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-18 10:25:26,865 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-18 10:25:27,351 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/messages "HTTP/1.1 200 OK" -2025-03-18 10:25:28,268 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs "HTTP/1.1 200 OK" -2025-03-18 10:25:28,598 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-18 10:25:29,047 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-18 10:25:29,322 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:30,055 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/messages "HTTP/1.1 200 OK" -2025-03-18 10:25:30,792 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:31,034 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs "HTTP/1.1 200 OK" -2025-03-18 10:25:31,539 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" -2025-03-18 10:25:32,253 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:33,110 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" -2025-03-18 10:25:33,725 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:34,607 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" -2025-03-18 10:25:35,195 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:36,067 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" -2025-03-18 10:25:36,695 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/runs/run_6MiqZYz6sqIJvyN0jZReNjGn "HTTP/1.1 200 OK" -2025-03-18 10:25:37,061 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_53zDFOccKPQnwk9Tn3GvXug6/messages "HTTP/1.1 200 OK" -2025-03-18 10:25:37,088 - app.api.routes.threads - ERROR - Callback failed: HTTPSConnectionPool(host='api.glific.test', port=4001): Max retries exceeded with url: /webhook/flow_resume (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)'))) -2025-03-18 10:25:37,721 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/runs/run_VZMErKIXrO5VBJyWxdgXwc8h "HTTP/1.1 200 OK" -2025-03-18 10:25:38,129 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_TiBP4TNdytmH7wZWnU6Lt8tz/messages "HTTP/1.1 200 OK" -2025-03-18 10:25:38,153 - app.api.routes.threads - ERROR - Callback failed: HTTPSConnectionPool(host='api.glific.test', port=4001): Max retries exceeded with url: /webhook/flow_resume (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)'))) -2025-03-19 10:11:34,457 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:11:34,929 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:11:35,391 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/messages "HTTP/1.1 200 OK" -2025-03-19 10:11:36,502 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs "HTTP/1.1 200 OK" -2025-03-19 10:11:37,031 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" -2025-03-19 10:11:38,499 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" -2025-03-19 10:11:39,972 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" -2025-03-19 10:11:41,640 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/runs/run_ydrL73SWUKPZhe7L3umcbz2r "HTTP/1.1 200 OK" -2025-03-19 10:11:42,002 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_yXsQWAh8nmiDOoJgzINEiRc8/messages "HTTP/1.1 200 OK" -2025-03-19 10:13:10,066 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:13:10,527 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:13:10,988 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/messages "HTTP/1.1 200 OK" -2025-03-19 10:13:12,480 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs "HTTP/1.1 200 OK" -2025-03-19 10:13:12,908 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:14,358 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:15,845 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:17,335 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:18,823 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:20,357 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:21,801 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:23,429 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:24,989 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:26,463 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/runs/run_HX3wR8c4L2rTXyUu0gMreWQZ "HTTP/1.1 200 OK" -2025-03-19 10:13:26,852 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ich4fmpT65Ni70gYqXzapgVG/messages "HTTP/1.1 200 OK" -2025-03-19 10:13:46,783 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:13:47,255 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:13:47,723 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/messages "HTTP/1.1 200 OK" -2025-03-19 10:13:48,630 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs "HTTP/1.1 200 OK" -2025-03-19 10:13:49,110 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" -2025-03-19 10:13:50,611 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" -2025-03-19 10:13:52,205 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" -2025-03-19 10:13:53,723 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/runs/run_Ve4BoXTYxzYJoWxmGqCc0XSo "HTTP/1.1 200 OK" -2025-03-19 10:13:54,149 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Szb9iLA3OU6Xr8pv5O5rBHOL/messages "HTTP/1.1 200 OK" -2025-03-19 10:15:27,642 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:15:28,091 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:15:28,668 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/messages "HTTP/1.1 200 OK" -2025-03-19 10:15:33,547 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs "HTTP/1.1 200 OK" -2025-03-19 10:15:34,123 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" -2025-03-19 10:15:35,630 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" -2025-03-19 10:15:37,122 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" -2025-03-19 10:15:38,595 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/runs/run_hrP3rk30MfOlcMRnaxLUFvb0 "HTTP/1.1 200 OK" -2025-03-19 10:15:39,008 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_ncMOgUg3dyHU1FXJgtNAXa5L/messages "HTTP/1.1 200 OK" -2025-03-19 10:15:40,362 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:15:41,467 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:15:41,952 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/messages "HTTP/1.1 200 OK" -2025-03-19 10:15:42,841 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs "HTTP/1.1 200 OK" -2025-03-19 10:15:43,261 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs/run_z0xhauhIjbRE4pm3qPkT5DLJ "HTTP/1.1 200 OK" -2025-03-19 10:15:44,848 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/runs/run_z0xhauhIjbRE4pm3qPkT5DLJ "HTTP/1.1 200 OK" -2025-03-19 10:15:45,236 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_fSkg75yDtnlYtTG77vsdDiE8/messages "HTTP/1.1 200 OK" -2025-03-19 10:19:32,793 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:19:33,213 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:19:33,679 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/messages "HTTP/1.1 200 OK" -2025-03-19 10:19:35,150 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs "HTTP/1.1 200 OK" -2025-03-19 10:19:35,571 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:37,173 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:38,706 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:40,168 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:41,703 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:43,240 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:44,776 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/runs/run_pQ4unbOxaS1kHWccj0uyCAQd "HTTP/1.1 200 OK" -2025-03-19 10:19:45,285 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_9zerpWBvPkZ0NqHMSSycqZPp/messages "HTTP/1.1 200 OK" -2025-03-19 10:19:46,663 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:19:47,658 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:19:48,359 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/messages "HTTP/1.1 200 OK" -2025-03-19 10:19:49,149 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs "HTTP/1.1 200 OK" -2025-03-19 10:19:49,606 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" -2025-03-19 10:19:51,123 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" -2025-03-19 10:19:52,589 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/runs/run_cRicvHZlYbtImt7y4gRkYGAw "HTTP/1.1 200 OK" -2025-03-19 10:19:53,107 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_4j4zl1rn2eLD6gWpU6vdIAcn/messages "HTTP/1.1 200 OK" -2025-03-19 10:24:16,700 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:24:17,117 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:24:17,628 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/messages "HTTP/1.1 200 OK" -2025-03-19 10:24:19,052 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs "HTTP/1.1 200 OK" -2025-03-19 10:24:19,564 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:21,333 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:22,840 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:24,320 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:25,868 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:28,017 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/runs/run_pVFNEVT3hDizBSVIU02hzApd "HTTP/1.1 200 OK" -2025-03-19 10:24:28,435 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Q1WdOFLNzANJKy1vpWPzQbQR/messages "HTTP/1.1 200 OK" -2025-03-19 10:24:29,146 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:24:29,655 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:24:30,111 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/messages "HTTP/1.1 200 OK" -2025-03-19 10:24:31,346 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs "HTTP/1.1 200 OK" -2025-03-19 10:24:31,900 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" -2025-03-19 10:24:34,481 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" -2025-03-19 10:24:35,992 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/runs/run_tig7qHm5r7MMPby9yZcGo4wu "HTTP/1.1 200 OK" -2025-03-19 10:24:36,375 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_Uf0GJl9Eu0J8OuyXd7k0JSdl/messages "HTTP/1.1 200 OK" -2025-03-19 10:28:04,292 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:28:05,261 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:28:05,713 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/messages "HTTP/1.1 200 OK" -2025-03-19 10:28:06,604 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs "HTTP/1.1 200 OK" -2025-03-19 10:28:07,158 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:08,693 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:10,204 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:11,695 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:13,200 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:14,735 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/runs/run_uHPcc2CCtxloVC5LgxMXktK4 "HTTP/1.1 200 OK" -2025-03-19 10:28:17,395 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_5yj7kLzGUKlxbN74tznwEaji/messages "HTTP/1.1 200 OK" -2025-03-19 10:28:18,831 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:28:19,874 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:28:20,393 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/messages "HTTP/1.1 200 OK" -2025-03-19 10:28:21,596 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs "HTTP/1.1 200 OK" -2025-03-19 10:28:22,107 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs/run_J7ATFRXWfPXl7ir2R00UuJD7 "HTTP/1.1 200 OK" -2025-03-19 10:28:47,093 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/runs/run_J7ATFRXWfPXl7ir2R00UuJD7 "HTTP/1.1 200 OK" -2025-03-19 10:28:47,603 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_NE4Hlogcq9X2RI3lLLB8XqFJ/messages "HTTP/1.1 200 OK" -2025-03-19 10:31:43,058 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:31:43,471 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:31:44,044 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/messages "HTTP/1.1 200 OK" -2025-03-19 10:31:46,179 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs "HTTP/1.1 200 OK" -2025-03-19 10:31:46,665 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:48,220 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:49,843 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:51,519 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:53,057 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:54,496 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/runs/run_kTf7FO6f519MYkRFXSp3EOui "HTTP/1.1 200 OK" -2025-03-19 10:31:54,864 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_nPA7IcNxDcoIcb0pUfqYAK74/messages "HTTP/1.1 200 OK" -2025-03-19 10:31:55,602 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:31:56,054 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:31:56,538 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/messages "HTTP/1.1 200 OK" -2025-03-19 10:31:58,483 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs "HTTP/1.1 200 OK" -2025-03-19 10:31:59,010 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" -2025-03-19 10:32:00,633 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" -2025-03-19 10:32:02,476 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/runs/run_gP0ZO3Dz2srJb108JskyLz7T "HTTP/1.1 200 OK" -2025-03-19 10:32:02,837 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_EeHzl6JjXLsECGph8csJIiDP/messages "HTTP/1.1 200 OK" -2025-03-19 10:33:27,777 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-19 10:33:28,494 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:33:28,939 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/messages "HTTP/1.1 200 OK" -2025-03-19 10:33:29,835 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs "HTTP/1.1 200 OK" -2025-03-19 10:33:30,296 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:31,872 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:33,363 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:36,358 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:37,843 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:39,285 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/runs/run_sNqNFDFlPxlJn3v9t3eOiTo6 "HTTP/1.1 200 OK" -2025-03-19 10:33:39,665 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_FJIYi5SZhgcM5wY6EaK7iLoW/messages "HTTP/1.1 200 OK" -2025-03-19 10:33:40,372 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_7TY0ZIsej1dVqpzA99DJXwTm "HTTP/1.1 200 OK" -2025-03-19 10:33:40,883 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads "HTTP/1.1 200 OK" -2025-03-19 10:33:41,976 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/messages "HTTP/1.1 200 OK" -2025-03-19 10:33:43,240 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs "HTTP/1.1 200 OK" -2025-03-19 10:33:43,694 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" -2025-03-19 10:33:45,261 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" -2025-03-19 10:33:46,775 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/runs/run_H5dyTAAWUDvxn4tX7IPdswIY "HTTP/1.1 200 OK" -2025-03-19 10:33:47,233 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_35oLlBBLW2Zp6Hzn3Ey7w3dc/messages "HTTP/1.1 200 OK" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 1c77b83de..e35dd3ce9 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "pydantic-settings<3.0.0,>=2.2.1", "sentry-sdk[fastapi]<2.0.0,>=1.40.6", "pyjwt<3.0.0,>=2.8.0", + "openai>=1.67.0", ] [tool.uv] diff --git a/backend/uv.lock b/backend/uv.lock index cfc200d3c..b11935da0 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version < '3.13'", @@ -55,6 +56,7 @@ dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "httpx" }, { name = "jinja2" }, + { name = "openai" }, { name = "passlib", extra = ["bcrypt"] }, { name = "psycopg", extra = ["binary"] }, { name = "pydantic" }, @@ -85,6 +87,7 @@ requires-dist = [ { name = "fastapi", extras = ["standard"], specifier = ">=0.114.2,<1.0.0" }, { name = "httpx", specifier = ">=0.25.1,<1.0.0" }, { name = "jinja2", specifier = ">=3.1.4,<4.0.0" }, + { name = "openai", specifier = ">=1.67.0" }, { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.4,<2.0.0" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.1.13,<4.0.0" }, { name = "pydantic", specifier = ">2.0" }, @@ -220,7 +223,7 @@ name = "click" version = "8.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } wheels = [ @@ -325,6 +328,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 }, ] +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + [[package]] name = "dnspython" version = "2.6.1" @@ -581,6 +593,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, ] +[[package]] +name = "jiter" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/82/39f7c9e67b3b0121f02a0b90d433626caa95a565c3d2449fea6bcfa3f5f5/jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad", size = 314540 }, + { url = "https://files.pythonhosted.org/packages/01/07/7bf6022c5a152fca767cf5c086bb41f7c28f70cf33ad259d023b53c0b858/jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea", size = 321065 }, + { url = "https://files.pythonhosted.org/packages/6c/b2/de3f3446ecba7c48f317568e111cc112613da36c7b29a6de45a1df365556/jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51", size = 341664 }, + { url = "https://files.pythonhosted.org/packages/13/cf/6485a4012af5d407689c91296105fcdb080a3538e0658d2abf679619c72f/jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538", size = 364635 }, + { url = "https://files.pythonhosted.org/packages/0d/f7/4a491c568f005553240b486f8e05c82547340572d5018ef79414b4449327/jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d", size = 406288 }, + { url = "https://files.pythonhosted.org/packages/d3/ca/f4263ecbce7f5e6bded8f52a9f1a66540b270c300b5c9f5353d163f9ac61/jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12", size = 397499 }, + { url = "https://files.pythonhosted.org/packages/ac/a2/522039e522a10bac2f2194f50e183a49a360d5f63ebf46f6d890ef8aa3f9/jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51", size = 352926 }, + { url = "https://files.pythonhosted.org/packages/b1/67/306a5c5abc82f2e32bd47333a1c9799499c1c3a415f8dde19dbf876f00cb/jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708", size = 384506 }, + { url = "https://files.pythonhosted.org/packages/0f/89/c12fe7b65a4fb74f6c0d7b5119576f1f16c79fc2953641f31b288fad8a04/jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5", size = 520621 }, + { url = "https://files.pythonhosted.org/packages/c4/2b/d57900c5c06e6273fbaa76a19efa74dbc6e70c7427ab421bf0095dfe5d4a/jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678", size = 512613 }, + { url = "https://files.pythonhosted.org/packages/89/05/d8b90bfb21e58097d5a4e0224f2940568366f68488a079ae77d4b2653500/jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4", size = 206613 }, + { url = "https://files.pythonhosted.org/packages/2c/1d/5767f23f88e4f885090d74bbd2755518050a63040c0f59aa059947035711/jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322", size = 208371 }, + { url = "https://files.pythonhosted.org/packages/23/44/e241a043f114299254e44d7e777ead311da400517f179665e59611ab0ee4/jiter-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6c4d99c71508912a7e556d631768dcdef43648a93660670986916b297f1c54af", size = 314654 }, + { url = "https://files.pythonhosted.org/packages/fb/1b/a7e5e42db9fa262baaa9489d8d14ca93f8663e7f164ed5e9acc9f467fc00/jiter-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f60fb8ce7df529812bf6c625635a19d27f30806885139e367af93f6e734ef58", size = 320909 }, + { url = "https://files.pythonhosted.org/packages/60/bf/8ebdfce77bc04b81abf2ea316e9c03b4a866a7d739cf355eae4d6fd9f6fe/jiter-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c4e1a4f8ea84d98b7b98912aa4290ac3d1eabfde8e3c34541fae30e9d1f08b", size = 341733 }, + { url = "https://files.pythonhosted.org/packages/a8/4e/754ebce77cff9ab34d1d0fa0fe98f5d42590fd33622509a3ba6ec37ff466/jiter-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f4c677c424dc76684fea3e7285a7a2a7493424bea89ac441045e6a1fb1d7b3b", size = 365097 }, + { url = "https://files.pythonhosted.org/packages/32/2c/6019587e6f5844c612ae18ca892f4cd7b3d8bbf49461ed29e384a0f13d98/jiter-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2221176dfec87f3470b21e6abca056e6b04ce9bff72315cb0b243ca9e835a4b5", size = 406603 }, + { url = "https://files.pythonhosted.org/packages/da/e9/c9e6546c817ab75a1a7dab6dcc698e62e375e1017113e8e983fccbd56115/jiter-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c7adb66f899ffa25e3c92bfcb593391ee1947dbdd6a9a970e0d7e713237d572", size = 396625 }, + { url = "https://files.pythonhosted.org/packages/be/bd/976b458add04271ebb5a255e992bd008546ea04bb4dcadc042a16279b4b4/jiter-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98d27330fdfb77913c1097a7aab07f38ff2259048949f499c9901700789ac15", size = 351832 }, + { url = "https://files.pythonhosted.org/packages/07/51/fe59e307aaebec9265dbad44d9d4381d030947e47b0f23531579b9a7c2df/jiter-0.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eda3f8cc74df66892b1d06b5d41a71670c22d95a1ca2cbab73654745ce9d0419", size = 384590 }, + { url = "https://files.pythonhosted.org/packages/db/55/5dcd2693794d8e6f4889389ff66ef3be557a77f8aeeca8973a97a7c00557/jiter-0.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd5ab5ddc11418dce28343123644a100f487eaccf1de27a459ab36d6cca31043", size = 520690 }, + { url = "https://files.pythonhosted.org/packages/54/d5/9f51dc90985e9eb251fbbb747ab2b13b26601f16c595a7b8baba964043bd/jiter-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42f8a68a69f047b310319ef8e2f52fdb2e7976fb3313ef27df495cf77bcad965", size = 512649 }, + { url = "https://files.pythonhosted.org/packages/a6/e5/4e385945179bcf128fa10ad8dca9053d717cbe09e258110e39045c881fe5/jiter-0.9.0-cp311-cp311-win32.whl", hash = "sha256:a25519efb78a42254d59326ee417d6f5161b06f5da827d94cf521fed961b1ff2", size = 206920 }, + { url = "https://files.pythonhosted.org/packages/4c/47/5e0b94c603d8e54dd1faab439b40b832c277d3b90743e7835879ab663757/jiter-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:923b54afdd697dfd00d368b7ccad008cccfeb1efb4e621f32860c75e9f25edbd", size = 210119 }, + { url = "https://files.pythonhosted.org/packages/af/d7/c55086103d6f29b694ec79156242304adf521577530d9031317ce5338c59/jiter-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7b46249cfd6c48da28f89eb0be3f52d6fdb40ab88e2c66804f546674e539ec11", size = 309203 }, + { url = "https://files.pythonhosted.org/packages/b0/01/f775dfee50beb420adfd6baf58d1c4d437de41c9b666ddf127c065e5a488/jiter-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:609cf3c78852f1189894383cf0b0b977665f54cb38788e3e6b941fa6d982c00e", size = 319678 }, + { url = "https://files.pythonhosted.org/packages/ab/b8/09b73a793714726893e5d46d5c534a63709261af3d24444ad07885ce87cb/jiter-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d726a3890a54561e55a9c5faea1f7655eda7f105bd165067575ace6e65f80bb2", size = 341816 }, + { url = "https://files.pythonhosted.org/packages/35/6f/b8f89ec5398b2b0d344257138182cc090302854ed63ed9c9051e9c673441/jiter-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e89dc075c1fef8fa9be219e249f14040270dbc507df4215c324a1839522ea75", size = 364152 }, + { url = "https://files.pythonhosted.org/packages/9b/ca/978cc3183113b8e4484cc7e210a9ad3c6614396e7abd5407ea8aa1458eef/jiter-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e8ffa3c353b1bc4134f96f167a2082494351e42888dfcf06e944f2729cbe1d", size = 406991 }, + { url = "https://files.pythonhosted.org/packages/13/3a/72861883e11a36d6aa314b4922125f6ae90bdccc225cd96d24cc78a66385/jiter-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203f28a72a05ae0e129b3ed1f75f56bc419d5f91dfacd057519a8bd137b00c42", size = 395824 }, + { url = "https://files.pythonhosted.org/packages/87/67/22728a86ef53589c3720225778f7c5fdb617080e3deaed58b04789418212/jiter-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fca1a02ad60ec30bb230f65bc01f611c8608b02d269f998bc29cca8619a919dc", size = 351318 }, + { url = "https://files.pythonhosted.org/packages/69/b9/f39728e2e2007276806d7a6609cda7fac44ffa28ca0d02c49a4f397cc0d9/jiter-0.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:237e5cee4d5d2659aaf91bbf8ec45052cc217d9446070699441a91b386ae27dc", size = 384591 }, + { url = "https://files.pythonhosted.org/packages/eb/8f/8a708bc7fd87b8a5d861f1c118a995eccbe6d672fe10c9753e67362d0dd0/jiter-0.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:528b6b71745e7326eed73c53d4aa57e2a522242320b6f7d65b9c5af83cf49b6e", size = 520746 }, + { url = "https://files.pythonhosted.org/packages/95/1e/65680c7488bd2365dbd2980adaf63c562d3d41d3faac192ebc7ef5b4ae25/jiter-0.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9f48e86b57bc711eb5acdfd12b6cb580a59cc9a993f6e7dcb6d8b50522dcd50d", size = 512754 }, + { url = "https://files.pythonhosted.org/packages/78/f3/fdc43547a9ee6e93c837685da704fb6da7dba311fc022e2766d5277dfde5/jiter-0.9.0-cp312-cp312-win32.whl", hash = "sha256:699edfde481e191d81f9cf6d2211debbfe4bd92f06410e7637dffb8dd5dfde06", size = 207075 }, + { url = "https://files.pythonhosted.org/packages/cd/9d/742b289016d155f49028fe1bfbeb935c9bf0ffeefdf77daf4a63a42bb72b/jiter-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:099500d07b43f61d8bd780466d429c45a7b25411b334c60ca875fa775f68ccb0", size = 207999 }, + { url = "https://files.pythonhosted.org/packages/e7/1b/4cd165c362e8f2f520fdb43245e2b414f42a255921248b4f8b9c8d871ff1/jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7", size = 308197 }, + { url = "https://files.pythonhosted.org/packages/13/aa/7a890dfe29c84c9a82064a9fe36079c7c0309c91b70c380dc138f9bea44a/jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b", size = 318160 }, + { url = "https://files.pythonhosted.org/packages/6a/38/5888b43fc01102f733f085673c4f0be5a298f69808ec63de55051754e390/jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69", size = 341259 }, + { url = "https://files.pythonhosted.org/packages/3d/5e/bbdbb63305bcc01006de683b6228cd061458b9b7bb9b8d9bc348a58e5dc2/jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103", size = 363730 }, + { url = "https://files.pythonhosted.org/packages/75/85/53a3edc616992fe4af6814c25f91ee3b1e22f7678e979b6ea82d3bc0667e/jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635", size = 405126 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/1ee26b12b2693bd3f0b71d3188e4e5d817b12e3c630a09e099e0a89e28fa/jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4", size = 393668 }, + { url = "https://files.pythonhosted.org/packages/11/87/e084ce261950c1861773ab534d49127d1517b629478304d328493f980791/jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d", size = 352350 }, + { url = "https://files.pythonhosted.org/packages/f0/06/7dca84b04987e9df563610aa0bc154ea176e50358af532ab40ffb87434df/jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3", size = 384204 }, + { url = "https://files.pythonhosted.org/packages/16/2f/82e1c6020db72f397dd070eec0c85ebc4df7c88967bc86d3ce9864148f28/jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5", size = 520322 }, + { url = "https://files.pythonhosted.org/packages/36/fd/4f0cd3abe83ce208991ca61e7e5df915aa35b67f1c0633eb7cf2f2e88ec7/jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d", size = 512184 }, + { url = "https://files.pythonhosted.org/packages/a0/3c/8a56f6d547731a0b4410a2d9d16bf39c861046f91f57c98f7cab3d2aa9ce/jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53", size = 206504 }, + { url = "https://files.pythonhosted.org/packages/f4/1c/0c996fd90639acda75ed7fa698ee5fd7d80243057185dc2f63d4c1c9f6b9/jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7", size = 204943 }, + { url = "https://files.pythonhosted.org/packages/78/0f/77a63ca7aa5fed9a1b9135af57e190d905bcd3702b36aca46a01090d39ad/jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001", size = 317281 }, + { url = "https://files.pythonhosted.org/packages/f9/39/a3a1571712c2bf6ec4c657f0d66da114a63a2e32b7e4eb8e0b83295ee034/jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a", size = 350273 }, + { url = "https://files.pythonhosted.org/packages/ee/47/3729f00f35a696e68da15d64eb9283c330e776f3b5789bac7f2c0c4df209/jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf", size = 206867 }, +] + [[package]] name = "lxml" version = "5.3.0" @@ -790,6 +861,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] +[[package]] +name = "openai" +version = "1.67.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/63/6fd027fa4cb7c3b6bee4c3150f44803b3a7e4335f0b6e49e83a0c51c321b/openai-1.67.0.tar.gz", hash = "sha256:3b386a866396daa4bf80e05a891c50a7746ecd7863b8a27423b62136e3b8f6bc", size = 403596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/de/b42ddabe211411645105ae99ad93f4f3984f53be7ced2ad441378c27f62e/openai-1.67.0-py3-none-any.whl", hash = "sha256:dbbb144f38739fc0e1d951bc67864647fca0b9ffa05aef6b70eeea9f71d79663", size = 580168 }, +] + [[package]] name = "packaging" version = "24.1" @@ -1313,6 +1403,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, ] +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + [[package]] name = "typer" version = "0.12.5" From 2e32a387d881694e987332edd0a5a789011cd21d Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Fri, 21 Mar 2025 11:01:13 +0530 Subject: [PATCH 07/14] refactor testcases --- backend/app/tests/api/routes/test_threads.py | 43 ++++++++++---------- backend/pyproject.toml | 1 + backend/uv.lock | 2 + 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/backend/app/tests/api/routes/test_threads.py b/backend/app/tests/api/routes/test_threads.py index bf3ecb595..e915f0834 100644 --- a/backend/app/tests/api/routes/test_threads.py +++ b/backend/app/tests/api/routes/test_threads.py @@ -4,7 +4,7 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from app.api.routes.threads import router, process_run, validate_assistant_id -from models.thread import MessageRequest, AckPayload +from app.utils import APIResponse # Wrap the router in a FastAPI app instance. app = FastAPI() @@ -45,10 +45,10 @@ def test_threads_endpoint(mock_openai): response = client.post("/threads", json=request_data) assert response.status_code == 200 response_json = response.json() - assert response_json["status"] == "processing" - assert response_json["message"] == "Run started" - assert response_json["success"] == True - assert response_json["thread_id"] == "dummy_thread_id" + assert response_json["success"] is True + assert response_json["data"]["status"] == "processing" + assert response_json["data"]["message"] == "Run started" + assert response_json["data"]["thread_id"] == "dummy_thread_id" @patch("src.app.api.v1.threads.OpenAI") @@ -76,13 +76,13 @@ def test_process_run_variants(mock_openai, remove_citation, expected_message): mock_openai.return_value = mock_client # Create the request with the variable remove_citation flag. - request = MessageRequest( - question="What is Glific?", - assistant_id="assistant_123", - callback_url="http://example.com/callback", - thread_id="thread_123", - remove_citation=remove_citation, - ) + request = { + "question": "What is Glific?", + "assistant_id": "assistant_123", + "callback_url": "http://example.com/callback", + "thread_id": "thread_123", + "remove_citation": remove_citation, + } # Simulate a completed run. mock_run = MagicMock() @@ -102,10 +102,11 @@ def test_process_run_variants(mock_openai, remove_citation, expected_message): mock_send_callback.assert_called_once() callback_url, payload = mock_send_callback.call_args[0] print(payload) - assert callback_url == request.callback_url - assert payload.get("message", "") == expected_message - assert payload.get("status", "") == "success" - assert payload.get("thread_id", "") == "thread_123" + assert callback_url == request["callback_url"] + assert payload["data"]["message"] == expected_message + assert payload["data"]["status"] == "success" + assert payload["data"]["thread_id"] == "thread_123" + assert payload["success"] is True @patch("src.app.api.v1.threads.OpenAI") @@ -113,7 +114,7 @@ def test_validate_assistant_id(mock_openai): """ Test validate_assistant_id: - For a valid assistant ID, it should return None. - - For an invalid assistant ID, it should return an AckPayload with an error. + - For an invalid assistant ID, it should return an APIResponse with an error. """ mock_client = MagicMock() mock_openai.return_value = mock_client @@ -126,7 +127,7 @@ def test_validate_assistant_id(mock_openai): mock_client.beta.assistants.retrieve.side_effect = openai.NotFoundError( "Not found", response=MagicMock(), body={"message": "Not found"} ) - ack_payload = validate_assistant_id("invalid_assistant_id", mock_client) - assert isinstance(ack_payload, AckPayload) - assert ack_payload.status == "error" - assert "invalid_assistant_id" in ack_payload.message \ No newline at end of file + response = validate_assistant_id("invalid_assistant_id", mock_client) + assert isinstance(response, APIResponse) + assert response.success is False + assert "invalid_assistant_id" in response.error diff --git a/backend/pyproject.toml b/backend/pyproject.toml index e35dd3ce9..bfe699646 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "sentry-sdk[fastapi]<2.0.0,>=1.40.6", "pyjwt<3.0.0,>=2.8.0", "openai>=1.67.0", + "pytest>=7.4.4", ] [tool.uv] diff --git a/backend/uv.lock b/backend/uv.lock index b11935da0..48108f893 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -62,6 +62,7 @@ dependencies = [ { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyjwt" }, + { name = "pytest" }, { name = "python-multipart" }, { name = "sentry-sdk", extra = ["fastapi"] }, { name = "sqlmodel" }, @@ -93,6 +94,7 @@ requires-dist = [ { name = "pydantic", specifier = ">2.0" }, { name = "pydantic-settings", specifier = ">=2.2.1,<3.0.0" }, { name = "pyjwt", specifier = ">=2.8.0,<3.0.0" }, + { name = "pytest", specifier = ">=7.4.4" }, { name = "python-multipart", specifier = ">=0.0.7,<1.0.0" }, { name = "sentry-sdk", extras = ["fastapi"], specifier = ">=1.40.6,<2.0.0" }, { name = "sqlmodel", specifier = ">=0.0.21,<1.0.0" }, From 508138308859adc1aa0e349d88fcfbe64b19363f Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 24 Mar 2025 12:52:37 +0530 Subject: [PATCH 08/14] setting up init.py --- backend/app/api/routes/threads.py | 5 ++--- backend/app/core/__init__.py | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 5b2aba744..8b03fbb8c 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -4,8 +4,7 @@ from openai import OpenAI from fastapi import APIRouter, BackgroundTasks from app.utils import APIResponse -from ...core.config import settings -from ...core.logger import logging +from app.core import settings, logging logger = logging.getLogger(__name__) router = APIRouter(tags=["threads"]) @@ -16,7 +15,7 @@ def send_callback(callback_url: str, data: dict): try: session = requests.Session() # uncomment this to run locally without SSL - # session.verify = False + session.verify = False response = session.post(callback_url, json=data) response.raise_for_status() return True diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py index e69de29bb..8a64c3009 100644 --- a/backend/app/core/__init__.py +++ b/backend/app/core/__init__.py @@ -0,0 +1,4 @@ +from .config import settings +from .logger import logging + +__all__ = ['settings', 'logging'] From d45984a6705b97e76bb6febb024b146a336b1dbe Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 24 Mar 2025 13:18:07 +0530 Subject: [PATCH 09/14] fixing review comments --- backend/app/api/routes/threads.py | 5 +++-- backend/app/core/config.py | 4 ++++ backend/app/core/logger.py | 8 +++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 8b03fbb8c..0f5fbdf88 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -1,8 +1,10 @@ -import openai import re import requests + +import openai from openai import OpenAI from fastapi import APIRouter, BackgroundTasks + from app.utils import APIResponse from app.core import settings, logging @@ -81,7 +83,6 @@ def validate_assistant_id(assistant_id: str, client: OpenAI): client.beta.assistants.retrieve(assistant_id=assistant_id) except openai.NotFoundError: return APIResponse.failure_response(error=f"Invalid assistant ID provided {assistant_id}") - return None @router.post("/threads") diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 38ca5e9ec..5e20683bb 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,5 +1,6 @@ import secrets import warnings +import os from typing import Annotated, Any, Literal from pydantic import ( @@ -96,6 +97,9 @@ def emails_enabled(self) -> bool: FIRST_SUPERUSER: EmailStr FIRST_SUPERUSER_PASSWORD: str + LOG_DIR: str = os.path.join(os.path.dirname( + os.path.dirname(__file__)), "logs") + def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": message = ( diff --git a/backend/app/core/logger.py b/backend/app/core/logger.py index c2aba337f..70605b5a1 100644 --- a/backend/app/core/logger.py +++ b/backend/app/core/logger.py @@ -1,8 +1,9 @@ import logging import os from logging.handlers import RotatingFileHandler +from app.core.config import settings -LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "logs") +LOG_DIR = settings.LOG_DIR if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) @@ -13,8 +14,9 @@ logging.basicConfig(level=LOGGING_LEVEL, format=LOGGING_FORMAT) -file_handler = RotatingFileHandler(LOG_FILE_PATH, maxBytes=10485760, backupCount=5) +file_handler = RotatingFileHandler( + LOG_FILE_PATH, maxBytes=10485760, backupCount=5) file_handler.setLevel(LOGGING_LEVEL) file_handler.setFormatter(logging.Formatter(LOGGING_FORMAT)) -logging.getLogger("").addHandler(file_handler) \ No newline at end of file +logging.getLogger("").addHandler(file_handler) From 941a81c8eb754ad3dbef9ebc1c4d83da534ac4b2 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 24 Mar 2025 13:22:25 +0530 Subject: [PATCH 10/14] cleanup --- backend/app/logs/app.log | 11 +++++++++++ backend/app/tests/api/routes/test_threads.py | 2 ++ 2 files changed, 13 insertions(+) create mode 100644 backend/app/logs/app.log diff --git a/backend/app/logs/app.log b/backend/app/logs/app.log new file mode 100644 index 000000000..f62d94bf4 --- /dev/null +++ b/backend/app/logs/app.log @@ -0,0 +1,11 @@ +2025-03-24 13:20:41,986 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" +2025-03-24 13:20:43,070 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs "HTTP/1.1 200 OK" +2025-03-24 13:20:44,300 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/messages "HTTP/1.1 200 OK" +2025-03-24 13:20:45,665 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs "HTTP/1.1 200 OK" +2025-03-24 13:20:46,737 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" +2025-03-24 13:20:48,293 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" +2025-03-24 13:20:49,851 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" +2025-03-24 13:20:51,364 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" +2025-03-24 13:20:51,765 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/messages "HTTP/1.1 200 OK" +2025-03-24 13:21:51,596 - watchfiles.main - INFO - 1 change detected +2025-03-24 13:21:52,106 - watchfiles.main - INFO - 2 changes detected diff --git a/backend/app/tests/api/routes/test_threads.py b/backend/app/tests/api/routes/test_threads.py index e915f0834..292486df8 100644 --- a/backend/app/tests/api/routes/test_threads.py +++ b/backend/app/tests/api/routes/test_threads.py @@ -1,8 +1,10 @@ import pytest import openai + from unittest.mock import MagicMock, patch from fastapi import FastAPI from fastapi.testclient import TestClient + from app.api.routes.threads import router, process_run, validate_assistant_id from app.utils import APIResponse From ad85de8eb43beb444a0ea8bb8efdde8793d53f29 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Mon, 24 Mar 2025 13:22:49 +0530 Subject: [PATCH 11/14] cleanup --- backend/app/logs/app.log | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 backend/app/logs/app.log diff --git a/backend/app/logs/app.log b/backend/app/logs/app.log deleted file mode 100644 index f62d94bf4..000000000 --- a/backend/app/logs/app.log +++ /dev/null @@ -1,11 +0,0 @@ -2025-03-24 13:20:41,986 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/assistants/asst_fz7oIQ2goRLfrP1mWceBcjje "HTTP/1.1 200 OK" -2025-03-24 13:20:43,070 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs "HTTP/1.1 200 OK" -2025-03-24 13:20:44,300 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/messages "HTTP/1.1 200 OK" -2025-03-24 13:20:45,665 - httpx - INFO - HTTP Request: POST https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs "HTTP/1.1 200 OK" -2025-03-24 13:20:46,737 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" -2025-03-24 13:20:48,293 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" -2025-03-24 13:20:49,851 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" -2025-03-24 13:20:51,364 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/runs/run_nCyrWmiEVvemb1Q1twBKIw6c "HTTP/1.1 200 OK" -2025-03-24 13:20:51,765 - httpx - INFO - HTTP Request: GET https://api.openai.com/v1/threads/thread_pQFdoYYHxaQ5j5lV9G8Ce3P3/messages "HTTP/1.1 200 OK" -2025-03-24 13:21:51,596 - watchfiles.main - INFO - 1 change detected -2025-03-24 13:21:52,106 - watchfiles.main - INFO - 2 changes detected From 13d913a733d504500e331282ac9cdf00e4b4a9d6 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 26 Mar 2025 09:56:06 +0530 Subject: [PATCH 12/14] removed validate thread as it can be handled by default --- backend/app/api/routes/threads.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index 0f5fbdf88..ad742e2ae 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -78,13 +78,6 @@ def process_run(request: dict, client: OpenAI): send_callback(request["callback_url"], callback_response.model_dump()) -def validate_assistant_id(assistant_id: str, client: OpenAI): - try: - client.beta.assistants.retrieve(assistant_id=assistant_id) - except openai.NotFoundError: - return APIResponse.failure_response(error=f"Invalid assistant ID provided {assistant_id}") - - @router.post("/threads") async def threads(request: dict, background_tasks: BackgroundTasks): """ @@ -93,10 +86,7 @@ async def threads(request: dict, background_tasks: BackgroundTasks): Once completed, calls send_callback with the final result. """ client = OpenAI(api_key=settings.OPENAI_API_KEY) - assistant_error = validate_assistant_id(request["assistant_id"], client) - if assistant_error: - return assistant_error - + # Use get method to safely access thread_id thread_id = request.get("thread_id") From 146341ee150ae400fde1633213c44396a6047855 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 26 Mar 2025 10:00:27 +0530 Subject: [PATCH 13/14] fixing few code review suggestions --- backend/app/api/routes/threads.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/app/api/routes/threads.py b/backend/app/api/routes/threads.py index ad742e2ae..9da6b3373 100644 --- a/backend/app/api/routes/threads.py +++ b/backend/app/api/routes/threads.py @@ -17,7 +17,7 @@ def send_callback(callback_url: str, data: dict): try: session = requests.Session() # uncomment this to run locally without SSL - session.verify = False + # session.verify = False response = session.post(callback_url, json=data) response.raise_for_status() return True @@ -44,7 +44,9 @@ def process_run(request: dict, client: OpenAI): latest_message = messages.data[0] message_content = latest_message.content[0].text.value - if request["remove_citation"]: + remove_citation = request.get("remove_citation", False) + + if remove_citation: message = re.sub(r"【\d+(?::\d+)?†[^】]*】", "", message_content) else: message = message_content From 7c79deaf4b6c2a449f47de17948c603430848780 Mon Sep 17 00:00:00 2001 From: Akhilesh Negi Date: Wed, 26 Mar 2025 10:16:54 +0530 Subject: [PATCH 14/14] removed validation testcases for assistant ID --- backend/app/tests/api/routes/test_threads.py | 28 ++------------------ 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/backend/app/tests/api/routes/test_threads.py b/backend/app/tests/api/routes/test_threads.py index 292486df8..fee3f30ef 100644 --- a/backend/app/tests/api/routes/test_threads.py +++ b/backend/app/tests/api/routes/test_threads.py @@ -5,7 +5,7 @@ from fastapi import FastAPI from fastapi.testclient import TestClient -from app.api.routes.threads import router, process_run, validate_assistant_id +from app.api.routes.threads import router, process_run from app.utils import APIResponse # Wrap the router in a FastAPI app instance. @@ -108,28 +108,4 @@ def test_process_run_variants(mock_openai, remove_citation, expected_message): assert payload["data"]["message"] == expected_message assert payload["data"]["status"] == "success" assert payload["data"]["thread_id"] == "thread_123" - assert payload["success"] is True - - -@patch("src.app.api.v1.threads.OpenAI") -def test_validate_assistant_id(mock_openai): - """ - Test validate_assistant_id: - - For a valid assistant ID, it should return None. - - For an invalid assistant ID, it should return an APIResponse with an error. - """ - mock_client = MagicMock() - mock_openai.return_value = mock_client - - # Simulate a valid assistant ID. - result_valid = validate_assistant_id("valid_assistant_id", mock_client) - assert result_valid is None - - # Simulate an invalid assistant ID by raising NotFoundError with required kwargs. - mock_client.beta.assistants.retrieve.side_effect = openai.NotFoundError( - "Not found", response=MagicMock(), body={"message": "Not found"} - ) - response = validate_assistant_id("invalid_assistant_id", mock_client) - assert isinstance(response, APIResponse) - assert response.success is False - assert "invalid_assistant_id" in response.error + assert payload["success"] is True \ No newline at end of file