Skip to content

Commit 1cc5aee

Browse files
authored
feat(headless): change citation from custom to click event (#4492)
[SVCC-4170](https://coveord.atlassian.net/browse/SVCC-4170) Change Citation Click from Custom Event to Click Event: - Bump `coveo.analytics` to `2.30.39`. - Use the new `logGeneratedAnswerCitationClick` event. - This new method make the click event assume a `document position` of 1 for the purpose of the click event and click rank. - Changes are made both in the search page client as well as the insight client. Future click event example: ``` { "language": "en", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "collectionName": "default", -- Deprecated "documentAuthor": "unknown", -- Available in stream response? "documentPosition": 1, -- Hardcoded to 1 "documentTitle": "Relevance Generative Answering", -- In stream API response "documentUri": "https://levelup.coveo.com/learn/courses/relevance-generative-answering", -- In stream API response as URI "documentUriHash": "bK2L18lxLvsUB5h4", - Depecreated "documentUrl": "https://levelup.coveo.com/learn/courses/relevance-generative-answering", -- In stream API response as Click URI "sourceName": "Level Up", -- Needs to be added stream API response "queryPipeline": "Customer Zero Community search - RGA Update V3", "originContext": "buildersearchpage", -- Same as custom event "originLevel1": "Connect RGA V3 Testing", -- Same as custom event "originLevel2": "default", -- Same as custom event "originLevel3": "https://search.cloud.coveo.com/builder/", -- Same as custom event "customData": { "coveoHeadlessVersion": "2.78.0", -- Inherited "generativeQuestionAnsweringId": "queryStream01_coveosearch_1ffa0550-3d4b-4d09-9574-8c4225b1edf9", --TO ADD"contentIDKey": "permanentid", --TO ADD"contentIDValue": "29bd12f1-12fb-4ac4-9463-66e37a0cb686", "coveoAtomicVersion": "2.77.1" -- Inherited --From Stream API"citationId": "42.20035$https://levelup.coveo.com/learn/courses/relevance-generative-answering-3541ae00-8d76-4b85-aa02-75478f4b6a6e", }, "facetState": [], "anonymous": false, "clientId": "d4a6b215-241a-40f2-ad82-4890895843ad", "actionCause": "openGeneratedAnswerSource", -- New actionCause "searchQueryUid": "6549c762-4b4d-466d-ba16-bfe3b8d21928" -- From search event } ``` [SVCC-4170]: https://coveord.atlassian.net/browse/SVCC-4170?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 6d17ed7 commit 1cc5aee

15 files changed

+149
-49
lines changed

Diff for: package-lock.json

+5-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: packages/atomic/cypress/e2e/generated-answer-assertions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {should} from './common-assertions';
22
import {GeneratedAnswerSelectors} from './generated-answer-selectors';
33

44
export function assertLogOpenGeneratedAnswerSource() {
5-
cy.expectCustomEvent('generatedAnswer', 'openGeneratedAnswerSource');
5+
cy.expectClickEvent('generatedAnswerCitationClick');
66
}
77

88
export function assertLogGeneratedAnswerSourceHover() {

Diff for: packages/headless/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
"@microsoft/fetch-event-source": "2.0.1",
152152
"@reduxjs/toolkit": "2.2.7",
153153
"abortcontroller-polyfill": "1.7.5",
154-
"coveo.analytics": "2.30.38",
154+
"coveo.analytics": "2.30.39",
155155
"dayjs": "1.11.12",
156156
"exponential-backoff": "3.1.0",
157157
"fast-equals": "5.0.1",

Diff for: packages/headless/src/api/generated-answer/generated-answer-event-payload.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface GeneratedAnswerCitation {
1414
title: string;
1515
uri: string;
1616
permanentid: string;
17+
source: string;
1718
clickUri?: string;
1819
text?: string;
1920
fields?: Raw;

Diff for: packages/headless/src/features/analytics/analytics-utils.ts

+33
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
SearchAnalyticsProvider,
4545
StateNeededBySearchAnalyticsProvider,
4646
} from '../../api/analytics/search-analytics.js';
47+
import {GeneratedAnswerCitation} from '../../api/generated-answer/generated-answer-event-payload.js';
4748
import {PreprocessRequest} from '../../api/preprocess-request.js';
4849
import {Raw} from '../../api/search/search/raw.js';
4950
import {Result} from '../../api/search/search/result.js';
@@ -623,6 +624,38 @@ export const partialRecommendationInformation = (
623624

624625
return buildPartialDocumentInformation(result, resultIndex, state);
625626
};
627+
628+
export const partialCitationInformation = (
629+
citation: GeneratedAnswerCitation,
630+
state: Partial<SearchAppState>
631+
): PartialDocumentInformation => {
632+
return {
633+
sourceName: getCitationSourceName(citation),
634+
documentPosition: 1,
635+
documentTitle: citation.title,
636+
documentUri: citation.uri,
637+
documentUrl: citation.clickUri,
638+
queryPipeline: state.pipeline || getPipelineInitialState(),
639+
};
640+
};
641+
642+
function getCitationSourceName(citation: GeneratedAnswerCitation) {
643+
const source = citation.source;
644+
if (isNullOrUndefined(source)) {
645+
return 'unknown';
646+
}
647+
return source;
648+
}
649+
650+
export const citationDocumentIdentifier = (
651+
citation: GeneratedAnswerCitation
652+
) => {
653+
return {
654+
contentIdKey: 'permanentid',
655+
contentIdValue: citation.permanentid || '',
656+
};
657+
};
658+
626659
function buildPartialDocumentInformation(
627660
result: Result,
628661
resultIndex: number,

Diff for: packages/headless/src/features/generated-answer/__snapshots__/generated-answer-analytics-actions.test.ts.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ exports[`generated answer analytics actions > when analyticsMode is \`next\` > s
8888
"title": "Sample citation",
8989
"uniqueFieldName": "permanentid",
9090
"uniqueFieldValue": "citation-permanent-id",
91-
"url": undefined,
91+
"url": "http://localhost/citations/some-citation-id",
9292
},
9393
"responseId": "123",
9494
},
@@ -114,7 +114,7 @@ exports[`generated answer analytics actions > when analyticsMode is \`next\` > s
114114
"title": "Sample citation",
115115
"uniqueFieldName": "permanentid",
116116
"uniqueFieldValue": "citation-permanent-id",
117-
"url": undefined,
117+
"url": "http://localhost/citations/some-citation-id",
118118
},
119119
"responseId": "123",
120120
},

Diff for: packages/headless/src/features/generated-answer/__snapshots__/generated-answer-insight-analytics-actions.test.ts.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ exports[`generated answer insight analytics actions > when analyticsMode is \`ne
8888
"title": "example title",
8989
"uniqueFieldName": "permanentid",
9090
"uniqueFieldValue": "citation_permanentid",
91-
"url": undefined,
91+
"url": "example: click uri",
9292
},
9393
"responseId": "123",
9494
},
@@ -114,7 +114,7 @@ exports[`generated answer insight analytics actions > when analyticsMode is \`ne
114114
"title": "example title",
115115
"uniqueFieldName": "permanentid",
116116
"uniqueFieldValue": "citation_permanentid",
117-
"url": undefined,
117+
"url": "example: click uri",
118118
},
119119
"responseId": "123",
120120
},

Diff for: packages/headless/src/features/generated-answer/generated-answer-analytics-actions.test.ts

+27-5
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const mockMakeGeneratedAnswerFeedbackSubmit = vi.fn(() => ({
3333
const mockMakeRetryGeneratedAnswer = vi.fn(() => ({
3434
log: mockLogFunction,
3535
}));
36-
const mockMakeOpenGeneratedAnswerSource = vi.fn(() => ({
36+
const mockMakeGeneratedAnswerCitationClick = vi.fn((..._args) => ({
3737
log: mockLogFunction,
3838
}));
3939
const mockMakeGeneratedAnswerSourceHover = vi.fn(() => ({
@@ -84,7 +84,7 @@ vi.mock('coveo.analytics', () => {
8484
disable: vi.fn(),
8585
makeGeneratedAnswerFeedbackSubmit: mockMakeGeneratedAnswerFeedbackSubmit,
8686
makeRetryGeneratedAnswer: mockMakeRetryGeneratedAnswer,
87-
makeOpenGeneratedAnswerSource: mockMakeOpenGeneratedAnswerSource,
87+
makeGeneratedAnswerCitationClick: mockMakeGeneratedAnswerCitationClick,
8888
makeGeneratedAnswerSourceHover: mockMakeGeneratedAnswerSourceHover,
8989
makeLikeGeneratedAnswer: mockMakeLikeGeneratedAnswer,
9090
makeDislikeGeneratedAnswer: mockMakeDislikeGeneratedAnswer,
@@ -119,6 +119,22 @@ const exampleCitation: GeneratedAnswerCitation = {
119119
permanentid: 'citation-permanent-id',
120120
title: 'Sample citation',
121121
uri: 'http://localhost/citations/some-citation-id',
122+
source: 'source-name',
123+
clickUri: 'http://localhost/citations/some-citation-id',
124+
};
125+
126+
const expectedCitationDocumentInfo = {
127+
queryPipeline: '',
128+
documentUri: exampleCitation.uri,
129+
sourceName: exampleCitation.source,
130+
documentPosition: 1,
131+
documentTitle: exampleCitation.title,
132+
documentUrl: exampleCitation.clickUri,
133+
};
134+
135+
const exampleDocumentId = {
136+
contentIdKey: 'permanentid',
137+
contentIdValue: exampleCitation.permanentid,
122138
};
123139

124140
describe('generated answer analytics actions', () => {
@@ -177,14 +193,20 @@ describe('generated answer analytics actions', () => {
177193
{} as ThunkExtraArguments
178194
);
179195

180-
const mockToUse = mockMakeOpenGeneratedAnswerSource;
196+
const mockToUse = mockMakeGeneratedAnswerCitationClick;
181197

182198
expect(mockToUse).toHaveBeenCalledTimes(1);
183-
expect(mockToUse).toHaveBeenCalledWith({
199+
200+
expect(mockToUse.mock.calls[0][0]).toStrictEqual(
201+
expectedCitationDocumentInfo
202+
);
203+
204+
expect(mockToUse.mock.calls[0][1]).toStrictEqual({
184205
generativeQuestionAnsweringId: exampleGenerativeQuestionAnsweringId,
185206
citationId: exampleCitation.id,
186-
permanentId: exampleCitation.permanentid,
207+
documentId: exampleDocumentId,
187208
});
209+
188210
expect(mockLogFunction).toHaveBeenCalledTimes(1);
189211
});
190212

Diff for: packages/headless/src/features/generated-answer/generated-answer-analytics-actions.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {Rga} from '@coveo/relay-event-types';
22
import {
3+
citationDocumentIdentifier,
34
CustomAction,
45
LegacySearchAction,
56
makeAnalyticsAction,
7+
partialCitationInformation,
68
} from '../analytics/analytics-utils.js';
79
import {SearchPageEvents} from '../analytics/search-action-cause.js';
810
import {SearchAction} from '../search/search-actions.js';
@@ -54,13 +56,16 @@ export const logOpenGeneratedAnswerSource = (
5456
return null;
5557
}
5658

57-
return client.makeOpenGeneratedAnswerSource({
58-
...(answerAPIEnabled
59-
? {answerAPIStreamId: rgaID}
60-
: {generativeQuestionAnsweringId: rgaID}),
61-
permanentId: citation.permanentid,
62-
citationId: citation.id,
63-
});
59+
return client.makeGeneratedAnswerCitationClick(
60+
partialCitationInformation(citation, state),
61+
{
62+
...(answerAPIEnabled
63+
? {answerAPIStreamId: rgaID}
64+
: {generativeQuestionAnsweringId: rgaID}),
65+
citationId: citation.id,
66+
documentId: citationDocumentIdentifier(citation),
67+
}
68+
);
6469
},
6570
analyticsType: 'Rga.CitationClick',
6671
analyticsPayloadBuilder: (state): Rga.CitationClick | undefined => {

Diff for: packages/headless/src/features/generated-answer/generated-answer-insight-analytics-actions.test.ts

+33-9
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {getGeneratedAnswerInitialState} from './generated-answer-state.js';
2727

2828
const mockLogGeneratedAnswerFeedbackSubmit = vi.fn();
2929
const mockLogRetryGeneratedAnswer = vi.fn();
30-
const mockLogOpenGeneratedAnswerSource = vi.fn();
30+
const mockLogGeneratedAnswerCitationClick = vi.fn();
3131
const mockLogHoverCitation = vi.fn();
3232
const mockLogLikeGeneratedAnswer = vi.fn();
3333
const mockLogDislikeGeneratedAnswer = vi.fn();
@@ -56,7 +56,7 @@ vi.mock('coveo.analytics', () => {
5656
disable: vi.fn(),
5757
logGeneratedAnswerFeedbackSubmit: mockLogGeneratedAnswerFeedbackSubmit,
5858
logRetryGeneratedAnswer: mockLogRetryGeneratedAnswer,
59-
logOpenGeneratedAnswerSource: mockLogOpenGeneratedAnswerSource,
59+
logGeneratedAnswerCitationClick: mockLogGeneratedAnswerCitationClick,
6060
logGeneratedAnswerSourceHover: mockLogHoverCitation,
6161
logLikeGeneratedAnswer: mockLogLikeGeneratedAnswer,
6262
logDislikeGeneratedAnswer: mockLogDislikeGeneratedAnswer,
@@ -90,6 +90,10 @@ const exampleSubject = 'example subject';
9090
const exampleDescription = 'example description';
9191
const exampleCaseId = '1234';
9292
const exampleCaseNumber = '5678';
93+
const exampleCitationTitle = 'example title';
94+
const exampleCitationUri = 'example: uri';
95+
const exampleCitationSource = 'example source name';
96+
const exampleCitationClickUri = 'example: click uri';
9397

9498
const expectedCaseContext = {
9599
caseContext: {
@@ -100,6 +104,20 @@ const expectedCaseContext = {
100104
caseNumber: exampleCaseNumber,
101105
};
102106

107+
const expectedCitationDocumentInfo = {
108+
queryPipeline: '',
109+
documentUri: exampleCitationUri,
110+
sourceName: exampleCitationSource,
111+
documentPosition: 1,
112+
documentTitle: exampleCitationTitle,
113+
documentUrl: exampleCitationClickUri,
114+
};
115+
116+
const exampleDocumentId = {
117+
contentIdKey: 'permanentid',
118+
contentIdValue: exampleCitationPermanentid,
119+
};
120+
103121
describe('generated answer insight analytics actions', () => {
104122
let engine: MockedInsightEngine;
105123
const searchState = buildMockSearchState({
@@ -116,8 +134,10 @@ describe('generated answer insight analytics actions', () => {
116134
{
117135
id: exampleCitationId,
118136
permanentid: exampleCitationPermanentid,
119-
title: 'example title',
120-
uri: 'example: uri',
137+
title: exampleCitationTitle,
138+
uri: exampleCitationUri,
139+
source: exampleCitationSource,
140+
clickUri: exampleCitationClickUri,
121141
},
122142
],
123143
};
@@ -172,18 +192,22 @@ describe('generated answer insight analytics actions', () => {
172192
{} as ThunkExtraArguments
173193
);
174194

175-
const mockToUse = mockLogOpenGeneratedAnswerSource;
195+
const mockToUse = mockLogGeneratedAnswerCitationClick;
176196
const expectedMetadata = {
177197
generativeQuestionAnsweringId: exampleGenerativeQuestionAnsweringId,
178-
permanentId: exampleCitationPermanentid,
179198
citationId: exampleCitationId,
199+
documentId: exampleDocumentId,
180200
};
181201

182202
expect(mockToUse).toHaveBeenCalledTimes(1);
183-
expect(mockToUse).toHaveBeenCalledWith(
184-
expectedMetadata,
185-
expectedCaseContext
203+
204+
expect(mockToUse.mock.calls[0][0]).toStrictEqual(
205+
expectedCitationDocumentInfo
186206
);
207+
208+
expect(mockToUse.mock.calls[0][1]).toStrictEqual(expectedMetadata);
209+
210+
expect(mockToUse.mock.calls[0][2]).toStrictEqual(expectedCaseContext);
187211
});
188212

189213
it('should log #logHoverCitation with the right payload', async () => {

Diff for: packages/headless/src/features/generated-answer/generated-answer-insight-analytics-actions.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {Rga} from '@coveo/relay-event-types';
22
import {
3+
citationDocumentIdentifier,
34
InsightAction,
45
makeInsightAnalyticsActionFactory,
6+
partialCitationInformation,
57
} from '../analytics/analytics-utils.js';
68
import {SearchPageEvents} from '../analytics/search-action-cause.js';
79
import {getCaseContextAnalyticsMetadata} from '../case-context/case-context-state.js';
@@ -38,13 +40,14 @@ export const logOpenGeneratedAnswerSource = (
3840
if (!rgaID || !citation) {
3941
return null;
4042
}
41-
return client.logOpenGeneratedAnswerSource(
43+
return client.logGeneratedAnswerCitationClick(
44+
partialCitationInformation(citation, state),
4245
{
4346
...(answerAPIEnabled
4447
? {answerAPIStreamId: rgaID}
4548
: {generativeQuestionAnsweringId: rgaID}),
46-
permanentId: citation.permanentid,
4749
citationId: citation.id,
50+
documentId: citationDocumentIdentifier(citation),
4851
},
4952
getCaseContextAnalyticsMetadata(state.insightCaseContext)
5053
);

Diff for: packages/headless/src/test/mock-citation.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function buildMockCitation(
1616
uri: '',
1717
permanentid: '',
1818
clickUri: '',
19+
source: '',
1920
text: '',
2021
...config,
2122
};

0 commit comments

Comments
 (0)