@hiper2d/ai-agents 0.1.5 → 0.2.1
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 +18 -0
- package/dist/index.d.mts +232 -1
- package/dist/index.d.ts +232 -1
- package/dist/index.js +345 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +326 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/dist/index.mjs
CHANGED
|
@@ -1618,6 +1618,263 @@ function extractTokenUsageFromResponse7(response) {
|
|
|
1618
1618
|
return extractMistralTokenUsage(response);
|
|
1619
1619
|
}
|
|
1620
1620
|
|
|
1621
|
+
// src/voice/voice-catalog.ts
|
|
1622
|
+
var VOICE_MODEL_CONSTANTS = {
|
|
1623
|
+
OPENAI_TTS: "gpt-4o-mini-tts",
|
|
1624
|
+
OPENAI_STT: "whisper-1",
|
|
1625
|
+
// ai.google.dev/gemini-api/docs/speech-generation
|
|
1626
|
+
GOOGLE_TTS: "gemini-3.1-flash-tts-preview",
|
|
1627
|
+
// ai.google.dev/gemini-api/docs/transcribe — Interactions API only (see google-stt.ts)
|
|
1628
|
+
GOOGLE_STT: "gemini-3.5-transcribe"
|
|
1629
|
+
};
|
|
1630
|
+
var SUPPORTED_VOICE_PROVIDERS = ["openai", "google"];
|
|
1631
|
+
var VOICE_PROVIDER_API_KEY = {
|
|
1632
|
+
openai: API_KEY_CONSTANTS.OPENAI,
|
|
1633
|
+
google: API_KEY_CONSTANTS.GOOGLE
|
|
1634
|
+
};
|
|
1635
|
+
var VOICE_MODEL_PRICING = {
|
|
1636
|
+
[VOICE_MODEL_CONSTANTS.OPENAI_TTS]: { pricePerMillionCharacters: 15 },
|
|
1637
|
+
[VOICE_MODEL_CONSTANTS.OPENAI_STT]: { pricePerMinute: 6e-3 },
|
|
1638
|
+
// Measured 2026-09-05: ~32 audio tokens per second of speech, so a 15-second
|
|
1639
|
+
// line is ~$0.01 — about 3-5x an OpenAI line of the same length.
|
|
1640
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM: 1, audioOutputPricePerM: 20 },
|
|
1641
|
+
// ~25 audio tokens per second in, ~175 text tokens per minute out: ≈ $0.005/min.
|
|
1642
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_STT]: { audioInputPricePerM: 2, textOutputPricePerM: 12 }
|
|
1643
|
+
};
|
|
1644
|
+
|
|
1645
|
+
// src/voice/voice-pricing.ts
|
|
1646
|
+
function roundUSD(value) {
|
|
1647
|
+
return parseFloat((value || 0).toFixed(6));
|
|
1648
|
+
}
|
|
1649
|
+
function calculateOpenAiTtsCost(characterCount) {
|
|
1650
|
+
const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_TTS]?.pricePerMillionCharacters ?? 0;
|
|
1651
|
+
if (!characterCount || characterCount <= 0 || rate <= 0) return 0;
|
|
1652
|
+
return roundUSD(characterCount / 1e6 * rate);
|
|
1653
|
+
}
|
|
1654
|
+
function calculateOpenAiSttCost(durationSeconds) {
|
|
1655
|
+
const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_STT]?.pricePerMinute ?? 0;
|
|
1656
|
+
if (!durationSeconds || durationSeconds <= 0 || rate <= 0) return 0;
|
|
1657
|
+
return roundUSD(durationSeconds / 60 * rate);
|
|
1658
|
+
}
|
|
1659
|
+
function calculateGeminiTtsCost(usage) {
|
|
1660
|
+
const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_TTS];
|
|
1661
|
+
return tokenPairCost(usage, pricing?.textInputPricePerM ?? 0, pricing?.audioOutputPricePerM ?? 0);
|
|
1662
|
+
}
|
|
1663
|
+
function calculateGeminiSttCost(usage) {
|
|
1664
|
+
const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_STT];
|
|
1665
|
+
return tokenPairCost(usage, pricing?.audioInputPricePerM ?? 0, pricing?.textOutputPricePerM ?? 0);
|
|
1666
|
+
}
|
|
1667
|
+
function tokenPairCost(usage, inputRate, outputRate) {
|
|
1668
|
+
const inputTokens = Math.max(0, usage.inputTokens || 0);
|
|
1669
|
+
const outputTokens = Math.max(0, usage.outputTokens || 0);
|
|
1670
|
+
return inputTokens / 1e6 * inputRate + outputTokens / 1e6 * outputRate;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// src/voice/openai-tts.ts
|
|
1674
|
+
import { OpenAI } from "openai";
|
|
1675
|
+
async function generateOpenAiTtsAudio(text, apiKey, options = {}) {
|
|
1676
|
+
const client = new OpenAI({ apiKey });
|
|
1677
|
+
const speechOptions = {
|
|
1678
|
+
model: VOICE_MODEL_CONSTANTS.OPENAI_TTS,
|
|
1679
|
+
voice: options.voice || "alloy",
|
|
1680
|
+
input: text,
|
|
1681
|
+
speed: options.speed || 1,
|
|
1682
|
+
response_format: options.format || "wav"
|
|
1683
|
+
};
|
|
1684
|
+
if (options.instructions) {
|
|
1685
|
+
speechOptions.instructions = options.instructions;
|
|
1686
|
+
}
|
|
1687
|
+
const response = await client.audio.speech.create(speechOptions);
|
|
1688
|
+
return await response.arrayBuffer();
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// src/voice/openai-stt.ts
|
|
1692
|
+
import { OpenAI as OpenAI2 } from "openai";
|
|
1693
|
+
async function transcribeWithOpenAi(audioBuffer, apiKey, options = {}) {
|
|
1694
|
+
const client = new OpenAI2({ apiKey });
|
|
1695
|
+
const audioFile = new File([new Uint8Array(audioBuffer)], options.fileName || "audio.webm", { type: options.mimeType || "audio/webm" });
|
|
1696
|
+
const transcription = await client.audio.transcriptions.create({
|
|
1697
|
+
file: audioFile,
|
|
1698
|
+
model: VOICE_MODEL_CONSTANTS.OPENAI_STT,
|
|
1699
|
+
language: options.language || "en",
|
|
1700
|
+
prompt: options.prompt,
|
|
1701
|
+
temperature: options.temperature || 0,
|
|
1702
|
+
response_format: "verbose_json"
|
|
1703
|
+
});
|
|
1704
|
+
const explicitDuration = Number(transcription?.duration) || 0;
|
|
1705
|
+
const segments = Array.isArray(transcription?.segments) ? transcription.segments : [];
|
|
1706
|
+
const segmentsDuration = segments.reduce((max, segment) => {
|
|
1707
|
+
const end = Number(segment?.end);
|
|
1708
|
+
return end > max ? end : max;
|
|
1709
|
+
}, 0);
|
|
1710
|
+
const text = typeof transcription?.text === "string" ? transcription.text : segments.map((segment) => segment?.text || "").join(" ");
|
|
1711
|
+
return { text: text.trim(), durationSeconds: explicitDuration || segmentsDuration };
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/voice/openai-voice-agent.ts
|
|
1715
|
+
var OpenAiVoiceAgent = class {
|
|
1716
|
+
constructor(apiKey) {
|
|
1717
|
+
this.apiKey = apiKey;
|
|
1718
|
+
}
|
|
1719
|
+
apiKey;
|
|
1720
|
+
provider = "openai";
|
|
1721
|
+
ttsModel = VOICE_MODEL_CONSTANTS.OPENAI_TTS;
|
|
1722
|
+
sttModel = VOICE_MODEL_CONSTANTS.OPENAI_STT;
|
|
1723
|
+
async speak(request) {
|
|
1724
|
+
const audio = await generateOpenAiTtsAudio(request.text, this.apiKey, {
|
|
1725
|
+
voice: request.voice,
|
|
1726
|
+
instructions: request.voiceStyle || void 0
|
|
1727
|
+
});
|
|
1728
|
+
const characters = request.text.length;
|
|
1729
|
+
return { audio, costUSD: calculateOpenAiTtsCost(characters), usage: { characters } };
|
|
1730
|
+
}
|
|
1731
|
+
async transcribe(request) {
|
|
1732
|
+
const { text, durationSeconds } = await transcribeWithOpenAi(request.audio, this.apiKey, {
|
|
1733
|
+
language: request.language,
|
|
1734
|
+
prompt: request.prompt,
|
|
1735
|
+
fileName: request.fileName,
|
|
1736
|
+
mimeType: request.mimeType
|
|
1737
|
+
});
|
|
1738
|
+
return { text, durationSeconds, costUSD: calculateOpenAiSttCost(durationSeconds), usage: { audioSeconds: durationSeconds } };
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
// src/voice/google-tts.ts
|
|
1743
|
+
import { GoogleGenAI } from "@google/genai";
|
|
1744
|
+
var AUDIO_TOKENS_PER_SECOND = 32;
|
|
1745
|
+
var SAMPLE_RATE = 24e3;
|
|
1746
|
+
var PCM_BYTES_PER_SECOND = SAMPLE_RATE * 2;
|
|
1747
|
+
function buildGoogleTtsPrompt(text, voiceStyle) {
|
|
1748
|
+
const style = voiceStyle?.trim().replace(/[:.!,;\s]+$/, "");
|
|
1749
|
+
if (!style) return text;
|
|
1750
|
+
const isShort = style.split(/\s+/).length <= 3 && !/[.!?,;]/.test(style);
|
|
1751
|
+
return isShort ? `Say ${style}: ${text}` : `${style}:
|
|
1752
|
+
${text}`;
|
|
1753
|
+
}
|
|
1754
|
+
function pcmToWav(pcmData, sampleRate = SAMPLE_RATE) {
|
|
1755
|
+
const numChannels = 1;
|
|
1756
|
+
const bitsPerSample = 16;
|
|
1757
|
+
const blockAlign = numChannels * (bitsPerSample / 8);
|
|
1758
|
+
const byteRate = sampleRate * blockAlign;
|
|
1759
|
+
const headerSize = 44;
|
|
1760
|
+
const buffer = new ArrayBuffer(headerSize + pcmData.length);
|
|
1761
|
+
const view = new DataView(buffer);
|
|
1762
|
+
const writeString = (offset, str) => {
|
|
1763
|
+
for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));
|
|
1764
|
+
};
|
|
1765
|
+
writeString(0, "RIFF");
|
|
1766
|
+
view.setUint32(4, 36 + pcmData.length, true);
|
|
1767
|
+
writeString(8, "WAVE");
|
|
1768
|
+
writeString(12, "fmt ");
|
|
1769
|
+
view.setUint32(16, 16, true);
|
|
1770
|
+
view.setUint16(20, 1, true);
|
|
1771
|
+
view.setUint16(22, numChannels, true);
|
|
1772
|
+
view.setUint32(24, sampleRate, true);
|
|
1773
|
+
view.setUint32(28, byteRate, true);
|
|
1774
|
+
view.setUint16(32, blockAlign, true);
|
|
1775
|
+
view.setUint16(34, bitsPerSample, true);
|
|
1776
|
+
writeString(36, "data");
|
|
1777
|
+
view.setUint32(40, pcmData.length, true);
|
|
1778
|
+
new Uint8Array(buffer, headerSize).set(pcmData);
|
|
1779
|
+
return buffer;
|
|
1780
|
+
}
|
|
1781
|
+
async function generateGoogleTtsAudio(text, apiKey, options) {
|
|
1782
|
+
const client = new GoogleGenAI({ apiKey });
|
|
1783
|
+
const response = await client.models.generateContent({
|
|
1784
|
+
model: VOICE_MODEL_CONSTANTS.GOOGLE_TTS,
|
|
1785
|
+
contents: [{ parts: [{ text: buildGoogleTtsPrompt(text, options.voiceStyle) }] }],
|
|
1786
|
+
config: {
|
|
1787
|
+
responseModalities: ["AUDIO"],
|
|
1788
|
+
speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: options.voiceName } } }
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
const parts = response.candidates?.[0]?.content?.parts ?? [];
|
|
1792
|
+
const audioPart = parts.find((part) => part.inlineData?.mimeType?.startsWith("audio/"));
|
|
1793
|
+
if (!audioPart?.inlineData?.data) {
|
|
1794
|
+
throw new Error("No audio data in Google TTS response");
|
|
1795
|
+
}
|
|
1796
|
+
const pcmData = new Uint8Array(Buffer.from(audioPart.inlineData.data, "base64"));
|
|
1797
|
+
const usageMetadata = response.usageMetadata ?? {};
|
|
1798
|
+
const inputTokens = usageMetadata.promptTokenCount ?? 0;
|
|
1799
|
+
const reportedOutput = usageMetadata.candidatesTokenCount;
|
|
1800
|
+
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(pcmData.length / PCM_BYTES_PER_SECOND * AUDIO_TOKENS_PER_SECOND);
|
|
1801
|
+
return { audio: pcmToWav(pcmData), usage: { inputTokens, outputTokens } };
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/voice/google-stt.ts
|
|
1805
|
+
import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
|
|
1806
|
+
var GEMINI_AUDIO_TOKENS_PER_SECOND = 25;
|
|
1807
|
+
async function transcribeWithGemini(audioBuffer, apiKey, options = {}) {
|
|
1808
|
+
const client = new GoogleGenAI2({ apiKey });
|
|
1809
|
+
const interaction = await client.interactions.create({
|
|
1810
|
+
model: VOICE_MODEL_CONSTANTS.GOOGLE_STT,
|
|
1811
|
+
input: [{ type: "audio", data: Buffer.from(audioBuffer).toString("base64"), mime_type: options.mimeType || "audio/webm" }]
|
|
1812
|
+
});
|
|
1813
|
+
const text = typeof interaction?.output_text === "string" ? interaction.output_text.trim() : "";
|
|
1814
|
+
const usage = interaction?.usage ?? {};
|
|
1815
|
+
const byModality = (rows, modality) => (rows ?? []).filter((r) => r?.modality === modality).reduce((sum, r) => sum + (Number(r?.tokens) || 0), 0);
|
|
1816
|
+
const inputTokens = byModality(usage.input_tokens_by_modality, "audio") || usage.total_input_tokens || 0;
|
|
1817
|
+
const invocationOutput = (usage.model_invocation_token_counts ?? []).reduce((sum, inv) => sum + (inv?.candidates_tokens_details ?? []).reduce((s, d) => s + (Number(d?.tokens) || 0), 0), 0);
|
|
1818
|
+
const outputTokens = usage.total_output_tokens || invocationOutput || Math.ceil(text.length / 4);
|
|
1819
|
+
return { text, durationSeconds: inputTokens / GEMINI_AUDIO_TOKENS_PER_SECOND, usage: { inputTokens, outputTokens } };
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// src/voice/google-voice-agent.ts
|
|
1823
|
+
var GoogleVoiceAgent = class {
|
|
1824
|
+
constructor(apiKey) {
|
|
1825
|
+
this.apiKey = apiKey;
|
|
1826
|
+
}
|
|
1827
|
+
apiKey;
|
|
1828
|
+
provider = "google";
|
|
1829
|
+
ttsModel = VOICE_MODEL_CONSTANTS.GOOGLE_TTS;
|
|
1830
|
+
sttModel = VOICE_MODEL_CONSTANTS.GOOGLE_STT;
|
|
1831
|
+
async speak(request) {
|
|
1832
|
+
const { audio, usage } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
1833
|
+
voiceName: request.voice,
|
|
1834
|
+
voiceStyle: request.voiceStyle
|
|
1835
|
+
});
|
|
1836
|
+
return { audio, costUSD: calculateGeminiTtsCost(usage), usage };
|
|
1837
|
+
}
|
|
1838
|
+
async transcribe(request) {
|
|
1839
|
+
const { text, durationSeconds, usage } = await transcribeWithGemini(request.audio, this.apiKey, { mimeType: request.mimeType });
|
|
1840
|
+
return { text, durationSeconds, costUSD: calculateGeminiSttCost(usage), usage };
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
|
|
1844
|
+
// src/voice/voice-agent-factory.ts
|
|
1845
|
+
var VoiceAgentFactory = class _VoiceAgentFactory {
|
|
1846
|
+
/** Build an agent from a key already resolved by the caller. */
|
|
1847
|
+
static createAgent(provider, apiKey) {
|
|
1848
|
+
switch (provider) {
|
|
1849
|
+
case "openai":
|
|
1850
|
+
return new OpenAiVoiceAgent(apiKey);
|
|
1851
|
+
case "google":
|
|
1852
|
+
return new GoogleVoiceAgent(apiKey);
|
|
1853
|
+
default:
|
|
1854
|
+
throw new Error(`Unknown voice provider: ${provider}`);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Build an agent from a key map (the shape AgentFactory takes), reading the
|
|
1859
|
+
* provider's key by its API_KEY_CONSTANTS name. Throws when the key is missing
|
|
1860
|
+
* so the host can report a misconfiguration before any SDK is touched.
|
|
1861
|
+
*/
|
|
1862
|
+
static createAgentFromKeys(provider, apiKeys) {
|
|
1863
|
+
if (!SUPPORTED_VOICE_PROVIDERS.includes(provider)) {
|
|
1864
|
+
throw new Error(`Unknown voice provider: ${provider}`);
|
|
1865
|
+
}
|
|
1866
|
+
const keyName = VOICE_PROVIDER_API_KEY[provider];
|
|
1867
|
+
const apiKey = apiKeys[keyName];
|
|
1868
|
+
if (!apiKey) {
|
|
1869
|
+
throw new Error(`Missing API key ${keyName} for voice provider ${provider}`);
|
|
1870
|
+
}
|
|
1871
|
+
return _VoiceAgentFactory.createAgent(provider, apiKey);
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
function createVoiceAgent(provider, apiKey) {
|
|
1875
|
+
return VoiceAgentFactory.createAgent(provider, apiKey);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1621
1878
|
// src/agents/abstract-agent.ts
|
|
1622
1879
|
var AbstractAgent = class {
|
|
1623
1880
|
name;
|
|
@@ -1764,10 +2021,15 @@ ${msg.content}` };
|
|
|
1764
2021
|
};
|
|
1765
2022
|
|
|
1766
2023
|
// src/agents/gpt-5-agent.ts
|
|
1767
|
-
import
|
|
2024
|
+
import OpenAI3 from "openai";
|
|
1768
2025
|
import { zodTextFormat } from "openai/helpers/zod";
|
|
1769
2026
|
var Gpt5Agent = class extends AbstractAgent {
|
|
1770
2027
|
client;
|
|
2028
|
+
// Routing hint for OpenAI's prefix cache (same scheme as the Mistral/Grok agents): one
|
|
2029
|
+
// key per agent+instruction, so an agent's own calls group together instead of every
|
|
2030
|
+
// agent that shares a static prefix hashing to the same route. Keys influence routing
|
|
2031
|
+
// only; they do not guarantee a hit.
|
|
2032
|
+
promptCacheKey;
|
|
1771
2033
|
// Log message templates
|
|
1772
2034
|
logTemplates = {
|
|
1773
2035
|
error: (name, error) => `Error in ${name} agent: ${error}`
|
|
@@ -1780,7 +2042,9 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1780
2042
|
};
|
|
1781
2043
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
1782
2044
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
1783
|
-
this.
|
|
2045
|
+
this.promptCacheKey = stableHashHex(`${name}
|
|
2046
|
+
${instruction}`);
|
|
2047
|
+
this.client = new OpenAI3({
|
|
1784
2048
|
apiKey
|
|
1785
2049
|
});
|
|
1786
2050
|
}
|
|
@@ -1794,10 +2058,7 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1794
2058
|
try {
|
|
1795
2059
|
this.logAsking(messages);
|
|
1796
2060
|
this.logMessages(messages);
|
|
1797
|
-
const input =
|
|
1798
|
-
`System: ${this.instruction}`,
|
|
1799
|
-
...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
|
|
1800
|
-
].join("\n\n");
|
|
2061
|
+
const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
|
|
1801
2062
|
const schemaToSend = zodSchema;
|
|
1802
2063
|
let response;
|
|
1803
2064
|
try {
|
|
@@ -1806,6 +2067,7 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1806
2067
|
instructions: this.instruction,
|
|
1807
2068
|
input,
|
|
1808
2069
|
max_output_tokens: this.maxOutputTokens,
|
|
2070
|
+
prompt_cache_key: this.promptCacheKey,
|
|
1809
2071
|
text: {
|
|
1810
2072
|
format: zodTextFormat(schemaToSend, "response_schema")
|
|
1811
2073
|
}
|
|
@@ -1884,15 +2146,13 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1884
2146
|
try {
|
|
1885
2147
|
this.logAsking(messages);
|
|
1886
2148
|
this.logMessages(messages);
|
|
1887
|
-
const input =
|
|
1888
|
-
`System: ${this.instruction}`,
|
|
1889
|
-
...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
|
|
1890
|
-
].join("\n\n");
|
|
2149
|
+
const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
|
|
1891
2150
|
const response = await this.client.responses.create({
|
|
1892
2151
|
model: this.model,
|
|
1893
2152
|
instructions: this.instruction,
|
|
1894
2153
|
input,
|
|
1895
|
-
max_output_tokens: this.maxOutputTokens
|
|
2154
|
+
max_output_tokens: this.maxOutputTokens,
|
|
2155
|
+
prompt_cache_key: this.promptCacheKey
|
|
1896
2156
|
});
|
|
1897
2157
|
const content = response.output_text;
|
|
1898
2158
|
if (!content) {
|
|
@@ -1948,13 +2208,24 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1948
2208
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
1949
2209
|
var ClaudeAgent = class extends AbstractAgent {
|
|
1950
2210
|
client;
|
|
2211
|
+
/**
|
|
2212
|
+
* TTL for every breakpoint this agent places (system tiers and the message anchor).
|
|
2213
|
+
* Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
|
|
2214
|
+
* refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
|
|
2215
|
+
* pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
|
|
2216
|
+
* expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
|
|
2217
|
+
* under five minutes). Set '5m' for continuous traffic where every call lands inside the
|
|
2218
|
+
* window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
|
|
2219
|
+
* requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
|
|
2220
|
+
*/
|
|
2221
|
+
cacheTtl = "1h";
|
|
1951
2222
|
// System-prompt breakpoints, one per cache tier (see CACHE_TIER_MARKER):
|
|
1952
|
-
// block 1
|
|
1953
|
-
// same rule set, so one
|
|
1954
|
-
// refreshes
|
|
1955
|
-
// block 2
|
|
1956
|
-
//
|
|
1957
|
-
//
|
|
2223
|
+
// block 1 - shared static rules, byte-identical across all bots and games with the
|
|
2224
|
+
// same rule set. Caches are scoped per model, so one entry serves every bot
|
|
2225
|
+
// ON THAT MODEL (not the whole lobby), and any of their calls refreshes it;
|
|
2226
|
+
// block 2 - per-bot identity + game state + summaries, byte-stable between the game's
|
|
2227
|
+
// state writes (a lynch, the night resolution, the summary rewrite, the new
|
|
2228
|
+
// day), so every call inside one of those windows reads it.
|
|
1958
2229
|
// GM prompts have no marker → single block, same behavior as before. Haiku 4.5 needs a
|
|
1959
2230
|
// 4096-token cacheable prefix, so tiers below that silently no-op on Haiku — expected.
|
|
1960
2231
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -1962,7 +2233,7 @@ var ClaudeAgent = class extends AbstractAgent {
|
|
|
1962
2233
|
get defaultParams() {
|
|
1963
2234
|
return {
|
|
1964
2235
|
max_tokens: this.maxOutputTokens,
|
|
1965
|
-
system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral" } })),
|
|
2236
|
+
system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral", ttl: this.cacheTtl } })),
|
|
1966
2237
|
model: this.model
|
|
1967
2238
|
};
|
|
1968
2239
|
}
|
|
@@ -2067,14 +2338,14 @@ var ClaudeAgent = class extends AbstractAgent {
|
|
|
2067
2338
|
const anchor = messages[messages.length - 2];
|
|
2068
2339
|
if (typeof anchor.content === "string") {
|
|
2069
2340
|
if (anchor.content.length > 0) {
|
|
2070
|
-
anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral" } }];
|
|
2341
|
+
anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral", ttl: this.cacheTtl } }];
|
|
2071
2342
|
}
|
|
2072
2343
|
return;
|
|
2073
2344
|
}
|
|
2074
2345
|
for (let i = anchor.content.length - 1; i >= 0; i--) {
|
|
2075
2346
|
const block = anchor.content[i];
|
|
2076
2347
|
if (block.type === "text" && block.text.length > 0) {
|
|
2077
|
-
block.cache_control = { type: "ephemeral" };
|
|
2348
|
+
block.cache_control = { type: "ephemeral", ttl: this.cacheTtl };
|
|
2078
2349
|
return;
|
|
2079
2350
|
}
|
|
2080
2351
|
}
|
|
@@ -2312,7 +2583,7 @@ ${schemaDescription}`;
|
|
|
2312
2583
|
};
|
|
2313
2584
|
|
|
2314
2585
|
// src/agents/google-agent.ts
|
|
2315
|
-
import { GoogleGenAI } from "@google/genai";
|
|
2586
|
+
import { GoogleGenAI as GoogleGenAI3 } from "@google/genai";
|
|
2316
2587
|
var GoogleAgent = class extends AbstractAgent {
|
|
2317
2588
|
client;
|
|
2318
2589
|
defaultConfig = {
|
|
@@ -2331,7 +2602,7 @@ var GoogleAgent = class extends AbstractAgent {
|
|
|
2331
2602
|
};
|
|
2332
2603
|
constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
2333
2604
|
super(name, instruction, model, 0.2, enableThinking, agentLoggingConfig);
|
|
2334
|
-
this.client = new
|
|
2605
|
+
this.client = new GoogleGenAI3({
|
|
2335
2606
|
apiKey
|
|
2336
2607
|
});
|
|
2337
2608
|
}
|
|
@@ -2892,7 +3163,7 @@ ${schemaDescription}`
|
|
|
2892
3163
|
};
|
|
2893
3164
|
|
|
2894
3165
|
// src/agents/deepseek-v2-agent.ts
|
|
2895
|
-
import
|
|
3166
|
+
import OpenAI4 from "openai";
|
|
2896
3167
|
var DeepSeekV2Agent = class extends AbstractAgent {
|
|
2897
3168
|
client;
|
|
2898
3169
|
// Log message templates
|
|
@@ -2908,7 +3179,7 @@ var DeepSeekV2Agent = class extends AbstractAgent {
|
|
|
2908
3179
|
};
|
|
2909
3180
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
2910
3181
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
2911
|
-
this.client = new
|
|
3182
|
+
this.client = new OpenAI4({
|
|
2912
3183
|
baseURL: "https://api.deepseek.com",
|
|
2913
3184
|
apiKey
|
|
2914
3185
|
});
|
|
@@ -3100,7 +3371,7 @@ ${schemaDescription}`
|
|
|
3100
3371
|
};
|
|
3101
3372
|
|
|
3102
3373
|
// src/agents/grok-agent.ts
|
|
3103
|
-
import { OpenAI as
|
|
3374
|
+
import { OpenAI as OpenAI5 } from "openai";
|
|
3104
3375
|
var GrokAgent = class extends AbstractAgent {
|
|
3105
3376
|
client;
|
|
3106
3377
|
// Log message templates
|
|
@@ -3117,7 +3388,7 @@ var GrokAgent = class extends AbstractAgent {
|
|
|
3117
3388
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3118
3389
|
const convId = stableHashHex(`${name}
|
|
3119
3390
|
${instruction}`);
|
|
3120
|
-
this.client = new
|
|
3391
|
+
this.client = new OpenAI5({
|
|
3121
3392
|
apiKey,
|
|
3122
3393
|
baseURL: "https://api.x.ai/v1",
|
|
3123
3394
|
timeout: 12e5,
|
|
@@ -3290,7 +3561,7 @@ ${input[0].content}`;
|
|
|
3290
3561
|
};
|
|
3291
3562
|
|
|
3292
3563
|
// src/agents/kimi-agent.ts
|
|
3293
|
-
import { OpenAI as
|
|
3564
|
+
import { OpenAI as OpenAI6 } from "openai";
|
|
3294
3565
|
var KimiAgent = class extends AbstractAgent {
|
|
3295
3566
|
client;
|
|
3296
3567
|
// kimi-k3 rejects any temperature other than 1, so we never send the field.
|
|
@@ -3317,7 +3588,7 @@ var KimiAgent = class extends AbstractAgent {
|
|
|
3317
3588
|
};
|
|
3318
3589
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3319
3590
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3320
|
-
this.client = new
|
|
3591
|
+
this.client = new OpenAI6({
|
|
3321
3592
|
apiKey,
|
|
3322
3593
|
baseURL: "https://api.moonshot.ai/v1"
|
|
3323
3594
|
});
|
|
@@ -3508,7 +3779,7 @@ ${openAIMessages[0].content}`;
|
|
|
3508
3779
|
};
|
|
3509
3780
|
|
|
3510
3781
|
// src/agents/glm-agent.ts
|
|
3511
|
-
import { OpenAI as
|
|
3782
|
+
import { OpenAI as OpenAI7 } from "openai";
|
|
3512
3783
|
var GlmAgent = class extends AbstractAgent {
|
|
3513
3784
|
client;
|
|
3514
3785
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -3534,7 +3805,7 @@ var GlmAgent = class extends AbstractAgent {
|
|
|
3534
3805
|
};
|
|
3535
3806
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3536
3807
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3537
|
-
this.client = new
|
|
3808
|
+
this.client = new OpenAI7({
|
|
3538
3809
|
apiKey,
|
|
3539
3810
|
baseURL: "https://api.z.ai/api/paas/v4/"
|
|
3540
3811
|
});
|
|
@@ -3688,7 +3959,7 @@ ${openAIMessages[0].content}`;
|
|
|
3688
3959
|
};
|
|
3689
3960
|
|
|
3690
3961
|
// src/agents/fugu-agent.ts
|
|
3691
|
-
import { OpenAI as
|
|
3962
|
+
import { OpenAI as OpenAI8 } from "openai";
|
|
3692
3963
|
var FuguAgent = class extends AbstractAgent {
|
|
3693
3964
|
client;
|
|
3694
3965
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -3712,7 +3983,7 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
3712
3983
|
};
|
|
3713
3984
|
constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3714
3985
|
super(name, instruction, model, 1, enableThinking, agentLoggingConfig);
|
|
3715
|
-
this.client = new
|
|
3986
|
+
this.client = new OpenAI8({
|
|
3716
3987
|
apiKey,
|
|
3717
3988
|
baseURL: "https://api.sakana.ai/v1",
|
|
3718
3989
|
timeout: 12e5
|
|
@@ -3879,7 +4150,7 @@ ${schemaDescription}`;
|
|
|
3879
4150
|
};
|
|
3880
4151
|
|
|
3881
4152
|
// src/agents/qwen-agent.ts
|
|
3882
|
-
import { OpenAI as
|
|
4153
|
+
import { OpenAI as OpenAI9 } from "openai";
|
|
3883
4154
|
var QwenAgent = class extends AbstractAgent {
|
|
3884
4155
|
client;
|
|
3885
4156
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -3904,7 +4175,7 @@ var QwenAgent = class extends AbstractAgent {
|
|
|
3904
4175
|
};
|
|
3905
4176
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3906
4177
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3907
|
-
this.client = new
|
|
4178
|
+
this.client = new OpenAI9({
|
|
3908
4179
|
apiKey,
|
|
3909
4180
|
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
3910
4181
|
});
|
|
@@ -4079,7 +4350,7 @@ ${openAIMessages[0].content}`;
|
|
|
4079
4350
|
};
|
|
4080
4351
|
|
|
4081
4352
|
// src/agents/minimax-agent.ts
|
|
4082
|
-
import { OpenAI as
|
|
4353
|
+
import { OpenAI as OpenAI10 } from "openai";
|
|
4083
4354
|
var MiniMaxAgent = class extends AbstractAgent {
|
|
4084
4355
|
client;
|
|
4085
4356
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -4104,7 +4375,7 @@ var MiniMaxAgent = class extends AbstractAgent {
|
|
|
4104
4375
|
};
|
|
4105
4376
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
4106
4377
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
4107
|
-
this.client = new
|
|
4378
|
+
this.client = new OpenAI10({
|
|
4108
4379
|
apiKey,
|
|
4109
4380
|
baseURL: "https://api.minimax.io/v1"
|
|
4110
4381
|
});
|
|
@@ -4341,10 +4612,12 @@ export {
|
|
|
4341
4612
|
DeepSeekV2Agent,
|
|
4342
4613
|
FUGU_REASONING_EFFORTS,
|
|
4343
4614
|
FuguAgent,
|
|
4615
|
+
GEMINI_AUDIO_TOKENS_PER_SECOND,
|
|
4344
4616
|
GEMINI_REASONING_EFFORTS,
|
|
4345
4617
|
GLM_REASONING_EFFORTS,
|
|
4346
4618
|
GlmAgent,
|
|
4347
4619
|
GoogleAgent,
|
|
4620
|
+
GoogleVoiceAgent,
|
|
4348
4621
|
Gpt5Agent,
|
|
4349
4622
|
GrokAgent,
|
|
4350
4623
|
KimiAgent,
|
|
@@ -4362,23 +4635,35 @@ export {
|
|
|
4362
4635
|
ModelRefusalError,
|
|
4363
4636
|
ModelUnavailableError,
|
|
4364
4637
|
OPENAI_REASONING_EFFORTS,
|
|
4638
|
+
OpenAiVoiceAgent,
|
|
4365
4639
|
QwenAgent,
|
|
4366
4640
|
REASONING_EFFORT_SCALE,
|
|
4641
|
+
SUPPORTED_VOICE_PROVIDERS,
|
|
4367
4642
|
SupportedAiKeyNames,
|
|
4368
4643
|
SupportedAiModels,
|
|
4644
|
+
VOICE_MODEL_CONSTANTS,
|
|
4645
|
+
VOICE_MODEL_PRICING,
|
|
4646
|
+
VOICE_PROVIDER_API_KEY,
|
|
4647
|
+
VoiceAgentFactory,
|
|
4369
4648
|
ZodSchemaConverter,
|
|
4649
|
+
buildGoogleTtsPrompt,
|
|
4370
4650
|
calculateAnthropicCost,
|
|
4371
4651
|
calculateCost,
|
|
4372
4652
|
calculateDeepSeekCost,
|
|
4653
|
+
calculateGeminiSttCost,
|
|
4654
|
+
calculateGeminiTtsCost,
|
|
4373
4655
|
calculateGoogleCost,
|
|
4374
4656
|
calculateGrokCost,
|
|
4375
4657
|
calculateKimiCost,
|
|
4376
4658
|
calculateMistralCost,
|
|
4377
4659
|
calculateModelCost,
|
|
4378
4660
|
calculateOpenAICost,
|
|
4661
|
+
calculateOpenAiSttCost,
|
|
4662
|
+
calculateOpenAiTtsCost,
|
|
4379
4663
|
clampReasoningEffort,
|
|
4380
4664
|
cleanResponse,
|
|
4381
4665
|
createCatalog,
|
|
4666
|
+
createVoiceAgent,
|
|
4382
4667
|
extractAnthropicTokenUsage,
|
|
4383
4668
|
extractTokenUsageFromResponse5 as extractAnthropicTokenUsageFromResponse,
|
|
4384
4669
|
extractDeepSeekTokenUsage,
|
|
@@ -4396,6 +4681,8 @@ export {
|
|
|
4396
4681
|
extractTokenUsageFromResponse as extractOpenAITokenUsageFromResponse,
|
|
4397
4682
|
extractTokenUsage,
|
|
4398
4683
|
extractUsageAndCalculateCost,
|
|
4684
|
+
generateGoogleTtsAudio,
|
|
4685
|
+
generateOpenAiTtsAudio,
|
|
4399
4686
|
generateSchemaInstructions,
|
|
4400
4687
|
getModelConfigByApiName,
|
|
4401
4688
|
getModelDisplayName,
|
|
@@ -4412,6 +4699,7 @@ export {
|
|
|
4412
4699
|
modelIsFast,
|
|
4413
4700
|
needsPromptBasedSchema,
|
|
4414
4701
|
parseAndValidateLlmJson,
|
|
4702
|
+
pcmToWav,
|
|
4415
4703
|
safeValidateResponse,
|
|
4416
4704
|
setLlmLogger,
|
|
4417
4705
|
stableHashHex,
|
|
@@ -4423,6 +4711,8 @@ export {
|
|
|
4423
4711
|
toGeminiEffort,
|
|
4424
4712
|
toGlmEffort,
|
|
4425
4713
|
toOpenAIEffort,
|
|
4714
|
+
transcribeWithGemini,
|
|
4715
|
+
transcribeWithOpenAi,
|
|
4426
4716
|
validateResponse
|
|
4427
4717
|
};
|
|
4428
4718
|
//# sourceMappingURL=index.mjs.map
|