@ai-ecoverse/pi-audio 1.10.0 → 1.12.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 +6 -0
- package/dist/gemini-live/index.js +71 -0
- package/dist/gemini-live/index.js.map +7 -0
- package/dist/gemini-live/session.js +599 -0
- package/dist/gemini-live/session.js.map +7 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +2 -2
- package/dist/tokens.js +200 -0
- package/dist/tokens.js.map +7 -0
- package/dist/types/gemini-live/index.d.ts +7 -0
- package/dist/types/gemini-live/session.d.ts +12 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/tokens.d.ts +11 -0
- package/dist/types/xai/catalog.d.ts +1 -0
- package/dist/types/xai/index.d.ts +1 -0
- package/dist/types/xai/listen.d.ts +4 -0
- package/dist/types/xai/speak.d.ts +4 -0
- package/dist/types/xai/transcribe.d.ts +4 -0
- package/dist/types/xai/wire.d.ts +20 -0
- package/dist/xai/catalog.js +133 -0
- package/dist/xai/catalog.js.map +7 -0
- package/dist/xai/index.js +2 -0
- package/dist/xai/index.js.map +2 -2
- package/dist/xai/listen.js +211 -0
- package/dist/xai/listen.js.map +7 -0
- package/dist/xai/speak.js +208 -0
- package/dist/xai/speak.js.map +7 -0
- package/dist/xai/transcribe.js +113 -0
- package/dist/xai/transcribe.js.map +7 -0
- package/dist/xai/wire.js +171 -0
- package/dist/xai/wire.js.map +7 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "./registry.js";
|
|
25
25
|
import { SpeechEventStream } from "./speech.js";
|
|
26
26
|
import { askAgent, bindThinker, talkerTools } from "./talker.js";
|
|
27
|
+
import { mintRealtimeToken } from "./tokens.js";
|
|
27
28
|
import { TranscriptionEventStream } from "./transcription.js";
|
|
28
29
|
import { voiceTurns } from "./turns.js";
|
|
29
30
|
import { addAudioCounters, priceAudio } from "./usage.js";
|
|
@@ -46,6 +47,7 @@ export {
|
|
|
46
47
|
getVoiceModel,
|
|
47
48
|
listVoiceModels,
|
|
48
49
|
listen,
|
|
50
|
+
mintRealtimeToken,
|
|
49
51
|
planDelivery,
|
|
50
52
|
planTruncation,
|
|
51
53
|
priceAudio,
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export type {\n AudioChunk,\n AudioEncoding,\n AudioFormat,\n AudioSink,\n AudioSource,\n AudioTag,\n CharAlignment,\n TextSpan,\n} from './audio.ts';\nexport { resolveVoiceAuth } from './auth.ts';\nexport { blankUsage } from './blank-usage.ts';\nexport {\n convertAudio,\n convertAudioSource,\n decodeAudioBase64,\n encodeAudioBase64,\n} from './convert.ts';\nexport { VoiceRequestError } from './errors.ts';\nexport type {\n Delivery,\n DeliverySupport,\n KnownRealtimeApi,\n KnownSpeechApi,\n KnownTranscriptionApi,\n RealtimeApi,\n RealtimeModel,\n SpeechApi,\n SpeechModel,\n TranscriptionApi,\n TranscriptionModel,\n VoiceInfo,\n VoiceModel,\n VoiceModelType,\n} from './models.ts';\nexport {\n getVoiceModel,\n listVoiceModels,\n registerVoiceModel,\n} from './models.ts';\nexport type {\n DeliveryPlan,\n RealtimeApiProvider,\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n RealtimeSocket,\n RealtimeToken,\n TruncationPlan,\n} from './realtime.ts';\nexport { planDelivery, planTruncation, RealtimeLedger, realtimeSocket } from './realtime.ts';\nexport {\n connectRealtime,\n listen,\n registerRealtimeApiProvider,\n registerSpeechApiProvider,\n registerTranscriptionApiProvider,\n speak,\n transcribe,\n} from './registry.ts';\nexport type { SpeechApiProvider, SpeechEvent, SpeechInput } from './speech.ts';\nexport { SpeechEventStream } from './speech.ts';\nexport type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';\nexport type {\n BindThinkerOptions,\n BoundThinker,\n Thinker,\n ThinkerCaption,\n ThinkerPhase,\n ThinkerUpdate,\n} from './talker.ts';\nexport { askAgent, bindThinker, talkerTools } from './talker.ts';\nexport type {\n AudioInput,\n ListenOptions,\n Transcript,\n TranscriptionApiProvider,\n TranscriptionEvent,\n TranscriptSegment,\n} from './transcription.ts';\nexport { TranscriptionEventStream } from './transcription.ts';\nexport type {\n BargeIn,\n CaptionEvent,\n EchoRecommendation,\n VoiceConversation,\n VoiceCut,\n VoiceModels,\n VoicePhase,\n VoiceSession,\n VoiceTurnMeta,\n VoiceTurnOptions,\n} from './turns.ts';\nexport { voiceTurns } from './turns.ts';\nexport type {\n AudioCost,\n AudioCounters,\n AudioUsage,\n RealtimeAudioCost,\n SpeechAudioCost,\n TranscriptionAudioCost,\n} from './usage.ts';\nexport { addAudioCounters, priceAudio } from './usage.ts';\nexport type { VadHook, VadUpdate } from './vad.ts';\nexport { energyVad } from './vad.ts';\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAiBlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYP,SAAS,cAAc,gBAAgB,gBAAgB,sBAAsB;AAC7E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB;AAUlC,SAAS,UAAU,aAAa,mBAAmB;
|
|
4
|
+
"sourcesContent": ["export type {\n AudioChunk,\n AudioEncoding,\n AudioFormat,\n AudioSink,\n AudioSource,\n AudioTag,\n CharAlignment,\n TextSpan,\n} from './audio.ts';\nexport { resolveVoiceAuth } from './auth.ts';\nexport { blankUsage } from './blank-usage.ts';\nexport {\n convertAudio,\n convertAudioSource,\n decodeAudioBase64,\n encodeAudioBase64,\n} from './convert.ts';\nexport { VoiceRequestError } from './errors.ts';\nexport type {\n Delivery,\n DeliverySupport,\n KnownRealtimeApi,\n KnownSpeechApi,\n KnownTranscriptionApi,\n RealtimeApi,\n RealtimeModel,\n SpeechApi,\n SpeechModel,\n TranscriptionApi,\n TranscriptionModel,\n VoiceInfo,\n VoiceModel,\n VoiceModelType,\n} from './models.ts';\nexport {\n getVoiceModel,\n listVoiceModels,\n registerVoiceModel,\n} from './models.ts';\nexport type {\n DeliveryPlan,\n RealtimeApiProvider,\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n RealtimeSocket,\n RealtimeToken,\n TruncationPlan,\n} from './realtime.ts';\nexport { planDelivery, planTruncation, RealtimeLedger, realtimeSocket } from './realtime.ts';\nexport {\n connectRealtime,\n listen,\n registerRealtimeApiProvider,\n registerSpeechApiProvider,\n registerTranscriptionApiProvider,\n speak,\n transcribe,\n} from './registry.ts';\nexport type { SpeechApiProvider, SpeechEvent, SpeechInput } from './speech.ts';\nexport { SpeechEventStream } from './speech.ts';\nexport type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';\nexport type {\n BindThinkerOptions,\n BoundThinker,\n Thinker,\n ThinkerCaption,\n ThinkerPhase,\n ThinkerUpdate,\n} from './talker.ts';\nexport { askAgent, bindThinker, talkerTools } from './talker.ts';\nexport type { RealtimeConstraints } from './tokens.ts';\nexport { mintRealtimeToken } from './tokens.ts';\nexport type {\n AudioInput,\n ListenOptions,\n Transcript,\n TranscriptionApiProvider,\n TranscriptionEvent,\n TranscriptSegment,\n} from './transcription.ts';\nexport { TranscriptionEventStream } from './transcription.ts';\nexport type {\n BargeIn,\n CaptionEvent,\n EchoRecommendation,\n VoiceConversation,\n VoiceCut,\n VoiceModels,\n VoicePhase,\n VoiceSession,\n VoiceTurnMeta,\n VoiceTurnOptions,\n} from './turns.ts';\nexport { voiceTurns } from './turns.ts';\nexport type {\n AudioCost,\n AudioCounters,\n AudioUsage,\n RealtimeAudioCost,\n SpeechAudioCost,\n TranscriptionAudioCost,\n} from './usage.ts';\nexport { addAudioCounters, priceAudio } from './usage.ts';\nexport type { VadHook, VadUpdate } from './vad.ts';\nexport { energyVad } from './vad.ts';\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAiBlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYP,SAAS,cAAc,gBAAgB,gBAAgB,sBAAsB;AAC7E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB;AAUlC,SAAS,UAAU,aAAa,mBAAmB;AAEnD,SAAS,yBAAyB;AASlC,SAAS,gCAAgC;AAazC,SAAS,kBAAkB;AAS3B,SAAS,kBAAkB,kBAAkB;AAE7C,SAAS,iBAAiB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { VoiceRequestError } from "./errors.js";
|
|
2
|
+
const openaiSecrets = "https://api.openai.com/v1/realtime/client_secrets";
|
|
3
|
+
const xaiSecrets = "https://api.x.ai/v1/realtime/client_secrets";
|
|
4
|
+
const geminiTokens = "https://generativelanguage.googleapis.com/v1beta/auth_tokens";
|
|
5
|
+
const elevenSigned = "https://api.elevenlabs.io/v1/convai/conversation/get-signed-url";
|
|
6
|
+
const elevenWebRtc = "https://api.elevenlabs.io/v1/convai/conversation/token";
|
|
7
|
+
const fishSessions = "https://api.fish.audio/v1/agent/sessions";
|
|
8
|
+
const fifteenMinutes = 15 * 60 * 1e3;
|
|
9
|
+
const thirtyMinutes = 30 * 60 * 1e3;
|
|
10
|
+
const oneMinute = 60 * 1e3;
|
|
11
|
+
const xaiLifetimeSeconds = 300;
|
|
12
|
+
function apiKey(options) {
|
|
13
|
+
const key = options.apiKey;
|
|
14
|
+
if (typeof key === "string" && key.length > 0) return key;
|
|
15
|
+
throw new VoiceRequestError("error", "missing realtime credential");
|
|
16
|
+
}
|
|
17
|
+
function mergedHeaders(options, defaults) {
|
|
18
|
+
const headers = { ...defaults };
|
|
19
|
+
const extra = options.headers;
|
|
20
|
+
if (extra === void 0) return headers;
|
|
21
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
22
|
+
if (value === null) delete headers[key];
|
|
23
|
+
else headers[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
function omitUndefined(source) {
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const [key, value] of Object.entries(source)) {
|
|
30
|
+
if (value !== void 0) out[key] = value;
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function functionTool(tool) {
|
|
35
|
+
return {
|
|
36
|
+
type: "function",
|
|
37
|
+
name: tool.name,
|
|
38
|
+
description: tool.description,
|
|
39
|
+
parameters: tool.parameters
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function declaration(tool) {
|
|
43
|
+
return {
|
|
44
|
+
name: tool.name,
|
|
45
|
+
description: tool.description,
|
|
46
|
+
parameters: tool.parameters
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function requiredAgent(constraints) {
|
|
50
|
+
const id = constraints.agentId;
|
|
51
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
52
|
+
throw new VoiceRequestError("error", "missing agent id");
|
|
53
|
+
}
|
|
54
|
+
function expiresAt(value, fallback) {
|
|
55
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
56
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === "string") {
|
|
59
|
+
const parsed = Date.parse(value);
|
|
60
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
61
|
+
}
|
|
62
|
+
return fallback;
|
|
63
|
+
}
|
|
64
|
+
function fromField(field) {
|
|
65
|
+
return (json, fallback) => expiresAt(json[field], fallback);
|
|
66
|
+
}
|
|
67
|
+
function openaiBody(model, constraints) {
|
|
68
|
+
const session = omitUndefined({
|
|
69
|
+
type: "realtime",
|
|
70
|
+
model: model.id,
|
|
71
|
+
instructions: constraints.instructions,
|
|
72
|
+
tools: constraints.tools?.map(functionTool)
|
|
73
|
+
});
|
|
74
|
+
if (constraints.voice !== void 0) session.audio = { output: { voice: constraints.voice } };
|
|
75
|
+
return { session };
|
|
76
|
+
}
|
|
77
|
+
function geminiBody(model, constraints, now) {
|
|
78
|
+
const instructions = constraints.instructions;
|
|
79
|
+
const voice = constraints.voice;
|
|
80
|
+
const tools = constraints.tools;
|
|
81
|
+
const config = omitUndefined({
|
|
82
|
+
responseModalities: ["AUDIO"],
|
|
83
|
+
sessionResumption: {},
|
|
84
|
+
systemInstruction: instructions === void 0 ? void 0 : { parts: [{ text: instructions }] },
|
|
85
|
+
speechConfig: voice === void 0 ? void 0 : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } },
|
|
86
|
+
tools: tools === void 0 ? void 0 : [{ functionDeclarations: tools.map(declaration) }]
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
uses: constraints.uses ?? 1,
|
|
90
|
+
expireTime: new Date(now + thirtyMinutes).toISOString(),
|
|
91
|
+
newSessionExpireTime: new Date(now + oneMinute).toISOString(),
|
|
92
|
+
liveConnectConstraints: { model: `models/${model.id}`, config }
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function fishBody(constraints) {
|
|
96
|
+
const overrides = omitUndefined({
|
|
97
|
+
system_prompt: constraints.instructions,
|
|
98
|
+
voice_id: constraints.voice
|
|
99
|
+
});
|
|
100
|
+
return omitUndefined({
|
|
101
|
+
agent_id: requiredAgent(constraints),
|
|
102
|
+
overrides: Object.keys(overrides).length > 0 ? overrides : void 0
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function webrtcOnly(model) {
|
|
106
|
+
return model.transports.length === 1 && model.transports[0] === "webrtc";
|
|
107
|
+
}
|
|
108
|
+
function elevenUrl(model, constraints) {
|
|
109
|
+
const url = new URL(webrtcOnly(model) ? elevenWebRtc : elevenSigned);
|
|
110
|
+
url.searchParams.set("agent_id", requiredAgent(constraints));
|
|
111
|
+
return url.toString();
|
|
112
|
+
}
|
|
113
|
+
function mintCall(model, constraints, options, key, now) {
|
|
114
|
+
const jsonHeaders = { "Content-Type": "application/json" };
|
|
115
|
+
switch (model.api) {
|
|
116
|
+
case "openai-realtime":
|
|
117
|
+
return {
|
|
118
|
+
url: openaiSecrets,
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
121
|
+
body: JSON.stringify(openaiBody(model, constraints)),
|
|
122
|
+
field: "value",
|
|
123
|
+
expires: fromField("expires_at"),
|
|
124
|
+
fallbackExpires: now
|
|
125
|
+
};
|
|
126
|
+
case "xai-realtime":
|
|
127
|
+
return {
|
|
128
|
+
url: xaiSecrets,
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
131
|
+
body: JSON.stringify({ expires_after: { seconds: xaiLifetimeSeconds } }),
|
|
132
|
+
field: "value",
|
|
133
|
+
expires: fromField("expires_at"),
|
|
134
|
+
fallbackExpires: now + xaiLifetimeSeconds * 1e3
|
|
135
|
+
};
|
|
136
|
+
case "gemini-live":
|
|
137
|
+
return {
|
|
138
|
+
url: geminiTokens,
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: mergedHeaders(options, { ...jsonHeaders, "x-goog-api-key": key }),
|
|
141
|
+
body: JSON.stringify(geminiBody(model, constraints, now)),
|
|
142
|
+
field: "name",
|
|
143
|
+
expires: fromField("expireTime"),
|
|
144
|
+
fallbackExpires: now + thirtyMinutes
|
|
145
|
+
};
|
|
146
|
+
case "elevenlabs-agents":
|
|
147
|
+
return {
|
|
148
|
+
url: elevenUrl(model, constraints),
|
|
149
|
+
method: "GET",
|
|
150
|
+
headers: mergedHeaders(options, { "xi-api-key": key }),
|
|
151
|
+
field: webrtcOnly(model) ? "token" : "signed_url",
|
|
152
|
+
expires: fromField("expires_at"),
|
|
153
|
+
fallbackExpires: now + fifteenMinutes
|
|
154
|
+
};
|
|
155
|
+
case "fish-agents":
|
|
156
|
+
return {
|
|
157
|
+
url: fishSessions,
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),
|
|
160
|
+
body: JSON.stringify(fishBody(constraints)),
|
|
161
|
+
field: "token",
|
|
162
|
+
expires: fromField("expires_at"),
|
|
163
|
+
fallbackExpires: now
|
|
164
|
+
};
|
|
165
|
+
default:
|
|
166
|
+
throw new VoiceRequestError("error", "unsupported realtime api");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function readValue(json, field) {
|
|
170
|
+
if (typeof json !== "object" || json === null) {
|
|
171
|
+
throw new VoiceRequestError("error", "mint failed (empty token)");
|
|
172
|
+
}
|
|
173
|
+
const value = json[field];
|
|
174
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
175
|
+
throw new VoiceRequestError("error", "mint failed (empty token)");
|
|
176
|
+
}
|
|
177
|
+
async function mintRealtimeToken(model, constraints, options) {
|
|
178
|
+
const settings = options ?? {};
|
|
179
|
+
const key = apiKey(settings);
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
const call = mintCall(model, constraints, settings, key, now);
|
|
182
|
+
const fetch = settings.fetch ?? globalThis.fetch;
|
|
183
|
+
const response = await fetch(call.url, {
|
|
184
|
+
method: call.method,
|
|
185
|
+
headers: call.headers,
|
|
186
|
+
body: call.body,
|
|
187
|
+
signal: settings.signal
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok) throw new VoiceRequestError("error", `mint failed (${response.status})`);
|
|
190
|
+
const json = await response.json();
|
|
191
|
+
return {
|
|
192
|
+
value: readValue(json, call.field),
|
|
193
|
+
expiresAt: call.expires(json, call.fallbackExpires),
|
|
194
|
+
model: model.id
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
mintRealtimeToken
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/tokens.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { RealtimeModel } from './models.ts';\nimport type { RealtimeToken } from './realtime.ts';\n\nexport interface RealtimeConstraints {\n instructions?: string;\n tools?: Tool[];\n voice?: string;\n uses?: number;\n agentId?: string;\n}\n\nconst openaiSecrets = 'https://api.openai.com/v1/realtime/client_secrets';\nconst xaiSecrets = 'https://api.x.ai/v1/realtime/client_secrets';\nconst geminiTokens = 'https://generativelanguage.googleapis.com/v1beta/auth_tokens';\nconst elevenSigned = 'https://api.elevenlabs.io/v1/convai/conversation/get-signed-url';\nconst elevenWebRtc = 'https://api.elevenlabs.io/v1/convai/conversation/token';\nconst fishSessions = 'https://api.fish.audio/v1/agent/sessions';\nconst fifteenMinutes = 15 * 60 * 1000;\nconst thirtyMinutes = 30 * 60 * 1000;\nconst oneMinute = 60 * 1000;\nconst xaiLifetimeSeconds = 300;\n\ntype Json = Record<string, unknown>;\n\ninterface MintCall {\n url: string;\n method: 'GET' | 'POST';\n headers: Record<string, string>;\n body?: string;\n field: string;\n expires: (json: Json, fallback: number) => number;\n fallbackExpires: number;\n}\n\nfunction apiKey(options: ProviderRequestOptions): string {\n const key = options.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction mergedHeaders(\n options: ProviderRequestOptions,\n defaults: Record<string, string>\n): Record<string, string> {\n const headers = { ...defaults };\n const extra = options.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction omitUndefined(source: Json): Json {\n const out: Json = {};\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n\nfunction functionTool(tool: Tool): Json {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown,\n };\n}\n\nfunction declaration(tool: Tool): Json {\n return {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown,\n };\n}\n\nfunction requiredAgent(constraints: RealtimeConstraints): string {\n const id = constraints.agentId;\n if (typeof id === 'string' && id.length > 0) return id;\n throw new VoiceRequestError('error', 'missing agent id');\n}\n\nfunction expiresAt(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value < 1e12 ? value * 1000 : value;\n }\n if (typeof value === 'string') {\n const parsed = Date.parse(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction fromField(field: string): (json: Json, fallback: number) => number {\n return (json, fallback) => expiresAt(json[field], fallback);\n}\n\nfunction openaiBody(model: RealtimeModel<string>, constraints: RealtimeConstraints): Json {\n const session = omitUndefined({\n type: 'realtime',\n model: model.id,\n instructions: constraints.instructions,\n tools: constraints.tools?.map(functionTool),\n });\n if (constraints.voice !== undefined) session.audio = { output: { voice: constraints.voice } };\n return { session };\n}\n\nfunction geminiBody(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n now: number\n): Json {\n const instructions = constraints.instructions;\n const voice = constraints.voice;\n const tools = constraints.tools;\n const config = omitUndefined({\n responseModalities: ['AUDIO'],\n sessionResumption: {},\n systemInstruction: instructions === undefined ? undefined : { parts: [{ text: instructions }] },\n speechConfig:\n voice === undefined\n ? undefined\n : { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } },\n tools: tools === undefined ? undefined : [{ functionDeclarations: tools.map(declaration) }],\n });\n return {\n uses: constraints.uses ?? 1,\n expireTime: new Date(now + thirtyMinutes).toISOString(),\n newSessionExpireTime: new Date(now + oneMinute).toISOString(),\n liveConnectConstraints: { model: `models/${model.id}`, config },\n };\n}\n\nfunction fishBody(constraints: RealtimeConstraints): Json {\n const overrides = omitUndefined({\n system_prompt: constraints.instructions,\n voice_id: constraints.voice,\n });\n return omitUndefined({\n agent_id: requiredAgent(constraints),\n overrides: Object.keys(overrides).length > 0 ? overrides : undefined,\n });\n}\n\nfunction webrtcOnly(model: RealtimeModel<string>): boolean {\n return model.transports.length === 1 && model.transports[0] === 'webrtc';\n}\n\nfunction elevenUrl(model: RealtimeModel<string>, constraints: RealtimeConstraints): string {\n const url = new URL(webrtcOnly(model) ? elevenWebRtc : elevenSigned);\n url.searchParams.set('agent_id', requiredAgent(constraints));\n return url.toString();\n}\n\nfunction mintCall(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n options: ProviderRequestOptions,\n key: string,\n now: number\n): MintCall {\n const jsonHeaders = { 'Content-Type': 'application/json' };\n switch (model.api) {\n case 'openai-realtime':\n return {\n url: openaiSecrets,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify(openaiBody(model, constraints)),\n field: 'value',\n expires: fromField('expires_at'),\n fallbackExpires: now,\n };\n case 'xai-realtime':\n return {\n url: xaiSecrets,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify({ expires_after: { seconds: xaiLifetimeSeconds } }),\n field: 'value',\n expires: fromField('expires_at'),\n fallbackExpires: now + xaiLifetimeSeconds * 1000,\n };\n case 'gemini-live':\n return {\n url: geminiTokens,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, 'x-goog-api-key': key }),\n body: JSON.stringify(geminiBody(model, constraints, now)),\n field: 'name',\n expires: fromField('expireTime'),\n fallbackExpires: now + thirtyMinutes,\n };\n case 'elevenlabs-agents':\n return {\n url: elevenUrl(model, constraints),\n method: 'GET',\n headers: mergedHeaders(options, { 'xi-api-key': key }),\n field: webrtcOnly(model) ? 'token' : 'signed_url',\n expires: fromField('expires_at'),\n fallbackExpires: now + fifteenMinutes,\n };\n case 'fish-agents':\n return {\n url: fishSessions,\n method: 'POST',\n headers: mergedHeaders(options, { ...jsonHeaders, Authorization: `Bearer ${key}` }),\n body: JSON.stringify(fishBody(constraints)),\n field: 'token',\n expires: fromField('expires_at'),\n fallbackExpires: now,\n };\n default:\n throw new VoiceRequestError('error', 'unsupported realtime api');\n }\n}\n\nfunction readValue(json: unknown, field: string): string {\n if (typeof json !== 'object' || json === null) {\n throw new VoiceRequestError('error', 'mint failed (empty token)');\n }\n const value = (json as Json)[field];\n if (typeof value === 'string' && value.length > 0) return value;\n throw new VoiceRequestError('error', 'mint failed (empty token)');\n}\n\nexport async function mintRealtimeToken(\n model: RealtimeModel<string>,\n constraints: RealtimeConstraints,\n options?: ProviderRequestOptions\n): Promise<RealtimeToken> {\n const settings = options ?? {};\n const key = apiKey(settings);\n const now = Date.now();\n const call = mintCall(model, constraints, settings, key, now);\n const fetch = settings.fetch ?? globalThis.fetch;\n const response = await fetch(call.url, {\n method: call.method,\n headers: call.headers,\n body: call.body,\n signal: settings.signal,\n });\n if (!response.ok) throw new VoiceRequestError('error', `mint failed (${response.status})`);\n const json: unknown = await response.json();\n return {\n value: readValue(json, call.field),\n expiresAt: call.expires(json as Json, call.fallbackExpires),\n model: model.id,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RealtimeModel } from '../models.ts';
|
|
2
|
+
import { type GeminiOptions } from './session.ts';
|
|
3
|
+
export declare const gemini38Live: RealtimeModel<"gemini-live">;
|
|
4
|
+
export declare const gemini25NativeAudio: RealtimeModel<"gemini-live">;
|
|
5
|
+
export declare const gemini31FlashLive: RealtimeModel<"gemini-live">;
|
|
6
|
+
export declare function registerGeminiLive(options?: GeminiOptions): () => void;
|
|
7
|
+
export type { GeminiOptions, VoiceWireFactory } from './session.ts';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AudioFormat } from '../audio.ts';
|
|
2
|
+
import type { RealtimeModel } from '../models.ts';
|
|
3
|
+
import { type VoiceSocketConstructor, type VoiceWire, type VoiceWireTarget } from '../openai-realtime/socket.ts';
|
|
4
|
+
import type { RealtimeContext, RealtimeOptions, RealtimeSession } from '../realtime.ts';
|
|
5
|
+
export type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;
|
|
6
|
+
export interface GeminiOptions {
|
|
7
|
+
wire?: VoiceWireFactory;
|
|
8
|
+
socket?: VoiceSocketConstructor;
|
|
9
|
+
vocabulary?: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function connectGeminiSession(model: RealtimeModel<string>, context: RealtimeContext, options: RealtimeOptions | undefined, gemini: GeminiOptions): Promise<RealtimeSession>;
|
|
12
|
+
export declare function pcmFormat(sampleRate: number): AudioFormat;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { SpeechEventStream } from './speech.ts';
|
|
|
13
13
|
export type { VoiceAgentEvent, VoiceMessageChange } from './spoken.ts';
|
|
14
14
|
export type { BindThinkerOptions, BoundThinker, Thinker, ThinkerCaption, ThinkerPhase, ThinkerUpdate, } from './talker.ts';
|
|
15
15
|
export { askAgent, bindThinker, talkerTools } from './talker.ts';
|
|
16
|
+
export type { RealtimeConstraints } from './tokens.ts';
|
|
17
|
+
export { mintRealtimeToken } from './tokens.ts';
|
|
16
18
|
export type { AudioInput, ListenOptions, Transcript, TranscriptionApiProvider, TranscriptionEvent, TranscriptSegment, } from './transcription.ts';
|
|
17
19
|
export { TranscriptionEventStream } from './transcription.ts';
|
|
18
20
|
export type { BargeIn, CaptionEvent, EchoRecommendation, VoiceConversation, VoiceCut, VoiceModels, VoicePhase, VoiceSession, VoiceTurnMeta, VoiceTurnOptions, } from './turns.ts';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProviderRequestOptions, Tool } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { RealtimeModel } from './models.ts';
|
|
3
|
+
import type { RealtimeToken } from './realtime.ts';
|
|
4
|
+
export interface RealtimeConstraints {
|
|
5
|
+
instructions?: string;
|
|
6
|
+
tools?: Tool[];
|
|
7
|
+
voice?: string;
|
|
8
|
+
uses?: number;
|
|
9
|
+
agentId?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function mintRealtimeToken(model: RealtimeModel<string>, constraints: RealtimeConstraints, options?: ProviderRequestOptions): Promise<RealtimeToken>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerXAI(): () => void;
|
|
@@ -2,3 +2,4 @@ import type { RealtimeModel } from '../models.ts';
|
|
|
2
2
|
import { type AdapterOptions } from '../openai-realtime/session.ts';
|
|
3
3
|
export declare const grokVoiceLatest: RealtimeModel<'xai-realtime'>;
|
|
4
4
|
export declare function registerXaiRealtime(options?: AdapterOptions): () => void;
|
|
5
|
+
export { registerXAI } from './catalog.ts';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AudioChunk } from '../audio.ts';
|
|
2
|
+
import type { TranscriptionModel } from '../models.ts';
|
|
3
|
+
import { type ListenOptions, TranscriptionEventStream } from '../transcription.ts';
|
|
4
|
+
export declare function listenXAI(model: TranscriptionModel<string>, audio: AsyncIterable<AudioChunk>, options?: ListenOptions): TranscriptionEventStream;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderRequestOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { SpeechModel } from '../models.ts';
|
|
3
|
+
import { SpeechEventStream, type SpeechInput } from '../speech.ts';
|
|
4
|
+
export declare function speakXAI(model: SpeechModel<string>, input: SpeechInput, options?: ProviderRequestOptions): SpeechEventStream;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderRequestOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { TranscriptionModel } from '../models.ts';
|
|
3
|
+
import type { AudioInput, Transcript } from '../transcription.ts';
|
|
4
|
+
export declare function transcribeXAI(model: TranscriptionModel<string>, input: AudioInput, options?: ProviderRequestOptions): Promise<Transcript>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AudioFormat } from '../audio.ts';
|
|
2
|
+
import type { TranscriptionModel } from '../models.ts';
|
|
3
|
+
export interface XaiFile {
|
|
4
|
+
data: Uint8Array;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
filename: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function mediaType(mimeType: string): string;
|
|
9
|
+
export declare function audioFilename(mimeType: string): string;
|
|
10
|
+
export declare function rawFields(mimeType: string, model: TranscriptionModel<string>): Record<string, string> | undefined;
|
|
11
|
+
export declare function encodeForm(file: XaiFile, fields: Record<string, string>): ArrayBuffer;
|
|
12
|
+
export declare function formType(): string;
|
|
13
|
+
export declare function socketUrl(baseUrl: string, path: string): URL;
|
|
14
|
+
export declare function codecName(encoding: AudioFormat['encoding']): string;
|
|
15
|
+
export declare function encodingName(encoding: AudioFormat['encoding']): string;
|
|
16
|
+
export declare function connectSocket(url: string, headers: Record<string, string>): WebSocket;
|
|
17
|
+
export declare function awaitOpen(socket: WebSocket, label: string, fail: (message: string) => void): Promise<void>;
|
|
18
|
+
export declare function messageText(data: unknown): Promise<string>;
|
|
19
|
+
export declare function sameFormat(left: AudioFormat, right: AudioFormat): boolean;
|
|
20
|
+
export declare function copiedBytes(data: Uint8Array): ArrayBuffer;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerVoiceModel
|
|
3
|
+
} from "../models.js";
|
|
4
|
+
import { registerSpeechApiProvider, registerTranscriptionApiProvider } from "../registry.js";
|
|
5
|
+
import { listenXAI } from "./listen.js";
|
|
6
|
+
import { speakXAI } from "./speak.js";
|
|
7
|
+
import { transcribeXAI } from "./transcribe.js";
|
|
8
|
+
const tokenCost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
9
|
+
const xai = "https://api.x.ai/v1";
|
|
10
|
+
const languages = [
|
|
11
|
+
"ar",
|
|
12
|
+
"bs",
|
|
13
|
+
"bg",
|
|
14
|
+
"yue",
|
|
15
|
+
"ca",
|
|
16
|
+
"zh",
|
|
17
|
+
"hr",
|
|
18
|
+
"cs",
|
|
19
|
+
"da",
|
|
20
|
+
"nl",
|
|
21
|
+
"en",
|
|
22
|
+
"fil",
|
|
23
|
+
"fi",
|
|
24
|
+
"fr",
|
|
25
|
+
"de",
|
|
26
|
+
"el",
|
|
27
|
+
"hi",
|
|
28
|
+
"hu",
|
|
29
|
+
"id",
|
|
30
|
+
"it",
|
|
31
|
+
"ja",
|
|
32
|
+
"ko",
|
|
33
|
+
"mk",
|
|
34
|
+
"ms",
|
|
35
|
+
"nb",
|
|
36
|
+
"fa",
|
|
37
|
+
"pl",
|
|
38
|
+
"pt",
|
|
39
|
+
"ro",
|
|
40
|
+
"ru",
|
|
41
|
+
"sk",
|
|
42
|
+
"es",
|
|
43
|
+
"sv",
|
|
44
|
+
"th",
|
|
45
|
+
"tr",
|
|
46
|
+
"uk",
|
|
47
|
+
"ur",
|
|
48
|
+
"vi"
|
|
49
|
+
];
|
|
50
|
+
const pcm16 = (sampleRate) => ({
|
|
51
|
+
encoding: "pcm16",
|
|
52
|
+
sampleRate,
|
|
53
|
+
channels: 1
|
|
54
|
+
});
|
|
55
|
+
const law = (encoding, sampleRate = 8e3) => ({
|
|
56
|
+
encoding,
|
|
57
|
+
sampleRate,
|
|
58
|
+
channels: 1
|
|
59
|
+
});
|
|
60
|
+
const container = (encoding, sampleRate) => ({
|
|
61
|
+
encoding,
|
|
62
|
+
sampleRate,
|
|
63
|
+
channels: 1
|
|
64
|
+
});
|
|
65
|
+
const voices = ["eve", "ara", "rex", "sal", "leo"].map((id) => ({ id, name: id }));
|
|
66
|
+
function xaiCatalog() {
|
|
67
|
+
const transcription = {
|
|
68
|
+
id: "grok-voice-transcribe-2.0",
|
|
69
|
+
name: "grok-voice-transcribe-2.0",
|
|
70
|
+
api: "xai-stt",
|
|
71
|
+
provider: "xai",
|
|
72
|
+
baseUrl: xai,
|
|
73
|
+
type: "transcription",
|
|
74
|
+
streaming: true,
|
|
75
|
+
turnDetection: "semantic",
|
|
76
|
+
inputFormats: [
|
|
77
|
+
pcm16(16e3),
|
|
78
|
+
pcm16(24e3),
|
|
79
|
+
law("mulaw"),
|
|
80
|
+
law("alaw"),
|
|
81
|
+
container("wav", 16e3),
|
|
82
|
+
container("mp3", 16e3),
|
|
83
|
+
container("flac", 16e3)
|
|
84
|
+
],
|
|
85
|
+
languages,
|
|
86
|
+
diarization: true,
|
|
87
|
+
cost: tokenCost,
|
|
88
|
+
audioCost: { perInputMinute: 0.2 / 60 }
|
|
89
|
+
};
|
|
90
|
+
const speech = {
|
|
91
|
+
id: "grok-voice-tts-1.0",
|
|
92
|
+
name: "grok-voice-tts-1.0",
|
|
93
|
+
api: "xai-tts",
|
|
94
|
+
provider: "xai",
|
|
95
|
+
baseUrl: xai,
|
|
96
|
+
type: "speech",
|
|
97
|
+
textStreaming: true,
|
|
98
|
+
outputFormats: [
|
|
99
|
+
container("mp3", 24e3),
|
|
100
|
+
pcm16(24e3),
|
|
101
|
+
container("wav", 24e3),
|
|
102
|
+
law("mulaw"),
|
|
103
|
+
law("alaw")
|
|
104
|
+
],
|
|
105
|
+
voices,
|
|
106
|
+
cloning: false,
|
|
107
|
+
cost: tokenCost,
|
|
108
|
+
audioCost: { perThousandChars: 0.015 }
|
|
109
|
+
};
|
|
110
|
+
return [transcription, speech];
|
|
111
|
+
}
|
|
112
|
+
function registerXAI() {
|
|
113
|
+
const models = xaiCatalog();
|
|
114
|
+
const unregisterModels = models.map((model) => registerVoiceModel(model));
|
|
115
|
+
const unregisterStt = registerTranscriptionApiProvider({
|
|
116
|
+
api: "xai-stt",
|
|
117
|
+
transcribe: transcribeXAI,
|
|
118
|
+
listen: listenXAI
|
|
119
|
+
});
|
|
120
|
+
const unregisterTts = registerSpeechApiProvider({ api: "xai-tts", speak: speakXAI });
|
|
121
|
+
let released = false;
|
|
122
|
+
return () => {
|
|
123
|
+
if (released) return;
|
|
124
|
+
released = true;
|
|
125
|
+
for (const unregister of unregisterModels) unregister();
|
|
126
|
+
unregisterStt();
|
|
127
|
+
unregisterTts();
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
registerXAI
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/xai/catalog.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AudioFormat } from '../audio.ts';\nimport {\n registerVoiceModel,\n type SpeechModel,\n type TranscriptionModel,\n type VoiceInfo,\n} from '../models.ts';\nimport { registerSpeechApiProvider, registerTranscriptionApiProvider } from '../registry.ts';\nimport { listenXAI } from './listen.ts';\nimport { speakXAI } from './speak.ts';\nimport { transcribeXAI } from './transcribe.ts';\n\nconst tokenCost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\nconst xai = 'https://api.x.ai/v1';\n\nconst languages = [\n 'ar',\n 'bs',\n 'bg',\n 'yue',\n 'ca',\n 'zh',\n 'hr',\n 'cs',\n 'da',\n 'nl',\n 'en',\n 'fil',\n 'fi',\n 'fr',\n 'de',\n 'el',\n 'hi',\n 'hu',\n 'id',\n 'it',\n 'ja',\n 'ko',\n 'mk',\n 'ms',\n 'nb',\n 'fa',\n 'pl',\n 'pt',\n 'ro',\n 'ru',\n 'sk',\n 'es',\n 'sv',\n 'th',\n 'tr',\n 'uk',\n 'ur',\n 'vi',\n];\n\nconst pcm16 = (sampleRate: number): AudioFormat => ({\n encoding: 'pcm16',\n sampleRate,\n channels: 1,\n});\n\nconst law = (encoding: 'mulaw' | 'alaw', sampleRate = 8000): AudioFormat => ({\n encoding,\n sampleRate,\n channels: 1,\n});\n\nconst container = (encoding: 'wav' | 'mp3' | 'flac', sampleRate: number): AudioFormat => ({\n encoding,\n sampleRate,\n channels: 1,\n});\n\nconst voices: VoiceInfo[] = ['eve', 'ara', 'rex', 'sal', 'leo'].map((id) => ({ id, name: id }));\n\nfunction xaiCatalog(): Array<TranscriptionModel<string> | SpeechModel<string>> {\n const transcription: TranscriptionModel<string> = {\n id: 'grok-voice-transcribe-2.0',\n name: 'grok-voice-transcribe-2.0',\n api: 'xai-stt',\n provider: 'xai',\n baseUrl: xai,\n type: 'transcription',\n streaming: true,\n turnDetection: 'semantic',\n inputFormats: [\n pcm16(16000),\n pcm16(24000),\n law('mulaw'),\n law('alaw'),\n container('wav', 16000),\n container('mp3', 16000),\n container('flac', 16000),\n ],\n languages,\n diarization: true,\n cost: tokenCost,\n audioCost: { perInputMinute: 0.2 / 60 },\n };\n const speech: SpeechModel<string> = {\n id: 'grok-voice-tts-1.0',\n name: 'grok-voice-tts-1.0',\n api: 'xai-tts',\n provider: 'xai',\n baseUrl: xai,\n type: 'speech',\n textStreaming: true,\n outputFormats: [\n container('mp3', 24000),\n pcm16(24000),\n container('wav', 24000),\n law('mulaw'),\n law('alaw'),\n ],\n voices,\n cloning: false,\n cost: tokenCost,\n audioCost: { perThousandChars: 0.015 },\n };\n return [transcription, speech];\n}\n\nexport function registerXAI(): () => void {\n const models = xaiCatalog();\n const unregisterModels = models.map((model) => registerVoiceModel(model));\n const unregisterStt = registerTranscriptionApiProvider({\n api: 'xai-stt',\n transcribe: transcribeXAI,\n listen: listenXAI,\n });\n const unregisterTts = registerSpeechApiProvider({ api: 'xai-tts', speak: speakXAI });\n let released = false;\n return () => {\n if (released) return;\n released = true;\n for (const unregister of unregisterModels) unregister();\n unregisterStt();\n unregisterTts();\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/xai/index.js
CHANGED
package/dist/xai/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/xai/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport {\n type AdapterOptions,\n connectVendorSession,\n pcmFormat,\n} from '../openai-realtime/session.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\n\nconst voices = [\n { id: 'eve', name: 'Eve' },\n { id: 'ara', name: 'Ara' },\n { id: 'rex', name: 'Rex' },\n { id: 'sal', name: 'Sal' },\n { id: 'leo', name: 'Leo' },\n];\n\nexport const grokVoiceLatest: RealtimeModel<'xai-realtime'> = {\n id: 'grok-voice-latest',\n name: 'Grok Voice',\n api: 'xai-realtime',\n provider: 'xai',\n baseUrl: 'wss://api.x.ai/v1/realtime',\n type: 'realtime',\n transports: ['websocket', 'webrtc'],\n toolCalls: 'blocking',\n inputFormat: pcmFormat(24000),\n outputFormat: pcmFormat(24000),\n maxSessionMs: 7_200_000,\n resumable: true,\n ephemeralAuth: true,\n voices,\n provisioning: 'none',\n delivery: { interrupt: 'native', when_idle: 'approximate', silent: 'native' },\n truncation: 'client',\n liveToolUpdate: true,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0.08, perOutputMinute: 0.08, perTextMessage: 0.004 },\n};\n\nexport function registerXaiRealtime(options: AdapterOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(grokVoiceLatest);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'xai-realtime',\n connect: (model, context, connectOptions) =>\n connectVendorSession(model, context, connectOptions, { ...options, dialect: 'xai' }),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AACnC;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAE5C,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAC3B;AAEO,MAAM,kBAAiD;AAAA,EAC5D,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,aAAa,QAAQ;AAAA,EAClC,WAAW;AAAA,EACX,aAAa,UAAU,IAAK;AAAA,EAC5B,cAAc,UAAU,IAAK;AAAA,EAC7B,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,UAAU,WAAW,eAAe,QAAQ,SAAS;AAAA,EAC5E,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,MAAM,iBAAiB,MAAM,gBAAgB,KAAM;AAClF;AAEO,SAAS,oBAAoB,UAA0B,CAAC,GAAe;AAC5E,QAAM,kBAAkB,mBAAmB,eAAe;AAC1D,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,qBAAqB,OAAO,SAAS,gBAAgB,EAAE,GAAG,SAAS,SAAS,MAAM,CAAC;AAAA,EACvF,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;",
|
|
4
|
+
"sourcesContent": ["import type { RealtimeModel } from '../models.ts';\nimport { registerVoiceModel } from '../models.ts';\nimport {\n type AdapterOptions,\n connectVendorSession,\n pcmFormat,\n} from '../openai-realtime/session.ts';\nimport { registerRealtimeApiProvider } from '../registry.ts';\n\nconst voices = [\n { id: 'eve', name: 'Eve' },\n { id: 'ara', name: 'Ara' },\n { id: 'rex', name: 'Rex' },\n { id: 'sal', name: 'Sal' },\n { id: 'leo', name: 'Leo' },\n];\n\nexport const grokVoiceLatest: RealtimeModel<'xai-realtime'> = {\n id: 'grok-voice-latest',\n name: 'Grok Voice',\n api: 'xai-realtime',\n provider: 'xai',\n baseUrl: 'wss://api.x.ai/v1/realtime',\n type: 'realtime',\n transports: ['websocket', 'webrtc'],\n toolCalls: 'blocking',\n inputFormat: pcmFormat(24000),\n outputFormat: pcmFormat(24000),\n maxSessionMs: 7_200_000,\n resumable: true,\n ephemeralAuth: true,\n voices,\n provisioning: 'none',\n delivery: { interrupt: 'native', when_idle: 'approximate', silent: 'native' },\n truncation: 'client',\n liveToolUpdate: true,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n audioCost: { perInputMinute: 0.08, perOutputMinute: 0.08, perTextMessage: 0.004 },\n};\n\nexport function registerXaiRealtime(options: AdapterOptions = {}): () => void {\n const unregisterModel = registerVoiceModel(grokVoiceLatest);\n const unregisterApi = registerRealtimeApiProvider({\n api: 'xai-realtime',\n connect: (model, context, connectOptions) =>\n connectVendorSession(model, context, connectOptions, { ...options, dialect: 'xai' }),\n });\n return () => {\n unregisterModel();\n unregisterApi();\n };\n}\n\nexport { registerXAI } from './catalog.ts';\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,0BAA0B;AACnC;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAE5C,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,OAAO,MAAM,MAAM;AAC3B;AAEO,MAAM,kBAAiD;AAAA,EAC5D,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,YAAY,CAAC,aAAa,QAAQ;AAAA,EAClC,WAAW;AAAA,EACX,aAAa,UAAU,IAAK;AAAA,EAC5B,cAAc,UAAU,IAAK;AAAA,EAC7B,cAAc;AAAA,EACd,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA,cAAc;AAAA,EACd,UAAU,EAAE,WAAW,UAAU,WAAW,eAAe,QAAQ,SAAS;AAAA,EAC5E,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,EACzD,WAAW,EAAE,gBAAgB,MAAM,iBAAiB,MAAM,gBAAgB,KAAM;AAClF;AAEO,SAAS,oBAAoB,UAA0B,CAAC,GAAe;AAC5E,QAAM,kBAAkB,mBAAmB,eAAe;AAC1D,QAAM,gBAAgB,4BAA4B;AAAA,IAChD,KAAK;AAAA,IACL,SAAS,CAAC,OAAO,SAAS,mBACxB,qBAAqB,OAAO,SAAS,gBAAgB,EAAE,GAAG,SAAS,SAAS,MAAM,CAAC;AAAA,EACvF,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB;AAChB,kBAAc;AAAA,EAChB;AACF;AAEA,SAAS,mBAAmB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|