@5minds/node-red-dashboard-2-processcube-chat 0.1.1-develop-e94793-mcvyrm7g → 0.1.1-develop-cb2322-mf120t2z
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/nodes/ui-deepchat.html +87 -102
- package/nodes/ui-deepchat.js +13 -126
- package/package.json +1 -1
- package/resources/ui-deepchat.umd.js +61 -61
- package/resources/ui-deepchat.umd.js.map +1 -1
- package/ui/components/UIDeepChat.vue +322 -290
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<deep-chat
|
|
4
|
-
ref="deepChat"
|
|
2
|
+
<div class="deep-chat-container">
|
|
3
|
+
<deep-chat
|
|
5
4
|
:style="deepChatStyle"
|
|
6
|
-
:
|
|
7
|
-
:
|
|
8
|
-
:speech-to-text="speechToTextConfig"
|
|
9
|
-
:camera="cameraConfig"
|
|
10
|
-
:microphone="microphoneConfig"
|
|
11
|
-
:mixed-files="attachmentsConfig"
|
|
12
|
-
:avatars="config.avatars"
|
|
13
|
-
:names="config.names"
|
|
14
|
-
:timestamps="config.timestamps"
|
|
15
|
-
:stream="config.stream"
|
|
5
|
+
:textInput="textInputConfig"
|
|
6
|
+
:introMessage="introMessageConfig"
|
|
16
7
|
:connect="connectConfig"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
:speechToText="props.speechToText"
|
|
9
|
+
:camera="props.camera"
|
|
10
|
+
:mixedFiles="props.attachments"
|
|
11
|
+
:avatars="props.avatars"
|
|
12
|
+
:names="props.names"
|
|
13
|
+
:timestamps="props.timestamps"
|
|
14
|
+
:stream="props.stream"
|
|
22
15
|
></deep-chat>
|
|
23
16
|
</div>
|
|
24
17
|
</template>
|
|
25
18
|
|
|
26
19
|
<script>
|
|
27
|
-
import 'deep-chat'
|
|
20
|
+
import 'deep-chat';
|
|
28
21
|
|
|
29
22
|
export default {
|
|
30
23
|
name: 'UIDeepChat',
|
|
@@ -32,311 +25,350 @@ export default {
|
|
|
32
25
|
inject: ['$socket'],
|
|
33
26
|
data() {
|
|
34
27
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
placeholder: 'Type a message...',
|
|
38
|
-
apiUrl: '',
|
|
39
|
-
apiKey: '',
|
|
40
|
-
model: 'gpt-3.5-turbo',
|
|
41
|
-
textInput: true,
|
|
42
|
-
speechToText: false,
|
|
43
|
-
camera: false,
|
|
44
|
-
microphone: false,
|
|
45
|
-
attachments: false,
|
|
46
|
-
avatars: true,
|
|
47
|
-
names: true,
|
|
48
|
-
timestamps: false,
|
|
49
|
-
stream: false
|
|
50
|
-
},
|
|
51
|
-
messages: []
|
|
52
|
-
}
|
|
28
|
+
conversation: [],
|
|
29
|
+
};
|
|
53
30
|
},
|
|
54
|
-
|
|
31
|
+
|
|
55
32
|
computed: {
|
|
56
|
-
|
|
33
|
+
deepChatStyle() {
|
|
57
34
|
return {
|
|
58
35
|
width: '100%',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
36
|
+
maxWidth: '600px',
|
|
37
|
+
height: '80vh',
|
|
38
|
+
borderRadius: '8px',
|
|
39
|
+
};
|
|
62
40
|
},
|
|
63
41
|
|
|
64
|
-
deepChatStyle() {
|
|
65
|
-
return {
|
|
66
|
-
width: '300px',
|
|
67
|
-
height: '300px',
|
|
68
|
-
flex: '1',
|
|
69
|
-
border: 'none',
|
|
70
|
-
borderRadius: '8px'
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
speechToTextConfig() {
|
|
75
|
-
return this.config.speechToText ? {
|
|
76
|
-
button: true,
|
|
77
|
-
displayInterimResults: true
|
|
78
|
-
} : false
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
cameraConfig() {
|
|
82
|
-
return this.config.camera ? {
|
|
83
|
-
button: true
|
|
84
|
-
} : false
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
microphoneConfig() {
|
|
88
|
-
return this.config.microphone ? {
|
|
89
|
-
button: true,
|
|
90
|
-
audio: true
|
|
91
|
-
} : false
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
attachmentsConfig() {
|
|
95
|
-
return this.config.attachments ? {
|
|
96
|
-
button: true,
|
|
97
|
-
acceptedFormats: ".jpeg,.jpg,.png,.gif,.pdf,.txt,.doc,.docx"
|
|
98
|
-
} : false
|
|
99
|
-
},
|
|
100
|
-
|
|
101
42
|
textInputConfig() {
|
|
102
|
-
if (!this.config.textInput) return false
|
|
103
|
-
|
|
104
43
|
return {
|
|
105
44
|
placeholder: {
|
|
106
|
-
text: this.
|
|
107
|
-
}
|
|
108
|
-
}
|
|
45
|
+
text: this.props.placeholder || 'Type a message...',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
109
48
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
} : false
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
cameraConfig() {
|
|
119
|
-
return this.config.camera ? {
|
|
120
|
-
button: true
|
|
121
|
-
} : false
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
microphoneConfig() {
|
|
125
|
-
return this.config.microphone ? {
|
|
126
|
-
button: true,
|
|
127
|
-
audio: true
|
|
128
|
-
} : false
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
attachmentsConfig() {
|
|
132
|
-
return this.config.attachments ? {
|
|
133
|
-
button: true,
|
|
134
|
-
acceptedFormats: ".jpeg,.jpg,.png,.gif,.pdf,.txt,.doc,.docx"
|
|
135
|
-
} : false
|
|
49
|
+
|
|
50
|
+
introMessageConfig() {
|
|
51
|
+
return {
|
|
52
|
+
text: this.props.introMessage || 'Hello! How can I help you today?',
|
|
53
|
+
};
|
|
136
54
|
},
|
|
137
|
-
|
|
55
|
+
|
|
138
56
|
connectConfig() {
|
|
139
|
-
if (this.config.apiUrl && this.config.apiKey) {
|
|
140
|
-
return {
|
|
141
|
-
url: this.config.apiUrl,
|
|
142
|
-
method: 'POST',
|
|
143
|
-
headers: {
|
|
144
|
-
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
145
|
-
'Content-Type': 'application/json'
|
|
146
|
-
},
|
|
147
|
-
body: {
|
|
148
|
-
model: this.config.model,
|
|
149
|
-
max_tokens: 2000,
|
|
150
|
-
temperature: 0.7
|
|
151
|
-
},
|
|
152
|
-
stream: this.config.stream
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Custom Handler für Node-RED Integration
|
|
157
57
|
return {
|
|
158
|
-
handler: this.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
mounted() {
|
|
165
|
-
this.setupSocketListeners()
|
|
166
|
-
this.applyConfiguration()
|
|
58
|
+
handler: this.handleConnection,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
167
61
|
},
|
|
168
|
-
|
|
169
62
|
beforeUnmount() {
|
|
170
|
-
this.
|
|
63
|
+
this.$socket.off('msg-input:' + this.id);
|
|
171
64
|
},
|
|
172
|
-
|
|
65
|
+
|
|
173
66
|
methods: {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.$socket.on('msg-input:' + this.id, (msg) => {
|
|
193
|
-
if (msg.payload) {
|
|
194
|
-
this.addMessage(msg.payload, msg.role || 'ai', msg.html, msg.files)
|
|
195
|
-
}
|
|
196
|
-
if (msg.config) {
|
|
197
|
-
this.config = { ...this.config, ...msg.config }
|
|
198
|
-
this.updateConfiguration()
|
|
199
|
-
}
|
|
200
|
-
if (msg.clear) {
|
|
201
|
-
this.clearMessages()
|
|
202
|
-
}
|
|
203
|
-
})
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
removeSocketListeners() {
|
|
207
|
-
this.$socket.off('deepchat-newMessage:' + this.id)
|
|
208
|
-
this.$socket.off('deepchat-updateConfig:' + this.id)
|
|
209
|
-
this.$socket.off('deepchat-clearMessages:' + this.id)
|
|
210
|
-
this.$socket.off('msg-input:' + this.id)
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
applyConfiguration() {
|
|
214
|
-
// Konfiguration aus props übernehmen
|
|
215
|
-
if (this.props) {
|
|
216
|
-
Object.keys(this.props).forEach(key => {
|
|
217
|
-
if (this.config.hasOwnProperty(key)) {
|
|
218
|
-
this.config[key] = this.props[key]
|
|
67
|
+
async handleConnection(body, signals) {
|
|
68
|
+
try {
|
|
69
|
+
// Extract messages and files from FormData
|
|
70
|
+
let newMessages = [];
|
|
71
|
+
let files = [];
|
|
72
|
+
|
|
73
|
+
if (body instanceof FormData) {
|
|
74
|
+
for (let [key, value] of body.entries()) {
|
|
75
|
+
if (key.startsWith('message')) {
|
|
76
|
+
try {
|
|
77
|
+
const messageContent = JSON.parse(value);
|
|
78
|
+
newMessages.push(messageContent);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.error('Error parsing message:', e);
|
|
81
|
+
}
|
|
82
|
+
} else if (key === 'files') {
|
|
83
|
+
files.push(value);
|
|
84
|
+
}
|
|
219
85
|
}
|
|
220
|
-
|
|
86
|
+
|
|
87
|
+
// Process files if present
|
|
88
|
+
if (files.length > 0 && newMessages.length > 0) {
|
|
89
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
90
|
+
lastMessage.files = await this.processFiles(files);
|
|
91
|
+
}
|
|
92
|
+
} else if (body.messages) {
|
|
93
|
+
newMessages = body.messages;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Add to conversation history
|
|
97
|
+
this.conversation.push(...newMessages);
|
|
98
|
+
|
|
99
|
+
// Send to Node-RED
|
|
100
|
+
const payload = this.formatForChatGPT(this.conversation);
|
|
101
|
+
this.sendToNodeRED(payload, signals);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Error in handleConnection:', error);
|
|
104
|
+
this.sendErrorResponse(signals, 'Sorry, there was an error processing your message.');
|
|
221
105
|
}
|
|
222
106
|
},
|
|
223
|
-
|
|
224
|
-
|
|
107
|
+
|
|
108
|
+
async processFiles(files) {
|
|
225
109
|
try {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
110
|
+
return await Promise.all(
|
|
111
|
+
files.map(async (file) => {
|
|
112
|
+
if (!(file instanceof File)) return file;
|
|
113
|
+
|
|
114
|
+
if (file.type.startsWith('image/')) {
|
|
115
|
+
return await this.processImageFile(file);
|
|
116
|
+
} else if (file.type.startsWith('audio/')) {
|
|
117
|
+
return await this.processAudioFile(file);
|
|
118
|
+
} else {
|
|
119
|
+
// Handle other file types (PDFs, documents, etc.)
|
|
120
|
+
return await this.processDocumentFile(file);
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
);
|
|
230
124
|
} catch (error) {
|
|
231
|
-
console.error('Error
|
|
125
|
+
console.error('Error processing files:', error);
|
|
126
|
+
return [];
|
|
232
127
|
}
|
|
233
128
|
},
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
129
|
+
|
|
130
|
+
processImageFile(file) {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
const reader = new FileReader();
|
|
133
|
+
reader.onload = (e) =>
|
|
134
|
+
resolve({
|
|
135
|
+
name: file.name,
|
|
136
|
+
type: file.type,
|
|
137
|
+
size: file.size,
|
|
138
|
+
src: e.target.result,
|
|
139
|
+
});
|
|
140
|
+
reader.onerror = () => reject(new Error('Failed to read image'));
|
|
141
|
+
reader.readAsDataURL(file);
|
|
142
|
+
});
|
|
240
143
|
},
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
144
|
+
|
|
145
|
+
processAudioFile(file) {
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
const reader = new FileReader();
|
|
148
|
+
reader.onload = (e) => {
|
|
149
|
+
try {
|
|
150
|
+
const result = e.target.result;
|
|
151
|
+
const commaIndex = result.indexOf(',');
|
|
152
|
+
if (commaIndex === -1) {
|
|
153
|
+
throw new Error('Invalid data URL format: missing comma separator');
|
|
154
|
+
}
|
|
155
|
+
const base64Data = result.split(',')[1]; // Remove data URL prefix
|
|
156
|
+
|
|
157
|
+
resolve({
|
|
158
|
+
name: file.name,
|
|
159
|
+
type: file.type,
|
|
160
|
+
size: file.size,
|
|
161
|
+
base64Data: base64Data,
|
|
162
|
+
});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
reject(error);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
reader.onerror = () => reject(new Error('Failed to read audio'));
|
|
168
|
+
reader.readAsDataURL(file);
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
processDocumentFile(file) {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const reader = new FileReader();
|
|
175
|
+
reader.onload = (e) => {
|
|
176
|
+
try {
|
|
177
|
+
const base64Data = e.target.result.split(',')[1]; // Remove data URL prefix
|
|
178
|
+
|
|
179
|
+
resolve({
|
|
180
|
+
name: file.name,
|
|
181
|
+
type: file.type,
|
|
182
|
+
size: file.size,
|
|
183
|
+
fileData: base64Data, // Use fileData for documents
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
reject(error);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
reader.onerror = () => reject(new Error('Failed to read document'));
|
|
190
|
+
reader.readAsDataURL(file);
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
formatForChatGPT(conversation, textOnly = false) {
|
|
195
|
+
const payload = {
|
|
196
|
+
messages: conversation.map((msg) => this.formatMessage(msg, textOnly)),
|
|
197
|
+
model: this.props.model,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Add ChatGPT API extensions
|
|
201
|
+
if (this.props.tools) {
|
|
202
|
+
payload.tools = this.props.tools;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (this.props.tool_choice) {
|
|
206
|
+
payload.tool_choice = this.props.tool_choice;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (this.props.temperature !== undefined) {
|
|
210
|
+
payload.temperature = this.props.temperature;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.props.max_tokens) {
|
|
214
|
+
payload.max_tokens = this.props.max_tokens;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.props.top_p !== undefined) {
|
|
218
|
+
payload.top_p = this.props.top_p;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (this.props.frequency_penalty !== undefined) {
|
|
222
|
+
payload.frequency_penalty = this.props.frequency_penalty;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.props.presence_penalty !== undefined) {
|
|
226
|
+
payload.presence_penalty = this.props.presence_penalty;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (this.props.response_format) {
|
|
230
|
+
payload.response_format = this.props.response_format;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return payload;
|
|
255
234
|
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
235
|
+
|
|
236
|
+
formatMessage(msg, textOnly = false) {
|
|
237
|
+
const message = {
|
|
238
|
+
role: msg.role === 'ai' ? 'assistant' : 'user',
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Handle tool calls (for assistant messages)
|
|
242
|
+
if (msg.tool_calls) {
|
|
243
|
+
message.tool_calls = msg.tool_calls;
|
|
244
|
+
message.content = msg.content || null;
|
|
245
|
+
return message;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Handle tool responses (for tool messages)
|
|
249
|
+
if (msg.role === 'tool') {
|
|
250
|
+
message.role = 'tool';
|
|
251
|
+
message.content = msg.content;
|
|
252
|
+
message.tool_call_id = msg.tool_call_id;
|
|
253
|
+
return message;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Handle messages with files
|
|
257
|
+
if (msg.files && msg.files.length > 0) {
|
|
258
|
+
message.content = this.buildContentArray(msg, textOnly);
|
|
259
|
+
return message;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Handle text-only messages
|
|
263
|
+
message.content = msg.text || msg.content || '';
|
|
264
|
+
return message;
|
|
264
265
|
},
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
266
|
+
|
|
267
|
+
buildContentArray(msg, textOnly) {
|
|
268
|
+
const content = [{ type: 'text', text: msg.text || '' }];
|
|
269
|
+
|
|
270
|
+
msg.files.forEach((file) => {
|
|
271
|
+
if (file.type && file.type.startsWith('image/') && file.src) {
|
|
272
|
+
content.push({
|
|
273
|
+
type: 'image_url',
|
|
274
|
+
image_url: { url: file.src },
|
|
275
|
+
});
|
|
276
|
+
} else if (!textOnly && file.type && file.type.startsWith('audio/') && file.base64Data) {
|
|
277
|
+
const format = this.getAudioFormat(file.type);
|
|
278
|
+
content.push({
|
|
279
|
+
type: 'input_audio',
|
|
280
|
+
input_audio: {
|
|
281
|
+
data: file.base64Data,
|
|
282
|
+
format: format,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
} else if (!textOnly && file.fileData) {
|
|
286
|
+
content.push({
|
|
287
|
+
type: 'file',
|
|
288
|
+
file: {
|
|
289
|
+
filename: file.name,
|
|
290
|
+
file_data: file.fileData,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return content;
|
|
271
297
|
},
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
298
|
+
|
|
299
|
+
getAudioFormat(mimeType) {
|
|
300
|
+
if (mimeType.includes('mp3')) return 'mp3';
|
|
301
|
+
if (mimeType.includes('wav')) return 'wav';
|
|
302
|
+
if (mimeType.includes('webm')) return 'webm';
|
|
303
|
+
if (mimeType.includes('m4a')) return 'm4a';
|
|
304
|
+
return 'wav';
|
|
279
305
|
},
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
306
|
+
|
|
307
|
+
sendToNodeRED(payload, signals, fallbackMessage = null) {
|
|
308
|
+
this.$socket.emit('widget-action', this.id, { payload });
|
|
309
|
+
|
|
310
|
+
this.$socket.once('msg-input:' + this.id, (msg) => {
|
|
311
|
+
this.handleNodeREDResponse(msg, signals);
|
|
312
|
+
});
|
|
283
313
|
},
|
|
284
|
-
|
|
285
|
-
handleError(error) {
|
|
286
|
-
console.error('Deep Chat Error:', error)
|
|
287
|
-
|
|
288
|
-
// Fehler an Node-RED melden
|
|
289
|
-
this.$socket.emit('chat-message', this.id, {
|
|
290
|
-
error: error.message || error,
|
|
291
|
-
timestamp: new Date().toISOString()
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
</script>
|
|
297
314
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
overflow: hidden;
|
|
303
|
-
background: var(--v-theme-surface);
|
|
304
|
-
}
|
|
315
|
+
handleNodeREDResponse(msg, signals) {
|
|
316
|
+
try {
|
|
317
|
+
// Store the complete ChatGPT response in conversation
|
|
318
|
+
const fullResponse = msg.payload;
|
|
305
319
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
320
|
+
// Create AI message with complete response data
|
|
321
|
+
const aiMessage = {
|
|
322
|
+
role: 'ai',
|
|
323
|
+
content: fullResponse.message?.content || fullResponse.content,
|
|
324
|
+
text: fullResponse.message?.content || fullResponse.content,
|
|
325
|
+
fullResponse: fullResponse,
|
|
326
|
+
};
|
|
314
327
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
--border-color: var(--v-theme-outline);
|
|
321
|
-
--text-color: var(--v-theme-on-surface);
|
|
322
|
-
--placeholder-color: var(--v-theme-on-surface-variant);
|
|
323
|
-
}
|
|
328
|
+
// Handle tool calls if present
|
|
329
|
+
if (fullResponse.message?.tool_calls) {
|
|
330
|
+
aiMessage.tool_calls = fullResponse.message.tool_calls;
|
|
331
|
+
aiMessage.content = fullResponse.message.content || null;
|
|
332
|
+
}
|
|
324
333
|
|
|
325
|
-
|
|
326
|
-
.ui-deepchat-container :deep(.text-input-container) {
|
|
327
|
-
border-top: 1px solid var(--v-theme-outline) !important;
|
|
328
|
-
background: var(--v-theme-surface) !important;
|
|
329
|
-
}
|
|
334
|
+
this.conversation.push(aiMessage);
|
|
330
335
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
336
|
+
// For the chat display, show appropriate response
|
|
337
|
+
if (fullResponse.message?.tool_calls) {
|
|
338
|
+
// If ChatGPT wants to call tools, show a message
|
|
339
|
+
signals.onResponse({
|
|
340
|
+
text: 'Function call requested...',
|
|
341
|
+
role: 'ai',
|
|
342
|
+
});
|
|
343
|
+
} else if (fullResponse.message?.content || fullResponse.content) {
|
|
344
|
+
// Normal text response
|
|
345
|
+
signals.onResponse({
|
|
346
|
+
text: fullResponse.message?.content || fullResponse.content,
|
|
347
|
+
role: 'ai',
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error('Error handling response:', error);
|
|
352
|
+
this.sendErrorResponse(signals, 'Error processing response');
|
|
353
|
+
}
|
|
354
|
+
},
|
|
335
355
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
356
|
+
sendErrorResponse(signals, message) {
|
|
357
|
+
if (signals && signals.onResponse) {
|
|
358
|
+
signals.onResponse({
|
|
359
|
+
text: message,
|
|
360
|
+
role: 'ai',
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
</script>
|
|
367
|
+
|
|
368
|
+
<style scoped>
|
|
369
|
+
.deep-chat-container {
|
|
370
|
+
display: flex;
|
|
371
|
+
justify-content: center;
|
|
372
|
+
width: 100%;
|
|
341
373
|
}
|
|
342
|
-
</style>
|
|
374
|
+
</style>
|