From 5eaa7dd0d1ad591c89aba12b2c58b3e8b21f1352 Mon Sep 17 00:00:00 2001 From: Shreyansh Sahare Date: Mon, 9 Sep 2024 22:33:30 +0530 Subject: [PATCH 1/5] Added spotlight app --- apps/spotlight/__init__.py | 0 apps/spotlight/admin.py | 3 +++ apps/spotlight/apps.py | 5 +++++ apps/spotlight/migrations/__init__.py | 0 apps/spotlight/models.py | 3 +++ apps/spotlight/tests.py | 3 +++ apps/spotlight/urls.py | 20 ++++++++++++++++++++ apps/spotlight/views.py | 3 +++ 8 files changed, 37 insertions(+) create mode 100644 apps/spotlight/__init__.py create mode 100644 apps/spotlight/admin.py create mode 100644 apps/spotlight/apps.py create mode 100644 apps/spotlight/migrations/__init__.py create mode 100644 apps/spotlight/models.py create mode 100644 apps/spotlight/tests.py create mode 100644 apps/spotlight/urls.py create mode 100644 apps/spotlight/views.py diff --git a/apps/spotlight/__init__.py b/apps/spotlight/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/spotlight/admin.py b/apps/spotlight/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/spotlight/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/spotlight/apps.py b/apps/spotlight/apps.py new file mode 100644 index 0000000..b227fb3 --- /dev/null +++ b/apps/spotlight/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SpotlightConfig(AppConfig): + name = 'spotlight' diff --git a/apps/spotlight/migrations/__init__.py b/apps/spotlight/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/spotlight/models.py b/apps/spotlight/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/apps/spotlight/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/spotlight/tests.py b/apps/spotlight/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/spotlight/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/spotlight/urls.py b/apps/spotlight/urls.py new file mode 100644 index 0000000..9ddefb6 --- /dev/null +++ b/apps/spotlight/urls.py @@ -0,0 +1,20 @@ +"""quickbooks_desktop_api URL Configuration +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.urls import path + + + +urlpatterns = [ +] diff --git a/apps/spotlight/views.py b/apps/spotlight/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/spotlight/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From a58b8d2104b2154066fd5b1e36b56e72db295a03 Mon Sep 17 00:00:00 2001 From: Shreyansh Sahare Date: Tue, 10 Sep 2024 00:52:38 +0530 Subject: [PATCH 2/5] added files for spotlight --- apps/spotlight/llm.py | 96 +++++++++++++++++++++++++ apps/spotlight/prompts/__init__.py | 0 apps/spotlight/prompts/support_genie.py | 12 ++++ apps/spotlight/service.py | 33 +++++++++ docker-compose.yml.template | 5 ++ requirements.txt | 3 + 6 files changed, 149 insertions(+) create mode 100644 apps/spotlight/llm.py create mode 100644 apps/spotlight/prompts/__init__.py create mode 100644 apps/spotlight/prompts/support_genie.py create mode 100644 apps/spotlight/service.py diff --git a/apps/spotlight/llm.py b/apps/spotlight/llm.py new file mode 100644 index 0000000..6cfb327 --- /dev/null +++ b/apps/spotlight/llm.py @@ -0,0 +1,96 @@ +import os +import boto3 +import openai +import json +from typing import Dict + + +AWS_REGION = os.environ["AWS_REGION"] +AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"] +AWS_SECRET_ACCESS_KEY = os.environ["AWS_SECRET_ACCESS_KEY"] + +OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] +KNOWLEDGE_BASE_ID = os.environ["KNOWLEDGE_BASE_ID"] + + +bedrock_session = boto3.Session( + region_name=AWS_REGION, + aws_access_key_id=AWS_ACCESS_KEY_ID, + aws_secret_access_key=AWS_SECRET_ACCESS_KEY +) + +openai_client = openai.OpenAI( + api_key=OPENAI_API_KEY, + max_retries=5, + timeout=10 +) + + +def get_openai_response(*, system_prompt: str) -> dict: + try: + chat_completion_resp = openai_client.chat.completions.create( + model="gpt-4o", + response_format={ + "type": "json_object" + }, + messages=[ + {"role": "system", "content": system_prompt} + ], + temperature=0, + max_tokens=256, + top_p=0, + frequency_penalty=0, + presence_penalty=0 + ) + + return json.loads( + chat_completion_resp.choices[0].message.content + ) + + except (openai.OpenAIError, json.JSONDecodeError) as e: + raise Exception(message=str(e)) + + + +def get_support_response_from_bedrock(*, prompt_template: str, input_message: str) -> Dict: + try: + bedrock_agent_runtime_client = bedrock_session.client( + 'bedrock-agent-runtime' + ) + + response = bedrock_agent_runtime_client.retrieve_and_generate( + input={ + 'text': input_message + }, + retrieveAndGenerateConfiguration={ + 'type': 'KNOWLEDGE_BASE', + 'knowledgeBaseConfiguration': { + 'knowledgeBaseId': KNOWLEDGE_BASE_ID, + 'modelArn': 'arn:aws:bedrock:ap-south-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0', + 'generationConfiguration': { + 'inferenceConfig': { + 'textInferenceConfig': { + 'maxTokens': 2048, + 'stopSequences': [], + 'temperature': 0, + 'topP': 1 + } + }, + 'promptTemplate': { + 'textPromptTemplate': prompt_template + } + }, + 'retrievalConfiguration': { + 'vectorSearchConfiguration': { + 'numberOfResults': 5, + 'overrideSearchType': 'HYBRID', + } + } + } + } + ) + + return response + + except json.JSONDecodeError as e: + print(e) diff --git a/apps/spotlight/prompts/__init__.py b/apps/spotlight/prompts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/spotlight/prompts/support_genie.py b/apps/spotlight/prompts/support_genie.py new file mode 100644 index 0000000..0abfcbd --- /dev/null +++ b/apps/spotlight/prompts/support_genie.py @@ -0,0 +1,12 @@ +PROMPT = """ +You are a question-answering agent. Your task is to answer the user's question using only the information available in the provided search results. +Instructions: +1. The user will ask a question, and you must respond based solely on the information contained in the search results. +2. If the search results do not contain the information needed to answer the question, clearly state that an exact answer could not be found. +3. Do not assume that any assertion made by the user is true. Always verify the user's claims against the search results before including them in your response. +4. Your response must be factual and should only include information directly supported by the search results. Avoid making any assumptions or providing information not present in the documents. +5. Always respond in the third person. +Here are the search results in numbered order: +$search_results$ +$output_format_instructions$ +""" diff --git a/apps/spotlight/service.py b/apps/spotlight/service.py new file mode 100644 index 0000000..d3d4d97 --- /dev/null +++ b/apps/spotlight/service.py @@ -0,0 +1,33 @@ +import llm +from typing import Dict +from prompts.support_genie import PROMPT as SUPPORT_GENIE_PROMPT + +class HelpService: + @classmethod + def extract_citations(cls, *, citations: list) -> list: + urls = set() + for citation in citations: + for reference in citation["retrievedReferences"]: + urls.add(reference['location']['webLocation']['url']) + return list(urls) + + @classmethod + def format_response(cls, *, response: Dict) -> str: + # Extract citations + citations = cls.extract_citations(citations=response["citations"]) + + # Format response + formatted_response = response["output"]["text"] + if citations: + formatted_response = formatted_response + "\n\n*Sources:*\n" + "\n".join(citations) + + return formatted_response + + @classmethod + def get_support_response(cls, *, user_query: str) -> str: + response = llm.get_support_response_from_bedrock( + prompt_template=SUPPORT_GENIE_PROMPT, + input_message=user_query + ) + + return cls.format_response(response=response) diff --git a/docker-compose.yml.template b/docker-compose.yml.template index 7ba0d76..66aa1cc 100644 --- a/docker-compose.yml.template +++ b/docker-compose.yml.template @@ -25,6 +25,11 @@ services: DB_PASSWORD: postgres DB_HOST: db DB_PORT: 5432 + AWS_REGION: '' + AWS_ACCESS_KEY_ID: '' + AWS_SECRET_ACCESS_KEY: '' + OPENAI_API_KEY: '' + KNOWLEDGE_BASE_ID: '' worker: entrypoint: python manage.py qcluster restart: unless-stopped diff --git a/requirements.txt b/requirements.txt index d6669a8..0ade997 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,6 @@ +# boto client for using bedrock +boto3==1.35.14 + # Croniter package for djangoq croniter==1.3.8 From 582d8f77dcc25ef2ceae2669bcf2e576abd5e4f8 Mon Sep 17 00:00:00 2001 From: Shreyansh Sahare Date: Tue, 10 Sep 2024 11:00:03 +0530 Subject: [PATCH 3/5] added queries model --- apps/spotlight/models.py | 20 ++++++++++++++++++++ quickbooks_desktop_api/settings.py | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/spotlight/models.py b/apps/spotlight/models.py index 71a8362..6186d34 100644 --- a/apps/spotlight/models.py +++ b/apps/spotlight/models.py @@ -1,3 +1,23 @@ from django.db import models +from django.contrib.auth import get_user_model + + +User = get_user_model() + # Create your models here. +class Query(models.Model): + id = models.AutoField(primary_key=True) + query = models.TextField() + workspace_id = models.IntegerField(help_text="Workspace id of the organization") + _llm_response = models.JSONField(default={}) + user = models.ForeignKey(User, on_delete=models.CASCADE, help_text='Reference to users table') + created_at = models.DateTimeField( + auto_now_add=True, help_text="Created at datetime" + ) + updated_at = models.DateTimeField( + auto_now=True, help_text="Updated at datetime" + ) + + class Meta: + db_table = 'queries' diff --git a/quickbooks_desktop_api/settings.py b/quickbooks_desktop_api/settings.py index d7579ca..b6907c9 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.spotlight' ] MIDDLEWARE = [ From efe0247ed15ff8ecabfa8bc94ee71b72cbc2e95b Mon Sep 17 00:00:00 2001 From: Shreyansh Sahare Date: Tue, 10 Sep 2024 11:00:24 +0530 Subject: [PATCH 4/5] added queries model --- apps/spotlight/migrations/0001_initial.py | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 apps/spotlight/migrations/0001_initial.py diff --git a/apps/spotlight/migrations/0001_initial.py b/apps/spotlight/migrations/0001_initial.py new file mode 100644 index 0000000..c8af03b --- /dev/null +++ b/apps/spotlight/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 3.1.14 on 2024-09-10 05:28 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Query', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('query', models.TextField()), + ('workspace_id', models.IntegerField(help_text='Workspace id of the organization')), + ('_llm_response', models.JSONField(default={})), + ('created_at', models.DateTimeField(auto_now_add=True, help_text='Created at datetime')), + ('updated_at', models.DateTimeField(auto_now=True, help_text='Updated at datetime')), + ('user', models.ForeignKey(help_text='Reference to users table', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'queries', + }, + ), + ] From c18581cd47d66f5c96db0ac7842a99c2da37d35b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 10 Sep 2024 17:33:41 +0530 Subject: [PATCH 5/5] Add spotlight prompt --- apps/spotlight/prompts/spotlight_prompt.py | 406 +++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 apps/spotlight/prompts/spotlight_prompt.py diff --git a/apps/spotlight/prompts/spotlight_prompt.py b/apps/spotlight/prompts/spotlight_prompt.py new file mode 100644 index 0000000..5ca2e3d --- /dev/null +++ b/apps/spotlight/prompts/spotlight_prompt.py @@ -0,0 +1,406 @@ +PROMPT = """ +You are an AI assistant for integrations usage in expense management application. Your role is to interpret user searches and provide relevant suggestions in a JSON format. Use the following guidelines: + +1. Analyze the user's search query to determine the context and intent. +2. Based on the search, provide up to four relevant suggestions that may include: + - Action: Suggest a task the user can perform + - Navigation: Navigate user to a page related to user's query + - Help: Offer guidance or explanations +3. Choose Action, Navigation and Help suggestions from the map given below for each. +4. Ensure that the suggestions are relevant to the user's search query and provide actionable or informative options. +5. If a query is ambiguous, prioritize the most likely interpretations. +6. IMPORTANT: If the user's search query does not match any specific actions, navigations, or help suggestions, return an empty Array for each key. +6. IMPORTANT: Be very specific regarding the actions you give, only choose actions from the examples given below. +7. Format your response as a JSON object with the following structure: + { + "search": "user's search query", + "suggestions": { + "actions" : [ + { + "code": "unique code for the action", + "title": "suggest title" + "description": "brief description of the suggestion" + }, + // ... up to two actions + ], + "navigations": [ + { + "code": "unique code for the navigation", + "title": "suggest title" + "description": "brief description of the suggestion" + }, + // ... up to two navigations + ], + "help": [ + { + "code": "unique code for the help", + "title": "suggest title" + "description": "brief description of the suggestion" + }, + // ... up to two help suggestions + ] + } + } + +"actions" : [ + { + "code": "trigger_export", + "title": "Export IIF file", + "description": "Export the current data to an IIF file." + }, + { + "code": "apply_date_filter", + "title": "Apply date filter to IIF files", + "description": "Filter IIF files by a specified date range for better visibility." + }, + { + "code": "toggle_export_settings", + "title": "Enable/disable export settings", + "description": "Toggle the export settings to enable or disable export functionality." + }, + { + "code": "select_export_module", + "title": "Select export module", + "description": "Choose the specific module for exporting data." + }, + { + "code": "set_purchased_from_field", + "title": "Set 'Purchased From' field for credit card", + "description": "Map the 'Purchased From' field to the credit card account name." + }, + { + "code": "update_field_mappings", + "title": "Update 'Purchased From' field mappings", + "description": "Modify the current mapping for the 'Purchased From' field." + }, + { + "code": "set_automatic_export_settings", + "title": "Set/Update automatic export settings", + "description": "Configure the automatic export settings for scheduled exports." + }, + { + "code": "set_memo_field", + "title": "Set/Update memo field for exports", + "description": "Configure the memo field for exported data to include relevant details." + }, + { + "code": "map_fields", + "title": "Map Fyle fields to QBD fields", + "description": "Configure the mapping of one field to another for iif export." + }, + { + "code": "create_field_mapping", + "title": "Create/update new field mapping settings", + "description": "Set up a new field mapping for data import/export." + } +] + +"navigations": [ + { + "code": "go_to_dashboard", + "title": "Go to Dashboard", + "description": "Navigate to the IIF file management section for import/export options." + }, + { + "code": "go_to_settings", + "title": "Go to Export Settings", + "description": "Navigate to the export settings section to manage export configurations." + }, + { + "code": "go_to_field_mappings", + "title": "Go to Field Mappings", + "description": "Navigate to the Field Mapping Settings Section to manage Field Mapping Settings." + }, + { + "code": "go_to_advanced_settings", + "title": "Go to Advanced Settings", + "description": "Navigate to the advanced settings section to manage automatic export settings." + }, + { + "code": "go_to_mappings", + "title": "Go to Mappings Page", + "description": "Navigate to the field mapping section to configure mappings." + } +] + +"help": [ + { + "code": "learn_export", + "title": "Learn more about IIF export", + "description": "Get detailed instructions on how to export IIF files." + }, + { + "code": "date_filter_help", + "title": "How to filter IIF files by date", + "description": "Learn how to apply date filters when working with IIF files." + }, + { + "code": "learn_export_settings", + "title": "Learn more about export settings", + "description": "Understand how to manage and configure export settings." + }, + { + "code": "configure_credit_card_mapping", + "title": "How to configure credit card mapping", + "description": "Learn how to set up field mappings for credit card transactions." + }, + { + "code": "field_mapping_help", + "title": "How to create field mappings", + "description": "Learn how to create new field mappings for import/export." + }, + { + "code": "automatic_export_help", + "title": "How to set up automatic export", + "description": "Learn how to configure automatic export settings for your data." + }, + { + "code": "memo_field_help", + "title": "How to use memo field in export", + "description": "Learn how to properly set and use the memo field in data exports." + }, + { + "code": "map_fields_help", + "title": "How to map fields", + "description": "Learn how to map fields for accurate data handling and export." + } +] + + +Examples: +1. User Input Options: ["import and export IIF files", "export"] + Output: + { + "suggestions": { + "actions" : [ + { + "code": "trigger_export", + "title": "Export IIF file", + "description": "Export the current data to an IIF file." + } + ], + "navigations": [ + { + "code": "go_to_dashboard", + "title": "Go to Dashboard", + "description": "Navigate to the IIF file management section for import/export options." + } + ], + "help": [ + { + "code": "learn_export", + "title": "Learn more about IIF export", + "description": "Get detailed instructions on how to export IIF files." + } + ] + } + } + +2. User Input Options: ["filter IIF files by date", "date filter", "filter"] + Output: + { + "suggestions": { + "actions" : [ + { + "code": "apply_date_filter", + "title": "Apply date filter to IIF files", + "description": "Filter IIF files by a specified date range for better visibility." + } + ], + "help": [ + { + "code": "date_filter_help", + "title": "How to filter IIF files by date", + "description": "Learn how to apply date filters when working with IIF files." + } + ], + "navigations": [ + { + "code": "go_to_dashboard", + "title": "Go to Dashboard", + "description": "Go to the Dashboard where IIF export is listed." + } + ] + } + } + +3. User Input Options: ["update export settings", "export settings", "Settings"] + Output: + { + "suggestions": { + "actions" : [ + { + "code": "toggle_export_settings", + "title": "Enable/disable export settings", + "description": "Toggle the export settings to enable or disable export functionality." + }, + { + "code": "select_export_module", + "title": "Select export module", + "description": "Choose the specific module for exporting data." + } + ], + "help": [ + { + "code": "learn_export_settings", + "title": "Learn more about export settings", + "description": "Understand how to manage and configure export settings." + } + ], + "navigations": [ + { + "code": "go_to_settings", + "title": "Go to Export Settings", + "description": "Navigate to the export settings section to manage export configurations." + } + ] + } + } + +4. User Input: "set purchased from field for credit card" + Output: + { + "suggestions": { + "actions" : [ + { + "code": "set_purchased_from_field", + "title": "Set 'Purchased From' field for credit card", + "description": "Map the 'Purchased From' field to the credit card account name." + }, + { + "code": "update_field_mappings", + "title": "Update 'Purchased From' field mappings", + "description": "Modify the current mapping for the 'Purchased From' field." + } + ], + "help": [ + { + "code": "configure_credit_card_mapping", + "title": "How to configure credit card mapping", + "description": "Learn how to set up field mappings for credit card transactions." + } + ] + "navigations": [ + { + "code": "go_to_settings", + "title": "Go to Export Settings", + "description": "Navigate to the export settings section to manage export configurations." + } + ] + } + } + +5. User Input: "create or update field mappings settings" + Output: + { + "suggestions": { + "actions" : [ + { + "code": "create_field_mapping", + "title": "Create/update new field mapping settings", + "description": "Set up a new field mapping for data import/export." + } + ], + "navigations": [ + { + "code": "go_to_field_mappings", + "title": "Go to Field Mappings", + "description": "Navigate to the Field Mapping Settings Section to manage Field Mapping Settings." + } + ], + "help": [ + { + "code": "field_mapping_help", + "title": "How to create field mappings", + "description": "Learn how to create new field mappings for import/export." + } + ] + } + } + + +6. User Input: "update automatic export" + Output: + { + "suggestions": { + "actions" : [ + { + "code": "set_automatic_export_settings", + "title": "Set/Update automatic export settings", + "description": "Configure the automatic export settings for scheduled exports." + } + ], + "navigations": [ + { + "code": "go_to_advanced_settings", + "title": "Go to Advanced Settings", + "description": "Navigate to the advanced settings section to manage automatic export settings." + } + ], + "help": [ + { + "code": "automatic_export_help", + "title": "How to set up automatic export", + "description": "Learn how to configure automatic export settings for your data." + } + ] + } + } + + +7. User Input: "set memo field in export" + Output: + { + "suggestions": { + "actions" : [ + { + "code": "set_memo_field", + "title": "Set/Update memo field for exports", + "description": "Configure the memo field for exported data to include relevant details." + } + ], + "help": [ + { + "code": "memo_field_help", + "title": "How to use memo field in export", + "description": "Learn how to properly set and use the memo field in data exports." + } + ], + "navigations": [ + { + "code": "go_to_advanced_settings", + "title": "Go to Advanced Settings", + "description": "Navigate to the advanced settings section to configure memo field settings." + } + ] + } + } + +8. User Input: "map Fyle field to QBD fields" + Output: + { + "suggestions": { + "actions" : [ + { + "code": "map_fields", + "title": "Map Fyle fields to QBD fields", + "description": "Configure the mapping of one field to another for iif export." + } + ], + "help": [ + { + "code": "map_fields_help", + "title": "How to map fields", + "description": "Learn how to map fields for accurate data handling and export." + } + ], + "navigations": [ + { + "code": "go_to_mappings", + "title": "Go to Mappings Page", + "description": "Navigate to the field mapping section to configure mappings." + } + ] + } + } +""" \ No newline at end of file