• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

digiteinfotech / kairon / 12112350604

02 Dec 2024 03:52AM UTC coverage: 89.891% (-0.04%) from 89.932%
12112350604

Pull #1611

github

web-flow
Merge 9176d03d1 into f2f296b80
Pull Request #1611: Mail channel implementation

383 of 434 new or added lines in 15 files covered. (88.25%)

12 existing lines in 2 files now uncovered.

24141 of 26856 relevant lines covered (89.89%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.77
/kairon/api/app/routers/bot/bot.py
1
import os
1✔
2
from datetime import date, datetime
1✔
3
from typing import List, Optional, Dict, Text
1✔
4

5
from fastapi import APIRouter, BackgroundTasks, Path, Security, Request
1✔
6
from fastapi import UploadFile
1✔
7
from fastapi.responses import FileResponse
1✔
8
from pydantic import constr
1✔
9

10
from kairon.api.models import (
1✔
11
    TextData,
12
    ListData,
13
    Response,
14
    Endpoint,
15
    RasaConfig,
16
    StoryRequest,
17
    SynonymRequest, RegexRequest,
18
    StoryType, ComponentConfig, SlotRequest, DictData, LookupTablesRequest, Forms,
19
    TextDataLowerCase, SlotMappingRequest, EventConfig, MultiFlowStoryRequest, BotSettingsRequest
20
)
21
from kairon.events.definitions.data_importer import TrainingDataImporterEvent
1✔
22
from kairon.events.definitions.model_testing import ModelTestingEvent
1✔
23
from kairon.events.definitions.model_training import ModelTrainingEvent
1✔
24
from kairon.exceptions import AppException
1✔
25
from kairon.shared.account.activity_log import UserActivityLogger
1✔
26
from kairon.shared.actions.data_objects import ActionServerLogs
1✔
27
from kairon.shared.auth import Authentication
1✔
28
from kairon.shared.channels.mail.data_objects import MailClassificationConfig
1✔
29
from kairon.shared.channels.mail.scheduler import MailScheduler
1✔
30
from kairon.shared.constants import TESTER_ACCESS, DESIGNER_ACCESS, CHAT_ACCESS, UserActivityType, ADMIN_ACCESS, \
1✔
31
    EventClass, AGENT_ACCESS
32
from kairon.shared.content_importer.content_processor import ContentImporterLogProcessor
1✔
33
from kairon.shared.content_importer.data_objects import ContentValidationLogs
1✔
34
from kairon.shared.data.assets_processor import AssetsProcessor
1✔
35
from kairon.shared.data.audit.processor import AuditDataProcessor
1✔
36
from kairon.shared.data.constant import ENDPOINT_TYPE, ModelTestType, \
1✔
37
    AuditlogActions
38
from kairon.shared.data.data_models import MailConfigRequest
1✔
39
from kairon.shared.data.data_objects import TrainingExamples, ModelTraining, Rules
1✔
40
from kairon.shared.data.model_processor import ModelProcessor
1✔
41
from kairon.shared.data.processor import MongoProcessor
1✔
42
from kairon.shared.events.processor import ExecutorProcessor
1✔
43
from kairon.shared.importer.data_objects import ValidationLogs
1✔
44
from kairon.shared.importer.processor import DataImporterLogProcessor
1✔
45
from kairon.shared.live_agent.live_agent import LiveAgentHandler
1✔
46
from kairon.shared.llm.processor import LLMProcessor
1✔
47
from kairon.shared.models import User, TemplateType
1✔
48
from kairon.shared.test.processor import ModelTestingLogProcessor
1✔
49
from kairon.shared.utils import Utility
1✔
50

51
router = APIRouter()
1✔
52
v2 = APIRouter()
1✔
53
mongo_processor = MongoProcessor()
1✔
54

55

56
@router.get("/intents", response_model=Response)
1✔
57
async def get_intents(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
58
    """
59
    Fetches list of existing intents for particular bot
60
    """
61
    return Response(data=mongo_processor.get_intents(current_user.get_bot())).dict()
1✔
62

63

64
@router.get("/intents/all", response_model=Response)
1✔
65
async def get_intents_with_training_examples(
1✔
66
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
67
    """
68
    Fetches list of existing intents and associated training examples for particular bot
69
    """
70
    return Response(data=mongo_processor.get_intents_and_training_examples(current_user.get_bot())).dict()
1✔
71

72

73
@router.post("/intents", response_model=Response)
1✔
74
async def add_intents(
1✔
75
        request_data: TextDataLowerCase,
76
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
77
):
78
    """
79
    Adds a new intent to the bot
80
    """
81
    intent_id = mongo_processor.add_intent(
1✔
82
        text=request_data.data,
83
        bot=current_user.get_bot(),
84
        user=current_user.get_user(),
85
        is_integration=current_user.get_integration_status()
86
    )
87
    return {"message": "Intent added successfully!", "data": {"_id": intent_id}}
1✔
88

89

90
@router.delete("/intents/{intent}", response_model=Response)
1✔
91
async def delete_intent(
1✔
92
        intent: str = Path(description="intent name", examples=["greet"]),
93
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
94
):
95
    """
96
    deletes an intent including training examples and stories
97
    """
98
    mongo_processor.delete_intent(
1✔
99
        intent, current_user.get_bot(), current_user.get_user(), current_user.get_integration_status(),
100
    )
101
    return {"message": "Intent deleted!"}
1✔
102

103

104
@router.post("/intents/search", response_model=Response)
1✔
105
async def search_training_examples(
1✔
106
        request_data: TextData,
107
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
108
):
109
    """
110
    Searches existing training examples
111
    """
UNCOV
112
    search_items = list(
×
113
        mongo_processor.search_training_examples(
114
            request_data.data, current_user.get_bot()
115
        )
116
    )
UNCOV
117
    return {"data": {"searched_items": search_items}}
×
118

119

120
@router.get("/training_examples/{intent}", response_model=Response)
1✔
121
async def get_training_examples(
1✔
122
        intent: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
123
):
124
    """
125
    Fetches all training examples against intent
126
    """
127
    return {
1✔
128
        "data": list(
129
            mongo_processor.get_training_examples(intent, current_user.get_bot())
130
        )
131
    }
132

133

134
@router.get("/training_examples", response_model=Response)
1✔
135
async def get_all_training_examples_for_bot(
1✔
136
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
137
    """
138
    Fetches all training examples against a bot.
139
    """
140
    return {
1✔
141
        "data": mongo_processor.get_training_examples_as_dict(current_user.get_bot())
142
    }
143

144

145
@router.get("/training_examples/exists/{text}", response_model=Response)
1✔
146
async def training_example_exists(
1✔
147
        text: str,
148
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
149
):
150
    """
151
    Checks if training example exists
152
    """
153
    return {
1✔
154
        "data": mongo_processor.check_training_example_exists(text, current_user.get_bot())
155
    }
156

157

158
@router.post("/training_examples/{intent}", response_model=Response)
1✔
159
async def add_training_examples(
1✔
160
        intent: constr(to_lower=True, strip_whitespace=True),
161
        request_data: ListData,
162
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
163
):
164
    """
165
    Adds training example in particular intent
166
    """
167
    results = list(
1✔
168
        mongo_processor.add_training_example(
169
            request_data.data, intent, current_user.get_bot(), current_user.get_user(),
170
            current_user.get_integration_status()
171
        )
172
    )
173
    return {"data": results}
1✔
174

175

176
@router.post("/training_examples/move/{intent}", response_model=Response)
1✔
177
async def move_training_examples(
1✔
178
        intent: constr(to_lower=True, strip_whitespace=True),
179
        request_data: ListData,
180
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
181
):
182
    """
183
    Moves training example to particular intent
184
    """
185
    results = list(
1✔
186
        mongo_processor.add_or_move_training_example(
187
            request_data.data, intent, current_user.get_bot(), current_user.get_user()
188
        )
189
    )
190
    return {"data": results}
1✔
191

192

193
@router.put("/training_examples/{intent}/{id}", response_model=Response)
1✔
194
async def edit_training_examples(
1✔
195
        intent: str,
196
        id: str,
197
        request_data: TextData,
198
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
199
):
200
    """
201
    Updates existing training example
202
    """
203
    mongo_processor.edit_training_example(
1✔
204
        id, request_data.data, intent, current_user.get_bot(), current_user.get_user()
205
    )
206
    return {"message": "Training Example updated!"}
1✔
207

208

209
@router.delete("/training_examples/{id}", response_model=Response)
1✔
210
async def remove_training_examples(
1✔
211
        id: str,
212
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
213
):
214
    """
215
    Deletes existing training example
216
    """
217
    mongo_processor.remove_document(
1✔
218
        TrainingExamples,
219
        id,
220
        current_user.get_bot(),
221
        current_user.get_user(),
222
    )
223
    return {"message": "Training Example removed!"}
1✔
224

225

226
@router.get("/response/all", response_model=Response)
1✔
227
async def get_all_responses(
1✔
228
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
229
):
230
    """
231
    Fetches list of all utterances added.
232
    """
233
    return {
1✔
234
        "data": list(mongo_processor.get_all_responses(current_user.get_bot()))
235
    }
236

237

238
@router.get("/response/{utterance}", response_model=Response)
1✔
239
async def get_responses(
1✔
240
        utterance: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
241
):
242
    """
243
    Fetches list of utterances against utterance name
244
    """
245
    return {
1✔
246
        "data": list(mongo_processor.get_response(utterance, current_user.get_bot()))
247
    }
248

249

250
@router.post("/response/{utterance}", response_model=Response)
1✔
251
async def add_responses(
1✔
252
        request_data: TextData,
253
        utterance: constr(to_lower=True, strip_whitespace=True),
254
        form_attached: Optional[str] = None,
255
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
256
):
257
    """
258
    Adds utterance value in particular utterance
259
    """
260
    utterance_id = mongo_processor.add_text_response(
1✔
261
        request_data.data, utterance, current_user.get_bot(), current_user.get_user(), form_attached
262
    )
263
    return {"message": "Response added!", "data": {"_id": utterance_id}}
1✔
264

265

266
@router.post("/response/json/{utterance}", response_model=Response)
1✔
267
async def add_custom_responses(
1✔
268
        request_data: DictData,
269
        utterance: constr(to_lower=True, strip_whitespace=True),
270
        form_attached: Optional[str] = None,
271
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
272
):
273
    """
274
    Adds utterance value in particular utterance
275
    """
276
    utterance_id = mongo_processor.add_custom_response(
1✔
277
        request_data.data, utterance, current_user.get_bot(), current_user.get_user(), form_attached
278
    )
279
    return {"message": "Response added!", "data": {"_id": utterance_id}}
1✔
280

281

282
@router.put("/response/{utterance}/{utterance_id}", response_model=Response)
1✔
283
async def edit_responses(
1✔
284
        utterance: str,
285
        utterance_id: str,
286
        request_data: TextData,
287
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
288
):
289
    """
290
    Updates existing utterance value
291
    """
292
    mongo_processor.edit_text_response(
1✔
293
        utterance_id,
294
        request_data.data,
295
        utterance,
296
        current_user.get_bot(),
297
        current_user.get_user(),
298
    )
299
    return {
1✔
300
        "message": "Utterance updated!",
301
    }
302

303

304
@router.put("/response/json/{utterance}/{utterance_id}", response_model=Response)
1✔
305
async def edit_custom_responses(
1✔
306
        utterance: str,
307
        utterance_id: str,
308
        request_data: DictData,
309
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
310
):
311
    """
312
    Updates existing utterance value
313
    """
314
    mongo_processor.edit_custom_response(
1✔
315
        utterance_id,
316
        request_data.data,
317
        utterance,
318
        current_user.get_bot(),
319
        current_user.get_user(),
320
    )
321
    return {
1✔
322
        "message": "Utterance updated!",
323
    }
324

325

326
@router.delete("/response/{response_id}", response_model=Response)
1✔
327
async def remove_response(
1✔
328
        response_id: str,
329
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
330
):
331
    """
332
    Deletes existing utterance example.
333
    """
334
    mongo_processor.delete_response(
1✔
335
        response_id, current_user.get_bot(), user=current_user.get_user()
336
    )
337
    return {
1✔
338
        "message": "Response removed!",
339
    }
340

341

342
@router.delete("/responses/{utterance}", response_model=Response)
1✔
343
async def remove_responses(
1✔
344
        utterance: str,
345
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
346
):
347
    """
348
    Deletes existing utterance completely along with its examples.
349
    """
350
    mongo_processor.delete_utterance(
1✔
351
        utterance, current_user.get_bot(), user=current_user.get_user()
352
    )
353
    return {
1✔
354
        "message": "Utterance removed!",
355
    }
356

357

358
@router.post("/stories", response_model=Response)
1✔
359
async def add_story(
1✔
360
        story: StoryRequest,
361
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
362
):
363
    """
364
    Adds a story (conversational flow) in the particular bot
365
    """
366
    return {
1✔
367
        "message": "Flow added successfully",
368
        "data": {
369
            "_id": mongo_processor.add_complex_story(
370
                story.dict(),
371
                current_user.get_bot(),
372
                current_user.get_user(),
373
            )
374
        },
375
    }
376

377

378
@router.put("/stories/{story_id}", response_model=Response)
1✔
379
async def update_story(
1✔
380
        story_id: str,
381
        story: StoryRequest,
382
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
383
):
384
    """
385
    Updates a story (conversational flow) in the particular bot
386
    """
387
    return {
1✔
388
        "message": "Flow updated successfully",
389
        "data": {
390
            "_id": mongo_processor.update_complex_story(
391
                story_id,
392
                story.dict(),
393
                current_user.get_bot(),
394
                current_user.get_user(),
395
            )
396
        },
397
    }
398

399

400
@router.get("/stories", response_model=Response)
1✔
401
async def get_stories(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
402
    """
403
    Fetches existing list of stories (conversation flows)
404
    """
405
    return {"data": list(mongo_processor.get_all_stories(current_user.get_bot()))}
1✔
406

407

408
@v2.post("/stories", response_model=Response)
1✔
409
async def add_story_multiflow(
1✔
410
        story: MultiFlowStoryRequest,
411
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
412
):
413
    """
414
    Adds a multiflow story (conversational flow) in the particular bot
415
    """
416
    return {
1✔
417
        "message": "Story flow added successfully",
418
        "data": {
419
            "_id": mongo_processor.add_multiflow_story(
420
                story.dict(),
421
                current_user.get_bot(),
422
                current_user.get_user(),
423
            )
424
        },
425
    }
426

427

428
@v2.put("/stories/{story_id}", response_model=Response)
1✔
429
async def update_story_multiflow(
1✔
430
        story_id: str,
431
        story: MultiFlowStoryRequest,
432
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
433
):
434
    """
435
    Updates a multiflow story (conversational flow) in the particular bot
436
    """
437
    return {
1✔
438
        "message": "Story flow updated successfully",
439
        "data": {
440
            "_id": mongo_processor.update_multiflow_story(
441
                story_id,
442
                story.dict(),
443
                current_user.get_bot(),
444
            )
445
        },
446
    }
447

448

449
@router.delete("/stories/{story_id}/{type}", response_model=Response)
1✔
450
async def delete_stories(story_id: str,
1✔
451
                         type: str = StoryType,
452
                         current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
453
                         ):
454
    """
455
    Updates a story (conversational flow) in the particular bot
456
    """
457
    mongo_processor.delete_complex_story(
1✔
458
        story_id,
459
        type,
460
        current_user.get_bot(),
461
        current_user.get_user(),
462
    )
463
    return {
1✔
464
        "message": "Flow deleted successfully"
465
    }
466

467

468
@router.get("/utterance_from_intent/{intent}", response_model=Response)
1✔
469
async def get_story_from_intent(
1✔
470
        intent: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
471
):
472
    """
473
    Fetches the utterance or response that is mapped to a particular intent
474
    """
475
    response = mongo_processor.get_utterance_from_intent(intent, current_user.get_bot())
1✔
476
    return_data = {"name": response[0], "type": response[1]}
1✔
477
    return {"data": return_data}
1✔
478

479

480
@router.post("/chat", response_model=Response)
1✔
481
async def chat(
1✔
482
        request_data: TextData,
483
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=CHAT_ACCESS)
484
):
485
    """
486
    Fetches a bot response for a given text/query.
487
    It is basically used to test the chat functionality of the agent
488
    """
489
    return await Utility.chat(request_data.data,
1✔
490
                              bot=current_user.get_bot(),
491
                              user=current_user.get_user(),
492
                              email=current_user.email)
493

494

495
@router.post("/chat/{user}", response_model=Response)
1✔
496
async def augment_chat(
1✔
497
        request_data: TextData,
498
        user: str = Path(description="user for which the chats needs to be log"),
499
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=CHAT_ACCESS)
500
):
501
    """
502
    Fetches a bot response for a given text/query for user on path parameter.
503
    It is basically used to test the chat functionality of the agent
504
    """
505
    return await Utility.chat(request_data.data,
1✔
506
                              bot=current_user.get_bot(),
507
                              user=user,
508
                              email=current_user.email)
509

510

511
@router.post("/train", response_model=Response)
1✔
512
async def train(
1✔
513
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
514
):
515
    """
516
    Trains the chatbot
517
    """
518
    event = ModelTrainingEvent(current_user.get_bot(), current_user.get_user())
1✔
519
    event.validate()
1✔
520
    event.enqueue()
1✔
521
    return {"message": "Model training started."}
1✔
522

523

524
@router.post("/abort/{event_type}", response_model=Response)
1✔
525
async def abort_event(
1✔
526
        event_type: EventClass = Path(description="Event type", examples=[e.value for e in EventClass]),
527
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
528
):
529
    """
530
    Aborts the event
531
    """
UNCOV
532
    mongo_processor.abort_current_event(current_user.get_bot(), current_user.get_user(), event_type)
×
533

UNCOV
534
    return {"message": f"{event_type} aborted."}
×
535

536

537
@router.get("/model/reload", response_model=Response)
1✔
538
async def reload_model(
1✔
539
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
540
):
541
    """
542
    Reloads model with configuration in cache
543
    """
544
    response = Utility.reload_model(
1✔
545
        bot=current_user.get_bot(),
546
        email=current_user.email)
547
    return response
1✔
548

549

550
@router.get("/train/history", response_model=Response)
1✔
551
async def get_model_training_history(
1✔
552
        start_idx: int = 0, page_size: int = 10,
553
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
554
):
555
    """
556
    Fetches model training history, when and who trained the bot
557
    """
558
    training_history = list(ModelProcessor.get_training_history(current_user.get_bot(), start_idx, page_size))
1✔
559
    row_cnt = mongo_processor.get_row_count(ModelTraining, current_user.get_bot())
1✔
560
    data = {
1✔
561
        "logs": training_history,
562
        "total": row_cnt
563
    }
564
    return {"data": {"training_history": data}}
1✔
565

566

567
@router.post("/deploy", response_model=Response)
1✔
568
async def deploy(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)):
1✔
569
    """
570
    Deploys the latest bot model to the particular http endpoint
571
    """
572
    response = mongo_processor.deploy_model(
1✔
573
        bot=current_user.get_bot(), user=current_user.get_user()
574
    )
575
    return {"message": response}
1✔
576

577

578
@router.get("/deploy/history", response_model=Response)
1✔
579
async def deployment_history(
1✔
580
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
581
    """
582
    Fetches model deployment history, when and who deployed the model
583
    """
584
    return {
1✔
585
        "data": {
586
            "deployment_history": list(
587
                mongo_processor.get_model_deployment_history(bot=current_user.get_bot())
588
            )
589
        }
590
    }
591

592

593
@router.post("/upload", response_model=Response)
1✔
594
def upload_files(
1✔
595
        training_files: List[UploadFile],
596
        import_data: bool = True,
597
        overwrite: bool = True,
598
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
599
):
600
    """
601
    Uploads training data nlu.yml, domain.yml, stories.yml, config.yml, rules.yml and actions.yml files.
602
    """
603
    event = TrainingDataImporterEvent(
1✔
604
        current_user.get_bot(), current_user.get_user(), import_data=import_data, overwrite=overwrite
605
    )
606
    is_event_data = event.validate(training_files=training_files, is_data_uploaded=True)
1✔
607
    if is_event_data:
1✔
608
        event.enqueue()
1✔
609
    return {"message": "Upload in progress! Check logs."}
1✔
610

611

612
@router.get("/download/data")
1✔
613
async def download_data(
1✔
614
        background_tasks: BackgroundTasks,
615
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
616
        download_multiflow_stories: bool = False
617
):
618
    """
619
    Downloads training data nlu.yml, domain.yml, stories.yml, config.yml, chat_client_config.yml files
620
    """
621
    file = mongo_processor.download_files(current_user.get_bot(), current_user.get_user(), download_multiflow_stories)
1✔
622
    response = FileResponse(
1✔
623
        file, filename=os.path.basename(file), background=background_tasks
624
    )
625
    AuditDataProcessor.log("Training Data", current_user.account, current_user.get_bot(), current_user.get_user(),
1✔
626
                           data={"download_multiflow_stories": download_multiflow_stories},
627
                           action=AuditlogActions.DOWNLOAD.value)
628
    response.headers[
1✔
629
        "Content-Disposition"
630
    ] = "attachment; filename=" + os.path.basename(file)
631
    return response
1✔
632

633

634
@router.get("/download/model")
1✔
635
async def download_model(
1✔
636
        background_tasks: BackgroundTasks,
637
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
638
):
639
    """
640
    Downloads latest trained model file
641
    """
642
    try:
1✔
643
        model_path = Utility.get_latest_model(current_user.get_bot())
1✔
644
        response = FileResponse(
1✔
645
            model_path,
646
            filename=os.path.basename(model_path),
647
            background=background_tasks,
648
            media_type='application/octet-stream'
649
        )
650
        AuditDataProcessor.log("Model", current_user.account, current_user.get_bot(), current_user.get_user(),
1✔
651
                               action=AuditlogActions.DOWNLOAD.value)
652
        response.headers[
1✔
653
            "Content-Disposition"
654
        ] = "attachment; filename=" + os.path.basename(model_path)
655
        return response
1✔
656
    except Exception as e:
×
UNCOV
657
        raise AppException(str(e))
×
658

659

660
@router.post("/test", response_model=Response)
1✔
661
async def test_model(
1✔
662
        augment_data: bool = True,
663
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
664
):
665
    """
666
    Run tests on a trained model.
667
    """
668
    event = ModelTestingEvent(current_user.get_bot(), current_user.get_user(), augment_data=augment_data)
1✔
669
    event.validate()
1✔
670
    event.enqueue()
1✔
671
    return {"message": "Testing in progress! Check logs."}
1✔
672

673

674
@router.get("/logs/test", response_model=Response)
1✔
675
async def model_testing_logs(
1✔
676
        log_type: ModelTestType = None, reference_id: str = None,
677
        start_idx: int = 0, page_size: int = 10,
678
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
679
):
680
    """
681
    List model testing logs.
682
    """
683
    logs, row_cnt = ModelTestingLogProcessor.get_logs(current_user.get_bot(), log_type, reference_id, start_idx, page_size)
1✔
684
    data = {
1✔
685
        "logs": logs,
686
        "total": row_cnt
687
    }
688
    return Response(data=data)
1✔
689

690

691
@router.get("/endpoint", response_model=Response)
1✔
692
async def get_endpoint(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=ADMIN_ACCESS)):
1✔
693
    """
694
    Fetches the http and mongo endpoint for the bot
695
    """
696
    endpoint = mongo_processor.get_endpoints(
1✔
697
        current_user.get_bot(), mask_characters=True, raise_exception=False
698
    )
699
    return {"data": {"endpoint": endpoint}}
1✔
700

701

702
@router.put("/endpoint", response_model=Response)
1✔
703
async def set_endpoint(
1✔
704
        background_tasks: BackgroundTasks,
705
        endpoint: Endpoint,
706
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=ADMIN_ACCESS),
707
):
708
    """
709
    Saves or Updates the bot endpoint configuration
710
    """
711
    mongo_processor.add_endpoints(
1✔
712
        endpoint.dict(), current_user.get_bot(), current_user.get_user()
713
    )
714

715
    if endpoint.action_endpoint:
1✔
716
        background_tasks.add_task(Utility.reload_model, current_user.get_bot(), current_user.email)
1✔
717
    return {"message": "Endpoint saved successfully!"}
1✔
718

719

720
@router.delete("/endpoint/{endpoint_type}", response_model=Response)
1✔
721
async def delete_endpoint(
1✔
722
        endpoint_type: ENDPOINT_TYPE = Path(description="One of bot_endpoint, action_endpoint, "
723
                                                                      "history_endpoint", examples=["bot_endpoint"]),
724
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=ADMIN_ACCESS)
725
):
726
    """
727
    Deletes the bot endpoint configuration
728
    """
729
    mongo_processor.delete_endpoint(
1✔
730
        current_user.get_bot(), endpoint_type
731
    )
732

733
    return {"message": "Endpoint removed"}
1✔
734

735

736
@router.get("/config", response_model=Response)
1✔
737
async def get_config(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
738
    """
739
    Fetches bot pipeline and polcies configurations
740
    """
741
    config = mongo_processor.load_config(current_user.get_bot())
1✔
742
    return {"data": {"config": config}}
1✔
743

744

745
@router.put("/config", response_model=Response)
1✔
746
async def set_config(
1✔
747
        config: RasaConfig,
748
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
749
):
750
    """
751
    Saves or Updates the bot pipeline and policies configurations
752
    """
753
    mongo_processor.save_config(
1✔
754
        config.dict(), current_user.get_bot(), current_user.get_user()
755
    )
756
    return {"message": "Config saved!"}
1✔
757

758

759
@router.put("/config/properties", response_model=Response)
1✔
760
async def set_epoch_and_fallback_properties(config: ComponentConfig,
1✔
761
                                            current_user: User = Security(Authentication.get_current_user_and_bot,
762
                                                                          scopes=DESIGNER_ACCESS)):
763
    """
764
    Set properties (epoch and fallback) in the bot pipeline and policies configurations
765
    """
766
    mongo_processor.save_component_properties(config.dict(), current_user.get_bot(), current_user.get_user())
1✔
767
    return {"message": "Config saved"}
1✔
768

769

770
@router.get("/config/properties", response_model=Response)
1✔
771
async def list_epoch_and_fallback_properties(
1✔
772
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
773
    """
774
    List properties (epoch and fallback) in the bot pipeline and policies configurations
775
    """
776
    config = mongo_processor.list_epoch_and_fallback_config(current_user.get_bot())
1✔
777
    return {"data": config}
1✔
778

779

780
@router.post("/templates/use-case", response_model=Response)
1✔
781
async def set_templates(
1✔
782
        request_data: TextData,
783
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
784
):
785
    """
786
    Applies the use-case template
787
    """
788
    await mongo_processor.apply_template(
1✔
789
        request_data.data, bot=current_user.get_bot(), user=current_user.get_user()
790
    )
791
    return {"message": "Data applied!"}
1✔
792

793

794
@router.get("/templates/config", response_model=Response)
1✔
795
async def get_config_template(
1✔
796
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
797
    """
798
    Fetches config templates
799
    """
800
    return {"data": {"config-templates": mongo_processor.get_config_templates()}}
1✔
801

802

803
@router.post("/templates/config", response_model=Response)
1✔
804
async def set_config_template(
1✔
805
        request_data: TextData,
806
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
807
):
808
    """
809
    Applies the config template
810
    """
811
    mongo_processor.apply_config(
1✔
812
        request_data.data, current_user.get_bot(), current_user.get_user()
813
    )
814
    return {"message": "Config applied!"}
1✔
815

816

817
@router.get("/actions", response_model=Response)
1✔
818
async def list_actions(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
819
    """
820
    Returns list of actions for bot.
821
    """
822
    actions = mongo_processor.list_actions(bot=current_user.get_bot())
1✔
823
    return Response(data=actions)
1✔
824

825

826
@router.get("/actions/logs", response_model=Response)
1✔
827
async def get_action_server_logs(start_idx: int = 0, page_size: int = 10,
1✔
828
                                 current_user: User = Security(Authentication.get_current_user_and_bot,
829
                                                               scopes=TESTER_ACCESS)):
830
    """
831
    Retrieves action server logs for the bot.
832
    """
833
    logs = list(mongo_processor.get_action_server_logs(current_user.get_bot(), start_idx, page_size))
1✔
834
    row_cnt = mongo_processor.get_row_count(ActionServerLogs, current_user.get_bot())
1✔
835
    data = {
1✔
836
        "logs": logs,
837
        "total": row_cnt
838
    }
839
    return Response(data=data)
1✔
840

841

842
@router.get("/slots", response_model=Response)
1✔
843
async def get_slots(
1✔
844
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
845
):
846
    """
847
    Fetches status for latest data generation request
848
    """
849
    slots = list(mongo_processor.get_existing_slots(current_user.get_bot()))
1✔
850
    return {"data": slots}
1✔
851

852

853
@router.post("/slots", response_model=Response)
1✔
854
async def add_slots(
1✔
855
        request_data: SlotRequest,
856
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
857
):
858
    """
859
    adds a new slot
860
    :param request_data:
861
    :param current_user:
862
    :return: Success message with slot id
863
    """
864
    slot_id = mongo_processor.add_slot(slot_value=request_data.dict(), bot=current_user.get_bot(),
1✔
865
                                       user=current_user.get_user(), raise_exception_if_exists=True)
866
    return {"message": "Slot added successfully!", "data": {"_id": slot_id}}
1✔
867

868

869
@router.delete("/slots/{slot}", response_model=Response)
1✔
870
async def delete_slots(
1✔
871
        slot: str = Path(description="slot name", examples=["bot"]),
872
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
873
):
874
    """
875
    deletes an existing slot
876
    :param slot:
877
    :param current_user:
878
    :return: Success message
879
    """
880
    mongo_processor.delete_slot(slot_name=slot, bot=current_user.get_bot(), user=current_user.get_user())
1✔
881

882
    return {"message": "Slot deleted!"}
1✔
883

884

885
@router.put("/slots", response_model=Response)
1✔
886
async def edit_slots(
1✔
887
        request_data: SlotRequest,
888
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
889
):
890
    """
891
    Updates an existing slot
892
    :param request_data:
893
    :param current_user:
894
    :return: Success message
895
    """
896
    try:
1✔
897
        slot_value = request_data.dict()
1✔
898
        mongo_processor.add_slot(slot_value=slot_value, bot=current_user.get_bot(), user=current_user.get_user(),
1✔
899
                                 raise_exception_if_exists=False)
900
    except Exception as e:
1✔
901
        raise AppException(e)
1✔
902

903
    return {"message": "Slot updated!"}
1✔
904

905

906
@router.get("/importer/logs", response_model=Response)
1✔
907
async def get_data_importer_logs(
1✔
908
        start_idx: int = 0, page_size: int = 10,
909
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
910
):
911
    """
912
    Get data importer event logs.
913
    """
914
    logs = list(DataImporterLogProcessor.get_logs(current_user.get_bot(), start_idx, page_size))
1✔
915
    row_cnt = mongo_processor.get_row_count(ValidationLogs, current_user.get_bot())
1✔
916
    data = {
1✔
917
        "logs": logs,
918
        "total": row_cnt
919
    }
920
    return Response(data=data)
1✔
921

922

923
@router.get("/content/logs", response_model=Response)
1✔
924
async def get_content_importer_logs(
1✔
925
        start_idx: int = 0, page_size: int = 10,
926
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
927
):
928
    """
929
    Get data importer event logs.
930
    """
931
    logs = list(ContentImporterLogProcessor.get_logs(current_user.get_bot(), start_idx, page_size))
1✔
932
    row_cnt = mongo_processor.get_row_count(ContentValidationLogs, current_user.get_bot())
1✔
933
    data = {
1✔
934
        "logs": logs,
935
        "total": row_cnt
936
    }
937
    return Response(data=data)
1✔
938

939

940
@router.post("/validate", response_model=Response)
1✔
941
async def validate_training_data(
1✔
942
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
943
):
944
    """
945
    Validates bot training data.
946
    """
947
    event = TrainingDataImporterEvent(current_user.get_bot(), current_user.get_user())
1✔
948
    event.validate()
1✔
949
    event.enqueue()
1✔
950
    return {"message": "Event triggered! Check logs."}
1✔
951

952

953
@router.get("/entity/synonyms", response_model=Response)
1✔
954
async def get_all_synonyms(
1✔
955
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
956
):
957
    """
958
    Fetches the stored synonyms of the bot
959
    """
960
    synonyms = list(mongo_processor.fetch_synonyms(current_user.get_bot()))
1✔
961
    return {"data": synonyms}
1✔
962

963

964
@router.get("/entity/synonym/{name:path}/values", response_model=Response)
1✔
965
async def get_synonym_values(
1✔
966
        name: constr(to_lower=True, strip_whitespace=True),
967
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
968
):
969
    """
970
    Fetches list of values against synonym name
971
    """
972
    return {
1✔
973
        "data": list(mongo_processor.get_synonym_values(name, current_user.get_bot()))
974
    }
975

976

977
@router.post("/entity/synonym", response_model=Response)
1✔
978
async def add_synonym(
1✔
979
        request_data: TextData,
980
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
981
):
982
    """
983
       adds a new synonym values
984
       :param name:
985
       :param request_data:
986
       :param current_user:
987
       :return: Success message and sysnonym value id
988
   """
989
    id = mongo_processor.add_synonym(synonym_name=request_data.data,
1✔
990
                                     bot=current_user.get_bot(),
991
                                     user=current_user.get_user())
992
    return {"data": {"_id": id}, "message": "Synonym added!"}
1✔
993

994

995
@router.post("/entity/synonym/{name:path}/value", response_model=Response)
1✔
996
async def add_synonym_value(
1✔
997
        name: str,
998
        request_data: TextData,
999
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1000
):
1001
    """
1002
       adds a new synonym values
1003
       :param name:
1004
       :param request_data:
1005
       :param current_user:
1006
       :return: Success message and sysnonym value id
1007
   """
1008
    id = mongo_processor.add_synonym_value(value=request_data.data,
1✔
1009
                                           synonym_name=name,
1010
                                           bot=current_user.get_bot(),
1011
                                           user=current_user.get_user())
1012
    return {"data": {"_id": id}, "message": "Synonym value added!"}
1✔
1013

1014

1015
@router.post("/entity/synonym/{name:path}/values", response_model=Response)
1✔
1016
async def add_synonym_values(
1✔
1017
        name: str,
1018
        request_data: SynonymRequest,
1019
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1020
):
1021
    """
1022
    adds values to synonym
1023
    :param name:
1024
    :param request_data:
1025
    :param current_user:
1026
    :return: Success message
1027
    """
1028
    data = request_data.dict()
1✔
1029
    data['name'] = name
1✔
1030
    added_synonyms = mongo_processor.add_synonym_values(synonyms_dict=data, bot=current_user.get_bot(),
1✔
1031
                                                        user=current_user.get_user())
1032

1033
    return {"data": added_synonyms, "message": "Synonym values added!"}
1✔
1034

1035

1036
@router.put("/entity/synonym/{name:path}/value/{id}", response_model=Response)
1✔
1037
async def edit_synonym_value(
1✔
1038
        name: constr(to_lower=True, strip_whitespace=True),
1039
        id: str,
1040
        request_data: TextData,
1041
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
1042
):
1043
    """
1044
    Updates existing synonym value
1045
    """
1046
    mongo_processor.edit_synonym(
1✔
1047
        id,
1048
        request_data.data,
1049
        name,
1050
        current_user.get_bot(),
1051
        current_user.get_user(),
1052
    )
1053
    return {
1✔
1054
        "message": "Synonym value updated!"
1055
    }
1056

1057

1058
@router.delete("/entity/synonym/{id}", response_model=Response)
1✔
1059
async def delete_synonym_value(
1✔
1060
        id: str,
1061
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1062
):
1063
    """
1064
    Deletes existing synonym with value.
1065
    """
1066
    mongo_processor.delete_synonym(
1✔
1067
        id=id, bot=current_user.get_bot(), user=current_user.get_user()
1068
    )
1069
    return {
1✔
1070
        "message": "Synonym removed!"
1071
    }
1072

1073

1074
@router.delete("/entity/synonym/{name:path}/value/{id}", response_model=Response)
1✔
1075
async def delete_synonym_value(
1✔
1076
        name: constr(to_lower=True, strip_whitespace=True),
1077
        id: str,
1078
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1079
):
1080
    """
1081
    Deletes existing synonym value.
1082
    """
1083
    mongo_processor.delete_synonym_value(
1✔
1084
        synonym_name=name, value_id=id, bot=current_user.get_bot(), user=current_user.get_user()
1085
    )
1086
    return {
1✔
1087
        "message": "Synonym value removed!"
1088
    }
1089

1090

1091
@router.post("/utterance", response_model=Response)
1✔
1092
async def add_utterance(request: TextDataLowerCase,
1✔
1093
                        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)):
1094
    mongo_processor.add_utterance_name(
1✔
1095
        request.data, current_user.get_bot(), current_user.get_user(), raise_error_if_exists=True
1096
    )
1097
    return {'message': 'Utterance added!'}
1✔
1098

1099

1100
@router.get("/utterance", response_model=Response)
1✔
1101
async def get_utterance(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
1102
    return {'data': {"utterances": list(mongo_processor.get_utterances(current_user.get_bot()))}}
1✔
1103

1104

1105
@router.get("/data/count", response_model=Response)
1✔
1106
async def get_training_data_count(
1✔
1107
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1108
    count = mongo_processor.get_training_data_count(current_user.get_bot())
1✔
1109
    return Response(data=count)
1✔
1110

1111

1112
@router.get("/chat/client/config/url", response_model=Response)
1✔
1113
async def get_chat_client_config_url(
1✔
1114
        request: Request,
1115
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)):
1116
    url = mongo_processor.get_chat_client_config_url(current_user.get_bot(), current_user.email,
1✔
1117
                                                     request=request, account=current_user.account,
1118
                                                     bot_account=current_user.bot_account)
1119
    return Response(data=url)
1✔
1120

1121

1122
@router.get("/chat/client/config/{token}", response_model=Response)
1✔
1123
async def get_client_config_using_uid(
1✔
1124
        request: Request, bot: Text = Path(description="Bot id"),
1125
        token: Text = Path(description="Token generated from api server"),
1126
        token_claims: Dict = Security(Authentication.validate_bot_specific_token, scopes=TESTER_ACCESS)
1127
):
1128
    config = mongo_processor.get_client_config_using_uid(bot, token_claims)
1✔
1129
    config = Utility.validate_domain(request, config)
1✔
1130
    return Response(data=config['config'])
1✔
1131

1132

1133
@router.get("/chat/client/config", response_model=Response)
1✔
1134
async def get_client_config(
1✔
1135
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1136
    config = mongo_processor.get_chat_client_config(current_user.get_bot(), current_user.email)
1✔
1137
    config = config.to_mongo().to_dict()
1✔
1138
    return Response(data=config['config'])
1✔
1139

1140

1141
@router.post("/chat/client/config", response_model=Response)
1✔
1142
async def set_client_config(request: DictData, current_user: User = Security(Authentication.get_current_user_and_bot,
1✔
1143
                                                                             scopes=DESIGNER_ACCESS)):
1144
    mongo_processor.save_chat_client_config(request.data, current_user.get_bot(), current_user.get_user())
1✔
1145
    return {"message": "Config saved"}
1✔
1146

1147

1148
@router.get("/regex", response_model=Response)
1✔
1149
async def get_all_regex_patterns(
1✔
1150
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1151
):
1152
    """
1153
    Fetches the stored regex patterns of the bot
1154
    """
1155
    regex = list(mongo_processor.fetch_regex_features(bot=current_user.get_bot()))
1✔
1156
    return {"data": regex}
1✔
1157

1158

1159
@router.post("/regex", response_model=Response)
1✔
1160
async def add_regex(
1✔
1161
        request_data: RegexRequest,
1162
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1163
):
1164
    """
1165
    adds a new regex and its pattern
1166
    :param request_data:
1167
    :param current_user:
1168
    :return: Success message
1169
    """
1170

1171
    regex_id = mongo_processor.add_regex(regex_dict=request_data.dict(), bot=current_user.get_bot(),
1✔
1172
                                         user=current_user.get_user())
1173

1174
    return {"message": "Regex pattern added successfully!", "data": {"_id": regex_id}}
1✔
1175

1176

1177
@router.put("/regex", response_model=Response)
1✔
1178
async def edit_regex(
1✔
1179
        request_data: RegexRequest,
1180
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1181
):
1182
    """
1183
    edits a regex pattern
1184
    :param request_data:
1185
    :param current_user:
1186
    :return: Success message
1187
    """
1188

1189
    mongo_processor.edit_regex(regex_dict=request_data.dict(), bot=current_user.get_bot(), user=current_user.get_user())
1✔
1190

1191
    return {"message": "Regex pattern modified successfully!"}
1✔
1192

1193

1194
@router.delete("/regex/{name}", response_model=Response)
1✔
1195
async def delete_regex(
1✔
1196
        name: str = Path(description="regex name", examples=["bot"]),
1197
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1198
):
1199
    """
1200
    deletes an existing regex pattern
1201
    :param name: regex pattern name
1202
    :param current_user:
1203
    :return: Success message
1204
    """
1205
    mongo_processor.delete_regex(regex_name=name, bot=current_user.get_bot(), user=current_user.get_user())
1✔
1206

1207
    return {"message": "Regex pattern deleted!"}
1✔
1208

1209

1210
@router.get("/lookups", response_model=Response)
1✔
1211
async def get_all_lookup_tables(
1✔
1212
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
1213
):
1214
    """
1215
    Fetches the stored lookup tables of the bot
1216
    """
1217
    lookup = list(mongo_processor.get_lookups(bot=current_user.get_bot()))
1✔
1218
    return {"data": lookup}
1✔
1219

1220

1221
@router.get("/lookup/{name:path}/values", response_model=Response)
1✔
1222
async def get_lookup_values(
1✔
1223
        name: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1224
):
1225
    """
1226
    Fetches list of values against lookup table name
1227
    """
1228
    return {
1✔
1229
        "data": list(mongo_processor.get_lookup_values(name, current_user.get_bot()))
1230
    }
1231

1232

1233
@router.post("/lookup", response_model=Response)
1✔
1234
async def add_lookup(
1✔
1235
        request_data: TextData,
1236
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1237
):
1238
    """
1239
    adds a new lookup
1240
    :param name:
1241
    :param request_data:
1242
    :param current_user:
1243
    :return: Success message
1244
    """
1245
    id = mongo_processor.add_lookup(lookup_name=request_data.data, bot=current_user.get_bot(),
1✔
1246
                               user=current_user.get_user())
1247

1248
    return {"message": "Lookup added!", "data": {"_id": id}}
1✔
1249

1250

1251
@router.post("/lookup/{name:path}/values", response_model=Response)
1✔
1252
async def add_lookup_values(
1✔
1253
        name: str,
1254
        request_data: LookupTablesRequest,
1255
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1256
):
1257
    """
1258
    adds a new lookup table and its values
1259
    :param name:
1260
    :param request_data:
1261
    :param current_user:
1262
    :return: Success message
1263
    """
1264
    data = request_data.dict()
1✔
1265
    data['name'] = name
1✔
1266
    values = mongo_processor.add_lookup_values(lookup_dict=data, bot=current_user.get_bot(),
1✔
1267
                                      user=current_user.get_user())
1268

1269
    return {"message": "Lookup values added!", "data": values}
1✔
1270

1271

1272
@router.post("/lookup/{name:path}/value", response_model=Response)
1✔
1273
async def add_lookup_value(
1✔
1274
        name: str,
1275
        request_data: TextData,
1276
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1277
):
1278
    """
1279
    adds a new lookup table and its values
1280
    :param name:
1281
    :param request_data:
1282
    :param current_user:
1283
    :return: Success message
1284
    """
1285
    id = mongo_processor.add_lookup_value(lookup_name=name,
1✔
1286
                                     lookup_value=request_data.data,
1287
                                     bot=current_user.get_bot(),
1288
                                     user=current_user.get_user())
1289

1290
    return {"message": "Lookup value added!", "data": {"_id": id}}
1✔
1291

1292

1293
@router.put("/lookup/{name:path}/value/{id}", response_model=Response)
1✔
1294
async def edit_lookup_value(
1✔
1295
        name: str,
1296
        id: str,
1297
        request_data: TextData,
1298
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
1299
):
1300
    """
1301
    Updates existing lookup table value
1302
    """
1303
    mongo_processor.edit_lookup_value(
1✔
1304
        id,
1305
        request_data.data,
1306
        name,
1307
        current_user.get_bot(),
1308
        current_user.get_user(),
1309
    )
1310
    return {
1✔
1311
        "message": "Lookup value updated!"
1312
    }
1313

1314

1315
@router.delete("/lookup/{name:path}/value/{id}", response_model=Response)
1✔
1316
async def delete_lookup_value(
1✔
1317
        name: str,
1318
        id: str,
1319
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1320
):
1321
    """
1322
    Deletes existing lookup value.
1323
    """
1324
    mongo_processor.delete_lookup_value(
1✔
1325
        id, name, current_user.get_bot(), user=current_user.get_user()
1326
    )
1327
    return {
1✔
1328
        "message": "Lookup value removed!"
1329
    }
1330

1331

1332
@router.delete("/lookup/{id}", response_model=Response)
1✔
1333
async def delete_lookup(
1✔
1334
        id: str,
1335
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1336
):
1337
    """
1338
    Deletes existing lookup.
1339
    """
1340
    mongo_processor.delete_lookup(
1✔
1341
        id, current_user.get_bot(), user=current_user.get_user()
1342
    )
1343
    return {
1✔
1344
        "message": "Lookup removed!"
1345
    }
1346

1347

1348
@router.post("/slots/mapping", response_model=Response)
1✔
1349
async def add_slot_mapping(request: SlotMappingRequest,
1✔
1350
                           current_user: User = Security(Authentication.get_current_user_and_bot,
1351
                                                         scopes=DESIGNER_ACCESS)):
1352
    """
1353
    Adds slot mapping.
1354
    """
1355
    mapping_id = mongo_processor.add_slot_mapping(request.dict(), current_user.get_bot(), current_user.get_user())
1✔
1356
    return Response(message='Slot mapping added', data={"id": mapping_id})
1✔
1357

1358

1359
@router.put("/slots/mapping/{mapping_id}", response_model=Response)
1✔
1360
async def update_slot_mapping(request: SlotMappingRequest,
1✔
1361
                              mapping_id: str = Path(description="Slot Mapping id"),
1362
                              current_user: User = Security(Authentication.get_current_user_and_bot,
1363
                                                            scopes=DESIGNER_ACCESS)):
1364
    """
1365
    Updates slot mapping.
1366
    """
1367
    mongo_processor.update_slot_mapping(request.dict(), mapping_id)
1✔
1368
    return Response(message='Slot mapping updated')
1✔
1369

1370

1371
@router.get("/slots/mapping", response_model=Response)
1✔
1372
async def get_slot_mapping(
1✔
1373
        form: str = None,
1374
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1375
):
1376
    """
1377
    Retrieves slot mapping.
1378
    If form name is given as `form` query parameter, then slot mappings for that particular form will be retrieved.
1379
    """
1380
    return Response(data=list(mongo_processor.get_slot_mappings(current_user.get_bot(), form, True)))
1✔
1381

1382

1383
@router.delete("/slots/mapping_id/{mapping_id}", response_model=Response)
1✔
1384
async def delete_slot_mapping(mapping_id: str = Path(description="Slot Mapping id"),
1✔
1385
                              current_user: User = Security(Authentication.get_current_user_and_bot,
1386
                                                            scopes=DESIGNER_ACCESS)):
1387
    """
1388
    Deletes a slot mapping.
1389
    """
1390
    mongo_processor.delete_singular_slot_mapping(mapping_id)
1✔
1391
    return Response(message='Slot mapping deleted')
1✔
1392

1393

1394
@router.delete("/slots/mapping/{name}", response_model=Response)
1✔
1395
async def delete_slot_mapping(name: str = Path(description="Name of the mapping"),
1✔
1396
                              current_user: User = Security(Authentication.get_current_user_and_bot,
1397
                                                            scopes=DESIGNER_ACCESS)):
1398
    """
1399
    Deletes a slot mapping.
1400
    """
1401
    mongo_processor.delete_slot_mapping(name, current_user.get_bot(), current_user.get_user())
1✔
1402
    return Response(message='Slot mapping deleted')
1✔
1403

1404

1405
@router.post("/forms", response_model=Response)
1✔
1406
async def add_form(
1✔
1407
        request: Forms, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1408
):
1409
    """
1410
    Adds a new form.
1411
    """
1412
    form = mongo_processor.add_form(request.name, request.dict()['settings'], current_user.get_bot(),
1✔
1413
                                    current_user.get_user())
1414
    return Response(data=form, message='Form added')
1✔
1415

1416

1417
@router.get("/forms", response_model=Response)
1✔
1418
async def list_forms(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
1419
    """
1420
    Lists all forms in the bot.
1421
    """
1422
    forms = list(mongo_processor.list_forms(current_user.get_bot()))
1✔
1423
    return Response(data=forms)
1✔
1424

1425

1426
@router.get("/forms/{form_id}", response_model=Response)
1✔
1427
async def get_form(
1✔
1428
        form_id: str = Path(description="Form id"),
1429
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1430
):
1431
    """
1432
    Get a particular form.
1433
    """
1434
    form = mongo_processor.get_form(form_id, current_user.get_bot())
1✔
1435
    return Response(data=form)
1✔
1436

1437

1438
@router.put("/forms", response_model=Response)
1✔
1439
async def edit_form(
1✔
1440
        request: Forms, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1441
):
1442
    """
1443
    Edits a form.
1444
    """
1445
    mongo_processor.edit_form(request.name, request.dict()['settings'], current_user.get_bot(), current_user.get_user())
1✔
1446
    return Response(message='Form updated')
1✔
1447

1448

1449
@router.delete("/forms/{form_name}", response_model=Response)
1✔
1450
async def delete_form(
1✔
1451
        form_name: str,
1452
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1453
):
1454
    """
1455
    Deletes a form and its associated utterances.
1456
    """
1457
    mongo_processor.delete_form(form_name, current_user.get_bot(), current_user.get_user())
1✔
1458
    return Response(message='Form deleted')
1✔
1459

1460

1461
@router.get("/entities", response_model=Response)
1✔
1462
async def list_entities(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1✔
1463
    """
1464
    Fetch entities registered in a bot.
1465
    """
1466
    return Response(data=mongo_processor.get_entities(current_user.get_bot()))
1✔
1467

1468

1469
@router.put("/assets/{asset_type}", response_model=Response)
1✔
1470
async def upload_bot_assets(
1✔
1471
        asset_type: str, asset: UploadFile,
1472
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1473
):
1474
    """
1475
    Uploads bot assets to repository.
1476
    """
1477
    data = {"url": await AssetsProcessor.add_asset(current_user.get_bot(), current_user.get_user(), asset, asset_type)}
1✔
1478
    UserActivityLogger.add_log(
1✔
1479
        UserActivityType.add_asset, current_user.account, current_user.get_user(), current_user.get_bot(),
1480
        [f"asset_type={asset_type}"]
1481
    )
1482
    return Response(message='Asset added', data=data)
1✔
1483

1484

1485
@router.delete("/assets/{asset_type}", response_model=Response)
1✔
1486
async def delete_bot_assets(
1✔
1487
        asset_type: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1488
):
1489
    """
1490
    Deletes bot assets from repository.
1491
    """
1492
    AssetsProcessor.delete_asset(current_user.get_bot(), current_user.get_user(), asset_type)
1✔
1493
    UserActivityLogger.add_log(
1✔
1494
        UserActivityType.delete_asset, current_user.account, current_user.get_user(), current_user.get_bot(),
1495
        [f"asset_type={asset_type}"]
1496
    )
1497
    return Response(message='Asset deleted')
1✔
1498

1499

1500
@router.get("/assets", response_model=Response)
1✔
1501
async def list_bot_assets(
1✔
1502
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1503
):
1504
    """
1505
    Deletes bot assets from repository.
1506
    """
1507
    return Response(data={"assets": list(AssetsProcessor.list_assets(current_user.get_bot()))})
1✔
1508

1509

1510
@router.post("/audit/event/config", response_model=Response)
1✔
1511
async def set_auditlog_config(request_data: EventConfig,
1✔
1512
                              current_user: User = Security(Authentication.get_current_user_and_bot,
1513
                                                            scopes=DESIGNER_ACCESS)):
1514
    mongo_processor.save_auditlog_event_config(current_user.get_bot(), current_user.get_user(), request_data.dict())
×
UNCOV
1515
    return {"message": "Event config saved"}
×
1516

1517

1518
@router.get("/audit/event/config", response_model=Response)
1✔
1519
async def get_auditlog_config(current_user: User = Security(Authentication.get_current_user_and_bot,
1✔
1520
                                                            scopes=DESIGNER_ACCESS)):
1521
    data = mongo_processor.get_auditlog_event_config(current_user.get_bot())
×
UNCOV
1522
    return Response(data=data)
×
1523

1524

1525
@router.get("/auditlog/data/{from_date}/{to_date}", response_model=Response)
1✔
1526
async def get_auditlog_for_bot(
1✔
1527
        start_idx: int = 0, page_size: int = 10,
1528
        from_date: date = Path(description="from date in yyyy-mm-dd format", examples=["1999-01-01"]),
1529
        to_date: date = Path(description="to date in yyyy-mm-dd format", examples=["1999-01-01"]),
1530
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
1531
):
1532
    logs, row_cnt = mongo_processor.get_auditlog_for_bot(current_user.get_bot(), from_date, to_date, start_idx, page_size)
1✔
1533
    data = {
1✔
1534
        "logs": logs,
1535
        "total": row_cnt
1536
    }
1537
    return Response(data=data)
1✔
1538

1539

1540
@router.get("/logs/download/{log_type}", response_model=Response)
1✔
1541
async def download_logs(
1✔
1542
        background_tasks: BackgroundTasks,
1543
        start_date: datetime, end_date: datetime,
1544
        log_type: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS),
1545
):
1546
    logs = mongo_processor.get_logs(current_user.get_bot(), log_type, start_date, end_date)
1✔
1547
    file, temp_path = Utility.download_csv(logs, message=f"Logs not found!", filename=f"{log_type}.csv")
1✔
1548
    response = FileResponse(
1✔
1549
        file, filename=os.path.basename(file), background=background_tasks
1550
    )
1551
    response.headers[
1✔
1552
        "Content-Disposition"
1553
    ] = "attachment; filename=" + os.path.basename(file)
1554
    background_tasks.add_task(Utility.delete_directory, temp_path)
1✔
1555
    return response
1✔
1556

1557

1558
@router.get("/qna/flatten", response_model=Response)
1✔
1559
async def get_qna_flattened(
1✔
1560
        start_idx: int = 0, page_size: int = 10,
1561
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS),
1562
):
1563
    qna = list(mongo_processor.flatten_qna(current_user.get_bot(), start_idx, page_size))
1✔
1564
    page_cnt = mongo_processor.get_row_count(Rules, current_user.get_bot(), status=True,
1✔
1565
                                             template_type=TemplateType.QNA.value)
1566
    data = {
1✔
1567
        "qna": qna,
1568
        "total": page_cnt
1569
    }
1570
    return Response(data=data)
1✔
1571

1572

1573
router.include_router(v2, prefix="/v2")
1✔
1574

1575

1576
@router.get("/settings", response_model=Response)
1✔
1577
async def get_bot_settings(
1✔
1578
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=AGENT_ACCESS),
1579
):
1580
    """Retrieves bot settings"""
1581
    bot_settings = MongoProcessor.get_bot_settings(current_user.get_bot(), current_user.get_user())
1✔
1582
    bot_settings = bot_settings.to_mongo().to_dict()
1✔
1583
    bot_settings.pop("_id")
1✔
1584
    return Response(data=bot_settings)
1✔
1585

1586

1587
@router.put("/settings", response_model=Response)
1✔
1588
async def update_bot_settings(
1✔
1589
        bot_settings: BotSettingsRequest,
1590
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=ADMIN_ACCESS)
1591
):
1592
    """Updates bot settings"""
1593
    MongoProcessor.edit_bot_settings(bot_settings.dict(), current_user.get_bot(), current_user.get_user())
1✔
1594
    return Response(message='Bot Settings updated')
1✔
1595

1596

1597
@router.get("/live_agent_token", response_model=Response)
1✔
1598
async def get_live_agent_token(current_user: User = Security(Authentication.get_current_user_and_bot, scopes=AGENT_ACCESS)):
1✔
1599
    """
1600
    Fetches existing list of stories (conversation flows)
1601
    """
1602
    data = await LiveAgentHandler.authenticate_agent(current_user.get_user(), current_user.get_bot())
×
UNCOV
1603
    return Response(data=data)
×
1604

1605

1606
@router.get("/llm/logs", response_model=Response)
1✔
1607
async def get_llm_logs(
1✔
1608
        start_idx: int = 0, page_size: int = 10,
1609
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1610
):
1611
    """
1612
    Get data llm event logs.
1613
    """
1614
    logs = list(LLMProcessor.get_logs(current_user.get_bot(), start_idx, page_size))
1✔
1615
    row_cnt = LLMProcessor.get_row_count(current_user.get_bot())
1✔
1616
    data = {
1✔
1617
        "logs": logs,
1618
        "total": row_cnt
1619
    }
1620
    return Response(data=data)
1✔
1621

1622

1623
@router.get("/executor/logs", response_model=Response)
1✔
1624
async def get_executor_logs(
1✔
1625
        start_idx: int = 0, page_size: int = 10,
1626
        event_class: str = None, task_type: str = None,
1627
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)
1628
):
1629
    """
1630
    Get executor logs data based on filters provided.
1631
    """
1632
    logs = list(ExecutorProcessor.get_executor_logs(current_user.get_bot(), start_idx, page_size,
1✔
1633
                                                    event_class=event_class, task_type=task_type))
1634
    row_cnt = ExecutorProcessor.get_row_count(current_user.get_bot(),
1✔
1635
                                              event_class=event_class,
1636
                                              task_type=task_type)
1637
    data = {
1✔
1638
        "logs": logs,
1639
        "total": row_cnt
1640
    }
1641
    return Response(data=data)
1✔
1642

1643

1644
@router.get("/metadata/llm", response_model=Response)
1✔
1645
async def get_llm_metadata(
1✔
1646
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)) -> Response:
1647
    """
1648
    Returns a list of LLMs and their corresponding models available for the bot.
1649
    """
1650
    llm_models = LLMProcessor.fetch_llm_metadata(current_user.get_bot())
1✔
1651
    return Response(data=llm_models)
1✔
1652

1653

1654
@router.get("/slots/{slot_name}", response_model=Response)
1✔
1655
async def get_slot_actions(
1✔
1656
        slot_name: str = Path(description="slot name", examples=["audio", "order"]),
1657
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)) -> Response:
1658
    """
1659
    Returns a list of Actions mapped to that particular slot name.
1660
    """
1661
    llm_models = MongoProcessor.get_slot_mapped_actions(current_user.get_bot(), slot_name)
1✔
1662
    return Response(data=llm_models)
1✔
1663

1664

1665
@router.get("/mail/config", response_model=Response)
1✔
1666
async def get_all_mail_configs(
1✔
1667
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=TESTER_ACCESS)):
1668
    """
1669
    Fetches mail config
1670
    """
1671
    data = MailClassificationConfig.objects(bot=current_user.get_bot(), status=True)
1✔
1672
    formatted_data = [
1✔
1673
        {key: value for key, value in item.to_mongo().items() if key not in {"_id", "user"}}
1674
        for item in data
1675
    ]
1676

1677
    return {"data": formatted_data}
1✔
1678

1679

1680

1681
@router.post("/mail/config", response_model=Response)
1✔
1682
async def set_mail_config(
1✔
1683
        request_data: MailConfigRequest,
1684
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1685
):
1686
    """
1687
    Applies the mail config
1688
    """
1689
    request_dict = request_data.dict()
1✔
1690
    MailClassificationConfig.create_doc(**request_dict, bot=current_user.get_bot(), user=current_user.get_user())
1✔
1691
    return {"message": "Config applied!"}
1✔
1692

1693

1694
@router.put("/mail/config", response_model=Response)
1✔
1695
async def update_mail_config(
1✔
1696
        request_data: MailConfigRequest,
1697
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1698
):
1699
    """
1700
    update the mail config
1701
    """
1702
    request_dict = request_data.dict()
1✔
1703
    MailClassificationConfig.update_doc(**request_dict, bot=current_user.get_bot())
1✔
1704
    return {"message": "Config updated!"}
1✔
1705

1706

1707

1708
@router.delete("/mail/config/{intent}", response_model=Response)
1✔
1709
async def del_soft_mail_config(
1✔
1710
        intent: str,
1711
        current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS)
1712
):
1713
    """
1714
    delete the mail config
1715
    """
1716
    MailClassificationConfig.soft_delete_doc(current_user.get_bot(), intent)
1✔
1717
    return {"message": "Config deleted!"}
1✔
1718

1719

1720

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc