Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ repos:
language_version: python3
args: [--line-length=120]

- repo: https://github.com/facebook/pyrefly-pre-commit
rev: 0.49.0
hooks:
- id: pyrefly-check
name: Pyrefly (type checking)
pass_filenames: false
language: system

- repo: local
hooks:
- id: lint-githubactions
Expand Down
6 changes: 5 additions & 1 deletion .vscode/eps-assist-me.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,18 @@
"jest.nodeEnv": {
"POWERTOOLS_DEV": true
},
"python.defaultInterpreterPath": "/workspaces/eps-assist-me/.venv/bin/python"
"python.defaultInterpreterPath": "/workspaces/eps-assist-me/.venv/bin/python",
"python.pyrefly.displayTypeErrors": "default",
"python.pyrefly.disableLanguageServices": false,
"python.analysis.showHoverGoToLinks": true
},
"extensions": {
"recommendations": [
"AmazonWebServices.aws-toolkit-vscode",
"redhat.vscode-yaml",
"ms-python.python",
"ms-python.flake8",
"meta.pyrefly",
"eamodio.gitlens",
"github.vscode-pull-request-github",
"orta.vscode-jest",
Expand Down
2 changes: 2 additions & 0 deletions packages/bedrockLoggingConfigFunction/tests/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import os
import pytest
from unittest.mock import patch, MagicMock

# pyrefly: ignore [missing-module-attribute]
from app.handler import handler, send_response


Expand Down
1 change: 1 addition & 0 deletions packages/slackBotFunction/app/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def handler(event: dict, context: LambdaContext) -> dict:
"""
# direct invocation bypasses slack infrastructure entirely
if event.get("invocation_type") == "direct":
# pyrefly: ignore [bad-return]
return handle_direct_invocation(event, context)

app = get_app(logger=logger)
Expand Down
3 changes: 3 additions & 0 deletions packages/slackBotFunction/app/services/ai_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def process_ai_query(user_query: str, session_id: str | None = None) -> AIProces
reformulated_query = reformulate_query(user_query)

# session_id enables conversation continuity across multiple queries
# pyrefly: ignore [bad-argument-type]
kb_response = query_bedrock(reformulated_query, session_id)

logger.info(
Expand All @@ -29,6 +30,8 @@ def process_ai_query(user_query: str, session_id: str | None = None) -> AIProces
return {
"text": kb_response["output"]["text"],
"session_id": kb_response.get("sessionId"),
# pyrefly: ignore [bad-typed-dict-key]
"citations": kb_response.get("citations", []),
# pyrefly: ignore [bad-typed-dict-key]
"kb_response": kb_response, # slack needs raw bedrock data for session handling
}
4 changes: 4 additions & 0 deletions packages/slackBotFunction/app/services/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
logger = get_logger()


# pyrefly: ignore [bad-function-definition]
def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerateResponseTypeDef:
"""
Query Amazon Bedrock Knowledge Base using RAG (Retrieval-Augmented Generation)
Expand Down Expand Up @@ -67,19 +68,22 @@ def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerat
if prompt_template:
request_params["retrieveAndGenerateConfiguration"]["knowledgeBaseConfiguration"]["generationConfiguration"][
"promptTemplate"
# pyrefly: ignore [bad-typed-dict-key]
] = {"textPromptTemplate": prompt_template.get("prompt_text")}
logger.info(
"Using prompt template for RAG response generation", extra={"prompt_name": config.RAG_RESPONSE_PROMPT_NAME}
)

# Include session ID for conversation continuity across messages
if session_id:
# pyrefly: ignore [bad-typed-dict-key]
request_params["sessionId"] = session_id
logger.info("Using existing session", extra={"session_id": session_id})
else:
logger.info("Starting new conversation")

logger.debug("Retrieve and Generate", extra={"params": request_params})
# pyrefly: ignore [bad-argument-type]
response = client.retrieve_and_generate(**request_params)
logger.info(
"Got Bedrock response",
Expand Down
1 change: 1 addition & 0 deletions packages/slackBotFunction/app/services/dynamo.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def get_state_information(key: dict[str, Any]) -> GetItemOutputTableTypeDef:
return results


# pyrefly: ignore [bad-function-definition]
def store_state_information(item: dict[str, Any], condition: str = None) -> None:
start_time = time()
table = get_slack_bot_state_table()
Expand Down
2 changes: 2 additions & 0 deletions packages/slackBotFunction/app/services/prompt_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def parse_system_message(chat_cfg: dict) -> str:
return "\n\n".join(parts)


# pyrefly: ignore [bad-function-definition]
def load_prompt(prompt_name: str, prompt_version: str = None) -> dict:
"""
Load a prompt template from Amazon Bedrock Prompt Management.
Expand Down Expand Up @@ -106,6 +107,7 @@ def load_prompt(prompt_name: str, prompt_version: str = None) -> dict:

# Extract and render the prompt template
template_config = variant["templateConfiguration"]
# pyrefly: ignore [bad-argument-type]
prompt_text = _render_prompt(template_config)
actual_version = response.get("version", "DRAFT")

Expand Down
6 changes: 5 additions & 1 deletion packages/slackBotFunction/app/services/query_reformulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ def reformulate_query(user_query: str) -> str:
)

# Format the prompt with the user query (using double braces from Bedrock template)
# pyrefly: ignore [missing-attribute]
prompt = prompt_template.get("prompt_text").replace("{{user_query}}", user_query)
result = invoke_model(
prompt=prompt, model_id=model_id, client=client, inference_config=prompt_template.get("inference_config")
prompt=prompt,
model_id=model_id,
client=client,
inference_config=prompt_template.get("inference_config", {}),
)

reformulated_query = result["content"][0]["text"].strip()
Expand Down
1 change: 1 addition & 0 deletions packages/slackBotFunction/app/services/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def get_friendly_channel_name(channel_id: str, client: WebClient) -> str:
try:
conversations_info_response = client.conversations_info(channel=channel_id)
if conversations_info_response["ok"]:
# pyrefly: ignore [unsupported-operation]
friendly_channel_name = conversations_info_response["channel"]["name"]
else:
logger.warning(
Expand Down
26 changes: 26 additions & 0 deletions packages/slackBotFunction/app/slack/slack_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def cleanup_previous_unfeedback_qa(

except ClientError as e:
if e.response.get("Error", {}).get("Code") == "ConditionalCheckFailedException":
# pyrefly: ignore [unbound-name]
logger.info("Q&A has feedback - keeping for user", extra={"message_ts": previous_message_ts})
else:
logger.error("Error cleaning up Q&A", extra={"error": traceback.format_exc()})
Expand Down Expand Up @@ -163,8 +164,10 @@ def _create_feedback_blocks(

# Feedback buttons
blocks.append({"type": "divider", "block_id": "feedback-divider"})
# pyrefly: ignore [bad-argument-type]
blocks.append({"type": "context", "elements": [{"type": "mrkdwn", "text": "Was this response helpful?"}]})
blocks.append(
# pyrefly: ignore [bad-argument-type]
{
"type": "actions",
"block_id": "feedback_block",
Expand Down Expand Up @@ -201,6 +204,7 @@ def _create_response_body(citations: list[dict[str, str]], feedback_data: dict[s
result = _create_citation(citation, feedback_data, response_text)

action_buttons += result.get("action_buttons", [])
# pyrefly: ignore [bad-assignment]
response_text = result.get("response_text", response_text)

# Remove any citations that have not been returned
Expand All @@ -213,6 +217,7 @@ def _create_response_body(citations: list[dict[str, str]], feedback_data: dict[s
# Citation action block
if action_buttons:
blocks.append(
# pyrefly: ignore [bad-argument-type]
{
"type": "actions",
"block_id": "citation_actions",
Expand Down Expand Up @@ -400,6 +405,7 @@ def process_async_slack_event(event: Dict[str, Any], event_id: str, client: WebC
pull_request_id, _ = extract_pull_request_id(text=message_text)
forward_to_pull_request_lambda(
body={},
# pyrefly: ignore [bad-argument-type]
pull_request_id=pull_request_id,
event=event,
event_id=event_id,
Expand Down Expand Up @@ -453,6 +459,7 @@ def process_pull_request_slack_event(slack_event_data: Dict[str, Any]) -> None:
process_async_slack_event(event=event, event_id=event_id, client=client)
except Exception:
# we cant post a reply to slack for this error as we may not have details about where to post it
# pyrefly: ignore [unbound-name]
logger.error(processing_error_message, extra={"event_id": event_id, "error": traceback.format_exc()})


Expand Down Expand Up @@ -535,18 +542,23 @@ def process_slack_message(event: Dict[str, Any], event_id: str, client: WebClien
)

_handle_session_management(
# pyrefly: ignore [bad-argument-type]
**feedback_data,
session_data=session_data,
# pyrefly: ignore [bad-argument-type]
session_id=session_id,
# pyrefly: ignore [bad-argument-type]
kb_response=kb_response,
user_id=user_id,
conversation_key=conversation_key,
)

# Store Q&A pair for feedback correlation
# pyrefly: ignore [bad-argument-type, missing-attribute]
store_qa_pair(conversation_key, user_query, response_text, message_ts, kb_response.get("sessionId"), user_id)

try:
# pyrefly: ignore [bad-argument-type]
client.chat_update(channel=channel, ts=message_ts, text=response_text, blocks=blocks)
except Exception as e:
logger.error(
Expand All @@ -558,6 +570,7 @@ def process_slack_message(event: Dict[str, Any], event_id: str, client: WebClien
logger.error(f"Error processing message: {e}", extra={"event_id": event_id, "error": traceback.format_exc()})

# Try to notify user of error via Slack
# pyrefly: ignore [unbound-name]
post_error_message(channel=channel, thread_ts=thread_ts, client=client)


Expand Down Expand Up @@ -650,6 +663,7 @@ def store_feedback(
if feedback_text:
feedback_item["feedback_text"] = feedback_text[:4000]

# pyrefly: ignore [bad-argument-type]
store_state_information(item=feedback_item, condition=condition)

# Mark Q&A as having received feedback to prevent deletion
Expand Down Expand Up @@ -705,6 +719,7 @@ def process_formatted_bedrock_query(

blocks = _create_feedback_blocks(response_text, citations, feedback_data)

# pyrefly: ignore [bad-return]
return kb_response, response_text, blocks


Expand All @@ -715,6 +730,7 @@ def open_citation(channel: str, timestamp: str, message: Any, params: Dict[str,
# Citation details
title: str = params.get("title", "No title available.").strip()
body: str = params.get("body", "No citation text available.").strip()
# pyrefly: ignore [bad-assignment]
source_number: str = params.get("source_number")

# Remove any existing citation block/divider
Expand Down Expand Up @@ -792,6 +808,7 @@ def _toggle_button_style(element: dict) -> bool:


def process_command_test_request(command: Dict[str, Any], client: WebClient) -> None:
# pyrefly: ignore [not-iterable]
if "help" in command.get("text"):
process_command_test_help(command=command, client=client)
else:
Expand All @@ -806,6 +823,7 @@ def process_command_test_questions(command: Dict[str, Any], client: WebClient) -
logger.info(acknowledgement_msg, extra={"command": command})

# Extract parameters
# pyrefly: ignore [bad-argument-type]
params = extract_test_command_params(command.get("text"))

# Is the command targeting a PR
Expand Down Expand Up @@ -836,6 +854,7 @@ def process_command_test_questions(command: Dict[str, Any], client: WebClient) -
"channel": command["channel_id"],
"text": acknowledgement_msg,
}
# pyrefly: ignore [bad-argument-type]
client.chat_postEphemeral(**post_params, user=command.get("user_id"))

# Retrieve sample questions
Expand All @@ -857,12 +876,14 @@ def process_command_test_questions(command: Dict[str, Any], client: WebClient) -
process_command_test_ai_request,
question=question,
response=response, # Pass the response object we just got
# pyrefly: ignore [bad-argument-type]
output=output,
client=client,
)
futures.append(future)

post_params["text"] = "Testing complete, generating file..."
# pyrefly: ignore [bad-argument-type]
client.chat_postEphemeral(**post_params, user=command.get("user_id"))

aggregated_results = []
Expand Down Expand Up @@ -974,9 +995,11 @@ def get_conversation_session_data(conversation_key: str) -> Dict[str, Any]:
if "Item" in response:
logger.info("Found existing session", extra={"conversation_key": conversation_key})
return response["Item"]
# pyrefly: ignore [bad-return]
return None
except Exception:
logger.error("Error getting session", extra={"error": traceback.format_exc()})
# pyrefly: ignore [bad-return]
return None


Expand All @@ -987,6 +1010,7 @@ def get_latest_message_ts(conversation_key: str) -> str | None:
try:
response = get_state_information(key={"pk": conversation_key, "sk": constants.SESSION_SK})
if "Item" in response:
# pyrefly: ignore [bad-return]
return response["Item"].get("latest_message_ts")
return None
except Exception:
Expand All @@ -999,7 +1023,9 @@ def store_conversation_session(
session_id: str,
user_id: str,
channel_id: str,
# pyrefly: ignore [bad-function-definition]
thread_ts: str = None,
# pyrefly: ignore [bad-function-definition]
latest_message_ts: str = None,
) -> None:
"""
Expand Down
2 changes: 2 additions & 0 deletions packages/slackBotFunction/app/slack/slack_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def feedback_handler(body: Dict[str, Any], client: WebClient) -> None:
)
forward_to_pull_request_lambda(
body=body,
# pyrefly: ignore [bad-argument-type]
event=None,
event_id="",
store_pull_request_id=False,
Expand Down Expand Up @@ -157,6 +158,7 @@ def command_handler(body: Dict[str, Any], command: Dict[str, Any], client: WebCl
return

user_id = command.get("user_id")
# pyrefly: ignore [bad-argument-type]
session_pull_request_id = extract_test_command_params(command.get("text")).get("pr")
if session_pull_request_id:
logger.info(f"Command in pull request session {session_pull_request_id} from user {user_id}")
Expand Down
Loading
Loading