@contentgrowth/llm-service 1.0.2 → 1.0.4
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/index.cjs +128 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +128 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -602,11 +602,13 @@ var GeminiProvider = class extends BaseLLMProvider {
|
|
|
602
602
|
return { text: response.content };
|
|
603
603
|
}
|
|
604
604
|
async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
|
|
605
|
+
const tier = options.tier || "default";
|
|
606
|
+
const modelName = this.models[tier] || this.defaultModel;
|
|
605
607
|
return this._chatCompletionWithModel(
|
|
606
608
|
messages,
|
|
607
609
|
systemPrompt,
|
|
608
610
|
tools,
|
|
609
|
-
|
|
611
|
+
modelName,
|
|
610
612
|
this.config.maxTokens,
|
|
611
613
|
this.config.temperature,
|
|
612
614
|
options
|
|
@@ -1381,6 +1383,58 @@ function sanitizeError(error, context = "general") {
|
|
|
1381
1383
|
|
|
1382
1384
|
// src/transcription-service.js
|
|
1383
1385
|
var import_openai2 = __toESM(require("openai"), 1);
|
|
1386
|
+
|
|
1387
|
+
// src/utils/google-auth.js
|
|
1388
|
+
var jose = __toESM(require("jose"), 1);
|
|
1389
|
+
var GoogleAuth = class {
|
|
1390
|
+
/**
|
|
1391
|
+
* @param {Object} credentials - Parsed Service Account JSON
|
|
1392
|
+
*/
|
|
1393
|
+
constructor(credentials) {
|
|
1394
|
+
if (!credentials || !credentials.private_key || !credentials.client_email) {
|
|
1395
|
+
throw new Error("Invalid Google credentials. Missing private_key or client_email.");
|
|
1396
|
+
}
|
|
1397
|
+
this.credentials = credentials;
|
|
1398
|
+
this.tokenUrl = "https://oauth2.googleapis.com/token";
|
|
1399
|
+
this.scopes = ["https://www.googleapis.com/auth/cloud-platform"];
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Generate a signed JWT for the token exchange
|
|
1403
|
+
*/
|
|
1404
|
+
async createSignedJWT() {
|
|
1405
|
+
const alg = "RS256";
|
|
1406
|
+
const privateKey = await jose.importPKCS8(this.credentials.private_key, alg);
|
|
1407
|
+
const jwt = await new jose.SignJWT({
|
|
1408
|
+
scope: this.scopes.join(" ")
|
|
1409
|
+
}).setProtectedHeader({ alg }).setIssuedAt().setIssuer(this.credentials.client_email).setAudience(this.tokenUrl).setExpirationTime("1h").sign(privateKey);
|
|
1410
|
+
return jwt;
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Get an access token from Google OAuth2
|
|
1414
|
+
* @returns {Promise<string>} Access Token
|
|
1415
|
+
*/
|
|
1416
|
+
async getAccessToken() {
|
|
1417
|
+
const jwt = await this.createSignedJWT();
|
|
1418
|
+
const params = new URLSearchParams();
|
|
1419
|
+
params.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
|
|
1420
|
+
params.append("assertion", jwt);
|
|
1421
|
+
const response = await fetch(this.tokenUrl, {
|
|
1422
|
+
method: "POST",
|
|
1423
|
+
headers: {
|
|
1424
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1425
|
+
},
|
|
1426
|
+
body: params
|
|
1427
|
+
});
|
|
1428
|
+
if (!response.ok) {
|
|
1429
|
+
const errorText = await response.text();
|
|
1430
|
+
throw new Error(`Failed to get Google Access Token: ${response.status} ${errorText}`);
|
|
1431
|
+
}
|
|
1432
|
+
const data = await response.json();
|
|
1433
|
+
return data.access_token;
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
// src/transcription-service.js
|
|
1384
1438
|
var TranscriptionService = class {
|
|
1385
1439
|
/**
|
|
1386
1440
|
* @param {Object} config
|
|
@@ -1396,6 +1450,9 @@ var TranscriptionService = class {
|
|
|
1396
1450
|
this.model = config.model || "whisper-1";
|
|
1397
1451
|
if (this.provider === "openai") {
|
|
1398
1452
|
this.client = new import_openai2.default({ apiKey: this.apiKey });
|
|
1453
|
+
} else if (this.provider === "google") {
|
|
1454
|
+
this.googleAuth = new GoogleAuth(config.googleCredentials);
|
|
1455
|
+
this.projectId = config.googleCredentials.project_id;
|
|
1399
1456
|
}
|
|
1400
1457
|
}
|
|
1401
1458
|
/**
|
|
@@ -1413,6 +1470,8 @@ var TranscriptionService = class {
|
|
|
1413
1470
|
return this._transcribeOpenAI(audioFile, model, language);
|
|
1414
1471
|
} else if (this.provider === "generic") {
|
|
1415
1472
|
return this._transcribeGeneric(audioFile, model, language);
|
|
1473
|
+
} else if (this.provider === "google") {
|
|
1474
|
+
return this._transcribeGoogle(audioFile, model, language);
|
|
1416
1475
|
} else {
|
|
1417
1476
|
throw new TranscriptionServiceException(
|
|
1418
1477
|
`Unsupported transcription provider: ${this.provider}`,
|
|
@@ -1471,6 +1530,68 @@ var TranscriptionService = class {
|
|
|
1471
1530
|
);
|
|
1472
1531
|
}
|
|
1473
1532
|
}
|
|
1533
|
+
async _transcribeGoogle(audioFile, model, language) {
|
|
1534
|
+
try {
|
|
1535
|
+
const accessToken = await this.googleAuth.getAccessToken();
|
|
1536
|
+
const arrayBuffer = await audioFile.arrayBuffer();
|
|
1537
|
+
const audioBytes = Buffer.from(arrayBuffer).toString("base64");
|
|
1538
|
+
let location = "global";
|
|
1539
|
+
if (model === "chirp_3") {
|
|
1540
|
+
location = "us";
|
|
1541
|
+
} else if (model && model.includes("chirp")) {
|
|
1542
|
+
location = "us-central1";
|
|
1543
|
+
}
|
|
1544
|
+
const recognizer = `projects/${this.projectId}/locations/${location}/recognizers/_`;
|
|
1545
|
+
const apiEndpoint = location === "global" ? "https://speech.googleapis.com" : `https://${location}-speech.googleapis.com`;
|
|
1546
|
+
const url = `${apiEndpoint}/v2/${recognizer}:recognize`;
|
|
1547
|
+
const languageCodes = language ? [language] : ["en-US"];
|
|
1548
|
+
let decodingConfig = { autoDecodingConfig: {} };
|
|
1549
|
+
if (audioFile.type && audioFile.type.includes("webm")) {
|
|
1550
|
+
decodingConfig = {
|
|
1551
|
+
explicitDecodingConfig: {
|
|
1552
|
+
encoding: "WEBM_OPUS"
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
const body = {
|
|
1557
|
+
config: {
|
|
1558
|
+
...decodingConfig,
|
|
1559
|
+
languageCodes,
|
|
1560
|
+
// Sanitize model: Strict allowlist for Google v2 models
|
|
1561
|
+
model: model || "chirp_3"
|
|
1562
|
+
},
|
|
1563
|
+
content: audioBytes
|
|
1564
|
+
};
|
|
1565
|
+
const response = await fetch(url, {
|
|
1566
|
+
method: "POST",
|
|
1567
|
+
headers: {
|
|
1568
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1569
|
+
"Content-Type": "application/json",
|
|
1570
|
+
"X-Goog-User-Project": this.projectId
|
|
1571
|
+
},
|
|
1572
|
+
body: JSON.stringify(body)
|
|
1573
|
+
});
|
|
1574
|
+
if (!response.ok) {
|
|
1575
|
+
const errorText = await response.text();
|
|
1576
|
+
throw new Error(`Google Speech API Error ${response.status}: ${errorText}`);
|
|
1577
|
+
}
|
|
1578
|
+
const data = await response.json();
|
|
1579
|
+
let transcript = "";
|
|
1580
|
+
if (data.results) {
|
|
1581
|
+
transcript = data.results.map((r) => {
|
|
1582
|
+
var _a, _b;
|
|
1583
|
+
return ((_b = (_a = r.alternatives) == null ? void 0 : _a[0]) == null ? void 0 : _b.transcript) || "";
|
|
1584
|
+
}).join(" ");
|
|
1585
|
+
}
|
|
1586
|
+
return { text: transcript.trim() };
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
console.error("[TranscriptionService] Google transcription failed:", error);
|
|
1589
|
+
throw new TranscriptionServiceException(
|
|
1590
|
+
`Google Transcription failed: ${error.message}`,
|
|
1591
|
+
500
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1474
1595
|
};
|
|
1475
1596
|
var TranscriptionServiceException = class extends Error {
|
|
1476
1597
|
constructor(message, statusCode = 500) {
|
|
@@ -1485,9 +1606,14 @@ function createSpeechHandler(app, getConfig) {
|
|
|
1485
1606
|
app.post("/transcribe", async (c) => {
|
|
1486
1607
|
try {
|
|
1487
1608
|
const config = await getConfig(c);
|
|
1488
|
-
if (!config
|
|
1609
|
+
if (!config) {
|
|
1489
1610
|
return c.json({ error: "Transcription service not configured" }, 503);
|
|
1490
1611
|
}
|
|
1612
|
+
if (config.provider === "google" && !config.googleCredentials) {
|
|
1613
|
+
return c.json({ error: "Google credentials not configured" }, 503);
|
|
1614
|
+
} else if (config.provider !== "google" && !config.apiKey) {
|
|
1615
|
+
return c.json({ error: "API key not configured" }, 503);
|
|
1616
|
+
}
|
|
1491
1617
|
const formData = await c.req.formData();
|
|
1492
1618
|
const audioFile = formData.get("audio");
|
|
1493
1619
|
if (!audioFile) {
|