@tuturuuu/ai 0.0.11 → 0.1.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/package.json +44 -38
- package/src/chat/google/chat-request-schema.ts +3 -1
- package/src/chat/google/new/route.ts +12 -6
- package/src/chat/google/route-chat-resolution.ts +33 -2
- package/src/chat/google/route-mira-runtime.ts +101 -39
- package/src/chat/google/route.ts +17 -8
- package/src/chat-sdk/zalo-personal.ts +507 -0
- package/src/credits/check-credits.ts +3 -36
- package/src/credits/model-mapping.ts +1 -1
- package/src/tools/executors/markitdown.ts +0 -75
- package/src/tools/executors/parallel-checks.ts +165 -11
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { google } from '@ai-sdk/google';
|
|
2
|
-
import {
|
|
2
|
+
import { createAdminClient } from '@tuturuuu/supabase/next/server';
|
|
3
|
+
import {
|
|
4
|
+
gateway,
|
|
5
|
+
type LanguageModelUsage,
|
|
6
|
+
stepCountIs,
|
|
7
|
+
ToolLoopAgent,
|
|
8
|
+
} from 'ai';
|
|
3
9
|
import { z } from 'zod';
|
|
10
|
+
import { capMaxOutputTokensByCredits } from '../../credits/cap-output-tokens';
|
|
11
|
+
import { checkAiCredits, deductAiCredits } from '../../credits/check-credits';
|
|
12
|
+
import {
|
|
13
|
+
GEMINI_31_FLASH_LITE_MODEL,
|
|
14
|
+
isGoogleModelId,
|
|
15
|
+
toBareModelName,
|
|
16
|
+
} from '../../credits/model-mapping';
|
|
17
|
+
import {
|
|
18
|
+
PlanModelResolutionError,
|
|
19
|
+
resolvePlanModel,
|
|
20
|
+
} from '../../credits/resolve-plan-model';
|
|
4
21
|
import { withAiMemory } from '../../memory';
|
|
5
22
|
import type { MiraToolContext } from '../mira-tools';
|
|
6
23
|
|
|
7
|
-
const PARALLEL_CHECKS_MODEL =
|
|
24
|
+
const PARALLEL_CHECKS_MODEL = GEMINI_31_FLASH_LITE_MODEL;
|
|
8
25
|
|
|
9
26
|
const ParallelChecksArgsSchema = z.object({
|
|
10
27
|
question: z
|
|
@@ -37,6 +54,12 @@ type ParallelCheckResult = {
|
|
|
37
54
|
finding: string;
|
|
38
55
|
};
|
|
39
56
|
|
|
57
|
+
type MeteredUsage = {
|
|
58
|
+
inputTokens: number;
|
|
59
|
+
outputTokens: number;
|
|
60
|
+
reasoningTokens: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
40
63
|
const CHECK_INSTRUCTIONS: Record<CheckKind, string> = {
|
|
41
64
|
assumptions:
|
|
42
65
|
'You identify hidden assumptions, missing premises, and unclear decision points. Return concise findings only.',
|
|
@@ -69,26 +92,79 @@ ${context ? `Relevant context:\n${context}\n\n` : ''}Return:
|
|
|
69
92
|
- "No material issues" if this perspective has nothing important`;
|
|
70
93
|
}
|
|
71
94
|
|
|
95
|
+
function getCreditCheckErrorMessage(creditCheck: {
|
|
96
|
+
errorCode?: string | null;
|
|
97
|
+
errorMessage?: string | null;
|
|
98
|
+
}) {
|
|
99
|
+
const errorMessages: Record<string, string> = {
|
|
100
|
+
CREDIT_CHECK_FAILED: 'AI credit check failed. Please try again.',
|
|
101
|
+
CREDITS_EXHAUSTED: 'You have run out of AI credits for parallel checks.',
|
|
102
|
+
FEATURE_NOT_ALLOWED:
|
|
103
|
+
'Parallel checks are not available on your current plan.',
|
|
104
|
+
MODEL_NOT_ALLOWED:
|
|
105
|
+
'The parallel-checks model is not enabled for your workspace.',
|
|
106
|
+
NO_ALLOCATION: 'AI credits are not configured for your workspace.',
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
creditCheck.errorMessage ??
|
|
111
|
+
errorMessages[creditCheck.errorCode ?? ''] ??
|
|
112
|
+
'Parallel checks are not available. Please check your AI credit settings.'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function normalizeTokenCount(value: number | undefined) {
|
|
117
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return 0;
|
|
118
|
+
return Math.max(0, Math.trunc(value));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function extractMeteredUsage(usage: LanguageModelUsage): MeteredUsage {
|
|
122
|
+
return {
|
|
123
|
+
inputTokens: normalizeTokenCount(usage.inputTokens),
|
|
124
|
+
outputTokens: normalizeTokenCount(usage.outputTokens),
|
|
125
|
+
reasoningTokens: normalizeTokenCount(
|
|
126
|
+
usage.outputTokenDetails.reasoningTokens ?? usage.reasoningTokens
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getPerCheckMaxOutputTokens(
|
|
132
|
+
cappedMaxOutput: number | null,
|
|
133
|
+
checkCount: number
|
|
134
|
+
) {
|
|
135
|
+
if (cappedMaxOutput === null) return undefined;
|
|
136
|
+
return Math.max(1, Math.floor(cappedMaxOutput / checkCount));
|
|
137
|
+
}
|
|
138
|
+
|
|
72
139
|
async function runCheck({
|
|
73
140
|
abortSignal,
|
|
141
|
+
billingWsId,
|
|
74
142
|
check,
|
|
75
143
|
context,
|
|
144
|
+
maxOutputTokens,
|
|
145
|
+
modelId,
|
|
76
146
|
question,
|
|
77
147
|
toolContext,
|
|
78
148
|
}: {
|
|
79
149
|
abortSignal?: AbortSignal;
|
|
150
|
+
billingWsId: string;
|
|
80
151
|
check: CheckKind;
|
|
81
152
|
context?: string;
|
|
153
|
+
maxOutputTokens?: number;
|
|
154
|
+
modelId: string;
|
|
82
155
|
question: string;
|
|
83
156
|
toolContext: MiraToolContext;
|
|
84
157
|
}): Promise<ParallelCheckResult> {
|
|
158
|
+
const useGoogleNativeModel = isGoogleModelId(modelId);
|
|
85
159
|
const agent = new ToolLoopAgent({
|
|
86
160
|
model: await withAiMemory({
|
|
87
161
|
addMemory: 'never',
|
|
88
162
|
customId: toolContext.chatId
|
|
89
163
|
? `${toolContext.chatId}-parallel-checks-${check}`
|
|
90
164
|
: `parallel-checks-${check}`,
|
|
91
|
-
model:
|
|
165
|
+
model: useGoogleNativeModel
|
|
166
|
+
? google(toBareModelName(modelId))
|
|
167
|
+
: gateway(modelId),
|
|
92
168
|
product: 'mira',
|
|
93
169
|
source: 'mira_parallel_checks_tool',
|
|
94
170
|
surface: 'mira_parallel_checks_tool',
|
|
@@ -97,21 +173,51 @@ async function runCheck({
|
|
|
97
173
|
}),
|
|
98
174
|
instructions: CHECK_INSTRUCTIONS[check],
|
|
99
175
|
stopWhen: stepCountIs(2),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
176
|
+
...(useGoogleNativeModel
|
|
177
|
+
? {
|
|
178
|
+
providerOptions: {
|
|
179
|
+
google: {
|
|
180
|
+
thinkingConfig: {
|
|
181
|
+
thinkingBudget: 0,
|
|
182
|
+
includeThoughts: false,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
108
188
|
});
|
|
109
189
|
|
|
110
190
|
const result = await agent.generate({
|
|
111
191
|
prompt: buildPrompt({ check, context, question }),
|
|
112
192
|
abortSignal,
|
|
193
|
+
...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
|
|
113
194
|
});
|
|
114
195
|
|
|
196
|
+
const usage = extractMeteredUsage(result.totalUsage);
|
|
197
|
+
const deduction = await deductAiCredits({
|
|
198
|
+
wsId: billingWsId,
|
|
199
|
+
userId: toolContext.userId,
|
|
200
|
+
modelId,
|
|
201
|
+
inputTokens: usage.inputTokens,
|
|
202
|
+
outputTokens: usage.outputTokens,
|
|
203
|
+
reasoningTokens: usage.reasoningTokens,
|
|
204
|
+
feature: 'chat',
|
|
205
|
+
metadata: {
|
|
206
|
+
source: 'mira_parallel_checks_tool',
|
|
207
|
+
check,
|
|
208
|
+
creditWsId: billingWsId,
|
|
209
|
+
routeWsId: toolContext.wsId,
|
|
210
|
+
...(toolContext.chatId ? { chatId: toolContext.chatId } : {}),
|
|
211
|
+
...(toolContext.workspaceContext?.wsId
|
|
212
|
+
? { workspaceContextWsId: toolContext.workspaceContext.wsId }
|
|
213
|
+
: {}),
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!deduction.success) {
|
|
218
|
+
throw new Error('Failed to deduct AI credits for parallel checks.');
|
|
219
|
+
}
|
|
220
|
+
|
|
115
221
|
return {
|
|
116
222
|
label: check,
|
|
117
223
|
finding: result.text.trim() || 'No material issues.',
|
|
@@ -133,14 +239,55 @@ export async function executeParallelChecks(
|
|
|
133
239
|
|
|
134
240
|
const { context, question } = parsed.data;
|
|
135
241
|
const checks = parsed.data.checks ?? DEFAULT_CHECKS;
|
|
242
|
+
const billingWsId = ctx.creditWsId ?? ctx.wsId;
|
|
136
243
|
|
|
137
244
|
try {
|
|
245
|
+
const resolvedModel = await resolvePlanModel({
|
|
246
|
+
capability: 'language',
|
|
247
|
+
requestedModel: PARALLEL_CHECKS_MODEL,
|
|
248
|
+
wsId: billingWsId,
|
|
249
|
+
});
|
|
250
|
+
const modelId = resolvedModel.modelId;
|
|
251
|
+
const creditCheck = await checkAiCredits(billingWsId, modelId, 'chat', {
|
|
252
|
+
userId: ctx.userId,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (!creditCheck.allowed) {
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
error: getCreditCheckErrorMessage(creditCheck),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const sbAdmin = await createAdminClient();
|
|
263
|
+
const cappedMaxOutput = await capMaxOutputTokensByCredits(
|
|
264
|
+
sbAdmin,
|
|
265
|
+
modelId,
|
|
266
|
+
creditCheck.maxOutputTokens,
|
|
267
|
+
creditCheck.remainingCredits
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
if (cappedMaxOutput === null && creditCheck.remainingCredits <= 0) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: 'You have run out of AI credits for parallel checks.',
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const maxOutputTokens = getPerCheckMaxOutputTokens(
|
|
278
|
+
cappedMaxOutput,
|
|
279
|
+
checks.length
|
|
280
|
+
);
|
|
281
|
+
|
|
138
282
|
const results = await Promise.all(
|
|
139
283
|
checks.map((check) =>
|
|
140
284
|
runCheck({
|
|
141
285
|
abortSignal: options?.abortSignal,
|
|
286
|
+
billingWsId,
|
|
142
287
|
check,
|
|
143
288
|
context,
|
|
289
|
+
maxOutputTokens,
|
|
290
|
+
modelId,
|
|
144
291
|
question,
|
|
145
292
|
toolContext: ctx,
|
|
146
293
|
})
|
|
@@ -160,6 +307,13 @@ export async function executeParallelChecks(
|
|
|
160
307
|
checks: results,
|
|
161
308
|
};
|
|
162
309
|
} catch (error) {
|
|
310
|
+
if (error instanceof PlanModelResolutionError) {
|
|
311
|
+
return {
|
|
312
|
+
ok: false,
|
|
313
|
+
error: error.message,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
163
317
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
164
318
|
return {
|
|
165
319
|
ok: false,
|