@cobrowser/chatgpt 0.7.30 → 0.7.31-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,4 +10,4 @@ export declare const DEFAULT_PROMPT = "\n You are a helpful assistant. Your rol
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 **You MUST return the JSON format even if you fail to translate the text. In this case, data field will be empty array and isError should be true.** \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\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
@@ -53,14 +53,14 @@ exports.WORD_FREQUENCY_LANGUAGE_DETECTION_SYSTEM_ROLE = `
53
53
  // Message translation system role
54
54
  exports.TRANSLATION_SYSTEM_ROLE = `
55
55
  You are a translator that responds exclusively in JSON format. Your response must be a JSON object containing:
56
- - 'data': it is an array of objects. Each object in the array must contain:
57
- - 'translated_message': The original input message before translation.
58
- - '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.
59
59
  - 'isError': a boolean value (true if translation fails, false otherwise).
60
60
 
61
- **You MUST return the JSON format even if you fail to translate the text. In this case, data field will be empty array and isError should be true.**
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
62
 
63
- Ensure that 'translation' accurately represents the meaning of 'translated_message' while considering cultural nuances, usual expressions, and natural phrasing in the target language.
63
+ Ensure that 'translation' accurately represents the meaning of 'original' while considering cultural nuances, usual expressions, and natural phrasing in the target language.
64
64
 
65
65
  Your response must follow this structure strictly.
66
66
  `;
@@ -0,0 +1,8 @@
1
+ import ChatGPTUsageTokens from './ChatGPTUsageTokens';
2
+ interface ChatGPTResponse {
3
+ data?: string;
4
+ usageTokens?: ChatGPTUsageTokens;
5
+ threadId?: string;
6
+ requiredActions?: string;
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,5 +1,5 @@
1
1
  import { AssistantResponseFormatOption } from 'openai/resources/beta/threads/threads';
2
- import ChatGptResponse from '../../models/ChatGptResponse';
2
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
3
3
  import BaseService from '../BaseService/BaseService';
4
4
  export declare class AssistantService extends BaseService {
5
5
  assistantId: 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
  }
@@ -99,9 +99,9 @@ class AssistantService extends BaseService_1.default {
99
99
  const messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : '')));
100
100
  const assistantMessages = messages.filter(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
101
101
  if (assistantMessages.length) {
102
- let answer = assistantMessages.map(message => this.output(message.value, true)).join('\n');
102
+ let answer = assistantMessages.map(message => message.value).join('\n');
103
103
  return {
104
- firstChoice: answer.replace(/【.*?】/g, ''),
104
+ data: answer.replace(/【.*?】/g, ''),
105
105
  threadId: run.thread_id,
106
106
  usageTokens: run.usage,
107
107
  };
@@ -1,20 +1,30 @@
1
1
  import { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
2
- import ChatGptUsageTokens from '../../models/ChatGptUsageTokens';
2
+ import ChatGPTUsageTokens from '../../models/ChatGPTUsageTokens';
3
+ interface Response<Data = any> {
4
+ result: Data;
5
+ }
3
6
  declare class BaseService {
4
- request: AxiosInstance;
5
7
  readonly BASE_CHAT_COMPLETION_URL = "https://api.openai.com/v1/chat/completions";
8
+ request: AxiosInstance;
6
9
  openaiApiKey: string | undefined;
7
10
  chatGptModel: string;
8
11
  maxNumberOfChatCompletionResult: number;
9
12
  /**
10
- *
11
13
  * @param apiKey
12
14
  * @param model the main chatGPT model to use
13
15
  * @param apiUrl Api url to be used to make API calls to openAI services
14
16
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
15
- *
16
17
  */
17
18
  constructor(apiKey?: string | undefined, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
19
+ /**
20
+ * Automatically wraps all async methods to sanitize their return values
21
+ * This ensures ALL service methods are automatically sanitized without any developer intervention
22
+ */
23
+ private processAsyncMethods;
24
+ /**
25
+ * Recursively sanitizes any value
26
+ */
27
+ private clean;
18
28
  /**
19
29
  * Filter the response from chatGpt API and only get the first choice
20
30
  * @param response
@@ -24,17 +34,12 @@ declare class BaseService {
24
34
  * Filter the response from chatGpt API and get the usage tokens
25
35
  * @param response
26
36
  */
27
- getResponseUsageTokens(response: AxiosResponse): ChatGptUsageTokens | undefined;
37
+ getResponseUsageTokens(response: AxiosResponse): ChatGPTUsageTokens | undefined;
28
38
  /**
29
39
  * function to handle requests errors
30
40
  * @param error
31
41
  */
32
42
  handleErrors(error: AxiosError): void;
33
- /**
34
- * Sanitize ChatGPT response with our XSS validation package
35
- *
36
- * @param data
37
- */
38
- output(data: any, shouldStringify?: boolean): any;
43
+ protected response<Data>(data: Data): Response<Data>;
39
44
  }
40
45
  export default BaseService;
@@ -1,4 +1,13 @@
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
  };
@@ -8,25 +17,23 @@ const xss_validation_1 = require("@cobrowser/xss-validation");
8
17
  const logger_1 = __importDefault(require("../logger"));
9
18
  class BaseService {
10
19
  /**
11
- *
12
20
  * @param apiKey
13
21
  * @param model the main chatGPT model to use
14
22
  * @param apiUrl Api url to be used to make API calls to openAI services
15
23
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
16
- *
17
24
  */
18
25
  constructor(apiKey = process.env.OPEN_AI_API_KEY, model = 'gpt-4o', apiUrl, maxNumberOfChoices = 1) {
19
- Object.defineProperty(this, "request", {
26
+ Object.defineProperty(this, "BASE_CHAT_COMPLETION_URL", {
20
27
  enumerable: true,
21
28
  configurable: true,
22
29
  writable: true,
23
- value: void 0
30
+ value: 'https://api.openai.com/v1/chat/completions'
24
31
  });
25
- Object.defineProperty(this, "BASE_CHAT_COMPLETION_URL", {
32
+ Object.defineProperty(this, "request", {
26
33
  enumerable: true,
27
34
  configurable: true,
28
35
  writable: true,
29
- value: 'https://api.openai.com/v1/chat/completions'
36
+ value: void 0
30
37
  });
31
38
  Object.defineProperty(this, "openaiApiKey", {
32
39
  enumerable: true,
@@ -56,6 +63,64 @@ class BaseService {
56
63
  this.openaiApiKey = apiKey;
57
64
  this.chatGptModel = model;
58
65
  this.maxNumberOfChatCompletionResult = maxNumberOfChoices;
66
+ // Automatically wrap all async methods that return ChatGpt responses
67
+ this.processAsyncMethods();
68
+ }
69
+ /**
70
+ * Automatically wraps all async methods to sanitize their return values
71
+ * This ensures ALL service methods are automatically sanitized without any developer intervention
72
+ */
73
+ processAsyncMethods() {
74
+ const proto = Object.getPrototypeOf(this), methodNames = Object.getOwnPropertyNames(proto);
75
+ console.log('BASE SERVICE processAsyncMethods methodNames: ', methodNames);
76
+ for (const methodName of methodNames) {
77
+ const method = proto[methodName];
78
+ console.log('BASE SERVICE processAsyncMethods method: ', method);
79
+ // Skip constructor, non-functions, and utility methods
80
+ // But include service methods like getReply, getAssistant, etc.
81
+ if (methodName === 'constructor' ||
82
+ typeof method !== 'function' ||
83
+ (methodName.startsWith('get') && !methodName.startsWith('getReply') && !methodName.startsWith('getAssist')) ||
84
+ methodName.startsWith('handle') ||
85
+ methodName.includes('Sanitize') ||
86
+ methodName.includes('sanitize')) {
87
+ continue;
88
+ }
89
+ const originalMethod = method.bind(this);
90
+ // Replace the method with a sanitizing wrapper
91
+ this[methodName] = (...args) => __awaiter(this, void 0, void 0, function* () {
92
+ try {
93
+ const result = this.response(yield originalMethod(...args)), sanitizedResult = this.clean(result);
94
+ console.log('BASE SERVICE processAsyncMethods sanitizedResult: ', sanitizedResult);
95
+ return sanitizedResult;
96
+ }
97
+ catch (error) {
98
+ throw error;
99
+ }
100
+ });
101
+ }
102
+ }
103
+ /**
104
+ * Recursively sanitizes any value
105
+ */
106
+ clean(value) {
107
+ console.log('BASE SERVICE clean value: ', value);
108
+ if (!value) {
109
+ return;
110
+ }
111
+ // Data could be plain string or JSON string.
112
+ if (typeof value === 'string') {
113
+ try {
114
+ value = JSON.parse(value);
115
+ }
116
+ catch (error) { }
117
+ }
118
+ const sanitized = xss_validation_1.XSSProtector.sanitize(value), encoded = xss_validation_1.XSSProtector.encode(sanitized);
119
+ console.log('BASE SERVICE clean sanitized sssss: ', sanitized);
120
+ console.log('BASE SERVICE clean encoded: ', encoded);
121
+ console.log('BASE SERVICE clean encoded data: ', encoded.result.data);
122
+ logger_1.default.info({ value, sanitized, encoded }, ':: Sanitized Response ::');
123
+ return encoded;
59
124
  }
60
125
  /**
61
126
  * Filter the response from chatGpt API and only get the first choice
@@ -66,7 +131,7 @@ class BaseService {
66
131
  if ((_a = response.data.choices) === null || _a === void 0 ? void 0 : _a.length) {
67
132
  const firstChatGptResponseMessage = response.data.choices[0].message;
68
133
  logger_1.default.info(`First ChatGpt Message: ${firstChatGptResponseMessage.content}`);
69
- return firstChatGptResponseMessage.content;
134
+ return JSON.parse(firstChatGptResponseMessage.content);
70
135
  }
71
136
  return undefined;
72
137
  }
@@ -100,25 +165,10 @@ class BaseService {
100
165
  }
101
166
  logger_1.default.error(`error message ${error.message}`);
102
167
  }
103
- /**
104
- * Sanitize ChatGPT response with our XSS validation package
105
- *
106
- * @param data
107
- */
108
- output(data, shouldStringify = false) {
109
- if (!data) {
110
- return;
111
- }
112
- // Data could be plain string or JSON string.
113
- if (typeof data === 'string') {
114
- try {
115
- data = JSON.parse(data);
116
- }
117
- catch (error) { }
118
- }
119
- const sanitized = xss_validation_1.XSSProtector.sanitize(data), encoded = xss_validation_1.XSSProtector.encode(sanitized);
120
- logger_1.default.info({ data, sanitized, encoded }, ':: Sanitized Response ::');
121
- return shouldStringify ? JSON.stringify(encoded) : encoded;
168
+ response(data) {
169
+ return {
170
+ result: data,
171
+ };
122
172
  }
123
173
  }
124
174
  exports.default = BaseService;
@@ -48,7 +48,7 @@ class ChatService extends BaseService_1.default {
48
48
  logger_1.default.info(`:: ChatService.ask :: ${JSON.stringify(requestBody)}`);
49
49
  try {
50
50
  const response = yield this.request.post('', requestBody);
51
- return this.output(this.getResponseFirstChoice(response), true);
51
+ return this.getResponseFirstChoice(response);
52
52
  }
53
53
  catch (e) {
54
54
  this.handleErrors(e);
@@ -1,7 +1,7 @@
1
1
  import ChatGptMessage from '../../models/ChatGptMessage';
2
2
  import Item from '../../models/Item';
3
3
  import BaseService from '../BaseService/BaseService';
4
- import ChatGptResponse from '../../models/ChatGptResponse';
4
+ import ChatGPTResponse from '../../models/ChatGPTResponse';
5
5
  export declare class CopilotService extends BaseService {
6
6
  assistantId: string | undefined;
7
7
  threadId: string | undefined;
@@ -18,17 +18,17 @@ export declare class CopilotService extends BaseService {
18
18
  * this will make it easy to ask chatGpt for quick replies
19
19
  * @param conversation
20
20
  */
21
- suggest(conversation: string): Promise<ChatGptResponse | undefined>;
21
+ suggest(conversation: string): Promise<ChatGPTResponse | undefined>;
22
22
  /**
23
23
  * this will request chatGPT to recommend product or item based on the whole conversation
24
24
  * @param conversation the whole chat conversation
25
25
  * @param items the items to recommend from
26
26
  */
27
- recommend(conversation: ChatGptMessage[], items: Item[]): Promise<any>;
27
+ recommend(conversation: ChatGptMessage[], items: Item[]): Promise<string | undefined>;
28
28
  /**
29
29
  * Get the reply of the assistant if the assistant_id is available
30
30
  *
31
31
  * @param message
32
32
  */
33
- getAssistantReply(message: string): Promise<ChatGptResponse | undefined>;
33
+ getAssistantReply(message: string): Promise<ChatGPTResponse | undefined>;
34
34
  }
@@ -80,7 +80,7 @@ class CopilotService extends BaseService_1.default {
80
80
  messages: [mainPromptMessage],
81
81
  }, response = yield this.request.post('', requestBody);
82
82
  return {
83
- firstChoice: this.output(this.getResponseFirstChoice(response), true),
83
+ data: this.getResponseFirstChoice(response),
84
84
  usageTokens: this.getResponseUsageTokens(response),
85
85
  };
86
86
  }
@@ -114,7 +114,7 @@ class CopilotService extends BaseService_1.default {
114
114
  model: this.chatGptModel,
115
115
  messages: [firstMessage, ...conversation],
116
116
  }, response = yield this.request.post('', requestBody);
117
- return this.output(this.getResponseFirstChoice(response), true);
117
+ return this.getResponseFirstChoice(response);
118
118
  }
119
119
  catch (e) {
120
120
  this.handleErrors(e);
@@ -158,7 +158,7 @@ class CopilotService extends BaseService_1.default {
158
158
  if (assistantMessages.length) {
159
159
  let answers = assistantMessages.map(message => message.value);
160
160
  return {
161
- firstChoice: this.output((answers), true),
161
+ data: JSON.stringify(answers),
162
162
  threadId: run.thread_id,
163
163
  usageTokens: run.usage,
164
164
  };
@@ -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
  }
@@ -55,9 +55,10 @@ class TranslationService extends BaseService_1.default {
55
55
  logger_1.default.info({ requestBody }, ':: TranslationServicee.translate :: Translating ::');
56
56
  const response = yield this.request.post('', requestBody);
57
57
  return {
58
- firstChoice: this.output(this.getResponseFirstChoice(response), true),
58
+ data: this.getResponseFirstChoice(response),
59
59
  usageTokens: this.getResponseUsageTokens(response),
60
60
  };
61
+ // return response.data;
61
62
  }
62
63
  catch (e) {
63
64
  this.handleErrors(e);
@@ -94,7 +95,7 @@ class TranslationService extends BaseService_1.default {
94
95
  logger_1.default.info({ requestBody }, ':: TranslationServicee.findTextLanguage :: Finding text language ::');
95
96
  const response = yield this.request.post('', requestBody);
96
97
  return {
97
- firstChoice: this.output(this.getResponseFirstChoice(response), true),
98
+ data: this.getResponseFirstChoice(response),
98
99
  usageTokens: this.getResponseUsageTokens(response),
99
100
  };
100
101
  }
@@ -133,7 +134,7 @@ class TranslationService extends BaseService_1.default {
133
134
  logger_1.default.info({ requestBody }, ':: TranslationServicee.findTextLanguageBasedOnFrequency :: Finding text language ::');
134
135
  const response = yield this.request.post('', requestBody);
135
136
  return {
136
- firstChoice: this.output(this.getResponseFirstChoice(response), true),
137
+ data: this.getResponseFirstChoice(response),
137
138
  usageTokens: this.getResponseUsageTokens(response),
138
139
  };
139
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobrowser/chatgpt",
3
- "version": "0.7.30",
3
+ "version": "0.7.31-beta.1",
4
4
  "description": "chatgpt services to connect our projects with chatgpt api",
5
5
  "keywords": [
6
6
  "chatgpt",
@@ -22,7 +22,6 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@cobrowser/logger": "^2.0.2",
25
- "@cobrowser/xss-validation": "^2.0.1",
26
25
  "axios": "^1.6.1",
27
26
  "axios-mock-adapter": "^1.22.0",
28
27
  "openai": "^4.86.2"
@@ -40,5 +39,5 @@
40
39
  "bugs": {
41
40
  "url": "https://bitbucket.org/cobrowser/cb_utils/issues"
42
41
  },
43
- "gitHead": "c8747e61dff13093fa0593dd4a3219ff11eb7b82"
42
+ "gitHead": "4f4def1cb16e7e16058f1a50902551b1b1d35d47"
44
43
  }