med-scribe-alliance-ts-sdk 2.0.1 → 2.0.3
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 +239 -282
- package/dist/index.d.ts +90 -14
- package/dist/index.mjs +189 -72
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,382 +1,339 @@
|
|
|
1
|
+
# MedScribe Alliance TS SDK
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
TypeScript SDK for the [MedScribe Alliance Protocol](https://github.com/MedScribeAlliance/scribe-emr-protocol) — handles discovery, recording, audio chunking (VAD), MP3 compression, upload, session lifecycle, and output retrieval.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
## npm package link
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
https://www.npmjs.com/package/med-scribe-alliance-ts-sdk
|
|
7
8
|
|
|
8
|
-
- ✅ **Protocol Compliant**: Fully implements the MedScribe Alliance Protocol specification
|
|
9
|
-
- ✅ **Type-Safe**: Complete TypeScript definitions for all API interactions
|
|
10
|
-
- ✅ **Auto-Discovery**: Automatic service capability discovery via well-known endpoint
|
|
11
|
-
- ✅ **Schema Validation**: AJV-powered validation against OpenAPI schemas
|
|
12
|
-
- ✅ **Error Handling**: Comprehensive error handling with typed error codes
|
|
13
|
-
- ✅ **Event System**: Built-in event emitter for session lifecycle events
|
|
14
|
-
- ✅ **Polling Support**: Automatic polling for session completion
|
|
15
9
|
|
|
16
10
|
## Installation
|
|
17
11
|
|
|
18
12
|
```bash
|
|
19
|
-
npm install scribe-
|
|
13
|
+
npm install med-scribe-alliance-ts-sdk
|
|
20
14
|
```
|
|
21
15
|
|
|
16
|
+
Peer dependencies (installed automatically):
|
|
17
|
+
|
|
18
|
+
- `@ricky0123/vad-web` — Voice Activity Detection
|
|
19
|
+
- `@breezystack/lamejs` — MP3 encoding
|
|
20
|
+
- `zod` — Schema validation
|
|
21
|
+
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
|
-
```
|
|
25
|
-
import { ScribeClient } from 'scribe-
|
|
24
|
+
```ts
|
|
25
|
+
import { ScribeClient } from 'med-scribe-alliance-ts-sdk';
|
|
26
26
|
|
|
27
|
-
// Initialize the SDK
|
|
28
27
|
const client = new ScribeClient({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
debug: true, // Optional: enable debug logging
|
|
28
|
+
baseUrl: 'https://api.example.com/voice/api/v2',
|
|
29
|
+
accessToken: 'your-bearer-token',
|
|
32
30
|
});
|
|
33
31
|
|
|
34
|
-
//
|
|
35
|
-
|
|
32
|
+
// Register callbacks
|
|
33
|
+
client.registerCallback('onUploadEvent', (event) => {
|
|
34
|
+
if (event.type === 'progress') {
|
|
35
|
+
console.log(`Uploaded ${event.data.successCount}/${event.data.totalCount}`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
templates: ['soap', 'medications'],
|
|
40
|
-
languageHint: ['en'],
|
|
41
|
-
model: 'pro',
|
|
39
|
+
client.registerCallback('onError', (event) => {
|
|
40
|
+
console.error(`[${event.error.code}] ${event.error.message}`);
|
|
42
41
|
});
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
// Start recording — creates session, starts mic, begins chunked upload
|
|
44
|
+
const result = await client.startRecording({
|
|
45
|
+
templates: ['soap', 'prescription'],
|
|
46
|
+
uploadType: 'chunked',
|
|
47
|
+
});
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
if (!result.success) {
|
|
50
|
+
console.error(result.error);
|
|
51
|
+
}
|
|
48
52
|
|
|
49
|
-
//
|
|
50
|
-
const endResponse = await client.endRecording();
|
|
51
|
-
console.log('Session ended, processing started');
|
|
53
|
+
// ... user speaks ...
|
|
52
54
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
55
|
-
maxAttempts: 60,
|
|
56
|
-
intervalMs: 2000,
|
|
57
|
-
onProgress: (status) => {
|
|
58
|
-
console.log('Status:', status.status);
|
|
59
|
-
},
|
|
60
|
-
});
|
|
55
|
+
// Stop recording — flushes remaining audio, waits for uploads, ends session
|
|
56
|
+
const stopResult = await client.endRecording();
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console.log(
|
|
65
|
-
console.log('Templates:', result.templates);
|
|
58
|
+
if (stopResult.success) {
|
|
59
|
+
console.log(`${stopResult.data.totalFiles} files uploaded`);
|
|
60
|
+
console.log(`${stopResult.data.failedUploads.length} failed`);
|
|
66
61
|
}
|
|
62
|
+
|
|
63
|
+
// Poll for results
|
|
64
|
+
const status = await client.getSessionStatus(result.data.session_id, {
|
|
65
|
+
poll: {
|
|
66
|
+
maxAttempts: 60,
|
|
67
|
+
intervalMs: 2000,
|
|
68
|
+
onProgress: (s) => console.log(`Status: ${s.status}`),
|
|
69
|
+
},
|
|
70
|
+
});
|
|
67
71
|
```
|
|
68
72
|
|
|
69
|
-
##
|
|
73
|
+
## Configuration
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
```ts
|
|
76
|
+
interface ScribeSDKConfig {
|
|
77
|
+
/** Base URL of the scribe service (required) */
|
|
78
|
+
baseUrl: string;
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
/** Bearer token for authentication */
|
|
81
|
+
accessToken?: string;
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
/** Transport mode: 'direct' (HTTP) or 'ipc' (Electron). Default: 'direct' */
|
|
84
|
+
mode?: 'direct' | 'ipc';
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```
|
|
86
|
+
/** IPC bridge — required when mode is 'ipc' */
|
|
87
|
+
ipcTransport?: IpcBridge;
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- `baseUrl` (optional): Base URL of the Scribe service
|
|
84
|
-
- `debug` (optional): Enable debug logging (default: `false`)
|
|
85
|
-
- `autoDiscovery` (optional): Auto-fetch service capabilities (default: `true`)
|
|
89
|
+
/** SharedWorker: true (require), false (disable), 'auto' (detect). Default: 'auto' */
|
|
90
|
+
useWorker?: boolean | 'auto';
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
/** URL to worker.bundle.js. Use getWorkerUrl() to resolve. */
|
|
93
|
+
workerScriptUrl?: string;
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
/** Enable debug logging. Default: false */
|
|
96
|
+
debug?: boolean;
|
|
90
97
|
|
|
91
|
-
|
|
98
|
+
/** Auto-fetch discovery document on init. Default: true */
|
|
99
|
+
autoDiscovery?: boolean;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
## API Reference
|
|
104
|
+
|
|
105
|
+
### Lifecycle
|
|
106
|
+
|
|
107
|
+
| Method | Returns | Description |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `init()` | `SDKResult<void>` | Fetch discovery document. Called automatically by `startRecording`. |
|
|
110
|
+
| `reset()` | `Promise<void>` | Stop recording, clear all state and caches. |
|
|
111
|
+
|
|
112
|
+
### Recording
|
|
113
|
+
|
|
114
|
+
| Method | Returns | Description |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `startRecording(options)` | `SDKResult<CreateSessionResponse>` | Create session + start mic + begin upload. |
|
|
117
|
+
| `startRecordingWithSession(session, options?)` | `SDKResult<void>` | Attach recorder to an existing session. |
|
|
118
|
+
| `pauseRecording()` | `void` | Pause VAD (mic stays open, no chunks created). |
|
|
119
|
+
| `resumeRecording()` | `void` | Resume VAD processing. |
|
|
120
|
+
| `endRecording()` | `SDKResult<StopRecordingResult>` | Stop mic, flush audio, wait for uploads, end session. |
|
|
121
|
+
| `isRecording()` | `boolean` | Whether a recording is active. |
|
|
122
|
+
| `isRecordingPaused()` | `boolean` | Whether the active recording is paused. |
|
|
123
|
+
| `retryFailedUploads()` | `SDKResult<RetryUploadResult>` | Retry uploads that failed during the last recording. |
|
|
124
|
+
| `hasFailedUploads()` | `boolean` | Whether there are retryable failed uploads. |
|
|
125
|
+
|
|
126
|
+
#### Recording Options
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
interface RecordingOptions {
|
|
130
|
+
templates: string[]; // Template IDs for extraction (required)
|
|
131
|
+
model?: string; // Model ID from discovery
|
|
132
|
+
languageHint?: string[]; // Language codes for audio input
|
|
133
|
+
transcriptLanguage?: string[];// Language codes for transcript output
|
|
134
|
+
uploadType?: string; // 'chunked' | 'single' (default: 'chunked')
|
|
135
|
+
communicationProtocol?: string;// 'http' | 'websocket' (default: 'http')
|
|
136
|
+
additionalData?: Record<string, any>;
|
|
137
|
+
deviceId?: string; // Specific microphone device ID
|
|
138
|
+
}
|
|
95
139
|
```
|
|
96
140
|
|
|
97
|
-
|
|
141
|
+
### Session
|
|
98
142
|
|
|
99
|
-
|
|
143
|
+
| Method | Returns | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `createSession(request)` | `SDKResult<CreateSessionResponse>` | Create a session without starting a recording. |
|
|
146
|
+
| `getSessionStatus(sessionId?, options?)` | `SDKResult<GetSessionStatusResponse>` | Get status. Pass `{ poll: PollOptions }` to poll until completion. |
|
|
147
|
+
| `getCurrentSession()` | `CreateSessionResponse \| null` | Get the active session if any. |
|
|
100
148
|
|
|
101
|
-
|
|
102
|
-
- `templates` (required): Array of template IDs to extract (e.g., `['soap', 'medications']`)
|
|
103
|
-
- `model` (optional): Model ID from discovery
|
|
104
|
-
- `languageHint` (optional): Array of ISO 639-1 language codes for audio input (e.g., `['en', 'es']`)
|
|
105
|
-
- `transcriptLanguage` (optional): Array of ISO 639-1 codes for transcript output (e.g., `['en']`)
|
|
106
|
-
- `uploadType` (optional): `'chunked'` or `'single'`
|
|
107
|
-
- `communicationProtocol` (optional): `'http'`, `'websocket'`, or `'rpc'` (default: `'http'`)
|
|
108
|
-
- `additionalData` (optional): Pass-through data for your application
|
|
149
|
+
#### Polling
|
|
109
150
|
|
|
110
|
-
|
|
111
|
-
- `session_id`: Unique session identifier
|
|
112
|
-
- `status`: Session status (`'created'`)
|
|
113
|
-
- `created_at`: ISO 8601 timestamp
|
|
114
|
-
- `expires_at`: ISO 8601 expiry timestamp
|
|
115
|
-
- `upload_url`: URL for uploading audio
|
|
151
|
+
Pass `poll` options to `getSessionStatus` to poll until the session reaches a terminal state:
|
|
116
152
|
|
|
117
|
-
```
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
patient_id: 'pat_123',
|
|
124
|
-
encounter_id: 'enc_456',
|
|
153
|
+
```ts
|
|
154
|
+
const result = await client.getSessionStatus(sessionId, {
|
|
155
|
+
poll: {
|
|
156
|
+
maxAttempts: 60,
|
|
157
|
+
intervalMs: 2000,
|
|
158
|
+
onProgress: (status) => console.log(status.status),
|
|
125
159
|
},
|
|
126
160
|
});
|
|
127
161
|
```
|
|
128
162
|
|
|
129
|
-
|
|
163
|
+
### Discovery
|
|
130
164
|
|
|
131
|
-
|
|
165
|
+
| Method | Returns | Description |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `getDiscoveryDocument()` | `DiscoveryDocument \| null` | Raw discovery document. |
|
|
168
|
+
| `getDiscoveryConfig()` | `SDKResult<ResolvedConfig>` | Resolved config from discovery. |
|
|
169
|
+
| `refreshDiscovery()` | `SDKResult<ResolvedConfig>` | Force-refresh discovery. |
|
|
132
170
|
|
|
133
|
-
|
|
134
|
-
const response = await client.endRecording();
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
##### `getOutputStatus(sessionId?: string): Promise<GetSessionStatusResponse>`
|
|
171
|
+
### Auth
|
|
138
172
|
|
|
139
|
-
|
|
173
|
+
| Method | Description |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `setAccessToken(token)` | Update Bearer token. Propagates to transport, recorder, and worker. |
|
|
140
176
|
|
|
141
|
-
|
|
142
|
-
const status = await client.getOutputStatus(session.session_id);
|
|
177
|
+
### Callbacks
|
|
143
178
|
|
|
144
|
-
|
|
145
|
-
console.log('Transcript:', status.transcript);
|
|
146
|
-
console.log('Templates:', status.templates);
|
|
147
|
-
}
|
|
148
|
-
```
|
|
179
|
+
Register with `client.registerCallback(name, handler)`, remove with `client.removeCallback(name, handler)`.
|
|
149
180
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
181
|
+
| Callback | Payload | Description |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `onRecordingStateChange` | `RecordingStateChangeEvent` | Recording started, paused, resumed, or ended. |
|
|
184
|
+
| `onAudioEvent` | `AudioEvent` | Speech detection, silence warnings, chunk ready. |
|
|
185
|
+
| `onUploadEvent` | `UploadEvent` | Upload progress and failures. |
|
|
186
|
+
| `onSessionEvent` | `SessionEvent` | Session created, ended, status updates. |
|
|
187
|
+
| `onError` | `ErrorEvent` | VAD, worker, transport, or validation errors. |
|
|
188
|
+
| `onTokenRequired` | `TokenRequiredEvent` | 401 received — call `event.resolve(newToken)` to retry. |
|
|
156
189
|
|
|
157
|
-
|
|
190
|
+
## Error Handling
|
|
158
191
|
|
|
159
|
-
|
|
192
|
+
All public async methods return `SDKResult<T>` — errors are returned, not thrown:
|
|
160
193
|
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
onProgress: (status) => {
|
|
166
|
-
console.log('Current status:', status.status);
|
|
167
|
-
},
|
|
168
|
-
});
|
|
194
|
+
```ts
|
|
195
|
+
type SDKResult<T> =
|
|
196
|
+
| { success: true; data: T }
|
|
197
|
+
| { success: false; error: ScribeError };
|
|
169
198
|
```
|
|
170
199
|
|
|
171
|
-
|
|
200
|
+
```ts
|
|
201
|
+
const result = await client.startRecording({ templates: ['soap'] });
|
|
172
202
|
|
|
173
|
-
|
|
203
|
+
if (!result.success) {
|
|
204
|
+
console.error(result.error.code, result.error.message);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
174
207
|
|
|
175
|
-
|
|
176
|
-
|
|
208
|
+
// result.data is typed as CreateSessionResponse
|
|
209
|
+
console.log(result.data.session_id);
|
|
177
210
|
```
|
|
178
211
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
212
|
+
### Error Classes
|
|
213
|
+
|
|
214
|
+
| Error | HTTP | Description |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| `ScribeError` | — | Base error class |
|
|
217
|
+
| `ValidationError` | 400 | Invalid request or config |
|
|
218
|
+
| `AuthenticationError` | 401 | Auth failed (after token refresh attempt) |
|
|
219
|
+
| `ForbiddenError` | 403 | Access denied |
|
|
220
|
+
| `SessionNotFoundError` | 404 | Session doesn't exist |
|
|
221
|
+
| `SessionExpiredError` | 410 | Session expired |
|
|
222
|
+
| `RateLimitError` | 429 | Rate limit exceeded |
|
|
223
|
+
| `DiscoveryError` | — | Discovery fetch/parse failed |
|
|
224
|
+
| `TransportError` | — | Network / IPC failure |
|
|
225
|
+
| `WorkerError` | — | SharedWorker failure |
|
|
226
|
+
| `UploadError` | — | Audio upload failure |
|
|
227
|
+
|
|
228
|
+
### Auto Token Refresh
|
|
229
|
+
|
|
230
|
+
When a 401 is received, the SDK dispatches `onTokenRequired`. Supply a new token to retry the request:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
client.registerCallback('onTokenRequired', async (event) => {
|
|
234
|
+
const newToken = await refreshMyAuthToken();
|
|
235
|
+
event.resolve(newToken);
|
|
236
|
+
});
|
|
187
237
|
```
|
|
188
238
|
|
|
189
|
-
|
|
239
|
+
Concurrent 401s are deduplicated — only one callback fires regardless of how many requests failed simultaneously.
|
|
190
240
|
|
|
191
|
-
|
|
241
|
+
## SharedWorker Support
|
|
192
242
|
|
|
193
|
-
|
|
194
|
-
client.on('discovery:complete', (event) => {
|
|
195
|
-
console.log('Discovery complete:', event.data);
|
|
196
|
-
});
|
|
243
|
+
The SDK offloads MP3 compression and upload to a SharedWorker for better main-thread performance. The worker is bundled separately as `dist/worker.bundle.js`.
|
|
197
244
|
|
|
198
|
-
|
|
199
|
-
console.log('Session created:', event.data);
|
|
200
|
-
});
|
|
245
|
+
### Setup
|
|
201
246
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
});
|
|
247
|
+
```ts
|
|
248
|
+
import { ScribeClient, getWorkerUrl } from 'med-scribe-alliance-ts-sdk';
|
|
205
249
|
|
|
206
|
-
client
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
client.on('error', (event) => {
|
|
211
|
-
console.error('Error:', event.error);
|
|
250
|
+
const client = new ScribeClient({
|
|
251
|
+
baseUrl: 'https://api.example.com',
|
|
252
|
+
workerScriptUrl: getWorkerUrl(), // or a custom path
|
|
212
253
|
});
|
|
213
254
|
```
|
|
214
255
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The SDK automatically validates all API requests against the OpenAPI schema using AJV. This ensures that:
|
|
218
|
-
|
|
219
|
-
- Request bodies conform to the expected structure
|
|
220
|
-
- Required fields are present
|
|
221
|
-
- Field types match the specification
|
|
222
|
-
- Session IDs follow the correct pattern (`ses_[a-zA-Z0-9]+`)
|
|
223
|
-
- Enum values are valid (e.g., `model`, `upload_type`, `communication_protocol`)
|
|
224
|
-
|
|
225
|
-
Validation happens automatically before any API call:
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// This will throw a ValidationError if the request is invalid
|
|
229
|
-
try {
|
|
230
|
-
const session = await client.startRecording({
|
|
231
|
-
templates: ['soap', 'medications'],
|
|
232
|
-
model: 'invalid-model', // ❌ Will fail validation if not in enum
|
|
233
|
-
uploadType: 'chunked',
|
|
234
|
-
});
|
|
235
|
-
} catch (error) {
|
|
236
|
-
if (error instanceof ValidationError) {
|
|
237
|
-
console.error('Validation failed:', error.message);
|
|
238
|
-
// Example: "Validation failed for CreateSessionRequest:
|
|
239
|
-
// - /model: must be equal to one of the allowed values (allowed values: pro, lite)"
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
```
|
|
256
|
+
### Serving the Worker
|
|
243
257
|
|
|
244
|
-
|
|
245
|
-
- `createSession()`: Validates request body against `CreateSessionRequest` schema
|
|
246
|
-
- `getSessionStatus()`: Validates session ID format
|
|
247
|
-
- `endSession()`: Validates session ID format
|
|
248
|
-
- `pollSessionStatus()`: Validates session ID format
|
|
258
|
+
The worker file must be served as a static asset:
|
|
249
259
|
|
|
250
|
-
|
|
260
|
+
**Copy to your public directory:**
|
|
261
|
+
```bash
|
|
262
|
+
cp node_modules/med-scribe-alliance-ts-sdk/dist/worker.bundle.js public/
|
|
263
|
+
```
|
|
251
264
|
|
|
252
|
-
|
|
253
|
-
|
|
265
|
+
**Or use a CDN blob URL (avoids same-origin restrictions):**
|
|
266
|
+
```ts
|
|
267
|
+
import { createWorkerBlobUrl } from 'med-scribe-alliance-ts-sdk';
|
|
254
268
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Validate a create session request
|
|
263
|
-
try {
|
|
264
|
-
schemaValidator.validateCreateSessionRequest({
|
|
265
|
-
templates: ['soap'],
|
|
266
|
-
upload_type: 'chunked',
|
|
267
|
-
communication_protocol: 'http',
|
|
268
|
-
});
|
|
269
|
-
} catch (error) {
|
|
270
|
-
console.error('Invalid request:', error.message);
|
|
271
|
-
}
|
|
269
|
+
const workerUrl = await createWorkerBlobUrl();
|
|
270
|
+
const client = new ScribeClient({
|
|
271
|
+
baseUrl: '...',
|
|
272
|
+
workerScriptUrl: workerUrl,
|
|
273
|
+
});
|
|
272
274
|
```
|
|
273
275
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
import {
|
|
280
|
-
ScribeError,
|
|
281
|
-
AuthenticationError,
|
|
282
|
-
SessionNotFoundError,
|
|
283
|
-
SessionExpiredError,
|
|
284
|
-
RateLimitError,
|
|
285
|
-
ValidationError,
|
|
286
|
-
} from 'scribe-standard-sdk';
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
await client.startRecording({ templates: ['soap'] });
|
|
290
|
-
} catch (error) {
|
|
291
|
-
if (error instanceof ValidationError) {
|
|
292
|
-
console.error('Validation error:', error.message);
|
|
293
|
-
} else if (error instanceof AuthenticationError) {
|
|
294
|
-
console.error('Authentication failed:', error.message);
|
|
295
|
-
} else if (error instanceof SessionExpiredError) {
|
|
296
|
-
console.error('Session expired:', error.details);
|
|
297
|
-
} else if (error instanceof RateLimitError) {
|
|
298
|
-
console.error('Rate limited, retry after:', error.details?.retry_after_seconds);
|
|
299
|
-
} else if (error instanceof ScribeError) {
|
|
300
|
-
console.error('Scribe error:', error.code, error.message);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
276
|
+
**Or set a global override:**
|
|
277
|
+
```ts
|
|
278
|
+
window.__MEDSCRIBE_WORKER_URL__ = '/assets/worker.bundle.js';
|
|
303
279
|
```
|
|
304
280
|
|
|
305
|
-
|
|
281
|
+
If the SharedWorker fails to initialize, the SDK silently falls back to main-thread compression and upload.
|
|
306
282
|
|
|
307
|
-
|
|
283
|
+
## Electron / IPC Mode
|
|
308
284
|
|
|
309
|
-
|
|
310
|
-
const status = await client.getOutputStatus(sessionId);
|
|
285
|
+
For Electron apps where network requests must go through the main process:
|
|
311
286
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const soap = status.templates['soap'];
|
|
315
|
-
if (soap.status === 'success') {
|
|
316
|
-
console.log('SOAP Note:', soap.data);
|
|
317
|
-
} else {
|
|
318
|
-
console.error('SOAP extraction failed:', soap.error);
|
|
319
|
-
}
|
|
287
|
+
```ts
|
|
288
|
+
import { ScribeClient, TransportMode } from 'med-scribe-alliance-ts-sdk';
|
|
320
289
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
290
|
+
const client = new ScribeClient({
|
|
291
|
+
baseUrl: 'https://api.example.com',
|
|
292
|
+
mode: TransportMode.IPC,
|
|
293
|
+
ipcTransport: {
|
|
294
|
+
send: (request) => ipcRenderer.send('scribe-request', request),
|
|
295
|
+
onResponse: (handler) => ipcRenderer.on('scribe-response', (_, res) => handler(res)),
|
|
296
|
+
},
|
|
297
|
+
});
|
|
327
298
|
```
|
|
328
299
|
|
|
329
|
-
|
|
300
|
+
IPC mode always uses main-thread compression (SharedWorker can't access the IPC bridge).
|
|
330
301
|
|
|
331
|
-
|
|
302
|
+
## Two-Step Flow
|
|
332
303
|
|
|
333
|
-
|
|
334
|
-
const discovery = client.getDiscoveryDocument();
|
|
304
|
+
For apps that need to create the session separately from recording:
|
|
335
305
|
|
|
336
|
-
|
|
337
|
-
|
|
306
|
+
```ts
|
|
307
|
+
// Step 1: Create session
|
|
308
|
+
const session = await client.createSession({
|
|
309
|
+
templates: ['soap'],
|
|
310
|
+
upload_type: 'chunked',
|
|
311
|
+
communication_protocol: 'http',
|
|
312
|
+
});
|
|
338
313
|
|
|
339
|
-
|
|
340
|
-
console.log('Max chunk duration:', discovery.capabilities.max_chunk_duration_seconds);
|
|
314
|
+
if (!session.success) return;
|
|
341
315
|
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
console.log(` Languages: ${model.languages.join(', ')}`);
|
|
346
|
-
console.log(` Max duration: ${model.max_session_duration_seconds}s`);
|
|
347
|
-
console.log(` Features:`, model.features);
|
|
316
|
+
// Step 2: Start recording with the existing session
|
|
317
|
+
await client.startRecordingWithSession(session.data, {
|
|
318
|
+
uploadType: 'chunked',
|
|
348
319
|
});
|
|
349
320
|
```
|
|
350
321
|
|
|
351
|
-
##
|
|
352
|
-
|
|
353
|
-
The SDK is written in TypeScript and provides full type definitions:
|
|
322
|
+
## Building from Source
|
|
354
323
|
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
RecordingOptions,
|
|
359
|
-
CreateSessionResponse,
|
|
360
|
-
GetSessionStatusResponse,
|
|
361
|
-
TemplatesOutput,
|
|
362
|
-
DiscoveryDocument,
|
|
363
|
-
} from 'scribe-standard-sdk';
|
|
324
|
+
```bash
|
|
325
|
+
npm install
|
|
326
|
+
npm run build
|
|
364
327
|
```
|
|
365
328
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
This SDK implements the following specifications:
|
|
329
|
+
Build output (`dist/`):
|
|
369
330
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
331
|
+
| File | Description |
|
|
332
|
+
|---|---|
|
|
333
|
+
| `index.mjs` | Minified ESM bundle |
|
|
334
|
+
| `index.d.ts` | Bundled type declarations |
|
|
335
|
+
| `worker.bundle.js` | Self-contained IIFE SharedWorker |
|
|
374
336
|
|
|
375
337
|
## License
|
|
376
338
|
|
|
377
339
|
MIT
|
|
378
|
-
|
|
379
|
-
## Contributing
|
|
380
|
-
|
|
381
|
-
Contributions are welcome! Please open an issue or submit a pull request.
|
|
382
|
-
|