@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.js
CHANGED
|
@@ -44,10 +44,12 @@ __export(index_exports, {
|
|
|
44
44
|
DeepSeekV2Agent: () => DeepSeekV2Agent,
|
|
45
45
|
FUGU_REASONING_EFFORTS: () => FUGU_REASONING_EFFORTS,
|
|
46
46
|
FuguAgent: () => FuguAgent,
|
|
47
|
+
GEMINI_AUDIO_TOKENS_PER_SECOND: () => GEMINI_AUDIO_TOKENS_PER_SECOND,
|
|
47
48
|
GEMINI_REASONING_EFFORTS: () => GEMINI_REASONING_EFFORTS,
|
|
48
49
|
GLM_REASONING_EFFORTS: () => GLM_REASONING_EFFORTS,
|
|
49
50
|
GlmAgent: () => GlmAgent,
|
|
50
51
|
GoogleAgent: () => GoogleAgent,
|
|
52
|
+
GoogleVoiceAgent: () => GoogleVoiceAgent,
|
|
51
53
|
Gpt5Agent: () => Gpt5Agent,
|
|
52
54
|
GrokAgent: () => GrokAgent,
|
|
53
55
|
KimiAgent: () => KimiAgent,
|
|
@@ -65,23 +67,35 @@ __export(index_exports, {
|
|
|
65
67
|
ModelRefusalError: () => ModelRefusalError,
|
|
66
68
|
ModelUnavailableError: () => ModelUnavailableError,
|
|
67
69
|
OPENAI_REASONING_EFFORTS: () => OPENAI_REASONING_EFFORTS,
|
|
70
|
+
OpenAiVoiceAgent: () => OpenAiVoiceAgent,
|
|
68
71
|
QwenAgent: () => QwenAgent,
|
|
69
72
|
REASONING_EFFORT_SCALE: () => REASONING_EFFORT_SCALE,
|
|
73
|
+
SUPPORTED_VOICE_PROVIDERS: () => SUPPORTED_VOICE_PROVIDERS,
|
|
70
74
|
SupportedAiKeyNames: () => SupportedAiKeyNames,
|
|
71
75
|
SupportedAiModels: () => SupportedAiModels,
|
|
76
|
+
VOICE_MODEL_CONSTANTS: () => VOICE_MODEL_CONSTANTS,
|
|
77
|
+
VOICE_MODEL_PRICING: () => VOICE_MODEL_PRICING,
|
|
78
|
+
VOICE_PROVIDER_API_KEY: () => VOICE_PROVIDER_API_KEY,
|
|
79
|
+
VoiceAgentFactory: () => VoiceAgentFactory,
|
|
72
80
|
ZodSchemaConverter: () => ZodSchemaConverter,
|
|
81
|
+
buildGoogleTtsPrompt: () => buildGoogleTtsPrompt,
|
|
73
82
|
calculateAnthropicCost: () => calculateAnthropicCost,
|
|
74
83
|
calculateCost: () => calculateCost,
|
|
75
84
|
calculateDeepSeekCost: () => calculateDeepSeekCost,
|
|
85
|
+
calculateGeminiSttCost: () => calculateGeminiSttCost,
|
|
86
|
+
calculateGeminiTtsCost: () => calculateGeminiTtsCost,
|
|
76
87
|
calculateGoogleCost: () => calculateGoogleCost,
|
|
77
88
|
calculateGrokCost: () => calculateGrokCost,
|
|
78
89
|
calculateKimiCost: () => calculateKimiCost,
|
|
79
90
|
calculateMistralCost: () => calculateMistralCost,
|
|
80
91
|
calculateModelCost: () => calculateModelCost,
|
|
81
92
|
calculateOpenAICost: () => calculateOpenAICost,
|
|
93
|
+
calculateOpenAiSttCost: () => calculateOpenAiSttCost,
|
|
94
|
+
calculateOpenAiTtsCost: () => calculateOpenAiTtsCost,
|
|
82
95
|
clampReasoningEffort: () => clampReasoningEffort,
|
|
83
96
|
cleanResponse: () => cleanResponse,
|
|
84
97
|
createCatalog: () => createCatalog,
|
|
98
|
+
createVoiceAgent: () => createVoiceAgent,
|
|
85
99
|
extractAnthropicTokenUsage: () => extractAnthropicTokenUsage,
|
|
86
100
|
extractAnthropicTokenUsageFromResponse: () => extractTokenUsageFromResponse5,
|
|
87
101
|
extractDeepSeekTokenUsage: () => extractDeepSeekTokenUsage,
|
|
@@ -99,6 +113,8 @@ __export(index_exports, {
|
|
|
99
113
|
extractOpenAITokenUsageFromResponse: () => extractTokenUsageFromResponse,
|
|
100
114
|
extractTokenUsage: () => extractTokenUsage,
|
|
101
115
|
extractUsageAndCalculateCost: () => extractUsageAndCalculateCost,
|
|
116
|
+
generateGoogleTtsAudio: () => generateGoogleTtsAudio,
|
|
117
|
+
generateOpenAiTtsAudio: () => generateOpenAiTtsAudio,
|
|
102
118
|
generateSchemaInstructions: () => generateSchemaInstructions,
|
|
103
119
|
getModelConfigByApiName: () => getModelConfigByApiName,
|
|
104
120
|
getModelDisplayName: () => getModelDisplayName,
|
|
@@ -115,6 +131,7 @@ __export(index_exports, {
|
|
|
115
131
|
modelIsFast: () => modelIsFast,
|
|
116
132
|
needsPromptBasedSchema: () => needsPromptBasedSchema,
|
|
117
133
|
parseAndValidateLlmJson: () => parseAndValidateLlmJson,
|
|
134
|
+
pcmToWav: () => pcmToWav,
|
|
118
135
|
safeValidateResponse: () => safeValidateResponse,
|
|
119
136
|
setLlmLogger: () => setLlmLogger,
|
|
120
137
|
stableHashHex: () => stableHashHex,
|
|
@@ -126,6 +143,8 @@ __export(index_exports, {
|
|
|
126
143
|
toGeminiEffort: () => toGeminiEffort,
|
|
127
144
|
toGlmEffort: () => toGlmEffort,
|
|
128
145
|
toOpenAIEffort: () => toOpenAIEffort,
|
|
146
|
+
transcribeWithGemini: () => transcribeWithGemini,
|
|
147
|
+
transcribeWithOpenAi: () => transcribeWithOpenAi,
|
|
129
148
|
validateResponse: () => validateResponse
|
|
130
149
|
});
|
|
131
150
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -1750,6 +1769,263 @@ function extractTokenUsageFromResponse7(response) {
|
|
|
1750
1769
|
return extractMistralTokenUsage(response);
|
|
1751
1770
|
}
|
|
1752
1771
|
|
|
1772
|
+
// src/voice/voice-catalog.ts
|
|
1773
|
+
var VOICE_MODEL_CONSTANTS = {
|
|
1774
|
+
OPENAI_TTS: "gpt-4o-mini-tts",
|
|
1775
|
+
OPENAI_STT: "whisper-1",
|
|
1776
|
+
// ai.google.dev/gemini-api/docs/speech-generation
|
|
1777
|
+
GOOGLE_TTS: "gemini-3.1-flash-tts-preview",
|
|
1778
|
+
// ai.google.dev/gemini-api/docs/transcribe — Interactions API only (see google-stt.ts)
|
|
1779
|
+
GOOGLE_STT: "gemini-3.5-transcribe"
|
|
1780
|
+
};
|
|
1781
|
+
var SUPPORTED_VOICE_PROVIDERS = ["openai", "google"];
|
|
1782
|
+
var VOICE_PROVIDER_API_KEY = {
|
|
1783
|
+
openai: API_KEY_CONSTANTS.OPENAI,
|
|
1784
|
+
google: API_KEY_CONSTANTS.GOOGLE
|
|
1785
|
+
};
|
|
1786
|
+
var VOICE_MODEL_PRICING = {
|
|
1787
|
+
[VOICE_MODEL_CONSTANTS.OPENAI_TTS]: { pricePerMillionCharacters: 15 },
|
|
1788
|
+
[VOICE_MODEL_CONSTANTS.OPENAI_STT]: { pricePerMinute: 6e-3 },
|
|
1789
|
+
// Measured 2026-09-05: ~32 audio tokens per second of speech, so a 15-second
|
|
1790
|
+
// line is ~$0.01 — about 3-5x an OpenAI line of the same length.
|
|
1791
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_TTS]: { textInputPricePerM: 1, audioOutputPricePerM: 20 },
|
|
1792
|
+
// ~25 audio tokens per second in, ~175 text tokens per minute out: ≈ $0.005/min.
|
|
1793
|
+
[VOICE_MODEL_CONSTANTS.GOOGLE_STT]: { audioInputPricePerM: 2, textOutputPricePerM: 12 }
|
|
1794
|
+
};
|
|
1795
|
+
|
|
1796
|
+
// src/voice/voice-pricing.ts
|
|
1797
|
+
function roundUSD(value) {
|
|
1798
|
+
return parseFloat((value || 0).toFixed(6));
|
|
1799
|
+
}
|
|
1800
|
+
function calculateOpenAiTtsCost(characterCount) {
|
|
1801
|
+
const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_TTS]?.pricePerMillionCharacters ?? 0;
|
|
1802
|
+
if (!characterCount || characterCount <= 0 || rate <= 0) return 0;
|
|
1803
|
+
return roundUSD(characterCount / 1e6 * rate);
|
|
1804
|
+
}
|
|
1805
|
+
function calculateOpenAiSttCost(durationSeconds) {
|
|
1806
|
+
const rate = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.OPENAI_STT]?.pricePerMinute ?? 0;
|
|
1807
|
+
if (!durationSeconds || durationSeconds <= 0 || rate <= 0) return 0;
|
|
1808
|
+
return roundUSD(durationSeconds / 60 * rate);
|
|
1809
|
+
}
|
|
1810
|
+
function calculateGeminiTtsCost(usage) {
|
|
1811
|
+
const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_TTS];
|
|
1812
|
+
return tokenPairCost(usage, pricing?.textInputPricePerM ?? 0, pricing?.audioOutputPricePerM ?? 0);
|
|
1813
|
+
}
|
|
1814
|
+
function calculateGeminiSttCost(usage) {
|
|
1815
|
+
const pricing = VOICE_MODEL_PRICING[VOICE_MODEL_CONSTANTS.GOOGLE_STT];
|
|
1816
|
+
return tokenPairCost(usage, pricing?.audioInputPricePerM ?? 0, pricing?.textOutputPricePerM ?? 0);
|
|
1817
|
+
}
|
|
1818
|
+
function tokenPairCost(usage, inputRate, outputRate) {
|
|
1819
|
+
const inputTokens = Math.max(0, usage.inputTokens || 0);
|
|
1820
|
+
const outputTokens = Math.max(0, usage.outputTokens || 0);
|
|
1821
|
+
return inputTokens / 1e6 * inputRate + outputTokens / 1e6 * outputRate;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/voice/openai-tts.ts
|
|
1825
|
+
var import_openai = require("openai");
|
|
1826
|
+
async function generateOpenAiTtsAudio(text, apiKey, options = {}) {
|
|
1827
|
+
const client = new import_openai.OpenAI({ apiKey });
|
|
1828
|
+
const speechOptions = {
|
|
1829
|
+
model: VOICE_MODEL_CONSTANTS.OPENAI_TTS,
|
|
1830
|
+
voice: options.voice || "alloy",
|
|
1831
|
+
input: text,
|
|
1832
|
+
speed: options.speed || 1,
|
|
1833
|
+
response_format: options.format || "wav"
|
|
1834
|
+
};
|
|
1835
|
+
if (options.instructions) {
|
|
1836
|
+
speechOptions.instructions = options.instructions;
|
|
1837
|
+
}
|
|
1838
|
+
const response = await client.audio.speech.create(speechOptions);
|
|
1839
|
+
return await response.arrayBuffer();
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/voice/openai-stt.ts
|
|
1843
|
+
var import_openai2 = require("openai");
|
|
1844
|
+
async function transcribeWithOpenAi(audioBuffer, apiKey, options = {}) {
|
|
1845
|
+
const client = new import_openai2.OpenAI({ apiKey });
|
|
1846
|
+
const audioFile = new File([new Uint8Array(audioBuffer)], options.fileName || "audio.webm", { type: options.mimeType || "audio/webm" });
|
|
1847
|
+
const transcription = await client.audio.transcriptions.create({
|
|
1848
|
+
file: audioFile,
|
|
1849
|
+
model: VOICE_MODEL_CONSTANTS.OPENAI_STT,
|
|
1850
|
+
language: options.language || "en",
|
|
1851
|
+
prompt: options.prompt,
|
|
1852
|
+
temperature: options.temperature || 0,
|
|
1853
|
+
response_format: "verbose_json"
|
|
1854
|
+
});
|
|
1855
|
+
const explicitDuration = Number(transcription?.duration) || 0;
|
|
1856
|
+
const segments = Array.isArray(transcription?.segments) ? transcription.segments : [];
|
|
1857
|
+
const segmentsDuration = segments.reduce((max, segment) => {
|
|
1858
|
+
const end = Number(segment?.end);
|
|
1859
|
+
return end > max ? end : max;
|
|
1860
|
+
}, 0);
|
|
1861
|
+
const text = typeof transcription?.text === "string" ? transcription.text : segments.map((segment) => segment?.text || "").join(" ");
|
|
1862
|
+
return { text: text.trim(), durationSeconds: explicitDuration || segmentsDuration };
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/voice/openai-voice-agent.ts
|
|
1866
|
+
var OpenAiVoiceAgent = class {
|
|
1867
|
+
constructor(apiKey) {
|
|
1868
|
+
this.apiKey = apiKey;
|
|
1869
|
+
}
|
|
1870
|
+
apiKey;
|
|
1871
|
+
provider = "openai";
|
|
1872
|
+
ttsModel = VOICE_MODEL_CONSTANTS.OPENAI_TTS;
|
|
1873
|
+
sttModel = VOICE_MODEL_CONSTANTS.OPENAI_STT;
|
|
1874
|
+
async speak(request) {
|
|
1875
|
+
const audio = await generateOpenAiTtsAudio(request.text, this.apiKey, {
|
|
1876
|
+
voice: request.voice,
|
|
1877
|
+
instructions: request.voiceStyle || void 0
|
|
1878
|
+
});
|
|
1879
|
+
const characters = request.text.length;
|
|
1880
|
+
return { audio, costUSD: calculateOpenAiTtsCost(characters), usage: { characters } };
|
|
1881
|
+
}
|
|
1882
|
+
async transcribe(request) {
|
|
1883
|
+
const { text, durationSeconds } = await transcribeWithOpenAi(request.audio, this.apiKey, {
|
|
1884
|
+
language: request.language,
|
|
1885
|
+
prompt: request.prompt,
|
|
1886
|
+
fileName: request.fileName,
|
|
1887
|
+
mimeType: request.mimeType
|
|
1888
|
+
});
|
|
1889
|
+
return { text, durationSeconds, costUSD: calculateOpenAiSttCost(durationSeconds), usage: { audioSeconds: durationSeconds } };
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
// src/voice/google-tts.ts
|
|
1894
|
+
var import_genai = require("@google/genai");
|
|
1895
|
+
var AUDIO_TOKENS_PER_SECOND = 32;
|
|
1896
|
+
var SAMPLE_RATE = 24e3;
|
|
1897
|
+
var PCM_BYTES_PER_SECOND = SAMPLE_RATE * 2;
|
|
1898
|
+
function buildGoogleTtsPrompt(text, voiceStyle) {
|
|
1899
|
+
const style = voiceStyle?.trim().replace(/[:.!,;\s]+$/, "");
|
|
1900
|
+
if (!style) return text;
|
|
1901
|
+
const isShort = style.split(/\s+/).length <= 3 && !/[.!?,;]/.test(style);
|
|
1902
|
+
return isShort ? `Say ${style}: ${text}` : `${style}:
|
|
1903
|
+
${text}`;
|
|
1904
|
+
}
|
|
1905
|
+
function pcmToWav(pcmData, sampleRate = SAMPLE_RATE) {
|
|
1906
|
+
const numChannels = 1;
|
|
1907
|
+
const bitsPerSample = 16;
|
|
1908
|
+
const blockAlign = numChannels * (bitsPerSample / 8);
|
|
1909
|
+
const byteRate = sampleRate * blockAlign;
|
|
1910
|
+
const headerSize = 44;
|
|
1911
|
+
const buffer = new ArrayBuffer(headerSize + pcmData.length);
|
|
1912
|
+
const view = new DataView(buffer);
|
|
1913
|
+
const writeString = (offset, str) => {
|
|
1914
|
+
for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));
|
|
1915
|
+
};
|
|
1916
|
+
writeString(0, "RIFF");
|
|
1917
|
+
view.setUint32(4, 36 + pcmData.length, true);
|
|
1918
|
+
writeString(8, "WAVE");
|
|
1919
|
+
writeString(12, "fmt ");
|
|
1920
|
+
view.setUint32(16, 16, true);
|
|
1921
|
+
view.setUint16(20, 1, true);
|
|
1922
|
+
view.setUint16(22, numChannels, true);
|
|
1923
|
+
view.setUint32(24, sampleRate, true);
|
|
1924
|
+
view.setUint32(28, byteRate, true);
|
|
1925
|
+
view.setUint16(32, blockAlign, true);
|
|
1926
|
+
view.setUint16(34, bitsPerSample, true);
|
|
1927
|
+
writeString(36, "data");
|
|
1928
|
+
view.setUint32(40, pcmData.length, true);
|
|
1929
|
+
new Uint8Array(buffer, headerSize).set(pcmData);
|
|
1930
|
+
return buffer;
|
|
1931
|
+
}
|
|
1932
|
+
async function generateGoogleTtsAudio(text, apiKey, options) {
|
|
1933
|
+
const client = new import_genai.GoogleGenAI({ apiKey });
|
|
1934
|
+
const response = await client.models.generateContent({
|
|
1935
|
+
model: VOICE_MODEL_CONSTANTS.GOOGLE_TTS,
|
|
1936
|
+
contents: [{ parts: [{ text: buildGoogleTtsPrompt(text, options.voiceStyle) }] }],
|
|
1937
|
+
config: {
|
|
1938
|
+
responseModalities: ["AUDIO"],
|
|
1939
|
+
speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: options.voiceName } } }
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
const parts = response.candidates?.[0]?.content?.parts ?? [];
|
|
1943
|
+
const audioPart = parts.find((part) => part.inlineData?.mimeType?.startsWith("audio/"));
|
|
1944
|
+
if (!audioPart?.inlineData?.data) {
|
|
1945
|
+
throw new Error("No audio data in Google TTS response");
|
|
1946
|
+
}
|
|
1947
|
+
const pcmData = new Uint8Array(Buffer.from(audioPart.inlineData.data, "base64"));
|
|
1948
|
+
const usageMetadata = response.usageMetadata ?? {};
|
|
1949
|
+
const inputTokens = usageMetadata.promptTokenCount ?? 0;
|
|
1950
|
+
const reportedOutput = usageMetadata.candidatesTokenCount;
|
|
1951
|
+
const outputTokens = reportedOutput && reportedOutput > 0 ? reportedOutput : Math.ceil(pcmData.length / PCM_BYTES_PER_SECOND * AUDIO_TOKENS_PER_SECOND);
|
|
1952
|
+
return { audio: pcmToWav(pcmData), usage: { inputTokens, outputTokens } };
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
// src/voice/google-stt.ts
|
|
1956
|
+
var import_genai2 = require("@google/genai");
|
|
1957
|
+
var GEMINI_AUDIO_TOKENS_PER_SECOND = 25;
|
|
1958
|
+
async function transcribeWithGemini(audioBuffer, apiKey, options = {}) {
|
|
1959
|
+
const client = new import_genai2.GoogleGenAI({ apiKey });
|
|
1960
|
+
const interaction = await client.interactions.create({
|
|
1961
|
+
model: VOICE_MODEL_CONSTANTS.GOOGLE_STT,
|
|
1962
|
+
input: [{ type: "audio", data: Buffer.from(audioBuffer).toString("base64"), mime_type: options.mimeType || "audio/webm" }]
|
|
1963
|
+
});
|
|
1964
|
+
const text = typeof interaction?.output_text === "string" ? interaction.output_text.trim() : "";
|
|
1965
|
+
const usage = interaction?.usage ?? {};
|
|
1966
|
+
const byModality = (rows, modality) => (rows ?? []).filter((r) => r?.modality === modality).reduce((sum, r) => sum + (Number(r?.tokens) || 0), 0);
|
|
1967
|
+
const inputTokens = byModality(usage.input_tokens_by_modality, "audio") || usage.total_input_tokens || 0;
|
|
1968
|
+
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);
|
|
1969
|
+
const outputTokens = usage.total_output_tokens || invocationOutput || Math.ceil(text.length / 4);
|
|
1970
|
+
return { text, durationSeconds: inputTokens / GEMINI_AUDIO_TOKENS_PER_SECOND, usage: { inputTokens, outputTokens } };
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
// src/voice/google-voice-agent.ts
|
|
1974
|
+
var GoogleVoiceAgent = class {
|
|
1975
|
+
constructor(apiKey) {
|
|
1976
|
+
this.apiKey = apiKey;
|
|
1977
|
+
}
|
|
1978
|
+
apiKey;
|
|
1979
|
+
provider = "google";
|
|
1980
|
+
ttsModel = VOICE_MODEL_CONSTANTS.GOOGLE_TTS;
|
|
1981
|
+
sttModel = VOICE_MODEL_CONSTANTS.GOOGLE_STT;
|
|
1982
|
+
async speak(request) {
|
|
1983
|
+
const { audio, usage } = await generateGoogleTtsAudio(request.text, this.apiKey, {
|
|
1984
|
+
voiceName: request.voice,
|
|
1985
|
+
voiceStyle: request.voiceStyle
|
|
1986
|
+
});
|
|
1987
|
+
return { audio, costUSD: calculateGeminiTtsCost(usage), usage };
|
|
1988
|
+
}
|
|
1989
|
+
async transcribe(request) {
|
|
1990
|
+
const { text, durationSeconds, usage } = await transcribeWithGemini(request.audio, this.apiKey, { mimeType: request.mimeType });
|
|
1991
|
+
return { text, durationSeconds, costUSD: calculateGeminiSttCost(usage), usage };
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
// src/voice/voice-agent-factory.ts
|
|
1996
|
+
var VoiceAgentFactory = class _VoiceAgentFactory {
|
|
1997
|
+
/** Build an agent from a key already resolved by the caller. */
|
|
1998
|
+
static createAgent(provider, apiKey) {
|
|
1999
|
+
switch (provider) {
|
|
2000
|
+
case "openai":
|
|
2001
|
+
return new OpenAiVoiceAgent(apiKey);
|
|
2002
|
+
case "google":
|
|
2003
|
+
return new GoogleVoiceAgent(apiKey);
|
|
2004
|
+
default:
|
|
2005
|
+
throw new Error(`Unknown voice provider: ${provider}`);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Build an agent from a key map (the shape AgentFactory takes), reading the
|
|
2010
|
+
* provider's key by its API_KEY_CONSTANTS name. Throws when the key is missing
|
|
2011
|
+
* so the host can report a misconfiguration before any SDK is touched.
|
|
2012
|
+
*/
|
|
2013
|
+
static createAgentFromKeys(provider, apiKeys) {
|
|
2014
|
+
if (!SUPPORTED_VOICE_PROVIDERS.includes(provider)) {
|
|
2015
|
+
throw new Error(`Unknown voice provider: ${provider}`);
|
|
2016
|
+
}
|
|
2017
|
+
const keyName = VOICE_PROVIDER_API_KEY[provider];
|
|
2018
|
+
const apiKey = apiKeys[keyName];
|
|
2019
|
+
if (!apiKey) {
|
|
2020
|
+
throw new Error(`Missing API key ${keyName} for voice provider ${provider}`);
|
|
2021
|
+
}
|
|
2022
|
+
return _VoiceAgentFactory.createAgent(provider, apiKey);
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
function createVoiceAgent(provider, apiKey) {
|
|
2026
|
+
return VoiceAgentFactory.createAgent(provider, apiKey);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
1753
2029
|
// src/agents/abstract-agent.ts
|
|
1754
2030
|
var AbstractAgent = class {
|
|
1755
2031
|
name;
|
|
@@ -1896,10 +2172,15 @@ ${msg.content}` };
|
|
|
1896
2172
|
};
|
|
1897
2173
|
|
|
1898
2174
|
// src/agents/gpt-5-agent.ts
|
|
1899
|
-
var
|
|
2175
|
+
var import_openai3 = __toESM(require("openai"));
|
|
1900
2176
|
var import_zod2 = require("openai/helpers/zod");
|
|
1901
2177
|
var Gpt5Agent = class extends AbstractAgent {
|
|
1902
2178
|
client;
|
|
2179
|
+
// Routing hint for OpenAI's prefix cache (same scheme as the Mistral/Grok agents): one
|
|
2180
|
+
// key per agent+instruction, so an agent's own calls group together instead of every
|
|
2181
|
+
// agent that shares a static prefix hashing to the same route. Keys influence routing
|
|
2182
|
+
// only; they do not guarantee a hit.
|
|
2183
|
+
promptCacheKey;
|
|
1903
2184
|
// Log message templates
|
|
1904
2185
|
logTemplates = {
|
|
1905
2186
|
error: (name, error) => `Error in ${name} agent: ${error}`
|
|
@@ -1912,7 +2193,9 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1912
2193
|
};
|
|
1913
2194
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
1914
2195
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
1915
|
-
this.
|
|
2196
|
+
this.promptCacheKey = stableHashHex(`${name}
|
|
2197
|
+
${instruction}`);
|
|
2198
|
+
this.client = new import_openai3.default({
|
|
1916
2199
|
apiKey
|
|
1917
2200
|
});
|
|
1918
2201
|
}
|
|
@@ -1926,10 +2209,7 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1926
2209
|
try {
|
|
1927
2210
|
this.logAsking(messages);
|
|
1928
2211
|
this.logMessages(messages);
|
|
1929
|
-
const input =
|
|
1930
|
-
`System: ${this.instruction}`,
|
|
1931
|
-
...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
|
|
1932
|
-
].join("\n\n");
|
|
2212
|
+
const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
|
|
1933
2213
|
const schemaToSend = zodSchema;
|
|
1934
2214
|
let response;
|
|
1935
2215
|
try {
|
|
@@ -1938,6 +2218,7 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
1938
2218
|
instructions: this.instruction,
|
|
1939
2219
|
input,
|
|
1940
2220
|
max_output_tokens: this.maxOutputTokens,
|
|
2221
|
+
prompt_cache_key: this.promptCacheKey,
|
|
1941
2222
|
text: {
|
|
1942
2223
|
format: (0, import_zod2.zodTextFormat)(schemaToSend, "response_schema")
|
|
1943
2224
|
}
|
|
@@ -2016,15 +2297,13 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
2016
2297
|
try {
|
|
2017
2298
|
this.logAsking(messages);
|
|
2018
2299
|
this.logMessages(messages);
|
|
2019
|
-
const input =
|
|
2020
|
-
`System: ${this.instruction}`,
|
|
2021
|
-
...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
|
|
2022
|
-
].join("\n\n");
|
|
2300
|
+
const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
|
|
2023
2301
|
const response = await this.client.responses.create({
|
|
2024
2302
|
model: this.model,
|
|
2025
2303
|
instructions: this.instruction,
|
|
2026
2304
|
input,
|
|
2027
|
-
max_output_tokens: this.maxOutputTokens
|
|
2305
|
+
max_output_tokens: this.maxOutputTokens,
|
|
2306
|
+
prompt_cache_key: this.promptCacheKey
|
|
2028
2307
|
});
|
|
2029
2308
|
const content = response.output_text;
|
|
2030
2309
|
if (!content) {
|
|
@@ -2080,13 +2359,24 @@ var Gpt5Agent = class extends AbstractAgent {
|
|
|
2080
2359
|
var import_sdk = require("@anthropic-ai/sdk");
|
|
2081
2360
|
var ClaudeAgent = class extends AbstractAgent {
|
|
2082
2361
|
client;
|
|
2362
|
+
/**
|
|
2363
|
+
* TTL for every breakpoint this agent places (system tiers and the message anchor).
|
|
2364
|
+
* Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
|
|
2365
|
+
* refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
|
|
2366
|
+
* pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
|
|
2367
|
+
* expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
|
|
2368
|
+
* under five minutes). Set '5m' for continuous traffic where every call lands inside the
|
|
2369
|
+
* window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
|
|
2370
|
+
* requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
|
|
2371
|
+
*/
|
|
2372
|
+
cacheTtl = "1h";
|
|
2083
2373
|
// System-prompt breakpoints, one per cache tier (see CACHE_TIER_MARKER):
|
|
2084
|
-
// block 1
|
|
2085
|
-
// same rule set, so one
|
|
2086
|
-
// refreshes
|
|
2087
|
-
// block 2
|
|
2088
|
-
//
|
|
2089
|
-
//
|
|
2374
|
+
// block 1 - shared static rules, byte-identical across all bots and games with the
|
|
2375
|
+
// same rule set. Caches are scoped per model, so one entry serves every bot
|
|
2376
|
+
// ON THAT MODEL (not the whole lobby), and any of their calls refreshes it;
|
|
2377
|
+
// block 2 - per-bot identity + game state + summaries, byte-stable between the game's
|
|
2378
|
+
// state writes (a lynch, the night resolution, the summary rewrite, the new
|
|
2379
|
+
// day), so every call inside one of those windows reads it.
|
|
2090
2380
|
// GM prompts have no marker → single block, same behavior as before. Haiku 4.5 needs a
|
|
2091
2381
|
// 4096-token cacheable prefix, so tiers below that silently no-op on Haiku — expected.
|
|
2092
2382
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -2094,7 +2384,7 @@ var ClaudeAgent = class extends AbstractAgent {
|
|
|
2094
2384
|
get defaultParams() {
|
|
2095
2385
|
return {
|
|
2096
2386
|
max_tokens: this.maxOutputTokens,
|
|
2097
|
-
system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral" } })),
|
|
2387
|
+
system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral", ttl: this.cacheTtl } })),
|
|
2098
2388
|
model: this.model
|
|
2099
2389
|
};
|
|
2100
2390
|
}
|
|
@@ -2199,14 +2489,14 @@ var ClaudeAgent = class extends AbstractAgent {
|
|
|
2199
2489
|
const anchor = messages[messages.length - 2];
|
|
2200
2490
|
if (typeof anchor.content === "string") {
|
|
2201
2491
|
if (anchor.content.length > 0) {
|
|
2202
|
-
anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral" } }];
|
|
2492
|
+
anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral", ttl: this.cacheTtl } }];
|
|
2203
2493
|
}
|
|
2204
2494
|
return;
|
|
2205
2495
|
}
|
|
2206
2496
|
for (let i = anchor.content.length - 1; i >= 0; i--) {
|
|
2207
2497
|
const block = anchor.content[i];
|
|
2208
2498
|
if (block.type === "text" && block.text.length > 0) {
|
|
2209
|
-
block.cache_control = { type: "ephemeral" };
|
|
2499
|
+
block.cache_control = { type: "ephemeral", ttl: this.cacheTtl };
|
|
2210
2500
|
return;
|
|
2211
2501
|
}
|
|
2212
2502
|
}
|
|
@@ -2444,7 +2734,7 @@ ${schemaDescription}`;
|
|
|
2444
2734
|
};
|
|
2445
2735
|
|
|
2446
2736
|
// src/agents/google-agent.ts
|
|
2447
|
-
var
|
|
2737
|
+
var import_genai3 = require("@google/genai");
|
|
2448
2738
|
var GoogleAgent = class extends AbstractAgent {
|
|
2449
2739
|
client;
|
|
2450
2740
|
defaultConfig = {
|
|
@@ -2463,7 +2753,7 @@ var GoogleAgent = class extends AbstractAgent {
|
|
|
2463
2753
|
};
|
|
2464
2754
|
constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
2465
2755
|
super(name, instruction, model, 0.2, enableThinking, agentLoggingConfig);
|
|
2466
|
-
this.client = new
|
|
2756
|
+
this.client = new import_genai3.GoogleGenAI({
|
|
2467
2757
|
apiKey
|
|
2468
2758
|
});
|
|
2469
2759
|
}
|
|
@@ -3024,7 +3314,7 @@ ${schemaDescription}`
|
|
|
3024
3314
|
};
|
|
3025
3315
|
|
|
3026
3316
|
// src/agents/deepseek-v2-agent.ts
|
|
3027
|
-
var
|
|
3317
|
+
var import_openai4 = __toESM(require("openai"));
|
|
3028
3318
|
var DeepSeekV2Agent = class extends AbstractAgent {
|
|
3029
3319
|
client;
|
|
3030
3320
|
// Log message templates
|
|
@@ -3040,7 +3330,7 @@ var DeepSeekV2Agent = class extends AbstractAgent {
|
|
|
3040
3330
|
};
|
|
3041
3331
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3042
3332
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3043
|
-
this.client = new
|
|
3333
|
+
this.client = new import_openai4.default({
|
|
3044
3334
|
baseURL: "https://api.deepseek.com",
|
|
3045
3335
|
apiKey
|
|
3046
3336
|
});
|
|
@@ -3232,7 +3522,7 @@ ${schemaDescription}`
|
|
|
3232
3522
|
};
|
|
3233
3523
|
|
|
3234
3524
|
// src/agents/grok-agent.ts
|
|
3235
|
-
var
|
|
3525
|
+
var import_openai5 = require("openai");
|
|
3236
3526
|
var GrokAgent = class extends AbstractAgent {
|
|
3237
3527
|
client;
|
|
3238
3528
|
// Log message templates
|
|
@@ -3249,7 +3539,7 @@ var GrokAgent = class extends AbstractAgent {
|
|
|
3249
3539
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3250
3540
|
const convId = stableHashHex(`${name}
|
|
3251
3541
|
${instruction}`);
|
|
3252
|
-
this.client = new
|
|
3542
|
+
this.client = new import_openai5.OpenAI({
|
|
3253
3543
|
apiKey,
|
|
3254
3544
|
baseURL: "https://api.x.ai/v1",
|
|
3255
3545
|
timeout: 12e5,
|
|
@@ -3422,7 +3712,7 @@ ${input[0].content}`;
|
|
|
3422
3712
|
};
|
|
3423
3713
|
|
|
3424
3714
|
// src/agents/kimi-agent.ts
|
|
3425
|
-
var
|
|
3715
|
+
var import_openai6 = require("openai");
|
|
3426
3716
|
var KimiAgent = class extends AbstractAgent {
|
|
3427
3717
|
client;
|
|
3428
3718
|
// kimi-k3 rejects any temperature other than 1, so we never send the field.
|
|
@@ -3449,7 +3739,7 @@ var KimiAgent = class extends AbstractAgent {
|
|
|
3449
3739
|
};
|
|
3450
3740
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3451
3741
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3452
|
-
this.client = new
|
|
3742
|
+
this.client = new import_openai6.OpenAI({
|
|
3453
3743
|
apiKey,
|
|
3454
3744
|
baseURL: "https://api.moonshot.ai/v1"
|
|
3455
3745
|
});
|
|
@@ -3640,7 +3930,7 @@ ${openAIMessages[0].content}`;
|
|
|
3640
3930
|
};
|
|
3641
3931
|
|
|
3642
3932
|
// src/agents/glm-agent.ts
|
|
3643
|
-
var
|
|
3933
|
+
var import_openai7 = require("openai");
|
|
3644
3934
|
var GlmAgent = class extends AbstractAgent {
|
|
3645
3935
|
client;
|
|
3646
3936
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -3666,7 +3956,7 @@ var GlmAgent = class extends AbstractAgent {
|
|
|
3666
3956
|
};
|
|
3667
3957
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3668
3958
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
3669
|
-
this.client = new
|
|
3959
|
+
this.client = new import_openai7.OpenAI({
|
|
3670
3960
|
apiKey,
|
|
3671
3961
|
baseURL: "https://api.z.ai/api/paas/v4/"
|
|
3672
3962
|
});
|
|
@@ -3820,7 +4110,7 @@ ${openAIMessages[0].content}`;
|
|
|
3820
4110
|
};
|
|
3821
4111
|
|
|
3822
4112
|
// src/agents/fugu-agent.ts
|
|
3823
|
-
var
|
|
4113
|
+
var import_openai8 = require("openai");
|
|
3824
4114
|
var FuguAgent = class extends AbstractAgent {
|
|
3825
4115
|
client;
|
|
3826
4116
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -3844,7 +4134,7 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
3844
4134
|
};
|
|
3845
4135
|
constructor(name, instruction, model, apiKey, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
3846
4136
|
super(name, instruction, model, 1, enableThinking, agentLoggingConfig);
|
|
3847
|
-
this.client = new
|
|
4137
|
+
this.client = new import_openai8.OpenAI({
|
|
3848
4138
|
apiKey,
|
|
3849
4139
|
baseURL: "https://api.sakana.ai/v1",
|
|
3850
4140
|
timeout: 12e5
|
|
@@ -4011,7 +4301,7 @@ ${schemaDescription}`;
|
|
|
4011
4301
|
};
|
|
4012
4302
|
|
|
4013
4303
|
// src/agents/qwen-agent.ts
|
|
4014
|
-
var
|
|
4304
|
+
var import_openai9 = require("openai");
|
|
4015
4305
|
var QwenAgent = class extends AbstractAgent {
|
|
4016
4306
|
client;
|
|
4017
4307
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -4036,7 +4326,7 @@ var QwenAgent = class extends AbstractAgent {
|
|
|
4036
4326
|
};
|
|
4037
4327
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
4038
4328
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
4039
|
-
this.client = new
|
|
4329
|
+
this.client = new import_openai9.OpenAI({
|
|
4040
4330
|
apiKey,
|
|
4041
4331
|
baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
4042
4332
|
});
|
|
@@ -4211,7 +4501,7 @@ ${openAIMessages[0].content}`;
|
|
|
4211
4501
|
};
|
|
4212
4502
|
|
|
4213
4503
|
// src/agents/minimax-agent.ts
|
|
4214
|
-
var
|
|
4504
|
+
var import_openai10 = require("openai");
|
|
4215
4505
|
var MiniMaxAgent = class extends AbstractAgent {
|
|
4216
4506
|
client;
|
|
4217
4507
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
@@ -4236,7 +4526,7 @@ var MiniMaxAgent = class extends AbstractAgent {
|
|
|
4236
4526
|
};
|
|
4237
4527
|
constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
|
|
4238
4528
|
super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
|
|
4239
|
-
this.client = new
|
|
4529
|
+
this.client = new import_openai10.OpenAI({
|
|
4240
4530
|
apiKey,
|
|
4241
4531
|
baseURL: "https://api.minimax.io/v1"
|
|
4242
4532
|
});
|
|
@@ -4474,10 +4764,12 @@ var AgentFactory = class {
|
|
|
4474
4764
|
DeepSeekV2Agent,
|
|
4475
4765
|
FUGU_REASONING_EFFORTS,
|
|
4476
4766
|
FuguAgent,
|
|
4767
|
+
GEMINI_AUDIO_TOKENS_PER_SECOND,
|
|
4477
4768
|
GEMINI_REASONING_EFFORTS,
|
|
4478
4769
|
GLM_REASONING_EFFORTS,
|
|
4479
4770
|
GlmAgent,
|
|
4480
4771
|
GoogleAgent,
|
|
4772
|
+
GoogleVoiceAgent,
|
|
4481
4773
|
Gpt5Agent,
|
|
4482
4774
|
GrokAgent,
|
|
4483
4775
|
KimiAgent,
|
|
@@ -4495,23 +4787,35 @@ var AgentFactory = class {
|
|
|
4495
4787
|
ModelRefusalError,
|
|
4496
4788
|
ModelUnavailableError,
|
|
4497
4789
|
OPENAI_REASONING_EFFORTS,
|
|
4790
|
+
OpenAiVoiceAgent,
|
|
4498
4791
|
QwenAgent,
|
|
4499
4792
|
REASONING_EFFORT_SCALE,
|
|
4793
|
+
SUPPORTED_VOICE_PROVIDERS,
|
|
4500
4794
|
SupportedAiKeyNames,
|
|
4501
4795
|
SupportedAiModels,
|
|
4796
|
+
VOICE_MODEL_CONSTANTS,
|
|
4797
|
+
VOICE_MODEL_PRICING,
|
|
4798
|
+
VOICE_PROVIDER_API_KEY,
|
|
4799
|
+
VoiceAgentFactory,
|
|
4502
4800
|
ZodSchemaConverter,
|
|
4801
|
+
buildGoogleTtsPrompt,
|
|
4503
4802
|
calculateAnthropicCost,
|
|
4504
4803
|
calculateCost,
|
|
4505
4804
|
calculateDeepSeekCost,
|
|
4805
|
+
calculateGeminiSttCost,
|
|
4806
|
+
calculateGeminiTtsCost,
|
|
4506
4807
|
calculateGoogleCost,
|
|
4507
4808
|
calculateGrokCost,
|
|
4508
4809
|
calculateKimiCost,
|
|
4509
4810
|
calculateMistralCost,
|
|
4510
4811
|
calculateModelCost,
|
|
4511
4812
|
calculateOpenAICost,
|
|
4813
|
+
calculateOpenAiSttCost,
|
|
4814
|
+
calculateOpenAiTtsCost,
|
|
4512
4815
|
clampReasoningEffort,
|
|
4513
4816
|
cleanResponse,
|
|
4514
4817
|
createCatalog,
|
|
4818
|
+
createVoiceAgent,
|
|
4515
4819
|
extractAnthropicTokenUsage,
|
|
4516
4820
|
extractAnthropicTokenUsageFromResponse,
|
|
4517
4821
|
extractDeepSeekTokenUsage,
|
|
@@ -4529,6 +4833,8 @@ var AgentFactory = class {
|
|
|
4529
4833
|
extractOpenAITokenUsageFromResponse,
|
|
4530
4834
|
extractTokenUsage,
|
|
4531
4835
|
extractUsageAndCalculateCost,
|
|
4836
|
+
generateGoogleTtsAudio,
|
|
4837
|
+
generateOpenAiTtsAudio,
|
|
4532
4838
|
generateSchemaInstructions,
|
|
4533
4839
|
getModelConfigByApiName,
|
|
4534
4840
|
getModelDisplayName,
|
|
@@ -4545,6 +4851,7 @@ var AgentFactory = class {
|
|
|
4545
4851
|
modelIsFast,
|
|
4546
4852
|
needsPromptBasedSchema,
|
|
4547
4853
|
parseAndValidateLlmJson,
|
|
4854
|
+
pcmToWav,
|
|
4548
4855
|
safeValidateResponse,
|
|
4549
4856
|
setLlmLogger,
|
|
4550
4857
|
stableHashHex,
|
|
@@ -4556,6 +4863,8 @@ var AgentFactory = class {
|
|
|
4556
4863
|
toGeminiEffort,
|
|
4557
4864
|
toGlmEffort,
|
|
4558
4865
|
toOpenAIEffort,
|
|
4866
|
+
transcribeWithGemini,
|
|
4867
|
+
transcribeWithOpenAi,
|
|
4559
4868
|
validateResponse
|
|
4560
4869
|
});
|
|
4561
4870
|
//# sourceMappingURL=index.js.map
|