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.
@@ -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
  */
@@ -1177,7 +1177,7 @@
1177
1177
  }
1178
1178
 
1179
1179
  const SDK_TYPE = 'Javascript';
1180
- const SDK_VERSION = '"1.3.0"';
1180
+ const SDK_VERSION = '"1.4.0-beta.1"';
1181
1181
  function getSDKHeaders() {
1182
1182
  return {
1183
1183
  'X-SDK-Type': SDK_TYPE,
@@ -2773,7 +2773,7 @@
2773
2773
  * Handles JWT exchange and token management
2774
2774
  */
2775
2775
  // @ts-ignore - replaced at build time
2776
- const DEFAULT_BASE_URL$5 = "https://api.playkit.ai";
2776
+ const DEFAULT_BASE_URL$6 = "https://api.playkit.ai";
2777
2777
  const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
2778
2778
  const TOKEN_REFRESH_ENDPOINT = '/api/auth/refresh';
2779
2779
  class AuthManager extends EventEmitter {
@@ -2791,7 +2791,7 @@
2791
2791
  this.storage = new TokenStorage({
2792
2792
  mode: config.mode === 'server' ? 'server' : 'browser',
2793
2793
  });
2794
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$5;
2794
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$6;
2795
2795
  this.authState = {
2796
2796
  isAuthenticated: false,
2797
2797
  };
@@ -3880,7 +3880,7 @@
3880
3880
  * Player client for managing player information and credits
3881
3881
  */
3882
3882
  // @ts-ignore - replaced at build time
3883
- const DEFAULT_BASE_URL$4 = "https://api.playkit.ai";
3883
+ const DEFAULT_BASE_URL$5 = "https://api.playkit.ai";
3884
3884
  const PLAYER_INFO_ENDPOINT = '/api/external/player-info';
3885
3885
  const SET_NICKNAME_ENDPOINT = '/api/external/set-game-player-nickname';
3886
3886
  class PlayerClient extends EventEmitter {
@@ -3892,7 +3892,7 @@
3892
3892
  this.balanceCheckInterval = null;
3893
3893
  this.logger = Logger.getLogger('PlayerClient');
3894
3894
  this.authManager = authManager;
3895
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$4;
3895
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$5;
3896
3896
  this.gameId = config.gameId;
3897
3897
  this.rechargeConfig = {
3898
3898
  autoShowBalanceModal: (_a = rechargeConfig.autoShowBalanceModal) !== null && _a !== void 0 ? _a : true,
@@ -4240,12 +4240,12 @@
4240
4240
  return textParts.map(part => part.text).join('');
4241
4241
  }
4242
4242
  // @ts-ignore - replaced at build time
4243
- const DEFAULT_BASE_URL$3 = "https://api.playkit.ai";
4243
+ const DEFAULT_BASE_URL$4 = "https://api.playkit.ai";
4244
4244
  class ChatProvider {
4245
4245
  constructor(authManager, config) {
4246
4246
  this.authManager = authManager;
4247
4247
  this.config = config;
4248
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
4248
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$4;
4249
4249
  }
4250
4250
  /**
4251
4251
  * Set player client for balance checking
@@ -4572,12 +4572,12 @@
4572
4572
  * Image generation provider for HTTP communication with image API
4573
4573
  */
4574
4574
  // @ts-ignore - replaced at build time
4575
- const DEFAULT_BASE_URL$2 = "https://api.playkit.ai";
4575
+ const DEFAULT_BASE_URL$3 = "https://api.playkit.ai";
4576
4576
  class ImageProvider {
4577
4577
  constructor(authManager, config) {
4578
4578
  this.authManager = authManager;
4579
4579
  this.config = config;
4580
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$2;
4580
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$3;
4581
4581
  }
4582
4582
  /**
4583
4583
  * Set player client for balance checking
@@ -4661,12 +4661,12 @@
4661
4661
  * Transcription provider for HTTP communication with audio transcription API
4662
4662
  */
4663
4663
  // @ts-ignore - replaced at build time
4664
- const DEFAULT_BASE_URL$1 = "https://api.playkit.ai";
4664
+ const DEFAULT_BASE_URL$2 = "https://api.playkit.ai";
4665
4665
  class TranscriptionProvider {
4666
4666
  constructor(authManager, config) {
4667
4667
  this.authManager = authManager;
4668
4668
  this.config = config;
4669
- this.baseURL = config.baseURL || DEFAULT_BASE_URL$1;
4669
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$2;
4670
4670
  }
4671
4671
  /**
4672
4672
  * Set player client for balance checking
@@ -4760,6 +4760,116 @@
4760
4760
  }
4761
4761
  }
4762
4762
 
4763
+ /**
4764
+ * TTS provider for HTTP communication with the text-to-speech API
4765
+ */
4766
+ // @ts-ignore - replaced at build time
4767
+ const DEFAULT_BASE_URL$1 = "https://api.playkit.ai";
4768
+ class TTSProvider {
4769
+ constructor(authManager, config) {
4770
+ this.authManager = authManager;
4771
+ this.config = config;
4772
+ this.baseURL = config.baseURL || DEFAULT_BASE_URL$1;
4773
+ }
4774
+ /**
4775
+ * Set player client for balance checking
4776
+ */
4777
+ setPlayerClient(playerClient) {
4778
+ this.playerClient = playerClient;
4779
+ }
4780
+ /**
4781
+ * Synthesize text into speech audio
4782
+ */
4783
+ async synthesize(ttsConfig) {
4784
+ // Ensure token is valid, auto-refresh if needed (browser mode only)
4785
+ await this.authManager.ensureValidToken();
4786
+ const token = this.authManager.getToken();
4787
+ if (!token) {
4788
+ throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
4789
+ }
4790
+ const model = ttsConfig.model || this.config.defaultTTSModel || 'default-tts-model';
4791
+ const endpoint = `/ai/${this.config.gameId}/v2/audio/speech`;
4792
+ const requestBody = {
4793
+ model,
4794
+ text: ttsConfig.text,
4795
+ };
4796
+ // Add optional parameters (only when defined)
4797
+ if (ttsConfig.voice !== undefined) {
4798
+ requestBody.voice = ttsConfig.voice;
4799
+ }
4800
+ if (ttsConfig.speed !== undefined) {
4801
+ requestBody.speed = ttsConfig.speed;
4802
+ }
4803
+ if (ttsConfig.vol !== undefined) {
4804
+ requestBody.vol = ttsConfig.vol;
4805
+ }
4806
+ if (ttsConfig.pitch !== undefined) {
4807
+ requestBody.pitch = ttsConfig.pitch;
4808
+ }
4809
+ if (ttsConfig.emotion !== undefined) {
4810
+ requestBody.emotion = ttsConfig.emotion;
4811
+ }
4812
+ if (ttsConfig.languageBoost !== undefined) {
4813
+ requestBody.language_boost = ttsConfig.languageBoost;
4814
+ }
4815
+ if (ttsConfig.format !== undefined) {
4816
+ requestBody.response_format = ttsConfig.format;
4817
+ }
4818
+ if (ttsConfig.voiceSetting !== undefined) {
4819
+ requestBody.voice_setting = ttsConfig.voiceSetting;
4820
+ }
4821
+ if (ttsConfig.audioSetting !== undefined) {
4822
+ requestBody.audio_setting = ttsConfig.audioSetting;
4823
+ }
4824
+ try {
4825
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
4826
+ method: 'POST',
4827
+ headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
4828
+ body: JSON.stringify(requestBody),
4829
+ });
4830
+ if (!response.ok) {
4831
+ const error = await response.json().catch(() => ({ message: 'Speech synthesis failed' }));
4832
+ const playKitError = new PlayKitError(error.message || 'Speech synthesis failed', error.code, response.status);
4833
+ // Check for insufficient credits error
4834
+ if (error.code === 'INSUFFICIENT_CREDITS' ||
4835
+ error.code === 'PLAYER_INSUFFICIENT_CREDIT' ||
4836
+ response.status === 402) {
4837
+ if (this.playerClient) {
4838
+ await this.playerClient.handleInsufficientCredits(playKitError);
4839
+ }
4840
+ }
4841
+ throw playKitError;
4842
+ }
4843
+ // SUCCESS: response is raw audio bytes, NOT JSON.
4844
+ const audio = await response.arrayBuffer();
4845
+ const contentType = response.headers.get('Content-Type');
4846
+ const usageHeader = response.headers.get('X-Usage-Characters');
4847
+ const audioLengthHeader = response.headers.get('X-Audio-Length-Ms');
4848
+ const result = {
4849
+ audio,
4850
+ format: contentType || ttsConfig.format || 'mp3',
4851
+ usageCharacters: Number(usageHeader) || 0,
4852
+ };
4853
+ if (audioLengthHeader !== null) {
4854
+ result.audioLengthMs = Number(audioLengthHeader) || 0;
4855
+ }
4856
+ // Check balance after successful API call
4857
+ if (this.playerClient) {
4858
+ this.playerClient.checkBalanceAfterApiCall().catch(() => {
4859
+ // Silently fail
4860
+ });
4861
+ }
4862
+ return result;
4863
+ }
4864
+ catch (error) {
4865
+ if (error instanceof PlayKitError) {
4866
+ throw error;
4867
+ }
4868
+ throw new PlayKitError(error instanceof Error ? error.message : 'Unknown error', 'TTS_ERROR');
4869
+ }
4870
+ }
4871
+ }
4872
+
4763
4873
  /******************************************************************************
4764
4874
  Copyright (c) Microsoft Corporation.
4765
4875
 
@@ -5424,6 +5534,80 @@
5424
5534
  }
5425
5535
  }
5426
5536
 
5537
+ /**
5538
+ * High-level client for text-to-speech synthesis
5539
+ */
5540
+ /**
5541
+ * Resolve an audio format/content-type string into a valid MIME type.
5542
+ * The provider's `result.format` may be a full MIME (e.g. 'audio/mpeg' from
5543
+ * the Content-Type header) or a bare token (e.g. 'mp3' from the fallback).
5544
+ * Full MIME strings pass through; bare tokens are mapped.
5545
+ */
5546
+ function contentTypeFor(format) {
5547
+ if (format.includes('/')) {
5548
+ return format;
5549
+ }
5550
+ switch (format.toLowerCase()) {
5551
+ case 'mp3':
5552
+ return 'audio/mpeg';
5553
+ case 'wav':
5554
+ return 'audio/wav';
5555
+ case 'ogg':
5556
+ return 'audio/ogg';
5557
+ case 'flac':
5558
+ return 'audio/flac';
5559
+ case 'aac':
5560
+ return 'audio/aac';
5561
+ case 'pcm':
5562
+ return 'audio/pcm';
5563
+ default:
5564
+ return 'audio/mpeg';
5565
+ }
5566
+ }
5567
+ class TTSClient {
5568
+ constructor(provider, model) {
5569
+ this.provider = provider;
5570
+ this.model = model || 'default-tts-model';
5571
+ }
5572
+ /**
5573
+ * Get the current model name
5574
+ */
5575
+ get modelName() {
5576
+ return this.model;
5577
+ }
5578
+ /**
5579
+ * Synthesize text into speech audio
5580
+ * @param config - Full TTS configuration
5581
+ * @returns TTS result containing raw audio bytes and usage metadata
5582
+ */
5583
+ async synthesize(config) {
5584
+ return this.provider.synthesize(Object.assign(Object.assign({}, config), { model: config.model || this.model }));
5585
+ }
5586
+ /**
5587
+ * Synthesize text into speech and return it as a Blob (browser-friendly)
5588
+ * @param config - Full TTS configuration
5589
+ * @returns Audio Blob with the appropriate MIME type
5590
+ */
5591
+ async synthesizeToBlob(config) {
5592
+ const result = await this.synthesize(config);
5593
+ return new Blob([result.audio], { type: contentTypeFor(result.format) });
5594
+ }
5595
+ /**
5596
+ * Synthesize text into speech and return an object URL (browser only)
5597
+ * @param config - Full TTS configuration
5598
+ * @returns An object URL that can be assigned to an <audio> element
5599
+ * @throws PlayKitError if URL.createObjectURL is unavailable (e.g. Node.js)
5600
+ */
5601
+ async synthesizeToObjectURL(config) {
5602
+ if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') {
5603
+ throw new PlayKitError('URL.createObjectURL is not available in this environment. ' +
5604
+ 'Use synthesize() to access the raw audio bytes instead.', 'TTS_OBJECT_URL_UNAVAILABLE');
5605
+ }
5606
+ const blob = await this.synthesizeToBlob(config);
5607
+ return URL.createObjectURL(blob);
5608
+ }
5609
+ }
5610
+
5427
5611
  /**
5428
5612
  * Global AI Context Manager for managing NPC conversations and player context.
5429
5613
  *
@@ -6747,10 +6931,12 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
6747
6931
  this.chatProvider = new ChatProvider(this.authManager, this.config);
6748
6932
  this.imageProvider = new ImageProvider(this.authManager, this.config);
6749
6933
  this.transcriptionProvider = new TranscriptionProvider(this.authManager, this.config);
6934
+ this.ttsProvider = new TTSProvider(this.authManager, this.config);
6750
6935
  // Connect providers to player client for balance checking
6751
6936
  this.chatProvider.setPlayerClient(this.playerClient);
6752
6937
  this.imageProvider.setPlayerClient(this.playerClient);
6753
6938
  this.transcriptionProvider.setPlayerClient(this.playerClient);
6939
+ this.ttsProvider.setPlayerClient(this.playerClient);
6754
6940
  // Initialize AI context manager
6755
6941
  this.contextManager = new AIContextManager(this.config.aiContext);
6756
6942
  // Set chat client factory for compaction
@@ -6999,6 +7185,14 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
6999
7185
  this.ensureInitialized();
7000
7186
  return new TranscriptionClient(this.transcriptionProvider, model || this.config.defaultTranscriptionModel);
7001
7187
  }
7188
+ /**
7189
+ * Create a TTS client for text-to-speech
7190
+ * @param model - TTS model to use (default: 'default-tts-model')
7191
+ */
7192
+ createTTSClient(model) {
7193
+ this.ensureInitialized();
7194
+ return new TTSClient(this.ttsProvider, model || this.config.defaultTTSModel);
7195
+ }
7002
7196
  /**
7003
7197
  * Create an NPC client
7004
7198
  * Automatically registers with AIContextManager
@@ -7415,6 +7609,7 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
7415
7609
  RechargeManager: RechargeManager,
7416
7610
  SchemaLibrary: SchemaLibrary,
7417
7611
  StreamParser: StreamParser,
7612
+ TTSClient: TTSClient,
7418
7613
  TokenStorage: TokenStorage,
7419
7614
  TokenValidator: TokenValidator,
7420
7615
  TranscriptionClient: TranscriptionClient,