@constructor-io/constructorio-ui-pia 2.11.0 → 2.13.0
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.
- package/dist/constructorio-ui-pia-bundled.js +62 -62
- package/lib/cjs/components/CioPia/CioPiaQna.js +10 -4
- package/lib/cjs/components/CioPia/CioPiaStyles.css +5 -0
- package/lib/cjs/components/CioPia/PiaCustomCarousel.js +34 -29
- package/lib/cjs/components/ConversationHistory/ConversationHistory.js +10 -5
- package/lib/cjs/components/Error/ErrorBlock.js +7 -5
- package/lib/cjs/components/Feedback/Feedback.js +4 -4
- package/lib/cjs/components/Input/Input.js +2 -1
- package/lib/cjs/components/LoadingSkeleton/LoadingSkeleton.js +2 -1
- package/lib/cjs/components/PiaConversation/PiaModal.js +22 -4
- package/lib/cjs/components/StatusRegion/StatusRegion.js +34 -0
- package/lib/cjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +1 -1
- package/lib/cjs/components/icons/QuestionIcon.js +1 -1
- package/lib/cjs/components/icons/SendIcon.js +1 -1
- package/lib/cjs/components/icons/SparklesIcon.js +1 -1
- package/lib/cjs/hooks/useAnswerResults.js +4 -14
- package/lib/cjs/hooks/useCioPia.js +42 -4
- package/lib/cjs/hooks/useConversation.js +10 -8
- package/lib/cjs/hooks/usePiaClient.js +2 -7
- package/lib/cjs/hooks/useRecsPod.js +10 -5
- package/lib/cjs/hooks/useSuggestedQuestions.js +10 -26
- package/lib/cjs/types.js +0 -2
- package/lib/cjs/utils/translate.js +12 -0
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/CioPia/CioPiaQna.js +10 -4
- package/lib/mjs/components/CioPia/CioPiaStyles.css +5 -0
- package/lib/mjs/components/CioPia/PiaCustomCarousel.js +53 -30
- package/lib/mjs/components/ConversationHistory/ConversationHistory.js +10 -5
- package/lib/mjs/components/Error/ErrorBlock.js +7 -5
- package/lib/mjs/components/Feedback/Feedback.js +4 -4
- package/lib/mjs/components/Input/Input.js +2 -1
- package/lib/mjs/components/LoadingSkeleton/LoadingSkeleton.js +2 -1
- package/lib/mjs/components/PiaConversation/PiaModal.js +22 -4
- package/lib/mjs/components/StatusRegion/StatusRegion.js +28 -0
- package/lib/mjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +1 -1
- package/lib/mjs/components/icons/QuestionIcon.js +1 -1
- package/lib/mjs/components/icons/SendIcon.js +1 -1
- package/lib/mjs/components/icons/SparklesIcon.js +1 -1
- package/lib/mjs/hooks/useAnswerResults.js +4 -13
- package/lib/mjs/hooks/useCioPia.js +43 -4
- package/lib/mjs/hooks/useConversation.js +7 -5
- package/lib/mjs/hooks/usePiaClient.js +2 -7
- package/lib/mjs/hooks/useRecsPod.js +9 -5
- package/lib/mjs/hooks/useSuggestedQuestions.js +12 -24
- package/lib/mjs/types.js +0 -1
- package/lib/mjs/utils/translate.js +12 -0
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/CioPia/types.d.ts +20 -5
- package/lib/types/components/ConversationHistory/ConversationHistory.d.ts +7 -1
- package/lib/types/components/Error/ErrorBlock.d.ts +9 -1
- package/lib/types/components/StatusRegion/StatusRegion.d.ts +13 -0
- package/lib/types/hooks/useAnswerResults.d.ts +4 -4
- package/lib/types/hooks/useCioPia.d.ts +20 -5
- package/lib/types/hooks/usePiaClient.d.ts +4 -9
- package/lib/types/hooks/useRecsPod.d.ts +2 -2
- package/lib/types/hooks/useSuggestedQuestions.d.ts +3 -4
- package/lib/types/types.d.ts +53 -6
- package/lib/types/utils/translate.d.ts +3 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +3 -3
- package/lib/cjs/hooks/mocks/MockConstructorIOClient.js +0 -24
- package/lib/cjs/hooks/mocks/agent.js +0 -183
- package/lib/cjs/hooks/mocks/types.js +0 -2
- package/lib/mjs/hooks/mocks/MockConstructorIOClient.js +0 -38
- package/lib/mjs/hooks/mocks/agent.js +0 -173
- package/lib/mjs/hooks/mocks/types.js +0 -1
- package/lib/types/hooks/mocks/MockConstructorIOClient.d.ts +0 -14
- package/lib/types/hooks/mocks/agent.d.ts +0 -23
- package/lib/types/hooks/mocks/types.d.ts +0 -77
package/lib/types/types.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { Product, CarouselOverrides, ComponentOverrideProps } from '@constructor-io/constructorio-ui-components';
|
|
3
|
-
import { ConstructorClientOptions, Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
4
|
-
import {
|
|
5
|
-
import MockConstructorIOClient from './hooks/mocks/MockConstructorIOClient';
|
|
3
|
+
import { ConstructorClientOptions, FilterExpression, ItemData, Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
4
|
+
import type { CioClient } from './hooks/usePiaClient';
|
|
6
5
|
export declare enum FeedbackType {
|
|
7
6
|
UP = "up",
|
|
8
7
|
DOWN = "down"
|
|
9
8
|
}
|
|
10
9
|
export interface PiaContextValue {
|
|
11
|
-
cioClient: Nullable<
|
|
10
|
+
cioClient: Nullable<CioClient>;
|
|
12
11
|
cioClientOptions: CioClientOptions;
|
|
13
12
|
setCioClientOptions: React.Dispatch<CioClientOptions>;
|
|
14
13
|
itemId: string;
|
|
@@ -23,7 +22,7 @@ export interface CioPiaProviderProps {
|
|
|
23
22
|
variationId?: string;
|
|
24
23
|
/** Thread ID for conversation context. Must be a valid UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") */
|
|
25
24
|
threadId?: string;
|
|
26
|
-
cioClient?: Nullable<
|
|
25
|
+
cioClient?: Nullable<CioClient>;
|
|
27
26
|
}
|
|
28
27
|
export type CioPiaMode = 'default' | 'conversation' | 'recommendations';
|
|
29
28
|
export type CioPiaType = 'inline' | 'modal';
|
|
@@ -61,12 +60,22 @@ export type CioPiaDisplayConfigs = {
|
|
|
61
60
|
export type Translations = {
|
|
62
61
|
'Any questions about this product?'?: string;
|
|
63
62
|
'Ask anything'?: string;
|
|
63
|
+
/** Accessible name of the question field when its placeholder is blanked out. */
|
|
64
|
+
'Your question'?: string;
|
|
64
65
|
Send?: string;
|
|
65
66
|
'AI-generated answers aim to help, but they may occasionally miss details or be inaccurate. Double-check important information before purchasing.'?: string;
|
|
66
67
|
'Is this answer useful?'?: string;
|
|
67
68
|
'Learn More.'?: string;
|
|
68
69
|
'Ask about this product'?: string;
|
|
69
70
|
'Add to Cart'?: string;
|
|
71
|
+
'Unexpected error'?: string;
|
|
72
|
+
'thumbs up'?: string;
|
|
73
|
+
'thumbs down'?: string;
|
|
74
|
+
Close?: string;
|
|
75
|
+
Retry?: string;
|
|
76
|
+
'Conversation history'?: string;
|
|
77
|
+
'Loading answer'?: string;
|
|
78
|
+
'Answer ready'?: string;
|
|
70
79
|
/** Recommendations pod title shown while a request is in flight. */
|
|
71
80
|
'Adapting recommendations to your preference'?: string;
|
|
72
81
|
/** Recommendations pod title shown when a request fails or comes back degraded. */
|
|
@@ -244,4 +253,42 @@ export interface CioPiaComponentOverrides extends ComponentOverrideProps<CioPiaR
|
|
|
244
253
|
feedback?: ComponentOverrideProps<FeedbackRenderProps>;
|
|
245
254
|
loading?: ComponentOverrideProps<LoadingRenderProps>;
|
|
246
255
|
}
|
|
247
|
-
export
|
|
256
|
+
export interface Question {
|
|
257
|
+
value: string;
|
|
258
|
+
}
|
|
259
|
+
export interface QuestionResponse {
|
|
260
|
+
questions: Array<Question>;
|
|
261
|
+
}
|
|
262
|
+
export interface SuggestedQuestionsParameters {
|
|
263
|
+
numResults?: number;
|
|
264
|
+
preFilterExpression?: FilterExpression;
|
|
265
|
+
}
|
|
266
|
+
export interface AnswerRequestParameters {
|
|
267
|
+
preFilterExpression?: FilterExpression;
|
|
268
|
+
guard?: boolean;
|
|
269
|
+
fmtOptions?: Record<string, any>;
|
|
270
|
+
}
|
|
271
|
+
export interface ApiItemVariation extends Record<string, any> {
|
|
272
|
+
value: string;
|
|
273
|
+
data?: ItemData;
|
|
274
|
+
}
|
|
275
|
+
export interface ApiItem extends Record<string, any> {
|
|
276
|
+
value: string;
|
|
277
|
+
matched_terms: Array<string>;
|
|
278
|
+
data: ItemData;
|
|
279
|
+
variations?: Array<ApiItemVariation> | null;
|
|
280
|
+
variations_map?: Record<string, any> | Array<Record<string, any>> | null;
|
|
281
|
+
}
|
|
282
|
+
export interface AnswerItemResults {
|
|
283
|
+
request?: Record<string, any>;
|
|
284
|
+
response: {
|
|
285
|
+
results: Array<ApiItem>;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
export interface GetAnswerResultsResponse {
|
|
289
|
+
qna_result_id: string;
|
|
290
|
+
value: string;
|
|
291
|
+
item_results?: AnswerItemResults;
|
|
292
|
+
follow_up_questions?: Array<Question>;
|
|
293
|
+
thread_id?: string;
|
|
294
|
+
}
|
|
@@ -3,6 +3,9 @@ import { Translations } from '../types';
|
|
|
3
3
|
* Translates a word using the provided translations object.
|
|
4
4
|
* Falls back to English defaults if translation is not provided.
|
|
5
5
|
*
|
|
6
|
+
* An explicitly empty string is returned as-is: blanking out a string is how
|
|
7
|
+
* consumers hide it.
|
|
8
|
+
*
|
|
6
9
|
* @param word - The key to translate
|
|
7
10
|
* @param translations - Optional user-provided translations object
|
|
8
11
|
* @returns The translated string or the original word if no translation exists
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "2.
|
|
1
|
+
declare const _default: "2.13.0";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructor-io/constructorio-ui-pia",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "Constructor.io AI Product Insights Agent UI library for web applications",
|
|
5
5
|
"author": "Constructor.io Corporation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,14 +70,13 @@
|
|
|
70
70
|
"prepare": "husky"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@constructor-io/constructorio-client-javascript": "^2.
|
|
73
|
+
"@constructor-io/constructorio-client-javascript": "^2.93.0",
|
|
74
74
|
"@constructor-io/constructorio-ui-components": "^1.5.0",
|
|
75
75
|
"react": ">=16.12.0",
|
|
76
76
|
"react-dom": ">=16.12.0",
|
|
77
77
|
"tslib": "^2.4.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@axe-core/react": "^4.10.1",
|
|
81
80
|
"@babel/core": "^7.24.0",
|
|
82
81
|
"@babel/preset-env": "^7.24.0",
|
|
83
82
|
"@babel/preset-react": "^7.23.3",
|
|
@@ -104,6 +103,7 @@
|
|
|
104
103
|
"@types/jest": "^30.0.0",
|
|
105
104
|
"@types/react": "^19.0.10",
|
|
106
105
|
"@types/react-dom": "^19.0.4",
|
|
106
|
+
"axe-playwright": "^2.2.2",
|
|
107
107
|
"chromatic": "^11.27.0",
|
|
108
108
|
"eslint": "^9.39.4",
|
|
109
109
|
"eslint-config-prettier": "^10.1.1",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const constructorio_client_javascript_1 = tslib_1.__importDefault(require("@constructor-io/constructorio-client-javascript"));
|
|
5
|
-
const agent_1 = tslib_1.__importDefault(require("./agent"));
|
|
6
|
-
const version_1 = tslib_1.__importDefault(require("../../version"));
|
|
7
|
-
class MockConstructorIOClient {
|
|
8
|
-
constructor(options) {
|
|
9
|
-
const isServer = typeof window === 'undefined' || typeof document === 'undefined';
|
|
10
|
-
const clientOptions = Object.assign(Object.assign(Object.assign({ version: options.version || `cio-ui-pia-${version_1.default}`, serviceUrl: options.serviceUrl || 'https://ac.cnstrc.com', quizzesServiceUrl: options.quizzesServiceUrl || 'https://quizzes.cnstrc.com', agentServiceUrl: options.agentServiceUrl || 'https://agent.cnstrc.com' }, (isServer && {
|
|
11
|
-
clientId: options.clientId || 'this-is-a-random-client-id',
|
|
12
|
-
})), { sendTrackingEvents: options.sendTrackingEvents !== undefined ? options.sendTrackingEvents : true, beaconMode: options.beaconMode !== undefined ? options.beaconMode : true, networkParameters: options.networkParameters || {} }), options);
|
|
13
|
-
const cioClient = new constructorio_client_javascript_1.default(clientOptions);
|
|
14
|
-
const { clientId, sessionId } = cioClient.agent.options;
|
|
15
|
-
this.options = Object.assign(Object.assign({}, clientOptions), { clientId, sessionId });
|
|
16
|
-
this.search = cioClient.search;
|
|
17
|
-
this.browse = cioClient.browse;
|
|
18
|
-
this.recommendations = cioClient.recommendations;
|
|
19
|
-
this.tracker = cioClient.tracker;
|
|
20
|
-
this.quizzes = cioClient.quizzes;
|
|
21
|
-
this.agent = new agent_1.default(this.options);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
exports.default = MockConstructorIOClient;
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const errors_1 = require("../../errors");
|
|
5
|
-
// Create URL for PIA API
|
|
6
|
-
function createAgentUrl({ itemId, threadId, variationId, question, isStreaming = false, options, parameters = {}, }) {
|
|
7
|
-
const { apiKey, agentServiceUrl, clientId, sessionId, userId, segments, version } = options;
|
|
8
|
-
if (!agentServiceUrl)
|
|
9
|
-
throw new Error('Agent service URL is required');
|
|
10
|
-
let baseUrl = `${agentServiceUrl}/v1/item_questions`;
|
|
11
|
-
if (question) {
|
|
12
|
-
baseUrl += `/${encodeURIComponent(question)}/answer`;
|
|
13
|
-
}
|
|
14
|
-
if (isStreaming) {
|
|
15
|
-
baseUrl += '/streaming';
|
|
16
|
-
}
|
|
17
|
-
const url = new URL(baseUrl);
|
|
18
|
-
url.searchParams.append('item_id', itemId);
|
|
19
|
-
url.searchParams.append('key', apiKey);
|
|
20
|
-
if (threadId) {
|
|
21
|
-
url.searchParams.append('thread_id', threadId);
|
|
22
|
-
}
|
|
23
|
-
if (variationId) {
|
|
24
|
-
url.searchParams.append('variation_id', variationId);
|
|
25
|
-
}
|
|
26
|
-
if (clientId) {
|
|
27
|
-
url.searchParams.append('i', clientId);
|
|
28
|
-
}
|
|
29
|
-
if (sessionId != null) {
|
|
30
|
-
url.searchParams.append('s', String(sessionId));
|
|
31
|
-
}
|
|
32
|
-
if (userId) {
|
|
33
|
-
url.searchParams.append('ui', userId);
|
|
34
|
-
}
|
|
35
|
-
if (version) {
|
|
36
|
-
url.searchParams.append('c', version);
|
|
37
|
-
}
|
|
38
|
-
if (segments === null || segments === void 0 ? void 0 : segments.length) {
|
|
39
|
-
segments.forEach((segment) => url.searchParams.append('us', segment));
|
|
40
|
-
}
|
|
41
|
-
// Any additional parameters
|
|
42
|
-
Object.entries(parameters).forEach(([key, value]) => {
|
|
43
|
-
if (value !== undefined) {
|
|
44
|
-
url.searchParams.append(key, String(value));
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
return url.toString();
|
|
48
|
-
}
|
|
49
|
-
// Map camelCase SuggestedQuestionsParameters to snake_case query params expected by the API
|
|
50
|
-
function mapSuggestedQuestionsParams(params) {
|
|
51
|
-
const result = {};
|
|
52
|
-
if (params.numResults !== undefined)
|
|
53
|
-
result.num_results = params.numResults;
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
class MockAgent {
|
|
57
|
-
constructor(options) {
|
|
58
|
-
this.options = options;
|
|
59
|
-
}
|
|
60
|
-
getSuggestedQuestions({ itemId, variationId, threadId, parameters = {}, requestParameters = {}, }) {
|
|
61
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
62
|
-
if (!itemId)
|
|
63
|
-
throw new Error('Item ID is required');
|
|
64
|
-
if (!this.options.apiKey)
|
|
65
|
-
throw new Error('API key is required');
|
|
66
|
-
const url = createAgentUrl({
|
|
67
|
-
itemId,
|
|
68
|
-
variationId,
|
|
69
|
-
threadId,
|
|
70
|
-
options: this.options,
|
|
71
|
-
parameters: Object.assign(Object.assign({}, requestParameters), mapSuggestedQuestionsParams(parameters)),
|
|
72
|
-
});
|
|
73
|
-
try {
|
|
74
|
-
const response = yield fetch(url);
|
|
75
|
-
if (!response.ok)
|
|
76
|
-
throw new errors_1.AgentRequestError(response.status);
|
|
77
|
-
const data = yield response.json();
|
|
78
|
-
return data;
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
// Rethrow untouched so the status code and the original stack survive.
|
|
82
|
-
if (error instanceof Error)
|
|
83
|
-
throw error;
|
|
84
|
-
throw new Error(String(error));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
getAnswerResults({ itemId, variationId, threadId, question, parameters = {}, }) {
|
|
89
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
if (!itemId)
|
|
91
|
-
throw new Error('Item ID is required');
|
|
92
|
-
if (!question)
|
|
93
|
-
throw new Error('Question is required');
|
|
94
|
-
if (!this.options.apiKey)
|
|
95
|
-
throw new Error('API key is required');
|
|
96
|
-
const url = createAgentUrl({
|
|
97
|
-
itemId,
|
|
98
|
-
threadId,
|
|
99
|
-
variationId,
|
|
100
|
-
question,
|
|
101
|
-
options: this.options,
|
|
102
|
-
parameters,
|
|
103
|
-
});
|
|
104
|
-
try {
|
|
105
|
-
const response = yield fetch(url);
|
|
106
|
-
if (!response.ok)
|
|
107
|
-
throw new errors_1.AgentRequestError(response.status);
|
|
108
|
-
const data = yield response.json();
|
|
109
|
-
return data;
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
// Rethrow untouched so the status code and the original stack survive.
|
|
113
|
-
if (error instanceof Error)
|
|
114
|
-
throw error;
|
|
115
|
-
throw new Error(String(error));
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Fetches one set of recommendations, in the shape the pod renders.
|
|
121
|
-
*
|
|
122
|
-
* Recommendations are not available yet: there is no endpoint to ask, so nothing is requested and
|
|
123
|
-
* an empty result is returned. The pod renders nothing in that state, which leaves whatever the
|
|
124
|
-
* retailer already had in that slot showing through. Supply a `cioClient` with your own
|
|
125
|
-
* `agent.getRecs` to drive the pod from your own data in the meantime.
|
|
126
|
-
*
|
|
127
|
-
* `props` is still declared, because it is the contract a caller's own `getRecs` implements and
|
|
128
|
-
* `useRecsPod` passes it - there is simply nothing here to send it to yet.
|
|
129
|
-
*/
|
|
130
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
131
|
-
getRecs(props) {
|
|
132
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
return { title: '', items: null, refinement: null, status: 'complete' };
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
getAnswerResultsStream({ itemId, threadId, variationId, question, parameters, onStart, onMessage, onEnd, }) {
|
|
137
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
if (!itemId)
|
|
139
|
-
throw new Error('Item ID is required');
|
|
140
|
-
if (!question)
|
|
141
|
-
throw new Error('Question is required');
|
|
142
|
-
if (!this.options.apiKey)
|
|
143
|
-
throw new Error('API key is required');
|
|
144
|
-
const url = createAgentUrl({
|
|
145
|
-
itemId,
|
|
146
|
-
threadId,
|
|
147
|
-
variationId,
|
|
148
|
-
question,
|
|
149
|
-
isStreaming: true,
|
|
150
|
-
options: this.options,
|
|
151
|
-
parameters,
|
|
152
|
-
});
|
|
153
|
-
try {
|
|
154
|
-
const eventSource = new EventSource(url);
|
|
155
|
-
eventSource.addEventListener('open', (event) => {
|
|
156
|
-
const data = JSON.parse(event.data);
|
|
157
|
-
if (onStart)
|
|
158
|
-
onStart(data);
|
|
159
|
-
});
|
|
160
|
-
eventSource.addEventListener('message', (event) => {
|
|
161
|
-
const data = JSON.parse(event.data);
|
|
162
|
-
if (onMessage)
|
|
163
|
-
onMessage(data);
|
|
164
|
-
});
|
|
165
|
-
eventSource.addEventListener('end', (event) => {
|
|
166
|
-
const data = JSON.parse(event.data);
|
|
167
|
-
if (onEnd)
|
|
168
|
-
onEnd(data);
|
|
169
|
-
eventSource.close();
|
|
170
|
-
});
|
|
171
|
-
eventSource.onerror = () => {
|
|
172
|
-
eventSource.close();
|
|
173
|
-
throw new Error('Unexpected error occurred. Please try again.');
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
178
|
-
throw new Error(errorMessage);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
exports.default = MockAgent;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import ConstructorioClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import MockAgent from './agent';
|
|
3
|
-
import version from '../../version';
|
|
4
|
-
class MockConstructorIOClient {
|
|
5
|
-
options;
|
|
6
|
-
search;
|
|
7
|
-
browse;
|
|
8
|
-
recommendations;
|
|
9
|
-
tracker;
|
|
10
|
-
quizzes;
|
|
11
|
-
agent;
|
|
12
|
-
constructor(options) {
|
|
13
|
-
const isServer = typeof window === 'undefined' || typeof document === 'undefined';
|
|
14
|
-
const clientOptions = {
|
|
15
|
-
version: options.version || `cio-ui-pia-${version}`,
|
|
16
|
-
serviceUrl: options.serviceUrl || 'https://ac.cnstrc.com',
|
|
17
|
-
quizzesServiceUrl: options.quizzesServiceUrl || 'https://quizzes.cnstrc.com',
|
|
18
|
-
agentServiceUrl: options.agentServiceUrl || 'https://agent.cnstrc.com',
|
|
19
|
-
...(isServer && {
|
|
20
|
-
clientId: options.clientId || 'this-is-a-random-client-id',
|
|
21
|
-
}),
|
|
22
|
-
sendTrackingEvents: options.sendTrackingEvents !== undefined ? options.sendTrackingEvents : true,
|
|
23
|
-
beaconMode: options.beaconMode !== undefined ? options.beaconMode : true,
|
|
24
|
-
networkParameters: options.networkParameters || {},
|
|
25
|
-
...options,
|
|
26
|
-
};
|
|
27
|
-
const cioClient = new ConstructorioClient(clientOptions);
|
|
28
|
-
const { clientId, sessionId } = cioClient.agent.options;
|
|
29
|
-
this.options = { ...clientOptions, clientId, sessionId };
|
|
30
|
-
this.search = cioClient.search;
|
|
31
|
-
this.browse = cioClient.browse;
|
|
32
|
-
this.recommendations = cioClient.recommendations;
|
|
33
|
-
this.tracker = cioClient.tracker;
|
|
34
|
-
this.quizzes = cioClient.quizzes;
|
|
35
|
-
this.agent = new MockAgent(this.options);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export default MockConstructorIOClient;
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { AgentRequestError } from '../../errors';
|
|
2
|
-
// Create URL for PIA API
|
|
3
|
-
function createAgentUrl({ itemId, threadId, variationId, question, isStreaming = false, options, parameters = {}, }) {
|
|
4
|
-
const { apiKey, agentServiceUrl, clientId, sessionId, userId, segments, version } = options;
|
|
5
|
-
if (!agentServiceUrl)
|
|
6
|
-
throw new Error('Agent service URL is required');
|
|
7
|
-
let baseUrl = `${agentServiceUrl}/v1/item_questions`;
|
|
8
|
-
if (question) {
|
|
9
|
-
baseUrl += `/${encodeURIComponent(question)}/answer`;
|
|
10
|
-
}
|
|
11
|
-
if (isStreaming) {
|
|
12
|
-
baseUrl += '/streaming';
|
|
13
|
-
}
|
|
14
|
-
const url = new URL(baseUrl);
|
|
15
|
-
url.searchParams.append('item_id', itemId);
|
|
16
|
-
url.searchParams.append('key', apiKey);
|
|
17
|
-
if (threadId) {
|
|
18
|
-
url.searchParams.append('thread_id', threadId);
|
|
19
|
-
}
|
|
20
|
-
if (variationId) {
|
|
21
|
-
url.searchParams.append('variation_id', variationId);
|
|
22
|
-
}
|
|
23
|
-
if (clientId) {
|
|
24
|
-
url.searchParams.append('i', clientId);
|
|
25
|
-
}
|
|
26
|
-
if (sessionId != null) {
|
|
27
|
-
url.searchParams.append('s', String(sessionId));
|
|
28
|
-
}
|
|
29
|
-
if (userId) {
|
|
30
|
-
url.searchParams.append('ui', userId);
|
|
31
|
-
}
|
|
32
|
-
if (version) {
|
|
33
|
-
url.searchParams.append('c', version);
|
|
34
|
-
}
|
|
35
|
-
if (segments?.length) {
|
|
36
|
-
segments.forEach((segment) => url.searchParams.append('us', segment));
|
|
37
|
-
}
|
|
38
|
-
// Any additional parameters
|
|
39
|
-
Object.entries(parameters).forEach(([key, value]) => {
|
|
40
|
-
if (value !== undefined) {
|
|
41
|
-
url.searchParams.append(key, String(value));
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
return url.toString();
|
|
45
|
-
}
|
|
46
|
-
// Map camelCase SuggestedQuestionsParameters to snake_case query params expected by the API
|
|
47
|
-
function mapSuggestedQuestionsParams(params) {
|
|
48
|
-
const result = {};
|
|
49
|
-
if (params.numResults !== undefined)
|
|
50
|
-
result.num_results = params.numResults;
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
class MockAgent {
|
|
54
|
-
options;
|
|
55
|
-
constructor(options) {
|
|
56
|
-
this.options = options;
|
|
57
|
-
}
|
|
58
|
-
async getSuggestedQuestions({ itemId, variationId, threadId, parameters = {}, requestParameters = {}, }) {
|
|
59
|
-
if (!itemId)
|
|
60
|
-
throw new Error('Item ID is required');
|
|
61
|
-
if (!this.options.apiKey)
|
|
62
|
-
throw new Error('API key is required');
|
|
63
|
-
const url = createAgentUrl({
|
|
64
|
-
itemId,
|
|
65
|
-
variationId,
|
|
66
|
-
threadId,
|
|
67
|
-
options: this.options,
|
|
68
|
-
parameters: { ...requestParameters, ...mapSuggestedQuestionsParams(parameters) },
|
|
69
|
-
});
|
|
70
|
-
try {
|
|
71
|
-
const response = await fetch(url);
|
|
72
|
-
if (!response.ok)
|
|
73
|
-
throw new AgentRequestError(response.status);
|
|
74
|
-
const data = await response.json();
|
|
75
|
-
return data;
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
// Rethrow untouched so the status code and the original stack survive.
|
|
79
|
-
if (error instanceof Error)
|
|
80
|
-
throw error;
|
|
81
|
-
throw new Error(String(error));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async getAnswerResults({ itemId, variationId, threadId, question, parameters = {}, }) {
|
|
85
|
-
if (!itemId)
|
|
86
|
-
throw new Error('Item ID is required');
|
|
87
|
-
if (!question)
|
|
88
|
-
throw new Error('Question is required');
|
|
89
|
-
if (!this.options.apiKey)
|
|
90
|
-
throw new Error('API key is required');
|
|
91
|
-
const url = createAgentUrl({
|
|
92
|
-
itemId,
|
|
93
|
-
threadId,
|
|
94
|
-
variationId,
|
|
95
|
-
question,
|
|
96
|
-
options: this.options,
|
|
97
|
-
parameters,
|
|
98
|
-
});
|
|
99
|
-
try {
|
|
100
|
-
const response = await fetch(url);
|
|
101
|
-
if (!response.ok)
|
|
102
|
-
throw new AgentRequestError(response.status);
|
|
103
|
-
const data = await response.json();
|
|
104
|
-
return data;
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
// Rethrow untouched so the status code and the original stack survive.
|
|
108
|
-
if (error instanceof Error)
|
|
109
|
-
throw error;
|
|
110
|
-
throw new Error(String(error));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Fetches one set of recommendations, in the shape the pod renders.
|
|
115
|
-
*
|
|
116
|
-
* Recommendations are not available yet: there is no endpoint to ask, so nothing is requested and
|
|
117
|
-
* an empty result is returned. The pod renders nothing in that state, which leaves whatever the
|
|
118
|
-
* retailer already had in that slot showing through. Supply a `cioClient` with your own
|
|
119
|
-
* `agent.getRecs` to drive the pod from your own data in the meantime.
|
|
120
|
-
*
|
|
121
|
-
* `props` is still declared, because it is the contract a caller's own `getRecs` implements and
|
|
122
|
-
* `useRecsPod` passes it - there is simply nothing here to send it to yet.
|
|
123
|
-
*/
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
125
|
-
async getRecs(props) {
|
|
126
|
-
return { title: '', items: null, refinement: null, status: 'complete' };
|
|
127
|
-
}
|
|
128
|
-
async getAnswerResultsStream({ itemId, threadId, variationId, question, parameters, onStart, onMessage, onEnd, }) {
|
|
129
|
-
if (!itemId)
|
|
130
|
-
throw new Error('Item ID is required');
|
|
131
|
-
if (!question)
|
|
132
|
-
throw new Error('Question is required');
|
|
133
|
-
if (!this.options.apiKey)
|
|
134
|
-
throw new Error('API key is required');
|
|
135
|
-
const url = createAgentUrl({
|
|
136
|
-
itemId,
|
|
137
|
-
threadId,
|
|
138
|
-
variationId,
|
|
139
|
-
question,
|
|
140
|
-
isStreaming: true,
|
|
141
|
-
options: this.options,
|
|
142
|
-
parameters,
|
|
143
|
-
});
|
|
144
|
-
try {
|
|
145
|
-
const eventSource = new EventSource(url);
|
|
146
|
-
eventSource.addEventListener('open', (event) => {
|
|
147
|
-
const data = JSON.parse(event.data);
|
|
148
|
-
if (onStart)
|
|
149
|
-
onStart(data);
|
|
150
|
-
});
|
|
151
|
-
eventSource.addEventListener('message', (event) => {
|
|
152
|
-
const data = JSON.parse(event.data);
|
|
153
|
-
if (onMessage)
|
|
154
|
-
onMessage(data);
|
|
155
|
-
});
|
|
156
|
-
eventSource.addEventListener('end', (event) => {
|
|
157
|
-
const data = JSON.parse(event.data);
|
|
158
|
-
if (onEnd)
|
|
159
|
-
onEnd(data);
|
|
160
|
-
eventSource.close();
|
|
161
|
-
});
|
|
162
|
-
eventSource.onerror = () => {
|
|
163
|
-
eventSource.close();
|
|
164
|
-
throw new Error('Unexpected error occurred. Please try again.');
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
169
|
-
throw new Error(errorMessage);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
export default MockAgent;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ConstructorClientOptions } from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import { Recommendations, Browse, Search, Tracker, Quizzes } from '@constructor-io/constructorio-client-javascript/lib/types/constructorio';
|
|
3
|
-
import MockAgent from './agent';
|
|
4
|
-
declare class MockConstructorIOClient {
|
|
5
|
-
options: ConstructorClientOptions;
|
|
6
|
-
search: Search;
|
|
7
|
-
browse: Browse;
|
|
8
|
-
recommendations: Recommendations;
|
|
9
|
-
tracker: Tracker;
|
|
10
|
-
quizzes: Quizzes;
|
|
11
|
-
agent: MockAgent;
|
|
12
|
-
constructor(options: ConstructorClientOptions);
|
|
13
|
-
}
|
|
14
|
-
export default MockConstructorIOClient;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ConstructorClientOptions } from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import { QuestionResponse, GetSuggestedQuestionsProps, GetAnswerResultsStreamProps, GetAnswerResultsProps, GetAnswerResultsResponse } from './types';
|
|
3
|
-
import { GetRecsProps, RecsResult } from '../../types';
|
|
4
|
-
declare class MockAgent {
|
|
5
|
-
options: ConstructorClientOptions;
|
|
6
|
-
constructor(options: ConstructorClientOptions);
|
|
7
|
-
getSuggestedQuestions({ itemId, variationId, threadId, parameters, requestParameters, }: GetSuggestedQuestionsProps): Promise<QuestionResponse>;
|
|
8
|
-
getAnswerResults({ itemId, variationId, threadId, question, parameters, }: GetAnswerResultsProps): Promise<GetAnswerResultsResponse>;
|
|
9
|
-
/**
|
|
10
|
-
* Fetches one set of recommendations, in the shape the pod renders.
|
|
11
|
-
*
|
|
12
|
-
* Recommendations are not available yet: there is no endpoint to ask, so nothing is requested and
|
|
13
|
-
* an empty result is returned. The pod renders nothing in that state, which leaves whatever the
|
|
14
|
-
* retailer already had in that slot showing through. Supply a `cioClient` with your own
|
|
15
|
-
* `agent.getRecs` to drive the pod from your own data in the meantime.
|
|
16
|
-
*
|
|
17
|
-
* `props` is still declared, because it is the contract a caller's own `getRecs` implements and
|
|
18
|
-
* `useRecsPod` passes it - there is simply nothing here to send it to yet.
|
|
19
|
-
*/
|
|
20
|
-
getRecs(props: GetRecsProps): Promise<RecsResult>;
|
|
21
|
-
getAnswerResultsStream({ itemId, threadId, variationId, question, parameters, onStart, onMessage, onEnd, }: GetAnswerResultsStreamProps): Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
export default MockAgent;
|