@hiper2d/ai-agents 0.15.0 → 0.17.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 +27 -14
- package/dist/index.d.ts +27 -14
- package/dist/index.js +65 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +63 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -839,9 +839,14 @@ var SupportedAiModels = {
|
|
|
839
839
|
reasoningEffort: "high",
|
|
840
840
|
tags: ["expensive"]
|
|
841
841
|
},
|
|
842
|
+
// Sonnet moved 5 → 5.5 on 2026-09-28 (`claude-sonnet-5-5`, verified live with the platform key).
|
|
843
|
+
// Same price, 1M context / 128K output. Adaptive thinking, API default effort `high` (effort
|
|
844
|
+
// levels were recalibrated vs Sonnet 5 — same pin, not necessarily the same thinking depth).
|
|
845
|
+
// It rejects `thinking: {type: "disabled"}`; ClaudeAgent sends `between_tools` instead. It
|
|
846
|
+
// reads Sonnet 5 thinking blocks, so games moving onto it keep the bots' earlier reasoning.
|
|
842
847
|
[LLM_CONSTANTS.CLAUDE_SONNET]: {
|
|
843
|
-
displayName: "Claude 5 Sonnet",
|
|
844
|
-
modelApiName: "claude-sonnet-5",
|
|
848
|
+
displayName: "Claude 5.5 Sonnet",
|
|
849
|
+
modelApiName: "claude-sonnet-5-5",
|
|
845
850
|
apiKeyName: API_KEY_CONSTANTS.ANTHROPIC,
|
|
846
851
|
hasThinking: true,
|
|
847
852
|
reasoningEffort: "high",
|
|
@@ -1345,6 +1350,7 @@ var MODEL_PRICING = {
|
|
|
1345
1350
|
cacheHitPrice: 0.2
|
|
1346
1351
|
},
|
|
1347
1352
|
[SupportedAiModels[LLM_CONSTANTS.CLAUDE_SONNET].modelApiName]: {
|
|
1353
|
+
// Sonnet 5.5 (2026-09-28): same $2/$10 and $0.20 cache reads as Sonnet 5.
|
|
1348
1354
|
inputPrice: 2,
|
|
1349
1355
|
outputPrice: 10,
|
|
1350
1356
|
cacheHitPrice: 0.2
|
|
@@ -1756,8 +1762,9 @@ function extractTokenUsageFromResponse8(response) {
|
|
|
1756
1762
|
var VOICE_MODEL_CONSTANTS = {
|
|
1757
1763
|
OPENAI_TTS: "gpt-4o-mini-tts",
|
|
1758
1764
|
OPENAI_STT: "whisper-1",
|
|
1759
|
-
// ai.google.dev/gemini-api/docs/speech-generation
|
|
1760
|
-
|
|
1765
|
+
// ai.google.dev/gemini-api/docs/speech-generation — Google's named replacement
|
|
1766
|
+
// for gemini-3.1-flash-tts-preview (2026-09-24: ~6 s vs ~15 s per line, ~4x cheaper)
|
|
1767
|
+
GOOGLE_TTS: "gemini-3.8-flash-lite-tts",
|
|
1761
1768
|
// ai.google.dev/gemini-api/docs/transcribe — Interactions API only (see google-stt.ts)
|
|
1762
1769
|
GOOGLE_STT: "gemini-3.5-transcribe"
|
|
1763
1770
|
};
|
|
@@ -1769,9 +1776,9 @@ var VOICE_PROVIDER_API_KEY = {
|
|
|
1769
1776
|
var VOICE_MODEL_PRICING = {
|
|
1770
1777
|
[VOICE_MODEL_CONSTANTS.OPENAI_TTS]: { pricePerMillionCharacters: 15 },
|
|
1771
1778
|
[VOICE_MODEL_CONSTANTS.OPENAI_STT]: { pricePerMinute: 6e-3 },
|
|
1772
|
-
//
|
|
1773
|
-
//
|
|
1774
|
-
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM:
|
|
1779
|
+
// ~32 audio tokens per second of speech, so a 15-second line is ~$0.003.
|
|
1780
|
+
// Google doubles both rates to $1 / $12 on 2027-01-01 — update then.
|
|
1781
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM: 0.5, audioOutputPricePerM: 6 },
|
|
1775
1782
|
// ~25 audio tokens per second in, ~175 text tokens per minute out: ≈ $0.005/min.
|
|
1776
1783
|
[VOICE_MODEL_CONSTANTS.GOOGLE_STT]: { audioInputPricePerM: 2, textOutputPricePerM: 12 }
|
|
1777
1784
|
};
|
|
@@ -1878,12 +1885,10 @@ import { GoogleGenAI } from "@google/genai";
|
|
|
1878
1885
|
var AUDIO_TOKENS_PER_SECOND = 32;
|
|
1879
1886
|
var SAMPLE_RATE = 24e3;
|
|
1880
1887
|
var PCM_BYTES_PER_SECOND = SAMPLE_RATE * 2;
|
|
1881
|
-
|
|
1888
|
+
var WAV_HEADER_BYTES = 44;
|
|
1889
|
+
function normalizeTtsStyle(voiceStyle) {
|
|
1882
1890
|
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}`;
|
|
1891
|
+
return style || void 0;
|
|
1887
1892
|
}
|
|
1888
1893
|
function pcmToWav(pcmData, sampleRate = SAMPLE_RATE) {
|
|
1889
1894
|
const numChannels = 1;
|
|
@@ -1927,28 +1932,51 @@ function describeEmptyTtsResponse(response) {
|
|
|
1927
1932
|
if (!parts.length) bits.push("no parts");
|
|
1928
1933
|
return bits.join(", ");
|
|
1929
1934
|
}
|
|
1935
|
+
function isSafetyBlocked(response) {
|
|
1936
|
+
const finish = response?.candidates?.[0]?.finishReason;
|
|
1937
|
+
return finish === "SAFETY" || finish === "PROHIBITED_CONTENT" || !!response?.promptFeedback?.blockReason;
|
|
1938
|
+
}
|
|
1939
|
+
function findAudioPart(response) {
|
|
1940
|
+
const parts = response?.candidates?.[0]?.content?.parts ?? [];
|
|
1941
|
+
const inline = parts.find((part) => part.inlineData?.mimeType?.startsWith("audio/") && part.inlineData?.data)?.inlineData;
|
|
1942
|
+
return inline ? { data: inline.data, mimeType: inline.mimeType } : void 0;
|
|
1943
|
+
}
|
|
1930
1944
|
async function generateGoogleTtsAudio(text, apiKey, options) {
|
|
1931
1945
|
const client = new GoogleGenAI({ apiKey });
|
|
1932
|
-
const
|
|
1946
|
+
const request = (style2) => client.models.generateContent({
|
|
1933
1947
|
model: VOICE_MODEL_CONSTANTS.GOOGLE_TTS,
|
|
1934
|
-
contents: [{ parts: [{ text:
|
|
1948
|
+
contents: [{ parts: [style2 ? { text, speechMetadata: { style: style2 } } : { text }] }],
|
|
1935
1949
|
config: {
|
|
1936
1950
|
responseModalities: ["AUDIO"],
|
|
1937
1951
|
speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: options.voiceName } } }
|
|
1938
1952
|
}
|
|
1939
1953
|
});
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1954
|
+
const style = normalizeTtsStyle(options.voiceStyle);
|
|
1955
|
+
let response = await request(style);
|
|
1956
|
+
let styleDropped = false;
|
|
1957
|
+
let blockedInputTokens = 0;
|
|
1958
|
+
if (!findAudioPart(response) && style && isSafetyBlocked(response)) {
|
|
1959
|
+
blockedInputTokens = response?.usageMetadata?.promptTokenCount ?? 0;
|
|
1960
|
+
response = await request(void 0);
|
|
1961
|
+
styleDropped = true;
|
|
1962
|
+
}
|
|
1963
|
+
const audioPart = findAudioPart(response);
|
|
1964
|
+
if (!audioPart) {
|
|
1965
|
+
throw new Error(`No audio data in Google TTS response (${describeEmptyTtsResponse(response)}${styleDropped ? ", also without the style" : ""})`);
|
|
1966
|
+
}
|
|
1967
|
+
const bytes = new Uint8Array(Buffer.from(audioPart.data, "base64"));
|
|
1968
|
+
const isWav = /^audio\/(x-)?wav/i.test(audioPart.mimeType);
|
|
1969
|
+
const audio = isWav ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : pcmToWav(bytes);
|
|
1970
|
+
const pcmBytes = isWav ? Math.max(0, bytes.length - WAV_HEADER_BYTES) : bytes.length;
|
|
1947
1971
|
const usageMetadata = response.usageMetadata ?? {};
|
|
1948
|
-
const inputTokens = usageMetadata.promptTokenCount ?? 0;
|
|
1972
|
+
const inputTokens = (usageMetadata.promptTokenCount ?? 0) + blockedInputTokens;
|
|
1949
1973
|
const reportedOutput = usageMetadata.candidatesTokenCount;
|
|
1950
|
-
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(
|
|
1951
|
-
return {
|
|
1974
|
+
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(pcmBytes / PCM_BYTES_PER_SECOND * AUDIO_TOKENS_PER_SECOND);
|
|
1975
|
+
return {
|
|
1976
|
+
audio,
|
|
1977
|
+
usage: { inputTokens, outputTokens },
|
|
1978
|
+
...styleDropped ? { styleDropped } : {}
|
|
1979
|
+
};
|
|
1952
1980
|
}
|
|
1953
1981
|
|
|
1954
1982
|
// src/voice/google-stt.ts
|
|
@@ -1979,11 +2007,11 @@ var GoogleVoiceAgent = class {
|
|
|
1979
2007
|
ttsModel = VOICE_MODEL_CONSTANTS.GOOGLE_TTS;
|
|
1980
2008
|
sttModel = VOICE_MODEL_CONSTANTS.GOOGLE_STT;
|
|
1981
2009
|
async speak(request) {
|
|
1982
|
-
const { audio, usage } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
2010
|
+
const { audio, usage, styleDropped } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
1983
2011
|
voiceName: request.voice,
|
|
1984
2012
|
voiceStyle: request.voiceStyle
|
|
1985
2013
|
});
|
|
1986
|
-
return { audio, costUSD: calculateGeminiTtsCost(usage), usage };
|
|
2014
|
+
return { audio, costUSD: calculateGeminiTtsCost(usage), usage, ...styleDropped ? { styleDropped } : {} };
|
|
1987
2015
|
}
|
|
1988
2016
|
async transcribe(request) {
|
|
1989
2017
|
const { text, durationSeconds, usage } = await transcribeWithGemini(request.audio, this.apiKey, { mimeType: request.mimeType });
|
|
@@ -2572,6 +2600,7 @@ ${instruction}`);
|
|
|
2572
2600
|
// src/agents/anthropic-agent.ts
|
|
2573
2601
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
2574
2602
|
var rejectsDisabledThinking = (model) => model.includes("fable") || model.includes("opus-5-5");
|
|
2603
|
+
var usesBetweenToolsForOff = (model) => model.includes("sonnet-5-5");
|
|
2575
2604
|
var ClaudeAgent = class extends AbstractAgent {
|
|
2576
2605
|
client;
|
|
2577
2606
|
/**
|
|
@@ -2788,7 +2817,9 @@ ${schemaDescription}`;
|
|
|
2788
2817
|
params.temperature = 1;
|
|
2789
2818
|
}
|
|
2790
2819
|
} else if (usesAdaptiveThinking) {
|
|
2791
|
-
if (
|
|
2820
|
+
if (usesBetweenToolsForOff(this.model)) {
|
|
2821
|
+
params.thinking = { type: "between_tools" };
|
|
2822
|
+
} else if (!rejectsDisabledThinking(this.model)) {
|
|
2792
2823
|
params.thinking = { type: "disabled" };
|
|
2793
2824
|
}
|
|
2794
2825
|
} else {
|
|
@@ -2884,7 +2915,9 @@ ${schemaDescription}`;
|
|
|
2884
2915
|
params.temperature = 1;
|
|
2885
2916
|
}
|
|
2886
2917
|
} else if (usesAdaptiveThinking) {
|
|
2887
|
-
if (
|
|
2918
|
+
if (usesBetweenToolsForOff(this.model)) {
|
|
2919
|
+
params.thinking = { type: "between_tools" };
|
|
2920
|
+
} else if (!rejectsDisabledThinking(this.model)) {
|
|
2888
2921
|
params.thinking = { type: "disabled" };
|
|
2889
2922
|
}
|
|
2890
2923
|
} else {
|
|
@@ -5351,7 +5384,6 @@ export {
|
|
|
5351
5384
|
XAI_REASONING_EFFORTS,
|
|
5352
5385
|
ZodSchemaConverter,
|
|
5353
5386
|
applySpend,
|
|
5354
|
-
buildGoogleTtsPrompt,
|
|
5355
5387
|
calculateAnthropicCost,
|
|
5356
5388
|
calculateCost,
|
|
5357
5389
|
calculateDeepSeekCost,
|
|
@@ -5404,6 +5436,7 @@ export {
|
|
|
5404
5436
|
isHybridThinkingModel,
|
|
5405
5437
|
isInPeakWindow,
|
|
5406
5438
|
isPeakBilling,
|
|
5439
|
+
isSafetyBlocked,
|
|
5407
5440
|
isWeekendAt,
|
|
5408
5441
|
ledgerSpend,
|
|
5409
5442
|
logger,
|
|
@@ -5411,6 +5444,7 @@ export {
|
|
|
5411
5444
|
modelHasTag,
|
|
5412
5445
|
modelIsFast,
|
|
5413
5446
|
needsPromptBasedSchema,
|
|
5447
|
+
normalizeTtsStyle,
|
|
5414
5448
|
parseAndValidateLlmJson,
|
|
5415
5449
|
pcmToWav,
|
|
5416
5450
|
periodEnd,
|