@tuturuuu/ai 0.0.10
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/README.md +76 -0
- package/package.json +106 -0
- package/src/api-key-hash.ts +28 -0
- package/src/calendar/events.ts +34 -0
- package/src/calendar/route.ts +114 -0
- package/src/chat/credit-source.ts +1 -0
- package/src/chat/google/chat-request-schema.ts +150 -0
- package/src/chat/google/default-system-instruction.ts +198 -0
- package/src/chat/google/message-file-processing.ts +212 -0
- package/src/chat/google/mira-step-preparation.ts +221 -0
- package/src/chat/google/new/route.ts +368 -0
- package/src/chat/google/route-auth.ts +81 -0
- package/src/chat/google/route-chat-resolution.ts +98 -0
- package/src/chat/google/route-credits.ts +61 -0
- package/src/chat/google/route-message-preparation.ts +331 -0
- package/src/chat/google/route-mira-runtime.ts +206 -0
- package/src/chat/google/route.ts +632 -0
- package/src/chat/google/stream-finish-persistence.ts +722 -0
- package/src/chat/google/summary/route.ts +153 -0
- package/src/chat/mira-render-ui-policy.ts +540 -0
- package/src/chat/mira-system-instruction.ts +484 -0
- package/src/chat-sdk/adapters.ts +389 -0
- package/src/chat-sdk/registry.ts +197 -0
- package/src/chat-sdk.ts +33 -0
- package/src/core.ts +3 -0
- package/src/credits/cap-output-tokens.ts +90 -0
- package/src/credits/check-credits.ts +232 -0
- package/src/credits/constants.ts +30 -0
- package/src/credits/index.ts +46 -0
- package/src/credits/model-mapping.ts +92 -0
- package/src/credits/reservations.ts +514 -0
- package/src/credits/resolve-plan-model.ts +219 -0
- package/src/credits/sync-gateway-models.ts +351 -0
- package/src/credits/types.ts +109 -0
- package/src/credits/use-ai-credits.ts +3 -0
- package/src/embeddings/metered.ts +283 -0
- package/src/executions/route.ts +137 -0
- package/src/generate/route.ts +411 -0
- package/src/hooks.ts +7 -0
- package/src/meetings/summary/route.ts +7 -0
- package/src/meetings/transcription/route.ts +134 -0
- package/src/memory/client.ts +158 -0
- package/src/memory/config.ts +38 -0
- package/src/memory/index.ts +32 -0
- package/src/memory/ingest.ts +51 -0
- package/src/memory/middleware.ts +35 -0
- package/src/memory/operations.ts +480 -0
- package/src/memory/scope.ts +102 -0
- package/src/memory/settings.ts +121 -0
- package/src/memory/types.ts +101 -0
- package/src/memory/workspace.ts +36 -0
- package/src/memory.ts +1 -0
- package/src/mind/patch.ts +146 -0
- package/src/mind/route.ts +687 -0
- package/src/mind/tools.ts +1500 -0
- package/src/mind/types.ts +20 -0
- package/src/object/core.ts +3 -0
- package/src/object/flashcards/route.ts +140 -0
- package/src/object/quizzes/explanation/route.ts +145 -0
- package/src/object/quizzes/route.ts +142 -0
- package/src/object/types.ts +187 -0
- package/src/object/year-plan/route.ts +196 -0
- package/src/react.ts +1 -0
- package/src/scheduling/algorithm.ts +791 -0
- package/src/scheduling/default.ts +36 -0
- package/src/scheduling/duration-optimizer.ts +689 -0
- package/src/scheduling/index.ts +79 -0
- package/src/scheduling/priority-calculator.ts +187 -0
- package/src/scheduling/recurrence-calculator.ts +621 -0
- package/src/scheduling/templates.ts +892 -0
- package/src/scheduling/types.ts +136 -0
- package/src/scheduling/web-adapter.ts +308 -0
- package/src/scheduling.ts +6 -0
- package/src/supported-actions.ts +1 -0
- package/src/supported-providers.ts +6 -0
- package/src/tools/context-builder.ts +372 -0
- package/src/tools/core.ts +1 -0
- package/src/tools/definitions/calendar.ts +106 -0
- package/src/tools/definitions/finance.ts +197 -0
- package/src/tools/definitions/image.ts +74 -0
- package/src/tools/definitions/memory.ts +83 -0
- package/src/tools/definitions/meta.ts +154 -0
- package/src/tools/definitions/render-ui.ts +81 -0
- package/src/tools/definitions/tasks.ts +343 -0
- package/src/tools/definitions/time-tracking.ts +381 -0
- package/src/tools/definitions/workspace-context.ts +45 -0
- package/src/tools/definitions/workspace-user-chat.ts +111 -0
- package/src/tools/executors/calendar.ts +371 -0
- package/src/tools/executors/chat.ts +15 -0
- package/src/tools/executors/finance.ts +638 -0
- package/src/tools/executors/helpers/encryption.ts +107 -0
- package/src/tools/executors/image.ts +247 -0
- package/src/tools/executors/markitdown.ts +684 -0
- package/src/tools/executors/memory.ts +277 -0
- package/src/tools/executors/parallel-checks.ts +176 -0
- package/src/tools/executors/qr.ts +170 -0
- package/src/tools/executors/scope-helpers.ts +192 -0
- package/src/tools/executors/search.ts +149 -0
- package/src/tools/executors/settings.ts +40 -0
- package/src/tools/executors/tasks.ts +1087 -0
- package/src/tools/executors/theme.ts +23 -0
- package/src/tools/executors/timer/timer-categories-executor.ts +110 -0
- package/src/tools/executors/timer/timer-category-mutations.ts +240 -0
- package/src/tools/executors/timer/timer-goal-mutations.ts +323 -0
- package/src/tools/executors/timer/timer-goals-executor.ts +272 -0
- package/src/tools/executors/timer/timer-helpers.ts +372 -0
- package/src/tools/executors/timer/timer-mutation-schemas.ts +160 -0
- package/src/tools/executors/timer/timer-mutation-types.ts +212 -0
- package/src/tools/executors/timer/timer-mutations.ts +19 -0
- package/src/tools/executors/timer/timer-queries.ts +18 -0
- package/src/tools/executors/timer/timer-session-lifecycle.ts +299 -0
- package/src/tools/executors/timer/timer-session-mutations.ts +10 -0
- package/src/tools/executors/timer/timer-session-queries.ts +153 -0
- package/src/tools/executors/timer/timer-session-updates.ts +200 -0
- package/src/tools/executors/timer/timer-sessions-executor.ts +91 -0
- package/src/tools/executors/timer/timer-stats-executor.ts +157 -0
- package/src/tools/executors/timer.ts +22 -0
- package/src/tools/executors/user.ts +60 -0
- package/src/tools/executors/workspace.ts +135 -0
- package/src/tools/json-render-catalog.ts +875 -0
- package/src/tools/mira-tool-definitions.ts +55 -0
- package/src/tools/mira-tool-dispatcher.ts +265 -0
- package/src/tools/mira-tool-metadata.ts +164 -0
- package/src/tools/mira-tool-names.ts +95 -0
- package/src/tools/mira-tool-render-ui.ts +54 -0
- package/src/tools/mira-tool-types.ts +17 -0
- package/src/tools/mira-tools.ts +167 -0
- package/src/tools/normalize-render-ui-input.ts +321 -0
- package/src/tools/workspace-context.ts +233 -0
- package/src/types.ts +38 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { google } from '@ai-sdk/google';
|
|
2
|
+
import { checkAiCredits } from '@tuturuuu/ai/credits/check-credits';
|
|
3
|
+
import {
|
|
4
|
+
commitMeteredEmbeddingCredits,
|
|
5
|
+
releaseMeteredEmbeddingCredits,
|
|
6
|
+
reserveMeteredEmbeddingCredits,
|
|
7
|
+
} from '@tuturuuu/ai/credits/reservations';
|
|
8
|
+
import { embed } from 'ai';
|
|
9
|
+
|
|
10
|
+
export const GEMINI_EMBEDDING_2_MODEL_ID = 'gemini-embedding-2';
|
|
11
|
+
export const GEMINI_EMBEDDING_2_GATEWAY_MODEL_ID = 'google/gemini-embedding-2';
|
|
12
|
+
export const GEMINI_EMBEDDING_2_DIMENSIONS = 3072;
|
|
13
|
+
|
|
14
|
+
const DEFAULT_COUNT_TOKENS_ENDPOINT =
|
|
15
|
+
'https://generativelanguage.googleapis.com/v1beta/models';
|
|
16
|
+
|
|
17
|
+
export type MeteredEmbeddingTaskType =
|
|
18
|
+
| 'CLASSIFICATION'
|
|
19
|
+
| 'CLUSTERING'
|
|
20
|
+
| 'RETRIEVAL_DOCUMENT'
|
|
21
|
+
| 'RETRIEVAL_QUERY'
|
|
22
|
+
| 'SEMANTIC_SIMILARITY';
|
|
23
|
+
|
|
24
|
+
type MeteredEmbeddingSkipReason =
|
|
25
|
+
| 'credit_check_failed'
|
|
26
|
+
| 'credits_exhausted'
|
|
27
|
+
| 'embedding_generation_failed'
|
|
28
|
+
| 'empty_value'
|
|
29
|
+
| 'invalid_embedding_shape'
|
|
30
|
+
| 'missing_google_api_key'
|
|
31
|
+
| 'missing_scope'
|
|
32
|
+
| 'reservation_commit_failed'
|
|
33
|
+
| 'reservation_failed'
|
|
34
|
+
| 'token_count_failed';
|
|
35
|
+
|
|
36
|
+
export type MeteredEmbeddingResult =
|
|
37
|
+
| {
|
|
38
|
+
creditsDeducted: number;
|
|
39
|
+
embedding: number[];
|
|
40
|
+
inputTokens: number;
|
|
41
|
+
modelId: string;
|
|
42
|
+
ok: true;
|
|
43
|
+
skipped?: false;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
errorCode?: string | null;
|
|
47
|
+
ok: false;
|
|
48
|
+
reason: MeteredEmbeddingSkipReason | string;
|
|
49
|
+
skipped: true;
|
|
50
|
+
structuralDisable?: boolean;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type MeteredTextEmbeddingParams = {
|
|
54
|
+
metadata?: Record<string, unknown>;
|
|
55
|
+
source: string;
|
|
56
|
+
taskType: MeteredEmbeddingTaskType;
|
|
57
|
+
userId?: string | null;
|
|
58
|
+
value: string;
|
|
59
|
+
wsId?: string | null;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function isStructuralCreditError(errorCode?: string | null) {
|
|
63
|
+
return [
|
|
64
|
+
'CREDIT_CHECK_FAILED',
|
|
65
|
+
'FEATURE_NOT_ALLOWED',
|
|
66
|
+
'MODEL_DISABLED',
|
|
67
|
+
'MODEL_NOT_ALLOWED',
|
|
68
|
+
'MODEL_PRICING_UNAVAILABLE',
|
|
69
|
+
'NO_ALLOCATION',
|
|
70
|
+
'NO_BALANCE',
|
|
71
|
+
].includes(errorCode ?? '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function shouldDisableMemoryForMeteringReason(
|
|
75
|
+
result: MeteredEmbeddingResult
|
|
76
|
+
) {
|
|
77
|
+
if (result.ok) return false;
|
|
78
|
+
return (
|
|
79
|
+
result.structuralDisable ||
|
|
80
|
+
['missing_google_api_key', 'missing_scope'].includes(result.reason)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function googleApiKey() {
|
|
85
|
+
return process.env.GOOGLE_GENERATIVE_AI_API_KEY?.trim() ?? '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function countGeminiEmbeddingTokens({
|
|
89
|
+
apiKey,
|
|
90
|
+
modelId,
|
|
91
|
+
value,
|
|
92
|
+
}: {
|
|
93
|
+
apiKey: string;
|
|
94
|
+
modelId: string;
|
|
95
|
+
value: string;
|
|
96
|
+
}) {
|
|
97
|
+
const endpoint = (
|
|
98
|
+
process.env.GOOGLE_GENERATIVE_AI_COUNT_TOKENS_ENDPOINT ??
|
|
99
|
+
DEFAULT_COUNT_TOKENS_ENDPOINT
|
|
100
|
+
).replace(/\/+$/u, '');
|
|
101
|
+
const url = new URL(`${endpoint}/${modelId}:countTokens`);
|
|
102
|
+
url.searchParams.set('key', apiKey);
|
|
103
|
+
|
|
104
|
+
const response = await fetch(url, {
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
contents: [{ parts: [{ text: value }] }],
|
|
107
|
+
}),
|
|
108
|
+
headers: { 'Content-Type': 'application/json' },
|
|
109
|
+
method: 'POST',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
throw new Error(`CountTokens failed with HTTP ${response.status}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const json = (await response.json()) as {
|
|
117
|
+
totalTokens?: number;
|
|
118
|
+
total_tokens?: number;
|
|
119
|
+
};
|
|
120
|
+
const totalTokens = Number(json.totalTokens ?? json.total_tokens ?? 0);
|
|
121
|
+
if (!Number.isFinite(totalTokens) || totalTokens <= 0) {
|
|
122
|
+
throw new Error('CountTokens returned no billable tokens');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return Math.ceil(totalTokens);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function skipped(
|
|
129
|
+
reason: MeteredEmbeddingSkipReason | string,
|
|
130
|
+
options?: { errorCode?: string | null; structuralDisable?: boolean }
|
|
131
|
+
): MeteredEmbeddingResult {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
reason,
|
|
135
|
+
skipped: true,
|
|
136
|
+
...(options?.errorCode ? { errorCode: options.errorCode } : {}),
|
|
137
|
+
...(options?.structuralDisable
|
|
138
|
+
? { structuralDisable: options.structuralDisable }
|
|
139
|
+
: {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function createMeteredTextEmbedding({
|
|
144
|
+
metadata,
|
|
145
|
+
source,
|
|
146
|
+
taskType,
|
|
147
|
+
userId,
|
|
148
|
+
value,
|
|
149
|
+
wsId,
|
|
150
|
+
}: MeteredTextEmbeddingParams): Promise<MeteredEmbeddingResult> {
|
|
151
|
+
const text = value.trim();
|
|
152
|
+
if (!text) return skipped('empty_value');
|
|
153
|
+
if (!wsId || !userId) {
|
|
154
|
+
return skipped('missing_scope', { structuralDisable: true });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const apiKey = googleApiKey();
|
|
158
|
+
if (!apiKey) {
|
|
159
|
+
return skipped('missing_google_api_key', { structuralDisable: true });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const creditCheck = await checkAiCredits(
|
|
163
|
+
wsId,
|
|
164
|
+
GEMINI_EMBEDDING_2_GATEWAY_MODEL_ID,
|
|
165
|
+
'embeddings',
|
|
166
|
+
{ userId }
|
|
167
|
+
);
|
|
168
|
+
if (!creditCheck.allowed) {
|
|
169
|
+
return skipped(
|
|
170
|
+
creditCheck.errorCode === 'CREDITS_EXHAUSTED'
|
|
171
|
+
? 'credits_exhausted'
|
|
172
|
+
: 'credit_check_failed',
|
|
173
|
+
{
|
|
174
|
+
errorCode: creditCheck.errorCode,
|
|
175
|
+
structuralDisable: isStructuralCreditError(creditCheck.errorCode),
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let inputTokens: number;
|
|
181
|
+
try {
|
|
182
|
+
inputTokens = await countGeminiEmbeddingTokens({
|
|
183
|
+
apiKey,
|
|
184
|
+
modelId: GEMINI_EMBEDDING_2_MODEL_ID,
|
|
185
|
+
value: text,
|
|
186
|
+
});
|
|
187
|
+
} catch {
|
|
188
|
+
return skipped('token_count_failed', { structuralDisable: false });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const reservation = await reserveMeteredEmbeddingCredits({
|
|
192
|
+
inputTokens,
|
|
193
|
+
metadata: {
|
|
194
|
+
...(metadata ?? {}),
|
|
195
|
+
dimensions: GEMINI_EMBEDDING_2_DIMENSIONS,
|
|
196
|
+
inputTokens,
|
|
197
|
+
modelId: GEMINI_EMBEDDING_2_GATEWAY_MODEL_ID,
|
|
198
|
+
source,
|
|
199
|
+
taskType,
|
|
200
|
+
userId,
|
|
201
|
+
wsId,
|
|
202
|
+
},
|
|
203
|
+
modelId: GEMINI_EMBEDDING_2_GATEWAY_MODEL_ID,
|
|
204
|
+
userId,
|
|
205
|
+
wsId,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (!reservation.success || !reservation.reservationId) {
|
|
209
|
+
return skipped(
|
|
210
|
+
reservation.errorCode === 'INSUFFICIENT_CREDITS'
|
|
211
|
+
? 'credits_exhausted'
|
|
212
|
+
: 'reservation_failed',
|
|
213
|
+
{
|
|
214
|
+
errorCode: reservation.errorCode,
|
|
215
|
+
structuralDisable: isStructuralCreditError(reservation.errorCode),
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const result = await embed({
|
|
222
|
+
model: google.embedding(GEMINI_EMBEDDING_2_MODEL_ID),
|
|
223
|
+
providerOptions: {
|
|
224
|
+
google: {
|
|
225
|
+
outputDimensionality: GEMINI_EMBEDDING_2_DIMENSIONS,
|
|
226
|
+
taskType,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
value: text,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
!Array.isArray(result.embedding) ||
|
|
234
|
+
result.embedding.length !== GEMINI_EMBEDDING_2_DIMENSIONS
|
|
235
|
+
) {
|
|
236
|
+
await releaseMeteredEmbeddingCredits(reservation.reservationId, {
|
|
237
|
+
reason: 'invalid_embedding_shape',
|
|
238
|
+
source,
|
|
239
|
+
userId,
|
|
240
|
+
wsId,
|
|
241
|
+
});
|
|
242
|
+
return skipped('invalid_embedding_shape', { structuralDisable: false });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const commit = await commitMeteredEmbeddingCredits(
|
|
246
|
+
reservation.reservationId,
|
|
247
|
+
{
|
|
248
|
+
actualInputTokens: result.usage.tokens ?? inputTokens,
|
|
249
|
+
source,
|
|
250
|
+
userId,
|
|
251
|
+
wsId,
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
if (!commit.success) {
|
|
255
|
+
await releaseMeteredEmbeddingCredits(reservation.reservationId, {
|
|
256
|
+
reason: 'reservation_commit_failed',
|
|
257
|
+
source,
|
|
258
|
+
userId,
|
|
259
|
+
wsId,
|
|
260
|
+
});
|
|
261
|
+
return skipped('reservation_commit_failed', {
|
|
262
|
+
errorCode: commit.errorCode,
|
|
263
|
+
structuralDisable: false,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
creditsDeducted: commit.creditsDeducted,
|
|
269
|
+
embedding: result.embedding,
|
|
270
|
+
inputTokens,
|
|
271
|
+
modelId: GEMINI_EMBEDDING_2_GATEWAY_MODEL_ID,
|
|
272
|
+
ok: true,
|
|
273
|
+
};
|
|
274
|
+
} catch {
|
|
275
|
+
await releaseMeteredEmbeddingCredits(reservation.reservationId, {
|
|
276
|
+
reason: 'embedding_generation_failed',
|
|
277
|
+
source,
|
|
278
|
+
userId,
|
|
279
|
+
wsId,
|
|
280
|
+
});
|
|
281
|
+
return skipped('embedding_generation_failed');
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createAdminClient } from '@tuturuuu/supabase/next/server';
|
|
2
|
+
import type { WorkspaceAIExecution } from '@tuturuuu/types';
|
|
3
|
+
import { ROOT_WORKSPACE_ID } from '@tuturuuu/utils/constants';
|
|
4
|
+
import { type NextRequest, NextResponse } from 'next/server';
|
|
5
|
+
import { validateApiKeyHash } from '../api-key-hash';
|
|
6
|
+
|
|
7
|
+
export function createPOST({
|
|
8
|
+
getLast30DaysStats,
|
|
9
|
+
getAllTimeStats,
|
|
10
|
+
}: {
|
|
11
|
+
getLast30DaysStats: (wsId: string) => Promise<{
|
|
12
|
+
summary: any;
|
|
13
|
+
dailyStats: any[];
|
|
14
|
+
modelStats: any[];
|
|
15
|
+
}>;
|
|
16
|
+
getAllTimeStats: (wsId: string) => Promise<{
|
|
17
|
+
summary: any;
|
|
18
|
+
dailyStats: any[];
|
|
19
|
+
modelStats: any[];
|
|
20
|
+
}>;
|
|
21
|
+
}) {
|
|
22
|
+
// Higher-order function that returns the actual request handler
|
|
23
|
+
return async function handler(req: NextRequest): Promise<Response> {
|
|
24
|
+
const sbAdmin = await createAdminClient();
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
accessKey,
|
|
28
|
+
query,
|
|
29
|
+
configs = {
|
|
30
|
+
wsId: ROOT_WORKSPACE_ID,
|
|
31
|
+
},
|
|
32
|
+
} = (await req.json()) as {
|
|
33
|
+
accessKey?: {
|
|
34
|
+
id: string;
|
|
35
|
+
value: string;
|
|
36
|
+
};
|
|
37
|
+
query?: {
|
|
38
|
+
q?: string;
|
|
39
|
+
page?: string;
|
|
40
|
+
pageSize?: string;
|
|
41
|
+
};
|
|
42
|
+
configs?: {
|
|
43
|
+
wsId: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (!accessKey?.id || !accessKey?.value) {
|
|
49
|
+
console.error('Missing accessId or accessKey');
|
|
50
|
+
return new Response('Missing accessId or accessKey', { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { data: apiKeyData, error: apiKeyError } = await sbAdmin
|
|
54
|
+
.from('workspace_api_keys')
|
|
55
|
+
.select('id, scopes, key_hash')
|
|
56
|
+
.eq('ws_id', configs.wsId)
|
|
57
|
+
.eq('id', accessKey.id)
|
|
58
|
+
.single();
|
|
59
|
+
|
|
60
|
+
const isValidAccessKey =
|
|
61
|
+
!apiKeyError &&
|
|
62
|
+
!!apiKeyData?.key_hash &&
|
|
63
|
+
(await validateApiKeyHash(accessKey.value, apiKeyData.key_hash));
|
|
64
|
+
|
|
65
|
+
if (!isValidAccessKey) {
|
|
66
|
+
console.error('Invalid accessId or accessKey');
|
|
67
|
+
return new Response('Invalid accessId or accessKey', { status: 400 });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const [executionData, analyticsData, allTimeStats] = await Promise.all([
|
|
71
|
+
getData(configs.wsId, query || {}),
|
|
72
|
+
getLast30DaysStats(configs.wsId),
|
|
73
|
+
getAllTimeStats(configs.wsId),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
return NextResponse.json({
|
|
77
|
+
executionData,
|
|
78
|
+
analyticsData,
|
|
79
|
+
allTimeStats,
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error instanceof Error) {
|
|
83
|
+
console.log(error.message);
|
|
84
|
+
return NextResponse.json(
|
|
85
|
+
{
|
|
86
|
+
message: `## Edge API Failure\nCould not complete the request. Please view the **Stack trace** below.\n\`\`\`bash\n${error instanceof Error ? error.stack : 'Unknown error'}`,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
status: 500,
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
console.log(error);
|
|
94
|
+
return new Response('Internal Server Error', { status: 500 });
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function getData(
|
|
100
|
+
wsId: string,
|
|
101
|
+
{
|
|
102
|
+
q,
|
|
103
|
+
page = '1',
|
|
104
|
+
pageSize = '10',
|
|
105
|
+
retry = true,
|
|
106
|
+
}: {
|
|
107
|
+
q?: string | undefined;
|
|
108
|
+
page?: string | undefined;
|
|
109
|
+
pageSize?: string | undefined;
|
|
110
|
+
retry?: boolean | undefined;
|
|
111
|
+
}
|
|
112
|
+
) {
|
|
113
|
+
const sbAdmin = await createAdminClient();
|
|
114
|
+
const queryBuilder = sbAdmin
|
|
115
|
+
.from('workspace_ai_executions')
|
|
116
|
+
.select('*', { count: 'exact' })
|
|
117
|
+
.eq('ws_id', wsId)
|
|
118
|
+
.order('created_at', { ascending: false });
|
|
119
|
+
|
|
120
|
+
if (page && pageSize) {
|
|
121
|
+
const parsedPage = parseInt(page, 10);
|
|
122
|
+
const parsedSize = parseInt(pageSize, 10);
|
|
123
|
+
const start = (parsedPage - 1) * parsedSize;
|
|
124
|
+
const end = parsedPage * parsedSize;
|
|
125
|
+
queryBuilder.range(start, end).limit(parsedSize);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const { data, error, count } = await queryBuilder;
|
|
129
|
+
if (error) {
|
|
130
|
+
if (!retry) throw error;
|
|
131
|
+
return getData(wsId, { q, pageSize, retry: false });
|
|
132
|
+
}
|
|
133
|
+
return { data, count } as {
|
|
134
|
+
data: WorkspaceAIExecution[];
|
|
135
|
+
count: number;
|
|
136
|
+
};
|
|
137
|
+
}
|