From 6596d5c799b04b5775f8caad6fc9a0b95d60a12c Mon Sep 17 00:00:00 2001 From: Graham Neubig Date: Fri, 31 May 2024 15:00:09 -0400 Subject: [PATCH] Fix: Feedback should be sent through the backend to avoid CORS issues (#2046) * Fix: Feedback should be sent through the backend to avoid CORS issues * Update * Fix merge error * Revert unnecessary change * Lint * Moved to services * Fixed bugs --------- Co-authored-by: OpenDevin --- frontend/src/api/index.ts | 19 ------------ .../src/components/chat/ChatInterface.tsx | 5 ++- frontend/src/services/feedbackService.ts | 19 ++++++++++++ opendevin/server/data_models/feedback.py | 31 +++++++++++++++++++ opendevin/server/listen.py | 26 ++++++++++++++++ 5 files changed, 80 insertions(+), 20 deletions(-) delete mode 100644 frontend/src/api/index.ts create mode 100644 frontend/src/services/feedbackService.ts create mode 100644 opendevin/server/data_models/feedback.py diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts deleted file mode 100644 index c6a83e14cb..0000000000 --- a/frontend/src/api/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface FeedbackData { - email: string; - token: string; - feedback: "positive" | "negative"; - trajectory: unknown[]; -} - -export const sendFeedback = async (data: FeedbackData) => - fetch( - "https://kttkfkoju5.execute-api.us-east-2.amazonaws.com/od-share-trajectory", - { - method: "POST", - mode: "no-cors", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }, - ); diff --git a/frontend/src/components/chat/ChatInterface.tsx b/frontend/src/components/chat/ChatInterface.tsx index 41b8e40636..c12b213b58 100644 --- a/frontend/src/components/chat/ChatInterface.tsx +++ b/frontend/src/components/chat/ChatInterface.tsx @@ -17,8 +17,8 @@ import { useScrollToBottom } from "#/hooks/useScrollToBottom"; import Session from "#/services/session"; import { getToken } from "#/services/auth"; import toast from "#/utils/toast"; -import { FeedbackData, sendFeedback } from "#/api"; import { removeApiKey } from "#/utils/utils"; +import { FeedbackData, sendFeedback } from "#/services/feedbackService"; interface ScrollButtonProps { onClick: () => void; @@ -57,10 +57,13 @@ function ChatInterface() { const [feedbackLoading, setFeedbackLoading] = React.useState(false); const shareFeedback = async (feedback: "positive" | "negative") => { + // TODO: implement email and permissions + // https://github.com/OpenDevin/OpenDevin/issues/2033 const data: FeedbackData = { email: "NOT_PROVIDED", token: getToken(), feedback, + permissions: "private", trajectory: removeApiKey(Session._history), }; diff --git a/frontend/src/services/feedbackService.ts b/frontend/src/services/feedbackService.ts new file mode 100644 index 0000000000..0dc2d667ea --- /dev/null +++ b/frontend/src/services/feedbackService.ts @@ -0,0 +1,19 @@ +import { request } from "./api"; + +export interface FeedbackData { + email: string; + token: string; + feedback: "positive" | "negative"; + permissions: "public" | "private"; + trajectory: unknown[]; +} + +export async function sendFeedback(data: FeedbackData) { + await request("/api/submit-feedback", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); +} diff --git a/opendevin/server/data_models/feedback.py b/opendevin/server/data_models/feedback.py new file mode 100644 index 0000000000..cf0c2651a4 --- /dev/null +++ b/opendevin/server/data_models/feedback.py @@ -0,0 +1,31 @@ +from typing import Any, Literal + +import requests +from pydantic import BaseModel + +from opendevin.core.logger import opendevin_logger as logger + + +class FeedbackDataModel(BaseModel): + email: str + token: str + feedback: Literal['positive', 'negative'] + permissions: Literal['public', 'private'] + trajectory: list[dict[str, Any]] + + +FEEDBACK_URL = ( + 'https://kttkfkoju5.execute-api.us-east-2.amazonaws.com/od-share-trajectory' +) + + +def store_feedback(feedback: FeedbackDataModel): + logger.info(f'Got feedback: {feedback.model_dump_json()}') + response = requests.post( + FEEDBACK_URL, + headers={'Content-Type': 'application/json'}, + json=feedback.model_dump(), + ) + logger.info(f'Stored feedback: {response.status_code} {response.text}') + if response.status_code != 200: + raise ValueError(f'Failed to store feedback: {response.text}') diff --git a/opendevin/server/listen.py b/opendevin/server/listen.py index 48e67522e8..c855f721ba 100644 --- a/opendevin/server/listen.py +++ b/opendevin/server/listen.py @@ -1,6 +1,8 @@ import uuid import warnings +from opendevin.server.data_models.feedback import FeedbackDataModel, store_feedback + with warnings.catch_warnings(): warnings.simplefilter('ignore') import litellm @@ -279,6 +281,30 @@ async def upload_file(request: Request, files: list[UploadFile]): return {'message': 'Files uploaded successfully', 'file_count': len(files)} +@app.post('/api/submit-feedback') +async def submit_feedback(request: Request, feedback: FeedbackDataModel): + """ + Upload files to the workspace. + + To upload files: + ```sh + curl -X POST -F "email=test@example.com" -F "token=abc" -F "feedback=positive" -F "permissions=private" -F "trajectory={}" http://localhost:3000/api/submit-feedback + ``` + """ + # Assuming the storage service is already configured in the backend + # and there is a function to handle the storage. + try: + store_feedback(feedback) + return JSONResponse( + status_code=200, content={'message': 'Feedback submitted successfully'} + ) + except Exception as e: + logger.error(f'Error submitting feedback: {e}') + return JSONResponse( + status_code=500, content={'error': 'Failed to submit feedback'} + ) + + @app.get('/api/root_task') def get_root_task(request: Request): """