Skip to content

Commit

Permalink
#3 Added Test gen lambda.
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaVidakovic committed Jun 8, 2024
1 parent ba06ad2 commit e72d09e
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 2 deletions.
50 changes: 50 additions & 0 deletions backend/src/test_gen/ai_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import openai
import logging
import boto3
from botocore.exceptions import ClientError
import json

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def get_secret():
secret_name = "Hakaton"
region_name = "eu-central-1"

# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(service_name="secretsmanager", region_name=region_name)

try:
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
# Assume the secret is stored as a JSON object
secret = json.loads(get_secret_value_response["SecretString"])
logger.info("Successfully retrieved secret for OpenAI API key")
return secret["OPENAI_API_KEY"]
except ClientError as e:
logger.exception(f"Unable to retrieve secret: {e}")
raise e


# Set the OpenAI API key from the Secrets Manager
openai.api_key = get_secret()


def get_gpt_answer(messages):
if not messages:
logger.warning("Messages list is empty.")
return "Pitanje ne može biti prazno."

logger.info("Calling OpenAI for Q&A")
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
temperature=0,
)
logger.info("Received response from OpenAI")
return response.choices[0].message.content
except Exception as e:
logger.exception("Error calling OpenAI API: %s", str(e))
return "Došlo je do greške prilikom pozivanja OpenAI API-ja. Pokušajte ponovo kasnije."
123 changes: 123 additions & 0 deletions backend/src/test_gen/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import json
import boto3
import uuid
import os
from datetime import datetime
from pytz import timezone
from ai_response import get_gpt_answer
import logging

# Initialize DynamoDB resources
DYNAMODB_QUESTIONS_TABLE = os.getenv("QUESTION_TABLE")
DYNAMODB_CHATS_TABLE = os.getenv("CHATS_TABLE")
dynamodb = boto3.resource("dynamodb")
questions_table = dynamodb.Table(DYNAMODB_QUESTIONS_TABLE)
chats_table = dynamodb.Table(DYNAMODB_CHATS_TABLE)

# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
logger.info("Received event: %s", event)
try:
body = json.loads(event.get("body", "{}"))
user_id = body.get("UserID")
chat_id = body.get("ChatID")
question = body.get("Question")

if not user_id or not question or not chat_id:
logger.error("Missing UserID, ChatID or Question in the request")
return {
"statusCode": 400,
"body": json.dumps(
{"error": "UserID, ChatID and Question are required"}
),
}

logger.info("Processing question for user: %s", user_id)

# Get chat topic from Chats table
chat_response = chats_table.get_item(Key={"ChatID": chat_id})
if "Item" not in chat_response:
logger.error("ChatID not found in Chats table")
return {
"statusCode": 404,
"body": json.dumps({"error": "ChatID not found"}),
}

chat_topic = chat_response["Item"]["ChatTopic"]

# Get the last 10 questions and responses for the chat
response = questions_table.query(
IndexName="ChatID-index",
KeyConditionExpression=boto3.dynamodb.conditions.Key("ChatID").eq(chat_id),
Limit=10,
ScanIndexForward=False, # Get the latest items first
)

osnovna_skola = "Odgovaras uceniku osnovne skole. Potrudi se da odgovor bude zanimljiv i postavi dodatno pitanje da zainteresujes ucenika."
srednja_skola = "Odgovaras uceniku srednje skole. Potrudi se da odgovor bude na nivou znanja srednjoskolca."
fakultet = "Odgovaras studentu na fakultetu. Daj kompletan odgovor sa referencama. Pretrazi internet za najnovije clanke koji mu mogu pomoci u daljem istrazivanju."
nivo_znanja = fakultet

SYSPROMPT = f"""
Ti si ekspert za pravljenje testova. Tvoj zadatak je da osnovu teme koja je data izmedju ''' i
pitanja koje je student imao naprvis test od 10 pitanja za studenta.
{nivo_znanja}
Test treba da bude sa 4 ponudjena odgovora od koji je samo jedan tacan. Sve pises na sprpskom jeziku.
Ispisi svih 10 pitanja u json formatu: "pitanje", "ponudjeni odgovori", "slovo za tacan odgovor".
'''{chat_topic}'''
"""

messages = [{"role": "system", "content": SYSPROMPT}]
for item in reversed(
response["Items"]
): # Reverse to maintain chronological order
messages.append({"role": "user", "content": item["Question"]})
messages.append({"role": "assistant", "content": item["AIResponse"]})

# Add the current question to the messages
messages.append({"role": "user", "content": question})

ai_response = get_gpt_answer(messages)

# Generate UUID for QuestionID
question_id = str(uuid.uuid4())

# Get current time in Central European Time (CET)
cet = timezone("Europe/Belgrade")
current_time = datetime.now(cet).isoformat()

# Save question and AI response to DynamoDB
questions_table.put_item(
Item={
"QuestionID": question_id,
"UserID": user_id,
"ChatID": chat_id,
"Question": question,
"Timestamp": current_time,
"AIResponse": ai_response,
}
)

logger.info("Successfully processed question for user: %s", user_id)
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST",
},
"body": json.dumps({"Response": ai_response}),
}
except Exception as e:
logger.exception("Error processing request: %s", str(e))
return {
"statusCode": 500,
"body": json.dumps({"error": "An error occurred. Please try again later."}),
}
2 changes: 2 additions & 0 deletions backend/src/test_gen/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
openai == 1.30.3
pytz
4 changes: 2 additions & 2 deletions frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const Home: React.FC<HomeProps> = ({ user, chatID }) => {

// Format the data for messages state
const formattedMessages = data.messages.map((item: any) => [
{ text: item.Question, sender: 'user' as const },
{ text: item.AIResponse, sender: 'bot' as const }
{ text: item.Question.replace(/\\n/g, '\n'), sender: 'user' as const },
{ text: item.AIResponse.replace(/\\n/g, '\n'), sender: 'bot' as const }
]).flat();

setMessages(formattedMessages);
Expand Down
68 changes: 68 additions & 0 deletions template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Resources:
Resource:
- !GetAtt AskLambda.Arn
- !GetAtt CreateChatLambda.Arn
- !GetAtt TestGenLambda.Arn

UsersTable:
Type: "AWS::DynamoDB::Table"
Expand Down Expand Up @@ -189,6 +190,20 @@ Resources:
QUESTION_TABLE: !Ref QuestionsTable
CHATS_TABLE: !Ref ChatsTable

TestGenLambda:
Type: "AWS::Serverless::Function"
Properties:
Handler: app.lambda_handler # Pretpostavimo da je handler definisan u app.py
Runtime: python3.11
CodeUri: ./backend/src/test_gen/
MemorySize: 512
Timeout: 600
Role: !GetAtt LambdaRole.Arn
Environment:
Variables:
QUESTION_TABLE: !Ref QuestionsTable
CHATS_TABLE: !Ref ChatsTable

ApiGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Expand Down Expand Up @@ -292,6 +307,54 @@ Resources:
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true

TestGenResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt ApiGateway.RootResourceId
PathPart: "gentest"
RestApiId: !Ref ApiGateway

TestGenInvokeLambda:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: "NONE"
HttpMethod: "POST"
ResourceId: !Ref TestGenResource
RestApiId: !Ref ApiGateway
Integration:
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${TestGenLambda.Arn}/invocations
Credentials: !GetAtt ApiGatewayRole.Arn
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true

TestGenOptionsMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: "NONE"
HttpMethod: "OPTIONS"
ResourceId: !Ref TestGenResource
RestApiId: !Ref ApiGateway
Integration:
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST,PUT,GET'"
method.response.header.Access-Control-Allow-Origin: "'*'"
RequestTemplates:
application/json: '{"statusCode": 200}'
Type: MOCK
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true

GetChatsResource:
Type: AWS::ApiGateway::Resource
Properties:
Expand Down Expand Up @@ -557,3 +620,8 @@ Outputs:
Description: "API Gateway endpoint URL for getting chat history"
Value:
Fn::Sub: "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod/getChatHistory"

GetChatHistoryUrl:
Description: "API Gateway endpoint URL for getting chat history"
Value:
Fn::Sub: "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod/gentest"

0 comments on commit e72d09e

Please sign in to comment.