@voquill/voice-ai 0.1.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/dist/aldea.utils.d.ts +16 -0
- package/dist/aldea.utils.d.ts.map +1 -0
- package/dist/aldea.utils.js +42 -0
- package/dist/assemblyai.utils.d.ts +6 -0
- package/dist/assemblyai.utils.d.ts.map +1 -0
- package/dist/assemblyai.utils.js +24 -0
- package/dist/azure-openai.utils.d.ts +29 -0
- package/dist/azure-openai.utils.d.ts.map +1 -0
- package/dist/azure-openai.utils.js +67 -0
- package/dist/azure.utils.d.ts +30 -0
- package/dist/azure.utils.d.ts.map +1 -0
- package/dist/azure.utils.js +253 -0
- package/dist/claude.utils.d.ts +26 -0
- package/dist/claude.utils.d.ts.map +1 -0
- package/dist/claude.utils.js +229 -0
- package/dist/deepgram.utils.d.ts +5 -0
- package/dist/deepgram.utils.d.ts.map +1 -0
- package/dist/deepgram.utils.js +25 -0
- package/dist/deepseek.utils.d.ts +26 -0
- package/dist/deepseek.utils.d.ts.map +1 -0
- package/dist/deepseek.utils.js +102 -0
- package/dist/elevenlabs.utils.d.ts +6 -0
- package/dist/elevenlabs.utils.d.ts.map +1 -0
- package/dist/elevenlabs.utils.js +29 -0
- package/dist/gemini.utils.d.ts +41 -0
- package/dist/gemini.utils.d.ts.map +1 -0
- package/dist/gemini.utils.js +271 -0
- package/dist/groq.utils.d.ts +42 -0
- package/dist/groq.utils.d.ts.map +1 -0
- package/dist/groq.utils.js +141 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/openai.utils.d.ts +55 -0
- package/dist/openai.utils.d.ts.map +1 -0
- package/dist/openai.utils.js +275 -0
- package/dist/openrouter.utils.d.ts +69 -0
- package/dist/openrouter.utils.d.ts.map +1 -0
- package/dist/openrouter.utils.js +148 -0
- package/dist/speaches.utils.d.ts +18 -0
- package/dist/speaches.utils.d.ts.map +1 -0
- package/dist/speaches.utils.js +38 -0
- package/package.json +35 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { GoogleGenAI, Type, } from "@google/genai";
|
|
2
|
+
import { retry, countWords } from "@voquill/utilities";
|
|
3
|
+
export const GEMINI_GENERATE_TEXT_MODELS = [
|
|
4
|
+
"gemini-2.5-flash",
|
|
5
|
+
"gemini-2.5-pro",
|
|
6
|
+
"gemini-3-flash-preview",
|
|
7
|
+
"gemini-3-pro-preview",
|
|
8
|
+
"gemini-2.5-flash-lite",
|
|
9
|
+
];
|
|
10
|
+
export const GEMINI_TRANSCRIPTION_MODELS = [
|
|
11
|
+
"gemini-2.5-flash",
|
|
12
|
+
"gemini-2.5-pro",
|
|
13
|
+
"gemini-3-flash-preview",
|
|
14
|
+
];
|
|
15
|
+
const createClient = (apiKey) => {
|
|
16
|
+
return new GoogleGenAI({ apiKey: apiKey.trim() });
|
|
17
|
+
};
|
|
18
|
+
const convertJsonSchemaToGeminiSchema = (schema) => {
|
|
19
|
+
if (!schema || typeof schema !== "object") {
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
const converted = {};
|
|
23
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
24
|
+
if (key === "type" && typeof value === "string") {
|
|
25
|
+
const typeMap = {
|
|
26
|
+
string: Type.STRING,
|
|
27
|
+
number: Type.NUMBER,
|
|
28
|
+
integer: Type.INTEGER,
|
|
29
|
+
boolean: Type.BOOLEAN,
|
|
30
|
+
array: Type.ARRAY,
|
|
31
|
+
object: Type.OBJECT,
|
|
32
|
+
};
|
|
33
|
+
converted[key] = typeMap[value] ?? value;
|
|
34
|
+
}
|
|
35
|
+
else if (typeof value === "object" &&
|
|
36
|
+
value !== null &&
|
|
37
|
+
!Array.isArray(value)) {
|
|
38
|
+
converted[key] = convertJsonSchemaToGeminiSchema(value);
|
|
39
|
+
}
|
|
40
|
+
else if (Array.isArray(value)) {
|
|
41
|
+
converted[key] = value.map((item) => typeof item === "object" && item !== null
|
|
42
|
+
? convertJsonSchemaToGeminiSchema(item)
|
|
43
|
+
: item);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
converted[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return converted;
|
|
50
|
+
};
|
|
51
|
+
export const geminiTranscribeAudio = async ({ apiKey, model = "gemini-2.5-flash", blob, mimeType = "audio/wav", prompt, language, }) => {
|
|
52
|
+
return retry({
|
|
53
|
+
retries: 3,
|
|
54
|
+
fn: async () => {
|
|
55
|
+
const client = createClient(apiKey);
|
|
56
|
+
const bytes = new Uint8Array(blob);
|
|
57
|
+
let binary = "";
|
|
58
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
59
|
+
binary += String.fromCharCode(bytes[i]);
|
|
60
|
+
}
|
|
61
|
+
const base64Audio = btoa(binary);
|
|
62
|
+
let transcriptionPrompt = "Transcribe this audio accurately.";
|
|
63
|
+
if (language) {
|
|
64
|
+
transcriptionPrompt += ` The audio is in ${language}.`;
|
|
65
|
+
}
|
|
66
|
+
if (prompt) {
|
|
67
|
+
transcriptionPrompt += ` Context: ${prompt}`;
|
|
68
|
+
}
|
|
69
|
+
const response = await client.models.generateContent({
|
|
70
|
+
model,
|
|
71
|
+
contents: [
|
|
72
|
+
{
|
|
73
|
+
inlineData: {
|
|
74
|
+
mimeType,
|
|
75
|
+
data: base64Audio,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{ text: transcriptionPrompt },
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
const text = response.text ?? "";
|
|
82
|
+
if (!text) {
|
|
83
|
+
throw new Error("Transcription failed - empty response");
|
|
84
|
+
}
|
|
85
|
+
return { text, wordsUsed: countWords(text) };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
export const geminiGenerateTextResponse = async ({ apiKey, model = "gemini-2.5-flash", system, prompt, jsonResponse, }) => {
|
|
90
|
+
return retry({
|
|
91
|
+
retries: 3,
|
|
92
|
+
fn: async () => {
|
|
93
|
+
const client = createClient(apiKey);
|
|
94
|
+
let fullPrompt = prompt;
|
|
95
|
+
if (system) {
|
|
96
|
+
fullPrompt = `${system}\n\n${prompt}`;
|
|
97
|
+
}
|
|
98
|
+
const config = {};
|
|
99
|
+
if (jsonResponse) {
|
|
100
|
+
config.responseMimeType = "application/json";
|
|
101
|
+
if (jsonResponse.schema) {
|
|
102
|
+
config.responseSchema = convertJsonSchemaToGeminiSchema(jsonResponse.schema);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const response = await client.models.generateContent({
|
|
106
|
+
model,
|
|
107
|
+
contents: fullPrompt,
|
|
108
|
+
config: Object.keys(config).length > 0 ? config : undefined,
|
|
109
|
+
});
|
|
110
|
+
const text = response.text ?? "";
|
|
111
|
+
if (!text) {
|
|
112
|
+
throw new Error("No response from Gemini");
|
|
113
|
+
}
|
|
114
|
+
const usageMetadata = response.usageMetadata;
|
|
115
|
+
const tokensUsed = usageMetadata?.totalTokenCount ?? countWords(text);
|
|
116
|
+
console.log("gemini llm usage:", usageMetadata);
|
|
117
|
+
return {
|
|
118
|
+
text,
|
|
119
|
+
tokensUsed,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
export const geminiTestIntegration = async ({ apiKey, }) => {
|
|
125
|
+
const client = createClient(apiKey);
|
|
126
|
+
const response = await client.models.generateContent({
|
|
127
|
+
model: "gemini-2.5-flash",
|
|
128
|
+
contents: 'Reply with the single word "Hello."',
|
|
129
|
+
});
|
|
130
|
+
const text = response.text ?? "";
|
|
131
|
+
if (!text) {
|
|
132
|
+
throw new Error("Response content is empty");
|
|
133
|
+
}
|
|
134
|
+
return text.toLowerCase().includes("hello");
|
|
135
|
+
};
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Streaming Chat
|
|
138
|
+
// ============================================================================
|
|
139
|
+
function llmMessagesToGemini(messages) {
|
|
140
|
+
let systemInstruction;
|
|
141
|
+
const contents = [];
|
|
142
|
+
for (const msg of messages) {
|
|
143
|
+
if (msg.role === "system") {
|
|
144
|
+
systemInstruction = msg.content;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (msg.role === "user") {
|
|
148
|
+
contents.push({ role: "user", parts: [{ text: msg.content }] });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (msg.role === "assistant") {
|
|
152
|
+
const parts = [];
|
|
153
|
+
if (msg.content) {
|
|
154
|
+
parts.push({ text: msg.content });
|
|
155
|
+
}
|
|
156
|
+
for (const tc of msg.toolCalls ?? []) {
|
|
157
|
+
let parsedArgs;
|
|
158
|
+
try {
|
|
159
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
parsedArgs = {};
|
|
163
|
+
}
|
|
164
|
+
parts.push({
|
|
165
|
+
functionCall: { name: tc.name, args: parsedArgs },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (parts.length > 0) {
|
|
169
|
+
contents.push({ role: "model", parts });
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (msg.role === "tool") {
|
|
174
|
+
contents.push({
|
|
175
|
+
role: "user",
|
|
176
|
+
parts: [
|
|
177
|
+
{
|
|
178
|
+
functionResponse: {
|
|
179
|
+
name: msg.toolCallId,
|
|
180
|
+
response: { result: msg.content },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return { systemInstruction, contents };
|
|
188
|
+
}
|
|
189
|
+
function geminiFinishReason(raw) {
|
|
190
|
+
switch (raw) {
|
|
191
|
+
case "STOP":
|
|
192
|
+
return "stop";
|
|
193
|
+
case "MAX_TOKENS":
|
|
194
|
+
return "length";
|
|
195
|
+
case "SAFETY":
|
|
196
|
+
return "content-filter";
|
|
197
|
+
default:
|
|
198
|
+
return "other";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export async function* geminiStreamChat({ apiKey, model, input, }) {
|
|
202
|
+
const client = createClient(apiKey);
|
|
203
|
+
const { systemInstruction, contents } = llmMessagesToGemini(input.messages);
|
|
204
|
+
const tools = input.tools && input.tools.length > 0
|
|
205
|
+
? input.tools.map((t) => ({
|
|
206
|
+
name: t.name,
|
|
207
|
+
description: t.description,
|
|
208
|
+
parameters: t.parameters
|
|
209
|
+
? convertJsonSchemaToGeminiSchema(t.parameters)
|
|
210
|
+
: undefined,
|
|
211
|
+
}))
|
|
212
|
+
: undefined;
|
|
213
|
+
const stream = await client.models.generateContentStream({
|
|
214
|
+
model,
|
|
215
|
+
contents,
|
|
216
|
+
config: {
|
|
217
|
+
systemInstruction: systemInstruction
|
|
218
|
+
? { parts: [{ text: systemInstruction }] }
|
|
219
|
+
: undefined,
|
|
220
|
+
maxOutputTokens: input.maxTokens,
|
|
221
|
+
temperature: input.temperature,
|
|
222
|
+
topP: input.topP,
|
|
223
|
+
stopSequences: input.stopSequences,
|
|
224
|
+
tools: tools ? [{ functionDeclarations: tools }] : undefined,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const pendingToolCalls = [];
|
|
228
|
+
let finishReason = "other";
|
|
229
|
+
let promptTokens;
|
|
230
|
+
let completionTokens;
|
|
231
|
+
let toolCallCounter = 0;
|
|
232
|
+
for await (const chunk of stream) {
|
|
233
|
+
const candidate = chunk.candidates?.[0];
|
|
234
|
+
if (!candidate)
|
|
235
|
+
continue;
|
|
236
|
+
for (const part of candidate.content?.parts ?? []) {
|
|
237
|
+
if (part.text) {
|
|
238
|
+
yield { type: "text-delta", text: part.text };
|
|
239
|
+
}
|
|
240
|
+
if (part.functionCall) {
|
|
241
|
+
pendingToolCalls.push({
|
|
242
|
+
id: `gemini-tc-${toolCallCounter++}`,
|
|
243
|
+
name: part.functionCall.name ?? "",
|
|
244
|
+
arguments: JSON.stringify(part.functionCall.args ?? {}),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (candidate.finishReason) {
|
|
249
|
+
finishReason = geminiFinishReason(candidate.finishReason);
|
|
250
|
+
}
|
|
251
|
+
if (chunk.usageMetadata) {
|
|
252
|
+
promptTokens = chunk.usageMetadata.promptTokenCount ?? undefined;
|
|
253
|
+
completionTokens = chunk.usageMetadata.candidatesTokenCount ?? undefined;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const tc of pendingToolCalls) {
|
|
257
|
+
yield {
|
|
258
|
+
type: "tool-call",
|
|
259
|
+
id: tc.id,
|
|
260
|
+
name: tc.name,
|
|
261
|
+
arguments: tc.arguments,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
yield {
|
|
265
|
+
type: "finish",
|
|
266
|
+
finishReason,
|
|
267
|
+
usage: promptTokens != null || completionTokens != null
|
|
268
|
+
? { promptTokens, completionTokens }
|
|
269
|
+
: undefined,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { JsonResponse, LlmChatInput, LlmStreamEvent } from "@voquill/types";
|
|
2
|
+
export declare const GENERATE_TEXT_MODELS: readonly ["meta-llama/llama-4-scout-17b-16e-instruct", "moonshotai/kimi-k2-instruct-0905", "openai/gpt-oss-20b", "openai/gpt-oss-120b"];
|
|
3
|
+
export type GenerateTextModel = (typeof GENERATE_TEXT_MODELS)[number];
|
|
4
|
+
export declare const TRANSCRIPTION_MODELS: readonly ["whisper-large-v3-turbo"];
|
|
5
|
+
export type TranscriptionModel = (typeof TRANSCRIPTION_MODELS)[number];
|
|
6
|
+
export type GroqTranscriptionArgs = {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
model?: TranscriptionModel;
|
|
9
|
+
blob: ArrayBuffer | Buffer;
|
|
10
|
+
ext: string;
|
|
11
|
+
prompt?: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
};
|
|
14
|
+
export type GroqTranscribeAudioOutput = {
|
|
15
|
+
text: string;
|
|
16
|
+
wordsUsed: number;
|
|
17
|
+
};
|
|
18
|
+
export declare const groqTranscribeAudio: ({ apiKey, model, blob, ext, prompt, language, }: GroqTranscriptionArgs) => Promise<GroqTranscribeAudioOutput>;
|
|
19
|
+
export type GroqGenerateTextArgs = {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
model?: GenerateTextModel;
|
|
22
|
+
system?: string;
|
|
23
|
+
prompt: string;
|
|
24
|
+
imageUrls?: string[];
|
|
25
|
+
jsonResponse?: JsonResponse;
|
|
26
|
+
};
|
|
27
|
+
export type GroqGenerateResponseOutput = {
|
|
28
|
+
text: string;
|
|
29
|
+
tokensUsed: number;
|
|
30
|
+
};
|
|
31
|
+
export declare const groqGenerateTextResponse: ({ apiKey, model, system, prompt, imageUrls, jsonResponse, }: GroqGenerateTextArgs) => Promise<GroqGenerateResponseOutput>;
|
|
32
|
+
export type GroqTestIntegrationArgs = {
|
|
33
|
+
apiKey: string;
|
|
34
|
+
};
|
|
35
|
+
export declare const groqTestIntegration: ({ apiKey, }: GroqTestIntegrationArgs) => Promise<boolean>;
|
|
36
|
+
export type GroqStreamChatArgs = {
|
|
37
|
+
apiKey: string;
|
|
38
|
+
model: string;
|
|
39
|
+
input: LlmChatInput;
|
|
40
|
+
};
|
|
41
|
+
export declare function groqStreamChat({ apiKey, model, input, }: GroqStreamChatArgs): AsyncGenerator<LlmStreamEvent>;
|
|
42
|
+
//# sourceMappingURL=groq.utils.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import Groq, { toFile } from "groq-sdk/index";
|
|
2
|
+
import { retry, countWords } from "@voquill/utilities";
|
|
3
|
+
import OpenAI from "openai";
|
|
4
|
+
import { openaiCompatibleStreamChat } from "./openai.utils";
|
|
5
|
+
export const GENERATE_TEXT_MODELS = [
|
|
6
|
+
"meta-llama/llama-4-scout-17b-16e-instruct",
|
|
7
|
+
"moonshotai/kimi-k2-instruct-0905",
|
|
8
|
+
"openai/gpt-oss-20b",
|
|
9
|
+
"openai/gpt-oss-120b",
|
|
10
|
+
];
|
|
11
|
+
export const TRANSCRIPTION_MODELS = ["whisper-large-v3-turbo"];
|
|
12
|
+
const contentToString = (content) => {
|
|
13
|
+
if (!content) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
if (typeof content === "string") {
|
|
17
|
+
return content;
|
|
18
|
+
}
|
|
19
|
+
return content
|
|
20
|
+
.map((part) => {
|
|
21
|
+
if (part.type === "text") {
|
|
22
|
+
return part.text ?? "";
|
|
23
|
+
}
|
|
24
|
+
return "";
|
|
25
|
+
})
|
|
26
|
+
.join("")
|
|
27
|
+
.trim();
|
|
28
|
+
};
|
|
29
|
+
const createClient = (apiKey) => {
|
|
30
|
+
// `dangerouslyAllowBrowser` is needed because this runs on a desktop tauri app.
|
|
31
|
+
// The Tauri app doesn't run in a web browser and encyrpts API keys locally, so this
|
|
32
|
+
// is safe.
|
|
33
|
+
return new Groq({ apiKey: apiKey.trim(), dangerouslyAllowBrowser: true });
|
|
34
|
+
};
|
|
35
|
+
export const groqTranscribeAudio = async ({ apiKey, model = "whisper-large-v3-turbo", blob, ext, prompt, language, }) => {
|
|
36
|
+
return retry({
|
|
37
|
+
retries: 3,
|
|
38
|
+
fn: async () => {
|
|
39
|
+
const client = createClient(apiKey);
|
|
40
|
+
const file = await toFile(blob, `audio.${ext}`);
|
|
41
|
+
const response = await client.audio.transcriptions.create({
|
|
42
|
+
file,
|
|
43
|
+
model,
|
|
44
|
+
prompt,
|
|
45
|
+
language: language ?? "en",
|
|
46
|
+
});
|
|
47
|
+
if (!response.text) {
|
|
48
|
+
throw new Error("Transcription failed");
|
|
49
|
+
}
|
|
50
|
+
return { text: response.text, wordsUsed: countWords(response.text) };
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
export const groqGenerateTextResponse = async ({ apiKey, model = "meta-llama/llama-4-scout-17b-16e-instruct", system, prompt, imageUrls = [], jsonResponse, }) => {
|
|
55
|
+
return retry({
|
|
56
|
+
retries: 3,
|
|
57
|
+
fn: async () => {
|
|
58
|
+
const client = createClient(apiKey);
|
|
59
|
+
const messages = [];
|
|
60
|
+
if (system) {
|
|
61
|
+
messages.push({ role: "system", content: system });
|
|
62
|
+
}
|
|
63
|
+
const userParts = [];
|
|
64
|
+
for (const url of imageUrls) {
|
|
65
|
+
userParts.push({
|
|
66
|
+
type: "image_url",
|
|
67
|
+
image_url: { url },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
userParts.push({ type: "text", text: prompt });
|
|
71
|
+
messages.push({ role: "user", content: userParts });
|
|
72
|
+
const response = await client.chat.completions.create({
|
|
73
|
+
messages,
|
|
74
|
+
model,
|
|
75
|
+
temperature: 0,
|
|
76
|
+
max_completion_tokens: 8192,
|
|
77
|
+
top_p: 1,
|
|
78
|
+
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
|
+
}
|
|
87
|
+
: undefined,
|
|
88
|
+
});
|
|
89
|
+
console.log("groq llm usage:", response.usage);
|
|
90
|
+
if (!response.choices || response.choices.length === 0) {
|
|
91
|
+
throw new Error("No response from Groq");
|
|
92
|
+
}
|
|
93
|
+
const result = response.choices[0].message.content;
|
|
94
|
+
if (!result) {
|
|
95
|
+
throw new Error("Content is empty");
|
|
96
|
+
}
|
|
97
|
+
const content = contentToString(result);
|
|
98
|
+
return {
|
|
99
|
+
text: content,
|
|
100
|
+
tokensUsed: response.usage?.total_tokens ?? countWords(content),
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
export const groqTestIntegration = async ({ apiKey, }) => {
|
|
106
|
+
const client = createClient(apiKey);
|
|
107
|
+
const response = await client.chat.completions.create({
|
|
108
|
+
messages: [
|
|
109
|
+
{
|
|
110
|
+
role: "user",
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: `Reply with the single word "Hello."`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
model: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
120
|
+
temperature: 0,
|
|
121
|
+
max_completion_tokens: 32,
|
|
122
|
+
top_p: 1,
|
|
123
|
+
});
|
|
124
|
+
if (!response.choices || response.choices.length === 0) {
|
|
125
|
+
throw new Error("No response from Groq");
|
|
126
|
+
}
|
|
127
|
+
const first = response.choices[0];
|
|
128
|
+
const content = contentToString(first?.message?.content);
|
|
129
|
+
if (!content) {
|
|
130
|
+
throw new Error("Response content is empty");
|
|
131
|
+
}
|
|
132
|
+
return content.toLowerCase().includes("hello");
|
|
133
|
+
};
|
|
134
|
+
export async function* groqStreamChat({ apiKey, model, input, }) {
|
|
135
|
+
const client = new OpenAI({
|
|
136
|
+
apiKey: apiKey.trim(),
|
|
137
|
+
baseURL: "https://api.groq.com/openai/v1",
|
|
138
|
+
dangerouslyAllowBrowser: true,
|
|
139
|
+
});
|
|
140
|
+
yield* openaiCompatibleStreamChat(client, model, input);
|
|
141
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./claude.utils";
|
|
2
|
+
export * from "./groq.utils";
|
|
3
|
+
export * from "./openai.utils";
|
|
4
|
+
export * from "./aldea.utils";
|
|
5
|
+
export * from "./assemblyai.utils";
|
|
6
|
+
export * from "./elevenlabs.utils";
|
|
7
|
+
export * from "./deepgram.utils";
|
|
8
|
+
export * from "./openrouter.utils";
|
|
9
|
+
export * from "./azure.utils";
|
|
10
|
+
export * from "./azure-openai.utils";
|
|
11
|
+
export * from "./deepseek.utils";
|
|
12
|
+
export * from "./gemini.utils";
|
|
13
|
+
export * from "./speaches.utils";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./claude.utils";
|
|
2
|
+
export * from "./groq.utils";
|
|
3
|
+
export * from "./openai.utils";
|
|
4
|
+
export * from "./aldea.utils";
|
|
5
|
+
export * from "./assemblyai.utils";
|
|
6
|
+
export * from "./elevenlabs.utils";
|
|
7
|
+
export * from "./deepgram.utils";
|
|
8
|
+
export * from "./openrouter.utils";
|
|
9
|
+
export * from "./azure.utils";
|
|
10
|
+
export * from "./azure-openai.utils";
|
|
11
|
+
export * from "./deepseek.utils";
|
|
12
|
+
export * from "./gemini.utils";
|
|
13
|
+
export * from "./speaches.utils";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { JsonResponse, LlmChatInput, LlmMessage, LlmStreamEvent } from "@voquill/types";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";
|
|
4
|
+
export declare const OPENAI_GENERATE_TEXT_MODELS: readonly ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo", "gpt-5.2", "gpt-5.3", "gpt-5.4"];
|
|
5
|
+
export type OpenAIGenerateTextModel = (typeof OPENAI_GENERATE_TEXT_MODELS)[number];
|
|
6
|
+
export declare const OPENAI_TRANSCRIPTION_MODELS: readonly ["whisper-1"];
|
|
7
|
+
export type OpenAITranscriptionModel = (typeof OPENAI_TRANSCRIPTION_MODELS)[number];
|
|
8
|
+
export type OpenAITranscriptionArgs = {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
model?: OpenAITranscriptionModel;
|
|
11
|
+
blob: ArrayBuffer | Buffer;
|
|
12
|
+
ext: string;
|
|
13
|
+
prompt?: string;
|
|
14
|
+
language?: string;
|
|
15
|
+
};
|
|
16
|
+
export type OpenAITranscribeAudioOutput = {
|
|
17
|
+
text: string;
|
|
18
|
+
wordsUsed: number;
|
|
19
|
+
};
|
|
20
|
+
export declare const openaiTranscribeAudio: ({ apiKey, model, blob, ext, prompt, language, }: OpenAITranscriptionArgs) => Promise<OpenAITranscribeAudioOutput>;
|
|
21
|
+
export type OpenAIGenerateTextArgs = {
|
|
22
|
+
apiKey: string;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
system?: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
imageUrls?: string[];
|
|
28
|
+
jsonResponse?: JsonResponse;
|
|
29
|
+
customFetch?: typeof globalThis.fetch;
|
|
30
|
+
};
|
|
31
|
+
export type OpenAIGenerateResponseOutput = {
|
|
32
|
+
text: string;
|
|
33
|
+
tokensUsed: number;
|
|
34
|
+
};
|
|
35
|
+
export declare const openaiGenerateTextResponse: ({ apiKey, baseUrl, model, system, prompt, imageUrls, jsonResponse, customFetch, }: OpenAIGenerateTextArgs) => Promise<OpenAIGenerateResponseOutput>;
|
|
36
|
+
export type OpenAITestIntegrationArgs = {
|
|
37
|
+
apiKey: string;
|
|
38
|
+
};
|
|
39
|
+
export type OpenAICompatibleTestIntegrationArgs = {
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
apiKey?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare const openaiCompatibleTestIntegration: ({ baseUrl, apiKey, }: OpenAICompatibleTestIntegrationArgs) => Promise<boolean>;
|
|
44
|
+
export declare const openaiTestIntegration: ({ apiKey, }: OpenAITestIntegrationArgs) => Promise<boolean>;
|
|
45
|
+
export declare function llmMessagesToOpenAI(messages: LlmMessage[]): ChatCompletionMessageParam[];
|
|
46
|
+
export declare function openaiCompatibleStreamChat(client: OpenAI, model: string, input: LlmChatInput): AsyncGenerator<LlmStreamEvent>;
|
|
47
|
+
export type OpenAIStreamChatArgs = {
|
|
48
|
+
apiKey: string;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
model: string;
|
|
51
|
+
input: LlmChatInput;
|
|
52
|
+
customFetch?: typeof globalThis.fetch;
|
|
53
|
+
};
|
|
54
|
+
export declare function openaiStreamChat({ apiKey, baseUrl, model, input, customFetch, }: OpenAIStreamChatArgs): AsyncGenerator<LlmStreamEvent>;
|
|
55
|
+
//# sourceMappingURL=openai.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.utils.d.ts","sourceRoot":"","sources":["../src/openai.utils.ts"],"names":[],"mappings":"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"}
|