playkit-sdk 1.3.0 → 1.4.0-beta.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 CHANGED
@@ -83,6 +83,26 @@ const imgElement = await image.toHTMLImage();
83
83
  document.body.appendChild(imgElement);
84
84
  ```
85
85
 
86
+ ### Text-to-Speech (TTS)
87
+
88
+ ```typescript
89
+ const tts = sdk.createTTSClient(); // defaults to 'default-tts-model'
90
+
91
+ // Get raw audio bytes plus usage metadata
92
+ const result = await tts.synthesize({
93
+ text: 'Welcome to the game, brave adventurer!',
94
+ voice: 'male-qn-qingse',
95
+ format: 'mp3',
96
+ });
97
+ console.log('Characters billed:', result.usageCharacters);
98
+ console.log('Audio length (ms):', result.audioLengthMs);
99
+
100
+ // Or get a playable object URL directly (browser)
101
+ const url = await tts.synthesizeToObjectURL({ text: 'Hello there!' });
102
+ const audio = new Audio(url);
103
+ audio.play();
104
+ ```
105
+
86
106
  ### NPC Conversations
87
107
 
88
108
  ```typescript
@@ -1,5 +1,5 @@
1
1
  /**
2
- * playkit-sdk v1.3.0
2
+ * playkit-sdk v1.4.0-beta.1
3
3
  * PlayKit SDK for JavaScript
4
4
  * @license SEE LICENSE IN LICENSE
5
5
  */
@@ -830,7 +830,7 @@ class TokenStorage {
830
830
  }
831
831
 
832
832
  const SDK_TYPE = 'Javascript';
833
- const SDK_VERSION = '"1.3.0"';
833
+ const SDK_VERSION = '"1.4.0-beta.1"';
834
834
  function getSDKHeaders() {
835
835
  return {
836
836
  'X-SDK-Type': SDK_TYPE,
@@ -2426,7 +2426,7 @@ DeviceAuthFlowManager.activeInstance = null;
2426
2426
  * Handles JWT exchange and token management
2427
2427
  */
2428
2428
  // @ts-ignore - replaced at build time
2429
- const DEFAULT_BASE_URL$5 = "https://api.playkit.ai";
2429
+ const DEFAULT_BASE_URL$6 = "https://api.playkit.ai";
2430
2430
  const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
2431
2431
  const TOKEN_REFRESH_ENDPOINT = '/api/auth/refresh';
2432
2432
  class AuthManager extends EventEmitter {
@@ -2444,7 +2444,7 @@ class AuthManager extends EventEmitter {
2444
2444
  this.storage = new TokenStorage({
2445
2445
  mode: config.mode === 'server' ? 'server' : 'browser',
2446
2446
  });
2447
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$5;
2447
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$6;
2448
2448
  this.authState = {
2449
2449
  isAuthenticated: false,
2450
2450
  };
@@ -3533,7 +3533,7 @@ class RechargeManager extends EventEmitter {
3533
3533
  * Player client for managing player information and credits
3534
3534
  */
3535
3535
  // @ts-ignore - replaced at build time
3536
- const DEFAULT_BASE_URL$4 = "https://api.playkit.ai";
3536
+ const DEFAULT_BASE_URL$5 = "https://api.playkit.ai";
3537
3537
  const PLAYER_INFO_ENDPOINT = '/api/external/player-info';
3538
3538
  const SET_NICKNAME_ENDPOINT = '/api/external/set-game-player-nickname';
3539
3539
  class PlayerClient extends EventEmitter {
@@ -3545,7 +3545,7 @@ class PlayerClient extends EventEmitter {
3545
3545
  this.balanceCheckInterval = null;
3546
3546
  this.logger = Logger.getLogger('PlayerClient');
3547
3547
  this.authManager = authManager;
3548
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$4;
3548
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$5;
3549
3549
  this.gameId = config.gameId;
3550
3550
  this.rechargeConfig = {
3551
3551
  autoShowBalanceModal: (_a = rechargeConfig.autoShowBalanceModal) !== null && _a !== void 0 ? _a : true,
@@ -3893,12 +3893,12 @@ function contentToString$1(content) {
3893
3893
  return textParts.map(part => part.text).join('');
3894
3894
  }
3895
3895
  // @ts-ignore - replaced at build time
3896
- const DEFAULT_BASE_URL$3 = "https://api.playkit.ai";
3896
+ const DEFAULT_BASE_URL$4 = "https://api.playkit.ai";
3897
3897
  class ChatProvider {
3898
3898
  constructor(authManager, config) {
3899
3899
  this.authManager = authManager;
3900
3900
  this.config = config;
3901
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
3901
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$4;
3902
3902
  }
3903
3903
  /**
3904
3904
  * Set player client for balance checking
@@ -4225,12 +4225,12 @@ class ChatProvider {
4225
4225
  * Image generation provider for HTTP communication with image API
4226
4226
  */
4227
4227
  // @ts-ignore - replaced at build time
4228
- const DEFAULT_BASE_URL$2 = "https://api.playkit.ai";
4228
+ const DEFAULT_BASE_URL$3 = "https://api.playkit.ai";
4229
4229
  class ImageProvider {
4230
4230
  constructor(authManager, config) {
4231
4231
  this.authManager = authManager;
4232
4232
  this.config = config;
4233
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$2;
4233
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
4234
4234
  }
4235
4235
  /**
4236
4236
  * Set player client for balance checking
@@ -4314,12 +4314,12 @@ class ImageProvider {
4314
4314
  * Transcription provider for HTTP communication with audio transcription API
4315
4315
  */
4316
4316
  // @ts-ignore - replaced at build time
4317
- const DEFAULT_BASE_URL$1 = "https://api.playkit.ai";
4317
+ const DEFAULT_BASE_URL$2 = "https://api.playkit.ai";
4318
4318
  class TranscriptionProvider {
4319
4319
  constructor(authManager, config) {
4320
4320
  this.authManager = authManager;
4321
4321
  this.config = config;
4322
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$1;
4322
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$2;
4323
4323
  }
4324
4324
  /**
4325
4325
  * Set player client for balance checking
@@ -4413,6 +4413,116 @@ class TranscriptionProvider {
4413
4413
  }
4414
4414
  }
4415
4415
 
4416
+ /**
4417
+ * TTS provider for HTTP communication with the text-to-speech API
4418
+ */
4419
+ // @ts-ignore - replaced at build time
4420
+ const DEFAULT_BASE_URL$1 = "https://api.playkit.ai";
4421
+ class TTSProvider {
4422
+ constructor(authManager, config) {
4423
+ this.authManager = authManager;
4424
+ this.config = config;
4425
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$1;
4426
+ }
4427
+ /**
4428
+ * Set player client for balance checking
4429
+ */
4430
+ setPlayerClient(playerClient) {
4431
+ this.playerClient = playerClient;
4432
+ }
4433
+ /**
4434
+ * Synthesize text into speech audio
4435
+ */
4436
+ async synthesize(ttsConfig) {
4437
+ // Ensure token is valid, auto-refresh if needed (browser mode only)
4438
+ await this.authManager.ensureValidToken();
4439
+ const token = this.authManager.getToken();
4440
+ if (!token) {
4441
+ throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
4442
+ }
4443
+ const model = ttsConfig.model || this.config.defaultTTSModel || 'default-tts-model';
4444
+ const endpoint = `/ai/${this.config.gameId}/v2/audio/speech`;
4445
+ const requestBody = {
4446
+ model,
4447
+ text: ttsConfig.text,
4448
+ };
4449
+ // Add optional parameters (only when defined)
4450
+ if (ttsConfig.voice !== undefined) {
4451
+ requestBody.voice = ttsConfig.voice;
4452
+ }
4453
+ if (ttsConfig.speed !== undefined) {
4454
+ requestBody.speed = ttsConfig.speed;
4455
+ }
4456
+ if (ttsConfig.vol !== undefined) {
4457
+ requestBody.vol = ttsConfig.vol;
4458
+ }
4459
+ if (ttsConfig.pitch !== undefined) {
4460
+ requestBody.pitch = ttsConfig.pitch;
4461
+ }
4462
+ if (ttsConfig.emotion !== undefined) {
4463
+ requestBody.emotion = ttsConfig.emotion;
4464
+ }
4465
+ if (ttsConfig.languageBoost !== undefined) {
4466
+ requestBody.language_boost = ttsConfig.languageBoost;
4467
+ }
4468
+ if (ttsConfig.format !== undefined) {
4469
+ requestBody.response_format = ttsConfig.format;
4470
+ }
4471
+ if (ttsConfig.voiceSetting !== undefined) {
4472
+ requestBody.voice_setting = ttsConfig.voiceSetting;
4473
+ }
4474
+ if (ttsConfig.audioSetting !== undefined) {
4475
+ requestBody.audio_setting = ttsConfig.audioSetting;
4476
+ }
4477
+ try {
4478
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
4479
+ method: 'POST',
4480
+ headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
4481
+ body: JSON.stringify(requestBody),
4482
+ });
4483
+ if (!response.ok) {
4484
+ const error = await response.json().catch(() => ({ message: 'Speech synthesis failed' }));
4485
+ const playKitError = new PlayKitError(error.message || 'Speech synthesis failed', error.code, response.status);
4486
+ // Check for insufficient credits error
4487
+ if (error.code === 'INSUFFICIENT_CREDITS' ||
4488
+ error.code === 'PLAYER_INSUFFICIENT_CREDIT' ||
4489
+ response.status === 402) {
4490
+ if (this.playerClient) {
4491
+ await this.playerClient.handleInsufficientCredits(playKitError);
4492
+ }
4493
+ }
4494
+ throw playKitError;
4495
+ }
4496
+ // SUCCESS: response is raw audio bytes, NOT JSON.
4497
+ const audio = await response.arrayBuffer();
4498
+ const contentType = response.headers.get('Content-Type');
4499
+ const usageHeader = response.headers.get('X-Usage-Characters');
4500
+ const audioLengthHeader = response.headers.get('X-Audio-Length-Ms');
4501
+ const result = {
4502
+ audio,
4503
+ format: contentType || ttsConfig.format || 'mp3',
4504
+ usageCharacters: Number(usageHeader) || 0,
4505
+ };
4506
+ if (audioLengthHeader !== null) {
4507
+ result.audioLengthMs = Number(audioLengthHeader) || 0;
4508
+ }
4509
+ // Check balance after successful API call
4510
+ if (this.playerClient) {
4511
+ this.playerClient.checkBalanceAfterApiCall().catch(() => {
4512
+ // Silently fail
4513
+ });
4514
+ }
4515
+ return result;
4516
+ }
4517
+ catch (error) {
4518
+ if (error instanceof PlayKitError) {
4519
+ throw error;
4520
+ }
4521
+ throw new PlayKitError(error instanceof Error ? error.message : 'Unknown error', 'TTS_ERROR');
4522
+ }
4523
+ }
4524
+ }
4525
+
4416
4526
  /******************************************************************************
4417
4527
  Copyright (c) Microsoft Corporation.
4418
4528
 
@@ -5077,6 +5187,80 @@ class TranscriptionClient {
5077
5187
  }
5078
5188
  }
5079
5189
 
5190
+ /**
5191
+ * High-level client for text-to-speech synthesis
5192
+ */
5193
+ /**
5194
+ * Resolve an audio format/content-type string into a valid MIME type.
5195
+ * The provider's `result.format` may be a full MIME (e.g. 'audio/mpeg' from
5196
+ * the Content-Type header) or a bare token (e.g. 'mp3' from the fallback).
5197
+ * Full MIME strings pass through; bare tokens are mapped.
5198
+ */
5199
+ function contentTypeFor(format) {
5200
+ if (format.includes('/')) {
5201
+ return format;
5202
+ }
5203
+ switch (format.toLowerCase()) {
5204
+ case 'mp3':
5205
+ return 'audio/mpeg';
5206
+ case 'wav':
5207
+ return 'audio/wav';
5208
+ case 'ogg':
5209
+ return 'audio/ogg';
5210
+ case 'flac':
5211
+ return 'audio/flac';
5212
+ case 'aac':
5213
+ return 'audio/aac';
5214
+ case 'pcm':
5215
+ return 'audio/pcm';
5216
+ default:
5217
+ return 'audio/mpeg';
5218
+ }
5219
+ }
5220
+ class TTSClient {
5221
+ constructor(provider, model) {
5222
+ this.provider = provider;
5223
+ this.model = model || 'default-tts-model';
5224
+ }
5225
+ /**
5226
+ * Get the current model name
5227
+ */
5228
+ get modelName() {
5229
+ return this.model;
5230
+ }
5231
+ /**
5232
+ * Synthesize text into speech audio
5233
+ * @param config - Full TTS configuration
5234
+ * @returns TTS result containing raw audio bytes and usage metadata
5235
+ */
5236
+ async synthesize(config) {
5237
+ return this.provider.synthesize(Object.assign(Object.assign({}, config), { model: config.model || this.model }));
5238
+ }
5239
+ /**
5240
+ * Synthesize text into speech and return it as a Blob (browser-friendly)
5241
+ * @param config - Full TTS configuration
5242
+ * @returns Audio Blob with the appropriate MIME type
5243
+ */
5244
+ async synthesizeToBlob(config) {
5245
+ const result = await this.synthesize(config);
5246
+ return new Blob([result.audio], { type: contentTypeFor(result.format) });
5247
+ }
5248
+ /**
5249
+ * Synthesize text into speech and return an object URL (browser only)
5250
+ * @param config - Full TTS configuration
5251
+ * @returns An object URL that can be assigned to an <audio> element
5252
+ * @throws PlayKitError if URL.createObjectURL is unavailable (e.g. Node.js)
5253
+ */
5254
+ async synthesizeToObjectURL(config) {
5255
+ if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') {
5256
+ throw new PlayKitError('URL.createObjectURL is not available in this environment. ' +
5257
+ 'Use synthesize() to access the raw audio bytes instead.', 'TTS_OBJECT_URL_UNAVAILABLE');
5258
+ }
5259
+ const blob = await this.synthesizeToBlob(config);
5260
+ return URL.createObjectURL(blob);
5261
+ }
5262
+ }
5263
+
5080
5264
  /**
5081
5265
  * Global AI Context Manager for managing NPC conversations and player context.
5082
5266
  *
@@ -6400,10 +6584,12 @@ class PlayKitSDK extends EventEmitter {
6400
6584
  this.chatProvider = new ChatProvider(this.authManager, this.config);
6401
6585
  this.imageProvider = new ImageProvider(this.authManager, this.config);
6402
6586
  this.transcriptionProvider = new TranscriptionProvider(this.authManager, this.config);
6587
+ this.ttsProvider = new TTSProvider(this.authManager, this.config);
6403
6588
  // Connect providers to player client for balance checking
6404
6589
  this.chatProvider.setPlayerClient(this.playerClient);
6405
6590
  this.imageProvider.setPlayerClient(this.playerClient);
6406
6591
  this.transcriptionProvider.setPlayerClient(this.playerClient);
6592
+ this.ttsProvider.setPlayerClient(this.playerClient);
6407
6593
  // Initialize AI context manager
6408
6594
  this.contextManager = new AIContextManager(this.config.aiContext);
6409
6595
  // Set chat client factory for compaction
@@ -6652,6 +6838,14 @@ class PlayKitSDK extends EventEmitter {
6652
6838
  this.ensureInitialized();
6653
6839
  return new TranscriptionClient(this.transcriptionProvider, model || this.config.defaultTranscriptionModel);
6654
6840
  }
6841
+ /**
6842
+ * Create a TTS client for text-to-speech
6843
+ * @param model - TTS model to use (default: 'default-tts-model')
6844
+ */
6845
+ createTTSClient(model) {
6846
+ this.ensureInitialized();
6847
+ return new TTSClient(this.ttsProvider, model || this.config.defaultTTSModel);
6848
+ }
6655
6849
  /**
6656
6850
  * Create an NPC client
6657
6851
  * Automatically registers with AIContextManager
@@ -7059,6 +7253,7 @@ exports.PlayerClient = PlayerClient;
7059
7253
  exports.RechargeManager = RechargeManager;
7060
7254
  exports.SchemaLibrary = SchemaLibrary;
7061
7255
  exports.StreamParser = StreamParser;
7256
+ exports.TTSClient = TTSClient;
7062
7257
  exports.TokenStorage = TokenStorage;
7063
7258
  exports.TokenValidator = TokenValidator;
7064
7259
  exports.TranscriptionClient = TranscriptionClient;