@hazeljs/realtime 0.2.0-rc.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.
Files changed (45) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +243 -0
  3. package/dist/index.d.ts +22 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +28 -0
  6. package/dist/providers/openai/index.d.ts +5 -0
  7. package/dist/providers/openai/index.d.ts.map +1 -0
  8. package/dist/providers/openai/index.js +7 -0
  9. package/dist/providers/openai/openai-realtime.client.d.ts +74 -0
  10. package/dist/providers/openai/openai-realtime.client.d.ts.map +1 -0
  11. package/dist/providers/openai/openai-realtime.client.js +211 -0
  12. package/dist/providers/openai/openai-realtime.client.test.d.ts +2 -0
  13. package/dist/providers/openai/openai-realtime.client.test.d.ts.map +1 -0
  14. package/dist/providers/openai/openai-realtime.client.test.js +262 -0
  15. package/dist/providers/openai/openai-realtime.session.d.ts +63 -0
  16. package/dist/providers/openai/openai-realtime.session.d.ts.map +1 -0
  17. package/dist/providers/openai/openai-realtime.session.js +95 -0
  18. package/dist/providers/openai/openai-realtime.session.test.d.ts +2 -0
  19. package/dist/providers/openai/openai-realtime.session.test.d.ts.map +1 -0
  20. package/dist/providers/openai/openai-realtime.session.test.js +149 -0
  21. package/dist/realtime-bootstrap.service.d.ts +16 -0
  22. package/dist/realtime-bootstrap.service.d.ts.map +1 -0
  23. package/dist/realtime-bootstrap.service.js +23 -0
  24. package/dist/realtime.gateway.d.ts +45 -0
  25. package/dist/realtime.gateway.d.ts.map +1 -0
  26. package/dist/realtime.gateway.js +94 -0
  27. package/dist/realtime.gateway.test.d.ts +2 -0
  28. package/dist/realtime.gateway.test.d.ts.map +1 -0
  29. package/dist/realtime.gateway.test.js +230 -0
  30. package/dist/realtime.module.d.ts +40 -0
  31. package/dist/realtime.module.d.ts.map +1 -0
  32. package/dist/realtime.module.js +87 -0
  33. package/dist/realtime.service.d.ts +41 -0
  34. package/dist/realtime.service.d.ts.map +1 -0
  35. package/dist/realtime.service.js +81 -0
  36. package/dist/realtime.service.test.d.ts +2 -0
  37. package/dist/realtime.service.test.d.ts.map +1 -0
  38. package/dist/realtime.service.test.js +129 -0
  39. package/dist/realtime.types.d.ts +81 -0
  40. package/dist/realtime.types.d.ts.map +1 -0
  41. package/dist/realtime.types.js +5 -0
  42. package/dist/realtime.types.test.d.ts +2 -0
  43. package/dist/realtime.types.test.d.ts.map +1 -0
  44. package/dist/realtime.types.test.js +61 -0
  45. package/package.json +58 -0
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * RealtimeModule - Real-time voice AI for HazelJS
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var RealtimeModule_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.RealtimeModule = void 0;
14
+ const core_1 = require("@hazeljs/core");
15
+ const realtime_service_1 = require("./realtime.service");
16
+ const realtime_gateway_1 = require("./realtime.gateway");
17
+ const realtime_bootstrap_service_1 = require("./realtime-bootstrap.service");
18
+ let RealtimeModule = RealtimeModule_1 = class RealtimeModule {
19
+ /**
20
+ * Configure Realtime module with OpenAI Realtime API
21
+ */
22
+ static forRoot(options = {}) {
23
+ const realtimeService = new realtime_service_1.RealtimeService({
24
+ defaultProvider: options.defaultProvider ?? 'openai',
25
+ openaiApiKey: options.openaiApiKey ?? process.env.OPENAI_API_KEY,
26
+ defaultSessionConfig: options.defaultSessionConfig,
27
+ });
28
+ const realtimeGateway = new realtime_gateway_1.RealtimeGateway(realtimeService, {
29
+ path: options.path ?? '/realtime',
30
+ });
31
+ return {
32
+ module: RealtimeModule_1,
33
+ providers: [
34
+ { provide: realtime_service_1.RealtimeService, useValue: realtimeService },
35
+ { provide: realtime_gateway_1.RealtimeGateway, useValue: realtimeGateway },
36
+ {
37
+ provide: realtime_bootstrap_service_1.RealtimeBootstrapService,
38
+ useClass: realtime_bootstrap_service_1.RealtimeBootstrapService,
39
+ },
40
+ ],
41
+ exports: [realtime_service_1.RealtimeService, realtime_gateway_1.RealtimeGateway],
42
+ global: true,
43
+ };
44
+ }
45
+ /**
46
+ * Configure Realtime module asynchronously (e.g. from config service)
47
+ */
48
+ static forRootAsync(options) {
49
+ return {
50
+ module: RealtimeModule_1,
51
+ providers: [
52
+ {
53
+ provide: 'REALTIME_OPTIONS',
54
+ useFactory: options.useFactory,
55
+ inject: options.inject ?? [],
56
+ },
57
+ {
58
+ provide: realtime_service_1.RealtimeService,
59
+ useFactory: (opts) => new realtime_service_1.RealtimeService({
60
+ defaultProvider: opts.defaultProvider ?? 'openai',
61
+ openaiApiKey: opts.openaiApiKey ?? process.env.OPENAI_API_KEY,
62
+ defaultSessionConfig: opts.defaultSessionConfig,
63
+ }),
64
+ inject: ['REALTIME_OPTIONS'],
65
+ },
66
+ {
67
+ provide: realtime_gateway_1.RealtimeGateway,
68
+ useFactory: (service, opts) => new realtime_gateway_1.RealtimeGateway(service, { path: opts.path ?? '/realtime' }),
69
+ inject: [realtime_service_1.RealtimeService, 'REALTIME_OPTIONS'],
70
+ },
71
+ {
72
+ provide: realtime_bootstrap_service_1.RealtimeBootstrapService,
73
+ useClass: realtime_bootstrap_service_1.RealtimeBootstrapService,
74
+ },
75
+ ],
76
+ exports: [realtime_service_1.RealtimeService, realtime_gateway_1.RealtimeGateway],
77
+ global: true,
78
+ };
79
+ }
80
+ };
81
+ exports.RealtimeModule = RealtimeModule;
82
+ exports.RealtimeModule = RealtimeModule = RealtimeModule_1 = __decorate([
83
+ (0, core_1.HazelModule)({
84
+ providers: [],
85
+ exports: [],
86
+ })
87
+ ], RealtimeModule);
@@ -0,0 +1,41 @@
1
+ /**
2
+ * RealtimeService - manages realtime voice sessions and provider config
3
+ */
4
+ import { OpenAIRealtimeSession, type RealtimeClientAdapter } from './providers/openai';
5
+ import type { RealtimeProvider, RealtimeSessionConfig } from './realtime.types';
6
+ export interface RealtimeServiceOptions {
7
+ defaultProvider?: RealtimeProvider;
8
+ openaiApiKey?: string;
9
+ defaultSessionConfig?: RealtimeSessionConfig;
10
+ }
11
+ export declare class RealtimeService {
12
+ private readonly options;
13
+ private sessions;
14
+ constructor(options?: RealtimeServiceOptions);
15
+ /**
16
+ * Create and connect an OpenAI Realtime session for a client
17
+ */
18
+ createOpenAISession(client: RealtimeClientAdapter, overrides?: {
19
+ model?: string;
20
+ sessionConfig?: RealtimeSessionConfig;
21
+ }): Promise<OpenAIRealtimeSession>;
22
+ /**
23
+ * Get session by client ID
24
+ */
25
+ getSession(clientId: string): OpenAIRealtimeSession | undefined;
26
+ /**
27
+ * Remove and disconnect session
28
+ */
29
+ removeSession(clientId: string): void;
30
+ /**
31
+ * Get all active session stats
32
+ */
33
+ getStats(): Array<{
34
+ clientId: string;
35
+ } & ReturnType<OpenAIRealtimeSession['getStats']>>;
36
+ /**
37
+ * Get session count
38
+ */
39
+ getSessionCount(): number;
40
+ }
41
+ //# sourceMappingURL=realtime.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.service.d.ts","sourceRoot":"","sources":["../src/realtime.service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,MAAM,WAAW,sBAAsB;IACrC,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAED,qBACa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAA4C;gBAEhD,OAAO,GAAE,sBAA2B;IAShD;;OAEG;IACG,mBAAmB,CACvB,MAAM,EAAE,qBAAqB,EAC7B,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,qBAAqB,CAAA;KAAE,GACpE,OAAO,CAAC,qBAAqB,CAAC;IAmBjC;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS;IAI/D;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQrC;;OAEG;IACH,QAAQ,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAC;IAOvF;;OAEG;IACH,eAAe,IAAI,MAAM;CAG1B"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * RealtimeService - manages realtime voice sessions and provider config
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var __metadata = (this && this.__metadata) || function (k, v) {
12
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RealtimeService = void 0;
16
+ const core_1 = require("@hazeljs/core");
17
+ const openai_1 = require("./providers/openai");
18
+ let RealtimeService = class RealtimeService {
19
+ constructor(options = {}) {
20
+ this.sessions = new Map();
21
+ this.options = {
22
+ defaultProvider: options.defaultProvider ?? 'openai',
23
+ openaiApiKey: options.openaiApiKey ?? process.env.OPENAI_API_KEY,
24
+ defaultSessionConfig: options.defaultSessionConfig,
25
+ ...options,
26
+ };
27
+ }
28
+ /**
29
+ * Create and connect an OpenAI Realtime session for a client
30
+ */
31
+ async createOpenAISession(client, overrides) {
32
+ const apiKey = this.options.openaiApiKey;
33
+ if (!apiKey) {
34
+ throw new Error('OPENAI_API_KEY is required for OpenAI Realtime. Set env or openaiApiKey in RealtimeModule.');
35
+ }
36
+ const session = new openai_1.OpenAIRealtimeSession(client, {
37
+ apiKey,
38
+ model: overrides?.model,
39
+ sessionConfig: overrides?.sessionConfig ?? this.options.defaultSessionConfig,
40
+ });
41
+ await session.connect();
42
+ this.sessions.set(client.id, session);
43
+ return session;
44
+ }
45
+ /**
46
+ * Get session by client ID
47
+ */
48
+ getSession(clientId) {
49
+ return this.sessions.get(clientId);
50
+ }
51
+ /**
52
+ * Remove and disconnect session
53
+ */
54
+ removeSession(clientId) {
55
+ const session = this.sessions.get(clientId);
56
+ if (session) {
57
+ session.disconnect();
58
+ this.sessions.delete(clientId);
59
+ }
60
+ }
61
+ /**
62
+ * Get all active session stats
63
+ */
64
+ getStats() {
65
+ return Array.from(this.sessions.entries()).map(([clientId, session]) => ({
66
+ clientId,
67
+ ...session.getStats(),
68
+ }));
69
+ }
70
+ /**
71
+ * Get session count
72
+ */
73
+ getSessionCount() {
74
+ return this.sessions.size;
75
+ }
76
+ };
77
+ exports.RealtimeService = RealtimeService;
78
+ exports.RealtimeService = RealtimeService = __decorate([
79
+ (0, core_1.Service)(),
80
+ __metadata("design:paramtypes", [Object])
81
+ ], RealtimeService);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=realtime.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.service.test.d.ts","sourceRoot":"","sources":["../src/realtime.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const realtime_service_1 = require("./realtime.service");
4
+ const openai_1 = require("./providers/openai");
5
+ jest.mock('./providers/openai', () => ({
6
+ OpenAIRealtimeSession: jest.fn().mockImplementation((client, _options) => ({
7
+ connect: jest.fn().mockResolvedValue(undefined),
8
+ disconnect: jest.fn(),
9
+ handleClientMessage: jest.fn(),
10
+ getStats: jest.fn().mockReturnValue({
11
+ sessionId: client.id,
12
+ provider: 'openai',
13
+ connectedAt: Date.now(),
14
+ audioChunksReceived: 0,
15
+ audioChunksSent: 0,
16
+ eventsReceived: 0,
17
+ eventsSent: 0,
18
+ }),
19
+ isConnected: true,
20
+ })),
21
+ }));
22
+ describe('RealtimeService', () => {
23
+ const mockClient = {
24
+ id: 'client-1',
25
+ send: jest.fn(),
26
+ };
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ process.env.OPENAI_API_KEY = 'test-key';
30
+ });
31
+ afterEach(() => {
32
+ delete process.env.OPENAI_API_KEY;
33
+ });
34
+ describe('constructor', () => {
35
+ it('should use default provider openai', () => {
36
+ const service = new realtime_service_1.RealtimeService({});
37
+ expect(service.getSessionCount()).toBe(0);
38
+ });
39
+ it('should use provided options', () => {
40
+ const service = new realtime_service_1.RealtimeService({
41
+ defaultProvider: 'openai',
42
+ openaiApiKey: 'custom-key',
43
+ });
44
+ expect(service.getSessionCount()).toBe(0);
45
+ });
46
+ });
47
+ describe('createOpenAISession', () => {
48
+ it('should create and return a session', async () => {
49
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
50
+ const session = await service.createOpenAISession(mockClient);
51
+ expect(session).toBeDefined();
52
+ expect(service.getSession('client-1')).toBe(session);
53
+ expect(service.getSessionCount()).toBe(1);
54
+ });
55
+ it('should throw when OPENAI_API_KEY is missing', async () => {
56
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: undefined });
57
+ delete process.env.OPENAI_API_KEY;
58
+ await expect(service.createOpenAISession(mockClient)).rejects.toThrow('OPENAI_API_KEY is required for OpenAI Realtime');
59
+ });
60
+ it('should pass overrides to session', async () => {
61
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
62
+ await service.createOpenAISession(mockClient, {
63
+ model: 'gpt-4o',
64
+ sessionConfig: { instructions: 'Custom instructions' },
65
+ });
66
+ expect(openai_1.OpenAIRealtimeSession).toHaveBeenCalledWith(mockClient, expect.objectContaining({
67
+ apiKey: 'test-key',
68
+ model: 'gpt-4o',
69
+ sessionConfig: { instructions: 'Custom instructions' },
70
+ }));
71
+ });
72
+ });
73
+ describe('getSession', () => {
74
+ it('should return session when exists', async () => {
75
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
76
+ const session = await service.createOpenAISession(mockClient);
77
+ expect(service.getSession('client-1')).toBe(session);
78
+ });
79
+ it('should return undefined when session does not exist', () => {
80
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
81
+ expect(service.getSession('non-existent')).toBeUndefined();
82
+ });
83
+ });
84
+ describe('removeSession', () => {
85
+ it('should remove and disconnect session', async () => {
86
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
87
+ const session = await service.createOpenAISession(mockClient);
88
+ const disconnectSpy = jest.spyOn(session, 'disconnect');
89
+ service.removeSession('client-1');
90
+ expect(disconnectSpy).toHaveBeenCalled();
91
+ expect(service.getSession('client-1')).toBeUndefined();
92
+ expect(service.getSessionCount()).toBe(0);
93
+ });
94
+ it('should do nothing when session does not exist', () => {
95
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
96
+ expect(() => service.removeSession('non-existent')).not.toThrow();
97
+ });
98
+ });
99
+ describe('getStats', () => {
100
+ it('should return stats for all sessions', async () => {
101
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
102
+ await service.createOpenAISession(mockClient);
103
+ const stats = service.getStats();
104
+ expect(stats).toHaveLength(1);
105
+ expect(stats[0]).toMatchObject({
106
+ clientId: 'client-1',
107
+ sessionId: 'client-1',
108
+ provider: 'openai',
109
+ });
110
+ });
111
+ it('should return empty array when no sessions', () => {
112
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
113
+ expect(service.getStats()).toEqual([]);
114
+ });
115
+ });
116
+ describe('getSessionCount', () => {
117
+ it('should return correct count', async () => {
118
+ const service = new realtime_service_1.RealtimeService({ openaiApiKey: 'test-key' });
119
+ expect(service.getSessionCount()).toBe(0);
120
+ await service.createOpenAISession(mockClient);
121
+ expect(service.getSessionCount()).toBe(1);
122
+ const mockClient2 = { id: 'client-2', send: jest.fn() };
123
+ await service.createOpenAISession(mockClient2);
124
+ expect(service.getSessionCount()).toBe(2);
125
+ service.removeSession('client-1');
126
+ expect(service.getSessionCount()).toBe(1);
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @hazeljs/realtime - Shared types for real-time voice AI
3
+ */
4
+ /**
5
+ * Supported real-time voice providers
6
+ */
7
+ export type RealtimeProvider = 'openai' | 'gemini';
8
+ /**
9
+ * OpenAI Realtime voice options
10
+ */
11
+ export type OpenAIVoice = 'alloy' | 'ash' | 'ballad' | 'coral' | 'echo' | 'sage' | 'shimmer' | 'verse' | 'marin' | 'cedar';
12
+ /**
13
+ * Audio format for real-time sessions
14
+ */
15
+ export interface RealtimeAudioFormat {
16
+ type: 'audio/pcm' | 'audio/pcmu' | 'audio/pcma';
17
+ rate?: 8000 | 16000 | 24000;
18
+ }
19
+ /**
20
+ * Session configuration for real-time voice
21
+ */
22
+ export interface RealtimeSessionConfig {
23
+ /** System instructions for the model */
24
+ instructions?: string;
25
+ /** Voice for audio output (OpenAI) */
26
+ voice?: OpenAIVoice;
27
+ /** Output modalities: audio, text, or both */
28
+ outputModalities?: ('audio' | 'text')[];
29
+ /** Audio input format */
30
+ inputFormat?: RealtimeAudioFormat;
31
+ /** Audio output format */
32
+ outputFormat?: RealtimeAudioFormat;
33
+ /** Enable turn detection (VAD) - when disabled, use manual commit */
34
+ turnDetection?: boolean;
35
+ /** Model to use (OpenAI: gpt-realtime) */
36
+ model?: string;
37
+ }
38
+ /**
39
+ * OpenAI Realtime API client event (client → server)
40
+ */
41
+ export interface RealtimeClientEvent {
42
+ type: string;
43
+ [key: string]: unknown;
44
+ }
45
+ /**
46
+ * OpenAI Realtime API server event (server → client)
47
+ */
48
+ export interface RealtimeServerEvent {
49
+ type: string;
50
+ [key: string]: unknown;
51
+ }
52
+ /**
53
+ * Realtime session lifecycle events
54
+ */
55
+ export type RealtimeSessionEvent = 'session.created' | 'session.updated' | 'session.ended' | 'error' | 'response.output_audio.delta' | 'response.output_audio.done' | 'response.output_text.delta' | 'response.output_text.done' | 'response.done' | 'input_audio_buffer.speech_started' | 'input_audio_buffer.speech_stopped';
56
+ /**
57
+ * Realtime module options
58
+ */
59
+ export interface RealtimeModuleOptions {
60
+ /** Default provider */
61
+ defaultProvider?: RealtimeProvider;
62
+ /** OpenAI API key (or use OPENAI_API_KEY env) */
63
+ openaiApiKey?: string;
64
+ /** WebSocket path for realtime endpoint */
65
+ path?: string;
66
+ /** Default session config */
67
+ defaultSessionConfig?: RealtimeSessionConfig;
68
+ }
69
+ /**
70
+ * Realtime session stats
71
+ */
72
+ export interface RealtimeSessionStats {
73
+ sessionId: string;
74
+ provider: RealtimeProvider;
75
+ connectedAt: number;
76
+ audioChunksReceived: number;
77
+ audioChunksSent: number;
78
+ eventsReceived: number;
79
+ eventsSent: number;
80
+ }
81
+ //# sourceMappingURL=realtime.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.types.d.ts","sourceRoot":"","sources":["../src/realtime.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnD;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,KAAK,GACL,QAAQ,GACR,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IAChD,IAAI,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,CAAC,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;IACxC,yBAAyB;IACzB,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,0BAA0B;IAC1B,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,OAAO,GACP,6BAA6B,GAC7B,4BAA4B,GAC5B,4BAA4B,GAC5B,2BAA2B,GAC3B,eAAe,GACf,mCAAmC,GACnC,mCAAmC,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * @hazeljs/realtime - Shared types for real-time voice AI
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=realtime.types.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.types.test.d.ts","sourceRoot":"","sources":["../src/realtime.types.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ describe('realtime.types', () => {
4
+ describe('RealtimeProvider', () => {
5
+ it('should allow openai and gemini', () => {
6
+ const providers = ['openai', 'gemini'];
7
+ expect(providers).toContain('openai');
8
+ expect(providers).toContain('gemini');
9
+ });
10
+ });
11
+ describe('RealtimeSessionConfig', () => {
12
+ it('should accept valid config', () => {
13
+ const config = {
14
+ instructions: 'Be helpful',
15
+ voice: 'marin',
16
+ outputModalities: ['audio', 'text'],
17
+ turnDetection: true,
18
+ };
19
+ expect(config.instructions).toBe('Be helpful');
20
+ expect(config.voice).toBe('marin');
21
+ });
22
+ });
23
+ describe('RealtimeModuleOptions', () => {
24
+ it('should accept valid options', () => {
25
+ const options = {
26
+ defaultProvider: 'openai',
27
+ openaiApiKey: 'sk-test',
28
+ path: '/realtime',
29
+ defaultSessionConfig: { instructions: 'Test' },
30
+ };
31
+ expect(options.path).toBe('/realtime');
32
+ });
33
+ });
34
+ describe('OpenAIVoice', () => {
35
+ it('should include standard voices', () => {
36
+ const voices = [
37
+ 'alloy',
38
+ 'ash',
39
+ 'ballad',
40
+ 'coral',
41
+ 'echo',
42
+ 'sage',
43
+ 'shimmer',
44
+ 'verse',
45
+ 'marin',
46
+ 'cedar',
47
+ ];
48
+ expect(voices).toHaveLength(10);
49
+ });
50
+ });
51
+ describe('RealtimeAudioFormat', () => {
52
+ it('should accept PCM formats', () => {
53
+ const format = {
54
+ type: 'audio/pcm',
55
+ rate: 24000,
56
+ };
57
+ expect(format.type).toBe('audio/pcm');
58
+ expect(format.rate).toBe(24000);
59
+ });
60
+ });
61
+ });
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@hazeljs/realtime",
3
+ "version": "0.2.0-rc.3",
4
+ "description": "Real-time voice AI for HazelJS - OpenAI Realtime API & Gemini Live integration for low-latency speech-to-speech",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "jest --coverage --passWithNoTests",
13
+ "lint": "eslint \"src/**/*.ts\"",
14
+ "lint:fix": "eslint \"src/**/*.ts\" --fix",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@hazeljs/websocket": "^0.2.0-rc.3"
19
+ },
20
+ "peerDependencies": {
21
+ "@hazeljs/core": ">=0.2.0-beta.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.17.50",
25
+ "@types/ws": "^8.5.13",
26
+ "@typescript-eslint/eslint-plugin": "^8.18.2",
27
+ "@typescript-eslint/parser": "^8.18.2",
28
+ "eslint": "^8.56.0",
29
+ "jest": "^29.7.0",
30
+ "ts-jest": "^29.1.2",
31
+ "typescript": "^5.3.3"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/hazel-js/hazeljs.git",
39
+ "directory": "packages/realtime"
40
+ },
41
+ "keywords": [
42
+ "hazeljs",
43
+ "realtime",
44
+ "voice",
45
+ "speech-to-speech",
46
+ "openai",
47
+ "realtime-api",
48
+ "ai",
49
+ "websocket"
50
+ ],
51
+ "author": "Muhammad Arslan <muhammad.arslan@hazeljs.ai>",
52
+ "license": "Apache-2.0",
53
+ "bugs": {
54
+ "url": "https://github.com/hazeljs/hazel-js/issues"
55
+ },
56
+ "homepage": "https://hazeljs.com",
57
+ "gitHead": "2e173141ee8e35a9db7f3975bbbc78216ac850f7"
58
+ }