@coveo/quantic 3.42.1 → 3.43.1

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.
Files changed (32) hide show
  1. package/force-app/main/default/labels/CustomLabels.labels-meta.xml +21 -0
  2. package/force-app/main/default/lwc/quanticGeneratedAnswer/__tests__/quanticGeneratedAnswer.test.js +145 -60
  3. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/agentData.ts +248 -0
  4. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture-agent.ts +77 -0
  5. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/fixture.ts +74 -65
  6. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/pageObject.ts +157 -15
  7. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswer.e2e.ts +1 -5
  8. package/force-app/main/default/lwc/quanticGeneratedAnswer/e2e/quanticGeneratedAnswerAgent.e2e.ts +233 -0
  9. package/force-app/main/default/lwc/quanticGeneratedAnswer/quanticGeneratedAnswer.js +12 -108
  10. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/conversation.html +1 -1
  11. package/force-app/main/default/lwc/quanticGeneratedAnswer/templates/generatedAnswer.html +8 -47
  12. package/force-app/main/default/lwc/quanticGeneratedAnswerContent/__tests__/quanticGeneratedAnswerContent.test.js +27 -0
  13. package/force-app/main/default/lwc/quanticGeneratedAnswerContent/quanticGeneratedAnswerContent.js +8 -1
  14. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/__tests__/quanticGeneratedAnswerFollowUpInput.test.js +242 -10
  15. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.css +2 -2
  16. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.html +16 -1
  17. package/force-app/main/default/lwc/quanticGeneratedAnswerFollowUpInput/quanticGeneratedAnswerFollowUpInput.js +47 -4
  18. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/__tests__/quanticGeneratedAnswerStreamOfThought.test.js +95 -41
  19. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/__tests__/stepsUtils.test.js +118 -0
  20. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/quanticGeneratedAnswerStreamOfThought.js +21 -35
  21. package/force-app/main/default/lwc/quanticGeneratedAnswerStreamOfThought/stepsUtils.js +66 -0
  22. package/force-app/main/default/staticresources/coveoheadless/case-assist/headless.js +15 -15
  23. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/product-listing/facets/headless-product-listing-facet-options.d.ts +4 -39
  24. package/force-app/main/default/staticresources/coveoheadless/definitions/controllers/commerce/search/facets/headless-search-facet-options.d.ts +4 -39
  25. package/force-app/main/default/staticresources/coveoheadless/definitions/features/commerce/facets/facet-set/facet-set-selector.d.ts +3 -9
  26. package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-search-set/category/category-facet-search-state-selector.d.ts +1 -27
  27. package/force-app/main/default/staticresources/coveoheadless/definitions/features/facets/facet-search-set/specific/specific-facet-search-state-selector.d.ts +1 -27
  28. package/force-app/main/default/staticresources/coveoheadless/headless.js +17 -17
  29. package/force-app/main/default/staticresources/coveoheadless/insight/headless.js +16 -16
  30. package/force-app/main/default/staticresources/coveoheadless/recommendation/headless.js +14 -14
  31. package/force-app/main/default/staticresources/dompurify/purify.min.js +2 -2
  32. package/package.json +7 -6
@@ -1652,6 +1652,13 @@
1652
1652
  <protected>false</protected>
1653
1653
  <shortDescription>Submit follow-up</shortDescription>
1654
1654
  </labels>
1655
+ <labels>
1656
+ <fullName>quantic_FollowUpInputTooLong</fullName>
1657
+ <value>Your question is too long. Please use {{0}} characters or fewer.</value>
1658
+ <language>en_US</language>
1659
+ <protected>false</protected>
1660
+ <shortDescription>Follow-up question character limit exceeded</shortDescription>
1661
+ </labels>
1655
1662
  <labels>
1656
1663
  <fullName>quantic_ShowPreviousQuestions_plural</fullName>
1657
1664
  <value>Show {{0}} previous questions</value>
@@ -1687,6 +1694,20 @@
1687
1694
  <protected>false</protected>
1688
1695
  <shortDescription>Knowledge base searched</shortDescription>
1689
1696
  </labels>
1697
+ <labels>
1698
+ <fullName>quantic_AgentGenerationStepSearchQuery</fullName>
1699
+ <value>Searching for {{0}}…</value>
1700
+ <language>en_US</language>
1701
+ <protected>false</protected>
1702
+ <shortDescription>Searching for query</shortDescription>
1703
+ </labels>
1704
+ <labels>
1705
+ <fullName>quantic_AgentGenerationStepSearchQueryCompleted</fullName>
1706
+ <value>Searched for {{0}}</value>
1707
+ <language>en_US</language>
1708
+ <protected>false</protected>
1709
+ <shortDescription>Searched for query</shortDescription>
1710
+ </labels>
1690
1711
  <labels>
1691
1712
  <fullName>quantic_AgentGenerationStepAnalyzingResults</fullName>
1692
1713
  <value>Analyzing results…</value>
@@ -89,9 +89,8 @@ const selectors = {
89
89
  generatedAnswer: '[data-testid="generated-answer__answer"]',
90
90
  generatedAnswerBadge: '[data-testid="generated-answer__header-title"]',
91
91
  generatedAnswerRetryButton: '[data-testid="generated-answer__retry-button"]',
92
- generatedAnswerActions: '[data-testid="generated-answer__actions"]',
93
92
  generatedAnswerToggleButton: 'c-quantic-generated-answer-toggle',
94
- generatedAnswerContent: 'c-quantic-generated-answer-content',
93
+ generatedAnswerBodyComponent: 'c-quantic-generated-answer-body',
95
94
  generatingMessageWhenAnswerCollapsed:
96
95
  '[data-testid="generated-answer__collapse-generating-message"]',
97
96
  generatedAnswerCollapseToggle:
@@ -101,10 +100,8 @@ const selectors = {
101
100
  '[data-testid="generated-answer__no-answer-card"]',
102
101
  generatedAnswerNoAnswerMessage:
103
102
  '[data-testid="generated-answer__no-answer-message"]',
104
- generatedAnswerCitations: 'c-quantic-source-citations',
105
103
  generatedAnswerThread: 'c-quantic-generated-answer-thread',
106
104
  generatedAnswerFollowUpInput: 'c-quantic-generated-answer-follow-up-input',
107
- generatedAnswerFeedback: 'c-quantic-feedback',
108
105
  loadingSpinner: 'lightning-spinner',
109
106
  };
110
107
 
@@ -128,6 +125,7 @@ const functionsMocks = {
128
125
  like: functionsMocks.like,
129
126
  dislike: functionsMocks.dislike,
130
127
  closeFeedbackModal: functionsMocks.closeFeedbackModal,
128
+ logCitationHover: functionsMocks.logCitationHover,
131
129
  })),
132
130
  buildSearchStatus: jest.fn(() => ({
133
131
  state: searchStatusState,
@@ -148,6 +146,7 @@ const functionsMocks = {
148
146
  like: jest.fn(),
149
147
  dislike: jest.fn(),
150
148
  closeFeedbackModal: jest.fn(),
149
+ logCitationHover: jest.fn(),
151
150
  };
152
151
 
153
152
  /**
@@ -504,33 +503,25 @@ describe('c-quantic-generated-answer', () => {
504
503
  });
505
504
  });
506
505
 
507
- it('should display the generated answer content', async () => {
506
+ it('should forward the streaming answer to the generated answer body', async () => {
508
507
  const element = createTestComponent();
509
508
  await flushPromises();
510
509
 
511
- const generatedAnswerContent = element.shadowRoot.querySelector(
512
- selectors.generatedAnswerContent
513
- );
514
-
515
- expect(generatedAnswerContent).not.toBeNull();
516
- expect(generatedAnswerContent.isStreaming).toBe(true);
517
- expect(generatedAnswerContent.answer).toBe(exampleAnswer);
518
- expect(generatedAnswerContent.answerContentFormat).toBe(
519
- exampleAnswerContentFormat
510
+ const generatedAnswerBody = element.shadowRoot.querySelector(
511
+ selectors.generatedAnswerBodyComponent
520
512
  );
521
- expect(generatedAnswerContent.engineId).toBe(exampleEngineId);
522
- expect(generatedAnswerContent.answerId).toBe(exampleAnswerId);
523
- });
524
-
525
- it('should not display the generated answer actions', async () => {
526
- const element = createTestComponent();
527
- await flushPromises();
528
513
 
529
- const generatedAnswerActions = element.shadowRoot.querySelector(
530
- selectors.generatedAnswerActions
514
+ expect(generatedAnswerBody).not.toBeNull();
515
+ expect(generatedAnswerBody.engineId).toBe(exampleEngineId);
516
+ expect(generatedAnswerBody.disableCitationAnchoring).toBe(false);
517
+ expect(generatedAnswerBody.generatedAnswer).toEqual(
518
+ expect.objectContaining({
519
+ answer: exampleAnswer,
520
+ answerContentFormat: exampleAnswerContentFormat,
521
+ answerId: exampleAnswerId,
522
+ isStreaming: true,
523
+ })
531
524
  );
532
-
533
- expect(generatedAnswerActions).toBeNull();
534
525
  });
535
526
 
536
527
  it('should not display the generated answer disclaimer in the footer', async () => {
@@ -769,38 +760,48 @@ describe('c-quantic-generated-answer', () => {
769
760
  });
770
761
  });
771
762
 
772
- it('should display the generated answer content', async () => {
763
+ it('should forward the ready answer with citations to the generated answer body', async () => {
773
764
  const element = createTestComponent();
774
765
  await flushPromises();
775
766
 
776
- const generatedAnswerContent = element.shadowRoot.querySelector(
777
- selectors.generatedAnswerContent
767
+ const generatedAnswerBody = element.shadowRoot.querySelector(
768
+ selectors.generatedAnswerBodyComponent
778
769
  );
779
770
 
780
- expect(generatedAnswerContent).not.toBeNull();
781
- expect(generatedAnswerContent.isStreaming).toBe(false);
782
- expect(generatedAnswerContent.answer).toBe(exampleAnswer);
783
- expect(generatedAnswerContent.answerContentFormat).toBe(
784
- exampleAnswerContentFormat
771
+ expect(generatedAnswerBody).not.toBeNull();
772
+ expect(generatedAnswerBody.engineId).toBe(exampleEngineId);
773
+ expect(generatedAnswerBody.disableCitationAnchoring).toBe(false);
774
+ expect(generatedAnswerBody.generatedAnswer).toEqual(
775
+ expect.objectContaining({
776
+ answer: exampleAnswer,
777
+ answerContentFormat: exampleAnswerContentFormat,
778
+ citations: exampleCitations,
779
+ isStreaming: false,
780
+ })
785
781
  );
786
782
  });
787
783
 
788
- it('should display the generated answer actions', async () => {
789
- const element = createTestComponent();
784
+ it('should collapse the answer when the body reports a content height exceeding the maximum collapsed height', async () => {
785
+ const element = createTestComponent({
786
+ ...defaultOptions,
787
+ collapsible: true,
788
+ });
790
789
  await flushPromises();
791
790
 
792
791
  const generatedAnswerBody = element.shadowRoot.querySelector(
793
- selectors.generatedAnswerBody
792
+ selectors.generatedAnswerBodyComponent
794
793
  );
795
- const generatedAnswerActions = element.shadowRoot.querySelector(
796
- selectors.generatedAnswerActions
794
+ generatedAnswerBody.dispatchEvent(
795
+ new CustomEvent('quantic__answercontentupdated', {
796
+ detail: {height: defaultAnswerHeight + 100},
797
+ })
797
798
  );
799
+ await flushPromises();
798
800
 
799
- expect(generatedAnswerBody).not.toBeNull();
800
- expect(generatedAnswerActions).not.toBeNull();
801
- expect(generatedAnswerActions.closest('section')).toBe(
802
- generatedAnswerBody
801
+ const generatedAnswerCollapseToggle = element.shadowRoot.querySelector(
802
+ selectors.generatedAnswerCollapseToggle
803
803
  );
804
+ expect(generatedAnswerCollapseToggle).not.toBeNull();
804
805
  });
805
806
 
806
807
  it('should display the generated answer disclaimer', async () => {
@@ -833,17 +834,6 @@ describe('c-quantic-generated-answer', () => {
833
834
  expect(generatedAnswerBadge).not.toBeNull();
834
835
  });
835
836
 
836
- it('should pass the disableCitationAnchoring property to the source citations component', async () => {
837
- const element = createTestComponent();
838
- await flushPromises();
839
-
840
- const generatedAnswerCitations = element.shadowRoot.querySelector(
841
- selectors.generatedAnswerCitations
842
- );
843
- expect(generatedAnswerCitations).not.toBeNull();
844
- expect(generatedAnswerCitations.disableCitationAnchoring).toBe(false);
845
- });
846
-
847
837
  describe('when follow-ups are enabled', () => {
848
838
  const exampleAgentId = 'example agent id';
849
839
  const exampleFollowUpAnswers = [
@@ -1385,6 +1375,103 @@ describe('c-quantic-generated-answer', () => {
1385
1375
  });
1386
1376
  });
1387
1377
 
1378
+ describe('the citation hover event', () => {
1379
+ const exampleAnswer = 'answer generated successfully';
1380
+ const exampleAgentId = 'example agent id';
1381
+ const exampleCitationId = 'citation-id-1';
1382
+ const exampleCitationHoverTimeMs = 1500;
1383
+
1384
+ describe('when follow-ups are not enabled', () => {
1385
+ beforeEach(() => {
1386
+ generatedAnswerState = {
1387
+ ...initialGeneratedAnswerState,
1388
+ isStreaming: false,
1389
+ answer: exampleAnswer,
1390
+ answerContentFormat: 'text/markdown',
1391
+ answerId: exampleAnswerId,
1392
+ citations: exampleCitations,
1393
+ };
1394
+ mockSuccessfulHeadlessInitialization();
1395
+ prepareHeadlessState();
1396
+ });
1397
+
1398
+ afterAll(() => {
1399
+ generatedAnswerState = initialGeneratedAnswerState;
1400
+ });
1401
+
1402
+ it('should call logCitationHover on the controller with the correct parameters', async () => {
1403
+ const element = createTestComponent();
1404
+ await flushPromises();
1405
+
1406
+ const generatedAnswerBody = element.shadowRoot.querySelector(
1407
+ selectors.generatedAnswerBodyComponent
1408
+ );
1409
+ generatedAnswerBody.dispatchEvent(
1410
+ new CustomEvent('quantic__citationhover', {
1411
+ detail: {
1412
+ citationId: exampleCitationId,
1413
+ citationHoverTimeMs: exampleCitationHoverTimeMs,
1414
+ answerId: exampleAnswerId,
1415
+ },
1416
+ })
1417
+ );
1418
+ await flushPromises();
1419
+
1420
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledTimes(1);
1421
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledWith(
1422
+ exampleCitationId,
1423
+ exampleCitationHoverTimeMs,
1424
+ exampleAnswerId
1425
+ );
1426
+ });
1427
+ });
1428
+
1429
+ describe('when follow-ups are enabled', () => {
1430
+ beforeEach(() => {
1431
+ generatedAnswerState = {
1432
+ ...initialGeneratedAnswerState,
1433
+ answer: exampleAnswer,
1434
+ followUpAnswers: {isEnabled: true, followUpAnswers: []},
1435
+ };
1436
+ mockSuccessfulHeadlessInitialization();
1437
+ prepareHeadlessState();
1438
+ });
1439
+
1440
+ afterAll(() => {
1441
+ generatedAnswerState = initialGeneratedAnswerState;
1442
+ });
1443
+
1444
+ it('should call logCitationHover on the controller with the correct parameters', async () => {
1445
+ const element = createTestComponent({
1446
+ ...defaultOptions,
1447
+ agentId: exampleAgentId,
1448
+ });
1449
+ await flushPromises();
1450
+
1451
+ const thread = element.shadowRoot.querySelector(
1452
+ selectors.generatedAnswerThread
1453
+ );
1454
+ thread.dispatchEvent(
1455
+ new CustomEvent('quantic__citationhover', {
1456
+ detail: {
1457
+ citationId: exampleCitationId,
1458
+ citationHoverTimeMs: exampleCitationHoverTimeMs,
1459
+ answerId: exampleAnswerId,
1460
+ },
1461
+ })
1462
+ );
1463
+ await flushPromises();
1464
+
1465
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledTimes(1);
1466
+ expect(functionsMocks.logCitationHover).toHaveBeenCalledWith(
1467
+ exampleCitationId,
1468
+ exampleCitationHoverTimeMs,
1469
+ exampleAnswerId
1470
+ );
1471
+ });
1472
+ });
1473
+ });
1474
+
1388
1475
  describe('the generated answer feedback', () => {
1389
1476
  const exampleAnswer = 'answer generated successfully';
1390
1477
  const exampleAgentId = 'example agent id';
@@ -1392,19 +1479,17 @@ describe('c-quantic-generated-answer', () => {
1392
1479
  describe.each([
1393
1480
  {
1394
1481
  action: 'like',
1395
- feedbackEvent: 'quantic__like',
1396
1482
  threadEvent: 'quantic__generatedanswerlike',
1397
1483
  controllerMethod: 'like',
1398
1484
  },
1399
1485
  {
1400
1486
  action: 'dislike',
1401
- feedbackEvent: 'quantic__dislike',
1402
1487
  threadEvent: 'quantic__generatedanswerdislike',
1403
1488
  controllerMethod: 'dislike',
1404
1489
  },
1405
1490
  ])(
1406
1491
  'when the user clicks the $action button',
1407
- ({feedbackEvent, threadEvent, controllerMethod}) => {
1492
+ ({threadEvent, controllerMethod}) => {
1408
1493
  describe('when follow-ups are not enabled', () => {
1409
1494
  beforeEach(() => {
1410
1495
  generatedAnswerState = {
@@ -1427,11 +1512,11 @@ describe('c-quantic-generated-answer', () => {
1427
1512
  const element = createTestComponent();
1428
1513
  await flushPromises();
1429
1514
 
1430
- const feedback = element.shadowRoot.querySelector(
1431
- selectors.generatedAnswerFeedback
1515
+ const generatedAnswerBody = element.shadowRoot.querySelector(
1516
+ selectors.generatedAnswerBodyComponent
1432
1517
  );
1433
- feedback.dispatchEvent(
1434
- new CustomEvent(feedbackEvent, {
1518
+ generatedAnswerBody.dispatchEvent(
1519
+ new CustomEvent(threadEvent, {
1435
1520
  detail: {answerId: exampleAnswerId},
1436
1521
  })
1437
1522
  );
@@ -0,0 +1,248 @@
1
+ type AgentRunType = {
2
+ type: 'RUN_STARTED' | 'RUN_FINISHED';
3
+ timestamp: number;
4
+ threadId: string;
5
+ runId: string;
6
+ result?: {
7
+ completionReason: string;
8
+ };
9
+ };
10
+
11
+ type AgentCustomType = {
12
+ type: 'CUSTOM';
13
+ timestamp: number;
14
+ name: string;
15
+ value: any;
16
+ };
17
+
18
+ type AgentStepType = {
19
+ type: 'STEP_STARTED' | 'STEP_FINISHED';
20
+ timestamp: number;
21
+ stepName: string;
22
+ };
23
+
24
+ type AgentToolCallType = {
25
+ type: 'TOOL_CALL_START' | 'TOOL_CALL_END';
26
+ timestamp: number;
27
+ toolCallId: string;
28
+ toolCallName: string;
29
+ parentMessageId: string;
30
+ };
31
+
32
+ type AgentToolCallArgsType = {
33
+ type: 'TOOL_CALL_ARGS';
34
+ timestamp: number;
35
+ toolCallId: string;
36
+ delta: any;
37
+ };
38
+
39
+ type AgentTextMessageType = {
40
+ type: 'TEXT_MESSAGE_CHUNK';
41
+ timestamp: number;
42
+ messageId: string;
43
+ parentMessageId: string;
44
+ delta: string;
45
+ };
46
+
47
+ export type AgentCitation = {
48
+ id: string;
49
+ title: string;
50
+ uri: string;
51
+ permanentid: string;
52
+ primaryId: string;
53
+ clickUri: string;
54
+ text: string;
55
+ source: string;
56
+ filetype: string;
57
+ fields: Record<string, any>;
58
+ };
59
+
60
+ type AgentCitationType = {
61
+ type: 'CUSTOM';
62
+ timestamp: number;
63
+ name: 'citations';
64
+ value: {
65
+ citations: Array<AgentCitation>;
66
+ };
67
+ };
68
+
69
+ const STREAM_ID = 'conv_1';
70
+ const ANSWER_ID_1 = 'conv_1_0001';
71
+ const ANSWER_ID_2 = 'conv_1_0002';
72
+ const ANSWER_ID_3 = 'conv_1_0003';
73
+ const CONVERSATION_TOKEN = 'conv_1_token';
74
+
75
+ const runStartStream = (answerId: string): AgentRunType => ({
76
+ type: 'RUN_STARTED',
77
+ timestamp: new Date().valueOf(),
78
+ threadId: STREAM_ID,
79
+ runId: answerId,
80
+ });
81
+
82
+ const customInitialHeadersStream = (followUpEnabled: boolean): AgentCustomType => ({
83
+ type: 'CUSTOM',
84
+ timestamp: new Date().valueOf(),
85
+ name: 'header',
86
+ value: {
87
+ contentFormat: 'text/markdown',
88
+ conversationId: STREAM_ID,
89
+ followUpEnabled,
90
+ conversationToken: CONVERSATION_TOKEN,
91
+ },
92
+ });
93
+
94
+ const stepStartedStream = (stepName: string): AgentStepType => ({
95
+ type: 'STEP_STARTED',
96
+ timestamp: new Date().valueOf(),
97
+ stepName,
98
+ });
99
+
100
+ const stepFinishedStream = (stepName: string): AgentStepType => ({
101
+ type: 'STEP_FINISHED',
102
+ timestamp: new Date().valueOf(),
103
+ stepName,
104
+ });
105
+
106
+ const toolCallStartStream = (toolCallName: string): AgentToolCallType => ({
107
+ type: 'TOOL_CALL_START',
108
+ timestamp: new Date().valueOf(),
109
+ toolCallId: 'tool_call_1',
110
+ toolCallName,
111
+ parentMessageId: 'message_1',
112
+ });
113
+
114
+ const searchQueryToolCallArgsStream = (q: string): AgentToolCallArgsType => ({
115
+ type: 'TOOL_CALL_ARGS',
116
+ timestamp: new Date().valueOf(),
117
+ toolCallId: 'tool_call_1',
118
+ delta: JSON.stringify({
119
+ q,
120
+ }),
121
+ });
122
+
123
+ const toolCallEndStream = (toolCallName: string): AgentToolCallType => ({
124
+ type: 'TOOL_CALL_END',
125
+ timestamp: new Date().valueOf(),
126
+ toolCallId: 'tool_call_1',
127
+ toolCallName,
128
+ parentMessageId: 'message_1',
129
+ });
130
+
131
+ const textMessageStream = (delta: string): AgentTextMessageType => ({
132
+ type: 'TEXT_MESSAGE_CHUNK',
133
+ timestamp: new Date().valueOf(),
134
+ messageId: 'message_2',
135
+ parentMessageId: 'message_1',
136
+ delta,
137
+ });
138
+
139
+ const exampleCitation: AgentCitation = {
140
+ id: 'some-id-1',
141
+ title: 'Some Title 1',
142
+ uri: 'https://www.coveo.com',
143
+ permanentid: 'some-permanent-id-1',
144
+ clickUri: 'https://www.coveo.com',
145
+ text: 'example text 1',
146
+ source: 'Some source 1',
147
+ filetype: 'pdf',
148
+ fields: {},
149
+ primaryId: 'primary-id-1',
150
+ };
151
+
152
+ const citationStream: AgentCitationType = {
153
+ type: 'CUSTOM',
154
+ timestamp: new Date().valueOf(),
155
+ name: 'citations',
156
+ value: {
157
+ citations: [exampleCitation],
158
+ },
159
+ };
160
+
161
+ const runFinishedStream = (answerId: string): AgentRunType => ({
162
+ type: 'RUN_FINISHED',
163
+ timestamp: new Date().valueOf(),
164
+ threadId: STREAM_ID,
165
+ runId: answerId,
166
+ result: {
167
+ completionReason: 'ANSWERED',
168
+ },
169
+ });
170
+
171
+ export type AgentMessage =
172
+ | AgentRunType
173
+ | AgentCustomType
174
+ | AgentStepType
175
+ | AgentToolCallType
176
+ | AgentToolCallArgsType
177
+ | AgentTextMessageType
178
+ | AgentCitationType;
179
+
180
+ export type AgentData = {
181
+ answerStreams: Array<AgentMessage>;
182
+ followUpStreams: Array<Array<AgentMessage>>;
183
+ conversationId: string;
184
+ answerId1: string;
185
+ answerId2: string;
186
+ answerId3: string;
187
+ conversationToken: string;
188
+ citations: Array<AgentCitation>;
189
+ };
190
+
191
+ const agentData: AgentData = {
192
+ answerStreams: [
193
+ runStartStream(ANSWER_ID_1),
194
+ customInitialHeadersStream(true),
195
+ stepStartedStream('Searching'),
196
+ toolCallStartStream('search'),
197
+ searchQueryToolCallArgsStream('test'),
198
+ toolCallEndStream('search'),
199
+ stepFinishedStream('Searching'),
200
+ stepStartedStream('Answering'),
201
+ textMessageStream('### Testing in Coveo\n\nclick **Edit components**.\n - On the **A/B test** tab, click **Configure A/B test**.\n - Set the traffic ratio and configure the **Test scenario**.\n - Click **Start** to activate the A/B test.\n\n2. **Edit an A/B Test**\n - Follow the same steps as creating an A/B test.\n - Click **Edit A/B test** and adjust the traffic ratio.'),
202
+ citationStream,
203
+ stepFinishedStream('Answering'),
204
+ runFinishedStream(ANSWER_ID_1),
205
+ ],
206
+ followUpStreams: [
207
+ [
208
+ runStartStream(ANSWER_ID_2),
209
+ customInitialHeadersStream(true),
210
+ stepStartedStream('Thinking'),
211
+ stepFinishedStream('Thinking'),
212
+ stepStartedStream('Searching'),
213
+ toolCallStartStream('search'),
214
+ searchQueryToolCallArgsStream('follow-up question'),
215
+ toolCallEndStream('search'),
216
+ stepFinishedStream('Searching'),
217
+ stepStartedStream('Answering'),
218
+ textMessageStream('This is a follow-up answer.'),
219
+ citationStream,
220
+ stepFinishedStream('Answering'),
221
+ runFinishedStream(ANSWER_ID_2),
222
+ ],
223
+ [
224
+ runStartStream(ANSWER_ID_3),
225
+ customInitialHeadersStream(true),
226
+ stepStartedStream('Thinking'),
227
+ stepFinishedStream('Thinking'),
228
+ stepStartedStream('Searching'),
229
+ toolCallStartStream('search'),
230
+ searchQueryToolCallArgsStream('third question'),
231
+ toolCallEndStream('search'),
232
+ stepFinishedStream('Searching'),
233
+ stepStartedStream('Answering'),
234
+ textMessageStream('This is the third answer.'),
235
+ citationStream,
236
+ stepFinishedStream('Answering'),
237
+ runFinishedStream(ANSWER_ID_3),
238
+ ],
239
+ ],
240
+ conversationId: STREAM_ID,
241
+ answerId1: ANSWER_ID_1,
242
+ answerId2: ANSWER_ID_2,
243
+ answerId3: ANSWER_ID_3,
244
+ conversationToken: CONVERSATION_TOKEN,
245
+ citations: [exampleCitation],
246
+ };
247
+
248
+ export default agentData;
@@ -0,0 +1,77 @@
1
+ import {quanticBase} from '../../../../../../playwright/fixtures/baseFixture';
2
+ import {SearchObject} from '../../../../../../playwright/page-object/searchObject';
3
+ import {
4
+ searchRequestRegex,
5
+ agentAnswerRequestRegex,
6
+ agentFollowUpRequestRegex,
7
+ } from '../../../../../../playwright/utils/requests';
8
+ import {GeneratedAnswerObject} from './pageObject';
9
+ import agentData from './agentData';
10
+ import type {AgentData} from './agentData';
11
+ import {AnalyticsModeEnum} from '../../../../../../playwright/utils/analyticsMode';
12
+
13
+ const pageUrl = 's/quantic-generated-answer';
14
+
15
+ interface AgentOptions {
16
+ agentId: string;
17
+ withToggle: boolean;
18
+ }
19
+
20
+ type QuanticGeneratedAnswerAgentE2EFixtures = {
21
+ agentData: AgentData;
22
+ generatedAnswer: GeneratedAnswerObject;
23
+ search: SearchObject;
24
+ options: Partial<AgentOptions>;
25
+ };
26
+
27
+ export const exampleQuery = 'test';
28
+
29
+ export const testAgent =
30
+ quanticBase.extend<QuanticGeneratedAnswerAgentE2EFixtures>({
31
+ pageUrl,
32
+ agentData,
33
+ options: {},
34
+ analyticsMode: AnalyticsModeEnum.legacy,
35
+ search: async ({page}, use) => {
36
+ await use(new SearchObject(page, searchRequestRegex));
37
+ },
38
+ generatedAnswer: async (
39
+ {page, options, configuration, search, agentData: data, analytics},
40
+ use
41
+ ) => {
42
+ const generatedAnswerObject = new GeneratedAnswerObject(
43
+ page,
44
+ data.answerId1,
45
+ analytics,
46
+ {
47
+ headAnswerRequestRegex: agentAnswerRequestRegex,
48
+ agentFollowUpRequestRegex,
49
+ }
50
+ );
51
+
52
+ await generatedAnswerObject.mockAgentAnswerResponse(
53
+ data.answerStreams
54
+ );
55
+ await generatedAnswerObject.mockAgentFollowUpResponse(
56
+ data.followUpStreams
57
+ );
58
+
59
+ await search.mockSearchWithBaseResponse();
60
+ await page.goto(pageUrl);
61
+ await configuration.configure(options);
62
+ await search.waitForSearchResponse();
63
+
64
+ generatedAnswerObject.streamEndAnalyticRequestPromise =
65
+ generatedAnswerObject.waitForStreamEndAnalytics();
66
+ generatedAnswerObject.generateRequestPromise =
67
+ generatedAnswerObject.waitForHeadAnswerRequest();
68
+
69
+ await search.fillSearchInput(exampleQuery);
70
+ search.performSearch();
71
+ await search.waitForSearchResponse();
72
+
73
+ await use(generatedAnswerObject);
74
+ },
75
+ });
76
+
77
+ export {expect} from '@playwright/test';