@contentgrowth/llm-service 1.0.1 → 1.0.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/dist/index.cjs +117 -1
- 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 +117 -1
- package/dist/index.js.map +1 -1
- package/dist/ui/react/components/index.cjs +423 -246
- package/dist/ui/react/components/index.cjs.map +1 -1
- package/dist/ui/react/components/index.d.cts +1 -0
- package/dist/ui/react/components/index.d.ts +1 -0
- package/dist/ui/react/components/index.js +410 -233
- package/dist/ui/react/components/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -473,6 +473,29 @@ declare function handleApiError(error: Error, operation?: string, context?: stri
|
|
|
473
473
|
*/
|
|
474
474
|
declare function sanitizeError(error: Error, context?: string): Error;
|
|
475
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Google Auth Helper for Cloudflare Workers / Node.js
|
|
478
|
+
* Generates an OAuth2 access token using a Service Account JSON.
|
|
479
|
+
*/
|
|
480
|
+
declare class GoogleAuth {
|
|
481
|
+
/**
|
|
482
|
+
* @param {Object} credentials - Parsed Service Account JSON
|
|
483
|
+
*/
|
|
484
|
+
constructor(credentials: any);
|
|
485
|
+
credentials: any;
|
|
486
|
+
tokenUrl: string;
|
|
487
|
+
scopes: string[];
|
|
488
|
+
/**
|
|
489
|
+
* Generate a signed JWT for the token exchange
|
|
490
|
+
*/
|
|
491
|
+
createSignedJWT(): Promise<string>;
|
|
492
|
+
/**
|
|
493
|
+
* Get an access token from Google OAuth2
|
|
494
|
+
* @returns {Promise<string>} Access Token
|
|
495
|
+
*/
|
|
496
|
+
getAccessToken(): Promise<string>;
|
|
497
|
+
}
|
|
498
|
+
|
|
476
499
|
/**
|
|
477
500
|
* Transcription Service - Separate from LLM Service
|
|
478
501
|
* Handles audio-to-text transcription using various providers.
|
|
@@ -500,6 +523,8 @@ declare class TranscriptionService {
|
|
|
500
523
|
endpoint: string;
|
|
501
524
|
model: string;
|
|
502
525
|
client: OpenAI;
|
|
526
|
+
googleAuth: GoogleAuth;
|
|
527
|
+
projectId: any;
|
|
503
528
|
/**
|
|
504
529
|
* Transcribe audio to text
|
|
505
530
|
* @param {File|Blob} audioFile - Audio file to transcribe
|
|
@@ -520,6 +545,9 @@ declare class TranscriptionService {
|
|
|
520
545
|
_transcribeGeneric(audioFile: any, model: any, language: any): Promise<{
|
|
521
546
|
text: any;
|
|
522
547
|
}>;
|
|
548
|
+
_transcribeGoogle(audioFile: any, model: any, language: any): Promise<{
|
|
549
|
+
text: string;
|
|
550
|
+
}>;
|
|
523
551
|
}
|
|
524
552
|
declare class TranscriptionServiceException extends Error {
|
|
525
553
|
constructor(message: any, statusCode?: number);
|
package/dist/index.d.ts
CHANGED
|
@@ -473,6 +473,29 @@ declare function handleApiError(error: Error, operation?: string, context?: stri
|
|
|
473
473
|
*/
|
|
474
474
|
declare function sanitizeError(error: Error, context?: string): Error;
|
|
475
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Google Auth Helper for Cloudflare Workers / Node.js
|
|
478
|
+
* Generates an OAuth2 access token using a Service Account JSON.
|
|
479
|
+
*/
|
|
480
|
+
declare class GoogleAuth {
|
|
481
|
+
/**
|
|
482
|
+
* @param {Object} credentials - Parsed Service Account JSON
|
|
483
|
+
*/
|
|
484
|
+
constructor(credentials: any);
|
|
485
|
+
credentials: any;
|
|
486
|
+
tokenUrl: string;
|
|
487
|
+
scopes: string[];
|
|
488
|
+
/**
|
|
489
|
+
* Generate a signed JWT for the token exchange
|
|
490
|
+
*/
|
|
491
|
+
createSignedJWT(): Promise<string>;
|
|
492
|
+
/**
|
|
493
|
+
* Get an access token from Google OAuth2
|
|
494
|
+
* @returns {Promise<string>} Access Token
|
|
495
|
+
*/
|
|
496
|
+
getAccessToken(): Promise<string>;
|
|
497
|
+
}
|
|
498
|
+
|
|
476
499
|
/**
|
|
477
500
|
* Transcription Service - Separate from LLM Service
|
|
478
501
|
* Handles audio-to-text transcription using various providers.
|
|
@@ -500,6 +523,8 @@ declare class TranscriptionService {
|
|
|
500
523
|
endpoint: string;
|
|
501
524
|
model: string;
|
|
502
525
|
client: OpenAI;
|
|
526
|
+
googleAuth: GoogleAuth;
|
|
527
|
+
projectId: any;
|
|
503
528
|
/**
|
|
504
529
|
* Transcribe audio to text
|
|
505
530
|
* @param {File|Blob} audioFile - Audio file to transcribe
|
|
@@ -520,6 +545,9 @@ declare class TranscriptionService {
|
|
|
520
545
|
_transcribeGeneric(audioFile: any, model: any, language: any): Promise<{
|
|
521
546
|
text: any;
|
|
522
547
|
}>;
|
|
548
|
+
_transcribeGoogle(audioFile: any, model: any, language: any): Promise<{
|
|
549
|
+
text: string;
|
|
550
|
+
}>;
|
|
523
551
|
}
|
|
524
552
|
declare class TranscriptionServiceException extends Error {
|
|
525
553
|
constructor(message: any, statusCode?: number);
|
package/dist/index.js
CHANGED
|
@@ -1333,6 +1333,58 @@ function sanitizeError(error, context = "general") {
|
|
|
1333
1333
|
|
|
1334
1334
|
// src/transcription-service.js
|
|
1335
1335
|
import OpenAI2 from "openai";
|
|
1336
|
+
|
|
1337
|
+
// src/utils/google-auth.js
|
|
1338
|
+
import * as jose from "jose";
|
|
1339
|
+
var GoogleAuth = class {
|
|
1340
|
+
/**
|
|
1341
|
+
* @param {Object} credentials - Parsed Service Account JSON
|
|
1342
|
+
*/
|
|
1343
|
+
constructor(credentials) {
|
|
1344
|
+
if (!credentials || !credentials.private_key || !credentials.client_email) {
|
|
1345
|
+
throw new Error("Invalid Google credentials. Missing private_key or client_email.");
|
|
1346
|
+
}
|
|
1347
|
+
this.credentials = credentials;
|
|
1348
|
+
this.tokenUrl = "https://oauth2.googleapis.com/token";
|
|
1349
|
+
this.scopes = ["https://www.googleapis.com/auth/cloud-platform"];
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Generate a signed JWT for the token exchange
|
|
1353
|
+
*/
|
|
1354
|
+
async createSignedJWT() {
|
|
1355
|
+
const alg = "RS256";
|
|
1356
|
+
const privateKey = await jose.importPKCS8(this.credentials.private_key, alg);
|
|
1357
|
+
const jwt = await new jose.SignJWT({
|
|
1358
|
+
scope: this.scopes.join(" ")
|
|
1359
|
+
}).setProtectedHeader({ alg }).setIssuedAt().setIssuer(this.credentials.client_email).setAudience(this.tokenUrl).setExpirationTime("1h").sign(privateKey);
|
|
1360
|
+
return jwt;
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Get an access token from Google OAuth2
|
|
1364
|
+
* @returns {Promise<string>} Access Token
|
|
1365
|
+
*/
|
|
1366
|
+
async getAccessToken() {
|
|
1367
|
+
const jwt = await this.createSignedJWT();
|
|
1368
|
+
const params = new URLSearchParams();
|
|
1369
|
+
params.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
|
|
1370
|
+
params.append("assertion", jwt);
|
|
1371
|
+
const response = await fetch(this.tokenUrl, {
|
|
1372
|
+
method: "POST",
|
|
1373
|
+
headers: {
|
|
1374
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1375
|
+
},
|
|
1376
|
+
body: params
|
|
1377
|
+
});
|
|
1378
|
+
if (!response.ok) {
|
|
1379
|
+
const errorText = await response.text();
|
|
1380
|
+
throw new Error(`Failed to get Google Access Token: ${response.status} ${errorText}`);
|
|
1381
|
+
}
|
|
1382
|
+
const data = await response.json();
|
|
1383
|
+
return data.access_token;
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
// src/transcription-service.js
|
|
1336
1388
|
var TranscriptionService = class {
|
|
1337
1389
|
/**
|
|
1338
1390
|
* @param {Object} config
|
|
@@ -1348,6 +1400,9 @@ var TranscriptionService = class {
|
|
|
1348
1400
|
this.model = config.model || "whisper-1";
|
|
1349
1401
|
if (this.provider === "openai") {
|
|
1350
1402
|
this.client = new OpenAI2({ apiKey: this.apiKey });
|
|
1403
|
+
} else if (this.provider === "google") {
|
|
1404
|
+
this.googleAuth = new GoogleAuth(config.googleCredentials);
|
|
1405
|
+
this.projectId = config.googleCredentials.project_id;
|
|
1351
1406
|
}
|
|
1352
1407
|
}
|
|
1353
1408
|
/**
|
|
@@ -1365,6 +1420,8 @@ var TranscriptionService = class {
|
|
|
1365
1420
|
return this._transcribeOpenAI(audioFile, model, language);
|
|
1366
1421
|
} else if (this.provider === "generic") {
|
|
1367
1422
|
return this._transcribeGeneric(audioFile, model, language);
|
|
1423
|
+
} else if (this.provider === "google") {
|
|
1424
|
+
return this._transcribeGoogle(audioFile, model, language);
|
|
1368
1425
|
} else {
|
|
1369
1426
|
throw new TranscriptionServiceException(
|
|
1370
1427
|
`Unsupported transcription provider: ${this.provider}`,
|
|
@@ -1423,6 +1480,60 @@ var TranscriptionService = class {
|
|
|
1423
1480
|
);
|
|
1424
1481
|
}
|
|
1425
1482
|
}
|
|
1483
|
+
async _transcribeGoogle(audioFile, model, language) {
|
|
1484
|
+
try {
|
|
1485
|
+
const accessToken = await this.googleAuth.getAccessToken();
|
|
1486
|
+
const arrayBuffer = await audioFile.arrayBuffer();
|
|
1487
|
+
const audioBytes = Buffer.from(arrayBuffer).toString("base64");
|
|
1488
|
+
let location = "global";
|
|
1489
|
+
if (model === "chirp_3") {
|
|
1490
|
+
location = "us";
|
|
1491
|
+
} else if (model && model.includes("chirp")) {
|
|
1492
|
+
location = "us-central1";
|
|
1493
|
+
}
|
|
1494
|
+
const recognizer = `projects/${this.projectId}/locations/${location}/recognizers/_`;
|
|
1495
|
+
const apiEndpoint = location === "global" ? "https://speech.googleapis.com" : `https://${location}-speech.googleapis.com`;
|
|
1496
|
+
const url = `${apiEndpoint}/v2/${recognizer}:recognize`;
|
|
1497
|
+
const languageCodes = language ? [language] : ["en-US"];
|
|
1498
|
+
const body = {
|
|
1499
|
+
config: {
|
|
1500
|
+
autoDecodingConfig: {},
|
|
1501
|
+
languageCodes,
|
|
1502
|
+
// Sanitize model: Strict allowlist for Google v2 models
|
|
1503
|
+
model: model || "chirp_3"
|
|
1504
|
+
},
|
|
1505
|
+
content: audioBytes
|
|
1506
|
+
};
|
|
1507
|
+
const response = await fetch(url, {
|
|
1508
|
+
method: "POST",
|
|
1509
|
+
headers: {
|
|
1510
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1511
|
+
"Content-Type": "application/json",
|
|
1512
|
+
"X-Goog-User-Project": this.projectId
|
|
1513
|
+
},
|
|
1514
|
+
body: JSON.stringify(body)
|
|
1515
|
+
});
|
|
1516
|
+
if (!response.ok) {
|
|
1517
|
+
const errorText = await response.text();
|
|
1518
|
+
throw new Error(`Google Speech API Error ${response.status}: ${errorText}`);
|
|
1519
|
+
}
|
|
1520
|
+
const data = await response.json();
|
|
1521
|
+
let transcript = "";
|
|
1522
|
+
if (data.results) {
|
|
1523
|
+
transcript = data.results.map((r) => {
|
|
1524
|
+
var _a, _b;
|
|
1525
|
+
return ((_b = (_a = r.alternatives) == null ? void 0 : _a[0]) == null ? void 0 : _b.transcript) || "";
|
|
1526
|
+
}).join(" ");
|
|
1527
|
+
}
|
|
1528
|
+
return { text: transcript.trim() };
|
|
1529
|
+
} catch (error) {
|
|
1530
|
+
console.error("[TranscriptionService] Google transcription failed:", error);
|
|
1531
|
+
throw new TranscriptionServiceException(
|
|
1532
|
+
`Google Transcription failed: ${error.message}`,
|
|
1533
|
+
500
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1426
1537
|
};
|
|
1427
1538
|
var TranscriptionServiceException = class extends Error {
|
|
1428
1539
|
constructor(message, statusCode = 500) {
|
|
@@ -1437,9 +1548,14 @@ function createSpeechHandler(app, getConfig) {
|
|
|
1437
1548
|
app.post("/transcribe", async (c) => {
|
|
1438
1549
|
try {
|
|
1439
1550
|
const config = await getConfig(c);
|
|
1440
|
-
if (!config
|
|
1551
|
+
if (!config) {
|
|
1441
1552
|
return c.json({ error: "Transcription service not configured" }, 503);
|
|
1442
1553
|
}
|
|
1554
|
+
if (config.provider === "google" && !config.googleCredentials) {
|
|
1555
|
+
return c.json({ error: "Google credentials not configured" }, 503);
|
|
1556
|
+
} else if (config.provider !== "google" && !config.apiKey) {
|
|
1557
|
+
return c.json({ error: "API key not configured" }, 503);
|
|
1558
|
+
}
|
|
1443
1559
|
const formData = await c.req.formData();
|
|
1444
1560
|
const audioFile = formData.get("audio");
|
|
1445
1561
|
if (!audioFile) {
|