@voquill/voice-ai 0.2.4 → 0.3.0

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/LICENCE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2025-present Handaptive Software, LLC.
1
+ Copyright (c) 2025-present Voquill, Inc.
2
2
 
3
3
  Portions of this software are licensed as follows:
4
4
 
@@ -2,5 +2,16 @@ export type ElevenLabsTestIntegrationArgs = {
2
2
  apiKey: string;
3
3
  };
4
4
  export declare const elevenlabsTestIntegration: ({ apiKey, }: ElevenLabsTestIntegrationArgs) => Promise<boolean>;
5
+ export type ElevenLabsTranscriptionArgs = {
6
+ apiKey: string;
7
+ blob: ArrayBuffer | Buffer;
8
+ ext: string;
9
+ language?: string;
10
+ };
11
+ export type ElevenLabsTranscribeAudioOutput = {
12
+ text: string;
13
+ wordsUsed: number;
14
+ };
15
+ export declare const elevenlabsTranscribeAudio: ({ apiKey, blob, ext, language, }: ElevenLabsTranscriptionArgs) => Promise<ElevenLabsTranscribeAudioOutput>;
5
16
  export declare const convertFloat32ToBase64PCM16: (float32Array: Float32Array | number[]) => string;
6
17
  //# sourceMappingURL=elevenlabs.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"elevenlabs.utils.d.ts","sourceRoot":"","sources":["../src/elevenlabs.utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAU,aAE7C,6BAA6B,KAAG,OAAO,CAAC,OAAO,CAUjD,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,cAAc,YAAY,GAAG,MAAM,EAAE,KACpC,MAmBF,CAAC"}
1
+ {"version":3,"file":"elevenlabs.utils.d.ts","sourceRoot":"","sources":["../src/elevenlabs.utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAU,aAE7C,6BAA6B,KAAG,OAAO,CAAC,OAAO,CAcjD,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAU,kCAK7C,2BAA2B,KAAG,OAAO,CAAC,+BAA+B,CA4CvE,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,cAAc,YAAY,GAAG,MAAM,EAAE,KACpC,MAmBF,CAAC"}
@@ -1,14 +1,48 @@
1
+ import { retry, countWords } from "@voquill/utilities";
1
2
  export const elevenlabsTestIntegration = async ({ apiKey, }) => {
2
- try {
3
- const response = await fetch("https://api.elevenlabs.io/v1/user", {
4
- method: "GET",
5
- headers: { "xi-api-key": apiKey },
6
- });
7
- return response.ok;
8
- }
9
- catch {
10
- return false;
3
+ const response = await fetch("https://api.elevenlabs.io/v1/user", {
4
+ method: "GET",
5
+ headers: { "xi-api-key": apiKey },
6
+ });
7
+ if (!response.ok) {
8
+ const detail = await response.text().catch(() => "");
9
+ throw new Error(detail
10
+ ? `ElevenLabs responded ${response.status}: ${detail}`
11
+ : `ElevenLabs responded with status ${response.status}`);
11
12
  }
13
+ return true;
14
+ };
15
+ export const elevenlabsTranscribeAudio = async ({ apiKey, blob, ext, language, }) => {
16
+ return retry({
17
+ retries: 3,
18
+ fn: async () => {
19
+ const formData = new FormData();
20
+ const bodyData = blob instanceof ArrayBuffer ? blob : blob.buffer;
21
+ const audioBlob = new Blob([bodyData], { type: `audio/${ext}` });
22
+ formData.append("file", audioBlob, `audio.${ext}`);
23
+ formData.append("model_id", "scribe_v1");
24
+ if (language && language !== "auto") {
25
+ formData.append("language_code", language);
26
+ }
27
+ const response = await fetch("https://api.elevenlabs.io/v1/speech-to-text", {
28
+ method: "POST",
29
+ headers: {
30
+ "xi-api-key": apiKey.trim(),
31
+ },
32
+ body: formData,
33
+ });
34
+ if (!response.ok) {
35
+ const errorText = await response.text().catch(() => "Unknown error");
36
+ throw new Error(`ElevenLabs API request failed with status ${response.status}: ${errorText}`);
37
+ }
38
+ const data = (await response.json());
39
+ const transcript = data?.text;
40
+ if (!transcript) {
41
+ throw new Error("Transcription failed: No text in ElevenLabs API response");
42
+ }
43
+ return { text: transcript, wordsUsed: countWords(transcript) };
44
+ },
45
+ });
12
46
  };
13
47
  export const convertFloat32ToBase64PCM16 = (float32Array) => {
14
48
  const samples = Array.isArray(float32Array)
@@ -60,7 +60,7 @@ export const geminiTranscribeAudio = async ({ apiKey, model = "gemini-2.5-flash"
60
60
  }
61
61
  const base64Audio = btoa(binary);
62
62
  let transcriptionPrompt = "Transcribe this audio accurately.";
63
- if (language) {
63
+ if (language && language !== "auto") {
64
64
  transcriptionPrompt += ` The audio is in ${language}.`;
65
65
  }
66
66
  if (prompt) {
@@ -1 +1 @@
1
- {"version":3,"file":"groq.utils.d.ts","sourceRoot":"","sources":["../src/groq.utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIjF,eAAO,MAAM,oBAAoB,yIAKvB,CAAC;AACX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,eAAO,MAAM,oBAAoB,qCAAsC,CAAC;AACxE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AA+BvE,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iDAOvC,qBAAqB,KAAG,OAAO,CAAC,yBAAyB,CAqB3D,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,6DAO5C,oBAAoB,KAAG,OAAO,CAAC,0BAA0B,CAyD3D,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,aAEvC,uBAAuB,KAAG,OAAO,CAAC,OAAO,CAgC3C,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,wBAAuB,cAAc,CAAC,EACpC,MAAM,EACN,KAAK,EACL,KAAK,GACN,EAAE,kBAAkB,GAAG,cAAc,CAAC,cAAc,CAAC,CAOrD"}
1
+ {"version":3,"file":"groq.utils.d.ts","sourceRoot":"","sources":["../src/groq.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EACf,MAAM,gBAAgB,CAAC;AAUxB,eAAO,MAAM,oBAAoB,yIAKvB,CAAC;AACX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAYtE,eAAO,MAAM,oBAAoB,qCAAsC,CAAC;AACxE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AA+BvE,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iDAOvC,qBAAqB,KAAG,OAAO,CAAC,yBAAyB,CAqB3D,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,6DAO5C,oBAAoB,KAAG,OAAO,CAAC,0BAA0B,CAyD3D,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,aAEvC,uBAAuB,KAAG,OAAO,CAAC,OAAO,CAgC3C,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,wBAAuB,cAAc,CAAC,EACpC,MAAM,EACN,KAAK,EACL,KAAK,GACN,EAAE,kBAAkB,GAAG,cAAc,CAAC,cAAc,CAAC,CAOrD"}
@@ -1,5 +1,5 @@
1
+ import { countWords, retry } from "@voquill/utilities";
1
2
  import Groq, { toFile } from "groq-sdk/index";
2
- import { retry, countWords } from "@voquill/utilities";
3
3
  import OpenAI from "openai";
4
4
  import { openaiCompatibleStreamChat } from "./openai.utils";
5
5
  export const GENERATE_TEXT_MODELS = [
@@ -8,6 +8,15 @@ export const GENERATE_TEXT_MODELS = [
8
8
  "openai/gpt-oss-20b",
9
9
  "openai/gpt-oss-120b",
10
10
  ];
11
+ // Models that support `response_format: { type: "json_schema" }`.
12
+ // See https://console.groq.com/docs/structured-outputs
13
+ const JSON_SCHEMA_SUPPORTED_MODELS = new Set([
14
+ "moonshotai/kimi-k2-instruct-0905",
15
+ "openai/gpt-oss-20b",
16
+ "openai/gpt-oss-120b",
17
+ "meta-llama/llama-4-scout-17b-16e-instruct",
18
+ "meta-llama/llama-4-maverick-17b-128e-instruct",
19
+ ]);
11
20
  export const TRANSCRIPTION_MODELS = ["whisper-large-v3-turbo"];
12
21
  const contentToString = (content) => {
13
22
  if (!content) {
@@ -42,7 +51,7 @@ export const groqTranscribeAudio = async ({ apiKey, model = "whisper-large-v3-tu
42
51
  file,
43
52
  model,
44
53
  prompt,
45
- language: language ?? "en",
54
+ language: language && language !== "auto" ? language : undefined,
46
55
  });
47
56
  if (!response.text) {
48
57
  throw new Error("Transcription failed");
@@ -72,18 +81,18 @@ export const groqGenerateTextResponse = async ({ apiKey, model = "meta-llama/lla
72
81
  const response = await client.chat.completions.create({
73
82
  messages,
74
83
  model,
75
- temperature: 0,
76
84
  max_completion_tokens: 8192,
77
- top_p: 1,
78
85
  response_format: jsonResponse
79
- ? {
80
- type: "json_schema",
81
- json_schema: {
82
- name: jsonResponse.name,
83
- description: jsonResponse.description,
84
- schema: jsonResponse.schema,
85
- },
86
- }
86
+ ? JSON_SCHEMA_SUPPORTED_MODELS.has(model)
87
+ ? {
88
+ type: "json_schema",
89
+ json_schema: {
90
+ name: jsonResponse.name,
91
+ description: jsonResponse.description,
92
+ schema: jsonResponse.schema,
93
+ },
94
+ }
95
+ : { type: "json_object" }
87
96
  : undefined,
88
97
  });
89
98
  console.log("groq llm usage:", response.usage);
@@ -48,7 +48,7 @@ export const openaiTranscribeAudio = async ({ apiKey, model = "whisper-1", blob,
48
48
  file,
49
49
  model,
50
50
  prompt,
51
- language: language ?? "en",
51
+ language: language && language !== "auto" ? language : undefined,
52
52
  });
53
53
  if (!response.text) {
54
54
  throw new Error("Transcription failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voquill/voice-ai",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Shared Groq voice transcription helpers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,19 +15,19 @@
15
15
  "dependencies": {
16
16
  "@anthropic-ai/sdk": "^0.71.2",
17
17
  "@azure/openai": "^2.0.0",
18
- "@google/genai": "^1.37.0",
18
+ "@google/genai": "^1.48.0",
19
19
  "groq-sdk": "^0.37.0",
20
- "microsoft-cognitiveservices-speech-sdk": "^1.47.0",
21
- "openai": "^4.73.0",
20
+ "microsoft-cognitiveservices-speech-sdk": "^1.49.0",
21
+ "openai": "^4.104.0",
22
22
  "wavefile": "^11.0.0",
23
23
  "zod": "^3.25.76",
24
- "zod-to-json-schema": "^3.24.6",
25
- "@voquill/utilities": "0.2.4",
26
- "@voquill/types": "0.2.4"
24
+ "zod-to-json-schema": "^3.25.2",
25
+ "@voquill/utilities": "0.3.0",
26
+ "@voquill/types": "0.3.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "typescript": "5.9.2",
30
- "@voquill/typescript-config": "0.2.4"
30
+ "@voquill/typescript-config": "0.3.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"