Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @speechmatics/auth package for JWT creation #67

Merged
merged 76 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
5929c70
Introduce monorepo structure
mnemitz Sep 2, 2024
2b5926e
wip
mnemitz Sep 3, 2024
3f7e976
Fix package + GH workflow
mnemitz Sep 3, 2024
602e86d
Run lint/format on PR
mnemitz Sep 3, 2024
aed4ad4
update readme
mnemitz Sep 3, 2024
bd17854
Add SDK_VERSION const
mnemitz Sep 3, 2024
31bb7a9
Fix version
mnemitz Sep 3, 2024
181abe8
Trigger action
mnemitz Sep 3, 2024
2c5d64c
Add realtime
mnemitz Sep 3, 2024
18f9832
Add RT models + generation scripts
mnemitz Sep 3, 2024
cdad8d2
Polyfill websocket for node only
mnemitz Sep 4, 2024
598422c
Add browser field
mnemitz Sep 4, 2024
ca8fd3f
Rename batch/rt client packages
mnemitz Oct 17, 2024
7d32067
Use 5.0.1-alpha.0
mnemitz Oct 17, 2024
7378915
Draft: Flow client (#63)
mnemitz Oct 25, 2024
af1d7f8
README
mnemitz Oct 25, 2024
677c85a
Flow react package (#64)
mnemitz Oct 25, 2024
88e3803
Readme update
mnemitz Oct 25, 2024
eed2e07
logo
mnemitz Oct 25, 2024
fd1873f
Fix logo
mnemitz Oct 25, 2024
2e5f48c
Fix logo
mnemitz Oct 25, 2024
3b8158f
Fix links
mnemitz Oct 25, 2024
833ade7
Descriptions
mnemitz Oct 25, 2024
9dc9d3b
Spacing
mnemitz Oct 25, 2024
2bf377e
Fix links
mnemitz Oct 25, 2024
090782f
Logo bigger
mnemitz Oct 25, 2024
deec69b
Add note about legacy package
mnemitz Oct 25, 2024
fce5f5a
Note about JS runtimes
mnemitz Oct 25, 2024
f23d64e
Lint
mnemitz Oct 25, 2024
6c5d68a
Batch client init (#66)
mnemitz Oct 25, 2024
ce8ecc2
Realtime tweaks + example
mnemitz Oct 25, 2024
bb30a1e
Node examples readme
mnemitz Oct 25, 2024
60a1c64
Debug weird font thing
mnemitz Oct 25, 2024
1a5a97a
Revert
mnemitz Oct 25, 2024
901761a
Copy
mnemitz Oct 25, 2024
6cccbfb
Copy
mnemitz Oct 25, 2024
f87c032
Heading
mnemitz Oct 25, 2024
5965ea7
Admonition
mnemitz Oct 25, 2024
96eff5c
big logo
mnemitz Oct 25, 2024
400ec9f
Add format to batch example
mnemitz Oct 28, 2024
8f8dac1
Make examples private
mnemitz Oct 28, 2024
0f3bfd2
Rename nextjs example
mnemitz Oct 28, 2024
06962f3
Add batch/rt example pages for nextjs
mnemitz Oct 28, 2024
2f7298f
Clean up RT timeouts
mnemitz Oct 28, 2024
40728dc
Bump RT client to beta
mnemitz Oct 28, 2024
40510ba
Update CONTRIBUTING.md
mnemitz Oct 28, 2024
4ef3fe3
Batch + RT versions
mnemitz Oct 28, 2024
1ef2882
Add optional timeout override for transcribe function
mnemitz Oct 28, 2024
689d9a4
Readme for nextjs example
mnemitz Oct 28, 2024
9080233
Expand readme
mnemitz Oct 28, 2024
30b63ed
Header
mnemitz Oct 28, 2024
4edf1b1
Update RT types
mnemitz Oct 28, 2024
e3e821a
Fix message property for audio events
mnemitz Oct 28, 2024
99d1046
Throw errors instead of console warn + early return
mnemitz Oct 28, 2024
259d94e
Rename useFlowOn => useFlowEventListener
mnemitz Oct 28, 2024
27c1a42
Unused code
mnemitz Oct 28, 2024
8e4c8d5
Add build scripts
mnemitz Oct 28, 2024
45d0c23
Publish (dry run) on merge to next branch
mnemitz Oct 28, 2024
e39fd29
Fix action
mnemitz Oct 28, 2024
1026d6e
Update flow client readme
mnemitz Oct 28, 2024
658b596
Update readme
mnemitz Oct 28, 2024
1635246
Comments
mnemitz Oct 29, 2024
7e2c4dd
Remove transcript header
mnemitz Oct 29, 2024
99a9a3b
Update readme for flow react client
mnemitz Oct 29, 2024
fbdef90
Update pnpm installation notes
mnemitz Oct 29, 2024
4a1b8bd
Remove dry-run from publish script
mnemitz Oct 29, 2024
d8649fd
Add package for jwt fetching
mnemitz Oct 28, 2024
5cf8c26
Remove old file
mnemitz Oct 28, 2024
5254dd0
Fix license
mnemitz Oct 29, 2024
db523b0
Merge branch 'main' into jwt-package
mnemitz Nov 1, 2024
fd02025
Add @speechmatics/auth package, update examples
mnemitz Nov 1, 2024
635172b
Merge branch 'main' into jwt-package
mnemitz Nov 6, 2024
0d1109f
Add region
mnemitz Nov 6, 2024
770743c
Merge branch 'main' into jwt-package
mnemitz Nov 6, 2024
3b1130b
Add tests
mnemitz Nov 6, 2024
2335b3c
Add missing env var
mnemitz Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ jobs:
run: "pnpm lint:check && pnpm format:check"
- name: Test
run: pnpm -r test
env:
API_KEY: ${{ secrets.API_KEY }}
1 change: 1 addition & 0 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@speechmatics/flow-client-react": "workspace:*",
"@speechmatics/browser-audio-input-react": "workspace:*",
"@speechmatics/auth": "workspace:*",
"@picocss/pico": "^2.0.6",
"next": "15.0.1",
"react": "19.0.0-rc-69d4b800-20241021",
Expand Down
12 changes: 12 additions & 0 deletions examples/nextjs/src/app/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use server';

import { createSpeechmaticsJWT } from '@speechmatics/auth';

export async function getJWT() {
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('Please set the API_KEY environment variable');
}

return createSpeechmaticsJWT({ type: 'flow', apiKey, ttl: 60 });
}
10 changes: 7 additions & 3 deletions examples/nextjs/src/app/flow/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import { Status } from './Status';
import { ErrorFallback } from '../../lib/components/ErrorFallback';
import { OutputView } from './OutputView';
import { useFlow, useFlowEventListener } from '@speechmatics/flow-client-react';
import { getJWT } from '../actions';

export default function Component({
jwt,
personas,
}: {
jwt: string;
personas: Record<string, { name: string }>;
}) {
const { startConversation, sendAudio, endConversation } = useFlow();
Expand Down Expand Up @@ -47,8 +46,12 @@ export default function Component({
}: { personaId: string; deviceId?: string }) => {
try {
setLoading(true);

const jwt = await getJWT();

const audioContext = new AudioContext({ sampleRate: SAMPLE_RATE });
setAudioContext(audioContext);

await startConversation(jwt, {
config: {
template_id: personaId,
Expand All @@ -60,13 +63,14 @@ export default function Component({
sample_rate: SAMPLE_RATE,
},
});

const mediaStream = await startRecording(audioContext, deviceId);
setMediaStream(mediaStream);
} finally {
setLoading(false);
}
},
[startConversation, jwt, startRecording],
[startConversation, startRecording],
);

const stopSession = useCallback(async () => {
Expand Down
8 changes: 2 additions & 6 deletions examples/nextjs/src/app/flow/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { fetchCredentials } from '@/lib/fetch-credentials';
import Component from './Component';
import { fetchPersonas, FlowProvider } from '@speechmatics/flow-client-react';
import Component from './Component';

export default async function Home() {
// Credentials here are being fetched when rendering the server component.
// You could instead define an API action to request it on the fly.
const creds = await fetchCredentials();
const personas = await fetchPersonas();

return (
<FlowProvider appId="nextjs-example">
<Component jwt={creds.key_value} personas={personas} />
<Component personas={personas} />
</FlowProvider>
);
}
19 changes: 0 additions & 19 deletions examples/nextjs/src/lib/fetch-credentials.ts

This file was deleted.

59 changes: 37 additions & 22 deletions examples/nodejs/batch-example.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* This file showcases the batch-client package being used in NodeJS.
*
* It will connect to the batch API and transcribe a file.
* To run this example, you will need to have a Speechmatics API key,
* which can be generated from the Speechmatics Portal: https://portal.speechmatics.com/api-keys
*
* NOTE: This script is run as an ES Module via tsx, letting us use top-level await.
* The library also works with CommonJS, but the code would need to be wrapped in an async function.
*/
import { BatchClient } from '@speechmatics/batch-client';
import { openAsBlob } from 'node:fs';
import dotenv from 'dotenv';
Expand All @@ -13,26 +23,31 @@ const client = new BatchClient({ apiKey, appId: 'nodeJS-example' });

console.log('Sending file for transcription...');

(async () => {
const blob = await openAsBlob('./example.wav');
const file = new File([blob], 'example.wav');

const response = await client.transcribe(
file,
{
transcription_config: {
language: 'en',
},
const blob = await openAsBlob('./example.wav');
const file = new File([blob], 'example.wav');

const response = await client.transcribe(
// You can pass a File object...
file,
// ...or this:
// { data: blob, fileName: 'example.wav' },
// ...or this:
// {
// url: 'https://github.com/speechmatics/speechmatics-js-sdk/raw/7e0083b830421541091730455f875be2a1984dc6/examples/nodejs/example.wav',
// },
{
transcription_config: {
language: 'en',
},
'json-v2',
);

console.log('Transcription finished!');

console.log(
// Transcripts can be strings when the 'txt' format is chosen
typeof response === 'string'
? response
: response.results.map((r) => r.alternatives?.[0].content).join(' '),
);
})();
},
'json-v2',
);

console.log('Transcription finished!');

console.log(
// Transcripts can be strings when the 'txt' format is chosen
typeof response === 'string'
? response
: response.results.map((r) => r.alternatives?.[0].content).join(' '),
);
10 changes: 4 additions & 6 deletions examples/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
"version": "1.0.0",
"private": "true",
"description": "NodeJS examples showcasing the Speechmatics JS SDK",
"main": "index.js",
"type": "module",
"scripts": {
"run:batch": "tsx batch-example.ts",
"run:real-time-file": "tsx real-time-file-example.ts"
"run:batch": "node --import tsx/esm batch-example.ts",
"run:real-time-file": "node --import tsx/esm real-time-file-example.ts"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@speechmatics/batch-client": "workspace:*",
"@speechmatics/real-time-client": "workspace:*",
"@speechmatics/auth": "workspace:*",
"dotenv": "^16.4.5"
},
"devDependencies": {
"tsx": "^4.19.0"
}
}
83 changes: 38 additions & 45 deletions examples/nodejs/real-time-file-example.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
/**
* This file showcases the real-time-client package being used in NodeJS.
*
* It will connect to the real-time API and transcribe a file in real-time.
* To run this example, you will need to have a Speechmatics API key,
* which can be generated from the Speechmatics Portal: https://portal.speechmatics.com/api-keys
*
* NOTE: This script is run as an ES Module via tsx, letting us use top-level await.
* The library also works with CommonJS, but the code would need to be wrapped in an async function.
*/
import { RealtimeClient } from '@speechmatics/real-time-client';
import fs from 'node:fs';
import path from 'node:path';
import dotenv from 'dotenv';
import { createSpeechmaticsJWT } from '@speechmatics/auth';

dotenv.config();

const client = new RealtimeClient();

async function fetchJWT(): Promise<string> {
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('Please set API_KEY in .env file');
}
const resp = await fetch('https://mp.speechmatics.com/v1/api_keys?type=rt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.API_KEY}`,
},
body: JSON.stringify({
ttl: 3600,
}),
});
if (!resp.ok) {
throw new Error('Bad response from API', { cause: resp });
}
return (await resp.json()).key_value;
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('Please set the API_KEY environment variable');
}

const client = new RealtimeClient();

let finalText = '';

client.addEventListener('receiveMessage', ({ data }) => {
Expand All @@ -46,30 +40,29 @@ client.addEventListener('receiveMessage', ({ data }) => {
}
});

(async () => {
const jwt = await fetchJWT();
const jwt = await createSpeechmaticsJWT({
type: 'rt',
apiKey,
ttl: 60, // 1 minute
});

const fileStream = fs.createReadStream(
path.join(__dirname, './example.wav'),
{
highWaterMark: 4096, //avoid sending faster than realtime
},
);
const fileStream = fs.createReadStream('./example.wav', {
highWaterMark: 4096, //avoid sending faster than realtime
});

await client.start(jwt, {
transcription_config: {
language: 'en',
enable_partials: true,
},
});
await client.start(jwt, {
transcription_config: {
language: 'en',
enable_partials: true,
},
});

//send it
fileStream.on('data', (sample) => {
client.sendAudio(sample);
});
//send it
fileStream.on('data', (sample) => {
client.sendAudio(sample);
});

//end the session
fileStream.on('end', () => {
client.stopRecognition();
});
})();
//end the session
fileStream.on('end', () => {
client.stopRecognition();
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"clean:deps": "rm -rf node_modules && pnpm -r exec rm -rf node_modules",
"clean:builds": "pnpm -r exec rm -rf dist",
"clean": "pnpm clean:deps && pnpm clean:builds",

"format": "biome format --write .",
"lint": "biome lint --write .",

Expand All @@ -30,6 +29,7 @@
"rollup": "^4.21.2",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.1.1",
"tsx": "^4.19.0",
"typescript": "^5.5.4"
}
}
71 changes: 71 additions & 0 deletions packages/auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Speechmatics authentication 🔑

Library for managing authentication with Speechmatics APIs

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
- [Contributing](#contributing)
- [License](#license)

## Installation

```sh
npm i @speechmatics/auth
```

## Usage

```typescript
import { RealtimeClient } from '@speechmatics/real-time-client';
import { createSpeechmaticsJWT } from '@speechmatics/auth';

const client = new RealtimeClient();

client.addEventListener('receiveMessage', ({ data }) => {
// Handle transcription messages
});

async function transcribeFileRealtime () {
const jwt = await createSpeechmaticsJWT({
type: 'rt',
apiKey,
ttl: 60, // 1 minute
});

const fileStream = fs.createReadStream(
path.join(__dirname, './example.wav'),
{
highWaterMark: 4096, //avoid sending faster than realtime
},
);

await client.start(jwt, {
transcription_config: {
language: 'en',
enable_partials: true,
},
});

//send it
fileStream.on('data', (sample) => {
client.sendAudio(sample);
});

//end the session
fileStream.on('end', () => {
client.stopRecognition();
});
}

transcribeFileRealtime();
```

## Contributing

Contributions are welcome! Please open an issue or submit a pull request for any changes.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
Loading