feat: switch audio and storage providers to qwen3 tts and minio
This commit is contained in:
@@ -1,39 +1,36 @@
|
||||
"use strict";
|
||||
|
||||
const { S3Client, PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
const { Client } = require("minio");
|
||||
|
||||
function createStorageAdapter({
|
||||
bucket,
|
||||
region,
|
||||
endpoint,
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
endPoint,
|
||||
port,
|
||||
useSSL = true,
|
||||
accessKey,
|
||||
secretKey,
|
||||
region = "us-east-1",
|
||||
signedUrlTtlSec = 3600,
|
||||
client,
|
||||
signedUrlFactory,
|
||||
} = {}) {
|
||||
const s3 = client || (bucket && region && accessKeyId && secretAccessKey
|
||||
? new S3Client({
|
||||
const minio = client || (bucket && endPoint && accessKey && secretKey
|
||||
? new Client({
|
||||
endPoint,
|
||||
port,
|
||||
useSSL,
|
||||
accessKey,
|
||||
secretKey,
|
||||
region,
|
||||
endpoint: endpoint || undefined,
|
||||
forcePathStyle: Boolean(endpoint),
|
||||
credentials: {
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
},
|
||||
})
|
||||
: null);
|
||||
|
||||
const sign = signedUrlFactory || getSignedUrl;
|
||||
|
||||
return {
|
||||
isConfigured() {
|
||||
return Boolean(s3 && bucket);
|
||||
return Boolean(minio && bucket);
|
||||
},
|
||||
|
||||
async uploadAudio({ key, body, contentType = "audio/mpeg" }) {
|
||||
if (!s3 || !bucket) {
|
||||
if (!minio || !bucket) {
|
||||
throw new Error("storage_not_configured");
|
||||
}
|
||||
|
||||
@@ -41,12 +38,11 @@ function createStorageAdapter({
|
||||
throw new Error("storage_upload_payload_required");
|
||||
}
|
||||
|
||||
await s3.send(new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Body: body,
|
||||
ContentType: contentType,
|
||||
}));
|
||||
const metadata = {
|
||||
"Content-Type": contentType,
|
||||
};
|
||||
|
||||
await minio.putObject(bucket, key, body, body.length, metadata);
|
||||
|
||||
return {
|
||||
bucket,
|
||||
@@ -55,13 +51,12 @@ function createStorageAdapter({
|
||||
},
|
||||
|
||||
async getSignedDownloadUrl(key, ttlSec) {
|
||||
if (!s3 || !bucket) {
|
||||
if (!minio || !bucket) {
|
||||
throw new Error("storage_not_configured");
|
||||
}
|
||||
|
||||
const expiresIn = Number.isInteger(ttlSec) && ttlSec > 0 ? ttlSec : signedUrlTtlSec;
|
||||
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
|
||||
return sign(s3, command, { expiresIn });
|
||||
return minio.presignedGetObject(bucket, key, expiresIn);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
const { OpenAI } = require("openai");
|
||||
|
||||
function createTTSAdapter({
|
||||
apiKey,
|
||||
baseURL,
|
||||
model = "gpt-4o-mini-tts",
|
||||
voice = "alloy",
|
||||
baseURL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
||||
model = "qwen-tts-latest",
|
||||
voice = "Cherry",
|
||||
format = "mp3",
|
||||
client,
|
||||
fetchImpl = fetch,
|
||||
} = {}) {
|
||||
const openai = client || (apiKey ? new OpenAI({ apiKey, baseURL: baseURL || undefined }) : null);
|
||||
const endpoint = `${String(baseURL || "").replace(/\/+$/, "")}/audio/speech`;
|
||||
|
||||
return {
|
||||
isConfigured() {
|
||||
return Boolean(openai);
|
||||
return Boolean(apiKey && fetchImpl);
|
||||
},
|
||||
|
||||
async synthesize(text, options) {
|
||||
if (!openai) {
|
||||
if (!apiKey || !fetchImpl) {
|
||||
throw new Error("tts_not_configured");
|
||||
}
|
||||
|
||||
@@ -28,14 +26,26 @@ function createTTSAdapter({
|
||||
|
||||
const effectiveModel = options && options.model ? options.model : model;
|
||||
const effectiveVoice = options && options.voice ? options.voice : voice;
|
||||
const effectiveFormat = options && options.format ? options.format : format;
|
||||
|
||||
const response = await openai.audio.speech.create({
|
||||
model: effectiveModel,
|
||||
voice: effectiveVoice,
|
||||
input: String(text),
|
||||
format,
|
||||
const response = await fetchImpl(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
authorization: `Bearer ${apiKey}`,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: effectiveModel,
|
||||
voice: effectiveVoice,
|
||||
input: String(text),
|
||||
format: effectiveFormat,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`tts_request_failed:${response.status}`);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return Buffer.from(arrayBuffer);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user