@tuturuuu/ai 0.8.1 → 0.8.3
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuturuuu/ai",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.3",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tutur3u/platform",
|
|
@@ -22,68 +22,68 @@
|
|
|
22
22
|
"type-check": "tsc --project tsconfig.typecheck.json"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@ai-sdk/amazon-bedrock": "^5.0.
|
|
26
|
-
"@ai-sdk/anthropic": "^4.0.
|
|
27
|
-
"@ai-sdk/azure": "^4.0.
|
|
28
|
-
"@ai-sdk/cerebras": "^3.0.
|
|
29
|
-
"@ai-sdk/cohere": "^4.0.
|
|
30
|
-
"@ai-sdk/deepgram": "^3.0.
|
|
31
|
-
"@ai-sdk/deepinfra": "^3.0.
|
|
32
|
-
"@ai-sdk/deepseek": "^3.0.
|
|
33
|
-
"@ai-sdk/elevenlabs": "^3.0.
|
|
34
|
-
"@ai-sdk/fal": "^3.0.
|
|
35
|
-
"@ai-sdk/fireworks": "^3.0.
|
|
36
|
-
"@ai-sdk/gateway": "^4.0.
|
|
37
|
-
"@ai-sdk/gladia": "^3.0.
|
|
38
|
-
"@ai-sdk/google": "^4.0.
|
|
39
|
-
"@ai-sdk/google-vertex": "^5.0.
|
|
40
|
-
"@ai-sdk/groq": "^4.0.
|
|
41
|
-
"@ai-sdk/hume": "^3.0.
|
|
42
|
-
"@ai-sdk/lmnt": "^3.0.
|
|
43
|
-
"@ai-sdk/luma": "^3.0.
|
|
44
|
-
"@ai-sdk/mistral": "^4.0.
|
|
45
|
-
"@ai-sdk/openai": "^4.0.
|
|
46
|
-
"@ai-sdk/openai-compatible": "^3.0.
|
|
47
|
-
"@ai-sdk/perplexity": "^4.0.
|
|
48
|
-
"@ai-sdk/react": "^4.0.
|
|
49
|
-
"@ai-sdk/replicate": "^3.0.
|
|
50
|
-
"@ai-sdk/revai": "^3.0.
|
|
51
|
-
"@ai-sdk/togetherai": "^3.0.
|
|
52
|
-
"@ai-sdk/xai": "^4.0.
|
|
25
|
+
"@ai-sdk/amazon-bedrock": "^5.0.60",
|
|
26
|
+
"@ai-sdk/anthropic": "^4.0.40",
|
|
27
|
+
"@ai-sdk/azure": "^4.0.46",
|
|
28
|
+
"@ai-sdk/cerebras": "^3.0.34",
|
|
29
|
+
"@ai-sdk/cohere": "^4.0.28",
|
|
30
|
+
"@ai-sdk/deepgram": "^3.0.28",
|
|
31
|
+
"@ai-sdk/deepinfra": "^3.0.34",
|
|
32
|
+
"@ai-sdk/deepseek": "^3.0.29",
|
|
33
|
+
"@ai-sdk/elevenlabs": "^3.0.29",
|
|
34
|
+
"@ai-sdk/fal": "^3.0.29",
|
|
35
|
+
"@ai-sdk/fireworks": "^3.0.37",
|
|
36
|
+
"@ai-sdk/gateway": "^4.0.59",
|
|
37
|
+
"@ai-sdk/gladia": "^3.0.28",
|
|
38
|
+
"@ai-sdk/google": "^4.0.49",
|
|
39
|
+
"@ai-sdk/google-vertex": "^5.0.60",
|
|
40
|
+
"@ai-sdk/groq": "^4.0.29",
|
|
41
|
+
"@ai-sdk/hume": "^3.0.28",
|
|
42
|
+
"@ai-sdk/lmnt": "^3.0.28",
|
|
43
|
+
"@ai-sdk/luma": "^3.0.29",
|
|
44
|
+
"@ai-sdk/mistral": "^4.0.31",
|
|
45
|
+
"@ai-sdk/openai": "^4.0.45",
|
|
46
|
+
"@ai-sdk/openai-compatible": "^3.0.34",
|
|
47
|
+
"@ai-sdk/perplexity": "^4.0.30",
|
|
48
|
+
"@ai-sdk/react": "^4.0.76",
|
|
49
|
+
"@ai-sdk/replicate": "^3.0.29",
|
|
50
|
+
"@ai-sdk/revai": "^3.0.28",
|
|
51
|
+
"@ai-sdk/togetherai": "^3.0.35",
|
|
52
|
+
"@ai-sdk/xai": "^4.0.42",
|
|
53
53
|
"@beeper/chat-adapter-matrix": "^0.2.0",
|
|
54
54
|
"@bitbasti/chat-adapter-webex": "^0.1.0",
|
|
55
|
-
"@chat-adapter/discord": "^4.
|
|
56
|
-
"@chat-adapter/gchat": "^4.
|
|
57
|
-
"@chat-adapter/github": "^4.
|
|
58
|
-
"@chat-adapter/linear": "^4.
|
|
59
|
-
"@chat-adapter/messenger": "^4.
|
|
60
|
-
"@chat-adapter/slack": "^4.
|
|
61
|
-
"@chat-adapter/state-ioredis": "^4.
|
|
62
|
-
"@chat-adapter/state-memory": "^4.
|
|
63
|
-
"@chat-adapter/state-pg": "^4.
|
|
64
|
-
"@chat-adapter/state-redis": "^4.
|
|
65
|
-
"@chat-adapter/teams": "^4.
|
|
66
|
-
"@chat-adapter/telegram": "^4.
|
|
67
|
-
"@chat-adapter/web": "^4.
|
|
68
|
-
"@chat-adapter/whatsapp": "^4.
|
|
69
|
-
"@json-render/core": "^0.
|
|
70
|
-
"@json-render/react": "^0.
|
|
71
|
-
"@liveblocks/chat-sdk-adapter": "^3.
|
|
55
|
+
"@chat-adapter/discord": "^4.38.1",
|
|
56
|
+
"@chat-adapter/gchat": "^4.38.1",
|
|
57
|
+
"@chat-adapter/github": "^4.38.1",
|
|
58
|
+
"@chat-adapter/linear": "^4.38.1",
|
|
59
|
+
"@chat-adapter/messenger": "^4.38.1",
|
|
60
|
+
"@chat-adapter/slack": "^4.38.1",
|
|
61
|
+
"@chat-adapter/state-ioredis": "^4.38.1",
|
|
62
|
+
"@chat-adapter/state-memory": "^4.38.1",
|
|
63
|
+
"@chat-adapter/state-pg": "^4.38.1",
|
|
64
|
+
"@chat-adapter/state-redis": "^4.38.1",
|
|
65
|
+
"@chat-adapter/teams": "^4.38.1",
|
|
66
|
+
"@chat-adapter/telegram": "^4.38.1",
|
|
67
|
+
"@chat-adapter/web": "^4.38.1",
|
|
68
|
+
"@chat-adapter/whatsapp": "^4.38.1",
|
|
69
|
+
"@json-render/core": "^0.20.0",
|
|
70
|
+
"@json-render/react": "^0.20.0",
|
|
71
|
+
"@liveblocks/chat-sdk-adapter": "^3.24.1",
|
|
72
72
|
"@octokit/rest": "^22.0.1",
|
|
73
|
-
"@resend/chat-sdk-adapter": "^0.
|
|
73
|
+
"@resend/chat-sdk-adapter": "^0.3.0",
|
|
74
74
|
"@streamdown/cjk": "^1.0.3",
|
|
75
75
|
"@streamdown/code": "^1.1.1",
|
|
76
76
|
"@streamdown/math": "^1.0.2",
|
|
77
77
|
"@streamdown/mermaid": "^1.0.2",
|
|
78
78
|
"@tuturuuu/google": "0.1.0",
|
|
79
|
-
"@tuturuuu/internal-api": "0.
|
|
80
|
-
"@tuturuuu/supabase": "0.5.
|
|
81
|
-
"@tuturuuu/utils": "0.
|
|
82
|
-
"@vercel/sandbox": "^
|
|
83
|
-
"@zernio/chat-sdk-adapter": "^0.5.
|
|
84
|
-
"ai": "^7.0.
|
|
79
|
+
"@tuturuuu/internal-api": "0.31.1",
|
|
80
|
+
"@tuturuuu/supabase": "0.5.1",
|
|
81
|
+
"@tuturuuu/utils": "0.25.4",
|
|
82
|
+
"@vercel/sandbox": "^3.0.1",
|
|
83
|
+
"@zernio/chat-sdk-adapter": "^0.5.1",
|
|
84
|
+
"ai": "^7.0.73",
|
|
85
85
|
"bash-tool": "^1.3.18",
|
|
86
|
-
"chat": "^4.
|
|
86
|
+
"chat": "^4.38.1",
|
|
87
87
|
"chat-adapter-baileys": "^2.1.0",
|
|
88
88
|
"chat-adapter-blooio": "^0.1.0",
|
|
89
89
|
"chat-adapter-imessage": "^1.1.0",
|
|
@@ -92,20 +92,20 @@
|
|
|
92
92
|
"chat-adapter-zalo": "^0.1.0",
|
|
93
93
|
"chat-state-cloudflare-do": "^0.2.0",
|
|
94
94
|
"chat-state-mysql": "^0.1.0",
|
|
95
|
-
"dayjs": "^1.11.
|
|
96
|
-
"next": "^16.3.
|
|
95
|
+
"dayjs": "^1.11.23",
|
|
96
|
+
"next": "^16.3.2",
|
|
97
97
|
"qrcode": "^1.5.4",
|
|
98
98
|
"react": "^19.2.8",
|
|
99
99
|
"react-dom": "^19.2.8",
|
|
100
100
|
"streamdown": "^2.5.0",
|
|
101
|
-
"uuid": "^14.0.
|
|
101
|
+
"uuid": "^14.0.2",
|
|
102
102
|
"zca-js": "^2.1.2",
|
|
103
103
|
"zod": "^4.4.3"
|
|
104
104
|
},
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"@tuturuuu/types": "0.27.0",
|
|
107
|
-
"@tuturuuu/typescript-config": "0.1.
|
|
108
|
-
"@types/node": "^26.
|
|
107
|
+
"@tuturuuu/typescript-config": "0.1.2",
|
|
108
|
+
"@types/node": "^26.2.0",
|
|
109
109
|
"@types/qrcode": "^1.5.6",
|
|
110
110
|
"@types/react": "^19.2.18",
|
|
111
111
|
"typescript": "7.0.2"
|
|
@@ -113,5 +113,5 @@
|
|
|
113
113
|
"exports": {
|
|
114
114
|
"./*": "./src/*.ts"
|
|
115
115
|
},
|
|
116
|
-
"packageManager": "bun@1.
|
|
116
|
+
"packageManager": "bun@1.4.0"
|
|
117
117
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const MAX_INLINE_TRANSCRIPTION_AUDIO_BYTES = 18 * 1024 * 1024;
|
|
2
|
+
export const TRANSCRIPTION_MULTIPART_HEADROOM_BYTES = 1024 * 1024;
|
|
3
|
+
export const MAX_TRANSCRIPTION_MULTIPART_REQUEST_BYTES =
|
|
4
|
+
MAX_INLINE_TRANSCRIPTION_AUDIO_BYTES + TRANSCRIPTION_MULTIPART_HEADROOM_BYTES;
|
|
5
|
+
|
|
6
|
+
const TRANSCRIPTION_AUDIO_MEDIA_TYPES = [
|
|
7
|
+
'audio/webm',
|
|
8
|
+
'audio/mp4',
|
|
9
|
+
'audio/ogg',
|
|
10
|
+
'audio/mpeg',
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
export type TranscriptionAudioMediaType =
|
|
14
|
+
(typeof TRANSCRIPTION_AUDIO_MEDIA_TYPES)[number];
|
|
15
|
+
|
|
16
|
+
type TranscriptionAudioValidation =
|
|
17
|
+
| { mediaType: TranscriptionAudioMediaType; ok: true }
|
|
18
|
+
| { message: string; ok: false; status: 400 | 413 | 415 };
|
|
19
|
+
|
|
20
|
+
export function validateTranscriptionAudioFile(
|
|
21
|
+
audio: Pick<File, 'size' | 'type'>
|
|
22
|
+
): TranscriptionAudioValidation {
|
|
23
|
+
if (audio.size <= 0) {
|
|
24
|
+
return { message: 'The recording is empty.', ok: false, status: 400 };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (audio.size > MAX_INLINE_TRANSCRIPTION_AUDIO_BYTES) {
|
|
28
|
+
return {
|
|
29
|
+
message: 'The recording exceeds the inline transcription limit.',
|
|
30
|
+
ok: false,
|
|
31
|
+
status: 413,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const baseMediaType = audio.type.split(';', 1)[0]?.trim().toLowerCase();
|
|
36
|
+
const mediaType = TRANSCRIPTION_AUDIO_MEDIA_TYPES.find(
|
|
37
|
+
(allowedMediaType) => allowedMediaType === baseMediaType
|
|
38
|
+
);
|
|
39
|
+
if (!mediaType) {
|
|
40
|
+
return {
|
|
41
|
+
message:
|
|
42
|
+
'The recording format is not supported for inline transcription.',
|
|
43
|
+
ok: false,
|
|
44
|
+
status: 415,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { mediaType, ok: true };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function hasOversizedDeclaredContentLength(req: Request) {
|
|
52
|
+
const contentLength = req.headers.get('content-length')?.trim();
|
|
53
|
+
if (!contentLength || !/^\d+$/u.test(contentLength)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
BigInt(contentLength) > BigInt(MAX_TRANSCRIPTION_MULTIPART_REQUEST_BYTES)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -3,6 +3,10 @@ import { createClient } from '@tuturuuu/supabase/next/server';
|
|
|
3
3
|
import { generateObject } from 'ai';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { withAiMemory } from '../../memory';
|
|
6
|
+
import {
|
|
7
|
+
hasOversizedDeclaredContentLength,
|
|
8
|
+
validateTranscriptionAudioFile,
|
|
9
|
+
} from './input';
|
|
6
10
|
|
|
7
11
|
const DEFAULT_MODEL_NAME = 'gemini-3.1-flash-lite';
|
|
8
12
|
|
|
@@ -42,14 +46,23 @@ export function createPOST() {
|
|
|
42
46
|
return new Response('Unauthorized', { status: 401 });
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
if (hasOversizedDeclaredContentLength(req)) {
|
|
50
|
+
return new Response('Audio file too large', { status: 413 });
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
const formData = await req.formData();
|
|
46
|
-
const audioFile = formData.get('audio')
|
|
54
|
+
const audioFile = formData.get('audio');
|
|
47
55
|
const wsId = formData.get('wsId') as string | null;
|
|
48
56
|
|
|
49
|
-
if (!audioFile) {
|
|
57
|
+
if (!(audioFile instanceof File)) {
|
|
50
58
|
return new Response('No audio file provided', { status: 400 });
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
const validation = validateTranscriptionAudioFile(audioFile);
|
|
62
|
+
if (!validation.ok) {
|
|
63
|
+
return new Response(validation.message, { status: validation.status });
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
// Convert file to buffer
|
|
54
67
|
const audioBuffer = await audioFile.arrayBuffer();
|
|
55
68
|
const audioUint8Array = new Uint8Array(audioBuffer);
|
|
@@ -73,7 +86,7 @@ export function createPOST() {
|
|
|
73
86
|
content: [
|
|
74
87
|
{
|
|
75
88
|
type: 'file',
|
|
76
|
-
mediaType:
|
|
89
|
+
mediaType: validation.mediaType,
|
|
77
90
|
data: audioUint8Array,
|
|
78
91
|
},
|
|
79
92
|
],
|