Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CAI-183] Chatbot Patch query api #1198

Merged
merged 46 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ac9b39a
feat(chatbot): session GSI
batdevis Oct 8, 2024
36c89af
feat(chatbot): docker compose
batdevis Oct 9, 2024
a2c16c7
fix(chatbot): dynamodb and redis for local development with docker co…
batdevis Oct 9, 2024
c633df6
Merge branch 'chatbot/docker-compose-complete' into chatbot/sessions-…
batdevis Oct 9, 2024
08a7fec
chore(chatbot):remove duplicate imports
batdevis Oct 9, 2024
7fe9101
chore(chatbot): linting
batdevis Oct 9, 2024
9b2eb61
fix(chatbot):create index in docker
batdevis Oct 9, 2024
b251837
chore(chatbot): llamaindex index id
batdevis Oct 10, 2024
4ecd4b3
fix(chatbot): create vector index with all docs
batdevis Oct 10, 2024
a84581c
Merge branch 'main' into chatbot/docker-compose-complete
batdevis Oct 10, 2024
ea7d3db
chore(chatbot): terraform lint
batdevis Oct 10, 2024
28695e3
fix(chatbot): terraform syntax
batdevis Oct 10, 2024
238edfd
chore(chatbot): remove dynamodb options
batdevis Oct 10, 2024
5f63560
chore(chatbot): from global to local secondary index
batdevis Oct 10, 2024
a8f4c9a
feat(chatbot): find or create session
batdevis Oct 11, 2024
9f9e218
Merge branch 'main' into chatbot/sessions-query-by-date
batdevis Oct 11, 2024
859c298
Merge branch 'main' into chatbot/docker-compose-complete
batdevis Oct 11, 2024
1caa859
feat(chatbot): PATCH /sessions/{sessionId}/queries/{id} feedback API
batdevis Oct 11, 2024
812b4c4
chore(chatbot): lint
batdevis Oct 11, 2024
f43a771
chore: merge main
batdevis Oct 11, 2024
d96a9f9
chore: remove old var
batdevis Oct 11, 2024
9526c17
chore: merge main
batdevis Oct 11, 2024
a5177df
Update apps/chatbot/docker/compose.yaml
batdevis Oct 11, 2024
4daccf8
chore: remove logs
batdevis Oct 11, 2024
f5bdfd3
Merge branch 'chatbot/docker-compose-complete' of github.com:pagopa/d…
batdevis Oct 11, 2024
c123b5c
fix(chatbot): compose vars
batdevis Oct 13, 2024
aa59ca5
Merge branch 'main' into chatbot/docker-compose-complete
batdevis Oct 13, 2024
6d11e67
Merge branch 'chatbot/docker-compose-complete' into chatbot/sessions-…
batdevis Oct 13, 2024
4b6ee00
Merge branch 'chatbot/sessions-query-by-date' into patch-query-api
batdevis Oct 13, 2024
5e07dbe
Update modules
mdciri Oct 16, 2024
da8a41c
Update config prompts
mdciri Oct 16, 2024
b57d55c
Update env example
mdciri Oct 16, 2024
62fffa1
Merge branch 'main' into chatbot/docker-compose-complete
batdevis Oct 16, 2024
f7b05e6
Merge branch 'languages/chatbot/cai-198' into chatbot/docker-compose-…
batdevis Oct 16, 2024
278d56d
redis admin port
batdevis Oct 16, 2024
91c782a
chore: add env example var
batdevis Oct 16, 2024
ee03467
Merge branch 'chatbot/docker-compose-complete' into chatbot/sessions-…
batdevis Oct 16, 2024
964c202
Merge branch 'chatbot/sessions-query-by-date' into patch-query-api
batdevis Oct 16, 2024
01d1a2d
dynamodb utility scripts for local development
batdevis Oct 16, 2024
0a76aaa
page params
batdevis Oct 16, 2024
e52635f
CHB_SESSION_MAX_DURATION_DAYS env var
batdevis Oct 16, 2024
f5d1b31
chore: dynamodb local scripts
batdevis Oct 20, 2024
e46704c
fix(chatbot): query list filter
batdevis Oct 20, 2024
682c2de
chore(chatbot): remove query detail API
batdevis Oct 20, 2024
54c9cca
feat(chatbot): queries in ascending order
batdevis Oct 21, 2024
9ca62dd
chore: merge main
batdevis Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/chatbot/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ CHB_ENGINE_USE_STREAMING=...
CHB_QUERY_TABLE_PREFIX=chatbot-local
CHB_DYNAMODB_URL=http://locahost:8080
CHB_USE_PRESIDIO=True
CHB_SESSION_MAX_DURATION_DAYS=1
2 changes: 2 additions & 0 deletions apps/chatbot/docker/docker-compose-run-create_index.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
docker compose -f docker/compose.yaml -p chatbot run create_index
33 changes: 33 additions & 0 deletions apps/chatbot/scripts/dynamodb-create-table-queries-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
aws dynamodb create-table \
--table-name chatbot-local-queries \
--key-schema \
AttributeName=sessionId,KeyType=HASH \
AttributeName=id,KeyType=RANGE \
--attribute-definitions \
AttributeName=id,AttributeType=S \
AttributeName=sessionId,AttributeType=S \
AttributeName=createdAt,AttributeType=S \
--local-secondary-indexes '[
{
"IndexName": "QueriesByCreatedAtIndex",
"KeySchema": [
{
"AttributeName": "sessionId",
"KeyType": "HASH"
},
{
"AttributeName": "createdAt",
"KeyType": "RANGE"
}
],
"Projection": {
"ProjectionType": "ALL"
}
}
]' \
--table-class STANDARD \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url http://localhost:8000 \
--region eu-south-1 \
--profile dummy
33 changes: 33 additions & 0 deletions apps/chatbot/scripts/dynamodb-create-table-sessions-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
aws dynamodb create-table \
--table-name chatbot-local-sessions \
--key-schema \
AttributeName=userId,KeyType=HASH \
AttributeName=id,KeyType=RANGE \
--attribute-definitions \
AttributeName=id,AttributeType=S \
AttributeName=userId,AttributeType=S \
AttributeName=createdAt,AttributeType=S \
--local-secondary-indexes '[
{
"IndexName": "SessionsByCreatedAtIndex",
"KeySchema": [
{
"AttributeName": "userId",
"KeyType": "HASH"
},
{
"AttributeName": "createdAt",
"KeyType": "RANGE"
}
],
"Projection": {
"ProjectionType": "ALL"
}
}
]' \
--table-class STANDARD \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url http://localhost:8000 \
--region eu-south-1 \
--profile dummy
6 changes: 6 additions & 0 deletions apps/chatbot/scripts/dynamodb-delete-table-queries-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
aws dynamodb delete-table \
--table-name chatbot-local-queries \
--endpoint-url http://localhost:8000 \
--region eu-south-1 \
--profile dummy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
aws dynamodb delete-table \
--table-name chatbot-local-sessions \
--endpoint-url http://localhost:8000 \
--region eu-south-1 \
--profile dummy
6 changes: 6 additions & 0 deletions apps/chatbot/scripts/dynamodb-scan-queries.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
aws dynamodb scan \
--table-name chatbot-local-queries \
--region eu-south-1 \
--endpoint-url http://localhost:8000 \
--profile dummy
6 changes: 6 additions & 0 deletions apps/chatbot/scripts/dynamodb-scan-sessions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
aws dynamodb scan \
--table-name chatbot-local-sessions \
--region eu-south-1 \
--endpoint-url http://localhost:8000 \
--profile dummy
128 changes: 72 additions & 56 deletions apps/chatbot/src/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class Query(BaseModel):
question: str
queriedAt: str | None = None

class QueryFeedback(BaseModel):
badAnswer: bool

boto3_session = boto3.session.Session(
region_name=AWS_DEFAULT_REGION
)
Expand Down Expand Up @@ -85,7 +88,8 @@ async def query_creation (
"question": query.question,
"answer": answer,
"createdAt": now.isoformat(),
"queriedAt": queriedAt
"queriedAt": queriedAt,
"badAnswer": False
}

try:
Expand Down Expand Up @@ -115,12 +119,12 @@ def find_or_create_session(userId: str, now: datetime.datetime):
if userId is None:
userId = '-'

SESSION_MAX_DURATION_DAYS = int(os.getenv('SESSION_MAX_DURATION_DAYS', '1'))
SESSION_MAX_DURATION_DAYS = float(os.getenv('CHB_SESSION_MAX_DURATION_DAYS', '1'))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why float?

datetimeLimit = now - datetime.timedelta(SESSION_MAX_DURATION_DAYS - 1)
startOfDay = datetime.datetime.combine(datetimeLimit, datetime.time.min)
# trovare una sessione con createdAt > datetimeLimit
try:
db_response = table_sessions.query(
dbResponse = table_sessions.query(
KeyConditionExpression=Key("userId").eq(userId) &
Key('createdAt').gt(startOfDay.isoformat()),
IndexName='SessionsByCreatedAtIndex',
Expand All @@ -130,7 +134,7 @@ def find_or_create_session(userId: str, now: datetime.datetime):
except (BotoCoreError, ClientError) as e:
raise HTTPException(status_code=422, detail=f"[find_or_create_session] userId: {userId}, error: {e}")

items = db_response.get('Items', [])
items = dbResponse.get('Items', [])
if len(items) == 0:
body = {
"id": f'{uuid.uuid4()}',
Expand All @@ -149,29 +153,44 @@ def find_or_create_session(userId: str, now: datetime.datetime):
return body


@app.get("/queries/{id}")
async def query_fetching(id: str):
# TODO: dynamoDB integration
body = {
"id": id,
"sessionId": "",
"question": "",
"answer": "",
"createdAt": "",
"queriedAt": ""
}
return body
@app.get("/queries")
async def queries_fetching(
sessionId: str | None = None,
page: int | None = 1,
pageSize: int | None = 10,
authorization: Annotated[str | None, Header()] = None
):
userId = current_user_id(authorization)
if sessionId is None:
sessionId = last_session_id(userId)

if sessionId is None:
result = []
else:
try:
dbResponse = table_queries.query(
KeyConditionExpression=Key('sessionId').eq(sessionId),
IndexName='QueriesByCreatedAtIndex',
ScanIndexForward=True
)
except (BotoCoreError, ClientError) as e:
raise HTTPException(status_code=422, detail=f"[queries_fetching] sessionId: {sessionId}, error: {e}")
result = dbResponse.get('Items', [])

return result


# retrieve sessions of current user
@app.get("/sessions")
async def sessions_fetching(
page: int = 1,
pageSize: int = 10,
authorization: Annotated[str | None, Header()] = None
):
userId = current_user_id(authorization)

try:
db_response = table_sessions.query(
dbResponse = table_sessions.query(
KeyConditionExpression=Key("userId").eq(userId),
IndexName='SessionsByCreatedAtIndex',
ScanIndexForward=False
Expand All @@ -180,7 +199,7 @@ async def sessions_fetching(
raise HTTPException(status_code=422, detail=f"[sessions_fetching] userId: {userId}, error: {e}")

# TODO: pagination
items = db_response.get('Items', [])
items = dbResponse.get('Items', [])
result = {
"items": items,
"page": 1,
Expand All @@ -200,12 +219,12 @@ async def session_delete(
"id": id,
}
try:
db_response_queries = table_queries.query(
dbResponse_queries = table_queries.query(
KeyConditionExpression=Key("sessionId").eq(id)
)
# TODO: use batch writer
# with table_sessions.batch_writer() as batch:
for query in db_response_queries['Items']:
for query in dbResponse_queries['Items']:
table_queries.delete_item(
Key={
"id": query["id"],
Expand All @@ -225,50 +244,47 @@ async def session_delete(

return body

@app.get("/queries")
async def queries_fetching(
sessionId: str | None = None,
authorization: Annotated[str | None, Header()] = None
):
userId = current_user_id(authorization)
if sessionId is None:
sessionId = last_session_id(userId)

try:
db_response = table_queries.query(
KeyConditionExpression=Key("sessionId").eq(sessionId) &
Key("id").eq(userId)
)
except (BotoCoreError, ClientError) as e:
raise HTTPException(status_code=422, detail=f"[queries_fetching] sessionId: {sessionId}, error: {e}")

result = db_response.get('Items', [])
return result


def last_session_id(userId: str):
db_response = table_sessions.query(
dbResponse = table_sessions.query(
IndexName='SessionsByCreatedAtIndex',
KeyConditionExpression=Key('userId').eq(userId),
ScanIndexForward=False,
Limit=1
)
items = db_response.get('Items', [])
return items[0] if items else None
items = dbResponse.get('Items', [])
return items[0].get('id', None) if items else None

@app.patch("/queries/{id}")
async def query_feedback (badAnswer: bool):
# TODO: dynamoDB integration
body = {
"id": "",
"sessionId": "",
"question": "",
"answer": "",
"badAnswer": badAnswer,
"createdAt": "",
"queriedAt": ""
}
return body
@app.patch("/sessions/{sessionId}/queries/{id}")
async def query_feedback (
id: str,
sessionId: str,
query: QueryFeedback,
authorization: Annotated[str | None, Header()] = None
):

try:
dbResponse = table_queries.update_item(
Key={
'sessionId': sessionId,
'id': id
},
UpdateExpression='SET #badAnswer = :badAnswer',
ExpressionAttributeNames={
'#badAnswer': 'badAnswer'
},
ExpressionAttributeValues={
':badAnswer': query.badAnswer
},
ReturnValues='ALL_NEW'
)
except (BotoCoreError, ClientError) as e:
raise HTTPException(status_code=422, detail=f"[query_feedback] id: {id}, sessionId: {sessionId}, error: {e}")

if 'Attributes' in dbResponse:
return dbResponse.get('Attributes')
else:
raise HTTPException(status_code=404, detail="Record not found")

handler = mangum.Mangum(app, lifespan="off")

Expand Down
3 changes: 2 additions & 1 deletion apps/chatbot/src/modules/chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def generate(self, query_str: str) -> str:
response_str = "Mi dispiace, ma non posso fornire informazioni che potrebbero essere pericolose o dannose."
logging.info("Gemini Safety: blocked query because retrieved DANGEROUS_CONTENT in it.")
else:
logging.info(exception_str)
response_str = ""
logging.info(f"[chatbot.py] generate: exception_str = {exception_str}")

return response_str
14 changes: 14 additions & 0 deletions apps/infrastructure/src/modules/chatbot/dynamodb.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ module "dynamodb_chatbot_queries" {
name = "sessionId"
type = "S"
},
{
name = "createdAt"
type = "S"
},
]

# LSI for query on created_at
local_secondary_indexes = [
{
name = "QueriesByCreatedAtIndex"
hash_key = "userId"
range_key = "createdAt"
projection_type = "ALL"
}
]
}

Expand Down
Loading