Skip to content

Commit e586855

Browse files
feat: Add tests back in
1 parent aaf539a commit e586855

File tree

6 files changed

+622
-364
lines changed

6 files changed

+622
-364
lines changed

packages/cdk/prompts/systemPrompt.txt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
# 1. ROLE & OBJECTIVE
1+
# 1. Persona
22
You are an AI assistant designed to provide guidance and references from your knowledge base to help users make decisions during onboarding.
33

4-
**CRITICAL RULE:** It is **VERY** important that you return **ALL** references found in the context for user examination.
4+
It is **VERY** important that you return **ALL** references found in the context for user examination.
55

66
---
77

88
# 2. THINKING PROCESS & LOGIC
99
Before generating a response, adhere to these processing rules:
1010

11-
## Question Analysis
11+
## A. Question Analysis
1212
1. **Detection:** Determine if the query contains one or multiple questions.
1313
2. **Decomposition:** Split complex queries into individual sub-questions.
1414
3. **Classification:** Identify if the question is Factual, Procedural, Diagnostic, Troubleshooting, or Clarification-seeking.
1515
4. **Multi-Question Strategy:** Number sub-questions clearly (Q1, Q2, etc).
1616

17-
## Entity Correction
17+
## B. Entity Correction
1818
- If you encounter "National Health Service Digital (NHSD)", automatically treat and output it as **"National Health Service England (NHSE)"**.
1919

20-
## RAG Confidence Scoring
21-
Evaluate retrieved context using these thresholds:
20+
## C. RAG Confidence Scoring
21+
```
22+
Evaluate retrieved context using these relevance score thresholds:
2223
- `Score > 0.85` : **High confidence**
2324
- `Score 0.70 - 0.85` : **Medium confidence**
2425
- `Score < 0.70` : **Low confidence**
26+
```
2527

2628
---
2729

@@ -52,14 +54,14 @@ You must use a specific variation of markdown. Follow this table strictly:
5254
---
5355

5456
# 5. BIBLIOGRAPHY GENERATOR
55-
## Requirements:
57+
**Requirements:**
5658
- Return **ALL** retrieved documents from the context.
5759
- Title length must be **< 50 characters**.
58-
- Text excerpt should be a long as possible and include text used for reasoning
60+
- Use the exact string format below (do not render it as a table or list).
5961

60-
## Format
61-
Use the following format when creating bibliography entries:
62-
<cit>source number||summary title||file contents||relevance score</cit>
62+
**Template:**
63+
```text
64+
<cit>source number||summary title||excerpt||relevance score||source name</cit>
6365

6466
# 6. Example
6567
"""
@@ -70,6 +72,7 @@ Short summary text
7072
A longer answer, going into more detail gained from the knowledge base and using critical thinking.
7173

7274
------
73-
<cit>1||Some PDF file||This is the precise snippet of the pdf file which answers the question.||0.98</cit>
74-
<cit>1||Some TXT file||A 500 word text excerpt which gives some inference to the answer, but the long citation helps fill in the information for the user, so it's worth the tokens.||0.76</cit>
75+
<cit>1||A document||This is the precise snippet of the pdf file which answers the question.||0.98||very_helpful_doc.pdf</cit>
76+
<cit>2||Another file||A 500 word text excerpt which gives some inference to the answer, but the long citation helps fill in the information for the user, so it's worth the tokens.||0.76||something_interesting.txt</cit>
77+
<cit>3||A useless file||This file doesn't contain anything that useful||0.05||folder/another/some_file.txt</cit>
7578
"""

packages/cdk/resources/BedrockPromptResources.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ export class BedrockPromptResources extends Construct {
2020
constructor(scope: Construct, id: string, props: BedrockPromptResourcesProps) {
2121
super(scope, id)
2222

23-
// Nova Pro is recommended for text generation tasks requiring high accuracy and complex understanding.
24-
const novaProModel = BedrockFoundationModel.AMAZON_NOVA_PRO_V1
25-
// Nova Lite is recommended for tasks
26-
const novaLiteModel = BedrockFoundationModel.AMAZON_NOVA_LITE_V1
23+
const ragModel = new BedrockFoundationModel("meta.llama3-70b-instruct-v1:0")
24+
const reformulationModel = BedrockFoundationModel.AMAZON_NOVA_LITE_V1
2725

2826
const queryReformulationPromptVariant = PromptVariant.text({
2927
variantName: "default",
30-
model: novaLiteModel,
28+
model: reformulationModel,
3129
promptVariables: ["topic"],
3230
promptText: props.settings.reformulationPrompt.text
3331
})
@@ -41,7 +39,7 @@ export class BedrockPromptResources extends Construct {
4139

4240
const ragResponsePromptVariant = PromptVariant.chat({
4341
variantName: "default",
44-
model: novaProModel,
42+
model: ragModel,
4543
promptVariables: ["query", "search_results"],
4644
system: props.settings.systemPrompt.text,
4745
messages: [props.settings.userPrompt]
@@ -59,8 +57,8 @@ export class BedrockPromptResources extends Construct {
5957
})
6058

6159
// expose model IDs for use in Lambda environment variables
62-
this.ragModelId = novaProModel.modelId
63-
this.queryReformulationModelId = novaLiteModel.modelId
60+
this.ragModelId = ragModel.modelId
61+
this.queryReformulationModelId = reformulationModel.modelId
6462

6563
this.queryReformulationPrompt = queryReformulationPrompt
6664
this.ragResponsePrompt = ragPrompt

packages/slackBotFunction/app/slack/slack_events.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ def _create_response_body(citations: list[dict[str, str]], feedback_data: dict[s
222222

223223

224224
def _create_citation(citation: dict[str, str], feedback_data: dict, response_text: str):
225-
logger.info("Creating citation", extra={"Citation": citation})
226225
invalid_body = "No document excerpt available."
227226
action_buttons = []
228227

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import sys
2+
import pytest
3+
from unittest.mock import Mock, patch, MagicMock
4+
5+
6+
@pytest.fixture
7+
def mock_logger():
8+
return MagicMock()
9+
10+
11+
@patch("app.utils.handler_utils.forward_event_to_pull_request_lambda")
12+
def test_process_async_slack_event_feedback(
13+
mock_forward_event_to_pull_request_lambda: Mock,
14+
mock_get_parameter: Mock,
15+
mock_env: Mock,
16+
):
17+
"""Test successful async event processing"""
18+
# set up mocks
19+
mock_client = Mock()
20+
21+
# delete and import module to test
22+
if "app.slack.slack_events" in sys.modules:
23+
del sys.modules["app.slack.slack_events"]
24+
from app.slack.slack_events import process_async_slack_event
25+
26+
# perform operation
27+
slack_event_data = {
28+
"text": "feedback: this is some feedback",
29+
"user": "U456",
30+
"channel": "C789",
31+
"ts": "1234567890.123",
32+
}
33+
with patch("app.slack.slack_events.process_feedback_event") as mock_process_feedback_event, patch(
34+
"app.slack.slack_events.process_slack_message"
35+
) as mock_process_slack_message:
36+
process_async_slack_event(event=slack_event_data, event_id="evt123", client=mock_client)
37+
mock_forward_event_to_pull_request_lambda.assert_not_called()
38+
mock_process_feedback_event.assert_called_once_with(
39+
message_text="feedback: this is some feedback",
40+
conversation_key="thread#C789#1234567890.123",
41+
user_id="U456",
42+
channel_id="C789",
43+
thread_root="1234567890.123",
44+
client=mock_client,
45+
event=slack_event_data,
46+
)
47+
mock_process_slack_message.assert_not_called()
48+
49+
50+
@patch("app.utils.handler_utils.is_latest_message")
51+
def test_process_async_slack_action_positive(
52+
mock_is_latest_message: Mock,
53+
mock_get_parameter: Mock,
54+
mock_env: Mock,
55+
):
56+
"""Test successful async action processing"""
57+
# set up mocks
58+
mock_client = Mock()
59+
mock_is_latest_message.return_value = True
60+
61+
# delete and import module to test
62+
if "app.slack.slack_events" in sys.modules:
63+
del sys.modules["app.slack.slack_events"]
64+
from app.slack.slack_events import process_async_slack_action
65+
66+
feedback_value = '{"ck":"thread#C123#123","ch":"C123","mt":"1759845126.972219","tt":"1759845114.407989"}'
67+
68+
# perform operation
69+
slack_action_data = {
70+
"type": "block_actions",
71+
"user": {"id": "U123"},
72+
"channel": {"id": "C123"},
73+
"message": {"ts": "1759845126.972219"},
74+
"actions": [{"action_id": "feedback_yes", "value": feedback_value}],
75+
}
76+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
77+
process_async_slack_action(body=slack_action_data, client=mock_client)
78+
79+
# assertions
80+
mock_store_feedback.assert_called_once_with(
81+
conversation_key="thread#C123#123",
82+
feedback_type="positive",
83+
user_id="U123",
84+
channel_id="C123",
85+
thread_ts="1759845114.407989",
86+
message_ts="1759845126.972219",
87+
client=mock_client,
88+
)
89+
mock_client.chat_postMessage.assert_called_once_with(
90+
channel="C123",
91+
text="Thank you for your feedback.",
92+
thread_ts="1759845114.407989",
93+
)
94+
95+
96+
@patch("app.utils.handler_utils.is_latest_message")
97+
def test_process_async_slack_action_negative(
98+
mock_is_latest_message: Mock,
99+
mock_get_parameter: Mock,
100+
mock_env: Mock,
101+
):
102+
"""Test successful async action processing"""
103+
# set up mocks
104+
mock_client = Mock()
105+
mock_is_latest_message.return_value = True
106+
107+
# delete and import module to test
108+
if "app.slack.slack_events" in sys.modules:
109+
del sys.modules["app.slack.slack_events"]
110+
from app.slack.slack_events import process_async_slack_action
111+
112+
feedback_value = '{"ck":"thread#C123#123","ch":"C123","mt":"1759845126.972219","tt":"1759845114.407989"}'
113+
114+
# perform operation
115+
slack_action_data = {
116+
"type": "block_actions",
117+
"user": {"id": "U123"},
118+
"channel": {"id": "C123"},
119+
"message": {"ts": "1759845126.972219"},
120+
"actions": [{"action_id": "feedback_no", "value": feedback_value}],
121+
}
122+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
123+
process_async_slack_action(body=slack_action_data, client=mock_client)
124+
125+
# assertions
126+
mock_store_feedback.assert_called_once_with(
127+
conversation_key="thread#C123#123",
128+
feedback_type="negative",
129+
user_id="U123",
130+
channel_id="C123",
131+
thread_ts="1759845114.407989",
132+
message_ts="1759845126.972219",
133+
client=mock_client,
134+
)
135+
mock_client.chat_postMessage.assert_called_once_with(
136+
channel="C123",
137+
text='Please let us know how the answer could be improved. Start your message with "feedback:"',
138+
thread_ts="1759845114.407989",
139+
)
140+
141+
142+
@patch("app.utils.handler_utils.is_latest_message")
143+
def test_process_async_slack_action_not_latest(
144+
mock_is_latest_message: Mock,
145+
mock_get_parameter: Mock,
146+
mock_env: Mock,
147+
):
148+
"""Test successful async action processing"""
149+
# set up mocks
150+
mock_client = Mock()
151+
mock_is_latest_message.return_value = False
152+
153+
# delete and import module to test
154+
if "app.slack.slack_events" in sys.modules:
155+
del sys.modules["app.slack.slack_events"]
156+
from app.slack.slack_events import process_async_slack_action
157+
158+
feedback_value = '{"ck":"thread#C123#123","ch":"C123","mt":"1759845126.972219","tt":"1759845114.407989"}'
159+
160+
# perform operation
161+
slack_action_data = {
162+
"type": "block_actions",
163+
"user": {"id": "U123"},
164+
"channel": {"id": "C123"},
165+
"actions": [{"action_id": "feedback_no", "value": feedback_value}],
166+
}
167+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
168+
process_async_slack_action(body=slack_action_data, client=mock_client)
169+
170+
# assertions
171+
mock_store_feedback.assert_not_called()
172+
mock_client.chat_postMessage.assert_not_called()
173+
174+
175+
@patch("app.utils.handler_utils.is_latest_message")
176+
def test_process_async_slack_action_unknown_action(
177+
mock_is_latest_message: Mock,
178+
mock_get_parameter: Mock,
179+
mock_env: Mock,
180+
):
181+
"""Test successful async action processing"""
182+
# set up mocks
183+
mock_client = Mock()
184+
mock_is_latest_message.return_value = True
185+
186+
# delete and import module to test
187+
if "app.slack.slack_events" in sys.modules:
188+
del sys.modules["app.slack.slack_events"]
189+
from app.slack.slack_events import process_async_slack_action
190+
191+
feedback_value = '{"ck":"thread#C123#123","ch":"C123","mt":"1759845126.972219","tt":"1759845114.407989"}'
192+
193+
# perform operation
194+
slack_action_data = {
195+
"type": "block_actions",
196+
"user": {"id": "U123"},
197+
"channel": {"id": "C123"},
198+
"actions": [{"action_id": "I_Do_Not_Know_This_Action", "value": feedback_value}],
199+
}
200+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
201+
process_async_slack_action(body=slack_action_data, client=mock_client)
202+
203+
# assertions
204+
mock_store_feedback.assert_not_called()
205+
mock_client.chat_postMessage.assert_not_called()
206+
207+
208+
def test_process_feedback_event():
209+
# set up mocks
210+
mock_client = Mock()
211+
212+
# delete and import module to test
213+
if "app.slack.slack_events" in sys.modules:
214+
del sys.modules["app.slack.slack_events"]
215+
from app.slack.slack_events import process_feedback_event
216+
217+
# perform operation
218+
mock_event = {}
219+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
220+
process_feedback_event(
221+
message_text="feedback: this is some feedback",
222+
conversation_key="thread#C123#123",
223+
user_id="U123",
224+
channel_id="C123",
225+
thread_root="1759845114.407989",
226+
event=mock_event,
227+
client=mock_client,
228+
)
229+
230+
# assertions
231+
mock_store_feedback.assert_called_once_with(
232+
conversation_key="thread#C123#123",
233+
feedback_type="additional",
234+
user_id="U123",
235+
channel_id="C123",
236+
thread_ts="1759845114.407989",
237+
message_ts=None,
238+
feedback_text="this is some feedback",
239+
client=mock_client,
240+
)
241+
mock_client.chat_postMessage.assert_called_once_with(
242+
channel="C123", text="Thank you for your feedback.", thread_ts="1759845114.407989"
243+
)
244+
245+
246+
@patch("app.services.slack.post_error_message")
247+
def test_process_feedback_event_error(
248+
mock_post_error_message: Mock,
249+
):
250+
# set up mocks
251+
mock_client = Mock()
252+
253+
# delete and import module to test
254+
if "app.slack.slack_events" in sys.modules:
255+
del sys.modules["app.slack.slack_events"]
256+
from app.slack.slack_events import process_feedback_event
257+
258+
# perform operation
259+
mock_event = {
260+
"channel": "C123",
261+
"thread_ts": "123",
262+
}
263+
with patch("app.slack.slack_events.store_feedback") as mock_store_feedback:
264+
mock_store_feedback.side_effect = Exception("There was an error")
265+
process_feedback_event(
266+
message_text="feedback: this is some feedback",
267+
conversation_key="thread#C123#123",
268+
user_id="U123",
269+
channel_id="C123",
270+
thread_root="1759845114.407989",
271+
event=mock_event,
272+
client=mock_client,
273+
)
274+
275+
# assertions
276+
mock_post_error_message.assert_called_once_with(channel="C123", thread_ts="123", client=mock_client)

0 commit comments

Comments
 (0)