@wabot-dev/framework 0.8.2 → 0.9.0
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.
- package/dist/src/addon/async/pg/PgCronJobRepository.js +2 -0
- package/dist/src/addon/async/pg/PgJobRepository.js +2 -0
- package/dist/src/addon/auth/api-key/@apiKeyGuard.js +2 -2
- package/dist/src/addon/auth/api-key/PgApiKeyRepository.js +3 -1
- package/dist/src/addon/auth/jwt/@jwtGuard.js +2 -2
- package/dist/src/addon/auth/jwt/PgJwtRefreshTokenRepository.js +2 -0
- package/dist/src/addon/chat-bot/anthropic/AnthropicChatAdapter.js +38 -14
- package/dist/src/addon/chat-bot/deepseek/DeepSeekChatAdapter.js +42 -13
- package/dist/src/addon/chat-bot/google/GoogleChatAdapter.js +77 -26
- package/dist/src/addon/chat-bot/openia/OpenaiChatAdapter.js +31 -9
- package/dist/src/addon/chat-bot/openrouter/OpenRouterChatAdapter.js +23 -4
- package/dist/src/addon/chat-bot/pg/PgChatMemory.js +6 -1
- package/dist/src/addon/chat-bot/pg/PgChatRepository.js +5 -0
- package/dist/src/addon/chat-bot/ram/RamChatRepository.js +3 -0
- package/dist/src/addon/chat-bot/wabot/WabotChatAdapter.js +9 -0
- package/dist/src/addon/chat-controller/cmd/CmdChannel.js +3 -3
- package/dist/src/addon/chat-controller/wasender/WasenderWebhookController.js +1 -1
- package/dist/src/addon/chat-controller/whatsapp/PgWhatsAppRepository.js +2 -0
- package/dist/src/addon/chat-controller/whatsapp/WhatsAppSender.js +3 -0
- package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppSenderByCloudApi.js +3 -0
- package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppSenderByWabotProxy.js +3 -0
- package/dist/src/core/config/resolver.js +27 -0
- package/dist/src/core/config/tag-functions.js +10 -1
- package/dist/src/feature/chat-bot/Chat.js +2 -1
- package/dist/src/feature/chat-bot/ChatAdapterRegistry.js +26 -0
- package/dist/src/feature/chat-bot/ChatBot.js +9 -6
- package/dist/src/feature/chat-bot/UnionChatAdapter.js +64 -0
- package/dist/src/feature/chat-bot/isChatMessageEmpty.js +1 -1
- package/dist/src/feature/chat-bot/isRetryableError.js +63 -0
- package/dist/src/feature/chat-bot/metadata/@chatAdapter.js +11 -0
- package/dist/src/feature/chat-bot/metadata/ChatAdapterMetadataStore.js +17 -0
- package/dist/src/feature/chat-bot/runChatAdapters.js +22 -0
- package/dist/src/feature/chat-controller/ChatResolver.js +3 -0
- package/dist/src/feature/chat-controller/runChatControllers.js +3 -0
- package/dist/src/feature/http/HttpServerProvider.js +1 -1
- package/dist/src/feature/mindset/IMindset.js +4 -0
- package/dist/src/feature/mindset/MindsetOperator.js +30 -2
- package/dist/src/feature/pg/pgStorage.js +1 -1
- package/dist/src/feature/pg/query/@pgJsonRepository.js +73 -0
- package/dist/src/feature/pg/query/@query.js +14 -0
- package/dist/src/feature/pg/query/PgJsonRepository.js +23 -0
- package/dist/src/feature/pg/query/PgRepositoryMetadataStore.js +44 -0
- package/dist/src/feature/pg/query/buildQuerySql.js +164 -0
- package/dist/src/feature/pg/query/parseQueryMethodName.js +151 -0
- package/dist/src/feature/pg/withPgClient.js +1 -1
- package/dist/src/feature/rest-controller/runRestControllers.js +2 -2
- package/dist/src/index.d.ts +140 -18
- package/dist/src/index.js +15 -3
- package/dist/src/node_modules/cron-parser/dist/CronFileParser.js +2 -2
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@ import 'debug';
|
|
|
7
7
|
import 'node:crypto';
|
|
8
8
|
import '../../../feature/pg/withPgClient.js';
|
|
9
9
|
import '../../../feature/pg/pgStorage.js';
|
|
10
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
11
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
10
12
|
import { Pool } from 'pg';
|
|
11
13
|
|
|
12
14
|
let PgCronJobRepository = class PgCronJobRepository extends PgCrudRepository {
|
|
@@ -7,6 +7,8 @@ import 'debug';
|
|
|
7
7
|
import 'node:crypto';
|
|
8
8
|
import { withPgClient } from '../../../feature/pg/withPgClient.js';
|
|
9
9
|
import '../../../feature/pg/pgStorage.js';
|
|
10
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
11
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
10
12
|
import '../../../feature/async/AsyncMetadataStore.js';
|
|
11
13
|
import '../../../feature/async/Async.js';
|
|
12
14
|
import '../../../_virtual/index.js';
|
|
@@ -6,8 +6,8 @@ import 'debug';
|
|
|
6
6
|
import '../../../core/validation/metadata/ValidationMetadataStore.js';
|
|
7
7
|
import '../../../feature/express/ExpressProvider.js';
|
|
8
8
|
import 'express';
|
|
9
|
-
import 'path';
|
|
10
|
-
import 'http';
|
|
9
|
+
import 'node:path';
|
|
10
|
+
import 'node:http';
|
|
11
11
|
import { ApiKeyGuardMiddleware } from './ApiKeyGuardMiddleware.js';
|
|
12
12
|
|
|
13
13
|
function apiKeyGuard() {
|
|
@@ -6,10 +6,12 @@ import { CustomError } from '../../../core/error/CustomError.js';
|
|
|
6
6
|
import 'node:crypto';
|
|
7
7
|
import '../../../feature/pg/withPgClient.js';
|
|
8
8
|
import '../../../feature/pg/pgStorage.js';
|
|
9
|
+
import { singleton } from '../../../core/injection/index.js';
|
|
10
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
11
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
9
12
|
import { Pool } from 'pg';
|
|
10
13
|
import { ApiKey } from './ApiKey.js';
|
|
11
14
|
import '../../../core/error/setupErrorHandlers.js';
|
|
12
|
-
import { singleton } from '../../../core/injection/index.js';
|
|
13
15
|
|
|
14
16
|
let PgApiKeyRepository = class PgApiKeyRepository extends PgCrudRepository {
|
|
15
17
|
constructor(pool) {
|
|
@@ -6,8 +6,8 @@ import 'debug';
|
|
|
6
6
|
import '../../../core/validation/metadata/ValidationMetadataStore.js';
|
|
7
7
|
import '../../../feature/express/ExpressProvider.js';
|
|
8
8
|
import 'express';
|
|
9
|
-
import 'path';
|
|
10
|
-
import 'http';
|
|
9
|
+
import 'node:path';
|
|
10
|
+
import 'node:http';
|
|
11
11
|
import { JwtGuardMiddleware } from './JwtGuardMiddleware.js';
|
|
12
12
|
|
|
13
13
|
function jwtGuard() {
|
|
@@ -8,6 +8,8 @@ import 'debug';
|
|
|
8
8
|
import 'node:crypto';
|
|
9
9
|
import '../../../feature/pg/withPgClient.js';
|
|
10
10
|
import '../../../feature/pg/pgStorage.js';
|
|
11
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
12
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
11
13
|
import { Pool } from 'pg';
|
|
12
14
|
import { JwtRefreshToken } from './JwtRefreshToken.js';
|
|
13
15
|
|
|
@@ -2,13 +2,18 @@ import { __decorate, __metadata } from 'tslib';
|
|
|
2
2
|
import { Env } from '../../../core/env/Env.js';
|
|
3
3
|
import { singleton } from '../../../core/injection/index.js';
|
|
4
4
|
import { Logger } from '../../../core/logger/Logger.js';
|
|
5
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
5
6
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
6
7
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
8
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
9
|
+
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
10
|
+
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
11
|
+
import { isRetryableError } from '../../../feature/chat-bot/isRetryableError.js';
|
|
12
|
+
import { chatAdapter } from '../../../feature/chat-bot/metadata/@chatAdapter.js';
|
|
7
13
|
import 'uuid';
|
|
8
14
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
15
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
9
16
|
import { safeJsonParse } from '../../../feature/chat-bot/safeJsonParse.js';
|
|
10
|
-
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
11
|
-
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
12
17
|
import { Anthropic } from '@anthropic-ai/sdk';
|
|
13
18
|
|
|
14
19
|
const ANTHROPIC_SUPPORTED_IMAGE_MIME_TYPES = [
|
|
@@ -30,16 +35,28 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
|
|
|
30
35
|
async nextItems(req) {
|
|
31
36
|
const tools = req.tools.map((x) => this.mapTool(x));
|
|
32
37
|
const messages = this.mapChatItems(req.prevItems);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
let lastError;
|
|
39
|
+
for (const ref of req.models) {
|
|
40
|
+
const request = {
|
|
41
|
+
model: ref.model,
|
|
42
|
+
max_tokens: 4096,
|
|
43
|
+
system: req.systemPrompt,
|
|
44
|
+
messages,
|
|
45
|
+
tools: tools.length > 0 ? tools : undefined,
|
|
46
|
+
};
|
|
47
|
+
this.logger.debug(`Call Claude API with model: ${request.model}, messages: ${request.messages.length}, tools: ${request.tools?.length ?? 0}`);
|
|
48
|
+
try {
|
|
49
|
+
const response = await this.anthropic.messages.create(request);
|
|
50
|
+
return this.mapResponse(response, ref.model);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (!isRetryableError(err))
|
|
54
|
+
throw err;
|
|
55
|
+
this.logger.warn(`Anthropic model '${ref.model}' failed with retryable error, trying next`, err instanceof Error ? { message: err.message } : { err });
|
|
56
|
+
lastError = err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw lastError ?? new Error('No Anthropic model could handle the request');
|
|
43
60
|
}
|
|
44
61
|
mapChatItems(chatItems) {
|
|
45
62
|
const messages = [];
|
|
@@ -158,12 +175,18 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
|
|
|
158
175
|
},
|
|
159
176
|
};
|
|
160
177
|
}
|
|
161
|
-
mapResponse(response) {
|
|
178
|
+
mapResponse(response, modelName) {
|
|
162
179
|
let usage;
|
|
163
180
|
if (response.usage) {
|
|
181
|
+
const cacheRead = response.usage.cache_read_input_tokens ?? 0;
|
|
182
|
+
const cacheWrite = response.usage.cache_creation_input_tokens ?? 0;
|
|
164
183
|
usage = {
|
|
165
|
-
inputTokens: response.usage.input_tokens,
|
|
184
|
+
inputTokens: response.usage.input_tokens + cacheRead + cacheWrite,
|
|
166
185
|
outputTokens: response.usage.output_tokens,
|
|
186
|
+
cacheReadTokens: cacheRead || undefined,
|
|
187
|
+
cacheWriteTokens: cacheWrite || undefined,
|
|
188
|
+
provider: 'anthropic',
|
|
189
|
+
model: response.model ?? modelName,
|
|
167
190
|
};
|
|
168
191
|
}
|
|
169
192
|
else {
|
|
@@ -189,6 +212,7 @@ let AnthropicChatAdapter = class AnthropicChatAdapter {
|
|
|
189
212
|
}
|
|
190
213
|
};
|
|
191
214
|
AnthropicChatAdapter = __decorate([
|
|
215
|
+
chatAdapter({ provider: 'anthropic' }),
|
|
192
216
|
singleton(),
|
|
193
217
|
__metadata("design:paramtypes", [Env])
|
|
194
218
|
], AnthropicChatAdapter);
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
import { __decorate, __metadata } from 'tslib';
|
|
2
|
+
import { singleton } from '../../../core/injection/index.js';
|
|
1
3
|
import { Logger } from '../../../core/logger/Logger.js';
|
|
4
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
2
5
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
3
6
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
4
|
-
import '../../../
|
|
7
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
8
|
+
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
9
|
+
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
10
|
+
import { isRetryableError } from '../../../feature/chat-bot/isRetryableError.js';
|
|
11
|
+
import { chatAdapter } from '../../../feature/chat-bot/metadata/@chatAdapter.js';
|
|
5
12
|
import 'uuid';
|
|
6
13
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
14
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
7
15
|
import '../../../core/error/setupErrorHandlers.js';
|
|
8
|
-
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
9
|
-
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
10
16
|
import { OpenAI } from 'openai';
|
|
11
17
|
|
|
12
|
-
class DeepSeekChatAdapter {
|
|
18
|
+
let DeepSeekChatAdapter = class DeepSeekChatAdapter {
|
|
13
19
|
deepSeek;
|
|
14
20
|
logger = new Logger('wabot:deepseek-chat-adapter');
|
|
15
21
|
constructor() {
|
|
@@ -31,13 +37,25 @@ class DeepSeekChatAdapter {
|
|
|
31
37
|
deepSeekInput.push({ role: 'system', content: req.systemPrompt });
|
|
32
38
|
deepSeekInput.push(...this.mapChatItems(req.prevItems));
|
|
33
39
|
const tools = req.tools.map((x) => this.mapTool(x));
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
let lastError;
|
|
41
|
+
for (const ref of req.models) {
|
|
42
|
+
try {
|
|
43
|
+
const response = await this.deepSeek.chat.completions.create({
|
|
44
|
+
model: ref.model,
|
|
45
|
+
messages: deepSeekInput,
|
|
46
|
+
tools,
|
|
47
|
+
tool_choice: 'auto',
|
|
48
|
+
});
|
|
49
|
+
return this.mapResponse(response, ref.model);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (!isRetryableError(err))
|
|
53
|
+
throw err;
|
|
54
|
+
this.logger.warn(`DeepSeek model '${ref.model}' failed with retryable error, trying next`, err instanceof Error ? { message: err.message } : { err });
|
|
55
|
+
lastError = err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw lastError ?? new Error('No DeepSeek model could handle the request');
|
|
41
59
|
}
|
|
42
60
|
mapChatItems(chatItems) {
|
|
43
61
|
const deepSeekInput = [];
|
|
@@ -115,7 +133,7 @@ class DeepSeekChatAdapter {
|
|
|
115
133
|
},
|
|
116
134
|
};
|
|
117
135
|
}
|
|
118
|
-
mapResponse(response) {
|
|
136
|
+
mapResponse(response, modelName) {
|
|
119
137
|
let chatItem;
|
|
120
138
|
const { tool_calls: responseFunctionCall, content: responseText } = response.choices?.[0]?.message ?? {};
|
|
121
139
|
if (responseText) {
|
|
@@ -136,9 +154,15 @@ class DeepSeekChatAdapter {
|
|
|
136
154
|
}
|
|
137
155
|
let usage;
|
|
138
156
|
if (response.usage) {
|
|
157
|
+
// DeepSeek exposes prompt_cache_hit_tokens (not in OpenAI SDK types)
|
|
158
|
+
const cacheRead = response.usage
|
|
159
|
+
.prompt_cache_hit_tokens ?? 0;
|
|
139
160
|
usage = {
|
|
140
161
|
inputTokens: response.usage.prompt_tokens,
|
|
141
162
|
outputTokens: response.usage.completion_tokens,
|
|
163
|
+
cacheReadTokens: cacheRead || undefined,
|
|
164
|
+
provider: 'deepseek',
|
|
165
|
+
model: response.model ?? modelName,
|
|
142
166
|
};
|
|
143
167
|
}
|
|
144
168
|
else {
|
|
@@ -146,6 +170,11 @@ class DeepSeekChatAdapter {
|
|
|
146
170
|
}
|
|
147
171
|
return { nextItems: [chatItem], usage };
|
|
148
172
|
}
|
|
149
|
-
}
|
|
173
|
+
};
|
|
174
|
+
DeepSeekChatAdapter = __decorate([
|
|
175
|
+
chatAdapter({ provider: 'deepseek' }),
|
|
176
|
+
singleton(),
|
|
177
|
+
__metadata("design:paramtypes", [])
|
|
178
|
+
], DeepSeekChatAdapter);
|
|
150
179
|
|
|
151
180
|
export { DeepSeekChatAdapter };
|
|
@@ -3,13 +3,18 @@ import { Env } from '../../../core/env/Env.js';
|
|
|
3
3
|
import { singleton } from '../../../core/injection/index.js';
|
|
4
4
|
import { Logger } from '../../../core/logger/Logger.js';
|
|
5
5
|
import { Random } from '../../../core/random/Random.js';
|
|
6
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
6
7
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
7
8
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
9
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
10
|
+
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
11
|
+
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
12
|
+
import { isRetryableError } from '../../../feature/chat-bot/isRetryableError.js';
|
|
13
|
+
import { chatAdapter } from '../../../feature/chat-bot/metadata/@chatAdapter.js';
|
|
8
14
|
import 'uuid';
|
|
9
15
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
16
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
10
17
|
import { safeJsonParse } from '../../../feature/chat-bot/safeJsonParse.js';
|
|
11
|
-
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
12
|
-
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
13
18
|
import { GoogleGenAI } from '@google/genai';
|
|
14
19
|
|
|
15
20
|
const GOOGLE_SUPPORTED_IMAGE_MIME_TYPES = [
|
|
@@ -38,21 +43,37 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
38
43
|
async nextItems(req) {
|
|
39
44
|
const contents = [];
|
|
40
45
|
contents.push({ role: 'user', parts: [{ text: req.systemPrompt }] });
|
|
41
|
-
contents.push(...this.mapChatItems(req.prevItems));
|
|
46
|
+
contents.push(...(await this.mapChatItems(req.prevItems)));
|
|
42
47
|
const functionDeclarations = req.tools.map((x) => this.mapTool(x));
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const hasUnsignedFunctionCall = req.prevItems.some((item) => item.type === 'functionCall' && !item.functionCall.signature);
|
|
49
|
+
let lastError;
|
|
50
|
+
for (const ref of req.models) {
|
|
51
|
+
try {
|
|
52
|
+
const response = await this.ai.models.generateContent({
|
|
53
|
+
model: ref.model,
|
|
54
|
+
contents,
|
|
55
|
+
config: {
|
|
56
|
+
tools: [{ functionDeclarations }],
|
|
57
|
+
...(hasUnsignedFunctionCall ? { thinkingConfig: { thinkingBudget: 0 } } : {}),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return this.mapResponse(response, ref.model);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (!isRetryableError(err))
|
|
64
|
+
throw err;
|
|
65
|
+
this.logger.warn(`Google model '${ref.model}' failed with retryable error, trying next`, err instanceof Error ? { message: err.message } : { err });
|
|
66
|
+
lastError = err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw lastError ?? new Error('No Google model could handle the request');
|
|
49
70
|
}
|
|
50
|
-
mapChatItems(chatItems) {
|
|
71
|
+
async mapChatItems(chatItems) {
|
|
51
72
|
const contents = [];
|
|
52
73
|
for (const chatItem of chatItems) {
|
|
53
74
|
switch (chatItem.type) {
|
|
54
75
|
case 'humanMessage':
|
|
55
|
-
contents.push(this.mapHumanMessage(chatItem.humanMessage));
|
|
76
|
+
contents.push(await this.mapHumanMessage(chatItem.humanMessage));
|
|
56
77
|
break;
|
|
57
78
|
case 'botMessage':
|
|
58
79
|
contents.push(this.mapBotMessage(chatItem.botMessage));
|
|
@@ -64,7 +85,7 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
64
85
|
}
|
|
65
86
|
return contents;
|
|
66
87
|
}
|
|
67
|
-
mapHumanMessage(item) {
|
|
88
|
+
async mapHumanMessage(item) {
|
|
68
89
|
if (isChatMessageEmpty(item)) {
|
|
69
90
|
throw new Error('User message content is empty');
|
|
70
91
|
}
|
|
@@ -75,25 +96,32 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
75
96
|
supportedDocumentMimeTypes: GOOGLE_SUPPORTED_DOCUMENT_MIME_TYPES,
|
|
76
97
|
}),
|
|
77
98
|
});
|
|
99
|
+
const filesToSend = [];
|
|
78
100
|
if (item.images) {
|
|
79
101
|
for (const image of item.images) {
|
|
80
102
|
if (!GOOGLE_SUPPORTED_IMAGE_MIME_TYPES.includes(image.mimeType))
|
|
81
103
|
continue;
|
|
82
|
-
|
|
104
|
+
filesToSend.push(image);
|
|
83
105
|
}
|
|
84
106
|
}
|
|
85
107
|
if (item.documents) {
|
|
86
108
|
for (const doc of item.documents) {
|
|
87
109
|
if (!GOOGLE_SUPPORTED_DOCUMENT_MIME_TYPES.includes(doc.mimeType))
|
|
88
110
|
continue;
|
|
89
|
-
|
|
111
|
+
filesToSend.push(doc);
|
|
90
112
|
}
|
|
91
113
|
}
|
|
114
|
+
const fileParts = await Promise.all(filesToSend.map((f) => this.toGoogleFilePart(f)));
|
|
115
|
+
parts.push(...fileParts);
|
|
92
116
|
return { role: 'user', parts };
|
|
93
117
|
}
|
|
94
|
-
toGoogleFilePart(file) {
|
|
118
|
+
async toGoogleFilePart(file) {
|
|
95
119
|
if (file.publicUrl) {
|
|
96
|
-
|
|
120
|
+
if (isGoogleNativeFileUri(file.publicUrl)) {
|
|
121
|
+
return { fileData: { fileUri: file.publicUrl, mimeType: file.mimeType } };
|
|
122
|
+
}
|
|
123
|
+
const data = await fetchAsBase64(file.publicUrl);
|
|
124
|
+
return { inlineData: { data, mimeType: file.mimeType } };
|
|
97
125
|
}
|
|
98
126
|
return {
|
|
99
127
|
inlineData: { data: stripDataUrlPrefix(file.base64Url), mimeType: file.mimeType },
|
|
@@ -106,18 +134,20 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
106
134
|
return { role: 'model', parts: [{ text: extractChatMessageText(item) }] };
|
|
107
135
|
}
|
|
108
136
|
mapFunctionCall(item) {
|
|
137
|
+
const callPart = {
|
|
138
|
+
functionCall: {
|
|
139
|
+
id: item.id,
|
|
140
|
+
name: item.name,
|
|
141
|
+
args: safeJsonParse(item.arguments, 'function call arguments'),
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
if (item.signature) {
|
|
145
|
+
callPart.thoughtSignature = item.signature;
|
|
146
|
+
}
|
|
109
147
|
return [
|
|
110
148
|
{
|
|
111
149
|
role: 'model',
|
|
112
|
-
parts: [
|
|
113
|
-
{
|
|
114
|
-
functionCall: {
|
|
115
|
-
id: item.id,
|
|
116
|
-
name: item.name,
|
|
117
|
-
args: safeJsonParse(item.arguments, 'function call arguments'),
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
],
|
|
150
|
+
parts: [callPart],
|
|
121
151
|
},
|
|
122
152
|
{
|
|
123
153
|
role: 'function',
|
|
@@ -148,7 +178,7 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
148
178
|
},
|
|
149
179
|
};
|
|
150
180
|
}
|
|
151
|
-
mapResponse(response) {
|
|
181
|
+
mapResponse(response, modelName) {
|
|
152
182
|
if (!response.candidates || !response.candidates.length) {
|
|
153
183
|
throw new Error('No candidates in response');
|
|
154
184
|
}
|
|
@@ -177,18 +207,24 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
177
207
|
id: id ?? Random.alphaNumericLowerCase(10),
|
|
178
208
|
name,
|
|
179
209
|
arguments: args && JSON.stringify(args),
|
|
210
|
+
signature: part.thoughtSignature,
|
|
180
211
|
},
|
|
181
212
|
});
|
|
182
213
|
}
|
|
183
214
|
}
|
|
215
|
+
const cachedTokens = response.usageMetadata.cachedContentTokenCount ?? 0;
|
|
184
216
|
let usage = {
|
|
185
217
|
inputTokens: response.usageMetadata.promptTokenCount,
|
|
186
218
|
outputTokens: response.usageMetadata.candidatesTokenCount,
|
|
219
|
+
cacheReadTokens: cachedTokens || undefined,
|
|
220
|
+
provider: 'google',
|
|
221
|
+
model: response.modelVersion ?? modelName,
|
|
187
222
|
};
|
|
188
223
|
return { usage, nextItems };
|
|
189
224
|
}
|
|
190
225
|
};
|
|
191
226
|
GoogleChatAdapter = __decorate([
|
|
227
|
+
chatAdapter({ provider: 'google' }),
|
|
192
228
|
singleton(),
|
|
193
229
|
__metadata("design:paramtypes", [Env])
|
|
194
230
|
], GoogleChatAdapter);
|
|
@@ -196,5 +232,20 @@ function stripDataUrlPrefix(dataUrl) {
|
|
|
196
232
|
const commaIndex = dataUrl.indexOf(',');
|
|
197
233
|
return commaIndex >= 0 && dataUrl.startsWith('data:') ? dataUrl.slice(commaIndex + 1) : dataUrl;
|
|
198
234
|
}
|
|
235
|
+
function isGoogleNativeFileUri(url) {
|
|
236
|
+
if (url.startsWith('gs://'))
|
|
237
|
+
return true;
|
|
238
|
+
if (url.startsWith('https://generativelanguage.googleapis.com/'))
|
|
239
|
+
return true;
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
async function fetchAsBase64(url) {
|
|
243
|
+
const res = await fetch(url);
|
|
244
|
+
if (!res.ok) {
|
|
245
|
+
throw new Error(`Failed to fetch '${url}': ${res.status} ${res.statusText}`);
|
|
246
|
+
}
|
|
247
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
248
|
+
return buf.toString('base64');
|
|
249
|
+
}
|
|
199
250
|
|
|
200
251
|
export { GoogleChatAdapter };
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { __decorate } from 'tslib';
|
|
2
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
2
3
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
3
4
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
5
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
6
|
+
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
7
|
+
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
8
|
+
import { isRetryableError } from '../../../feature/chat-bot/isRetryableError.js';
|
|
9
|
+
import { chatAdapter } from '../../../feature/chat-bot/metadata/@chatAdapter.js';
|
|
4
10
|
import { singleton } from '../../../core/injection/index.js';
|
|
5
11
|
import 'uuid';
|
|
6
12
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
13
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
7
14
|
import '../../../core/error/setupErrorHandlers.js';
|
|
8
|
-
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
9
|
-
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
10
15
|
import { Logger } from '../../../core/logger/Logger.js';
|
|
11
16
|
import { OpenAI } from 'openai';
|
|
12
17
|
|
|
@@ -25,12 +30,24 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
|
|
|
25
30
|
openIaInput.push({ role: 'system', content: req.systemPrompt });
|
|
26
31
|
openIaInput.push(...this.mapChatItems(req.prevItems));
|
|
27
32
|
const tools = req.tools.map((x) => this.mapTool(x));
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
let lastError;
|
|
34
|
+
for (const ref of req.models) {
|
|
35
|
+
try {
|
|
36
|
+
const response = await this.openai.responses.create({
|
|
37
|
+
model: ref.model,
|
|
38
|
+
input: openIaInput,
|
|
39
|
+
tools,
|
|
40
|
+
});
|
|
41
|
+
return this.mapResponse(response, ref.model);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (!isRetryableError(err))
|
|
45
|
+
throw err;
|
|
46
|
+
this.logger.warn(`OpenAI model '${ref.model}' failed with retryable error, trying next`, err instanceof Error ? { message: err.message } : { err });
|
|
47
|
+
lastError = err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw lastError ?? new Error('No OpenAI model could handle the request');
|
|
34
51
|
}
|
|
35
52
|
mapChatItems(chatItems) {
|
|
36
53
|
const openIaInput = [];
|
|
@@ -128,12 +145,16 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
|
|
|
128
145
|
strict: true,
|
|
129
146
|
};
|
|
130
147
|
}
|
|
131
|
-
mapResponse(response) {
|
|
148
|
+
mapResponse(response, modelName) {
|
|
132
149
|
let usage;
|
|
133
150
|
if (response.usage) {
|
|
151
|
+
const cacheRead = response.usage.input_tokens_details?.cached_tokens ?? 0;
|
|
134
152
|
usage = {
|
|
135
153
|
inputTokens: response.usage.input_tokens,
|
|
136
154
|
outputTokens: response.usage.output_tokens,
|
|
155
|
+
cacheReadTokens: cacheRead || undefined,
|
|
156
|
+
provider: 'openai',
|
|
157
|
+
model: response.model ?? modelName,
|
|
137
158
|
};
|
|
138
159
|
}
|
|
139
160
|
else {
|
|
@@ -184,6 +205,7 @@ let OpenaiChatAdapter = class OpenaiChatAdapter {
|
|
|
184
205
|
}
|
|
185
206
|
};
|
|
186
207
|
OpenaiChatAdapter = __decorate([
|
|
208
|
+
chatAdapter({ provider: 'openai' }),
|
|
187
209
|
singleton()
|
|
188
210
|
], OpenaiChatAdapter);
|
|
189
211
|
|
|
@@ -2,13 +2,17 @@ import { __decorate, __metadata } from 'tslib';
|
|
|
2
2
|
import { Env } from '../../../core/env/Env.js';
|
|
3
3
|
import { singleton } from '../../../core/injection/index.js';
|
|
4
4
|
import { Logger } from '../../../core/logger/Logger.js';
|
|
5
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
5
6
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
6
7
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
8
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
9
|
+
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
10
|
+
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
11
|
+
import { chatAdapter } from '../../../feature/chat-bot/metadata/@chatAdapter.js';
|
|
7
12
|
import 'uuid';
|
|
8
13
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
14
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
9
15
|
import '../../../core/error/setupErrorHandlers.js';
|
|
10
|
-
import { extractChatMessageText } from '../../../feature/chat-bot/extractChatMessageText.js';
|
|
11
|
-
import { isChatMessageEmpty } from '../../../feature/chat-bot/isChatMessageEmpty.js';
|
|
12
16
|
import { OpenRouter } from '@openrouter/sdk';
|
|
13
17
|
|
|
14
18
|
const OPENROUTER_SUPPORTED_IMAGE_MIME_TYPES = [
|
|
@@ -32,10 +36,13 @@ let OpenRouterChatAdapter = class OpenRouterChatAdapter {
|
|
|
32
36
|
messages.push({ role: 'system', content: req.systemPrompt });
|
|
33
37
|
messages.push(...this.mapChatItems(req.prevItems));
|
|
34
38
|
const tools = req.tools.map((x) => this.mapTool(x));
|
|
35
|
-
|
|
39
|
+
const modelNames = req.models.map((m) => m.model);
|
|
40
|
+
const [primary, ...fallbacks] = modelNames;
|
|
41
|
+
this.logger.debug(`Call OpenRouter with model: ${primary}, fallbacks: ${fallbacks.length}, messages: ${messages.length}, tools: ${tools.length}`);
|
|
36
42
|
const response = await this.openRouter.chat.send({
|
|
37
43
|
chatRequest: {
|
|
38
|
-
model:
|
|
44
|
+
model: primary,
|
|
45
|
+
models: fallbacks.length > 0 ? fallbacks : undefined,
|
|
39
46
|
messages,
|
|
40
47
|
tools: tools.length > 0 ? tools : undefined,
|
|
41
48
|
},
|
|
@@ -160,9 +167,20 @@ let OpenRouterChatAdapter = class OpenRouterChatAdapter {
|
|
|
160
167
|
}
|
|
161
168
|
let usage;
|
|
162
169
|
if (response.usage) {
|
|
170
|
+
const cacheRead = response.usage.promptTokensDetails?.cachedTokens ?? 0;
|
|
171
|
+
const cacheWrite = response.usage.promptTokensDetails?.cacheWriteTokens ?? 0;
|
|
172
|
+
// OpenRouter exposes `cost` (and provider) on the response but the SDK
|
|
173
|
+
// types haven't surfaced them yet — read via cast.
|
|
174
|
+
const costUsd = response.usage.cost;
|
|
175
|
+
const upstreamProvider = response.provider;
|
|
163
176
|
usage = {
|
|
164
177
|
inputTokens: response.usage.promptTokens,
|
|
165
178
|
outputTokens: response.usage.completionTokens,
|
|
179
|
+
cacheReadTokens: cacheRead || undefined,
|
|
180
|
+
cacheWriteTokens: cacheWrite || undefined,
|
|
181
|
+
costUsd: typeof costUsd === 'number' ? costUsd : undefined,
|
|
182
|
+
provider: upstreamProvider ? `openrouter/${upstreamProvider}` : 'openrouter',
|
|
183
|
+
model: response.model,
|
|
166
184
|
};
|
|
167
185
|
}
|
|
168
186
|
else {
|
|
@@ -172,6 +190,7 @@ let OpenRouterChatAdapter = class OpenRouterChatAdapter {
|
|
|
172
190
|
}
|
|
173
191
|
};
|
|
174
192
|
OpenRouterChatAdapter = __decorate([
|
|
193
|
+
chatAdapter({ provider: 'openrouter' }),
|
|
175
194
|
singleton(),
|
|
176
195
|
__metadata("design:paramtypes", [Env])
|
|
177
196
|
], OpenRouterChatAdapter);
|
|
@@ -4,10 +4,15 @@ import 'debug';
|
|
|
4
4
|
import 'node:crypto';
|
|
5
5
|
import '../../../feature/pg/withPgClient.js';
|
|
6
6
|
import '../../../feature/pg/pgStorage.js';
|
|
7
|
+
import '../../../core/injection/index.js';
|
|
8
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
9
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
10
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
7
11
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
8
12
|
import { ChatItem } from '../../../feature/chat-bot/ChatItem.js';
|
|
9
13
|
import '../../../feature/chat-bot/ChatOperator.js';
|
|
10
|
-
import '../../../
|
|
14
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
15
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
11
16
|
import 'uuid';
|
|
12
17
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
13
18
|
import '../../../core/error/setupErrorHandlers.js';
|
|
@@ -8,9 +8,14 @@ import 'debug';
|
|
|
8
8
|
import 'node:crypto';
|
|
9
9
|
import '../../../feature/pg/withPgClient.js';
|
|
10
10
|
import '../../../feature/pg/pgStorage.js';
|
|
11
|
+
import '../../../feature/pg/query/PgJsonRepository.js';
|
|
12
|
+
import '../../../feature/pg/query/PgRepositoryMetadataStore.js';
|
|
11
13
|
import { Chat } from '../../../feature/chat-bot/Chat.js';
|
|
14
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
12
15
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
13
16
|
import { ChatOperator } from '../../../feature/chat-bot/ChatOperator.js';
|
|
17
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
18
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
14
19
|
import 'uuid';
|
|
15
20
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
16
21
|
import '../../../core/error/setupErrorHandlers.js';
|
|
@@ -2,8 +2,11 @@ import { __decorate } from 'tslib';
|
|
|
2
2
|
import { v4 } from 'uuid';
|
|
3
3
|
import { RamChatMemory } from './RamChatMemory.js';
|
|
4
4
|
import { singleton } from '../../../core/injection/index.js';
|
|
5
|
+
import '../../../feature/chat-bot/ChatAdapterRegistry.js';
|
|
5
6
|
import '../../../feature/chat-bot/ChatBot.js';
|
|
6
7
|
import { ChatOperator } from '../../../feature/chat-bot/ChatOperator.js';
|
|
8
|
+
import '../../../feature/chat-bot/UnionChatAdapter.js';
|
|
9
|
+
import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
|
|
7
10
|
import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
|
|
8
11
|
import '../../../core/error/setupErrorHandlers.js';
|
|
9
12
|
|