@cobrowser/chatgpt 0.7.40 → 0.7.42-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.
@@ -17,21 +17,22 @@ export declare class CopilotService extends BaseService {
17
17
  *
18
18
  * this will make it easy to ask chatGpt for quick replies
19
19
  * @param conversation
20
+ * @param suggestionLanguage Language we want to get suggestions in
20
21
  */
21
- suggest(conversation: string): Promise<ChatGPTResponse | undefined>;
22
+ suggest(conversation: string, suggestionLanguage?: string): Promise<ChatGPTResponse | undefined>;
22
23
  /**
23
24
  * Get suggestions from Chat Completion API.
24
25
  *
25
26
  * @param message
26
27
  */
27
- getChatCompletionSuggestions(conversation: string): Promise<ChatGPTResponse | undefined>;
28
+ getChatCompletionSuggestions(conversation: string, suggestionLanguage?: string): Promise<ChatGPTResponse | undefined>;
28
29
  /**
29
30
  * Get the reply of the assistant if the assistant_id is available
30
31
  *
31
32
  * @param message
32
33
  */
33
- getAssistantSuggestions(conversation: string): Promise<ChatGPTResponse | undefined>;
34
+ getAssistantSuggestions(conversation: string, suggestionLanguage?: string): Promise<ChatGPTResponse | undefined>;
34
35
  private isAssistantIdFormat;
35
36
  private isPromptIdFormat;
36
- getPromptSuggestions(message: string): Promise<ChatGPTResponse | undefined>;
37
+ getPromptSuggestions(conversation: string, suggestionLanguage?: string): Promise<ChatGPTResponse | undefined>;
37
38
  }
@@ -59,9 +59,11 @@ class CopilotService extends BaseService_1.default {
59
59
  *
60
60
  * this will make it easy to ask chatGpt for quick replies
61
61
  * @param conversation
62
+ * @param suggestionLanguage Language we want to get suggestions in
62
63
  */
63
- suggest(conversation) {
64
+ suggest(conversation, suggestionLanguage) {
64
65
  return __awaiter(this, void 0, void 0, function* () {
66
+ logger_1.default.info({ conversation, suggestionLanguage }, ':: CopilotServicee.suggest :: Suggestion Language ::');
65
67
  if (!(conversation === null || conversation === void 0 ? void 0 : conversation.length)) {
66
68
  logger_1.default.error('Conversation must be provided');
67
69
  return Promise.reject(new Error('Conversation must be provided'));
@@ -78,11 +80,11 @@ class CopilotService extends BaseService_1.default {
78
80
  }
79
81
  // Route based on id shape: asst_* => Assistant API, prompt_* => Responses API
80
82
  if (yield this.isAssistantIdFormat(this.assistantId)) {
81
- return yield this.getAssistantSuggestions(conversation);
83
+ return yield this.getAssistantSuggestions(conversation, suggestionLanguage);
82
84
  }
83
- return yield this.getPromptSuggestions(lastCustomerMessage);
85
+ return yield this.getPromptSuggestions(conversation, suggestionLanguage);
84
86
  }
85
- return yield this.getChatCompletionSuggestions(conversation);
87
+ return yield this.getChatCompletionSuggestions(conversation, suggestionLanguage);
86
88
  }
87
89
  catch (e) {
88
90
  this.handleErrors(e);
@@ -95,14 +97,19 @@ class CopilotService extends BaseService_1.default {
95
97
  *
96
98
  * @param message
97
99
  */
98
- getChatCompletionSuggestions(conversation) {
100
+ getChatCompletionSuggestions(conversation, suggestionLanguage) {
99
101
  return __awaiter(this, void 0, void 0, function* () {
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 = {
102
+ const mainPrompt = `
103
+ Propose some quick replies for the agent to be used on the following conversation:
104
+
105
+ ${conversation}
106
+
107
+ Your response must only contain a valid JSON array of strings. For example:
108
+
109
+ ["Reply 1", "Reply 2", "Reply 3"]
110
+
111
+ ${suggestionLanguage ? `The replies must be in ${suggestionLanguage}.` : ''}
112
+ `.trim(), mainPromptMessage = {
106
113
  role: ChatGPTMessage_1.ChatGPTRole.USER,
107
114
  content: mainPrompt,
108
115
  },
@@ -122,11 +129,10 @@ class CopilotService extends BaseService_1.default {
122
129
  *
123
130
  * @param message
124
131
  */
125
- getAssistantSuggestions(conversation) {
132
+ getAssistantSuggestions(conversation, suggestionLanguage) {
126
133
  var _a, _b;
127
134
  return __awaiter(this, void 0, void 0, function* () {
128
- const conversationLines = conversation.split('\n').filter(line => line.trim());
129
- const messages = [];
135
+ const conversationLines = conversation.split('\n').filter(line => line.trim()), messages = [];
130
136
  for (const line of conversationLines) {
131
137
  if (line.includes('customer:')) {
132
138
  messages.push({
@@ -188,9 +194,10 @@ class CopilotService extends BaseService_1.default {
188
194
  lastMessageIndex: (messages.length - 1).toString()
189
195
  }
190
196
  });
191
- const run = yield openai.beta.threads.runs.create(this.threadId, {
192
- assistant_id: (_b = this.assistantId) !== null && _b !== void 0 ? _b : ''
193
- });
197
+ const run = yield openai.beta.threads.runs.create(this.threadId, Object.assign({ assistant_id: (_b = this.assistantId) !== null && _b !== void 0 ? _b : '' }, (suggestionLanguage ? {
198
+ additional_instructions: `IMPORTANT: You must respond in ${suggestionLanguage} language only.
199
+ Regardless of the language used in the conversation, your response must be in ${suggestionLanguage}.`
200
+ } : {})));
194
201
  let currentRun = run;
195
202
  while (['queued', 'in_progress', 'cancelling'].includes(currentRun.status)) {
196
203
  yield new Promise(resolve => setTimeout(resolve, 500));
@@ -216,55 +223,161 @@ class CopilotService extends BaseService_1.default {
216
223
  isPromptIdFormat(id) {
217
224
  return !!id && /^pmpt_[A-Za-z0-9]+$/.test(id);
218
225
  }
219
- getPromptSuggestions(message) {
220
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
226
+ getPromptSuggestions(conversation, suggestionLanguage) {
227
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
221
228
  return __awaiter(this, void 0, void 0, function* () {
229
+ const conversationLines = conversation.split('\n').filter(line => line.trim());
230
+ const messages = [];
231
+ for (const line of conversationLines) {
232
+ if (line.includes('customer:')) {
233
+ messages.push({
234
+ role: 'user',
235
+ content: line.replace(/^customer:\s*/, '').trim()
236
+ });
237
+ }
238
+ else if (line.includes('agent:')) {
239
+ messages.push({
240
+ role: 'assistant',
241
+ content: line.replace(/^agent:\s*/, '').trim()
242
+ });
243
+ }
244
+ }
245
+ if (messages.length === 0) {
246
+ logger_1.default.error('No valid messages found in conversation');
247
+ return Promise.reject(new Error('No valid messages found in conversation'));
248
+ }
222
249
  const openai = new openai_1.default({ apiKey: this.openaiApiKey });
223
250
  let conversationId = this.conversationId;
251
+ let lastMessageIndex = -1;
224
252
  if (!conversationId) {
225
- const conv = yield openai.conversations.create();
253
+ const conv = yield openai.conversations.create({
254
+ metadata: {
255
+ lastMessageIndex: '-1',
256
+ conversationHistory: JSON.stringify([])
257
+ }
258
+ });
226
259
  conversationId = conv.id;
260
+ this.conversationId = conversationId;
227
261
  }
228
- const request = {
229
- //model: this.chatGptModel || 'gpt-4o-mini',
230
- input: message,
231
- store: true,
232
- conversation: { id: conversationId },
233
- include: ['file_search_call.results'],
234
- };
262
+ else {
263
+ // Retrieve conversation to get metadata
264
+ try {
265
+ const conv = yield openai.conversations.retrieve(conversationId);
266
+ lastMessageIndex = parseInt(((_a = conv.metadata) === null || _a === void 0 ? void 0 : _a.lastMessageIndex) || '-1', 10);
267
+ }
268
+ catch (e) {
269
+ logger_1.default.error('Failed to retrieve conversation metadata', e);
270
+ lastMessageIndex = -1;
271
+ }
272
+ }
273
+ // Get new messages that haven't been sent yet
274
+ const newMessages = messages.slice(lastMessageIndex + 1);
275
+ if (newMessages.length === 0) {
276
+ // Retrieve the last response from the conversation
277
+ try {
278
+ const convHistory = yield openai.conversations.retrieve(conversationId);
279
+ const lastResponse = convHistory.last_response;
280
+ if (lastResponse) {
281
+ const output = lastResponse.output || [];
282
+ const assistantMessages = output.filter((item) => item.type === 'message' && item.role === 'assistant');
283
+ if (assistantMessages.length) {
284
+ 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)) || []; });
285
+ return {
286
+ data: [text[text.length - 1]],
287
+ threadId: this.conversationId,
288
+ usageTokens: undefined,
289
+ };
290
+ }
291
+ }
292
+ }
293
+ catch (e) {
294
+ logger_1.default.error('Failed to retrieve last response from conversation', e);
295
+ }
296
+ return Promise.reject(new Error('No new messages to process and no previous response found'));
297
+ }
298
+ const allPreviousMessages = messages.slice(0, lastMessageIndex + 1);
299
+ const contextMessages = [];
300
+ for (const msg of allPreviousMessages) {
301
+ const roleLabel = msg.role === 'user' ? 'customer' : 'agent';
302
+ contextMessages.push(`${roleLabel}: ${msg.content}`);
303
+ }
304
+ const lastUserMessage = newMessages.filter(m => m.role === 'user').pop();
305
+ if (!lastUserMessage) {
306
+ if (conversationId) {
307
+ try {
308
+ yield openai.conversations.update(conversationId, {
309
+ metadata: {
310
+ lastMessageIndex: (messages.length - 1).toString(),
311
+ conversationHistory: JSON.stringify(messages)
312
+ }
313
+ });
314
+ }
315
+ catch (e) {
316
+ logger_1.default.error('Failed to update conversation metadata', e);
317
+ }
318
+ }
319
+ return Promise.reject(new Error('No new user messages to process'));
320
+ }
321
+ let inputWithContext = lastUserMessage.content;
322
+ if (contextMessages.length > 0 || newMessages.length > 1) {
323
+ const allNewMessages = [];
324
+ for (const msg of newMessages) {
325
+ const roleLabel = msg.role === 'user' ? 'customer' : 'agent';
326
+ allNewMessages.push(`${roleLabel}: ${msg.content}`);
327
+ }
328
+ const fullContext = [...contextMessages, ...allNewMessages].join('\n');
329
+ inputWithContext = fullContext;
330
+ }
331
+ const request = Object.assign({ input: inputWithContext, store: true, conversation: { id: conversationId }, include: ['file_search_call.results'] }, (suggestionLanguage ? {
332
+ instructions: `IMPORTANT: You must respond in ${suggestionLanguage} language only.
333
+ Regardless of the language used in the conversation, your response must be in ${suggestionLanguage}.`
334
+ } : {}));
235
335
  if (this.assistantId && (yield this.isPromptIdFormat(this.assistantId))) {
236
336
  request.prompt = { id: this.assistantId };
237
337
  }
238
- const response = yield openai.responses.create(request);
239
- response.output.forEach((item, index) => {
240
- });
241
- if ((_a = response.conversation) === null || _a === void 0 ? void 0 : _a.id) {
242
- this.conversationId = response.conversation.id;
338
+ const lastResponse = yield openai.responses.create(request);
339
+ if ((_b = lastResponse.conversation) === null || _b === void 0 ? void 0 : _b.id) {
340
+ this.conversationId = lastResponse.conversation.id;
341
+ conversationId = this.conversationId;
342
+ }
343
+ if (conversationId) {
344
+ try {
345
+ yield openai.conversations.update(conversationId, {
346
+ metadata: {
347
+ lastMessageIndex: (messages.length - 1).toString(),
348
+ conversationHistory: JSON.stringify(messages)
349
+ }
350
+ });
351
+ }
352
+ catch (e) {
353
+ logger_1.default.error('Failed to update conversation metadata', e);
354
+ }
355
+ }
356
+ if (!lastResponse) {
357
+ return Promise.reject(new Error('No response generated'));
243
358
  }
244
- // Sometimes if the prompt needs a file search, the output will be an array of messages
245
- // which includes multiple messages (some of them are not useful for the user, but the last one is)
246
- const output = response.output || [];
359
+ const output = lastResponse.output || [];
247
360
  const assistantMessages = output.filter((item) => item.type === 'message' && item.role === 'assistant');
248
361
  if (assistantMessages.length) {
249
362
  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)) || []; });
250
363
  return {
251
- data: [text.pop()],
364
+ data: [text[text.length - 1]],
252
365
  threadId: this.conversationId,
253
366
  usageTokens: {
254
- prompt_tokens: Number((_c = ((_b = response.usage) === null || _b === void 0 ? void 0 : _b.prompt_tokens)) !== null && _c !== void 0 ? _c : 0),
255
- completion_tokens: Number((_e = ((_d = response.usage) === null || _d === void 0 ? void 0 : _d.completion_tokens)) !== null && _e !== void 0 ? _e : 0),
256
- total_tokens: Number((_g = ((_f = response.usage) === null || _f === void 0 ? void 0 : _f.total_tokens)) !== null && _g !== void 0 ? _g : 0),
367
+ prompt_tokens: Number((_d = ((_c = lastResponse.usage) === null || _c === void 0 ? void 0 : _c.prompt_tokens)) !== null && _d !== void 0 ? _d : 0),
368
+ completion_tokens: Number((_f = ((_e = lastResponse.usage) === null || _e === void 0 ? void 0 : _e.completion_tokens)) !== null && _f !== void 0 ? _f : 0),
369
+ total_tokens: Number((_h = ((_g = lastResponse.usage) === null || _g === void 0 ? void 0 : _g.total_tokens)) !== null && _h !== void 0 ? _h : 0),
257
370
  },
258
371
  };
259
372
  }
260
- if (response.output_text) {
373
+ if (lastResponse.output_text) {
261
374
  return {
262
- data: [response.output_text],
375
+ data: [lastResponse.output_text],
263
376
  threadId: this.conversationId,
264
377
  usageTokens: {
265
- prompt_tokens: Number((_j = ((_h = response.usage) === null || _h === void 0 ? void 0 : _h.prompt_tokens)) !== null && _j !== void 0 ? _j : 0),
266
- completion_tokens: Number((_l = ((_k = response.usage) === null || _k === void 0 ? void 0 : _k.completion_tokens)) !== null && _l !== void 0 ? _l : 0),
267
- total_tokens: Number((_o = ((_m = response.usage) === null || _m === void 0 ? void 0 : _m.total_tokens)) !== null && _o !== void 0 ? _o : 0),
378
+ prompt_tokens: Number((_k = ((_j = lastResponse.usage) === null || _j === void 0 ? void 0 : _j.prompt_tokens)) !== null && _k !== void 0 ? _k : 0),
379
+ completion_tokens: Number((_m = ((_l = lastResponse.usage) === null || _l === void 0 ? void 0 : _l.completion_tokens)) !== null && _m !== void 0 ? _m : 0),
380
+ total_tokens: Number((_p = ((_o = lastResponse.usage) === null || _o === void 0 ? void 0 : _o.total_tokens)) !== null && _p !== void 0 ? _p : 0),
268
381
  },
269
382
  };
270
383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobrowser/chatgpt",
3
- "version": "0.7.40",
3
+ "version": "0.7.42-beta.1",
4
4
  "description": "chatgpt services to connect our projects with chatgpt api",
5
5
  "keywords": [
6
6
  "chatgpt",
@@ -40,5 +40,5 @@
40
40
  "bugs": {
41
41
  "url": "https://bitbucket.org/cobrowser/cb_utils/issues"
42
42
  },
43
- "gitHead": "16611610698982c71962d16bdf6508733b171847"
43
+ "gitHead": "1f4318b0c31d19c651c89e548b58841c060617af"
44
44
  }