@cobrowser/chatgpt 0.7.3 → 0.7.5

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.
@@ -5,6 +5,9 @@ export declare enum ChatGptRole {
5
5
  }
6
6
  export interface ChatGptRequestBody {
7
7
  model: string;
8
+ response_format?: {
9
+ type: string;
10
+ };
8
11
  messages: ChatGptMessage[];
9
12
  }
10
13
  interface ChatGptMessage {
@@ -0,0 +1,6 @@
1
+ import ChatGptUsageTokens from './ChatGptUsageTokens';
2
+ interface ChatGptResponse {
3
+ firstChoice?: string;
4
+ usageTokens?: ChatGptUsageTokens;
5
+ }
6
+ 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,21 +1,30 @@
1
1
  import { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
2
+ import ChatGptUsageTokens from '../../models/ChatGptUsageTokens';
2
3
  declare class BaseService {
3
4
  request: AxiosInstance;
4
5
  readonly BASE_CHAT_COMPLETION_URL = "https://api.openai.com/v1/chat/completions";
6
+ openaiApiKey: string | undefined;
5
7
  chatGptModel: string;
6
8
  maxNumberOfChatCompletionResult: number;
7
9
  /**
8
10
  *
9
- * @param apiUrl Api url to be used to make API calls to openAI services
11
+ * @param apiKey
10
12
  * @param model the main chatGPT model to use
13
+ * @param apiUrl Api url to be used to make API calls to openAI services
11
14
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
15
+ *
12
16
  */
13
- constructor(apiUrl?: string, model?: string, maxNumberOfChoices?: number, apiKey?: string | undefined);
17
+ constructor(apiKey?: string | undefined, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
14
18
  /**
15
19
  * Filter the response from chatGpt API and only get the first choice
16
20
  * @param response
17
21
  */
18
22
  getResponseFirstChoice(response: AxiosResponse): string | undefined;
23
+ /**
24
+ * Filter the response from chatGpt API and get the usage tokens
25
+ * @param response
26
+ */
27
+ getResponseUsageTokens(response: AxiosResponse): ChatGptUsageTokens | undefined;
19
28
  /**
20
29
  * function to handle requests errors
21
30
  * @param error
@@ -8,11 +8,13 @@ const logger_1 = __importDefault(require("../logger"));
8
8
  class BaseService {
9
9
  /**
10
10
  *
11
- * @param apiUrl Api url to be used to make API calls to openAI services
11
+ * @param apiKey
12
12
  * @param model the main chatGPT model to use
13
+ * @param apiUrl Api url to be used to make API calls to openAI services
13
14
  * @param maxNumberOfChoices How many chat completion choices to generate for each request.
15
+ *
14
16
  */
15
- constructor(apiUrl, model = 'gpt-3.5-turbo', maxNumberOfChoices = 1, apiKey = process.env.OPEN_AI_API_KEY) {
17
+ constructor(apiKey = process.env.OPEN_AI_API_KEY, model = 'gpt-3.5-turbo', apiUrl, maxNumberOfChoices = 1) {
16
18
  Object.defineProperty(this, "request", {
17
19
  enumerable: true,
18
20
  configurable: true,
@@ -25,6 +27,12 @@ class BaseService {
25
27
  writable: true,
26
28
  value: 'https://api.openai.com/v1/chat/completions'
27
29
  });
30
+ Object.defineProperty(this, "openaiApiKey", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: void 0
35
+ });
28
36
  Object.defineProperty(this, "chatGptModel", {
29
37
  enumerable: true,
30
38
  configurable: true,
@@ -44,6 +52,7 @@ class BaseService {
44
52
  Authorization: `Bearer ${apiKey} `,
45
53
  },
46
54
  });
55
+ this.openaiApiKey = apiKey;
47
56
  this.chatGptModel = model;
48
57
  this.maxNumberOfChatCompletionResult = maxNumberOfChoices;
49
58
  }
@@ -60,6 +69,19 @@ class BaseService {
60
69
  }
61
70
  return undefined;
62
71
  }
72
+ /**
73
+ * Filter the response from chatGpt API and get the usage tokens
74
+ * @param response
75
+ */
76
+ getResponseUsageTokens(response) {
77
+ var _a;
78
+ if ((_a = response.data) === null || _a === void 0 ? void 0 : _a.usage) {
79
+ const usage = response.data.usage;
80
+ logger_1.default.info(`ChatGpt response usage tokens: ${JSON.stringify(usage)}`);
81
+ return usage;
82
+ }
83
+ return undefined;
84
+ }
63
85
  /**
64
86
  * function to handle requests errors
65
87
  * @param error
@@ -3,6 +3,6 @@ import ConversationHistory from 'src/models/ConversationHistory';
3
3
  export declare class ChatService extends BaseService {
4
4
  #private;
5
5
  model: string | undefined;
6
- constructor(apiUrl?: string, model?: string, maxNumberOfChoices?: number, apiKey?: string);
6
+ constructor(apiKey?: string, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
7
7
  ask(context: string | undefined, conversationHistory: ConversationHistory[]): Promise<string | undefined>;
8
8
  }
@@ -24,8 +24,8 @@ const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
24
24
  const logger_1 = __importDefault(require("../logger"));
25
25
  const constants_1 = require("../../constants");
26
26
  class ChatService extends BaseService_1.default {
27
- constructor(apiUrl, model, maxNumberOfChoices, apiKey) {
28
- super(apiUrl, model, maxNumberOfChoices, apiKey);
27
+ constructor(apiKey, model, apiUrl, maxNumberOfChoices) {
28
+ super(apiKey, model, apiUrl, maxNumberOfChoices);
29
29
  _ChatService_instances.add(this);
30
30
  Object.defineProperty(this, "model", {
31
31
  enumerable: true,
@@ -1,8 +1,10 @@
1
+ import ChatGptResponse from '../../models/ChatGptResponse';
1
2
  import BaseService from '../BaseService/BaseService';
2
3
  import ChatGptMessage from '../../models/ChatGptMessage';
3
4
  import Item from '../../models/Item';
4
5
  export declare class CopilotService extends BaseService {
5
- constructor(apiUrl?: string, model?: string, maxNumberOfChoices?: number, apiKey?: string);
6
+ assistantId: string | undefined;
7
+ constructor(apiKey?: string, model?: string, assistantId?: string, apiUrl?: string, maxNumberOfChoices?: number);
6
8
  /**
7
9
  * a method to be used to suggest some quick replies for the agent based on the conversation
8
10
  * will be added as a single string in the format
@@ -15,11 +17,17 @@ export declare class CopilotService extends BaseService {
15
17
  * this will make it easy to ask chatGpt for quick replies
16
18
  * @param conversation
17
19
  */
18
- suggest(conversation: string): Promise<string | undefined>;
20
+ suggest(conversation: string): Promise<ChatGptResponse | undefined>;
19
21
  /**
20
22
  * this will request chatGPT to recommend product or item based on the whole conversation
21
23
  * @param conversation the whole chat conversation
22
24
  * @param items the items to recommend from
23
25
  */
24
26
  recommend(conversation: ChatGptMessage[], items: Item[]): Promise<string | undefined>;
27
+ /**
28
+ * Get the reply of the assistant if the assistant_id is available
29
+ *
30
+ * @param message
31
+ */
32
+ getAssistantReply(message: string): Promise<string | undefined>;
25
33
  }
@@ -16,9 +16,17 @@ exports.CopilotService = void 0;
16
16
  const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
17
17
  const logger_1 = __importDefault(require("../logger"));
18
18
  const ChatGptMessage_1 = require("../../models/ChatGptMessage");
19
+ const openai_1 = __importDefault(require("openai"));
19
20
  class CopilotService extends BaseService_1.default {
20
- constructor(apiUrl, model, maxNumberOfChoices, apiKey) {
21
- super(apiUrl, model, maxNumberOfChoices, apiKey);
21
+ constructor(apiKey, model, assistantId, apiUrl, maxNumberOfChoices) {
22
+ super(apiKey, model, apiUrl, maxNumberOfChoices);
23
+ Object.defineProperty(this, "assistantId", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ this.assistantId = assistantId;
22
30
  }
23
31
  /**
24
32
  * a method to be used to suggest some quick replies for the agent based on the conversation
@@ -39,21 +47,37 @@ class CopilotService extends BaseService_1.default {
39
47
  return Promise.reject(new Error('conversation must be provided'));
40
48
  }
41
49
  try {
50
+ if (this.assistantId) {
51
+ // Here we get the last customer message as we dont need the whole conversation
52
+ // as we are doing with the other approach
53
+ const conversationArray = conversation.split('\n');
54
+ const lastCustomerMessage = conversationArray.reverse().find(e => e.includes('customer:'));
55
+ if (!lastCustomerMessage) {
56
+ logger_1.default.error('last customer message not found');
57
+ return Promise.reject(new Error('last customer message not found'));
58
+ }
59
+ const assistantReply = yield this.getAssistantReply(lastCustomerMessage);
60
+ return {
61
+ firstChoice: assistantReply
62
+ };
63
+ }
42
64
  // main prompt used to instruct chatGPT to suggest some quick replies
43
65
  const mainPrompt = `Propose some quick replies for the agent to be used on the following conversation
44
- display your response as unordered list:-\n${conversation}`;
66
+ display your response as unordered list:-\n${conversation}`,
45
67
  // main prompt will be the whole conversation as string
46
- const mainPromptMessage = {
68
+ mainPromptMessage = {
47
69
  role: ChatGptMessage_1.ChatGptRole.USER,
48
70
  content: mainPrompt,
49
- };
71
+ },
50
72
  // the body we need to send to the request
51
- const requestBody = {
73
+ requestBody = {
52
74
  model: this.chatGptModel,
53
75
  messages: [mainPromptMessage],
76
+ }, response = yield this.request.post('', requestBody);
77
+ return {
78
+ firstChoice: this.getResponseFirstChoice(response),
79
+ usageTokens: this.getResponseUsageTokens(response),
54
80
  };
55
- const response = yield this.request.post('', requestBody);
56
- return this.getResponseFirstChoice(response);
57
81
  }
58
82
  catch (e) {
59
83
  this.handleErrors(e);
@@ -74,18 +98,17 @@ class CopilotService extends BaseService_1.default {
74
98
  }
75
99
  try {
76
100
  // main prompt used to instruct chatGPT to recommend products
77
- const mainPrompt = `we have this list of Items: ${JSON.stringify(items)}, what to recommend for you`;
101
+ const mainPrompt = `we have this list of Items: ${JSON.stringify(items)}, what to recommend for you`,
78
102
  // main prompt will be the first message of the whole conversation
79
- const firstMessage = {
103
+ firstMessage = {
80
104
  role: ChatGptMessage_1.ChatGptRole.ASSISTANT,
81
105
  content: mainPrompt,
82
- };
106
+ },
83
107
  // the body we need to send to the request
84
- const requestBody = {
108
+ requestBody = {
85
109
  model: this.chatGptModel,
86
110
  messages: [firstMessage, ...conversation],
87
- };
88
- const response = yield this.request.post('', requestBody);
111
+ }, response = yield this.request.post('', requestBody);
89
112
  return this.getResponseFirstChoice(response);
90
113
  }
91
114
  catch (e) {
@@ -94,5 +117,33 @@ class CopilotService extends BaseService_1.default {
94
117
  }
95
118
  });
96
119
  }
120
+ /**
121
+ * Get the reply of the assistant if the assistant_id is available
122
+ *
123
+ * @param message
124
+ */
125
+ getAssistantReply(message) {
126
+ var _a;
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ const openai = new openai_1.default({ apiKey: this.openaiApiKey });
129
+ let reply = undefined;
130
+ const thread = yield openai.beta.threads.create();
131
+ yield openai.beta.threads.messages.create(thread.id, { role: "user", content: message });
132
+ let run = yield openai.beta.threads.runs.create(thread.id, { assistant_id: (_a = this.assistantId) !== null && _a !== void 0 ? _a : '' });
133
+ while (['queued', 'in_progress', 'cancelling'].includes(run.status)) {
134
+ yield new Promise(resolve => setTimeout(resolve, 500));
135
+ run = yield openai.beta.threads.runs.retrieve(run.thread_id, run.id);
136
+ }
137
+ if (run.status === 'completed') {
138
+ const { data } = yield openai.beta.threads.messages.list(run.thread_id);
139
+ const messages = data.flatMap((message) => message === null || message === void 0 ? void 0 : message.content.map((content) => ((content.type === 'text' && message.role === 'assistant') ? content.text : '')));
140
+ const assistantMessage = messages.find(message => typeof message === 'object' && 'value' in message && typeof message.value === 'string');
141
+ if (assistantMessage) {
142
+ reply = assistantMessage.value;
143
+ }
144
+ }
145
+ return reply;
146
+ });
147
+ }
97
148
  }
98
149
  exports.CopilotService = CopilotService;
@@ -1,11 +1,16 @@
1
+ import ChatGptResponse from '../../models/ChatGptResponse';
1
2
  import BaseService from '../BaseService/BaseService';
2
3
  export declare class TranslationService extends BaseService {
3
- constructor(apiUrl?: string, model?: string, maxNumberOfChoices?: number, apiKey?: string);
4
+ SYSTEM_ROLE_MESSAGE: string;
5
+ FIND_TEXT_LANGUAGE_SYSTEM_ROLE: string;
6
+ constructor(apiKey?: string, model?: string, apiUrl?: string, maxNumberOfChoices?: number);
4
7
  /**
5
8
  * request the chatGpt Api to translate a specific test to a desired language
6
9
  * @param text the text to translate
7
10
  * @param toLanguage the desired language to translate to ( English is the default )
8
11
  * @param fromLanguage if we want to specify the language of the text, we can use this ( optional)
9
12
  */
10
- translate(text: string, toLanguage?: string, fromLanguage?: string): Promise<string | undefined>;
13
+ translate(text: string, toLanguage?: string, fromLanguage?: string): Promise<ChatGptResponse | undefined>;
14
+ findTextLanguage(text: string): Promise<ChatGptResponse | undefined>;
15
+ getFindTextLanguagePromptMessage(text: string): string;
11
16
  }
@@ -18,8 +18,26 @@ const BaseService_1 = __importDefault(require("../BaseService/BaseService"));
18
18
  const logger_1 = __importDefault(require("../logger"));
19
19
  const constants_1 = require("../../constants");
20
20
  class TranslationService extends BaseService_1.default {
21
- constructor(apiUrl, model, maxNumberOfChoices, apiKey) {
22
- super(apiUrl, model, maxNumberOfChoices, apiKey);
21
+ constructor(apiKey, model, apiUrl, maxNumberOfChoices) {
22
+ super(apiKey, model, apiUrl, maxNumberOfChoices);
23
+ Object.defineProperty(this, "SYSTEM_ROLE_MESSAGE", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: 'You are a translator that provides a response in JSON format only. '
28
+ + 'The response should be a JSON object with a boolean called \'error\' and \'data,\''
29
+ + ' which is an array of objects. Each object in the array will contain a \'translation\' property, '
30
+ + 'holding the translation of the message, and a \'translated_message\' property, '
31
+ + 'which will contain the original message before being translated.'
32
+ + ' Additionally, the \'translation\' property will hold the translation of the \'translated_message\' property.'
33
+ });
34
+ Object.defineProperty(this, "FIND_TEXT_LANGUAGE_SYSTEM_ROLE", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: 'You are a translator that provides a response in JSON format only—a JSON that contains'
39
+ + ' \'languageCode\' and \'isError\' if you cannot detect the language.'
40
+ });
23
41
  }
24
42
  /**
25
43
  * request the chatGpt Api to translate a specific test to a desired language
@@ -39,14 +57,24 @@ class TranslationService extends BaseService_1.default {
39
57
  // the body we need to send to the request
40
58
  requestBody = {
41
59
  model: this.chatGptModel,
42
- messages: [{
60
+ response_format: { type: 'json_object' },
61
+ messages: [
62
+ {
63
+ role: ChatGptMessage_1.ChatGptRole.SYSTEM,
64
+ content: this.SYSTEM_ROLE_MESSAGE,
65
+ },
66
+ {
43
67
  role: ChatGptMessage_1.ChatGptRole.USER,
44
68
  content: translateText,
45
- }],
69
+ },
70
+ ],
46
71
  };
47
72
  try {
48
73
  const response = yield this.request.post('', requestBody);
49
- return this.getResponseFirstChoice(response);
74
+ return {
75
+ firstChoice: this.getResponseFirstChoice(response),
76
+ usageTokens: this.getResponseUsageTokens(response),
77
+ };
50
78
  }
51
79
  catch (e) {
52
80
  this.handleErrors(e);
@@ -54,5 +82,45 @@ class TranslationService extends BaseService_1.default {
54
82
  }
55
83
  });
56
84
  }
85
+ findTextLanguage(text) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ if (!text) {
88
+ logger_1.default.error('translation text should be provided');
89
+ return Promise.reject(new Error('text must be provided'));
90
+ }
91
+ // instruction on what to do with the text
92
+ const translateText = this.getFindTextLanguagePromptMessage(text),
93
+ // the body we need to send to the request
94
+ requestBody = {
95
+ model: this.chatGptModel,
96
+ response_format: { type: 'json_object' },
97
+ messages: [
98
+ {
99
+ role: ChatGptMessage_1.ChatGptRole.SYSTEM,
100
+ content: this.FIND_TEXT_LANGUAGE_SYSTEM_ROLE,
101
+ },
102
+ {
103
+ role: ChatGptMessage_1.ChatGptRole.USER,
104
+ content: translateText,
105
+ }
106
+ ],
107
+ };
108
+ try {
109
+ const response = yield this.request.post('', requestBody);
110
+ return {
111
+ firstChoice: this.getResponseFirstChoice(response),
112
+ usageTokens: this.getResponseUsageTokens(response),
113
+ };
114
+ }
115
+ catch (e) {
116
+ this.handleErrors(e);
117
+ return Promise.reject(e);
118
+ }
119
+ });
120
+ }
121
+ getFindTextLanguagePromptMessage(text) {
122
+ return `In which language is this text in '${text}'? The response should be {'languageCode': 'language_code_here', 'isError': 'true or false based on whether you did find the language code or not'}.
123
+ Other responses or explanations are not allowed.`;
124
+ }
57
125
  }
58
126
  exports.TranslationService = TranslationService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobrowser/chatgpt",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "chatgpt services to connect our projects with chatgpt api",
5
5
  "keywords": [
6
6
  "chatgpt",
@@ -23,7 +23,8 @@
23
23
  "dependencies": {
24
24
  "@cobrowser/logger": "^1.0.0",
25
25
  "axios": "^1.6.1",
26
- "axios-mock-adapter": "^1.22.0"
26
+ "axios-mock-adapter": "^1.22.0",
27
+ "openai": "^4.32.0"
27
28
  },
28
29
  "directories": {
29
30
  "dist": "dist"
@@ -38,5 +39,5 @@
38
39
  "bugs": {
39
40
  "url": "https://bitbucket.org/cobrowser/cb_utils/issues"
40
41
  },
41
- "gitHead": "a47a44686f48c6618b3570470c46c9a8cb0e8d68"
42
+ "gitHead": "5a4bff51323eed4e0c17a865e7783d8dfc0323e1"
42
43
  }