@wabot-dev/framework 0.1.0-beta.41 → 0.1.0-beta.43

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.
@@ -14,109 +14,127 @@ let GoogleChatAdapter = class GoogleChatAdapter {
14
14
  this.genai = new GoogleGenAI({ apiKey });
15
15
  }
16
16
  async nextItem(req) {
17
- const contents = this.buildContents(req.prevItems, req.systemPrompt);
18
- const tools = req.tools.length > 0 ? req.tools.map(this.mapTool) : undefined;
19
- const request = { model: req.model, contents, tools };
17
+ const geminiInput = [];
18
+ geminiInput.push(...this.mapChatItems(req.prevItems, req.systemPrompt));
19
+ const tools = req.tools.map((x) => this.mapTool(x));
20
+ const request = {
21
+ model: req.model,
22
+ contents: geminiInput,
23
+ tools: tools.length > 0 ? tools : undefined,
24
+ };
20
25
  this.logger.debug(`Call Gemini API with Request: ${JSON.stringify(request)}`);
21
26
  const response = await this.genai.models.generateContent(request);
22
27
  return this.mapResponse(response);
23
28
  }
24
- buildContents(chatItems, systemPrompt) {
25
- const contents = [];
29
+ mapChatItems(chatItems, systemPrompt) {
30
+ const geminiInput = [];
26
31
  if (systemPrompt) {
27
- contents.push({ role: 'user', parts: [{ text: systemPrompt }] }, { role: 'model', parts: [{ text: 'I understand. I will follow these instructions.' }] });
32
+ geminiInput.push({ role: 'user', parts: [{ text: 'system: ' + systemPrompt }] });
33
+ geminiInput.push({ role: 'model', parts: [{ text: 'I understand.' }] });
28
34
  }
29
- chatItems.forEach((chatItem) => {
35
+ for (const chatItem of chatItems) {
30
36
  switch (chatItem.type) {
31
37
  case 'humanMessage':
32
- this.validateMessageContent(chatItem.humanMessage.text, 'User');
33
- contents.push({ role: 'user', parts: [{ text: chatItem.humanMessage.text }] });
38
+ geminiInput.push(this.mapHumanMessage(chatItem.humanMessage));
34
39
  break;
35
40
  case 'botMessage':
36
- this.validateMessageContent(chatItem.botMessage.text, 'Assistant');
37
- contents.push({ role: 'model', parts: [{ text: chatItem.botMessage.text }] });
41
+ geminiInput.push(this.mapBotMessage(chatItem.botMessage));
38
42
  break;
39
43
  case 'functionCall':
40
- contents.push(...this.mapFunctionCall(chatItem.functionCall));
44
+ geminiInput.push(...this.mapFunctionCall(chatItem.functionCall));
41
45
  break;
42
46
  }
43
- });
44
- return contents;
47
+ }
48
+ return geminiInput;
49
+ }
50
+ mapHumanMessage(item) {
51
+ if (!item.text) {
52
+ throw new Error('User message content is empty');
53
+ }
54
+ return { role: 'user', parts: [{ text: item.text }] };
45
55
  }
46
- validateMessageContent(text, messageType) {
47
- if (!text) {
48
- throw new Error(`${messageType} message content is empty`);
56
+ mapBotMessage(item) {
57
+ if (!item.text) {
58
+ throw new Error('Bot message content is empty');
49
59
  }
60
+ return { role: 'model', parts: [{ text: item.text }] };
50
61
  }
51
62
  mapFunctionCall(item) {
52
- const args = JSON.parse(item.arguments || '{}');
53
63
  return [
54
64
  {
55
65
  role: 'model',
56
- parts: [{ functionCall: { name: item.name, args } }],
66
+ parts: [
67
+ {
68
+ functionCall: {
69
+ name: item.name,
70
+ args: JSON.parse(item.arguments || '{}'),
71
+ },
72
+ },
73
+ ],
57
74
  },
58
75
  {
59
76
  role: 'user',
60
77
  parts: [
61
78
  {
62
- functionResponse: { name: item.name, response: { result: item.result || 'No result' } },
79
+ functionResponse: {
80
+ name: item.name,
81
+ response: {
82
+ result: item.result || 'No result',
83
+ },
84
+ },
63
85
  },
64
86
  ],
65
87
  },
66
88
  ];
67
89
  }
68
- mapTool = (tool) => ({
69
- functionDeclarations: [
70
- {
71
- name: tool.name,
72
- description: tool.description,
73
- parameters: {
74
- type: 'object',
75
- properties: tool.parameters.reduce((acc, param) => ({
76
- ...acc,
77
- [param.name]: { type: param.type, description: param.description },
78
- }), {}),
79
- required: tool.parameters.map((param) => param.name),
90
+ mapTool(tool) {
91
+ return {
92
+ functionDeclarations: [
93
+ {
94
+ name: tool.name,
95
+ description: tool.description,
96
+ parameters: {
97
+ type: 'object',
98
+ properties: tool.parameters.reduce((prev, param) => ({
99
+ ...prev,
100
+ [param.name]: { type: param.type, description: param.description },
101
+ }), {}),
102
+ required: tool.parameters.map((param) => param.name),
103
+ },
80
104
  },
81
- },
82
- ],
83
- });
105
+ ],
106
+ };
107
+ }
84
108
  mapResponse(response) {
85
- this.validateResponse(response);
109
+ let chatItem;
86
110
  const part = response.response.candidates[0].content.parts[0];
87
- const chatItem = part.text
88
- ? { type: 'botMessage', botMessage: { text: part.text } }
89
- : part.functionCall
90
- ? {
91
- type: 'functionCall',
92
- functionCall: {
93
- id: `gemini_${Date.now()}`,
94
- name: part.functionCall.name,
95
- arguments: JSON.stringify(part.functionCall.args || {}),
96
- },
97
- }
98
- : (() => {
99
- throw new Error('Not supported Gemini Response');
100
- })();
101
- const usage = {
102
- inputTokens: response.response.usageMetadata?.promptTokenCount || 0,
103
- outputTokens: response.response.usageMetadata?.candidatesTokenCount || 0,
104
- };
105
- if (!response.response.usageMetadata) {
106
- throw new Error('Unable to found usage info');
111
+ if (part.text) {
112
+ chatItem = { type: 'botMessage', botMessage: { text: part.text } };
107
113
  }
108
- return { chatItem, usage };
109
- }
110
- validateResponse(response) {
111
- if (!response.response) {
112
- throw new Error('Invalid Gemini response structure');
114
+ else if (part.functionCall) {
115
+ chatItem = {
116
+ type: 'functionCall',
117
+ functionCall: {
118
+ id: `gemini_${Date.now()}`,
119
+ name: part.functionCall.name,
120
+ arguments: JSON.stringify(part.functionCall.args || {}),
121
+ },
122
+ };
113
123
  }
114
- if (!response.response.candidates?.[0]) {
115
- throw new Error('No candidates in Gemini response');
124
+ else {
125
+ throw new Error('Not supported Gemini Response');
116
126
  }
117
- if (!response.response.candidates[0].content?.parts?.[0]) {
118
- throw new Error('No parts in Gemini response');
127
+ let usage;
128
+ if (response.response.usageMetadata) {
129
+ usage = {
130
+ inputTokens: response.response.usageMetadata.promptTokenCount || 0,
131
+ outputTokens: response.response.usageMetadata.candidatesTokenCount || 0,
132
+ };
119
133
  }
134
+ else {
135
+ throw new Error('Unable to found usage info');
136
+ }
137
+ return { chatItem, usage };
120
138
  }
121
139
  };
122
140
  GoogleChatAdapter = __decorate([
@@ -32,7 +32,7 @@ class PgCrudRepository extends PgRepositoryBase {
32
32
  const item = await this.find(id);
33
33
  if (!item) {
34
34
  throw new CustomError({
35
- message: `Not found ${this.config.constructor.name} with id = '${id}'}`,
35
+ message: `Not found ${this.config.constructor.name} with id = '${id}'`,
36
36
  httpCode: 404,
37
37
  });
38
38
  }
@@ -70,7 +70,7 @@ function runRestControllers(controllers) {
70
70
  }, {});
71
71
  res
72
72
  .status(httpCode ?? 500)
73
- .json({ error: { message: err.message, stack: err.stack, ...info } });
73
+ .json(removeCircular({ error: { message: err.message, stack: err.stack, ...info } }));
74
74
  }
75
75
  else {
76
76
  res.status(500).json({ error: { message: 'Unknown error' } });
@@ -84,5 +84,19 @@ function runRestControllers(controllers) {
84
84
  });
85
85
  expressProvider.listen();
86
86
  }
87
+ function removeCircular(obj, seen = new WeakSet()) {
88
+ if (obj && typeof obj === 'object') {
89
+ if (seen.has(obj)) {
90
+ return undefined; // remove circular ref
91
+ }
92
+ seen.add(obj);
93
+ const clone = Array.isArray(obj) ? [] : {};
94
+ for (const key in obj) {
95
+ clone[key] = removeCircular(obj[key], seen);
96
+ }
97
+ return clone;
98
+ }
99
+ return obj;
100
+ }
87
101
 
88
102
  export { runRestControllers };
@@ -807,7 +807,7 @@ declare class PgRepositoryBase<P extends Entity<IEntityData>> {
807
807
  constructor(pool: Pool, config: IPgRepositoryConfig<any>);
808
808
  protected values(item: P): (string | number | boolean | Date | null)[];
809
809
  protected exec(sql: string, values: any[]): Promise<void>;
810
- protected query(sql: string, values: any[]): Promise<any[]>;
810
+ protected query(sql: string, values: any[]): Promise<P[]>;
811
811
  protected connect(): Promise<Pool>;
812
812
  protected ensureTable(): Promise<void>;
813
813
  protected ensureColumns(): Promise<void>;
@@ -862,12 +862,12 @@ declare class GoogleChatAdapter implements IChatAdapter {
862
862
  private logger;
863
863
  constructor(env: Env);
864
864
  nextItem(req: IChatAdapterNextItemReq): Promise<IChatAdapterNextItemRes>;
865
- private buildContents;
866
- private validateMessageContent;
865
+ private mapChatItems;
866
+ private mapHumanMessage;
867
+ private mapBotMessage;
867
868
  private mapFunctionCall;
868
869
  private mapTool;
869
870
  private mapResponse;
870
- private validateResponse;
871
871
  }
872
872
 
873
873
  declare class OpenaiChatAdapter implements IChatAdapter {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wabot-dev/framework",
3
- "version": "0.1.0-beta.41",
3
+ "version": "0.1.0-beta.43",
4
4
  "description": "Framework for IA Chat Bots",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",