From 80d0aef5987810b620e56bac83d3170a9b6f8af8 Mon Sep 17 00:00:00 2001 From: Ashutosh619-sudo Date: Mon, 9 Sep 2024 19:55:14 +0530 Subject: [PATCH 1/6] Integration Helper Hackathon --- apps/integration_helper/__init__.py | 0 apps/integration_helper/admin.py | 6 + apps/integration_helper/apps.py | 5 + .../migrations/0001_initial.py | 27 ++++ .../integration_helper/migrations/__init__.py | 0 apps/integration_helper/models.py | 14 ++ apps/integration_helper/openai_utils.py | 25 ++++ apps/integration_helper/prompt.py | 124 ++++++++++++++++++ apps/integration_helper/tests.py | 3 + apps/integration_helper/urls.py | 10 ++ apps/integration_helper/views.py | 93 +++++++++++++ apps/workspaces/urls.py | 1 + quickbooks_desktop_api/settings.py | 3 +- requirements.txt | 2 + 14 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 apps/integration_helper/__init__.py create mode 100644 apps/integration_helper/admin.py create mode 100644 apps/integration_helper/apps.py create mode 100644 apps/integration_helper/migrations/0001_initial.py create mode 100644 apps/integration_helper/migrations/__init__.py create mode 100644 apps/integration_helper/models.py create mode 100644 apps/integration_helper/openai_utils.py create mode 100644 apps/integration_helper/prompt.py create mode 100644 apps/integration_helper/tests.py create mode 100644 apps/integration_helper/urls.py create mode 100644 apps/integration_helper/views.py diff --git a/apps/integration_helper/__init__.py b/apps/integration_helper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/integration_helper/admin.py b/apps/integration_helper/admin.py new file mode 100644 index 0000000..45277f5 --- /dev/null +++ b/apps/integration_helper/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin + +# Register your models here. +from apps.integration_helper.models import Conversation + +admin.site.register(Conversation) diff --git a/apps/integration_helper/apps.py b/apps/integration_helper/apps.py new file mode 100644 index 0000000..0154b90 --- /dev/null +++ b/apps/integration_helper/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class IntegrationHelperConfig(AppConfig): + name = 'apps.integration_helper' diff --git a/apps/integration_helper/migrations/0001_initial.py b/apps/integration_helper/migrations/0001_initial.py new file mode 100644 index 0000000..97186d7 --- /dev/null +++ b/apps/integration_helper/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1.14 on 2024-09-09 14:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Conversation', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('conversation_id', models.CharField(help_text='Unique id of the conversation', max_length=255)), + ('role', models.CharField(help_text='Role of the messenger', max_length=255)), + ('content', models.TextField(help_text='Content of the message')), + ('created_at', models.DateTimeField(auto_now_add=True, help_text='Created at datetime')), + ], + options={ + 'db_table': 'conversations', + }, + ), + ] diff --git a/apps/integration_helper/migrations/__init__.py b/apps/integration_helper/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/integration_helper/models.py b/apps/integration_helper/models.py new file mode 100644 index 0000000..16c986b --- /dev/null +++ b/apps/integration_helper/models.py @@ -0,0 +1,14 @@ +from django.db import models + + +class Conversation(models.Model): + id = models.AutoField(primary_key=True) + conversation_id = models.CharField(max_length=255, help_text="Unique id of the conversation") + role = models.CharField(max_length=255, help_text="Role of the messenger") + content = models.TextField(help_text="Content of the message") + created_at = models.DateTimeField( + auto_now_add=True, help_text="Created at datetime" + ) + + class Meta: + db_table = 'conversations' diff --git a/apps/integration_helper/openai_utils.py b/apps/integration_helper/openai_utils.py new file mode 100644 index 0000000..c416d4f --- /dev/null +++ b/apps/integration_helper/openai_utils.py @@ -0,0 +1,25 @@ +import os +from openai import OpenAI +from apps.integration_helper.prompt import PROMPT +import json + + +# OpenAI API Key Setup + +def get_openai_response(messages): + """ + Send the conversation history (messages) to OpenAI and get a response. + """ + api_key = os.getenv("OPENAI_API_KEY") + client = OpenAI( + api_key=api_key + ) + response = client.chat.completions.create( + model="gpt-4o-mini", + messages=messages, + response_format={"type": "json_object"}, + max_tokens=1000, + temperature=0, + ) + + return json.loads(response.choices[0].message.content) diff --git a/apps/integration_helper/prompt.py b/apps/integration_helper/prompt.py new file mode 100644 index 0000000..f0531e4 --- /dev/null +++ b/apps/integration_helper/prompt.py @@ -0,0 +1,124 @@ +PROMPT = """ +You are an Expense Management software assistant designed to help users conversationally set up their QuickBooks Desktop Integration. Your goal is to ask the user questions about their preferences, gather their responses, and ultimately return a final JSON payload that reflects their settings. + +========================================================================================================================= +STEP 1: Export Settings + +Your first task is to guide the user through the export settings for both Reimbursable Expenses and Credit Card Expenses. You must first determine the Export Type for each before proceeding with the sub-questions. The user can choose one or both categories, and for any category they don’t select, return `null` for the fields related to that category. + +### For Reimbursable Expenses (They can choose either Bills or Journal Entries as Export Type): +- If they choose **Bills**, ask: + - What is the name of the bank account you want to use for Accounts Payable? + - What is the name of the Mileage Account (if applicable)? (This is optional) + - The rest of the settings will be hardcoded and skipped: + - Expenses will be grouped by "REPORT" + - The Date of Export will be based on the last expense spent + - The state will be "PAYMENT_PROCESSING" + +- If they choose **Journal Entries**, ask: + - What is the name of the bank account you want to use for Accounts Payable? + - What is the name of the Mileage Account (if applicable)? (This is optional) + - The same hardcoded settings will apply as above. + +### For Card Expenses (They can choose either Credit Card Purchases or Journal Entries as Export Type): +- If they choose **Credit Card Purchases**, ask: + - What is the name of the Credit Card Account you want to use? + - The rest of the settings will be hardcoded and skipped: + - Expenses will always be grouped by "EXPENSE" + - The Date of Export will always be the spend date, no user input required + - The state always will be "APPROVED", no user input required + +- If they choose **Journal Entries**, ask: + - What is the name of the Credit Card Account you want to use? + - The same hardcoded settings will apply as above. + +========================================================================================================================= +STEP 2: Field Mapping + +Next, you'll ask the user if they want to map Projects and Classes to their expenses. + +- If they choose to map **Projects**, you will hardcode it to "Project". +- If they choose to map **Classes**, you will hardcode it to "Cost Center". +- The **Item** field will not be asked and will always be returned as `null`. + +========================================================================================================================= +STEP 3: Advanced Settings + +Lastly, you'll guide the user through the advanced settings where they can choose to schedule the export. Ask if they want to enable the scheduling feature, and if so, prompt them to set the frequency. The options are Daily, Weekly, or Monthly: + +- **Daily**: Ask for the time of day. +- **Weekly**: Ask for the day of the week and time of day. +- **Monthly**: Ask for the day of the month and time of day. + +Other advanced settings will be hardcoded and should not be asked: +- Emails will default to an empty list. +- Top Memo Structure will be set to include "employee_email". +- Expense Memo Structure will be set to include "employee_email", "merchant", "purpose", "category", "spent_on", "report_number", and "expense_link". + +========================================================================================================================= +FINAL OUTPUT: + +Your responses can only be in the form of below JSONs: + +For CONVERATION: +{ + "output_type": "CONVERSATION", // FINAL for the FINAL JSON PAYLOAD and CONVERSATION for questions + "output": { + "question": "What is the name of the bank account you want to use for Accounts Payable?", // this question is just an example + } +} + +For FINAL: +{ + "output_type": "FINAL", // FINAL for the FINAL JSON PAYLOAD and CONVERSATION for questions + "output_export_settings": { + "reimbursable_expenses_export_type": "BILL", + "bank_account_name": "Accounts Payable", + "mileage_account_name": "Mileage", + "reimbursable_expense_state": "PAYMENT_PROCESSING", + "reimbursable_expense_date": "last_spent_at", + "reimbursable_expense_grouped_by": "REPORT", + "credit_card_expense_export_type": "CREDIT_CARD_PURCHASE", + "credit_card_expense_state": "APPROVED", + "credit_card_entity_name_preference": "VENDOR", + "credit_card_account_name": "Capital One 2222", + "credit_card_expense_grouped_by": "EXPENSE", + "credit_card_expense_date": "spent_at" + }, + "output_field_mapping": { + "class_type": "COST_CENTER", + "project_type": "PROJECT", + "item_type": null + }, + "output_advanced_settings": { + "expense_memo_structure": [ + "employee_email", + "merchant", + "purpose", + "category", + "spent_on", + "report_number", + "expense_link" + ], + "top_memo_structure": [ + "employee_email" + ], + "schedule_is_enabled": true, + "emails_selected": [], + "day_of_month": null, + "day_of_week": null, + "frequency": "DAILY", + "time_of_day": "12:00:00" + } +} + +========================================================================================================================= + +Ensure the following guidelines: + +1. **Ask Questions Step-by-Step:** Return one question at a time in JSON format unless the user provides all information at once. +2. **If User Provides Info:** Respond with the next appropriate question. +3. **Final Output:** Once all questions are answered, output the final JSON payload as specified. +4. **Return JSON:** Return all the keys even if the value is `null`. + +""" diff --git a/apps/integration_helper/tests.py b/apps/integration_helper/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/integration_helper/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/integration_helper/urls.py b/apps/integration_helper/urls.py new file mode 100644 index 0000000..7a3304b --- /dev/null +++ b/apps/integration_helper/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import ConversationViewSet + +router = DefaultRouter() +router.register(r'conversation', ConversationViewSet, basename='conversation') + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/apps/integration_helper/views.py b/apps/integration_helper/views.py new file mode 100644 index 0000000..b0003be --- /dev/null +++ b/apps/integration_helper/views.py @@ -0,0 +1,93 @@ +import uuid +from rest_framework import viewsets, status +from rest_framework.response import Response +from rest_framework.decorators import action +from apps.integration_helper.models import Conversation +from apps.integration_helper.openai_utils import get_openai_response +from apps.integration_helper.prompt import PROMPT +from fyle_accounting_mappings.models import DestinationAttribute + + +class ConversationViewSet(viewsets.ViewSet): + """ + ViewSet for creating, retrieving, adding messages, and clearing conversations. + """ + + def create(self, request, *args, **kwargs): + """ + Create a new conversation and get the first OpenAI response. + """ + content = request.data.get('content') + + if not content: + return Response({"error": "content are required"}, status=status.HTTP_400_BAD_REQUEST) + + conversation_id = str(uuid.uuid4()) + + conversation = Conversation.objects.create( + conversation_id=conversation_id, role='system', content=PROMPT + ) + + conversation = Conversation.objects.create( + conversation_id=conversation_id, role='user', content=content + ) + messages = [{"role": "system", "content": PROMPT}, {"role": "user", "content": content}] + + assistant_response = get_openai_response(messages) + + Conversation.objects.create(conversation_id=conversation_id, role="assistant", content=assistant_response) + + return Response({ + 'conversation_id': conversation.conversation_id, + 'assistant_response': assistant_response + }, status=status.HTTP_201_CREATED) + + + @action(detail=True, methods=["post"]) + def add_message(self, request, pk=None, *args, **kwargs): + """ + Add a new message to an existing conversation using conversation_id and get an OpenAI response. + """ + content = request.data.get("content") + conversation_id = pk + + if not content: + return Response( + {"error": "content are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not Conversation.objects.filter(conversation_id=pk).first(): + return Response( + {"error": "Conversation id doesn't exists!"} + ) + + messages = list(Conversation.objects.filter(conversation_id=conversation_id).values("role", "content").order_by('created_at')) + + messages.append({"role": "user", "content": content}) + Conversation.objects.create(conversation_id=conversation_id, role="user", content=content) + + + assistant_response = get_openai_response(messages) + Conversation.objects.create(conversation_id=conversation_id, role="assistant", content=assistant_response) + + return Response( + {"assistant_response": assistant_response}, status=status.HTTP_201_CREATED + ) + + + @action(detail=True, methods=["delete"]) + def clear(self, request, pk=None): + """ + Clear the conversation history by deleting it using conversation_id. + """ + conversation = Conversation.objects.filter(conversation_id=pk) + if conversation.exists(): + conversation.delete() + return Response( + {"message": "Conversation cleared"}, status=status.HTTP_200_OK + ) + + return Response( + {"error": "Conversation not found"}, status=status.HTTP_404_NOT_FOUND + ) diff --git a/apps/workspaces/urls.py b/apps/workspaces/urls.py index 7b88a3e..58b0fd4 100644 --- a/apps/workspaces/urls.py +++ b/apps/workspaces/urls.py @@ -34,4 +34,5 @@ path('/accounting_exports/', include('apps.tasks.urls')), path('/qbd_mappings/', include('apps.mappings.urls')), path('/fyle/', include('apps.fyle.urls')), + path('/integration_helper/', include('apps.integration_helper.urls')) ] diff --git a/quickbooks_desktop_api/settings.py b/quickbooks_desktop_api/settings.py index d7579ca..27e095b 100644 --- a/quickbooks_desktop_api/settings.py +++ b/quickbooks_desktop_api/settings.py @@ -59,7 +59,8 @@ 'apps.fyle', 'apps.tasks', 'apps.qbd', - 'apps.mappings' + 'apps.mappings', + 'apps.integration_helper' ] MIDDLEWARE = [ diff --git a/requirements.txt b/requirements.txt index d6669a8..d81ed0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,3 +49,5 @@ pytest-mock==3.8.2 # Sendgrid for sending emails_selected sendgrid==6.9.7 sentry-sdk==1.19.1 + +openai From 88fb968544a0ef9751b9ed09b8498c94e441497b Mon Sep 17 00:00:00 2001 From: Ashutosh619-sudo Date: Mon, 9 Sep 2024 20:32:26 +0530 Subject: [PATCH 2/6] workspace_id added to conversations --- .../0002_conversation_workspace_id.py | 19 +++++++++++++++ apps/integration_helper/models.py | 1 + apps/integration_helper/views.py | 23 +++++++++++-------- 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 apps/integration_helper/migrations/0002_conversation_workspace_id.py diff --git a/apps/integration_helper/migrations/0002_conversation_workspace_id.py b/apps/integration_helper/migrations/0002_conversation_workspace_id.py new file mode 100644 index 0000000..7b53576 --- /dev/null +++ b/apps/integration_helper/migrations/0002_conversation_workspace_id.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.14 on 2024-09-09 14:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('integration_helper', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='conversation', + name='workspace_id', + field=models.IntegerField(help_text='Workspace id of the organization'), + preserve_default=False, + ), + ] diff --git a/apps/integration_helper/models.py b/apps/integration_helper/models.py index 16c986b..45a74ce 100644 --- a/apps/integration_helper/models.py +++ b/apps/integration_helper/models.py @@ -4,6 +4,7 @@ class Conversation(models.Model): id = models.AutoField(primary_key=True) conversation_id = models.CharField(max_length=255, help_text="Unique id of the conversation") + workspace_id = models.IntegerField(help_text="Workspace id of the organization") role = models.CharField(max_length=255, help_text="Role of the messenger") content = models.TextField(help_text="Content of the message") created_at = models.DateTimeField( diff --git a/apps/integration_helper/views.py b/apps/integration_helper/views.py index b0003be..807efc2 100644 --- a/apps/integration_helper/views.py +++ b/apps/integration_helper/views.py @@ -18,6 +18,7 @@ def create(self, request, *args, **kwargs): Create a new conversation and get the first OpenAI response. """ content = request.data.get('content') + workspace_id = kwargs['workspace_id'] if not content: return Response({"error": "content are required"}, status=status.HTTP_400_BAD_REQUEST) @@ -25,17 +26,17 @@ def create(self, request, *args, **kwargs): conversation_id = str(uuid.uuid4()) conversation = Conversation.objects.create( - conversation_id=conversation_id, role='system', content=PROMPT + conversation_id=conversation_id, workspace_id=workspace_id, role='system', content=PROMPT ) conversation = Conversation.objects.create( - conversation_id=conversation_id, role='user', content=content + conversation_id=conversation_id, workspace_id=workspace_id, role='user', content=content ) messages = [{"role": "system", "content": PROMPT}, {"role": "user", "content": content}] assistant_response = get_openai_response(messages) - Conversation.objects.create(conversation_id=conversation_id, role="assistant", content=assistant_response) + Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="assistant", content=assistant_response) return Response({ 'conversation_id': conversation.conversation_id, @@ -50,6 +51,7 @@ def add_message(self, request, pk=None, *args, **kwargs): """ content = request.data.get("content") conversation_id = pk + workspace_id = kwargs['workspace_id'] if not content: return Response( @@ -62,14 +64,14 @@ def add_message(self, request, pk=None, *args, **kwargs): {"error": "Conversation id doesn't exists!"} ) - messages = list(Conversation.objects.filter(conversation_id=conversation_id).values("role", "content").order_by('created_at')) + messages = list(Conversation.objects.filter(conversation_id=conversation_id, workspace_id=workspace_id).values("role", "content").order_by('created_at')) messages.append({"role": "user", "content": content}) - Conversation.objects.create(conversation_id=conversation_id, role="user", content=content) + Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="user", content=content) assistant_response = get_openai_response(messages) - Conversation.objects.create(conversation_id=conversation_id, role="assistant", content=assistant_response) + Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="assistant", content=assistant_response) return Response( {"assistant_response": assistant_response}, status=status.HTTP_201_CREATED @@ -77,13 +79,14 @@ def add_message(self, request, pk=None, *args, **kwargs): @action(detail=True, methods=["delete"]) - def clear(self, request, pk=None): + def clear(self, request, pk=None, *args, **kwargs): """ Clear the conversation history by deleting it using conversation_id. """ - conversation = Conversation.objects.filter(conversation_id=pk) - if conversation.exists(): - conversation.delete() + workspace_id = kwargs['workspace_id'] + conversations = Conversation.objects.filter(conversation_id=pk, workspace_id=workspace_id) + if conversations.exists(): + conversations.delete() return Response( {"message": "Conversation cleared"}, status=status.HTTP_200_OK ) From 4b8877340307be6862f75a065619890a9ab4ca2b Mon Sep 17 00:00:00 2001 From: Shwetabh Kumar Date: Thu, 12 Sep 2024 11:51:08 +0530 Subject: [PATCH 3/6] feat: Moving to generics and single message API --- apps/integration_helper/urls.py | 9 +-- apps/integration_helper/views.py | 107 +++++++++++++++---------------- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/apps/integration_helper/urls.py b/apps/integration_helper/urls.py index 7a3304b..595f3b9 100644 --- a/apps/integration_helper/urls.py +++ b/apps/integration_helper/urls.py @@ -1,10 +1,7 @@ -from django.urls import path, include -from rest_framework.routers import DefaultRouter -from .views import ConversationViewSet +from django.urls import path +from .views import CoversationsView -router = DefaultRouter() -router.register(r'conversation', ConversationViewSet, basename='conversation') urlpatterns = [ - path('', include(router.urls)), + path(route='', view=CoversationsView.as_view(), name='conversations') ] diff --git a/apps/integration_helper/views.py b/apps/integration_helper/views.py index 807efc2..60ebfb0 100644 --- a/apps/integration_helper/views.py +++ b/apps/integration_helper/views.py @@ -1,16 +1,15 @@ import uuid -from rest_framework import viewsets, status +from rest_framework import status, generics from rest_framework.response import Response from rest_framework.decorators import action from apps.integration_helper.models import Conversation from apps.integration_helper.openai_utils import get_openai_response from apps.integration_helper.prompt import PROMPT -from fyle_accounting_mappings.models import DestinationAttribute -class ConversationViewSet(viewsets.ViewSet): +class CoversationsView(generics.CreateAPIView, generics.DestroyAPIView): """ - ViewSet for creating, retrieving, adding messages, and clearing conversations. + View for creating and deleting conversations. """ def create(self, request, *args, **kwargs): @@ -18,79 +17,77 @@ def create(self, request, *args, **kwargs): Create a new conversation and get the first OpenAI response. """ content = request.data.get('content') + conversation_id = request.data.get('conversation_id') workspace_id = kwargs['workspace_id'] if not content: - return Response({"error": "content are required"}, status=status.HTTP_400_BAD_REQUEST) - - conversation_id = str(uuid.uuid4()) + return Response( + {'error': 'content are required'}, status=status.HTTP_400_BAD_REQUEST + ) - conversation = Conversation.objects.create( - conversation_id=conversation_id, workspace_id=workspace_id, role='system', content=PROMPT + if not conversation_id: + conversation_id = str(uuid.uuid4()) + + Conversation.objects.update_or_create( + defaults={'content': PROMPT}, + conversation_id=conversation_id, + workspace_id=workspace_id, + role='system' ) conversation = Conversation.objects.create( - conversation_id=conversation_id, workspace_id=workspace_id, role='user', content=content + conversation_id=conversation_id, + workspace_id=workspace_id, + role='user', + content=content ) - messages = [{"role": "system", "content": PROMPT}, {"role": "user", "content": content}] + + messages = list( + Conversation.objects.filter( + conversation_id=conversation_id, + workspace_id=workspace_id + ).values('role', 'content').order_by('created_at')) assistant_response = get_openai_response(messages) - Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="assistant", content=assistant_response) + Conversation.objects.create( + conversation_id=conversation_id, + workspace_id=workspace_id, + role='assistant', + content=assistant_response, + ) - return Response({ - 'conversation_id': conversation.conversation_id, - 'assistant_response': assistant_response - }, status=status.HTTP_201_CREATED) - + return Response( + { + 'conversation_id': conversation.conversation_id, + 'content': assistant_response, + }, + status=status.HTTP_201_CREATED, + ) - @action(detail=True, methods=["post"]) - def add_message(self, request, pk=None, *args, **kwargs): + def delete(self, request, *args, **kwargs): """ - Add a new message to an existing conversation using conversation_id and get an OpenAI response. + Clear the conversation history by deleting it using conversation_id. """ - content = request.data.get("content") - conversation_id = pk workspace_id = kwargs['workspace_id'] - - if not content: - return Response( - {"error": "content are required"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - if not Conversation.objects.filter(conversation_id=pk).first(): + conversation_id = request.data.get('conversation_id') + if not conversation_id: return Response( - {"error": "Conversation id doesn't exists!"} + { + 'error': 'conversation_id is required' + }, status=status.HTTP_400_BAD_REQUEST ) - - messages = list(Conversation.objects.filter(conversation_id=conversation_id, workspace_id=workspace_id).values("role", "content").order_by('created_at')) - - messages.append({"role": "user", "content": content}) - Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="user", content=content) - - assistant_response = get_openai_response(messages) - Conversation.objects.create(conversation_id=conversation_id, workspace_id=workspace_id, role="assistant", content=assistant_response) - - return Response( - {"assistant_response": assistant_response}, status=status.HTTP_201_CREATED + conversations = Conversation.objects.filter( + conversation_id=conversation_id, + workspace_id=workspace_id ) - - @action(detail=True, methods=["delete"]) - def clear(self, request, pk=None, *args, **kwargs): - """ - Clear the conversation history by deleting it using conversation_id. - """ - workspace_id = kwargs['workspace_id'] - conversations = Conversation.objects.filter(conversation_id=pk, workspace_id=workspace_id) if conversations.exists(): conversations.delete() - return Response( - {"message": "Conversation cleared"}, status=status.HTTP_200_OK - ) - + return Response( - {"error": "Conversation not found"}, status=status.HTTP_404_NOT_FOUND + { + 'message': 'Conversation cleared' + }, status=status.HTTP_200_OK ) From 6f85394afbd78e7306a7a3b6a1c837cb8b08998e Mon Sep 17 00:00:00 2001 From: Shwetabh Kumar Date: Thu, 12 Sep 2024 11:56:27 +0530 Subject: [PATCH 4/6] feat: Updating URL --- apps/workspaces/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/workspaces/urls.py b/apps/workspaces/urls.py index 58b0fd4..a93734d 100644 --- a/apps/workspaces/urls.py +++ b/apps/workspaces/urls.py @@ -34,5 +34,5 @@ path('/accounting_exports/', include('apps.tasks.urls')), path('/qbd_mappings/', include('apps.mappings.urls')), path('/fyle/', include('apps.fyle.urls')), - path('/integration_helper/', include('apps.integration_helper.urls')) + path('/conversations/', include('apps.integration_helper.urls')) ] From b0a4c64ce3b4d3988ff512312bafe6494ebc7faa Mon Sep 17 00:00:00 2001 From: Ashutosh619-sudo Date: Thu, 12 Sep 2024 12:38:17 +0530 Subject: [PATCH 5/6] prompt changes --- apps/integration_helper/openai_utils.py | 2 +- apps/integration_helper/prompt.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/integration_helper/openai_utils.py b/apps/integration_helper/openai_utils.py index c416d4f..cf66acc 100644 --- a/apps/integration_helper/openai_utils.py +++ b/apps/integration_helper/openai_utils.py @@ -15,7 +15,7 @@ def get_openai_response(messages): api_key=api_key ) response = client.chat.completions.create( - model="gpt-4o-mini", + model="gpt-4o", messages=messages, response_format={"type": "json_object"}, max_tokens=1000, diff --git a/apps/integration_helper/prompt.py b/apps/integration_helper/prompt.py index f0531e4..6d28359 100644 --- a/apps/integration_helper/prompt.py +++ b/apps/integration_helper/prompt.py @@ -60,7 +60,7 @@ Your responses can only be in the form of below JSONs: -For CONVERATION: +For CONVERSATION: { "output_type": "CONVERSATION", // FINAL for the FINAL JSON PAYLOAD and CONVERSATION for questions "output": { @@ -117,8 +117,10 @@ Ensure the following guidelines: 1. **Ask Questions Step-by-Step:** Return one question at a time in JSON format unless the user provides all information at once. -2. **If User Provides Info:** Respond with the next appropriate question. -3. **Final Output:** Once all questions are answered, output the final JSON payload as specified. -4. **Return JSON:** Return all the keys even if the value is `null`. +2. **Confusing Answers Clarification:** If the user answer is confusing and gibberish, please ask clarification questions. +3. **If User Provides Info:** Respond with the next appropriate question. +4. **Final Output:** Once all questions are answered and all steps are answered, output the final JSON payload as specified. +5. **Return JSON:** Return all the keys even if the value is `null`. +6. **Steps Ensurity:** Ensure every step has been answered, never give final JSON without completing all steps. """ From 0eb99ef097b276bf277f0ac594e20b7a655ea6c8 Mon Sep 17 00:00:00 2001 From: Shwetabh Kumar Date: Thu, 12 Sep 2024 12:39:44 +0530 Subject: [PATCH 6/6] fix: removing unused import --- apps/integration_helper/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/integration_helper/views.py b/apps/integration_helper/views.py index 60ebfb0..1cd1ce9 100644 --- a/apps/integration_helper/views.py +++ b/apps/integration_helper/views.py @@ -1,7 +1,6 @@ import uuid from rest_framework import status, generics from rest_framework.response import Response -from rest_framework.decorators import action from apps.integration_helper.models import Conversation from apps.integration_helper.openai_utils import get_openai_response from apps.integration_helper.prompt import PROMPT