@aj-archipelago/cortex 1.3.5 → 1.3.7
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/helper-apps/cortex-autogen/agents.py +31 -2
- package/helper-apps/cortex-realtime-voice-server/.env.sample +6 -0
- package/helper-apps/cortex-realtime-voice-server/README.md +22 -0
- package/helper-apps/cortex-realtime-voice-server/bun.lockb +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/bun.lockb +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/index.html +12 -0
- package/helper-apps/cortex-realtime-voice-server/client/package.json +65 -0
- package/helper-apps/cortex-realtime-voice-server/client/postcss.config.js +6 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/favicon.ico +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/index.html +43 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/logo192.png +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/logo512.png +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/manifest.json +25 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/robots.txt +3 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/sounds/connect.mp3 +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/sounds/disconnect.mp3 +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/App.test.tsx +9 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/App.tsx +126 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/SettingsModal.tsx +207 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/Chat.tsx +553 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubble.tsx +22 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleLeft.tsx +22 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleRight.tsx +21 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessage.tsx +27 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessageInput.tsx +74 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatTile.tsx +211 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/SoundEffects.ts +56 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavPacker.ts +112 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavRecorder.ts +571 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavStreamPlayer.ts +290 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/AudioAnalysis.ts +186 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/constants.ts +59 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/AudioProcessor.ts +214 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/StreamProcessor.ts +183 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/AudioVisualizer.tsx +151 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/CopyButton.tsx +32 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ImageOverlay.tsx +166 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/MicrophoneVisualizer.tsx +95 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ScreenshotCapture.tsx +116 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/hooks/useWindowResize.ts +27 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/utils/audio.ts +33 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/index.css +20 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/index.tsx +19 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/logo.svg +1 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/react-app-env.d.ts +1 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/reportWebVitals.ts +15 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/setupTests.ts +5 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/utils/logger.ts +45 -0
- package/helper-apps/cortex-realtime-voice-server/client/tailwind.config.js +14 -0
- package/helper-apps/cortex-realtime-voice-server/client/tsconfig.json +30 -0
- package/helper-apps/cortex-realtime-voice-server/client/vite.config.ts +22 -0
- package/helper-apps/cortex-realtime-voice-server/index.ts +19 -0
- package/helper-apps/cortex-realtime-voice-server/package.json +28 -0
- package/helper-apps/cortex-realtime-voice-server/src/ApiServer.ts +35 -0
- package/helper-apps/cortex-realtime-voice-server/src/SocketServer.ts +737 -0
- package/helper-apps/cortex-realtime-voice-server/src/Tools.ts +520 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/expert.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/image.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +91 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/reason.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/search.ts +30 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/style.ts +31 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/utils.ts +95 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/vision.ts +34 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/client.ts +499 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/realtimeTypes.ts +279 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/socket.ts +27 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/transcription.ts +75 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/utils.ts +33 -0
- package/helper-apps/cortex-realtime-voice-server/src/utils/logger.ts +45 -0
- package/helper-apps/cortex-realtime-voice-server/src/utils/prompt.ts +81 -0
- package/helper-apps/cortex-realtime-voice-server/tsconfig.json +28 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +3 -1
- package/pathways/system/entity/memory/sys_memory_manager.js +3 -0
- package/pathways/system/entity/memory/sys_memory_update.js +44 -45
- package/pathways/system/entity/memory/sys_read_memory.js +86 -6
- package/pathways/system/entity/memory/sys_search_memory.js +66 -0
- package/pathways/system/entity/shared/sys_entity_constants.js +2 -2
- package/pathways/system/entity/sys_entity_continue.js +2 -1
- package/pathways/system/entity/sys_entity_start.js +10 -0
- package/pathways/system/entity/sys_generator_expert.js +0 -2
- package/pathways/system/entity/sys_generator_memory.js +31 -0
- package/pathways/system/entity/sys_generator_voice_sample.js +36 -0
- package/pathways/system/entity/sys_router_tool.js +13 -10
- package/pathways/system/sys_parse_numbered_object_list.js +1 -1
- package/server/pathwayResolver.js +41 -31
- package/server/plugins/azureVideoTranslatePlugin.js +28 -16
- package/server/plugins/claude3VertexPlugin.js +0 -9
- package/server/plugins/gemini15ChatPlugin.js +18 -5
- package/server/plugins/modelPlugin.js +27 -6
- package/server/plugins/openAiChatPlugin.js +10 -8
- package/server/plugins/openAiVisionPlugin.js +56 -0
- package/tests/memoryfunction.test.js +73 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
type RealtimeEvent = {
|
|
2
|
+
event_id: string,
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
type RealtimeContentResponseEvent = RealtimeEvent & {
|
|
6
|
+
response_id: string,
|
|
7
|
+
item_id: string,
|
|
8
|
+
output_index: number,
|
|
9
|
+
content_index: number,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type RealtimeFunctionResponseEvent = RealtimeEvent & {
|
|
13
|
+
response_id: string,
|
|
14
|
+
item_id: string,
|
|
15
|
+
output_index: number,
|
|
16
|
+
call_id: string,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type AudioFormat = 'pcm16' | 'g711_ulaw' | 'g711_alaw';
|
|
20
|
+
export type AzureVoice = 'amuch' | 'dan' | 'elan' | 'marilyn' | 'meadow' | 'breeze' | 'cove' | 'ember' | 'jupiter' | 'alloy' | 'echo' | 'shimmer';
|
|
21
|
+
export type OpenAIVoice = 'alloy' | 'echo' | 'shimmer' | 'ash' | 'ballad' | 'coral' | 'sage' | 'verse';
|
|
22
|
+
export type Voice = AzureVoice | OpenAIVoice;
|
|
23
|
+
type Modality = 'text' | 'audio';
|
|
24
|
+
type ToolDefinition = {
|
|
25
|
+
type: string,
|
|
26
|
+
name: string,
|
|
27
|
+
description: string,
|
|
28
|
+
parameters: Record<string, any>,
|
|
29
|
+
}
|
|
30
|
+
type ToolChoice = 'auto' | 'none' | 'required' | { type: 'function'; name: string };
|
|
31
|
+
|
|
32
|
+
export type RealtimeResponseConfig = {
|
|
33
|
+
conversation: string,
|
|
34
|
+
metadata: Record<string, any>,
|
|
35
|
+
modalities: Array<Modality>,
|
|
36
|
+
instructions: string,
|
|
37
|
+
voice: Voice,
|
|
38
|
+
output_audio_format: AudioFormat,
|
|
39
|
+
tools: Array<ToolDefinition>,
|
|
40
|
+
tool_choice: ToolChoice,
|
|
41
|
+
temperature: number,
|
|
42
|
+
max_output_tokens: number | 'inf'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type RealtimeSessionConfig = {
|
|
46
|
+
modalities: Array<Modality>,
|
|
47
|
+
instructions: string,
|
|
48
|
+
voice: Voice,
|
|
49
|
+
input_audio_format: AudioFormat,
|
|
50
|
+
output_audio_format: AudioFormat,
|
|
51
|
+
input_audio_transcription: null | { model: 'whisper-1' | (string & {}) },
|
|
52
|
+
turn_detection: null | {
|
|
53
|
+
type: 'server_vad' | 'none',
|
|
54
|
+
threshold?: number,
|
|
55
|
+
prefix_padding_ms?: number,
|
|
56
|
+
silence_duration_ms?: number
|
|
57
|
+
},
|
|
58
|
+
tools: Array<ToolDefinition>,
|
|
59
|
+
tool_choice: ToolChoice,
|
|
60
|
+
temperature: number,
|
|
61
|
+
max_response_output_tokens: number | 'inf'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type RealtimeSession = RealtimeSessionConfig & {
|
|
65
|
+
id: string,
|
|
66
|
+
model: string,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type RealtimeItem = {
|
|
70
|
+
id: string,
|
|
71
|
+
type: 'message' | 'function_call' | 'function_call_output',
|
|
72
|
+
status?: 'in_progress' | 'completed' | 'incomplete',
|
|
73
|
+
role?: 'user' | 'assistant' | 'system',
|
|
74
|
+
content?: Array<{
|
|
75
|
+
type: 'input_text' | 'input_audio' | 'text' | 'audio',
|
|
76
|
+
audio?: string,
|
|
77
|
+
text?: string,
|
|
78
|
+
transcript?: string | null,
|
|
79
|
+
}>,
|
|
80
|
+
call_id?: string,
|
|
81
|
+
name?: string,
|
|
82
|
+
arguments?: string,
|
|
83
|
+
output?: string,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type RealtimeResponse = {
|
|
87
|
+
id: string,
|
|
88
|
+
status: 'in_progress' | 'completed' | 'incomplete' | 'failed',
|
|
89
|
+
status_details: null | {
|
|
90
|
+
type: 'incomplete',
|
|
91
|
+
reason: 'interruption' | 'max_output_tokens' | 'content_filter',
|
|
92
|
+
} | {
|
|
93
|
+
type: 'failed',
|
|
94
|
+
error?: Error | null,
|
|
95
|
+
},
|
|
96
|
+
output: Array<RealtimeItem>,
|
|
97
|
+
usage?: {
|
|
98
|
+
total_tokens: number
|
|
99
|
+
input_tokens: number
|
|
100
|
+
output_tokens: number
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type RealtimeContentPart = {
|
|
105
|
+
type: 'text' | 'audio',
|
|
106
|
+
text?: string,
|
|
107
|
+
audio?: string,
|
|
108
|
+
transcript?: string | null,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type RealtimeErrorEvent = RealtimeEvent & {
|
|
112
|
+
type: 'error',
|
|
113
|
+
error: {
|
|
114
|
+
type: string,
|
|
115
|
+
code: string,
|
|
116
|
+
message: string,
|
|
117
|
+
param: null,
|
|
118
|
+
event_id: string
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type SessionCreatedEvent = RealtimeEvent & {
|
|
123
|
+
type: 'session.created',
|
|
124
|
+
session: RealtimeSession
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type SessionUpdatedEvent = RealtimeEvent & {
|
|
128
|
+
type: 'session.updated',
|
|
129
|
+
session: RealtimeSession
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type ConversationCreatedEvent = RealtimeEvent & {
|
|
133
|
+
type: 'conversation.created',
|
|
134
|
+
conversation: {
|
|
135
|
+
id: string,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type ConversationItemCreatedEvent = RealtimeEvent & {
|
|
140
|
+
type: 'conversation.item.created',
|
|
141
|
+
previous_item_id: string,
|
|
142
|
+
item: RealtimeItem,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type ConversationItemInputAudioTranscriptionCompletedEvent = RealtimeEvent & {
|
|
146
|
+
type: 'conversation.item.input_audio_transcription.completed',
|
|
147
|
+
item_id: string,
|
|
148
|
+
content_index: number,
|
|
149
|
+
transcript: string,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type ConversationItemInputAudioTranscriptionFailedEvent = RealtimeEvent & {
|
|
153
|
+
type: 'conversation.item.input_audio_transcription.failed',
|
|
154
|
+
item_id: string,
|
|
155
|
+
content_index: number,
|
|
156
|
+
error: {
|
|
157
|
+
type: string,
|
|
158
|
+
code: string,
|
|
159
|
+
message: string,
|
|
160
|
+
param: null | string,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type ConversationItemTruncatedEvent = RealtimeEvent & {
|
|
165
|
+
type: 'conversation.item.truncated',
|
|
166
|
+
item_id: string,
|
|
167
|
+
content_index: number,
|
|
168
|
+
audio_end_ms: number,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type ConversationItemDeletedEvent = RealtimeEvent & {
|
|
172
|
+
type: 'conversation.item.deleted',
|
|
173
|
+
item_id: string,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type InputAudioBufferCommittedEvent = RealtimeEvent & {
|
|
177
|
+
type: 'input_audio_buffer.committed',
|
|
178
|
+
previous_item_id: string,
|
|
179
|
+
item_id: string,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export type InputAudioBufferClearedEvent = RealtimeEvent & {
|
|
183
|
+
type: 'input_audio_buffer.cleared',
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type InputAudioBufferSpeechStartedEvent = RealtimeEvent & {
|
|
187
|
+
type: 'input_audio_buffer.speech_started',
|
|
188
|
+
audio_start_ms: number,
|
|
189
|
+
item_id: string,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export type InputAudioBufferSpeechStoppedEvent = RealtimeEvent & {
|
|
193
|
+
type: 'input_audio_buffer.speech_stopped',
|
|
194
|
+
audio_end_ms: number,
|
|
195
|
+
item_id: string,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export type ResponseCreatedEvent = RealtimeEvent & {
|
|
199
|
+
type: 'response.created',
|
|
200
|
+
response: RealtimeResponse
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type ResponseDoneEvent = RealtimeEvent & {
|
|
204
|
+
type: 'response.done',
|
|
205
|
+
response: RealtimeResponse,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type ResponseOutputItemAddedEvent = RealtimeEvent & {
|
|
209
|
+
type: 'response.output_item.added',
|
|
210
|
+
response_id: string,
|
|
211
|
+
output_index: number,
|
|
212
|
+
item: RealtimeItem,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export type ResponseOutputItemDoneEvent = RealtimeEvent & {
|
|
216
|
+
type: 'response.output_item.done',
|
|
217
|
+
response_id: string,
|
|
218
|
+
output_index: number,
|
|
219
|
+
item: RealtimeItem,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export type ResponseContentPartAddedEvent = RealtimeContentResponseEvent & {
|
|
223
|
+
type: 'response.content_part.added',
|
|
224
|
+
part: RealtimeContentPart,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type ResponseContentPartDoneEvent = RealtimeContentResponseEvent & {
|
|
228
|
+
type: 'response.content_part.done',
|
|
229
|
+
part: RealtimeContentPart,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export type ResponseTextDeltaEvent = RealtimeContentResponseEvent & {
|
|
233
|
+
type: 'response.text.delta',
|
|
234
|
+
delta: string,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export type ResponseTextDoneEvent = RealtimeContentResponseEvent & {
|
|
238
|
+
type: 'response.text.done',
|
|
239
|
+
text: string,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type ResponseAudioTranscriptDeltaEvent = RealtimeContentResponseEvent & {
|
|
243
|
+
type: 'response.audio_transcript.delta',
|
|
244
|
+
delta: string,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export type ResponseAudioTranscriptDoneEvent = RealtimeContentResponseEvent & {
|
|
248
|
+
type: 'response.audio_transcript.done',
|
|
249
|
+
transcript: string,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export type ResponseAudioDeltaEvent = RealtimeContentResponseEvent & {
|
|
253
|
+
type: 'response.audio.delta',
|
|
254
|
+
delta: string,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export type ResponseAudioDoneEvent = RealtimeContentResponseEvent & {
|
|
258
|
+
type: 'response.audio.done',
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export type ResponseFunctionCallArgumentsDeltaEvent = RealtimeFunctionResponseEvent & {
|
|
262
|
+
type: 'response.function_call_arguments.delta',
|
|
263
|
+
delta: string,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type ResponseFunctionCallArgumentsDoneEvent = RealtimeFunctionResponseEvent & {
|
|
267
|
+
type: 'response.function_call_arguments.done',
|
|
268
|
+
arguments: string,
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type RateLimitsUpdatedEvent = RealtimeEvent & {
|
|
272
|
+
type: 'rate_limits.updated',
|
|
273
|
+
rate_limits: Array<{
|
|
274
|
+
name: 'requests' | 'tokens' | 'input_tokens' | 'output_tokens' | (string & {})
|
|
275
|
+
limit: number
|
|
276
|
+
remaining: number
|
|
277
|
+
reset_seconds: number
|
|
278
|
+
}>
|
|
279
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {RealtimeItem} from "./realtimeTypes";
|
|
2
|
+
|
|
3
|
+
type DeltaType = {
|
|
4
|
+
transcript?: string;
|
|
5
|
+
audio?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
arguments?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface ServerToClientEvents {
|
|
11
|
+
error: (message: string) => void;
|
|
12
|
+
ready: () => void;
|
|
13
|
+
conversationUpdated: (item: RealtimeItem, delta: DeltaType) => void;
|
|
14
|
+
conversationInterrupted: () => void;
|
|
15
|
+
imageCreated: (imageUrl: string) => void;
|
|
16
|
+
requestScreenshot: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ClientToServerEvents {
|
|
20
|
+
sendMessage: (message: string) => void;
|
|
21
|
+
appendAudio: (audio: string) => void;
|
|
22
|
+
cancelResponse: () => void;
|
|
23
|
+
conversationCompleted: () => void;
|
|
24
|
+
audioPlaybackComplete: (trackId: string) => void;
|
|
25
|
+
screenshotCaptured: (imageData: string) => void;
|
|
26
|
+
screenshotError: (error: string) => void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { RealtimeItem } from './realtimeTypes';
|
|
2
|
+
|
|
3
|
+
export class Transcription {
|
|
4
|
+
private readonly items: Record<string, { realtimeItem: RealtimeItem, previousItemId: string }>;
|
|
5
|
+
private lastItemId: string;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.items = {};
|
|
9
|
+
this.lastItemId = '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public addItem(realtimeItem: RealtimeItem, previousItemId: string): void {
|
|
13
|
+
const itemCopy = this.getItemCopy(realtimeItem);
|
|
14
|
+
this.items[itemCopy.id] = {
|
|
15
|
+
realtimeItem: itemCopy,
|
|
16
|
+
previousItemId,
|
|
17
|
+
};
|
|
18
|
+
this.lastItemId = itemCopy.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public addTranscriptToItem(itemId: string, transcript: string): void {
|
|
22
|
+
const item = this.items[itemId];
|
|
23
|
+
if (item) {
|
|
24
|
+
item.realtimeItem.content = [{
|
|
25
|
+
type: 'input_text',
|
|
26
|
+
text: transcript,
|
|
27
|
+
}];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public updateItem(itemId: string, realtimeItem: RealtimeItem): void {
|
|
32
|
+
const newItem = this.getItemCopy(realtimeItem);
|
|
33
|
+
if (newItem.role === 'assistant' && newItem.content) {
|
|
34
|
+
newItem.content = newItem.content.map((contentPart) => {
|
|
35
|
+
if (contentPart.type === 'audio') {
|
|
36
|
+
return { type: 'text', text: contentPart.transcript || '' };
|
|
37
|
+
}
|
|
38
|
+
return contentPart;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
this.items[itemId] = {
|
|
42
|
+
realtimeItem: newItem,
|
|
43
|
+
previousItemId: this.items[itemId]?.previousItemId || '',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public getItem(id: string): RealtimeItem | undefined {
|
|
48
|
+
return this.items[id]?.realtimeItem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public removeItem(id: string): void {
|
|
52
|
+
delete this.items[id];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public getOrderedItems(): RealtimeItem[] {
|
|
56
|
+
const orderedItems: RealtimeItem[] = [];
|
|
57
|
+
let currentItemId = this.lastItemId;
|
|
58
|
+
while (currentItemId) {
|
|
59
|
+
const item = this.items[currentItemId];
|
|
60
|
+
if (item) {
|
|
61
|
+
orderedItems.push(item.realtimeItem);
|
|
62
|
+
currentItemId = item.previousItemId;
|
|
63
|
+
} else {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return orderedItems.reverse();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
protected getItemCopy(item: RealtimeItem): RealtimeItem {
|
|
71
|
+
const itemCopy: any = structuredClone(item);
|
|
72
|
+
delete itemCopy['object'];
|
|
73
|
+
return itemCopy;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function hasNativeWebSocket(): boolean {
|
|
2
|
+
return !!process.versions.bun || !!globalThis.WebSocket;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function trimDebugEvent(event?: any): any {
|
|
6
|
+
if (!event) return event;
|
|
7
|
+
|
|
8
|
+
const maxLimit = 200;
|
|
9
|
+
const e = structuredClone(event);
|
|
10
|
+
|
|
11
|
+
// if (e.item?.content?.find((c: any) => c.audio)) {
|
|
12
|
+
// e.item.content = e.item.content.map(({ audio, c }: any) => {
|
|
13
|
+
// if (audio) {
|
|
14
|
+
// return {
|
|
15
|
+
// ...c,
|
|
16
|
+
// audio: '(base64 redacted...)',
|
|
17
|
+
// };
|
|
18
|
+
// } else {
|
|
19
|
+
// return c;
|
|
20
|
+
// }
|
|
21
|
+
// });
|
|
22
|
+
// }
|
|
23
|
+
//
|
|
24
|
+
// if (e.audio) {
|
|
25
|
+
// e.audio = '(audio redacted...)';
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
if (e.delta?.length > maxLimit) {
|
|
29
|
+
e.delta = e.delta.slice(0, maxLimit) + '... (truncated)';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return e;
|
|
33
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Logger utility for centralized logging control
|
|
2
|
+
|
|
3
|
+
// Environment-based logging control
|
|
4
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
5
|
+
let isLoggingEnabled = !isProduction;
|
|
6
|
+
|
|
7
|
+
export const logger = {
|
|
8
|
+
enable: () => {
|
|
9
|
+
isLoggingEnabled = true;
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
disable: () => {
|
|
13
|
+
isLoggingEnabled = false;
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
log: (...args: any[]) => {
|
|
17
|
+
if (isLoggingEnabled) {
|
|
18
|
+
console.log(...args);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Additional logging levels if needed
|
|
23
|
+
debug: (...args: any[]) => {
|
|
24
|
+
if (isLoggingEnabled) {
|
|
25
|
+
console.debug(...args);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
error: (...args: any[]) => {
|
|
30
|
+
// Always log errors, even in production
|
|
31
|
+
console.error(...args);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
warn: (...args: any[]) => {
|
|
35
|
+
if (isLoggingEnabled) {
|
|
36
|
+
console.warn(...args);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
info: (...args: any[]) => {
|
|
41
|
+
if (isLoggingEnabled) {
|
|
42
|
+
console.info(...args);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { RealtimeVoiceClient } from "../realtime/client";
|
|
2
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
3
|
+
import { logger } from "./logger";
|
|
4
|
+
|
|
5
|
+
// Time to wait after last user message before allowing AI to speak
|
|
6
|
+
const USER_SPEAKING_THRESHOLD_MS = 1500;
|
|
7
|
+
|
|
8
|
+
export interface SendPromptOptions {
|
|
9
|
+
allowTools?: boolean;
|
|
10
|
+
disposable?: boolean;
|
|
11
|
+
aiResponding?: boolean;
|
|
12
|
+
audioPlaying?: boolean;
|
|
13
|
+
lastUserMessageTime?: number;
|
|
14
|
+
userSpeaking?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function sendPrompt(
|
|
18
|
+
client: RealtimeVoiceClient,
|
|
19
|
+
prompt: string,
|
|
20
|
+
getOptions: (() => SendPromptOptions) | SendPromptOptions
|
|
21
|
+
): Promise<{ skipped: boolean }> {
|
|
22
|
+
const options = typeof getOptions === 'function' ? getOptions() : getOptions;
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
allowTools = false,
|
|
26
|
+
disposable = true,
|
|
27
|
+
aiResponding = false,
|
|
28
|
+
audioPlaying = false,
|
|
29
|
+
lastUserMessageTime = 0,
|
|
30
|
+
userSpeaking = false
|
|
31
|
+
} = options;
|
|
32
|
+
|
|
33
|
+
// Check if user is currently speaking (based on active speaking or recent message)
|
|
34
|
+
const timeSinceLastMessage = Date.now() - lastUserMessageTime;
|
|
35
|
+
const recentlySpoke = timeSinceLastMessage < USER_SPEAKING_THRESHOLD_MS;
|
|
36
|
+
const isUserActive = userSpeaking || recentlySpoke;
|
|
37
|
+
|
|
38
|
+
// Don't send prompt if AI is responding, audio is playing, or user is speaking/recently spoke
|
|
39
|
+
if (aiResponding || audioPlaying || isUserActive) {
|
|
40
|
+
logger.log(`${disposable ? 'Skipping' : 'Queuing'} prompt while ${
|
|
41
|
+
userSpeaking ? 'user is actively speaking' :
|
|
42
|
+
recentlySpoke ? 'user recently finished speaking' :
|
|
43
|
+
aiResponding ? 'AI is responding' :
|
|
44
|
+
'AI audio is playing'
|
|
45
|
+
}`);
|
|
46
|
+
if (!disposable) {
|
|
47
|
+
// Try again after a short delay if the message is important
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
sendPrompt(client, prompt, getOptions).then(resolve);
|
|
51
|
+
}, 1000);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return { skipped: true };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
logger.log('Sending prompt');
|
|
58
|
+
|
|
59
|
+
const promptText = `<INSTRUCTIONS>${prompt}</INSTRUCTIONS>`;
|
|
60
|
+
|
|
61
|
+
client.createConversationItem({
|
|
62
|
+
id: createId(),
|
|
63
|
+
type: 'message',
|
|
64
|
+
role: 'user',
|
|
65
|
+
content: [
|
|
66
|
+
{ type: 'input_text', text: promptText }
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
await this.realtimeClient.createConversationItem({
|
|
72
|
+
id: createId(),
|
|
73
|
+
type: 'function_call_output',
|
|
74
|
+
call_id: call.call_id,
|
|
75
|
+
output: response?.result || '',
|
|
76
|
+
});
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
client.createResponse({ tool_choice: allowTools ? 'auto' : 'none' });
|
|
80
|
+
return { skipped: false };
|
|
81
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"jsxImportSource": "hono/jsx",
|
|
10
|
+
"allowJs": true,
|
|
11
|
+
|
|
12
|
+
// Bundler mode
|
|
13
|
+
"moduleResolution": "node",
|
|
14
|
+
"allowImportingTsExtensions": true,
|
|
15
|
+
"verbatimModuleSyntax": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
|
|
18
|
+
// Best practices
|
|
19
|
+
"strict": true,
|
|
20
|
+
"skipLibCheck": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
|
|
23
|
+
// Some stricter flags (disabled by default)
|
|
24
|
+
"noUnusedLocals": false,
|
|
25
|
+
"noUnusedParameters": false,
|
|
26
|
+
"noPropertyAccessFromIndexSignature": false
|
|
27
|
+
}
|
|
28
|
+
}
|
package/package.json
CHANGED
package/pathways/basePathway.js
CHANGED
|
@@ -21,7 +21,7 @@ export default {
|
|
|
21
21
|
useInputSummarization: false, // true or false - instead of chunking, summarize the input and act on the summary
|
|
22
22
|
truncateFromFront: false, // true or false - if true, truncate from the front of the input instead of the back
|
|
23
23
|
timeout: 120, // seconds, cancels the pathway after this many seconds
|
|
24
|
-
enableDuplicateRequests:
|
|
24
|
+
enableDuplicateRequests: false, // true or false - if true, duplicate requests are sent if the request is not completed after duplicateRequestAfter seconds
|
|
25
25
|
duplicateRequestAfter: 10, // seconds, if the request is not completed after this many seconds, a backup request is sent
|
|
26
26
|
// override the default execution of the pathway
|
|
27
27
|
// callback signature: executeOverride({args: object, runAllPrompts: function})
|
|
@@ -32,5 +32,7 @@ export default {
|
|
|
32
32
|
temperature: 0.9,
|
|
33
33
|
// Require a valid JSON response from the model
|
|
34
34
|
json: false,
|
|
35
|
+
// Manage the token length of the input for the model
|
|
36
|
+
manageTokenLength: true,
|
|
35
37
|
};
|
|
36
38
|
|
|
@@ -36,6 +36,9 @@ export default {
|
|
|
36
36
|
await callPathway('sys_save_memory', { ...args, aiMemory: AI_MEMORY_DEFAULTS });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Update context for the conversation turn
|
|
40
|
+
callPathway('sys_search_memory', { ...args, section: 'memoryAll',updateContext: true });
|
|
41
|
+
|
|
39
42
|
// Check if this conversation turn requires memory updates
|
|
40
43
|
const memoryRequired = await callPathway('sys_memory_required', {
|
|
41
44
|
...args,
|