-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
87 lines (84 loc) · 4.02 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
module.exports = (RED) => {
const { default: got } = require('got')
RED.plugins.registerPlugin('flowfuse-nr-assistant', {
type: 'assistant',
name: 'Node-RED Assistant Plugin',
icon: 'font-awesome/fa-magic',
settings: {
'*': { exportable: true }
},
onadd: function () {
const assistantSettings = RED.settings.flowforge?.assistant || { enabled: false }
const clientSettings = {
enabled: assistantSettings.enabled !== false && !!assistantSettings.url,
requestTimeout: assistantSettings.requestTimeout || 60000
}
RED.comms.publish('nr-assistant/initialise', clientSettings, true /* retain */)
if (!assistantSettings || !assistantSettings.enabled) {
RED.log.info('FlowFuse Assistant Plugin is disabled')
return
}
if (!assistantSettings.url) {
RED.log.info('FlowFuse Assistant Plugin is missing url')
return
}
RED.log.info('FlowFuse Assistant Plugin loaded')
RED.httpAdmin.post('/nr-assistant/:method', RED.auth.needsPermission('write'), function (req, res) {
const method = req.params.method
// limit method to prevent path traversal
if (!method || typeof method !== 'string' || /[^a-z0-9-_]/.test(method)) {
res.status(400)
res.json({ status: 'error', message: 'Invalid method' })
return
}
const input = req.body
if (!input || !input.prompt || typeof input.prompt !== 'string') {
res.status(400)
res.json({ status: 'error', message: 'prompt is required' })
return
}
const body = {
prompt: input.prompt, // this is the prompt to the AI
promptHint: input.promptHint, // this is used to let the AI know what we are generating (`function node? Node JavaScript? flow?)
context: input.context, // this is used to provide additional context to the AI (e.g. the selected text of the function node)
transactionId: input.transactionId // used to correlate the request with the response
}
// join url & method (taking care of trailing slashes)
const url = `${assistantSettings.url.replace(/\/$/, '')}/${method.replace(/^\//, '')}`
got.post(url, {
headers: {
Accept: '*/*',
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8,es;q=0.7',
Authorization: `Bearer ${assistantSettings.token}`,
'Content-Type': 'application/json'
},
json: body
}).then(response => {
const data = JSON.parse(response.body)
res.json({
status: 'ok',
data
})
}).catch((error) => {
let body = error.response?.body
if (typeof body === 'string') {
try {
body = JSON.parse(body)
} catch (e) {
// ignore
}
}
let message = 'FlowFuse Assistant request was unsuccessful'
const errorData = { status: 'error', message, body }
const errorCode = error.response?.statusCode || 500
res.status(errorCode).json(errorData)
RED.log.trace('nr-assistant error:', error)
if (body && typeof body === 'object' && body.error) {
message = `${message}: ${body.error}`
}
RED.log.warn(message)
})
})
}
})
}