@ctchealth/plato-sdk 0.0.7 → 0.0.9

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
@@ -29,58 +29,51 @@ import { PlatoApiClient } from 'plato-sdk';
29
29
  const client = new PlatoApiClient({
30
30
  baseUrl: 'https://your-plato-api.com',
31
31
  token: 'your-api-token',
32
- user: 'test@test.test'
32
+ user: 'test@test.test',
33
33
  });
34
34
 
35
35
  // Create a simulation
36
36
  const simulation = await client.createSimulation({
37
- "persona": {
38
- "professionalProfile": {
39
- "location": "City Hospital, New York",
40
- "practiceSettings": "Outpatient Clinic",
41
- "yearOfExperience": 12,
42
- "specialityAndDepartment": "Cardiology"
37
+ persona: {
38
+ professionalProfile: {
39
+ location: 'City Hospital, New York',
40
+ practiceSettings: 'Outpatient Clinic',
41
+ yearOfExperience: 12,
42
+ specialityAndDepartment: 'Cardiology',
43
43
  },
44
- "segment": SegmentType.CostConsciousPrescriber,
45
- "assistantGender": AssistantVoiceGender.Male,
46
- "name": "Dr Vegapunk",
47
- "personalityAndBehaviour": {
48
- "riskTolerance": 40,
49
- "researchOrientation": 70,
50
- "recognitionNeed": 60,
51
- "brandLoyalty": 55,
52
- "patientEmpathy": 80,
53
- "trainingDifficulty": 50,
54
- "friendliness": 75
44
+ segment: SegmentType.CostConsciousPrescriber,
45
+ assistantGender: AssistantVoiceGender.Male,
46
+ name: 'Dr Vegapunk',
47
+ personalityAndBehaviour: {
48
+ riskTolerance: 40,
49
+ researchOrientation: 70,
50
+ recognitionNeed: 60,
51
+ brandLoyalty: 55,
52
+ patientEmpathy: 80,
53
+ },
54
+ context: {
55
+ subSpecialityOrTherapyFocus: 'Hypertension management',
56
+ typicalPatientMix: 'Elderly with comorbidities',
57
+ keyClinicalDrivers: 'Reducing cardiovascular risk',
55
58
  },
56
- "context": {
57
- "subSpecialityOrTherapyFocus": "Hypertension management",
58
- "typicalPatientMix": "Elderly with comorbidities",
59
- "keyClinicalDrivers": "Reducing cardiovascular risk"
60
- }
61
59
  },
62
- "product": {
63
- "name": "Ibuprofen 1000mg",
64
- "description": "A nonsteroidal anti-inflammatory drug used to reduce pain, inflammation, and fever"
60
+ product: {
61
+ name: 'Ibuprofen 1000mg',
62
+ description:
63
+ 'A nonsteroidal anti-inflammatory drug used to reduce pain, inflammation, and fever',
65
64
  },
66
- "presentation": "Oral tablets, 10 tablets per pack",
67
- "scenario": "Discussing treatment options for an elderly patient with chronic arthritis",
68
- "objectives": [
69
- "Demonstrate efficacy of Ibuprofen in pain management",
70
- "Highlight safety profile and contraindications",
71
- "Encourage patient adherence"
65
+ presentation: 'Oral tablets, 10 tablets per pack',
66
+ scenario: 'Discussing treatment options for an elderly patient with chronic arthritis',
67
+ objectives: [
68
+ 'Demonstrate efficacy of Ibuprofen in pain management',
69
+ 'Highlight safety profile and contraindications',
70
+ 'Encourage patient adherence',
72
71
  ],
73
- "anticipatedObjections": [
74
- "Concerns about gastrointestinal side effects",
75
- "Preference for lower-cost generic alternatives",
76
- "Potential interactions with other medications"
72
+ anticipatedObjections: [
73
+ 'Concerns about gastrointestinal side effects',
74
+ 'Preference for lower-cost generic alternatives',
75
+ 'Potential interactions with other medications',
77
76
  ],
78
- "trainingConfiguration": {
79
- "trainingDifficulty": 70,
80
- "communicationFormality": 75,
81
- "friendlinessLevel": 65,
82
- "subjectMatterExpertise": 80
83
- }
84
77
  });
85
78
 
86
79
  // Check the simulation status - If simulation creation phase is equals to FINISHED, the simulation is ready to use
@@ -96,7 +89,7 @@ call.on('call-start', () => {
96
89
  console.log('Call started');
97
90
  });
98
91
 
99
- call.on('message', (message) => {
92
+ call.on('message', message => {
100
93
  console.log('Message received:', message.transcript);
101
94
  });
102
95
 
@@ -121,6 +114,7 @@ new PlatoApiClient(config: ApiClientConfig)
121
114
  ```
122
115
 
123
116
  **Parameters:**
117
+
124
118
  - `config.baseUrl` (string): The base URL of the Plato API
125
119
  - `config.token` (string): Your API authentication token
126
120
  - `config.user` (string): Your user identifier
@@ -138,6 +132,7 @@ Creates a new medical training simulation. It may take a few minutes for the sim
138
132
  Starts a voice call with the AI persona for the specified simulation.
139
133
 
140
134
  **Returns:** Object with:
135
+
141
136
  - `stopCall()`: Function to end the call
142
137
  - `callId`: Unique identifier for the call
143
138
  - `on<K>(event: K, listener: CallEventListener<K>)`: Subscribe to call events
@@ -148,9 +143,11 @@ Starts a voice call with the AI persona for the specified simulation.
148
143
  Retrieves detailed information about a completed call, including transcript, summary, recording URL, ratings, and evaluation metrics.
149
144
 
150
145
  **Parameters:**
146
+
151
147
  - `callId` (string): The MongoDB `_id` of the call
152
148
 
153
149
  **Returns:** `Promise<CallDTO>` — An object containing:
150
+
154
151
  - `_id`: MongoDB ID of the call
155
152
  - `summary`: Summary of the conversation
156
153
  - `transcript`: Full transcript of the call
@@ -167,12 +164,63 @@ Retrieves detailed information about a completed call, including transcript, sum
167
164
  - And other call-related fields
168
165
 
169
166
  **Example:**
167
+
170
168
  ```typescript
171
169
  // After a call has ended, retrieve its details
172
170
  const callDetails = await client.getCallDetails(call._id);
173
171
  console.log('Call Summary:', callDetails.summary);
174
172
  ```
175
173
 
174
+ ##### getCallRecordings(queryParams: SimulationRecordingsQueryDto)
175
+
176
+ Retrieves a paginated list of call recordings for the authenticated user.
177
+
178
+ **Parameters:**
179
+
180
+ - `queryParams` (SimulationRecordingsQueryDto): Query parameters for filtering and pagination
181
+ - `limit` (optional): Number of recordings per page (`5`, `10`, or `25`)
182
+ - `page` (optional): Page number for pagination
183
+ - `sort` (optional): Sort order (`'asc'` or `'desc'`)
184
+
185
+ **Returns:** `Promise<SimulationRecordingsDto[]>` — An array of recording objects, each containing:
186
+
187
+ - `_id`: MongoDB ID of the call
188
+ - `createdAt`: Timestamp when the call was created
189
+ - `recordingStatus`: Status of the recording (`'STARTED'`, `'PROCESSING'`, `'FINISHED'`, or `'FAILED'`)
190
+
191
+ **Example:**
192
+
193
+ ```typescript
194
+ // Get the 10 most recent call recordings
195
+ const recordings = await client.getCallRecordings({
196
+ limit: 10,
197
+ page: 1,
198
+ sort: 'desc',
199
+ });
200
+
201
+ recordings.forEach(recording => {
202
+ console.log(`Call ${recording._id} - Status: ${recording.recordingStatus}`);
203
+ });
204
+ ```
205
+
206
+ ##### getCallRecording(callId: string)
207
+
208
+ Retrieves the recording URL for a specific call.
209
+
210
+ **Parameters:**
211
+
212
+ - `callId` (string): The MongoDB `_id` of the call
213
+
214
+ **Returns:** `Promise<string>` — The URL to access the call recording
215
+
216
+ **Example:**
217
+
218
+ ```typescript
219
+ // Get the recording URL for a specific call
220
+ const recordingUrl = await client.getCallRecording(call._id);
221
+ console.log('Recording URL:', recordingUrl);
222
+ ```
223
+
176
224
  ## Checking Simulation Status
177
225
 
178
226
  You can check the current phase/status of a simulation using the `checkSimulationStatus` method. This is useful for polling the simulation creation process until it is ready or has failed.
@@ -203,19 +251,18 @@ The SDK provides a comprehensive event system for managing voice calls:
203
251
 
204
252
  ```typescript
205
253
  // Subscribe to events
206
- call.on('message', (message) => {
254
+ call.on('message', message => {
207
255
  console.log('Transcript:', message.transcript);
208
256
  console.log('Type:', message.transcriptType); // 'final' or 'partial'
209
257
  });
210
258
 
211
- call.on('volume-level', (level) => {
259
+ call.on('volume-level', level => {
212
260
  console.log('Volume level:', level);
213
261
  });
214
262
 
215
- call.on('error', (error) => {
263
+ call.on('error', error => {
216
264
  console.error('Call error:', error);
217
265
  });
218
-
219
266
  ```
220
267
 
221
268
  ## Data Types
@@ -247,7 +294,7 @@ interface CharacterCreateDto {
247
294
  segment: SegmentType;
248
295
  personalityAndBehaviour: PersonalityAndBehaviourDto;
249
296
  context: ContextDto;
250
- assistantGender: AssistantVoiceGender;
297
+ assistantGender?: AssistantVoiceGender;
251
298
  }
252
299
  ```
253
300
 
@@ -260,7 +307,7 @@ try {
260
307
  const client = new PlatoApiClient({
261
308
  baseUrl: 'https://api.plato.com',
262
309
  token: 'your-token',
263
- user: 'your-user'
310
+ user: 'your-user',
264
311
  });
265
312
 
266
313
  const simulation = await client.createSimulation(simulationConfig);
@@ -270,7 +317,6 @@ try {
270
317
  }
271
318
  ```
272
319
 
273
-
274
320
  ## Support
275
321
 
276
322
  For support and questions, please contact the development team.
@@ -0,0 +1,121 @@
1
+ const { defineConfig, globalIgnores } = require('eslint/config');
2
+ const path = require('path');
3
+
4
+ const typescriptEslintEslintPlugin = require('@typescript-eslint/eslint-plugin');
5
+ const globals = require('globals');
6
+ const tsParser = require('@typescript-eslint/parser');
7
+ const jest = require('eslint-plugin-jest');
8
+ const js = require('@eslint/js');
9
+ const headers = require('eslint-plugin-headers');
10
+ const { FlatCompat } = require('@eslint/eslintrc');
11
+ const baseRules = require('../../eslint-config-base.cjs');
12
+
13
+ const compat = new FlatCompat({
14
+ baseDirectory: __dirname,
15
+ recommendedConfig: js.configs.recommended,
16
+ allConfig: js.configs.all,
17
+ });
18
+
19
+ module.exports = defineConfig([
20
+ globalIgnores([
21
+ '**/.eslintrc.js',
22
+ 'src/index.d.ts',
23
+ 'scripts/db-migrations/archived/**/*',
24
+ '**/*.spec.ts',
25
+ '**/*.config.js',
26
+ '**/*.config.cjs',
27
+ '**/jest.config.ts',
28
+ ]),
29
+ {
30
+ files: ['**/*.ts'],
31
+ plugins: {
32
+ headers,
33
+ },
34
+ rules: {
35
+ 'headers/header-format': [
36
+ 'error',
37
+ {
38
+ source: 'string',
39
+ content: `Copyright (c) 2025 ctcHealth. All rights reserved.
40
+
41
+ This file is part of the ctcHealth Plato Platform, a proprietary software system developed by ctcHealth.
42
+
43
+ This source code and all related materials are confidential and proprietary to ctcHealth.
44
+ Unauthorized access, use, copying, modification, distribution, or disclosure is strictly prohibited
45
+ and may result in disciplinary action and civil and/or criminal penalties.
46
+
47
+ This software is intended solely for authorized use within ctcHealth and its designated partners.
48
+
49
+ For internal use only.`,
50
+ style: 'jsdoc',
51
+ trailingNewlines: 1,
52
+ preservePragmas: false,
53
+ },
54
+ ],
55
+ },
56
+ },
57
+ {
58
+ extends: compat.extends(
59
+ 'plugin:@typescript-eslint/recommended',
60
+ 'plugin:@typescript-eslint/recommended-requiring-type-checking',
61
+ 'plugin:@typescript-eslint/strict'
62
+ ),
63
+
64
+ files: ['**/*.ts'],
65
+ ignores: ['**/*.json', '**/*.spec.ts'],
66
+
67
+ plugins: {
68
+ '@typescript-eslint': typescriptEslintEslintPlugin,
69
+ },
70
+
71
+ languageOptions: {
72
+ globals: {
73
+ ...globals.node,
74
+ ...globals.jest,
75
+ },
76
+
77
+ parser: tsParser,
78
+ ecmaVersion: 10,
79
+ sourceType: 'module',
80
+
81
+ parserOptions: {
82
+ project: path.join(__dirname, 'tsconfig.lib.json'),
83
+ },
84
+ },
85
+
86
+ rules: {
87
+ ...baseRules.rules, // shared rules
88
+ // project-specific rules
89
+ '@typescript-eslint/ban-ts-comment': 'warn',
90
+ '@typescript-eslint/interface-name-prefix': 'off',
91
+ '@typescript-eslint/explicit-function-return-type': 'warn',
92
+ '@typescript-eslint/promise-function-async': ['error'],
93
+ '@typescript-eslint/no-floating-promises': 'error',
94
+ '@typescript-eslint/await-thenable': 'error',
95
+ semi: ['error', 'always'],
96
+ '@typescript-eslint/unbound-method': 'error',
97
+ 'keyword-spacing': ['error'],
98
+ '@typescript-eslint/restrict-template-expressions': 'error',
99
+ '@typescript-eslint/prefer-regexp-exec': 'warn',
100
+ '@typescript-eslint/require-await': 'warn',
101
+ '@typescript-eslint/no-non-null-assertion': 'error',
102
+ '@typescript-eslint/no-unnecessary-condition': 'warn',
103
+ '@typescript-eslint/prefer-ts-expect-error': 'warn',
104
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
105
+ 'space-infix-ops': 'off',
106
+ },
107
+ },
108
+ {
109
+ files: ['**/*.spec.ts'],
110
+ extends: compat.extends('plugin:jest/recommended'),
111
+
112
+ plugins: {
113
+ jest,
114
+ },
115
+
116
+ rules: {
117
+ '@typescript-eslint/unbound-method': 'off',
118
+ 'jest/unbound-method': 'error',
119
+ },
120
+ },
121
+ ]);
package/jest.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ export default {
2
+ displayName: 'plato-sdk',
3
+ preset: '../../jest.preset.js',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../coverage/libs/plato-sdk',
10
+ };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@ctchealth/plato-sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "dependencies": {
5
5
  "tslib": "^2.3.0",
6
- "@vapi-ai/web": "2.1.8"
6
+ "@vapi-ai/web": "2.1.8",
7
+ "axios": "^1.7.9"
7
8
  },
8
9
  "type": "commonjs",
9
10
  "main": "./src/index.js",
10
11
  "typings": "./src/index.d.ts",
11
12
  "publishConfig": {
12
13
  "access": "public"
13
- },
14
- "types": "./src/index.d.ts"
15
- }
14
+ }
15
+ }
package/project.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "plato-sdk",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/plato-sdk/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/js:tsc",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/libs/plato-sdk",
13
+ "main": "libs/plato-sdk/src/index.ts",
14
+ "tsConfig": "libs/plato-sdk/tsconfig.lib.json",
15
+ "assets": ["libs/plato-sdk/*.md"]
16
+ }
17
+ },
18
+ "nx-release-publish": {
19
+ "options": {
20
+ "packageRoot": "dist/{projectRoot}"
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Copyright (c) 2025 ctcHealth. All rights reserved.
3
+ *
4
+ * This file is part of the ctcHealth Plato Platform, a proprietary software system developed by ctcHealth.
5
+ *
6
+ * This source code and all related materials are confidential and proprietary to ctcHealth.
7
+ * Unauthorized access, use, copying, modification, distribution, or disclosure is strictly prohibited
8
+ * and may result in disciplinary action and civil and/or criminal penalties.
9
+ *
10
+ * This software is intended solely for authorized use within ctcHealth and its designated partners.
11
+ *
12
+ * For internal use only.
13
+ */
14
+ export enum YearOfExperience {
15
+ '1-5' = '1-5 years',
16
+ '6-10' = '6-10 years',
17
+ '11-20' = '16-20 years',
18
+ '20+' = '20+ years',
19
+ }
20
+
21
+ export enum PracticeType {
22
+ Private = 'Private Practice',
23
+ Hospital = 'Hospital',
24
+ AcademicMedicalCenter = 'Academic Medical Center',
25
+ Clinic = 'Clinic',
26
+ }
27
+ export enum SegmentType {
28
+ Traditionalist = 'The Traditionalist',
29
+ Innovator = 'The Innovator',
30
+ PatientOrientedPhysician = 'The Patient-Oriented Physician',
31
+ FinanciallyDrivenPrescriber = 'The Financially-Driven Prescriber',
32
+ EvidencePurist = 'The Evidence-Purist',
33
+ CostConsciousPrescriber = 'The Cost-Conscious Prescriber',
34
+ }
35
+
36
+ export enum AssistantVoiceGender {
37
+ Male = 'Male',
38
+ Female = 'Female',
39
+ }
40
+
41
+ export class PersonalityAndBehaviourDto {
42
+ riskTolerance!: number;
43
+ researchOrientation!: number;
44
+ recognitionNeed!: number;
45
+ brandLoyalty!: number;
46
+ patientEmpathy!: number;
47
+ }
48
+
49
+ export class ProfessionalProfileDto {
50
+ practiceSettings!: string;
51
+ yearOfExperience!: number;
52
+ specialityAndDepartment!: string;
53
+ location!: string;
54
+ }
55
+
56
+ export class ContextDto {
57
+ subSpecialityOrTherapyFocus!: string;
58
+ typicalPatientMix!: string;
59
+ keyClinicalDrivers!: string;
60
+ }
61
+
62
+ export class CharacterCreateDto {
63
+ name!: string;
64
+ professionalProfile!: ProfessionalProfileDto;
65
+ segment!: SegmentType;
66
+ personalityAndBehaviour!: PersonalityAndBehaviourDto;
67
+ context!: ContextDto;
68
+ assistantGender?: AssistantVoiceGender;
69
+ }
70
+
71
+ export class ProductConfig {
72
+ name!: string;
73
+ description!: string;
74
+ }
75
+
76
+ export class CreateSimulationDto {
77
+ persona!: CharacterCreateDto;
78
+ product!: ProductConfig;
79
+ presentation?: string;
80
+ scenario!: string;
81
+ objectives?: string[];
82
+ anticipatedObjections?: string[];
83
+ }
84
+
85
+ export type RecordingsLimit = 5 | 10 | 25;
86
+
87
+ export class SimulationRecordingsQueryDto {
88
+ limit?: RecordingsLimit;
89
+ page?: number;
90
+ sort?: SortOrder;
91
+ }
92
+
93
+ export enum SortOrder {
94
+ ASC = 'asc',
95
+ DESC = 'desc',
96
+ }
97
+
98
+ export enum RecordingStatus {
99
+ STARTED = 'STARTED',
100
+ PROCESSING = 'PROCESSING',
101
+ FINISHED = 'FINISHED',
102
+ FAILED = 'FAILED',
103
+ }
104
+
105
+ export class SimulationRecordingsDto {
106
+ _id!: string;
107
+ createdAt!: Date;
108
+ recordingStatus?: RecordingStatus;
109
+ }
110
+
111
+ //
112
+ export interface CallCreateDto {
113
+ /**
114
+ * Call ID obtained
115
+ */
116
+ callId: string;
117
+ /**
118
+ * Call Assistant ID
119
+ */
120
+ assistantId: string;
121
+ }
122
+
123
+ export interface CallDTO {
124
+ /**
125
+ * call ID
126
+ */
127
+ _id: string;
128
+ /**
129
+ * call ID obtained
130
+ */
131
+ callId: string;
132
+ /**
133
+ * call User ID
134
+ */
135
+ platoUserId: string;
136
+ /**
137
+ * call Assistant ID
138
+ */
139
+ assistantId: string;
140
+ /**
141
+ * call summary of the conversation
142
+ */
143
+ summary: string;
144
+ /**
145
+ * call transcript of the conversation
146
+ */
147
+ transcript: string;
148
+ /**
149
+ * call feedback provided by the user
150
+ */
151
+ feedback: string;
152
+ /**
153
+ * Success Evaluation returned by model
154
+ */
155
+ successEvaluation: boolean;
156
+ /**
157
+ * call Recording URL
158
+ */
159
+ recordingUrl: string;
160
+ /**
161
+ * Status of recording processing (e.g., 'STARTED', 'PROCESSING', 'FINISHED', 'FAILED')
162
+ */
163
+ recordingStatus?: RecordingStatus;
164
+ /**
165
+ * Date and Time of the creation of the message
166
+ */
167
+ createdAt: Date;
168
+ /**
169
+ * Date and Time of the creation of the message
170
+ */
171
+ endedAt: Date;
172
+ /**
173
+ * Rating of the call given by the user
174
+ */
175
+ rating: number;
176
+ /**
177
+ * Main strenghts of the user conversation based on the analysis of the AI
178
+ */
179
+ strengths: Array<string>;
180
+ /**
181
+ * Main weak points of the user conversation based on the analysis of the AI
182
+ */
183
+ weaknesses: Array<string>;
184
+ /**
185
+ * Name of Metric for the AI feedback report
186
+ */
187
+ metric1: string;
188
+ /**
189
+ * Name of Metric for the AI feedback report
190
+ */
191
+ metric2: string;
192
+ /**
193
+ * Name of Metric for the AI feedback report
194
+ */
195
+ metric3: string;
196
+ /**
197
+ * AI feedback value for Metric 1
198
+ */
199
+ metric1Value: number;
200
+ /**
201
+ * AI feedback value for Metric 2
202
+ */
203
+ metric2Value: number;
204
+ /**
205
+ * AI feedback value for Metric 3
206
+ */
207
+ metric3Value: number;
208
+ /**
209
+ * AI feedback value for the call score
210
+ */
211
+ score?: number;
212
+ /**
213
+ * Defines if the calls will be consider for the memory feature
214
+ */
215
+ inMemory: boolean;
216
+ }
217
+
218
+ export enum CreationStatus {
219
+ CREATING = 'CREATING',
220
+ READY = 'READY',
221
+ ERROR = 'ERROR',
222
+ }
223
+ export enum CreationPhase {
224
+ STARTING = 'STARTING',
225
+ CORE = 'CORE',
226
+ BOUNDARIES = 'BOUNDARIES',
227
+ SPEECH_AND_THOUGHT = 'SPEECH_AND_THOUGHT',
228
+ CONVERSATION_EVOLUTION = 'CONVERSATION_EVOLUTION',
229
+ MEMORY = 'MEMORY',
230
+ FINISHED = 'FINISHED',
231
+ ERROR = 'ERROR',
232
+ }