@memberjunction/ai-groq 6.1.0-edge.4 → 6.1.0-edge.6
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groqAudio.d.ts","sourceRoot":"","sources":["../../src/models/groqAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EACV,aAAa,EACb,kBAAkB,
|
|
1
|
+
{"version":3,"file":"groqAudio.d.ts","sourceRoot":"","sources":["../../src/models/groqAudio.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EACV,aAAa,EACb,kBAAkB,EAGlB,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAElB,SAAS,EACZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,IAAgB,MAAM,UAAU,CAAC;AA2CxC;;;;;;;;;;;;;GAaG;AACH,qBACa,kBAAmB,SAAQ,kBAAkB;IACtD,OAAO,CAAC,OAAO,CAAO;gBAEV,MAAM,EAAE,MAAM;IAK1B;;OAEG;IACH,IAAW,UAAU,IAAI,IAAI,CAE5B;IAED;;;OAGG;IACI,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAQ;IAE7C;;;;;;;OAOG;IACU,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC;IAsC5E;;OAEG;IACH,OAAO,CAAC,YAAY;YAUN,aAAa;IA6Bd,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC;IAIhE,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAKjC,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IA0BlC,6BAA6B,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIpE,mBAAmB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAGxD"}
|
package/dist/models/groqAudio.js
CHANGED
|
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import { BaseAudioGenerator, ErrorAnalyzer, SpeechResult, } from '@memberjunction/ai';
|
|
10
|
+
import { BaseAudioGenerator, ErrorAnalyzer, ModelUsage, SpeechResult, } from '@memberjunction/ai';
|
|
11
11
|
import { RegisterClass } from '@memberjunction/global';
|
|
12
12
|
import Groq, { toFile } from 'groq-sdk';
|
|
13
13
|
/**
|
|
@@ -21,6 +21,23 @@ const MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
|
21
21
|
*/
|
|
22
22
|
const SPLIT_TARGET_BYTES = 24 * 1024 * 1024;
|
|
23
23
|
const DEFAULT_MODEL = 'whisper-large-v3';
|
|
24
|
+
/**
|
|
25
|
+
* Whether a transcription model accepts `response_format: 'verbose_json'`.
|
|
26
|
+
*
|
|
27
|
+
* Groq's STT surface is Whisper-only today, so asking unconditionally happens to work — but the
|
|
28
|
+
* moment a non-Whisper model appears, an unconditional request turns every transcription through it
|
|
29
|
+
* into a hard API error. The sibling OpenAI provider already carries this guard because OpenAI's
|
|
30
|
+
* GPT-4o transcription models reject `verbose_json` outright; the asymmetry was the bug, not the
|
|
31
|
+
* behaviour.
|
|
32
|
+
*
|
|
33
|
+
* Matched with `includes` rather than `startsWith`: Groq serves `distil-whisper-large-v3-en`
|
|
34
|
+
* alongside `whisper-large-v3`, and it is a Whisper model that supports `verbose_json`. A
|
|
35
|
+
* `startsWith('whisper')` test would deny it the duration field and leave every run through it
|
|
36
|
+
* uncosted — the exact failure this PR exists to remove.
|
|
37
|
+
*/
|
|
38
|
+
function supportsVerboseJson(model) {
|
|
39
|
+
return model.toLowerCase().includes('whisper');
|
|
40
|
+
}
|
|
24
41
|
/**
|
|
25
42
|
* Groq implementation of {@link BaseAudioGenerator}, covering speech-to-text via Whisper.
|
|
26
43
|
*
|
|
@@ -64,40 +81,20 @@ let GroqAudioGenerator = class GroqAudioGenerator extends BaseAudioGenerator {
|
|
|
64
81
|
try {
|
|
65
82
|
const audio = this.resolveAudio(params);
|
|
66
83
|
const model = params.model || DEFAULT_MODEL;
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
text = await this.transcribeOne(audio, model, params);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
if (!this.Splitter) {
|
|
73
|
-
throw new Error(`Audio is ${(audio.byteLength / (1024 * 1024)).toFixed(1)}MB, above Groq's 25MB ` +
|
|
74
|
-
`transcription limit. Assign an AudioSplitter to the Splitter property to transcribe ` +
|
|
75
|
-
`audio this size.`);
|
|
76
|
-
}
|
|
77
|
-
const pieces = await this.Splitter.Split(audio, SPLIT_TARGET_BYTES);
|
|
78
|
-
if (pieces.length === 0) {
|
|
79
|
-
throw new Error('The configured AudioSplitter returned no pieces');
|
|
80
|
-
}
|
|
81
|
-
const transcripts = [];
|
|
82
|
-
for (const piece of pieces) {
|
|
83
|
-
// A piece the splitter left oversized would fail at the API with a size
|
|
84
|
-
// error naming neither the splitter nor which piece; say so here instead.
|
|
85
|
-
if (piece.byteLength > MAX_UPLOAD_BYTES) {
|
|
86
|
-
throw new Error(`The configured AudioSplitter produced a ${(piece.byteLength / (1024 * 1024)).toFixed(1)}MB ` +
|
|
87
|
-
`piece, above Groq's 25MB limit`);
|
|
88
|
-
}
|
|
89
|
-
transcripts.push(await this.transcribeOne(piece, model, params));
|
|
90
|
-
}
|
|
91
|
-
text = transcripts.filter((t) => t.length > 0).join(' ');
|
|
92
|
-
}
|
|
93
|
-
if (text.trim().length === 0) {
|
|
84
|
+
const transcription = await this.TranscribeWithSplitting(audio, MAX_UPLOAD_BYTES, SPLIT_TARGET_BYTES, this.Splitter, 'Groq', (piece) => this.transcribeOne(piece, model, params));
|
|
85
|
+
if (transcription.text.trim().length === 0) {
|
|
94
86
|
// Whisper returns an empty string for silence, and for audio it could not
|
|
95
87
|
// decode. Both are failures from the caller's point of view — an empty
|
|
96
88
|
// transcript reported as success gets stored as if it were the content.
|
|
97
89
|
throw new Error('Transcription returned no text; the audio may be silent or in an unsupported format');
|
|
98
90
|
}
|
|
99
91
|
result.success = true;
|
|
100
|
-
result.content = text;
|
|
92
|
+
result.content = transcription.text;
|
|
93
|
+
if (transcription.durationSeconds != null) {
|
|
94
|
+
// Groq bills Whisper by audio-hour, so the duration is the billable quantity —
|
|
95
|
+
// without it a transcription run prices as free.
|
|
96
|
+
result.usage = ModelUsage.ForMedia('Seconds', transcription.durationSeconds);
|
|
97
|
+
}
|
|
101
98
|
}
|
|
102
99
|
catch (error) {
|
|
103
100
|
const errorInfo = ErrorAnalyzer.analyzeError(error, 'Groq Whisper');
|
|
@@ -120,15 +117,30 @@ let GroqAudioGenerator = class GroqAudioGenerator extends BaseAudioGenerator {
|
|
|
120
117
|
throw new Error('SpeechToText requires either audioData (Buffer) or audioFile (base 64 string)');
|
|
121
118
|
}
|
|
122
119
|
async transcribeOne(audio, model, params) {
|
|
120
|
+
// `verbose_json` rather than `json` purely for the `duration` field — the quantity Groq
|
|
121
|
+
// bills by. The transcript text is identical between the two formats. Models that would
|
|
122
|
+
// reject verbose_json fall back to `json` and simply report no duration.
|
|
123
123
|
const response = await this._client.audio.transcriptions.create({
|
|
124
124
|
file: await toFile(audio, params.fileName || 'audio.mp3'),
|
|
125
125
|
model,
|
|
126
|
-
response_format: 'json',
|
|
126
|
+
response_format: supportsVerboseJson(model) ? 'verbose_json' : 'json',
|
|
127
127
|
language: params.language,
|
|
128
128
|
prompt: params.prompt,
|
|
129
129
|
temperature: params.temperature,
|
|
130
130
|
});
|
|
131
|
-
|
|
131
|
+
// groq-sdk types the verbose response as the plain transcription shape, without `duration`.
|
|
132
|
+
// Narrow it here and range-check rather than trusting the cast: an absent or nonsense
|
|
133
|
+
// duration must leave usage unreported, not produce a NaN cost.
|
|
134
|
+
//
|
|
135
|
+
// `> 0`, not `>= 0`: a zero duration is not a billable quantity. Accepting it produces
|
|
136
|
+
// `ForMedia('Seconds', 0)`, which the pricing layer then refuses as "a measure with no
|
|
137
|
+
// quantity" and logs as an error — the right outcome reached by a route that reports
|
|
138
|
+
// genuinely silent audio as a fault. Leaving usage undefined says the same thing quietly.
|
|
139
|
+
const verbose = response;
|
|
140
|
+
const duration = typeof verbose.duration === 'number' && isFinite(verbose.duration) && verbose.duration > 0
|
|
141
|
+
? verbose.duration
|
|
142
|
+
: undefined;
|
|
143
|
+
return { text: response.text ?? '', durationSeconds: duration };
|
|
132
144
|
}
|
|
133
145
|
async CreateSpeech(_params) {
|
|
134
146
|
throw new Error('Groq does not offer a text-to-speech API');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groqAudio.js","sourceRoot":"","sources":["../../src/models/groqAudio.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAGH,kBAAkB,EAClB,aAAa,
|
|
1
|
+
{"version":3,"file":"groqAudio.js","sourceRoot":"","sources":["../../src/models/groqAudio.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAGH,kBAAkB,EAClB,aAAa,EACb,UAAU,EAEV,YAAY,GAKf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAExC;;;GAGG;AACH,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C;;;GAGG;AACH,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAWzC;;;;;;;;;;;;;GAaG;AACH,SAAS,mBAAmB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;GAaG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,kBAAkB;IAGtD,YAAY,MAAc;QACtB,KAAK,CAAC,MAAM,CAAC,CAAC;QAWlB;;;WAGG;QACI,aAAQ,GAAyB,IAAI,CAAC;QAdzC,IAAI,CAAC,OAAO,GAAG,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,IAAW,UAAU;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAQD;;;;;;;OAOG;IACI,KAAK,CAAC,YAAY,CAAC,MAA0B;QAChD,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAClC,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC;YAE5C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,uBAAuB,CACpD,KAAK,EACL,gBAAgB,EAChB,kBAAkB,EAClB,IAAI,CAAC,QAAQ,EACb,MAAM,EACN,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CACtD,CAAC;YAEF,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzC,0EAA0E;gBAC1E,uEAAuE;gBACvE,wEAAwE;gBACxE,MAAM,IAAI,KAAK,CAAC,qFAAqF,CAAC,CAAC;YAC3G,CAAC;YAED,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,MAAM,CAAC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC;YACpC,IAAI,aAAa,CAAC,eAAe,IAAI,IAAI,EAAE,CAAC;gBACxC,+EAA+E;gBAC/E,iDAAiD;gBACjD,MAAM,CAAC,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;YACjF,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YACpE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;YACvB,MAAM,CAAC,YAAY,GAAG,KAAK,EAAE,OAAO,IAAI,wBAAwB,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,MAA0B;QAC3C,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YACtD,OAAO,MAAM,CAAC,SAAS,CAAC;QAC5B,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IACrG,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,KAAa,EAAE,MAA0B;QAChF,wFAAwF;QACxF,wFAAwF;QACxF,yEAAyE;QACzE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;YAC5D,IAAI,EAAE,MAAM,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,IAAI,WAAW,CAAC;YACzD,KAAK;YACL,eAAe,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM;YACrE,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,WAAW,EAAE,MAAM,CAAC,WAAW;SAClC,CAAC,CAAC;QAEH,4FAA4F;QAC5F,sFAAsF;QACtF,gEAAgE;QAChE,EAAE;QACF,uFAAuF;QACvF,uFAAuF;QACvF,qFAAqF;QACrF,0FAA0F;QAC1F,MAAM,OAAO,GAAG,QAAoC,CAAC;QACrD,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,CAAC;YACvG,CAAC,CAAC,OAAO,CAAC,QAAQ;YAClB,CAAC,CAAC,SAAS,CAAC;QAEhB,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;IACpE,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,OAA2B;QACjD,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAChE,CAAC;IAEM,KAAK,CAAC,SAAS;QAClB,yDAAyD;QACzD,OAAO,EAAE,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,SAAS;QAClB,mFAAmF;QACnF,kFAAkF;QAClF,2CAA2C;QAC3C,OAAO;YACH;gBACI,EAAE,EAAE,kBAAkB;gBACtB,IAAI,EAAE,kBAAkB;gBACxB,oBAAoB,EAAE,KAAK;gBAC3B,uBAAuB,EAAE,KAAK;gBAC9B,aAAa,EAAE,KAAK;gBACpB,oBAAoB,EAAE,KAAK;gBAC3B,kBAAkB,EAAE,KAAK;aAC5B;YACD;gBACI,EAAE,EAAE,wBAAwB;gBAC5B,IAAI,EAAE,wBAAwB;gBAC9B,oBAAoB,EAAE,KAAK;gBAC3B,uBAAuB,EAAE,KAAK;gBAC9B,aAAa,EAAE,KAAK;gBACpB,oBAAoB,EAAE,KAAK;gBAC3B,kBAAkB,EAAE,KAAK;aAC5B;SACJ,CAAC;IACN,CAAC;IAEM,KAAK,CAAC,6BAA6B;QACtC,OAAO,EAAE,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,mBAAmB;QAC5B,OAAO,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IACzC,CAAC;CACJ,CAAA;AAvJY,kBAAkB;IAD9B,aAAa,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;;GAC3C,kBAAkB,CAuJ9B"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ai-groq",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.1.0-edge.
|
|
4
|
+
"version": "6.1.0-edge.6",
|
|
5
5
|
"description": "MemberJunction Wrapper for Groq AI LPU inference engine",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"author": "MemberJunction.com",
|
|
12
12
|
"license": "BUSL-1.1",
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@memberjunction/unit-testing": "6.1.0-edge.
|
|
14
|
+
"@memberjunction/unit-testing": "6.1.0-edge.6",
|
|
15
15
|
"ts-node-dev": "^2.0.0",
|
|
16
16
|
"typescript": "^5.9.3"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@memberjunction/ai": "6.1.0-edge.
|
|
20
|
-
"@memberjunction/global": "6.1.0-edge.
|
|
19
|
+
"@memberjunction/ai": "6.1.0-edge.6",
|
|
20
|
+
"@memberjunction/global": "6.1.0-edge.6",
|
|
21
21
|
"groq-sdk": "^0.37.0"
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|