esap-aiui-react 1.0.1 → 1.0.2
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 +10 -205
- package/package.json +1 -1
- package/dist/auuichat.d.ts +0 -57
- package/dist/auuichat.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -6,10 +6,17 @@
|
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://github.com/espai/aiui-react/pulls)
|
|
8
8
|
|
|
9
|
-
**Zero-configuration voice and chat control for React applications through autonomous semantic UI discovery.**
|
|
9
|
+
**Zero-configuration voice and chat control for any React applications through autonomous semantic UI discovery.**
|
|
10
10
|
|
|
11
11
|
AIUI React SDK enables natural language interaction with any React application through both voice commands and text chat, without manual UI annotation or intent mapping. The framework employs real-time DOM observation and semantic element discovery to automatically understand your application's interface, allowing users to control your app through conversational voice or text-based commands.
|
|
12
12
|
|
|
13
|
+
### Enterprise features at a glance
|
|
14
|
+
|
|
15
|
+
- Security: API-key protected WebSocket channels and safety-rule gates
|
|
16
|
+
- Privacy: client-side redaction and sensitive-field filtering
|
|
17
|
+
- Auditability: server-side action logs and context update tracing
|
|
18
|
+
- Deployment: cloud, private VPC, or fully on-prem
|
|
19
|
+
|
|
13
20
|
## Overview
|
|
14
21
|
|
|
15
22
|
Traditional voice control solutions require extensive manual configuration, predefined intent schemas, or explicit UI element annotation. AIUI eliminates this overhead through a novel semantic discovery architecture that automatically maps UI elements to their contextual meaning, enabling immediate voice interaction with zero setup.
|
|
@@ -29,7 +36,7 @@ The SDK implements a **hybrid discovery engine** combining MutationObserver-base
|
|
|
29
36
|
|
|
30
37
|
## Architecture
|
|
31
38
|
|
|
32
|
-

|
|
33
40
|
|
|
34
41
|
### Protocol Design
|
|
35
42
|
|
|
@@ -1026,208 +1033,6 @@ The SDK communicates with a backend server implementing the AIUI protocol. The s
|
|
|
1026
1033
|
}
|
|
1027
1034
|
```
|
|
1028
1035
|
|
|
1029
|
-
### Reference Server Implementation
|
|
1030
|
-
|
|
1031
|
-
```javascript
|
|
1032
|
-
const WebSocket = require('ws');
|
|
1033
|
-
|
|
1034
|
-
// Create WebSocket server with three endpoints
|
|
1035
|
-
const contextServer = new WebSocket.Server({ port: 8080, path: '/context' });
|
|
1036
|
-
const audioServer = new WebSocket.Server({ port: 8080, path: '/audio' });
|
|
1037
|
-
const chatServer = new WebSocket.Server({ port: 8080, path: '/chat' });
|
|
1038
|
-
|
|
1039
|
-
// Session management
|
|
1040
|
-
const sessions = new Map();
|
|
1041
|
-
|
|
1042
|
-
// Context channel handler
|
|
1043
|
-
contextServer.on('connection', (ws, req) => {
|
|
1044
|
-
const url = new URL(req.url, 'ws://localhost');
|
|
1045
|
-
const applicationId = url.searchParams.get('applicationId');
|
|
1046
|
-
const apiKey = url.searchParams.get('apiKey');
|
|
1047
|
-
|
|
1048
|
-
// Validate API key
|
|
1049
|
-
if (!validateApiKey(apiKey)) {
|
|
1050
|
-
ws.close(1008, 'Invalid API key');
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
console.log(`Context connected: ${applicationId}`);
|
|
1055
|
-
|
|
1056
|
-
// Store session
|
|
1057
|
-
if (!sessions.has(applicationId)) {
|
|
1058
|
-
sessions.set(applicationId, {});
|
|
1059
|
-
}
|
|
1060
|
-
sessions.get(applicationId).contextWs = ws;
|
|
1061
|
-
|
|
1062
|
-
ws.on('message', async (data) => {
|
|
1063
|
-
const message = JSON.parse(data.toString());
|
|
1064
|
-
|
|
1065
|
-
if (message.type === 'context_update' || message.type === 'context_append') {
|
|
1066
|
-
// Store UI context for LLM
|
|
1067
|
-
const session = sessions.get(applicationId);
|
|
1068
|
-
session.uiContext = message.context || message.elements;
|
|
1069
|
-
|
|
1070
|
-
console.log(`UI context updated: ${session.uiContext.elements?.length || 0} elements`);
|
|
1071
|
-
}
|
|
1072
|
-
});
|
|
1073
|
-
|
|
1074
|
-
ws.on('close', () => {
|
|
1075
|
-
console.log(`Context disconnected: ${applicationId}`);
|
|
1076
|
-
});
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
// Audio channel handler (for voice mode)
|
|
1080
|
-
audioServer.on('connection', (ws, req) => {
|
|
1081
|
-
const url = new URL(req.url, 'ws://localhost');
|
|
1082
|
-
const applicationId = url.searchParams.get('applicationId');
|
|
1083
|
-
|
|
1084
|
-
console.log(`Audio connected: ${applicationId}`);
|
|
1085
|
-
|
|
1086
|
-
const session = sessions.get(applicationId);
|
|
1087
|
-
if (session) {
|
|
1088
|
-
session.audioWs = ws;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
let audioBuffer = [];
|
|
1092
|
-
|
|
1093
|
-
ws.on('message', async (data) => {
|
|
1094
|
-
if (data instanceof Buffer) {
|
|
1095
|
-
// Accumulate audio chunks
|
|
1096
|
-
audioBuffer.push(data);
|
|
1097
|
-
|
|
1098
|
-
// Process when sufficient audio accumulated (e.g., 1 second)
|
|
1099
|
-
if (audioBuffer.length >= 50) { // 50 * 20ms = 1 second
|
|
1100
|
-
const audioData = Buffer.concat(audioBuffer);
|
|
1101
|
-
audioBuffer = [];
|
|
1102
|
-
|
|
1103
|
-
// Send to Speech-to-Text service
|
|
1104
|
-
const transcript = await speechToText(audioData);
|
|
1105
|
-
|
|
1106
|
-
if (transcript && session?.uiContext) {
|
|
1107
|
-
// Process with LLM
|
|
1108
|
-
const action = await processWithLLM(transcript, session.uiContext);
|
|
1109
|
-
|
|
1110
|
-
// Send action command via context channel
|
|
1111
|
-
session.contextWs?.send(JSON.stringify({
|
|
1112
|
-
type: 'action',
|
|
1113
|
-
action: action.type,
|
|
1114
|
-
params: action.params,
|
|
1115
|
-
timestamp: new Date().toISOString()
|
|
1116
|
-
}));
|
|
1117
|
-
|
|
1118
|
-
// Generate TTS response
|
|
1119
|
-
const audioResponse = await textToSpeech(action.response);
|
|
1120
|
-
|
|
1121
|
-
// Send audio response
|
|
1122
|
-
ws.send(audioResponse);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
});
|
|
1127
|
-
|
|
1128
|
-
ws.on('close', () => {
|
|
1129
|
-
console.log(`Audio disconnected: ${applicationId}`);
|
|
1130
|
-
});
|
|
1131
|
-
});
|
|
1132
|
-
|
|
1133
|
-
// Chat channel handler (for text mode)
|
|
1134
|
-
chatServer.on('connection', (ws, req) => {
|
|
1135
|
-
const url = new URL(req.url, 'ws://localhost');
|
|
1136
|
-
const applicationId = url.searchParams.get('applicationId');
|
|
1137
|
-
|
|
1138
|
-
console.log(`Chat connected: ${applicationId}`);
|
|
1139
|
-
|
|
1140
|
-
const session = sessions.get(applicationId);
|
|
1141
|
-
if (session) {
|
|
1142
|
-
session.chatWs = ws;
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
// Confirm connection
|
|
1146
|
-
ws.send(JSON.stringify({ type: 'chat_connected' }));
|
|
1147
|
-
|
|
1148
|
-
ws.on('message', async (data) => {
|
|
1149
|
-
const message = JSON.parse(data.toString());
|
|
1150
|
-
|
|
1151
|
-
if (message.type === 'chat_message') {
|
|
1152
|
-
const userMessage = message.content;
|
|
1153
|
-
console.log(`Chat message from ${applicationId}: ${userMessage}`);
|
|
1154
|
-
|
|
1155
|
-
// Send typing indicator
|
|
1156
|
-
ws.send(JSON.stringify({ type: 'chat_typing', typing: true }));
|
|
1157
|
-
|
|
1158
|
-
if (session?.uiContext) {
|
|
1159
|
-
// Process with LLM
|
|
1160
|
-
const response = await processWithLLM(userMessage, session.uiContext);
|
|
1161
|
-
|
|
1162
|
-
// Stop typing indicator
|
|
1163
|
-
ws.send(JSON.stringify({ type: 'chat_typing', typing: false }));
|
|
1164
|
-
|
|
1165
|
-
// If action required, send to context channel
|
|
1166
|
-
if (response.action) {
|
|
1167
|
-
session.contextWs?.send(JSON.stringify({
|
|
1168
|
-
type: 'action',
|
|
1169
|
-
action: response.type,
|
|
1170
|
-
params: response.params,
|
|
1171
|
-
timestamp: new Date().toISOString()
|
|
1172
|
-
}));
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// Send chat response
|
|
1176
|
-
ws.send(JSON.stringify({
|
|
1177
|
-
type: 'chat_message',
|
|
1178
|
-
role: 'assistant',
|
|
1179
|
-
content: response.message,
|
|
1180
|
-
timestamp: new Date().toISOString()
|
|
1181
|
-
}));
|
|
1182
|
-
} else {
|
|
1183
|
-
ws.send(JSON.stringify({
|
|
1184
|
-
type: 'chat_message',
|
|
1185
|
-
role: 'assistant',
|
|
1186
|
-
content: 'I don\'t have access to the UI context yet. Please ensure the page is loaded.',
|
|
1187
|
-
timestamp: new Date().toISOString()
|
|
1188
|
-
}));
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
|
|
1193
|
-
ws.on('close', () => {
|
|
1194
|
-
console.log(`Chat disconnected: ${applicationId}`);
|
|
1195
|
-
if (session) {
|
|
1196
|
-
session.chatWs = null;
|
|
1197
|
-
}
|
|
1198
|
-
});
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
async function processWithLLM(userInput, uiContext) {
|
|
1202
|
-
const prompt = `
|
|
1203
|
-
You are controlling a web application via ${typeof userInput === 'string' ? 'chat' : 'voice'}.
|
|
1204
|
-
|
|
1205
|
-
Current UI Context:
|
|
1206
|
-
${JSON.stringify(uiContext, null, 2)}
|
|
1207
|
-
|
|
1208
|
-
User input: "${userInput}"
|
|
1209
|
-
|
|
1210
|
-
Determine if an action is needed and respond in JSON format:
|
|
1211
|
-
{
|
|
1212
|
-
"action": true/false,
|
|
1213
|
-
"type": "click" | "set_value" | "select_from_dropdown" | "navigate",
|
|
1214
|
-
"params": { "semantic": "element description", "value": "..." },
|
|
1215
|
-
"message": "Response to user (what you did or information about the UI)"
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
If the user is just asking for information about the UI, set action to false and provide the information in message.
|
|
1219
|
-
`;
|
|
1220
|
-
|
|
1221
|
-
const response = await openai.chat.completions.create({
|
|
1222
|
-
model: "gpt-4",
|
|
1223
|
-
messages: [{ role: "user", content: prompt }],
|
|
1224
|
-
response_format: { type: "json_object" }
|
|
1225
|
-
});
|
|
1226
|
-
|
|
1227
|
-
return JSON.parse(response.choices[0].message.content);
|
|
1228
|
-
}
|
|
1229
|
-
```
|
|
1230
|
-
|
|
1231
1036
|
### Production Deployment Considerations
|
|
1232
1037
|
|
|
1233
1038
|
**Security:**
|
|
@@ -1481,4 +1286,4 @@ Special thanks to early adopters who provided production feedback and contribute
|
|
|
1481
1286
|
|
|
1482
1287
|
---
|
|
1483
1288
|
|
|
1484
|
-
**Built with precision for voice-first web experiences.**
|
|
1289
|
+
**Built with precision for voice-first web experiences.**
|
package/package.json
CHANGED
package/dist/auuichat.d.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import type { ReactNode } from 'react';
|
|
3
|
-
export interface AIUIConfig {
|
|
4
|
-
applicationId: string;
|
|
5
|
-
serverUrl: string;
|
|
6
|
-
apiKey?: string;
|
|
7
|
-
pages: MinimalPageConfig[];
|
|
8
|
-
safetyRules?: SafetyRules;
|
|
9
|
-
privacy?: PrivacyConfig;
|
|
10
|
-
onNavigate?: (route: string) => void | Promise<void>;
|
|
11
|
-
}
|
|
12
|
-
export interface MinimalPageConfig {
|
|
13
|
-
route: string;
|
|
14
|
-
title?: string;
|
|
15
|
-
safeActions?: string[];
|
|
16
|
-
dangerousActions?: string[];
|
|
17
|
-
}
|
|
18
|
-
export interface SafetyRules {
|
|
19
|
-
requireConfirmation?: string[];
|
|
20
|
-
blockedSelectors?: string[];
|
|
21
|
-
allowedDomains?: string[];
|
|
22
|
-
}
|
|
23
|
-
export interface PrivacyConfig {
|
|
24
|
-
redactPatterns?: string[];
|
|
25
|
-
exposePasswords?: boolean;
|
|
26
|
-
exposeCreditCards?: boolean;
|
|
27
|
-
}
|
|
28
|
-
interface AIUIContextValue {
|
|
29
|
-
config: AIUIConfig;
|
|
30
|
-
isConnected: boolean;
|
|
31
|
-
isListening: boolean;
|
|
32
|
-
currentPage: string | null;
|
|
33
|
-
connect: () => void;
|
|
34
|
-
disconnect: () => void;
|
|
35
|
-
startListening: () => Promise<void>;
|
|
36
|
-
stopListening: () => void;
|
|
37
|
-
sendChatMessage: (message: string) => Promise<void>;
|
|
38
|
-
chatMessages: ChatMessage[];
|
|
39
|
-
isChatConnected: boolean;
|
|
40
|
-
executeAction: (action: string, params: any) => Promise<any>;
|
|
41
|
-
getComponentValue: (selector: string) => any;
|
|
42
|
-
registerComponent: (componentId: string, element: HTMLElement) => void;
|
|
43
|
-
unregisterComponent: (componentId: string) => void;
|
|
44
|
-
}
|
|
45
|
-
export interface ChatMessage {
|
|
46
|
-
role: 'user' | 'assistant';
|
|
47
|
-
content: string;
|
|
48
|
-
timestamp: string;
|
|
49
|
-
}
|
|
50
|
-
export declare const useAIUI: () => AIUIContextValue;
|
|
51
|
-
export interface AIUIProviderProps {
|
|
52
|
-
config: AIUIConfig;
|
|
53
|
-
children: ReactNode;
|
|
54
|
-
}
|
|
55
|
-
export declare const AIUIProvider: React.FC<AIUIProviderProps>;
|
|
56
|
-
export default AIUIProvider;
|
|
57
|
-
//# sourceMappingURL=auuichat.d.ts.map
|
package/dist/auuichat.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auuichat.d.ts","sourceRoot":"","sources":["../src/auuichat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvC,MAAM,WAAW,UAAU;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,gBAAgB;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IAEzB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7D,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7C,iBAAiB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACvE,mBAAmB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACrB;AAID,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC;AAu3DF,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6RpD,CAAC;AA6BF,eAAe,YAAY,CAAC"}
|