@hiper2d/ai-agents 0.15.0 → 0.16.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/README.md +3 -3
- package/dist/index.d.mts +26 -13
- package/dist/index.d.ts +26 -13
- package/dist/index.js +50 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1756,8 +1756,9 @@ function extractTokenUsageFromResponse8(response) {
|
|
|
1756
1756
|
var VOICE_MODEL_CONSTANTS = {
|
|
1757
1757
|
OPENAI_TTS: "gpt-4o-mini-tts",
|
|
1758
1758
|
OPENAI_STT: "whisper-1",
|
|
1759
|
-
// ai.google.dev/gemini-api/docs/speech-generation
|
|
1760
|
-
|
|
1759
|
+
// ai.google.dev/gemini-api/docs/speech-generation — Google's named replacement
|
|
1760
|
+
// for gemini-3.1-flash-tts-preview (2026-09-24: ~6 s vs ~15 s per line, ~4x cheaper)
|
|
1761
|
+
GOOGLE_TTS: "gemini-3.8-flash-lite-tts",
|
|
1761
1762
|
// ai.google.dev/gemini-api/docs/transcribe — Interactions API only (see google-stt.ts)
|
|
1762
1763
|
GOOGLE_STT: "gemini-3.5-transcribe"
|
|
1763
1764
|
};
|
|
@@ -1769,9 +1770,9 @@ var VOICE_PROVIDER_API_KEY = {
|
|
|
1769
1770
|
var VOICE_MODEL_PRICING = {
|
|
1770
1771
|
[VOICE_MODEL_CONSTANTS.OPENAI_TTS]: { pricePerMillionCharacters: 15 },
|
|
1771
1772
|
[VOICE_MODEL_CONSTANTS.OPENAI_STT]: { pricePerMinute: 6e-3 },
|
|
1772
|
-
//
|
|
1773
|
-
//
|
|
1774
|
-
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM:
|
|
1773
|
+
// ~32 audio tokens per second of speech, so a 15-second line is ~$0.003.
|
|
1774
|
+
// Google doubles both rates to $1 / $12 on 2027-01-01 — update then.
|
|
1775
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM: 0.5, audioOutputPricePerM: 6 },
|
|
1775
1776
|
// ~25 audio tokens per second in, ~175 text tokens per minute out: ≈ $0.005/min.
|
|
1776
1777
|
[VOICE_MODEL_CONSTANTS.GOOGLE_STT]: { audioInputPricePerM: 2, textOutputPricePerM: 12 }
|
|
1777
1778
|
};
|
|
@@ -1878,12 +1879,10 @@ import { GoogleGenAI } from "@google/genai";
|
|
|
1878
1879
|
var AUDIO_TOKENS_PER_SECOND = 32;
|
|
1879
1880
|
var SAMPLE_RATE = 24e3;
|
|
1880
1881
|
var PCM_BYTES_PER_SECOND = SAMPLE_RATE * 2;
|
|
1881
|
-
|
|
1882
|
+
var WAV_HEADER_BYTES = 44;
|
|
1883
|
+
function normalizeTtsStyle(voiceStyle) {
|
|
1882
1884
|
const style = voiceStyle?.trim().replace(/[:.!,;\s]+$/, "");
|
|
1883
|
-
|
|
1884
|
-
const isShort = style.split(/\s+/).length <= 3 && !/[.!?,;]/.test(style);
|
|
1885
|
-
return isShort ? `Say ${style}: ${text}` : `${style}:
|
|
1886
|
-
${text}`;
|
|
1885
|
+
return style || void 0;
|
|
1887
1886
|
}
|
|
1888
1887
|
function pcmToWav(pcmData, sampleRate = SAMPLE_RATE) {
|
|
1889
1888
|
const numChannels = 1;
|
|
@@ -1927,28 +1926,51 @@ function describeEmptyTtsResponse(response) {
|
|
|
1927
1926
|
if (!parts.length) bits.push("no parts");
|
|
1928
1927
|
return bits.join(", ");
|
|
1929
1928
|
}
|
|
1929
|
+
function isSafetyBlocked(response) {
|
|
1930
|
+
const finish = response?.candidates?.[0]?.finishReason;
|
|
1931
|
+
return finish === "SAFETY" || finish === "PROHIBITED_CONTENT" || !!response?.promptFeedback?.blockReason;
|
|
1932
|
+
}
|
|
1933
|
+
function findAudioPart(response) {
|
|
1934
|
+
const parts = response?.candidates?.[0]?.content?.parts ?? [];
|
|
1935
|
+
const inline = parts.find((part) => part.inlineData?.mimeType?.startsWith("audio/") && part.inlineData?.data)?.inlineData;
|
|
1936
|
+
return inline ? { data: inline.data, mimeType: inline.mimeType } : void 0;
|
|
1937
|
+
}
|
|
1930
1938
|
async function generateGoogleTtsAudio(text, apiKey, options) {
|
|
1931
1939
|
const client = new GoogleGenAI({ apiKey });
|
|
1932
|
-
const
|
|
1940
|
+
const request = (style2) => client.models.generateContent({
|
|
1933
1941
|
model: VOICE_MODEL_CONSTANTS.GOOGLE_TTS,
|
|
1934
|
-
contents: [{ parts: [{ text:
|
|
1942
|
+
contents: [{ parts: [style2 ? { text, speechMetadata: { style: style2 } } : { text }] }],
|
|
1935
1943
|
config: {
|
|
1936
1944
|
responseModalities: ["AUDIO"],
|
|
1937
1945
|
speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: options.voiceName } } }
|
|
1938
1946
|
}
|
|
1939
1947
|
});
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1948
|
+
const style = normalizeTtsStyle(options.voiceStyle);
|
|
1949
|
+
let response = await request(style);
|
|
1950
|
+
let styleDropped = false;
|
|
1951
|
+
let blockedInputTokens = 0;
|
|
1952
|
+
if (!findAudioPart(response) && style && isSafetyBlocked(response)) {
|
|
1953
|
+
blockedInputTokens = response?.usageMetadata?.promptTokenCount ?? 0;
|
|
1954
|
+
response = await request(void 0);
|
|
1955
|
+
styleDropped = true;
|
|
1956
|
+
}
|
|
1957
|
+
const audioPart = findAudioPart(response);
|
|
1958
|
+
if (!audioPart) {
|
|
1959
|
+
throw new Error(`No audio data in Google TTS response (${describeEmptyTtsResponse(response)}${styleDropped ? ", also without the style" : ""})`);
|
|
1960
|
+
}
|
|
1961
|
+
const bytes = new Uint8Array(Buffer.from(audioPart.data, "base64"));
|
|
1962
|
+
const isWav = /^audio\/(x-)?wav/i.test(audioPart.mimeType);
|
|
1963
|
+
const audio = isWav ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : pcmToWav(bytes);
|
|
1964
|
+
const pcmBytes = isWav ? Math.max(0, bytes.length - WAV_HEADER_BYTES) : bytes.length;
|
|
1947
1965
|
const usageMetadata = response.usageMetadata ?? {};
|
|
1948
|
-
const inputTokens = usageMetadata.promptTokenCount ?? 0;
|
|
1966
|
+
const inputTokens = (usageMetadata.promptTokenCount ?? 0) + blockedInputTokens;
|
|
1949
1967
|
const reportedOutput = usageMetadata.candidatesTokenCount;
|
|
1950
|
-
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(
|
|
1951
|
-
return {
|
|
1968
|
+
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(pcmBytes / PCM_BYTES_PER_SECOND * AUDIO_TOKENS_PER_SECOND);
|
|
1969
|
+
return {
|
|
1970
|
+
audio,
|
|
1971
|
+
usage: { inputTokens, outputTokens },
|
|
1972
|
+
...styleDropped ? { styleDropped } : {}
|
|
1973
|
+
};
|
|
1952
1974
|
}
|
|
1953
1975
|
|
|
1954
1976
|
// src/voice/google-stt.ts
|
|
@@ -1979,11 +2001,11 @@ var GoogleVoiceAgent = class {
|
|
|
1979
2001
|
ttsModel = VOICE_MODEL_CONSTANTS.GOOGLE_TTS;
|
|
1980
2002
|
sttModel = VOICE_MODEL_CONSTANTS.GOOGLE_STT;
|
|
1981
2003
|
async speak(request) {
|
|
1982
|
-
const { audio, usage } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
2004
|
+
const { audio, usage, styleDropped } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
1983
2005
|
voiceName: request.voice,
|
|
1984
2006
|
voiceStyle: request.voiceStyle
|
|
1985
2007
|
});
|
|
1986
|
-
return { audio, costUSD: calculateGeminiTtsCost(usage), usage };
|
|
2008
|
+
return { audio, costUSD: calculateGeminiTtsCost(usage), usage, ...styleDropped ? { styleDropped } : {} };
|
|
1987
2009
|
}
|
|
1988
2010
|
async transcribe(request) {
|
|
1989
2011
|
const { text, durationSeconds, usage } = await transcribeWithGemini(request.audio, this.apiKey, { mimeType: request.mimeType });
|
|
@@ -5351,7 +5373,6 @@ export {
|
|
|
5351
5373
|
XAI_REASONING_EFFORTS,
|
|
5352
5374
|
ZodSchemaConverter,
|
|
5353
5375
|
applySpend,
|
|
5354
|
-
buildGoogleTtsPrompt,
|
|
5355
5376
|
calculateAnthropicCost,
|
|
5356
5377
|
calculateCost,
|
|
5357
5378
|
calculateDeepSeekCost,
|
|
@@ -5404,6 +5425,7 @@ export {
|
|
|
5404
5425
|
isHybridThinkingModel,
|
|
5405
5426
|
isInPeakWindow,
|
|
5406
5427
|
isPeakBilling,
|
|
5428
|
+
isSafetyBlocked,
|
|
5407
5429
|
isWeekendAt,
|
|
5408
5430
|
ledgerSpend,
|
|
5409
5431
|
logger,
|
|
@@ -5411,6 +5433,7 @@ export {
|
|
|
5411
5433
|
modelHasTag,
|
|
5412
5434
|
modelIsFast,
|
|
5413
5435
|
needsPromptBasedSchema,
|
|
5436
|
+
normalizeTtsStyle,
|
|
5414
5437
|
parseAndValidateLlmJson,
|
|
5415
5438
|
pcmToWav,
|
|
5416
5439
|
periodEnd,
|