rasa-pro 3.13.1a6__py3-none-any.whl → 3.13.1a8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/builder/project_generator.py +20 -8
- rasa/builder/service.py +32 -19
- rasa/cli/project_templates/finance/actions/database.py +108 -65
- rasa/cli/project_templates/finance/tests/conversation_repair/cancellations.yml +12 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/cannot_handle.yml +7 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/chitchat.yml +7 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/clarification.yml +9 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/completion.yml +18 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/corrections.yml +17 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/digressions.yml +32 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/human_handoff.yml +21 -0
- rasa/cli/project_templates/finance/tests/conversation_repair/skipping_collect_steps.yml +16 -0
- rasa/cli/project_templates/finance/tests/demo_scripts/main.yml +16 -0
- rasa/cli/project_templates/finance/tests/happy_paths/balance_verification.yml +15 -0
- rasa/cli/project_templates/finance/tests/happy_paths/banking_questions.yml +12 -0
- rasa/cli/project_templates/finance/tests/happy_paths/card_blocking.yml +52 -0
- rasa/cli/project_templates/finance/tests/happy_paths/money_transfer.yml +136 -0
- rasa/cli/project_templates/finance/tests/happy_paths/payee_management.yml +27 -0
- rasa/cli/project_templates/finance/tests/happy_paths/user_greeted.yml +5 -0
- rasa/cli/project_templates/plain/domain.yml +1 -1
- rasa/cli/project_templates/telco/actions/actions_billing.py +115 -70
- rasa/cli/project_templates/telco/actions/actions_get_data_from_db.py +26 -11
- rasa/core/channels/studio_chat.py +4 -2
- rasa/core/policies/enterprise_search_policy.py +1 -1
- rasa/dialogue_understanding/generator/flow_retrieval.py +10 -9
- rasa/version.py +1 -1
- {rasa_pro-3.13.1a6.dist-info → rasa_pro-3.13.1a8.dist-info}/METADATA +1 -1
- {rasa_pro-3.13.1a6.dist-info → rasa_pro-3.13.1a8.dist-info}/RECORD +31 -16
- rasa/cli/project_templates/finance/requirements.txt +0 -1
- {rasa_pro-3.13.1a6.dist-info → rasa_pro-3.13.1a8.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.1a6.dist-info → rasa_pro-3.13.1a8.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.1a6.dist-info → rasa_pro-3.13.1a8.dist-info}/entry_points.txt +0 -0
|
@@ -44,7 +44,7 @@ class ProjectGenerator:
|
|
|
44
44
|
skill_description: str,
|
|
45
45
|
template: ProjectTemplateName,
|
|
46
46
|
max_retries: Optional[int] = None,
|
|
47
|
-
) -> Dict[str, str]:
|
|
47
|
+
) -> Dict[str, Optional[str]]:
|
|
48
48
|
"""Generate a Rasa project with retry logic for validation failures.
|
|
49
49
|
|
|
50
50
|
Args:
|
|
@@ -72,7 +72,7 @@ class ProjectGenerator:
|
|
|
72
72
|
|
|
73
73
|
async def _generate_with_retry(
|
|
74
74
|
messages: List[Dict[str, Any]], attempts_left: int
|
|
75
|
-
):
|
|
75
|
+
) -> Dict[str, Optional[str]]:
|
|
76
76
|
try:
|
|
77
77
|
# Generate project data using LLM
|
|
78
78
|
project_data = await llm_service.generate_rasa_project(messages)
|
|
@@ -178,7 +178,7 @@ class ProjectGenerator:
|
|
|
178
178
|
except Exception as e:
|
|
179
179
|
raise ValidationError(f"Failed to create importer: {e}")
|
|
180
180
|
|
|
181
|
-
def get_bot_files(self) -> Dict[str, str]:
|
|
181
|
+
def get_bot_files(self) -> Dict[str, Optional[str]]:
|
|
182
182
|
"""Get the current bot files by reading from disk."""
|
|
183
183
|
bot_files = {}
|
|
184
184
|
|
|
@@ -190,15 +190,27 @@ class ProjectGenerator:
|
|
|
190
190
|
relative_path = file.relative_to(self.project_folder)
|
|
191
191
|
|
|
192
192
|
# Skip hidden files and directories (any path component starting with '.')
|
|
193
|
+
# as well as `__pycache__` folders
|
|
193
194
|
if any(part.startswith(".") for part in relative_path.parts):
|
|
194
195
|
continue
|
|
195
196
|
|
|
197
|
+
if "__pycache__" in relative_path.parts:
|
|
198
|
+
continue
|
|
199
|
+
|
|
196
200
|
# exclude the project_folder / models folder
|
|
197
201
|
if relative_path.parts[0] == "models":
|
|
198
202
|
continue
|
|
199
203
|
|
|
200
204
|
# Read file content and store with relative path as key
|
|
201
|
-
|
|
205
|
+
try:
|
|
206
|
+
bot_files[relative_path.as_posix()] = file.read_text(encoding="utf-8")
|
|
207
|
+
except Exception as e:
|
|
208
|
+
structlogger.debug(
|
|
209
|
+
"project_generator.get_bot_files.error",
|
|
210
|
+
error=str(e),
|
|
211
|
+
file_path=file.as_posix(),
|
|
212
|
+
)
|
|
213
|
+
bot_files[relative_path.as_posix()] = None
|
|
202
214
|
|
|
203
215
|
return bot_files
|
|
204
216
|
|
|
@@ -223,7 +235,7 @@ class ProjectGenerator:
|
|
|
223
235
|
else:
|
|
224
236
|
return f"data/flows/{flow_id}.yml"
|
|
225
237
|
|
|
226
|
-
def _update_bot_files_from_llm_response(self, project_data: Dict[str, Any]):
|
|
238
|
+
def _update_bot_files_from_llm_response(self, project_data: Dict[str, Any]) -> None:
|
|
227
239
|
"""Update the bot files with generated data by writing to disk."""
|
|
228
240
|
files = {"domain.yml": dump_obj_as_yaml_to_string(project_data["domain"])}
|
|
229
241
|
# split up flows into one file per flow in the /flows folder
|
|
@@ -236,21 +248,21 @@ class ProjectGenerator:
|
|
|
236
248
|
self._cleanup_flows()
|
|
237
249
|
self.update_bot_files(files)
|
|
238
250
|
|
|
239
|
-
def _cleanup_flows(self):
|
|
251
|
+
def _cleanup_flows(self) -> None:
|
|
240
252
|
"""Cleanup the flows folder."""
|
|
241
253
|
flows_folder = self.project_folder / "data" / "flows"
|
|
242
254
|
if flows_folder.exists():
|
|
243
255
|
shutil.rmtree(flows_folder)
|
|
244
256
|
flows_folder.mkdir(parents=True, exist_ok=True)
|
|
245
257
|
|
|
246
|
-
def update_bot_files(self, files: Dict[str, str]):
|
|
258
|
+
def update_bot_files(self, files: Dict[str, str]) -> None:
|
|
247
259
|
"""Update bot files with new content by writing to disk."""
|
|
248
260
|
for filename, content in files.items():
|
|
249
261
|
file_path = Path(subpath(self.project_folder, filename))
|
|
250
262
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
251
263
|
file_path.write_text(content, encoding="utf-8")
|
|
252
264
|
|
|
253
|
-
def cleanup(self):
|
|
265
|
+
def cleanup(self) -> None:
|
|
254
266
|
"""Cleanup the project folder."""
|
|
255
267
|
# remove all the files and folders in the project folder resulting
|
|
256
268
|
# in an empty folder
|
rasa/builder/service.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import structlog
|
|
6
|
-
from sanic import Sanic, response
|
|
6
|
+
from sanic import HTTPResponse, Sanic, response
|
|
7
7
|
from sanic.request import Request
|
|
8
8
|
|
|
9
9
|
from rasa.builder import config
|
|
@@ -72,7 +72,7 @@ class PromptToBotService:
|
|
|
72
72
|
)
|
|
73
73
|
return StudioChatInput.from_credentials(credentials=studio_chat_credentials)
|
|
74
74
|
|
|
75
|
-
def setup_routes(self):
|
|
75
|
+
def setup_routes(self) -> None:
|
|
76
76
|
"""Setup all API routes."""
|
|
77
77
|
# Core endpoints
|
|
78
78
|
self.app.add_route(
|
|
@@ -93,11 +93,11 @@ class PromptToBotService:
|
|
|
93
93
|
|
|
94
94
|
channels.channel.register([self.input_channel], self.app, route="/webhooks/")
|
|
95
95
|
|
|
96
|
-
def setup_middleware(self):
|
|
96
|
+
def setup_middleware(self) -> None:
|
|
97
97
|
"""Setup middleware for request/response processing."""
|
|
98
98
|
|
|
99
|
-
@self.app.middleware("request")
|
|
100
|
-
async def log_request(request):
|
|
99
|
+
@self.app.middleware("request") # type: ignore[no-untyped-call]
|
|
100
|
+
async def log_request(request: Request) -> None:
|
|
101
101
|
structlogger.info(
|
|
102
102
|
"request.received",
|
|
103
103
|
method=request.method,
|
|
@@ -105,8 +105,8 @@ class PromptToBotService:
|
|
|
105
105
|
remote_addr=request.remote_addr or "unknown",
|
|
106
106
|
)
|
|
107
107
|
|
|
108
|
-
@self.app.middleware("response")
|
|
109
|
-
async def log_response(request, response):
|
|
108
|
+
@self.app.middleware("response") # type: ignore[no-untyped-call]
|
|
109
|
+
async def log_response(request: Request, response: HTTPResponse) -> None:
|
|
110
110
|
structlogger.info(
|
|
111
111
|
"request.completed",
|
|
112
112
|
method=request.method,
|
|
@@ -114,11 +114,11 @@ class PromptToBotService:
|
|
|
114
114
|
status=response.status,
|
|
115
115
|
)
|
|
116
116
|
|
|
117
|
-
async def health(self, request: Request):
|
|
117
|
+
async def health(self, request: Request) -> HTTPResponse:
|
|
118
118
|
"""Health check endpoint."""
|
|
119
119
|
return response.json({"status": "ok", "service": "prompt-to-bot"})
|
|
120
120
|
|
|
121
|
-
async def handle_prompt_to_bot(self, request: Request):
|
|
121
|
+
async def handle_prompt_to_bot(self, request: Request) -> HTTPResponse:
|
|
122
122
|
"""Handle prompt-to-bot generation requests."""
|
|
123
123
|
try:
|
|
124
124
|
# Validate request
|
|
@@ -191,11 +191,13 @@ class PromptToBotService:
|
|
|
191
191
|
except Exception as e:
|
|
192
192
|
structlogger.error("prompt_to_bot.unexpected_error", error=str(e))
|
|
193
193
|
return response.json(
|
|
194
|
-
ApiErrorResponse(
|
|
194
|
+
ApiErrorResponse(
|
|
195
|
+
error="Unexpected error occurred", details=None
|
|
196
|
+
).model_dump(),
|
|
195
197
|
status=500,
|
|
196
198
|
)
|
|
197
199
|
|
|
198
|
-
async def handle_template_to_bot(self, request: Request):
|
|
200
|
+
async def handle_template_to_bot(self, request: Request) -> HTTPResponse:
|
|
199
201
|
"""Handle template-to-bot generation requests."""
|
|
200
202
|
try:
|
|
201
203
|
# Validate request
|
|
@@ -268,18 +270,24 @@ class PromptToBotService:
|
|
|
268
270
|
except Exception as e:
|
|
269
271
|
structlogger.error("template_to_bot.unexpected_error", error=str(e))
|
|
270
272
|
return response.json(
|
|
271
|
-
ApiErrorResponse(
|
|
273
|
+
ApiErrorResponse(
|
|
274
|
+
error="Unexpected error occurred", details=None
|
|
275
|
+
).model_dump(),
|
|
272
276
|
status=500,
|
|
273
277
|
)
|
|
274
278
|
|
|
275
|
-
async def get_bot_data(self, request: Request):
|
|
279
|
+
async def get_bot_data(self, request: Request) -> HTTPResponse:
|
|
276
280
|
"""Get current bot data."""
|
|
277
281
|
bot_files = self.project_generator.get_bot_files()
|
|
278
282
|
return response.json(
|
|
279
|
-
ApiResponse(
|
|
283
|
+
ApiResponse(
|
|
284
|
+
status="success",
|
|
285
|
+
message="Bot data fetched successfully",
|
|
286
|
+
data={"bot_data": bot_files},
|
|
287
|
+
).model_dump()
|
|
280
288
|
)
|
|
281
289
|
|
|
282
|
-
async def update_bot_data(self, request: Request):
|
|
290
|
+
async def update_bot_data(self, request: Request) -> None:
|
|
283
291
|
"""Update bot data with server-sent events for progress tracking."""
|
|
284
292
|
sse_response = await request.respond(content_type="text/event-stream")
|
|
285
293
|
|
|
@@ -376,7 +384,7 @@ class PromptToBotService:
|
|
|
376
384
|
finally:
|
|
377
385
|
await sse_response.eof()
|
|
378
386
|
|
|
379
|
-
async def llm_builder(self, request: Request):
|
|
387
|
+
async def llm_builder(self, request: Request) -> HTTPResponse:
|
|
380
388
|
"""Handle LLM builder requests."""
|
|
381
389
|
try:
|
|
382
390
|
# Validate request
|
|
@@ -413,7 +421,10 @@ class PromptToBotService:
|
|
|
413
421
|
except Exception as e:
|
|
414
422
|
structlogger.error("llm_builder.unexpected_error", error=str(e))
|
|
415
423
|
return response.json(
|
|
416
|
-
ApiErrorResponse(
|
|
424
|
+
ApiErrorResponse(
|
|
425
|
+
error="Unexpected error in LLM builder",
|
|
426
|
+
details=None,
|
|
427
|
+
).model_dump(),
|
|
417
428
|
status=500,
|
|
418
429
|
)
|
|
419
430
|
|
|
@@ -429,11 +440,13 @@ class PromptToBotService:
|
|
|
429
440
|
return None
|
|
430
441
|
|
|
431
442
|
@staticmethod
|
|
432
|
-
async def _send_sse_event(
|
|
443
|
+
async def _send_sse_event(
|
|
444
|
+
sse_response: HTTPResponse, event: ServerSentEvent
|
|
445
|
+
) -> None:
|
|
433
446
|
"""Send a server-sent event."""
|
|
434
447
|
await sse_response.send(event.format())
|
|
435
448
|
|
|
436
|
-
def run(self):
|
|
449
|
+
def run(self) -> None:
|
|
437
450
|
"""Run the service."""
|
|
438
451
|
structlogger.info(
|
|
439
452
|
"service.starting",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import csv
|
|
1
2
|
import logging
|
|
3
|
+
from datetime import datetime
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Any, Dict, List, Optional
|
|
4
6
|
|
|
5
|
-
import pandas as pd
|
|
6
|
-
|
|
7
7
|
|
|
8
8
|
class Database:
|
|
9
9
|
def __init__(self, csv_path: Optional[Path] = None) -> None:
|
|
@@ -28,14 +28,39 @@ class Database:
|
|
|
28
28
|
|
|
29
29
|
return logger
|
|
30
30
|
|
|
31
|
+
def _read_csv(self, filename: str) -> List[Dict[str, Any]]:
|
|
32
|
+
"""Read CSV file and return list of dictionaries."""
|
|
33
|
+
try:
|
|
34
|
+
with open(self.csv_path / filename, "r", newline="") as csvfile:
|
|
35
|
+
reader = csv.DictReader(csvfile)
|
|
36
|
+
return list(reader)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
self.logger.error(f"Error reading CSV file {filename}: {e}")
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
def _write_csv(self, filename: str, data: List[Dict[str, Any]]) -> bool:
|
|
42
|
+
"""Write list of dictionaries to CSV file."""
|
|
43
|
+
try:
|
|
44
|
+
if not data:
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
with open(self.csv_path / filename, "w", newline="") as csvfile:
|
|
48
|
+
writer = csv.DictWriter(csvfile, fieldnames=data[0].keys())
|
|
49
|
+
writer.writeheader()
|
|
50
|
+
writer.writerows(data)
|
|
51
|
+
return True
|
|
52
|
+
except Exception as e:
|
|
53
|
+
self.logger.error(f"Error writing CSV file {filename}: {e}")
|
|
54
|
+
return False
|
|
55
|
+
|
|
31
56
|
def get_user_by_name(self, username: str) -> Optional[Dict[str, Any]]:
|
|
32
57
|
"""Get user information by username."""
|
|
33
58
|
try:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
59
|
+
users = self._read_csv("users.csv")
|
|
60
|
+
for user in users:
|
|
61
|
+
if user["name"] == username:
|
|
62
|
+
return user
|
|
63
|
+
return None
|
|
39
64
|
except Exception as e:
|
|
40
65
|
self.logger.error(f"Error getting user by name: {e}")
|
|
41
66
|
return None
|
|
@@ -43,11 +68,11 @@ class Database:
|
|
|
43
68
|
def get_user_by_id(self, user_id: int) -> Optional[Dict[str, Any]]:
|
|
44
69
|
"""Get user information by user_id."""
|
|
45
70
|
try:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return
|
|
71
|
+
users = self._read_csv("users.csv")
|
|
72
|
+
for user in users:
|
|
73
|
+
if int(user["id"]) == user_id:
|
|
74
|
+
return user
|
|
75
|
+
return None
|
|
51
76
|
except Exception as e:
|
|
52
77
|
self.logger.error(f"Error getting user by id: {e}")
|
|
53
78
|
return None
|
|
@@ -57,13 +82,14 @@ class Database:
|
|
|
57
82
|
) -> Optional[Dict[str, Any]]:
|
|
58
83
|
"""Get account information by user_id and account number."""
|
|
59
84
|
try:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
accounts = self._read_csv("accounts.csv")
|
|
86
|
+
for account in accounts:
|
|
87
|
+
if (
|
|
88
|
+
int(account["user_id"]) == user_id
|
|
89
|
+
and account["number"] == account_number
|
|
90
|
+
):
|
|
91
|
+
return account
|
|
92
|
+
return None
|
|
67
93
|
except Exception as e:
|
|
68
94
|
self.logger.error(f"Error getting account: {e}")
|
|
69
95
|
return None
|
|
@@ -71,9 +97,10 @@ class Database:
|
|
|
71
97
|
def get_accounts_by_user(self, user_id: int) -> List[Dict[str, Any]]:
|
|
72
98
|
"""Get all accounts for a user."""
|
|
73
99
|
try:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
accounts = self._read_csv("accounts.csv")
|
|
101
|
+
return [
|
|
102
|
+
account for account in accounts if int(account["user_id"]) == user_id
|
|
103
|
+
]
|
|
77
104
|
except Exception as e:
|
|
78
105
|
self.logger.error(f"Error getting accounts by user: {e}")
|
|
79
106
|
return []
|
|
@@ -81,9 +108,8 @@ class Database:
|
|
|
81
108
|
def get_payees_by_user(self, user_id: int) -> List[Dict[str, Any]]:
|
|
82
109
|
"""Get all payees for a user."""
|
|
83
110
|
try:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return payees_data.to_dict("records")
|
|
111
|
+
payees = self._read_csv("payees.csv")
|
|
112
|
+
return [payee for payee in payees if int(payee["user_id"]) == user_id]
|
|
87
113
|
except Exception as e:
|
|
88
114
|
self.logger.error(f"Error getting payees by user: {e}")
|
|
89
115
|
return []
|
|
@@ -93,11 +119,11 @@ class Database:
|
|
|
93
119
|
) -> Optional[Dict[str, Any]]:
|
|
94
120
|
"""Get payee information by name and user_id."""
|
|
95
121
|
try:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return
|
|
122
|
+
payees = self._read_csv("payees.csv")
|
|
123
|
+
for payee in payees:
|
|
124
|
+
if payee["name"] == payee_name and int(payee["user_id"]) == user_id:
|
|
125
|
+
return payee
|
|
126
|
+
return None
|
|
101
127
|
except Exception as e:
|
|
102
128
|
self.logger.error(f"Error getting payee by name and user: {e}")
|
|
103
129
|
return None
|
|
@@ -105,9 +131,8 @@ class Database:
|
|
|
105
131
|
def get_cards_by_user(self, user_id: int) -> List[Dict[str, Any]]:
|
|
106
132
|
"""Get all cards for a user."""
|
|
107
133
|
try:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return cards_data.to_dict("records")
|
|
134
|
+
cards = self._read_csv("cards.csv")
|
|
135
|
+
return [card for card in cards if int(card["user_id"]) == user_id]
|
|
111
136
|
except Exception as e:
|
|
112
137
|
self.logger.error(f"Error getting cards by user: {e}")
|
|
113
138
|
return []
|
|
@@ -115,11 +140,11 @@ class Database:
|
|
|
115
140
|
def get_card_by_number(self, card_number: str) -> Optional[Dict[str, Any]]:
|
|
116
141
|
"""Get card information by card number."""
|
|
117
142
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return
|
|
143
|
+
cards = self._read_csv("cards.csv")
|
|
144
|
+
for card in cards:
|
|
145
|
+
if card["number"] == card_number:
|
|
146
|
+
return card
|
|
147
|
+
return None
|
|
123
148
|
except Exception as e:
|
|
124
149
|
self.logger.error(f"Error getting card by number: {e}")
|
|
125
150
|
return None
|
|
@@ -127,10 +152,12 @@ class Database:
|
|
|
127
152
|
def update_card_status(self, card_number: str, status: str) -> bool:
|
|
128
153
|
"""Update card status."""
|
|
129
154
|
try:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
cards = self._read_csv("cards.csv")
|
|
156
|
+
for card in cards:
|
|
157
|
+
if card["number"] == card_number:
|
|
158
|
+
card["status"] = status
|
|
159
|
+
break
|
|
160
|
+
return self._write_csv("cards.csv", cards)
|
|
134
161
|
except Exception as e:
|
|
135
162
|
self.logger.error(f"Error updating card status: {e}")
|
|
136
163
|
return False
|
|
@@ -146,27 +173,28 @@ class Database:
|
|
|
146
173
|
) -> bool:
|
|
147
174
|
"""Add a new payee."""
|
|
148
175
|
try:
|
|
149
|
-
|
|
176
|
+
payees = self._read_csv("payees.csv")
|
|
150
177
|
|
|
151
178
|
# Get the next ID
|
|
152
|
-
next_id =
|
|
179
|
+
next_id = 1
|
|
180
|
+
if payees:
|
|
181
|
+
next_id = max(int(payee["id"]) for payee in payees) + 1
|
|
153
182
|
|
|
154
183
|
# Create new payee record
|
|
155
184
|
new_payee = {
|
|
156
|
-
"id": next_id,
|
|
157
|
-
"user_id": user_id,
|
|
185
|
+
"id": str(next_id),
|
|
186
|
+
"user_id": str(user_id),
|
|
158
187
|
"name": name,
|
|
159
188
|
"sort_code": sort_code,
|
|
160
189
|
"account_number": account_number,
|
|
161
190
|
"type": payee_type,
|
|
162
191
|
"reference": reference,
|
|
163
|
-
"added_at":
|
|
192
|
+
"added_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
164
193
|
}
|
|
165
194
|
|
|
166
|
-
# Add to
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return True
|
|
195
|
+
# Add to list and save
|
|
196
|
+
payees.append(new_payee)
|
|
197
|
+
return self._write_csv("payees.csv", payees)
|
|
170
198
|
except Exception as e:
|
|
171
199
|
self.logger.error(f"Error adding payee: {e}")
|
|
172
200
|
return False
|
|
@@ -174,10 +202,15 @@ class Database:
|
|
|
174
202
|
def remove_payee(self, payee_name: str, user_id: int) -> bool:
|
|
175
203
|
"""Remove a payee."""
|
|
176
204
|
try:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
205
|
+
payees = self._read_csv("payees.csv")
|
|
206
|
+
payees = [
|
|
207
|
+
payee
|
|
208
|
+
for payee in payees
|
|
209
|
+
if not (
|
|
210
|
+
payee["name"] == payee_name and int(payee["user_id"]) == user_id
|
|
211
|
+
)
|
|
212
|
+
]
|
|
213
|
+
return self._write_csv("payees.csv", payees)
|
|
181
214
|
except Exception as e:
|
|
182
215
|
self.logger.error(f"Error removing payee: {e}")
|
|
183
216
|
return False
|
|
@@ -198,8 +231,7 @@ class Database:
|
|
|
198
231
|
def get_branches(self) -> List[Dict[str, Any]]:
|
|
199
232
|
"""Get all branches."""
|
|
200
233
|
try:
|
|
201
|
-
|
|
202
|
-
return df.to_dict("records")
|
|
234
|
+
return self._read_csv("branches.csv")
|
|
203
235
|
except Exception as e:
|
|
204
236
|
self.logger.error(f"Error getting branches: {e}")
|
|
205
237
|
return []
|
|
@@ -207,9 +239,12 @@ class Database:
|
|
|
207
239
|
def get_advisors_by_branch(self, branch_id: int) -> List[Dict[str, Any]]:
|
|
208
240
|
"""Get all advisors for a branch."""
|
|
209
241
|
try:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
242
|
+
advisors = self._read_csv("advisors.csv")
|
|
243
|
+
return [
|
|
244
|
+
advisor
|
|
245
|
+
for advisor in advisors
|
|
246
|
+
if int(advisor["branch_id"]) == branch_id
|
|
247
|
+
]
|
|
213
248
|
except Exception as e:
|
|
214
249
|
self.logger.error(f"Error getting advisors by branch: {e}")
|
|
215
250
|
return []
|
|
@@ -217,17 +252,25 @@ class Database:
|
|
|
217
252
|
def get_appointments_by_advisor(self, advisor_id: int) -> List[Dict[str, Any]]:
|
|
218
253
|
"""Get all appointments for an advisor."""
|
|
219
254
|
try:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
255
|
+
appointments = self._read_csv("appointments.csv")
|
|
256
|
+
return [
|
|
257
|
+
appointment
|
|
258
|
+
for appointment in appointments
|
|
259
|
+
if int(appointment["advisor_id"]) == advisor_id
|
|
260
|
+
]
|
|
223
261
|
except Exception as e:
|
|
224
262
|
self.logger.error(f"Error getting appointments by advisor: {e}")
|
|
225
263
|
return []
|
|
226
264
|
|
|
227
|
-
def __enter__(self):
|
|
265
|
+
def __enter__(self) -> "Database":
|
|
228
266
|
"""Enter the runtime context related to this object."""
|
|
229
267
|
return self
|
|
230
268
|
|
|
231
|
-
def __exit__(
|
|
269
|
+
def __exit__(
|
|
270
|
+
self,
|
|
271
|
+
exc_type: Optional[type],
|
|
272
|
+
exc_value: Optional[BaseException],
|
|
273
|
+
traceback: Optional[Any],
|
|
274
|
+
) -> None:
|
|
232
275
|
"""Exit the runtime context related to this object."""
|
|
233
276
|
self.logger.info("Database connection closed")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_simple_cancellation
|
|
3
|
+
steps:
|
|
4
|
+
- user: "hello"
|
|
5
|
+
- utter: utter_chitchat
|
|
6
|
+
- user: "who are my payees"
|
|
7
|
+
- bot: "You are authorised to transfer money to: Amy, Fitness Gym and William"
|
|
8
|
+
- user: "I want to transfer 55 to Amy"
|
|
9
|
+
- utter: utter_transfer_money_understand
|
|
10
|
+
- bot: "Which account would you like to transfer money from?"
|
|
11
|
+
- user: "sorry, I want to cancel that"
|
|
12
|
+
- utter: utter_flow_cancelled_rasa
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_chitchat
|
|
3
|
+
steps:
|
|
4
|
+
- user: "hello,"
|
|
5
|
+
- utter: utter_chitchat
|
|
6
|
+
- user: "How can we solve world hunger?"
|
|
7
|
+
- bot: "Based on the provided documents and the context of the recent conversation, the question about solving world hunger is not directly addressed. If you have any specific questions related to the documents or conversation context, feel free to ask for more information."
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_clarification
|
|
3
|
+
steps:
|
|
4
|
+
- user: "hello"
|
|
5
|
+
- utter: utter_chitchat
|
|
6
|
+
- user: "payee"
|
|
7
|
+
- utter: utter_clarification_options_rasa
|
|
8
|
+
- user: "view a list"
|
|
9
|
+
- bot: "You are authorised to transfer money to: Robert, James and Food Market"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_completion
|
|
3
|
+
steps:
|
|
4
|
+
- user: "hello"
|
|
5
|
+
- utter: utter_chitchat
|
|
6
|
+
- user: "Who are my payees"
|
|
7
|
+
- bot: "You are authorised to transfer money to: Amy, Fitness Gym and William"
|
|
8
|
+
- user: "I want to transfer money to William"
|
|
9
|
+
- utter: utter_transfer_money_understand
|
|
10
|
+
- bot: "Which account would you like to transfer money from?"
|
|
11
|
+
- user: "78901234"
|
|
12
|
+
- utter: utter_ask_amount
|
|
13
|
+
- user: "120"
|
|
14
|
+
- utter: utter_ask_timing
|
|
15
|
+
- user: "/SetSlots(timing=now)"
|
|
16
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
17
|
+
- user: "yes"
|
|
18
|
+
- utter: utter_transfer_successful
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_corrections
|
|
3
|
+
steps:
|
|
4
|
+
- user: "who are my payees"
|
|
5
|
+
- bot: "You are authorised to transfer money to: Amy, Fitness Gym and William"
|
|
6
|
+
- user: "I want to transfer 55 to Amy"
|
|
7
|
+
- utter: utter_transfer_money_understand
|
|
8
|
+
- bot: "Which account would you like to transfer money from?"
|
|
9
|
+
- user: "67890123"
|
|
10
|
+
- utter: utter_ask_timing
|
|
11
|
+
- user: "/SetSlots(timing=now)"
|
|
12
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
13
|
+
- user: "Sorry, I meant 65 and from Current"
|
|
14
|
+
- utter: utter_corrected_previous_input
|
|
15
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
16
|
+
- user: "yes"
|
|
17
|
+
- utter: utter_transfer_successful
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_digressions
|
|
3
|
+
steps:
|
|
4
|
+
- user: "I want to transfer money"
|
|
5
|
+
- utter: utter_transfer_money_understand
|
|
6
|
+
- bot: "Which account would you like to transfer money from?"
|
|
7
|
+
- user: "who are my payees"
|
|
8
|
+
- bot: "You are authorised to transfer money to: Richard, Susan and Electric Company"
|
|
9
|
+
- utter: utter_flow_continue_interrupted
|
|
10
|
+
- bot: "Which account would you like to transfer money from?"
|
|
11
|
+
- user: "I would like to add Brad as a payee"
|
|
12
|
+
- utter: utter_ask_account_number
|
|
13
|
+
- user: "123456"
|
|
14
|
+
- utter: utter_ask_payee_type
|
|
15
|
+
- user: "/SetSlots(payee_type=person)"
|
|
16
|
+
- utter: utter_ask_reference
|
|
17
|
+
- user: "Facebook Marketplace"
|
|
18
|
+
- utter: utter_ask_confirm_payee_details
|
|
19
|
+
- user: "/SetSlots(confirm_payee_details=True)"
|
|
20
|
+
- utter: utter_payee_added_success
|
|
21
|
+
- utter: utter_flow_continue_interrupted
|
|
22
|
+
- bot: "Which account would you like to transfer money from?"
|
|
23
|
+
- user: "12345678"
|
|
24
|
+
- utter: utter_ask_payee_name
|
|
25
|
+
- user: "Brad"
|
|
26
|
+
- utter: utter_ask_amount
|
|
27
|
+
- user: "55.23"
|
|
28
|
+
- utter: utter_ask_timing
|
|
29
|
+
- user: "immediate"
|
|
30
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
31
|
+
- user: "yup"
|
|
32
|
+
- utter: utter_transfer_successful
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: conversation_repair_human_handoff
|
|
3
|
+
steps:
|
|
4
|
+
- user: "hello"
|
|
5
|
+
- utter: utter_chitchat
|
|
6
|
+
- user: "i want to transfer 999 from savings"
|
|
7
|
+
- utter: utter_transfer_money_understand
|
|
8
|
+
- utter: utter_ask_payee_name
|
|
9
|
+
- user: "who are my payees"
|
|
10
|
+
- bot: "You are authorised to transfer money to: Amy, Fitness Gym and William"
|
|
11
|
+
- utter: utter_flow_continue_interrupted
|
|
12
|
+
- utter: utter_ask_payee_name
|
|
13
|
+
- user: "Amy"
|
|
14
|
+
- utter: utter_ask_timing
|
|
15
|
+
- user: "/SetSlots(timing=now)"
|
|
16
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
17
|
+
- user: "I want to talk to a human"
|
|
18
|
+
- utter: utter_human_handoff_not_available
|
|
19
|
+
- utter: utter_ask_confirm_immediate_payment
|
|
20
|
+
- user: "no"
|
|
21
|
+
- utter: utter_cancel_transfer
|