@cobrowser/chatgpt 0.7.32-dev.1 → 0.7.32-dev.3

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.
@@ -6,8 +6,8 @@ export declare const MESSAGE_TYPES: {
6
6
  text: string;
7
7
  button: string;
8
8
  };
9
- export declare const DEFAULT_PROMPT = "You are a helpful assistant. Your role is to address visitor \nqueries related to the ongoing conversation and the last message received. Please ensure \nthat responses remain within the ongoing conversation. Politely decline any user queries \nthat is not present in the ongoing conversation. \n\nYou will always respond with a JSON. It will be in the format: \n\n{ answer: '', suggestions: [], connectWithAgent: true or false }\n\nFields in the JSON:\n\nAnswer: It will consist of the user's query answer based on the ongoing conversation. \nRestrict this field to 100 characters.\n\nSuggestions: Request 2 suggestions from user's perspective that he can ask. \nIt should be an array of strings. Restrict each suggestion to 50 characters.\n \nConnectWithAgent: It is a boolean. It should be set to true if the user's query is \nnot found in the ongoing conversation. Politely tell the user to talk to a real agent. \nAlso when it is true, do not provide any suggestions.\n";
9
+ export declare const DEFAULT_PROMPT = "\n You are a helpful assistant. Your role is to address visitor \n queries related to the ongoing conversation and the last message received. Please ensure \n that responses remain within the ongoing conversation. Politely decline any user queries \n that is not present in the ongoing conversation. \n\n You will always respond with a JSON. It will be in the format: \n\n { answer: '', suggestions: [], connectWithAgent: true or false }\n\n Fields in the JSON:\n\n Answer: It will consist of the user's query answer based on the ongoing conversation. \n Restrict this field to 100 characters.\n\n Suggestions: Request 2 suggestions from user's perspective that he can ask. \n It should be an array of strings. Restrict each suggestion to 50 characters.\n \n ConnectWithAgent: It is a boolean. It should be set to true if the user's query is \n not found in the ongoing conversation. Politely tell the user to talk to a real agent. \n Also when it is true, do not provide any suggestions.\n";
10
10
  export declare const DESTINATION_LANGUAGE = "English";
11
11
  export declare const LANGUAGE_DETECTION_SYSTEM_ROLE = "\n You are a language detection assistant. Your task is to analyze short text input and determine \n its language using ISO language code. Respond only in JSON format with two key-value pairs: \n - 'languageCode': the detected language's ISO code (or undefined if detection fails).\n - 'isError': a boolean value (true if detection fails, false otherwise).\n";
12
12
  export declare const WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = "\n You are a language detection assistant. Your task is to analyze the most frequently occurring words in the given text \n and determine the language based on those words. Respond only in JSON format with two key-value pairs: \n - 'languageCode': the detected language's ISO code (or undefined if detection fails).\n - 'isError': a boolean value (true if detection fails, false otherwise).\n";
13
- export declare const TRANSLATION_SYSTEM_ROLE = "\n You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:\n - 'data': it is an array of objects. Each object in the array must contain:\n - 'translated_message': The original input message before translation.\n - 'translation: The translated version of 'translated_message', ensuring it is natural, culturally appropriate, and commonly used by native speakers.\n - 'isError': a boolean value (true if translation fails, false otherwise).\n\n Ensure that 'translation' accurately represents the meaning of 'translated_message' while considering cultural nuances, usual expressions, and natural phrasing in the target language. \n Your response must follow this structure strictly.\n";
13
+ export declare const TRANSLATION_SYSTEM_ROLE = "\n You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:\n - 'translations': it is an array of objects. Each object in the array must contain:\n - 'original': The original input message before translation.\n - 'translation: The translated version of 'original', ensuring it is natural, culturally appropriate, and commonly used by native speakers.\n - 'isError': a boolean value (true if translation fails, false otherwise).\n\n **You MUST return the JSON format even if you fail to translate the text. In this case, translations field will be empty array and isError should be true.** \n\n Ensure that 'translation' accurately represents the meaning of 'original' while considering cultural nuances, usual expressions, and natural phrasing in the target language.\n\n Your response must follow this structure strictly.\n";
package/dist/constants.js CHANGED
@@ -12,26 +12,27 @@ exports.MESSAGE_TYPES = {
12
12
  button: 'button'
13
13
  };
14
14
  // Default prompt for ChatGPT
15
- exports.DEFAULT_PROMPT = `You are a helpful assistant. Your role is to address visitor
16
- queries related to the ongoing conversation and the last message received. Please ensure
17
- that responses remain within the ongoing conversation. Politely decline any user queries
18
- that is not present in the ongoing conversation.
15
+ exports.DEFAULT_PROMPT = `
16
+ You are a helpful assistant. Your role is to address visitor
17
+ queries related to the ongoing conversation and the last message received. Please ensure
18
+ that responses remain within the ongoing conversation. Politely decline any user queries
19
+ that is not present in the ongoing conversation.
19
20
 
20
- You will always respond with a JSON. It will be in the format:
21
+ You will always respond with a JSON. It will be in the format:
21
22
 
22
- { answer: '', suggestions: [], connectWithAgent: true or false }
23
+ { answer: '', suggestions: [], connectWithAgent: true or false }
23
24
 
24
- Fields in the JSON:
25
+ Fields in the JSON:
25
26
 
26
- Answer: It will consist of the user's query answer based on the ongoing conversation.
27
- Restrict this field to 100 characters.
27
+ Answer: It will consist of the user's query answer based on the ongoing conversation.
28
+ Restrict this field to 100 characters.
28
29
 
29
- Suggestions: Request 2 suggestions from user's perspective that he can ask.
30
- It should be an array of strings. Restrict each suggestion to 50 characters.
31
-
32
- ConnectWithAgent: It is a boolean. It should be set to true if the user's query is
33
- not found in the ongoing conversation. Politely tell the user to talk to a real agent.
34
- Also when it is true, do not provide any suggestions.
30
+ Suggestions: Request 2 suggestions from user's perspective that he can ask.
31
+ It should be an array of strings. Restrict each suggestion to 50 characters.
32
+
33
+ ConnectWithAgent: It is a boolean. It should be set to true if the user's query is
34
+ not found in the ongoing conversation. Politely tell the user to talk to a real agent.
35
+ Also when it is true, do not provide any suggestions.
35
36
  `;
36
37
  // Default destination language
37
38
  exports.DESTINATION_LANGUAGE = 'English';
@@ -52,11 +53,14 @@ exports.WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = `
52
53
  // Message translation system role
53
54
  exports.TRANSLATION_SYSTEM_ROLE = `
54
55
  You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:
55
- - 'data': it is an array of objects. Each object in the array must contain:
56
- - 'translated_message': The original input message before translation.
57
- - 'translation: The translated version of 'translated_message', ensuring it is natural, culturally appropriate, and commonly used by native speakers.
56
+ - 'translations': it is an array of objects. Each object in the array must contain:
57
+ - 'original': The original input message before translation.
58
+ - 'translation: The translated version of 'original', ensuring it is natural, culturally appropriate, and commonly used by native speakers.
58
59
  - 'isError': a boolean value (true if translation fails, false otherwise).
59
60
 
60
- Ensure that 'translation' accurately represents the meaning of 'translated_message' while considering cultural nuances, usual expressions, and natural phrasing in the target language.
61
+ **You MUST return the JSON format even if you fail to translate the text. In this case, translations field will be empty array and isError should be true.**
62
+
63
+ Ensure that 'translation' accurately represents the meaning of 'original' while considering cultural nuances, usual expressions, and natural phrasing in the target language.
64
+
61
65
  Your response must follow this structure strictly.
62
66
  `;
@@ -0,0 +1,17 @@
1
+ export declare enum ChatGPTRole {
2
+ USER = "user",
3
+ SYSTEM = "system",
4
+ ASSISTANT = "assistant"
5
+ }
6
+ export interface ChatGPTRequestBody {
7
+ model: string;
8
+ response_format?: {
9
+ type: string;
10
+ };
11
+ messages: ChatGPTMessage[];
12
+ }
13
+ interface ChatGPTMessage {
14
+ role: ChatGPTRole;
15
+ content: string;
16
+ }
17
+ export default ChatGPTMessage;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChatGPTRole = void 0;
4
+ var ChatGPTRole;
5
+ (function (ChatGPTRole) {
6
+ ChatGPTRole["USER"] = "user";
7
+ ChatGPTRole["SYSTEM"] = "system";
8
+ ChatGPTRole["ASSISTANT"] = "assistant";
9
+ })(ChatGPTRole = exports.ChatGPTRole || (exports.ChatGPTRole = {}));
@@ -0,0 +1,8 @@
1
+ import ChatGPTUsageTokens from './ChatGPTUsageTokens';
2
+ interface ChatGPTResponse {
3
+ data?: any;
4
+ usageTokens?: ChatGPTUsageTokens;
5
+ threadId?: string;
6
+ requiredActions?: any;
7
+ }
8
+ export default ChatGPTResponse;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ interface ChatGPTUsageTokens {
2
+ prompt_tokens: Number;
3
+ completion_tokens: Number;
4
+ total_tokens: Number;
5
+ }
6
+ export default ChatGPTUsageTokens;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,13 +1,13 @@
1
1
  interface ConversationHistory {
2
- from: string;
3
2
  to: string;
4
- datetime: number;
3
+ from: string;
5
4
  body: MessageBody[];
5
+ datetime: number;
6
6
  }
7
7
  interface MessageBody {
8
+ id?: string;
8
9
  type: string;
9
10
  text?: string;
10
- id?: string;
11
11
  label?: string;
12
12
  }
13
13
  export default ConversationHistory;
@@ -1,10 +1,10 @@
1
1
  interface Item {
2
2
  id?: string;
3
3
  name?: string;
4
- description?: string;
5
4
  link?: string;
6
5
  price: number;
7
- category?: string;
8
6
  rating?: string;
7
+ category?: string;
8
+ description?: string;
9
9
  }
10
10
  export default Item;
@@ -0,0 +1,3 @@
1
+ export interface Response<Data = any> {
2
+ result: Data;
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
- import ChatGptResponse from '../../models/ChatGptResponse';
2
- import BaseService from '../BaseService/BaseService';
3
1
  import { AssistantResponseFormatOption } from 'openai/resources/beta/threads/threads';
2
+ import BaseService from '../BaseService/BaseService';
3
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
4
4
  export declare class AssistantService extends BaseService {
5
5
  assistantId: string | undefined;
6
6
  threadId: string | undefined;
@@ -22,5 +22,5 @@ export declare class AssistantService extends BaseService {
22
22
  tools?: any[];
23
23
  functionOutputs?: string;
24
24
  definedSchema?: AssistantResponseFormatOption;
25
- }): Promise<ChatGptResponse | undefined>;
25
+ }): Promise<ChatGPTResponse | undefined>;
26
26
  }
@@ -74,14 +74,16 @@ class AssistantService extends BaseService_1.default {
74
74
  });
75
75
  }
76
76
  else {
77
- const lastThreadMessasges = yield openai.beta.threads.messages.list(this.threadId, { order: "desc", limit: 2 });
78
- const isRepeatedMessage = lastThreadMessasges.data.find(msg => msg.content[0].type === 'text' && msg.content[0].text.value === message);
77
+ const lastThreadMessasges = yield openai.beta.threads.messages.list(this.threadId, { order: "desc", limit: 2 }), isRepeatedMessage = lastThreadMessasges.data.find(msg => msg.content[0].type === 'text' && msg.content[0].text.value === message);
79
78
  if (isRepeatedMessage) {
80
79
  const runs = yield openai.beta.threads.runs.list(this.threadId, { order: "desc", limit: 1 });
81
80
  run = runs.data[0];
82
81
  }
83
82
  else {
84
- yield openai.beta.threads.messages.create(this.threadId, { role: "user", content: message });
83
+ yield openai.beta.threads.messages.create(this.threadId, {
84
+ role: "user",
85
+ content: message
86
+ });
85
87
  run = yield openai.beta.threads.runs.create(this.threadId, {
86
88
  assistant_id: (_a = this.assistantId) !== null && _a !== void 0 ? _a : '',
87
89
  additional_instructions: runOptions.instructions,
@@ -98,13 +100,13 @@ class AssistantService extends BaseService_1.default {
98
100
  run = yield openai.beta.threads.runs.retrieve(run.id, { thread_id: run.thread_id });
99
101
  }
100
102
  if (run.status === 'completed') {
101
- const { data } = yield openai.beta.threads.messages.list(run.thread_id, { order: 'desc', limit: 1 });
102
- const messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : '')));
103
- const assistantMessages = messages.filter(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
103
+ const { data } = yield openai.beta.threads.messages.list(run.thread_id, { order: 'desc', limit: 1 }), messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : ''))), assistantMessages = messages.filter(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
104
104
  if (assistantMessages.length) {
105
- let answer = assistantMessages.map(message => message.value).join('\n');
105
+ // as we will always get one message (limit = 1 from desc order),
106
+ // we can just directly access the message here
107
+ const answer = assistantMessages[0].value, replaced = answer.replace(/【.*?】/g, ''), parsed = JSON.parse(replaced);
106
108
  return {
107
- firstChoice: answer.replace(/【.*?】/g, ''),
109
+ data: parsed,
108
110
  threadId: run.thread_id,
109
111
  usageTokens: run.usage,
110
112
  };
@@ -112,7 +114,7 @@ class AssistantService extends BaseService_1.default {
112
114
  }
113
115
  if (run.status === 'requires_action') {
114
116
  return {
115
- requiredActions: JSON.stringify(run.required_action),
117
+ requiredActions: run.required_action,
116
118
  threadId: run.thread_id,
117
119
  usageTokens: run.usage,
118
120
  };
@@ -1,30 +1,38 @@
1
1
  import { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
2
- import ChatGptUsageTokens from '../../models/ChatGptUsageTokens';
2
+ import ChatGPTUsageTokens from '../../models/ChatGPTUsageTokens';
3
3
  declare class BaseService {
4
- request: AxiosInstance;
5
4
  readonly BASE_CHAT_COMPLETION_URL = "https://api.openai.com/v1/chat/completions";
5
+ request: AxiosInstance;
6
6
  openaiApiKey: string | undefined;
7
7
  chatGptModel: string;
8
8
  maxNumberOfChatCompletionResult: number;
9
9
  /**
10
- *
11
10
  * @param apiKey
12
11
  * @param model the main chatGPT model to use
13
12
  * @param apiUrl Api url to be used to make API calls to openAI services
14
13
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
15
- *
16
14
  */
17
15
  constructor(apiKey?: string | undefined, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
16
+ /**
17
+ * Automatically wraps some specific async methods to return a specific sanitized
18
+ * and encoded JSON without any developer intervention.
19
+ */
20
+ private processAsyncMethods;
21
+ /**
22
+ * Recursively sanitizes any value
23
+ */
24
+ private sanitize;
18
25
  /**
19
26
  * Filter the response from chatGpt API and only get the first choice
27
+ *
20
28
  * @param response
21
29
  */
22
- getResponseFirstChoice(response: AxiosResponse): string | undefined;
30
+ getResponseFirstChoice(response: AxiosResponse): any;
23
31
  /**
24
32
  * Filter the response from chatGpt API and get the usage tokens
25
33
  * @param response
26
34
  */
27
- getResponseUsageTokens(response: AxiosResponse): ChatGptUsageTokens | undefined;
35
+ getResponseUsageTokens(response: AxiosResponse): ChatGPTUsageTokens | undefined;
28
36
  /**
29
37
  * function to handle requests errors
30
38
  * @param error
@@ -1,31 +1,40 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
5
14
  Object.defineProperty(exports, "__esModule", { value: true });
6
15
  const axios_1 = __importDefault(require("axios"));
16
+ const Response_1 = require("../../utils/Response");
17
+ const xss_validation_1 = require("@cobrowser/xss-validation");
7
18
  const logger_1 = __importDefault(require("../logger"));
8
19
  class BaseService {
9
20
  /**
10
- *
11
21
  * @param apiKey
12
22
  * @param model the main chatGPT model to use
13
23
  * @param apiUrl Api url to be used to make API calls to openAI services
14
24
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
15
- *
16
25
  */
17
26
  constructor(apiKey = process.env.OPEN_AI_API_KEY, model = 'gpt-4o', apiUrl, maxNumberOfChoices = 1) {
18
- Object.defineProperty(this, "request", {
27
+ Object.defineProperty(this, "BASE_CHAT_COMPLETION_URL", {
19
28
  enumerable: true,
20
29
  configurable: true,
21
30
  writable: true,
22
- value: void 0
31
+ value: 'https://api.openai.com/v1/chat/completions'
23
32
  });
24
- Object.defineProperty(this, "BASE_CHAT_COMPLETION_URL", {
33
+ Object.defineProperty(this, "request", {
25
34
  enumerable: true,
26
35
  configurable: true,
27
36
  writable: true,
28
- value: 'https://api.openai.com/v1/chat/completions'
37
+ value: void 0
29
38
  });
30
39
  Object.defineProperty(this, "openaiApiKey", {
31
40
  enumerable: true,
@@ -55,9 +64,60 @@ class BaseService {
55
64
  this.openaiApiKey = apiKey;
56
65
  this.chatGptModel = model;
57
66
  this.maxNumberOfChatCompletionResult = maxNumberOfChoices;
67
+ this.processAsyncMethods();
68
+ }
69
+ /**
70
+ * Automatically wraps some specific async methods to return a specific sanitized
71
+ * and encoded JSON without any developer intervention.
72
+ */
73
+ processAsyncMethods() {
74
+ const proto = Object.getPrototypeOf(this), methodNames = Object.getOwnPropertyNames(proto);
75
+ for (const methodName of methodNames) {
76
+ const method = proto[methodName];
77
+ // Skip constructor, non-functions, and utility methods But include service methods
78
+ // like getReply, getAssistant, etc. Please note that new methods that are not final
79
+ // output should be skipped here.
80
+ if (methodName === 'constructor' ||
81
+ methodName === 'suggest' ||
82
+ typeof method !== 'function' ||
83
+ (methodName.startsWith('get') &&
84
+ !methodName.startsWith('getReply') &&
85
+ !methodName.startsWith('getAssist') &&
86
+ !methodName.startsWith('getChat') &&
87
+ !methodName.startsWith('getPrompt')) ||
88
+ methodName.startsWith('handle') ||
89
+ methodName.includes('Sanitize') ||
90
+ methodName.includes('sanitize') ||
91
+ methodName.includes('createConversation')) {
92
+ continue;
93
+ }
94
+ const originalMethod = method.bind(this);
95
+ // Replace the method with a wrapper
96
+ this[methodName] = (...args) => __awaiter(this, void 0, void 0, function* () {
97
+ try {
98
+ const response = Response_1.ResponseBuilder.build(yield originalMethod(...args)), sanitized = this.sanitize(response);
99
+ return sanitized;
100
+ }
101
+ catch (error) {
102
+ throw error;
103
+ }
104
+ });
105
+ }
106
+ }
107
+ /**
108
+ * Recursively sanitizes any value
109
+ */
110
+ sanitize(data) {
111
+ if (!data) {
112
+ return;
113
+ }
114
+ const sanitized = xss_validation_1.XSSProtector.sanitize(data), encoded = xss_validation_1.XSSProtector.encode(sanitized);
115
+ logger_1.default.info({ data, sanitized, encoded }, ':: Sanitized Response ::');
116
+ return encoded;
58
117
  }
59
118
  /**
60
119
  * Filter the response from chatGpt API and only get the first choice
120
+ *
61
121
  * @param response
62
122
  */
63
123
  getResponseFirstChoice(response) {
@@ -65,7 +125,7 @@ class BaseService {
65
125
  if ((_a = response.data.choices) === null || _a === void 0 ? void 0 : _a.length) {
66
126
  const firstChatGptResponseMessage = response.data.choices[0].message;
67
127
  logger_1.default.info(`First ChatGpt Message: ${firstChatGptResponseMessage.content}`);
68
- return firstChatGptResponseMessage.content;
128
+ return JSON.parse(firstChatGptResponseMessage.content);
69
129
  }
70
130
  return undefined;
71
131
  }
@@ -1,8 +1,9 @@
1
1
  import BaseService from '../BaseService/BaseService';
2
+ import ChatGPTResponse from 'src/models/ChatGPTResponse';
2
3
  import ConversationHistory from 'src/models/ConversationHistory';
3
4
  export declare class ChatService extends BaseService {
4
5
  #private;
5
6
  model: string | undefined;
6
7
  constructor(apiKey?: string, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
7
- ask(context: string | undefined, conversationHistory: ConversationHistory[]): Promise<string | undefined>;
8
+ ask(context: string | undefined, conversationHistory: ConversationHistory[]): Promise<ChatGPTResponse | undefined>;
8
9
  }
@@ -19,10 +19,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  var _ChatService_instances, _ChatService_formatConversationHistory;
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.ChatService = void 0;
22
- const ChatGptMessage_1 = require("../../models/ChatGptMessage");
23
- const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
22
+ const ChatGPTMessage_1 = require("../../models/ChatGPTMessage");
23
+ const constants_1 = require("../../utils/constants");
24
24
  const logger_1 = __importDefault(require("../logger"));
25
- const constants_1 = require("../../constants");
25
+ const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
26
26
  class ChatService extends BaseService_1.default {
27
27
  constructor(apiKey, model, apiUrl, maxNumberOfChoices) {
28
28
  super(apiKey, model, apiUrl, maxNumberOfChoices);
@@ -45,10 +45,15 @@ class ChatService extends BaseService_1.default {
45
45
  model: this.model,
46
46
  messages: __classPrivateFieldGet(this, _ChatService_instances, "m", _ChatService_formatConversationHistory).call(this, context, conversationHistory),
47
47
  };
48
- logger_1.default.info(`:: ChatService.ask :: ${JSON.stringify(requestBody)}`);
48
+ logger_1.default.info({
49
+ requestBody: JSON.stringify(requestBody)
50
+ }, ':: ChatService.ask :: Request Body');
49
51
  try {
50
52
  const response = yield this.request.post('', requestBody);
51
- return this.getResponseFirstChoice(response);
53
+ return {
54
+ data: this.getResponseFirstChoice(response),
55
+ usageTokens: this.getResponseUsageTokens(response),
56
+ };
52
57
  }
53
58
  catch (e) {
54
59
  this.handleErrors(e);
@@ -61,7 +66,7 @@ exports.ChatService = ChatService;
61
66
  _ChatService_instances = new WeakSet(), _ChatService_formatConversationHistory = function _ChatService_formatConversationHistory(context, conversationHistory) {
62
67
  const formatted = [
63
68
  {
64
- role: ChatGptMessage_1.ChatGptRole.SYSTEM,
69
+ role: ChatGPTMessage_1.ChatGPTRole.SYSTEM,
65
70
  content: context || constants_1.DEFAULT_PROMPT,
66
71
  }
67
72
  ];
@@ -71,19 +76,19 @@ _ChatService_instances = new WeakSet(), _ChatService_formatConversationHistory =
71
76
  for (const body of messageBody) {
72
77
  if (body.type === constants_1.MESSAGE_TYPES.text && message.from === constants_1.MESSAGE_SENDER.visitor) {
73
78
  formatted.push({
74
- role: ChatGptMessage_1.ChatGptRole.USER,
79
+ role: ChatGPTMessage_1.ChatGPTRole.USER,
75
80
  content: body.text
76
81
  });
77
82
  }
78
83
  else if (body.type === constants_1.MESSAGE_TYPES.text && message.from === constants_1.MESSAGE_SENDER.chatbot) {
79
84
  formatted.push({
80
- role: ChatGptMessage_1.ChatGptRole.ASSISTANT,
85
+ role: ChatGPTMessage_1.ChatGPTRole.ASSISTANT,
81
86
  content: body.text
82
87
  });
83
88
  }
84
89
  else if (body.type === constants_1.MESSAGE_TYPES.button && message.from === constants_1.MESSAGE_SENDER.chatbot) {
85
90
  formatted.push({
86
- role: ChatGptMessage_1.ChatGptRole.ASSISTANT,
91
+ role: ChatGPTMessage_1.ChatGPTRole.ASSISTANT,
87
92
  content: body.label
88
93
  });
89
94
  }
@@ -1,8 +1,7 @@
1
- import ChatGptResponse from '../../models/ChatGptResponse';
2
1
  import BaseService from '../BaseService/BaseService';
3
- import ChatGptMessage from '../../models/ChatGptMessage';
4
- import Item from '../../models/Item';
2
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
5
3
  export declare class CopilotService extends BaseService {
4
+ #private;
6
5
  assistantId: string | undefined;
7
6
  threadId: string | undefined;
8
7
  conversationId: string | undefined;
@@ -19,21 +18,21 @@ export declare class CopilotService extends BaseService {
19
18
  * this will make it easy to ask chatGpt for quick replies
20
19
  * @param conversation
21
20
  */
22
- suggest(conversation: string): Promise<ChatGptResponse | undefined>;
21
+ suggest(conversation: string): Promise<ChatGPTResponse | undefined>;
23
22
  /**
24
- * this will request chatGPT to recommend product or item based on the whole conversation
25
- * @param conversation the whole chat conversation
26
- * @param items the items to recommend from
23
+ * Get suggestions from Chat Completion API.
24
+ *
25
+ * @param message
27
26
  */
28
- recommend(conversation: ChatGptMessage[], items: Item[]): Promise<string | undefined>;
27
+ getChatCompletionSuggestions(conversation: string): Promise<ChatGPTResponse | undefined>;
29
28
  /**
30
29
  * Get the reply of the assistant if the assistant_id is available
31
30
  *
32
31
  * @param message
33
32
  */
34
- getAssistantReply(message: string): Promise<ChatGptResponse | undefined>;
33
+ getAssistantSuggestions(conversation: string): Promise<ChatGPTResponse | undefined>;
35
34
  private isAssistantIdFormat;
36
35
  private isPromptIdFormat;
37
36
  private ensureConversation;
38
- getPromptReply(message: string): Promise<ChatGptResponse | undefined>;
37
+ getPromptSuggestions(message: string): Promise<ChatGPTResponse | undefined>;
39
38
  }
@@ -8,18 +8,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
+ };
11
16
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
18
  };
19
+ var _CopilotService_instances, _CopilotService_processSuggestions;
14
20
  Object.defineProperty(exports, "__esModule", { value: true });
15
21
  exports.CopilotService = void 0;
16
- const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
22
+ const ChatGPTMessage_1 = require("../../models/ChatGPTMessage");
17
23
  const logger_1 = __importDefault(require("../logger"));
18
- const ChatGptMessage_1 = require("../../models/ChatGptMessage");
19
24
  const openai_1 = __importDefault(require("openai"));
25
+ const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
20
26
  class CopilotService extends BaseService_1.default {
21
27
  constructor(apiKey, model, assistantId, threadId, apiUrl, maxNumberOfChoices) {
22
28
  super(apiKey, model, apiUrl, maxNumberOfChoices);
29
+ _CopilotService_instances.add(this);
23
30
  Object.defineProperty(this, "assistantId", {
24
31
  enumerable: true,
25
32
  configurable: true,
@@ -56,8 +63,8 @@ class CopilotService extends BaseService_1.default {
56
63
  suggest(conversation) {
57
64
  return __awaiter(this, void 0, void 0, function* () {
58
65
  if (!(conversation === null || conversation === void 0 ? void 0 : conversation.length)) {
59
- logger_1.default.error('conversation must be provided');
60
- return Promise.reject(new Error('conversation must be provided'));
66
+ logger_1.default.error('Conversation must be provided');
67
+ return Promise.reject(new Error('Conversation must be provided'));
61
68
  }
62
69
  try {
63
70
  if (this.assistantId) {
@@ -71,28 +78,11 @@ class CopilotService extends BaseService_1.default {
71
78
  }
72
79
  // Route based on id shape: asst_* => Assistant API, prompt_* => Responses API
73
80
  if (this.isAssistantIdFormat(this.assistantId)) {
74
- return yield this.getAssistantReply(lastCustomerMessage);
81
+ return yield this.getAssistantSuggestions(conversation);
75
82
  }
76
- return yield this.getPromptReply(lastCustomerMessage);
83
+ return yield this.getPromptSuggestions(lastCustomerMessage);
77
84
  }
78
- // main prompt used to instruct chatGPT to suggest some quick replies
79
- const mainPrompt = `Propose some quick replies for the agent to be used on the following conversation
80
- Your response must only contains a valid JSON array of strings like this:
81
- ["Reply 1", "Reply 2", "Reply 3"]. the conversation is:-\n${conversation}`,
82
- // main prompt will be the whole conversation as string
83
- mainPromptMessage = {
84
- role: ChatGptMessage_1.ChatGptRole.USER,
85
- content: mainPrompt,
86
- },
87
- // the body we need to send to the request
88
- requestBody = {
89
- model: this.chatGptModel,
90
- messages: [mainPromptMessage],
91
- }, response = yield this.request.post('', requestBody);
92
- return {
93
- firstChoice: this.getResponseFirstChoice(response),
94
- usageTokens: this.getResponseUsageTokens(response),
95
- };
85
+ return yield this.getChatCompletionSuggestions(conversation);
96
86
  }
97
87
  catch (e) {
98
88
  this.handleErrors(e);
@@ -101,35 +91,30 @@ class CopilotService extends BaseService_1.default {
101
91
  });
102
92
  }
103
93
  /**
104
- * this will request chatGPT to recommend product or item based on the whole conversation
105
- * @param conversation the whole chat conversation
106
- * @param items the items to recommend from
94
+ * Get suggestions from Chat Completion API.
95
+ *
96
+ * @param message
107
97
  */
108
- recommend(conversation, items) {
98
+ getChatCompletionSuggestions(conversation) {
109
99
  return __awaiter(this, void 0, void 0, function* () {
110
- if (!(items === null || items === void 0 ? void 0 : items.length) || !(conversation === null || conversation === void 0 ? void 0 : conversation.length)) {
111
- logger_1.default.error('conversation or items not found');
112
- return Promise.reject(new Error('conversation and items must be provided'));
113
- }
114
- try {
115
- // main prompt used to instruct chatGPT to recommend products
116
- const mainPrompt = `we have this list of Items: ${JSON.stringify(items)}, what to recommend for you`,
117
- // main prompt will be the first message of the whole conversation
118
- firstMessage = {
119
- role: ChatGptMessage_1.ChatGptRole.ASSISTANT,
120
- content: mainPrompt,
121
- },
122
- // the body we need to send to the request
123
- requestBody = {
124
- model: this.chatGptModel,
125
- messages: [firstMessage, ...conversation],
126
- }, response = yield this.request.post('', requestBody);
127
- return this.getResponseFirstChoice(response);
128
- }
129
- catch (e) {
130
- this.handleErrors(e);
131
- return Promise.reject(e);
132
- }
100
+ // main prompt used to instruct chatGPT to suggest some quick replies
101
+ const mainPrompt = `Propose some quick replies for the agent to be used on the following conversation
102
+ Your response must only contains a valid JSON array of strings like this:
103
+ ["Reply 1", "Reply 2", "Reply 3"]. the conversation is:-\n${conversation}`,
104
+ // main prompt will be the whole conversation as string
105
+ mainPromptMessage = {
106
+ role: ChatGPTMessage_1.ChatGPTRole.USER,
107
+ content: mainPrompt,
108
+ },
109
+ // the body we need to send to the request
110
+ requestBody = {
111
+ model: this.chatGptModel,
112
+ messages: [mainPromptMessage],
113
+ }, response = yield this.request.post('', requestBody);
114
+ return {
115
+ data: __classPrivateFieldGet(this, _CopilotService_instances, "m", _CopilotService_processSuggestions).call(this, this.getResponseFirstChoice(response)),
116
+ usageTokens: this.getResponseUsageTokens(response),
117
+ };
133
118
  });
134
119
  }
135
120
  /**
@@ -137,24 +122,30 @@ class CopilotService extends BaseService_1.default {
137
122
  *
138
123
  * @param message
139
124
  */
140
- getAssistantReply(message) {
125
+ getAssistantSuggestions(conversation) {
141
126
  var _a;
142
127
  return __awaiter(this, void 0, void 0, function* () {
128
+ // Here we get the last customer message as we dont need the whole conversation
129
+ // as we are doing with the other approach.
130
+ const conversationArray = conversation.split('\n'), lastCustomerMessage = conversationArray.reverse().find(e => e.includes('customer:'));
131
+ if (!lastCustomerMessage) {
132
+ logger_1.default.error('Last customer message not found');
133
+ return Promise.reject(new Error('Last customer message not found'));
134
+ }
143
135
  const openai = new openai_1.default({ apiKey: this.openaiApiKey });
144
136
  let run;
145
137
  if (!this.threadId) {
146
138
  const thread = yield openai.beta.threads.create();
147
139
  this.threadId = thread.id;
148
140
  }
149
- const lastThreadMessasges = yield openai.beta.threads.messages.list(this.threadId, { order: "desc", limit: 2 });
150
- const isRepeatedMessage = lastThreadMessasges.data.find(msg => msg.content[0].type === 'text' && msg.content[0].text.value === message);
141
+ const lastThreadMessasges = yield openai.beta.threads.messages.list(this.threadId, { order: "desc", limit: 2 }), isRepeatedMessage = lastThreadMessasges.data.find(msg => msg.content[0].type === 'text' && msg.content[0].text.value === lastCustomerMessage);
151
142
  // making sure that we're not saving repeated messages
152
143
  if (isRepeatedMessage) {
153
144
  const runs = yield openai.beta.threads.runs.list(this.threadId, { order: "desc", limit: 1 });
154
145
  run = runs.data[0];
155
146
  }
156
147
  else {
157
- yield openai.beta.threads.messages.create(this.threadId, { role: "user", content: message });
148
+ yield openai.beta.threads.messages.create(this.threadId, { role: "user", content: lastCustomerMessage });
158
149
  run = yield openai.beta.threads.runs.create(this.threadId, { assistant_id: (_a = this.assistantId) !== null && _a !== void 0 ? _a : '' });
159
150
  }
160
151
  while (['queued', 'in_progress', 'cancelling'].includes(run.status)) {
@@ -162,13 +153,11 @@ class CopilotService extends BaseService_1.default {
162
153
  run = yield openai.beta.threads.runs.retrieve(run.id, { thread_id: run.thread_id });
163
154
  }
164
155
  if (run.status === 'completed') {
165
- const { data } = yield openai.beta.threads.messages.list(run.thread_id, { order: 'desc', limit: 1 });
166
- const messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : '')));
167
- const assistantMessages = messages.filter(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
156
+ const { data } = yield openai.beta.threads.messages.list(run.thread_id, { order: 'desc', limit: 1 }), messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : ''))), assistantMessages = messages.filter(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
168
157
  if (assistantMessages.length) {
169
- let answers = assistantMessages.map(message => message.value);
158
+ const answers = assistantMessages.map(message => message.value);
170
159
  return {
171
- firstChoice: JSON.stringify(answers),
160
+ data: __classPrivateFieldGet(this, _CopilotService_instances, "m", _CopilotService_processSuggestions).call(this, answers),
172
161
  threadId: run.thread_id,
173
162
  usageTokens: run.usage,
174
163
  };
@@ -193,7 +182,7 @@ class CopilotService extends BaseService_1.default {
193
182
  return conv.id;
194
183
  });
195
184
  }
196
- getPromptReply(message) {
185
+ getPromptSuggestions(message) {
197
186
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
198
187
  return __awaiter(this, void 0, void 0, function* () {
199
188
  const openai = new openai_1.default({ apiKey: this.openaiApiKey });
@@ -221,7 +210,7 @@ class CopilotService extends BaseService_1.default {
221
210
  if (assistantMessages.length) {
222
211
  const text = assistantMessages.flatMap((m) => { var _a, _b; return ((_b = (_a = m.content) === null || _a === void 0 ? void 0 : _a.filter((c) => c.type === 'output_text')) === null || _b === void 0 ? void 0 : _b.map((c) => c.text)) || []; });
223
212
  return {
224
- firstChoice: JSON.stringify([text.pop()]),
213
+ data: JSON.stringify([text.pop()]),
225
214
  threadId: this.conversationId,
226
215
  usageTokens: {
227
216
  prompt_tokens: Number((_c = ((_b = response.usage) === null || _b === void 0 ? void 0 : _b.prompt_tokens)) !== null && _c !== void 0 ? _c : 0),
@@ -232,7 +221,7 @@ class CopilotService extends BaseService_1.default {
232
221
  }
233
222
  if (response.output_text) {
234
223
  return {
235
- firstChoice: JSON.stringify([response.output_text]),
224
+ data: JSON.stringify([response.output_text]),
236
225
  threadId: this.conversationId,
237
226
  usageTokens: {
238
227
  prompt_tokens: Number((_j = ((_h = response.usage) === null || _h === void 0 ? void 0 : _h.prompt_tokens)) !== null && _j !== void 0 ? _j : 0),
@@ -246,3 +235,8 @@ class CopilotService extends BaseService_1.default {
246
235
  }
247
236
  }
248
237
  exports.CopilotService = CopilotService;
238
+ _CopilotService_instances = new WeakSet(), _CopilotService_processSuggestions = function _CopilotService_processSuggestions(suggestions) {
239
+ suggestions = suggestions ? suggestions : [];
240
+ suggestions = suggestions.filter(suggestion => !!suggestion.trim());
241
+ return suggestions;
242
+ };
@@ -1,5 +1,4 @@
1
- import ChatGptResponse from '../../models/ChatGptResponse';
2
- import OpenAI from "openai";
1
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
3
2
  import BaseService from '../BaseService/BaseService';
4
3
  interface ResponseServiceOptions {
5
4
  instructions?: string;
@@ -14,7 +13,7 @@ export declare class ResponseService extends BaseService {
14
13
  lastResponseId: string | undefined;
15
14
  constructor(apiKey?: string, promptId?: string, conversationId?: string);
16
15
  /**
17
- * Create a new conversation/response chain
16
+ * Create a new conversation
18
17
  */
19
18
  createConversation(): Promise<string>;
20
19
  /**
@@ -23,9 +22,7 @@ export declare class ResponseService extends BaseService {
23
22
  * @param message
24
23
  * @param runOptions
25
24
  */
26
- getReply(message: string, runOptions?: ResponseServiceOptions): Promise<ChatGptResponse | undefined>;
27
- handleFunctionOutputs(openai: OpenAI, functionOutputs: string, runOptions?: ResponseServiceOptions): Promise<ChatGptResponse>;
28
- processResponseOutput(response: any): ChatGptResponse;
25
+ getReply(message: string, runOptions?: ResponseServiceOptions): Promise<ChatGPTResponse | undefined>;
29
26
  reset(): void;
30
27
  getConversationId(): string | undefined;
31
28
  getLastResponseId(): string | undefined;
@@ -41,11 +41,10 @@ class ResponseService extends BaseService_1.default {
41
41
  this.conversationId = conversationId;
42
42
  }
43
43
  /**
44
- * Create a new conversation/response chain
44
+ * Create a new conversation
45
45
  */
46
46
  createConversation() {
47
47
  return __awaiter(this, void 0, void 0, function* () {
48
- // Create a real conversation using the Conversations API
49
48
  const openai = new openai_1.default({ apiKey: this.openaiApiKey });
50
49
  const conversation = yield openai.conversations.create();
51
50
  this.conversationId = conversation.id;
@@ -68,38 +67,54 @@ class ResponseService extends BaseService_1.default {
68
67
  logger_1.default.info(`New response with message: ${message} & options: ${JSON.stringify(runOptions)}`);
69
68
  const openai = new openai_1.default({ apiKey: this.openaiApiKey });
70
69
  try {
71
- if (runOptions.functionOutputs) {
72
- return yield this.handleFunctionOutputs(openai, runOptions.functionOutputs, runOptions);
73
- }
74
- if (!runOptions.conversationId && !this.conversationId) {
75
- const createdId = yield this.createConversation();
76
- this.conversationId = createdId;
77
- }
78
70
  const responseRequest = {
79
- input: message,
80
71
  store: true,
81
- include: ['file_search_call.results'],
82
72
  };
73
+ // Handle function outputs
74
+ if (runOptions.functionOutputs) {
75
+ const toolOutputs = JSON.parse(runOptions.functionOutputs);
76
+ const formattedInputs = toolOutputs.map((output) => ({
77
+ type: 'function_call_output',
78
+ call_id: output.call_id,
79
+ output: output.output
80
+ }));
81
+ responseRequest.input = formattedInputs;
82
+ const conversationIdToUse = runOptions.conversationId || this.conversationId;
83
+ if (!conversationIdToUse) {
84
+ const createdId = yield this.createConversation();
85
+ this.conversationId = createdId;
86
+ }
87
+ responseRequest.conversation = runOptions.conversationId || this.conversationId;
88
+ }
89
+ else {
90
+ // Handle regular message
91
+ if (!runOptions.conversationId && !this.conversationId) {
92
+ const createdId = yield this.createConversation();
93
+ this.conversationId = createdId;
94
+ }
95
+ responseRequest.input = message;
96
+ responseRequest.include = ['file_search_call.results'];
97
+ if (runOptions.instructions) {
98
+ responseRequest.instructions = runOptions.instructions;
99
+ }
100
+ const conversationIdToUse = runOptions.conversationId || this.conversationId;
101
+ if (conversationIdToUse) {
102
+ responseRequest.conversation = conversationIdToUse;
103
+ }
104
+ if ((_a = runOptions.tools) === null || _a === void 0 ? void 0 : _a.length) {
105
+ responseRequest.tools = runOptions.tools;
106
+ }
107
+ if (runOptions.definedSchema) {
108
+ responseRequest.text = {
109
+ format: runOptions.definedSchema
110
+ };
111
+ }
112
+ }
83
113
  if (this.promptId) {
84
114
  responseRequest.prompt = {
85
115
  id: this.promptId
86
116
  };
87
117
  }
88
- if (runOptions.instructions) {
89
- responseRequest.instructions = runOptions.instructions;
90
- }
91
- const conversationIdToUse = runOptions.conversationId || this.conversationId;
92
- if (conversationIdToUse) {
93
- responseRequest.conversation = conversationIdToUse;
94
- }
95
- if ((_a = runOptions.tools) === null || _a === void 0 ? void 0 : _a.length) {
96
- responseRequest.tools = runOptions.tools;
97
- }
98
- if (runOptions.definedSchema) {
99
- responseRequest.text = {
100
- format: runOptions.definedSchema
101
- };
102
- }
103
118
  const response = yield openai.responses.create(responseRequest);
104
119
  this.lastResponseId = response.id;
105
120
  if ((_b = response.conversation) === null || _b === void 0 ? void 0 : _b.id) {
@@ -108,84 +123,49 @@ class ResponseService extends BaseService_1.default {
108
123
  else if (runOptions.conversationId) {
109
124
  this.conversationId = runOptions.conversationId;
110
125
  }
111
- return this.processResponseOutput(response);
112
- }
113
- catch (error) {
114
- logger_1.default.error('Error in ResponseService.getReply:', error);
115
- return Promise.reject(error);
116
- }
117
- });
118
- }
119
- handleFunctionOutputs(openai, functionOutputs, runOptions = {}) {
120
- return __awaiter(this, void 0, void 0, function* () {
121
- try {
122
- const toolOutputs = JSON.parse(functionOutputs);
123
- const formattedInputs = toolOutputs.map((output) => ({
124
- type: 'function_call_output',
125
- call_id: output.call_id,
126
- output: output.output
127
- }));
128
- const responseRequest = {
129
- input: formattedInputs,
130
- store: true,
131
- };
132
- const conversationIdToUse = runOptions.conversationId || this.conversationId;
133
- if (!conversationIdToUse) {
134
- const createdId = yield this.createConversation();
135
- this.conversationId = createdId;
126
+ // Process response output
127
+ const output = response.output || [];
128
+ const assistantMessages = output.filter((item) => item.type === 'message' && item.role === 'assistant');
129
+ const functionCalls = output.filter((item) => item.type === 'function_call');
130
+ if (functionCalls.length > 0) {
131
+ return {
132
+ requiredActions: JSON.stringify(functionCalls),
133
+ threadId: this.conversationId,
134
+ usageTokens: response.usage,
135
+ };
136
136
  }
137
- responseRequest.conversation = runOptions.conversationId || this.conversationId;
138
- if (this.promptId) {
139
- responseRequest.prompt = {
140
- id: this.promptId
137
+ if (assistantMessages.length > 0) {
138
+ const textContent = assistantMessages
139
+ .flatMap((msg) => msg.content || [])
140
+ .filter((content) => content.type === 'output_text' || content.type === 'text')
141
+ .map((content) => content.text || content.value)
142
+ .filter((text) => text);
143
+ if (textContent.length > 0) {
144
+ return {
145
+ data: textContent.pop(),
146
+ threadId: this.conversationId,
147
+ usageTokens: response.usage,
148
+ };
149
+ }
150
+ }
151
+ if (response.output_text) {
152
+ return {
153
+ data: response.output_text,
154
+ threadId: this.conversationId,
155
+ usageTokens: response.usage,
141
156
  };
142
157
  }
143
- const response = yield openai.responses.create(responseRequest);
144
- this.lastResponseId = response.id;
145
- return this.processResponseOutput(response);
146
- }
147
- catch (error) {
148
- logger_1.default.error('Error handling function outputs:', error);
149
- throw error;
150
- }
151
- });
152
- }
153
- processResponseOutput(response) {
154
- const output = response.output || [];
155
- const assistantMessages = output.filter((item) => item.type === 'message' && item.role === 'assistant');
156
- const functionCalls = output.filter((item) => item.type === 'function_call');
157
- if (functionCalls.length > 0) {
158
- return {
159
- requiredActions: JSON.stringify(functionCalls),
160
- threadId: this.conversationId,
161
- usageTokens: response.usage,
162
- };
163
- }
164
- if (assistantMessages.length > 0) {
165
- const textContent = assistantMessages
166
- .flatMap((msg) => msg.content || [])
167
- .filter((content) => content.type === 'output_text' || content.type === 'text')
168
- .map((content) => content.text || content.value)
169
- .filter((text) => text);
170
- if (textContent) {
171
158
  return {
172
- firstChoice: textContent.pop().replace(/【.*?】/g, ''),
159
+ data: undefined,
173
160
  threadId: this.conversationId,
174
161
  usageTokens: response.usage,
175
162
  };
176
163
  }
177
- }
178
- if (response.output_text) {
179
- return {
180
- firstChoice: response.output_text.replace(/【.*?】/g, ''),
181
- threadId: this.conversationId,
182
- usageTokens: response.usage,
183
- };
184
- }
185
- return {
186
- threadId: this.conversationId,
187
- usageTokens: response.usage,
188
- };
164
+ catch (error) {
165
+ logger_1.default.error('Error in ResponseService.getReply:', error);
166
+ return Promise.reject(error);
167
+ }
168
+ });
189
169
  }
190
170
  reset() {
191
171
  this.lastResponseId = undefined;
@@ -1,5 +1,5 @@
1
1
  import BaseService from '../BaseService/BaseService';
2
- import ChatGptResponse from '../../models/ChatGptResponse';
2
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
3
3
  export declare class TranslationService extends BaseService {
4
4
  constructor(apiKey?: string, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
5
5
  /**
@@ -9,17 +9,17 @@ export declare class TranslationService extends BaseService {
9
9
  * @param toLanguage the desired language to translate to ( English is the default )
10
10
  * @param fromLanguage if we want to specify the language of the text, we can use this ( optional)
11
11
  */
12
- translate(text: string, toLanguage?: string, fromLanguage?: string): Promise<ChatGptResponse | undefined>;
12
+ translate(text: string, toLanguage?: string, fromLanguage?: string): Promise<ChatGPTResponse | undefined>;
13
13
  /**
14
14
  * Detects the ISO language code of a text
15
15
  *
16
16
  * @param text text to detect
17
17
  */
18
- findTextLanguage(text: string): Promise<ChatGptResponse | undefined>;
18
+ findTextLanguage(text: string): Promise<ChatGPTResponse | undefined>;
19
19
  /**
20
20
  * Detects the ISO language code of a text based on frequency of words
21
21
  *
22
22
  * @param text text to detect
23
23
  */
24
- findTextLanguageBasedOnFrequency(text: string): Promise<ChatGptResponse | undefined>;
24
+ findTextLanguageBasedOnFrequency(text: string): Promise<ChatGPTResponse | undefined>;
25
25
  }
@@ -13,8 +13,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.TranslationService = void 0;
16
- const constants_1 = require("../../constants");
17
- const ChatGptMessage_1 = require("../../models/ChatGptMessage");
16
+ const constants_1 = require("../../utils/constants");
17
+ const ChatGPTMessage_1 = require("../../models/ChatGPTMessage");
18
18
  const logger_1 = __importDefault(require("../logger"));
19
19
  const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
20
20
  class TranslationService extends BaseService_1.default {
@@ -42,19 +42,20 @@ class TranslationService extends BaseService_1.default {
42
42
  response_format: { type: 'json_object' },
43
43
  messages: [
44
44
  {
45
- role: ChatGptMessage_1.ChatGptRole.SYSTEM,
45
+ role: ChatGPTMessage_1.ChatGPTRole.SYSTEM,
46
46
  content: constants_1.TRANSLATION_SYSTEM_ROLE,
47
47
  },
48
48
  {
49
- role: ChatGptMessage_1.ChatGptRole.USER,
49
+ role: ChatGPTMessage_1.ChatGPTRole.USER,
50
50
  content: translateText,
51
51
  },
52
52
  ],
53
53
  };
54
54
  try {
55
+ logger_1.default.info({ requestBody }, ':: TranslationServicee.translate :: Translating ::');
55
56
  const response = yield this.request.post('', requestBody);
56
57
  return {
57
- firstChoice: this.getResponseFirstChoice(response),
58
+ data: this.getResponseFirstChoice(response),
58
59
  usageTokens: this.getResponseUsageTokens(response),
59
60
  };
60
61
  }
@@ -80,19 +81,20 @@ class TranslationService extends BaseService_1.default {
80
81
  response_format: { type: 'json_object' },
81
82
  messages: [
82
83
  {
83
- role: ChatGptMessage_1.ChatGptRole.SYSTEM,
84
+ role: ChatGPTMessage_1.ChatGPTRole.SYSTEM,
84
85
  content: constants_1.LANGUAGE_DETECTION_SYSTEM_ROLE,
85
86
  },
86
87
  {
87
- role: ChatGptMessage_1.ChatGptRole.USER,
88
+ role: ChatGPTMessage_1.ChatGPTRole.USER,
88
89
  content: translateText,
89
90
  }
90
91
  ],
91
92
  };
92
93
  try {
94
+ logger_1.default.info({ requestBody }, ':: TranslationServicee.findTextLanguage :: Finding text language ::');
93
95
  const response = yield this.request.post('', requestBody);
94
96
  return {
95
- firstChoice: this.getResponseFirstChoice(response),
97
+ data: this.getResponseFirstChoice(response),
96
98
  usageTokens: this.getResponseUsageTokens(response),
97
99
  };
98
100
  }
@@ -118,19 +120,20 @@ class TranslationService extends BaseService_1.default {
118
120
  response_format: { type: 'json_object' },
119
121
  messages: [
120
122
  {
121
- role: ChatGptMessage_1.ChatGptRole.SYSTEM,
123
+ role: ChatGPTMessage_1.ChatGPTRole.SYSTEM,
122
124
  content: constants_1.WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE,
123
125
  },
124
126
  {
125
- role: ChatGptMessage_1.ChatGptRole.USER,
127
+ role: ChatGPTMessage_1.ChatGPTRole.USER,
126
128
  content: translateText,
127
129
  }
128
130
  ],
129
131
  };
130
132
  try {
133
+ logger_1.default.info({ requestBody }, ':: TranslationServicee.findTextLanguageBasedOnFrequency :: Finding text language ::');
131
134
  const response = yield this.request.post('', requestBody);
132
135
  return {
133
- firstChoice: this.getResponseFirstChoice(response),
136
+ data: this.getResponseFirstChoice(response),
134
137
  usageTokens: this.getResponseUsageTokens(response),
135
138
  };
136
139
  }
@@ -0,0 +1,4 @@
1
+ import { Response } from '../models/Response';
2
+ export declare class ResponseBuilder {
3
+ static build<Data>(data: Data): Response<Data>;
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResponseBuilder = void 0;
4
+ class ResponseBuilder {
5
+ static build(data) {
6
+ return {
7
+ result: data,
8
+ };
9
+ }
10
+ }
11
+ exports.ResponseBuilder = ResponseBuilder;
@@ -0,0 +1,13 @@
1
+ export declare const MESSAGE_SENDER: {
2
+ visitor: string;
3
+ chatbot: string;
4
+ };
5
+ export declare const MESSAGE_TYPES: {
6
+ text: string;
7
+ button: string;
8
+ };
9
+ export declare const DEFAULT_PROMPT = "\n You are a helpful assistant. Your role is to address visitor \n queries related to the ongoing conversation and the last message received. Please ensure \n that responses remain within the ongoing conversation. Politely decline any user queries \n that is not present in the ongoing conversation. \n\n You will always respond with a JSON. It will be in the format: \n\n { answer: '', suggestions: [], connectWithAgent: true or false }\n\n Fields in the JSON:\n\n Answer: It will consist of the user's query answer based on the ongoing conversation. \n Restrict this field to 100 characters.\n\n Suggestions: Request 2 suggestions from user's perspective that he can ask. \n It should be an array of strings. Restrict each suggestion to 50 characters.\n \n ConnectWithAgent: It is a boolean. It should be set to true if the user's query is \n not found in the ongoing conversation. Politely tell the user to talk to a real agent. \n Also when it is true, do not provide any suggestions.\n";
10
+ export declare const DESTINATION_LANGUAGE = "English";
11
+ export declare const LANGUAGE_DETECTION_SYSTEM_ROLE = "\n You are a language detection assistant. Your task is to analyze short text input and determine \n its language using ISO language code. Respond only in JSON format with two key-value pairs: \n - 'languageCode': the detected language's ISO code (or undefined if detection fails).\n - 'isError': a boolean value (true if detection fails, false otherwise).\n";
12
+ export declare const WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = "\n You are a language detection assistant. Your task is to analyze the most frequently occurring words in the given text \n and determine the language based on those words. Respond only in JSON format with two key-value pairs: \n - 'languageCode': the detected language's ISO code (or undefined if detection fails).\n - 'isError': a boolean value (true if detection fails, false otherwise).\n";
13
+ export declare const TRANSLATION_SYSTEM_ROLE = "\n You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:\n - 'translations': it is an array of objects. Each object in the array must contain:\n - 'original': The original input message before translation.\n - 'translation: The translated version of 'original', ensuring it is natural, culturally appropriate, and commonly used by native speakers. The tone should be formal.\n - 'isError': a boolean value (true if translation fails, false otherwise).\n\n **You MUST return the JSON format even if you fail to translate the text. In this case, translations field will be empty array and isError should be true.** \n\n Ensure that 'translation' accurately represents the meaning of 'original' while considering formal tone, cultural nuances, usual expressions, and natural phrasing in the target language.\n\n Do not translate commonly known Latin phrases or expressions (e.g., 'de facto', 'bona fide') when they appear mid-sentence in another language. Leave them exactly as they \n appear in the original text.\n\n Your response must follow this structure strictly.\n";
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TRANSLATION_SYSTEM_ROLE = exports.WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = exports.LANGUAGE_DETECTION_SYSTEM_ROLE = exports.DESTINATION_LANGUAGE = exports.DEFAULT_PROMPT = exports.MESSAGE_TYPES = exports.MESSAGE_SENDER = void 0;
4
+ // Message sender in conversation history
5
+ exports.MESSAGE_SENDER = {
6
+ visitor: 'visitor',
7
+ chatbot: 'chatbot'
8
+ };
9
+ // Message types in conversation history
10
+ exports.MESSAGE_TYPES = {
11
+ text: 'text',
12
+ button: 'button'
13
+ };
14
+ // Default prompt for ChatGPT
15
+ exports.DEFAULT_PROMPT = `
16
+ You are a helpful assistant. Your role is to address visitor
17
+ queries related to the ongoing conversation and the last message received. Please ensure
18
+ that responses remain within the ongoing conversation. Politely decline any user queries
19
+ that is not present in the ongoing conversation.
20
+
21
+ You will always respond with a JSON. It will be in the format:
22
+
23
+ { answer: '', suggestions: [], connectWithAgent: true or false }
24
+
25
+ Fields in the JSON:
26
+
27
+ Answer: It will consist of the user's query answer based on the ongoing conversation.
28
+ Restrict this field to 100 characters.
29
+
30
+ Suggestions: Request 2 suggestions from user's perspective that he can ask.
31
+ It should be an array of strings. Restrict each suggestion to 50 characters.
32
+
33
+ ConnectWithAgent: It is a boolean. It should be set to true if the user's query is
34
+ not found in the ongoing conversation. Politely tell the user to talk to a real agent.
35
+ Also when it is true, do not provide any suggestions.
36
+ `;
37
+ // Default destination language
38
+ exports.DESTINATION_LANGUAGE = 'English';
39
+ // Language detection system role
40
+ exports.LANGUAGE_DETECTION_SYSTEM_ROLE = `
41
+ You are a language detection assistant. Your task is to analyze short text input and determine
42
+ its language using ISO language code. Respond only in JSON format with two key-value pairs:
43
+ - 'languageCode': the detected language's ISO code (or undefined if detection fails).
44
+ - 'isError': a boolean value (true if detection fails, false otherwise).
45
+ `;
46
+ // Word frequency language detection system role
47
+ exports.WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = `
48
+ You are a language detection assistant. Your task is to analyze the most frequently occurring words in the given text
49
+ and determine the language based on those words. Respond only in JSON format with two key-value pairs:
50
+ - 'languageCode': the detected language's ISO code (or undefined if detection fails).
51
+ - 'isError': a boolean value (true if detection fails, false otherwise).
52
+ `;
53
+ // Message translation system role
54
+ exports.TRANSLATION_SYSTEM_ROLE = `
55
+ You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:
56
+ - 'translations': it is an array of objects. Each object in the array must contain:
57
+ - 'original': The original input message before translation.
58
+ - 'translation: The translated version of 'original', ensuring it is natural, culturally appropriate, and commonly used by native speakers. The tone should be formal.
59
+ - 'isError': a boolean value (true if translation fails, false otherwise).
60
+
61
+ **You MUST return the JSON format even if you fail to translate the text. In this case, translations field will be empty array and isError should be true.**
62
+
63
+ Ensure that 'translation' accurately represents the meaning of 'original' while considering formal tone, cultural nuances, usual expressions, and natural phrasing in the target language.
64
+
65
+ Do not translate commonly known Latin phrases or expressions (e.g., 'de facto', 'bona fide') when they appear mid-sentence in another language. Leave them exactly as they
66
+ appear in the original text.
67
+
68
+ Your response must follow this structure strictly.
69
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobrowser/chatgpt",
3
- "version": "0.7.32-dev.1",
3
+ "version": "0.7.32-dev.3",
4
4
  "description": "chatgpt services to connect our projects with chatgpt api",
5
5
  "keywords": [
6
6
  "chatgpt",
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@cobrowser/logger": "^2.0.6",
25
+ "@cobrowser/xss-validation": "^2.0.1",
25
26
  "axios": "^1.6.1",
26
27
  "axios-mock-adapter": "^1.22.0",
27
28
  "openai": "^6.3.0"
@@ -39,5 +40,5 @@
39
40
  "bugs": {
40
41
  "url": "https://bitbucket.org/cobrowser/cb_utils/issues"
41
42
  },
42
- "gitHead": "c957b03c571a10f77b380ca9c400eeb2eac9764e"
43
+ "gitHead": "a3466cedd1e54bb75ff0953d0452a053cacd5451"
43
44
  }