json-object-editor 0.10.425 → 0.10.431

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.
@@ -0,0 +1,359 @@
1
+
2
+ const { run } = require("googleapis/build/src/apis/run");
3
+ const OpenAI = require("openai");
4
+
5
+ function ChatGPTAssistants() {
6
+ const self = this;
7
+
8
+ function coloredLog(message) {
9
+ console.log(JOE.Utils.color('[chatgpt-assistants]', 'plugin', false), message);
10
+ }
11
+
12
+
13
+
14
+ function getAPIKey() {
15
+ const setting = JOE.Utils.Settings('OPENAI_API_KEY');
16
+ if (!setting) throw new Error("Missing OPENAI_API_KEY setting");
17
+ return setting;
18
+ }
19
+
20
+ function newClient() {
21
+ return new OpenAI({ apiKey: getAPIKey() });
22
+ }
23
+
24
+ function validateThreadId(thread_id) {
25
+ const validPattern = /^[a-zA-Z0-9_-]+$/;
26
+ if (!validPattern.test(thread_id)) {
27
+ console.error("❌ Invalid characters in thread_id!");
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ function stripHtml(html) {
33
+ if (!html) return "";
34
+ return html.replace(/<[^>]*>?/gm, '').trim();
35
+ }
36
+ this.getRandomTheme = async function(data, req, res) {
37
+ try {
38
+ const themes = ["hope", "peace", "perseverance", "joy", "strength", "love", "faith", "healing"];
39
+ const randomTheme = themes[Math.floor(Math.random() * themes.length)];
40
+
41
+ coloredLog("🎯 Selected Random Theme: " + randomTheme);
42
+
43
+ return res.jsonp({
44
+ theme: randomTheme
45
+ });
46
+
47
+ } catch (err) {
48
+ coloredLog("❌ Error in getRandomTheme: " + err.message);
49
+ return res.jsonp({ error: err.message });
50
+ }
51
+ };
52
+
53
+ this.testAssistant = async function(data, req, res){
54
+ const openai = newClient();
55
+ const assistant = await openai.beta.assistants.retrieve("asst_HFzrtyAVyzDDwn1PLOIwU26W");
56
+ thread = await openai.beta.threads.create();
57
+
58
+ // 2. Add a user message
59
+ await openai.beta.threads.messages.create({
60
+ thread_id: thread.id,
61
+ role: 'user',
62
+ content: `Please analyze this business: welltyme.com`
63
+ });
64
+ coloredLog('⚡ Starting Assistant thread');
65
+ console.log(assistant);
66
+ return res.jsonp({ assistant });
67
+ }
68
+ this.syncAssistantToOpenAI = async function(data, req, res) {
69
+ const openai = newClient();
70
+
71
+ try {
72
+ coloredLog("🔄 Starting syncAssistantToOpenAI");
73
+
74
+ const assistant = JOE.Data.ai_assistant.find(a => a._id === data._id);
75
+ if (!assistant) {
76
+ return ({ error: "AI Assistant not found in Joe." });
77
+ }
78
+
79
+ // Check if updating or creating
80
+ let existing = null;
81
+ if (assistant.assistant_id) {
82
+ try {
83
+ existing = await openai.beta.assistants.retrieve(assistant.assistant_id);
84
+ } catch (e) {
85
+ console.warn('Assistant ID exists but not found at OpenAI, will create new.');
86
+ existing = null;
87
+ }
88
+ }
89
+ const payload = {
90
+ name: assistant.name,
91
+ instructions: stripHtml(assistant.instructions || ""),
92
+ model: assistant.ai_model || "gpt-4o",
93
+ tools: [],
94
+ //file_search: { enabled: !!assistant.file_search_enabled },
95
+ //code_interpreter: { enabled: !!assistant.code_interpreter_enabled }
96
+ };
97
+ // 1. Add user-defined function tools
98
+ const parsedTools = Array.isArray(assistant.tools) ? assistant.tools : JSON.parse(assistant.tools || "[]");
99
+ payload.tools.push(...parsedTools);
100
+
101
+ // 2. Add built-in tools if toggled ON
102
+ if (assistant.file_search_enabled) {
103
+ payload.tools.push({ type: "file_search" });
104
+ }
105
+
106
+ if (assistant.code_interpreter_enabled) {
107
+ payload.tools.push({ type: "code_interpreter" });
108
+ }
109
+ // if (existing && existing.file_ids?.length) {
110
+ // payload.file_ids = existing.file_ids;
111
+ // }
112
+
113
+ // Only set file_ids if handling files manually
114
+ if (assistant.file_ids && assistant.file_ids.length > 0) {
115
+ payload.file_ids = assistant.file_ids;
116
+ }
117
+ coloredLog("📦 Payload for OpenAI sync:");
118
+ coloredLog(JSON.stringify(payload, null, 2));
119
+
120
+ let apiResponse;
121
+
122
+ if (!assistant.assistant_id) {
123
+ coloredLog("🆕 No assistant_id found. Creating new Assistant...");
124
+ apiResponse = await openai.beta.assistants.create(payload);
125
+ assistant.assistant_id = apiResponse.id;
126
+ } else {
127
+ coloredLog("♻️ assistant_id found. Updating existing Assistant...");
128
+ apiResponse = await openai.beta.assistants.update(assistant.assistant_id, payload);
129
+ }
130
+
131
+ assistant.last_synced = new Date().toISOString();
132
+
133
+ // 💾 Set status to "assistant_synced" if found
134
+ const syncStatus = JOE.Data.status.find(s => s.code === "assistant_synced");
135
+ if (syncStatus) {
136
+ assistant.status = syncStatus._id;
137
+ }
138
+
139
+ await new Promise((resolve, reject) => {
140
+ JOE.Storage.save(assistant, 'ai_assistant', function(err, saved) {
141
+ if (err) {
142
+ return reject(err);
143
+ }
144
+ resolve(saved);
145
+ });
146
+ });
147
+
148
+ coloredLog("💾 Assistant sync saved back to Joe successfully.");
149
+
150
+ return ({
151
+ success: true,
152
+ assistant_id: assistant.assistant_id,
153
+ message: assistant.assistant_id ? "Assistant synced successfully." : "Assistant created successfully."
154
+ });
155
+
156
+ } catch (err) {
157
+ coloredLog("❌ Error in syncAssistantToOpenAI: " + err.message);
158
+ return ({ error: err.message });
159
+ }
160
+ };
161
+ this.getThreadMessages = async function(data, req, res) {
162
+ coloredLog("🔄 Starting getThreadMessages");
163
+ try {
164
+ const thread_id = data.thread_id;
165
+ if (!thread_id) {
166
+ return ({ error: "Missing thread ID." });
167
+ }
168
+ if(data.polling){
169
+ coloredLog('Polling for thread messages: ' + thread_id);
170
+ }
171
+ const openai = newClient();
172
+ const messages = await openai.beta.threads.messages.list(thread_id);
173
+
174
+ //return res.jsonp({ success: true, messages: messages.data || [] });
175
+ var succResp = { success: true, messages: messages.data || [] }
176
+ return(succResp);
177
+ } catch (err) {
178
+ console.error('❌ getThreadMessages error:', err);
179
+
180
+ return({ error: "Failed to load thread messages." });
181
+ }
182
+ };
183
+ this.addMessage = async function(data, req, res) {
184
+ try {
185
+ const { conversation_id, content,object_id} = data;
186
+ if (!conversation_id || !content) {
187
+ return({ error: "Missing conversation ID or content." });
188
+ }
189
+
190
+ const openai = newClient();
191
+
192
+ const convo = await new Promise((resolve, reject) => {
193
+ JOE.Storage.load('ai_conversation', { _id: conversation_id }, (err, results) => {
194
+ if (err || !results || !results.length) {
195
+ return reject("Conversation not found.");
196
+ }
197
+ resolve(results[0]);
198
+ });
199
+ });
200
+ var resave = false;
201
+ //add objectID
202
+ if(object_id && !convo.context_objects.includes(object_id)){
203
+ if(!convo.context_objects) convo.context_objects = [];
204
+ convo.context_objects.push(object_id);
205
+ resave = true;
206
+ }
207
+
208
+ if (!convo.thread_id) {
209
+ const thread = await openai.beta.threads.create();
210
+ convo.thread_id = thread.id;
211
+ resave = true;
212
+ // await new Promise((resolve, reject) => {
213
+ // JOE.Storage.save(convo, 'ai_conversation', function(err, saved) {
214
+ // if (err) return reject(err);
215
+ // resolve(saved);
216
+ // });
217
+ // });
218
+ }
219
+
220
+ if(resave){
221
+ await new Promise((resolve, reject) => {
222
+ JOE.Storage.save(convo, 'ai_conversation', function(err, saved) {
223
+ if (err) return reject(err);
224
+ resolve(saved);
225
+ });
226
+ });
227
+
228
+ }
229
+
230
+ await openai.beta.threads.messages.create(convo.thread_id, {
231
+ role: "user",
232
+ content: content
233
+ });
234
+ var runObj = null;
235
+ // NOW ➔ trigger assistant reply if assistant is selected
236
+ if (convo.assistants && convo.assistants.length > 0) {
237
+ // const assistant_id = convo.assistants[0]; // Assuming you store OpenAI assistant ID here
238
+ //get assistant object by id
239
+ const assistant_id = data.assistant_id || convo.assistants?.[0]?.openai_id;
240
+ const assistant = JOE.Data.ai_assistant.find(a => a._id === assistant_id);
241
+ if (assistant.assistant_id) {
242
+ runObj = await openai.beta.threads.runs.create(convo.thread_id, {
243
+ assistant_id: assistant.assistant_id
244
+ });
245
+
246
+ console.log(`Assistant run started: ${runObj.id}`);
247
+ // You could optionally poll for completion here
248
+ }
249
+ }else if(convo.assistant){
250
+ const assistant = $J.get(convo.assistant);
251
+ runObj = await openai.beta.threads.runs.create(convo.thread_id, {
252
+ assistant_id: assistant.assistant_id // Assuming you store OpenAI assistant ID here
253
+ });
254
+
255
+ coloredLog(`Assistant run started: ${runObj.id}`);
256
+ }
257
+ return({ success: true,runObj });
258
+ } catch (err) {
259
+ console.error('❌ addMessage error:', err);
260
+ return({ error: "Failed to send message.", message: err.message });
261
+ }
262
+ };
263
+ this.getRunStatus = async function(data, req, res) {
264
+ try {
265
+ const run_id = data.run_id;
266
+ const thread_id = data.thread_id;
267
+ var errors = [];
268
+ if (!run_id) errors.push("Missing run ID.");
269
+ if(!thread_id) errors.push("Missing thread ID.");
270
+
271
+ if (errors.length) {
272
+ return { error: errors.join(" ") };
273
+ }
274
+
275
+ const openai = newClient();
276
+ const run = await openai.beta.threads.runs.retrieve(thread_id, run_id);
277
+ coloredLog("🔄 Run status retrieved: " + run.status +'assistant_id: ' + run.assistant_id);
278
+ return {
279
+ id: run.id,
280
+ status: run.status,
281
+ assistant_id: run.assistant_id,
282
+ completed_at: run.completed_at,
283
+ usage: run.usage || {}
284
+ };
285
+
286
+ } catch (err) {
287
+ console.error("❌ getRunStatus error:", err);
288
+ return { error: "Failed to check run status." };
289
+ }
290
+ };
291
+
292
+ function getDefaultAssistant() {
293
+ var asst_id = JOE.Utils.Settings('DEFAULT_AI_ASSISTANT');
294
+ const defaultAssistant = $J.get(asst_id);
295
+ if (!defaultAssistant) {
296
+ throw new Error("Default Assistant not found.");
297
+ }
298
+ return defaultAssistant;
299
+ }
300
+ this.createConversation = async function(data, req, res) {
301
+ //this function creates a new conversation
302
+ //it should use the default assistant and add the context of the passed object id after being flattend to the conversation.
303
+ //save user_is to the conversation object
304
+
305
+ try {
306
+ const { object_id, user_id } = data;
307
+ const openai = newClient();
308
+ const user = $J.get(user_id);
309
+ const contextObject = $J.get(object_id);
310
+ var newConvo = null;
311
+ var assistant = getDefaultAssistant();
312
+ var assistants = assistant? [assistant._id]:[];
313
+ const convo ={
314
+ _id:cuid(),
315
+ created: new Date().toISOString(),
316
+ _joeUpdated: new Date().toISOString(),
317
+ itemtype: 'ai_conversation',
318
+ name:`${user.name}'s conversation about ${contextObject.name} [${contextObject.itemtype}]`,
319
+ user:user_id,
320
+ members:[],
321
+ assistant:assistant._id,
322
+ context_objects:[object_id]
323
+ }
324
+
325
+ if (!convo.thread_id) {
326
+ const thread = await openai.beta.threads.create();
327
+ convo.thread_id = thread.id;
328
+
329
+ newConvo = await new Promise((resolve, reject) => {
330
+ JOE.Storage.save(convo, 'ai_conversation', function(err, saved) {
331
+ if (err) return reject(err);
332
+ resolve(saved);
333
+ });
334
+ });
335
+ }
336
+
337
+ return({ success: true,conversation:newConvo });
338
+ } catch (err) {
339
+ console.error('❌ conversation creation error:', err);
340
+ return({ error: "Failed to create conversation.",message: err.message });
341
+ }
342
+ };
343
+
344
+ //self.profileBusinessFromURL = profileBusinessFromURL;
345
+ this.async = {
346
+ syncAssistantToOpenAI : this.syncAssistantToOpenAI ,
347
+ getRunStatus: this.getRunStatus,
348
+ getThreadMessages: this.getThreadMessages,
349
+ addMessage: this.addMessage,
350
+ getRandomTheme: this.getRandomTheme,
351
+ testAssistant: this.testAssistant,
352
+ createConversation: this.createConversation
353
+
354
+ };
355
+ return self;
356
+
357
+ }
358
+
359
+ module.exports = new ChatGPTAssistants();
@@ -0,0 +1,79 @@
1
+ const OpenAI = require("openai");
2
+
3
+ function ChatGPTTools() {
4
+ const self = this;
5
+
6
+ function coloredLog(message) {
7
+ console.log(JOE.Utils.color('[chatgpt-tools]', 'plugin', false), message);
8
+ }
9
+
10
+
11
+
12
+ function getAPIKey() {
13
+ const setting = JOE.Utils.Settings('OPENAI_API_KEY');
14
+ if (!setting) throw new Error("Missing OPENAI_API_KEY setting");
15
+ return setting;
16
+ }
17
+
18
+ //search JOE data for ai_tool objects
19
+ function getTools(data){
20
+ var query = Object.assign({itemtype:'ai_tool'}, data||{});
21
+ var tools = JOE.Cache.search(query);
22
+ var functionalTools = {};
23
+ for(var i=0; i<tools.length; i++){
24
+ var tool = tools[i];
25
+ functionalTools[tool._id] = Object.assign(tool,{
26
+ tool_properties:JSON.parse(tool.tool_properties),
27
+ });
28
+ }
29
+ return functionalTools;
30
+ }
31
+
32
+ function newClient() {
33
+ return new OpenAI({ apiKey: getAPIKey() });
34
+ }
35
+
36
+
37
+ this.default = function(data, req, res) {
38
+ try {
39
+ var payload = {
40
+ params: req.params,
41
+ data: data,
42
+ tools:getTools(data),
43
+ success:true
44
+ };
45
+ } catch (e) {
46
+ return { errors: 'plugin error: ' + e, failedat: 'plugin' };
47
+ }
48
+ return payload;
49
+ };
50
+
51
+ this.flattened = function(data, req, res){
52
+ try {
53
+ var flattened = JOE.Utils.flattenObject(data._id);
54
+ var payload = {
55
+ params: req.params,
56
+ data: data,
57
+ object:flattened,
58
+ success:true
59
+ };
60
+ } catch (e) {
61
+ return { errors: 'plugin error: ' + e, failedat: 'plugin' };
62
+ }
63
+ return payload;
64
+ }
65
+ //1. start or continue a conversation
66
+
67
+
68
+
69
+
70
+ //self.profileBusinessFromURL = profileBusinessFromURL;
71
+ this.async = {
72
+
73
+
74
+ };
75
+ return self;
76
+
77
+ }
78
+
79
+ module.exports = new ChatGPTTools();
@@ -0,0 +1,46 @@
1
+ function PluginUtils(){
2
+ var self = this;
3
+
4
+ function getPluginList(){
5
+ const plugins = {}
6
+ for(var plug in JOE.Apps.plugins){
7
+ let plugin = JOE.Apps.plugins[plug];
8
+ plugins[plug]={
9
+ name:plug,
10
+ protected:plugin.protected,
11
+ async:Object.keys(plugin.async||{}),
12
+ _pathname:plugin._pathname,
13
+ _override:plugin._override,
14
+ }
15
+ };
16
+ return plugins;
17
+ }
18
+ this.default = function(data,req,res){
19
+ // list the plugins that are available
20
+ const plugins = getPluginList();
21
+ try{
22
+ var payload = {
23
+ params:req.params,
24
+ data:data,
25
+ plugins:plugins
26
+ }
27
+ }catch(e){
28
+ return {errors:'plugin error: '+e,failedat:'plugin'};
29
+ }
30
+
31
+
32
+
33
+ return payload;
34
+ };
35
+
36
+ this.html = function(data,req,res){
37
+
38
+
39
+
40
+
41
+ return JSON.stringify(self.default(data,req),'','\t\r\n <br/>');
42
+ }
43
+ this.protected = [];
44
+ return self;
45
+ }
46
+ module.exports = new PluginUtils();
@@ -1,11 +1,31 @@
1
1
  var schema = {
2
2
  title: "Ai Conversation | ${name}",
3
+ display:'Ai Convo',
3
4
  info: "Tracks AI conversations across users, assistants, and external members, storing only summaries for performance.",
4
5
  methods:{
5
6
  chatSpawner:async function(object_id){
6
7
  await _joe.Ai.spawnContextualChat(object_id);
7
8
  }
8
9
  },
10
+ listView:{
11
+ title: function(chat){
12
+ return `
13
+
14
+ <joe-subtext >${_joe.Utils.prettyPrintDTS(chat.created)}</joe-subtext>
15
+ <joe-title>${chat.name}</joe-title>
16
+ <joe-title>${_joe.SERVER.User.Render.cubes(chat.user,'fleft')}</joe-title>
17
+ <div>${(chat.context_objects||[]).map(function(ref){
18
+ var obj = $J.get(ref);
19
+ if(obj){
20
+ return `<joe-subtext>${obj.itemtype}:<b>${obj.name}</b> - ${obj._id}</joe-subtext>`;
21
+ }else{
22
+ return `<joe-subtext>${ref}</joe-subtext>`;
23
+ }
24
+ }).join('')}</div>
25
+ `;
26
+ },
27
+ listWindowTitle: 'Ai Conversations'
28
+ },
9
29
  fields: function() {
10
30
  return [
11
31
 
@@ -13,11 +33,22 @@ var schema = {
13
33
  "info",
14
34
 
15
35
  { section_start: "participants", display: "Participants", collapsed: false },
16
- { name: "user", type: "objectReference", values: "user", display: "JOE User" },
36
+ { name: "user", type: "select", values: "user", display: "JOE User", comment: "Select the JOE user who initiated this conversation.",width:'50%'},
37
+
38
+ {name:'assistant',type:"select",values:'ai_assistant',display:'AI Assistant',width:'50%',
39
+ comment:'Select the AI assistant to use for this conversation.',default:function(){
40
+ try{
41
+ var defAi = Ai.getDefaultAssistant();
42
+ return defAi._id;
43
+ }catch(e){
44
+ console.log('Error getting default assistant',e);
45
+ }
46
+ },
47
+ },
17
48
  { name: "members", type: "objectReference", values: "members", display: "External Members"},
18
- { name: "assistants", type: "group", cssClass:"ai-options", template:function(ass,curObj){
19
- return `${ass.name} || ${curObj.name}`;
20
- }, values: function() { return _joe.getDataset('ai_assistant'); }, display: "Assistants", cols:2 },
49
+ // { name: "assistants", type: "group", cssClass:"ai-options", template:function(ass,curObj){
50
+ // return `${ass.name} || ${curObj.name}`;
51
+ // }, values: function() { return _joe.getDataset('ai_assistant'); }, display: "Assistants", cols:2 },
21
52
  { section_end: "participants" },
22
53
 
23
54
  { section_start: "thread", display: "Thread", collapsed: true },
@@ -28,18 +59,11 @@ var schema = {
28
59
  { name: "summary", type: "wysiwyg", display: "Conversation Summary", height: "300px", comment: "Auto-generated after key points or closing." },
29
60
  { section_end: "summary" },
30
61
 
31
- { section_start: "linked", display: "Linked Objects", collapsed: true },
32
- { name: "linked_objects", type: "objectlist", display: "Linked Objects", fields: [
33
- { name: "schema", type: "text", display: "Schema" },
34
- { name: "object_id", type: "text", display: "Object ID" }
35
- ]
36
- },
37
- { section_end: "linked" },
38
62
 
39
63
  { section_start: "system", collapsed: true },
40
64
  "_id",
41
65
  "created",
42
- "updated",
66
+ "joeUpdated",
43
67
  "itemtype",
44
68
  { section_end: "system" },
45
69
 
@@ -49,7 +73,7 @@ var schema = {
49
73
  {
50
74
  name: "openChat",
51
75
  type: "button",
52
- display: "Open Chat",
76
+ display: "Continue Conversation",
53
77
  icon:"ai_assistant",
54
78
  onclick2:function(object){
55
79
  if (!object || !object._id) return '';
@@ -59,14 +83,21 @@ var schema = {
59
83
  if (!object || !object._id) return '';
60
84
  return `_joe.schemas.ai_conversation.methods.chatSpawner('${object._id}');`;
61
85
  },
86
+
62
87
  },
88
+ {name:'context_objects',type:"objectReference",
89
+ display:'Context Objects',
90
+ comment:'Objects included in this conversation for Ai context.',
91
+ locked:true,values:function(obj,prop){
92
+ return JOE.getData();
93
+ }},
63
94
  { sidebar_end: "right" },
64
95
 
65
96
  ];
66
97
  },
67
98
 
68
99
  idprop: "_id",
69
- sorter: ["-created"],
100
+ sorter: ["!created"],
70
101
  };
71
102
 
72
103
  module.exports = schema;
@@ -0,0 +1,94 @@
1
+ // Joe AI Tool Schema
2
+ // This schema defines reusable tools for OpenAI Assistants and Chat Completions.
3
+ // Each tool includes a JSON schema for function calling and an executable code block.
4
+
5
+ var schema = {
6
+ title: "Ai Tool | ${name}",
7
+ display: "Ai Tool",
8
+ info: "Reusable Ai tool function for OpenAI tool calling and local execution.",
9
+ //menuicon: `<svg viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h18"/></svg>`,
10
+
11
+ listView: {
12
+ title: '<joe-title>${name}</joe-title><joe-subtitle>${info}</joe-subtitle>',
13
+ listWindowTitle: 'Ai Tools'
14
+ },
15
+ methods:{
16
+ updateToolProperties:function(propObj){
17
+ //get the current tool properties as json
18
+ // set the tool_properties.name to the passed current objects tool_id
19
+ let rawProps = _jco(true).tool_properties ||"{}";
20
+ var toolProperties = eval('('+rawProps+')')||{};
21
+ toolProperties.type = 'function';
22
+ toolProperties.function = Object.assign({
23
+ description:'',
24
+ parameters:{
25
+ type:'object',
26
+ properties:{}
27
+ }
28
+ },toolProperties.function,{name:propObj.name});
29
+ //toolProperties.function.name = propObj.name;
30
+ _joe.Fields.set('tool_properties',JSON.stringify(toolProperties,'\n','\t'));
31
+
32
+ }
33
+ },
34
+ fields: function() {
35
+ return [
36
+ "name",
37
+ "info", // Unique identifier
38
+ "description", // Short tool description
39
+ {name:'tool_id', type:'text', display:'Tool ID', comment:'Unique identifier for the tool. Used in OpenAI function calling.',onblur:"_joe.schemas.ai_tool.methods.updateToolProperties({name:this.value});"},
40
+ { section_start: "schema", display: "OpenAI Tool Schema" },
41
+ {
42
+ name: "tool_properties",
43
+ type: "code",
44
+ display: "Tool Properties (OpenAI format)",
45
+ height: "300px",
46
+ language: "json",
47
+ comment:
48
+ `<div>Define the tool funcitonal properties as used in OpenAI tool calling.</div>
49
+ <a href="https://platform.openai.com/docs/guides/gpt/function-calling?api-mode=chat" target="_blank">OpenAI Function Calling</a><br/>
50
+ <pre>{
51
+ "type": "function",
52
+ "function": {
53
+ "name": "findBusinessLinks",
54
+ "description": "Find relevant links for a business.",
55
+ "parameters": {
56
+ "type": "object",
57
+ "properties": {
58
+ "business_name": { "type": "string" },
59
+ "website": { "type": "string", "format": "uri" }
60
+ },
61
+ "required": ["business_name", "website"]
62
+ }
63
+ }
64
+ }</pre>`
65
+ },
66
+ { section_end: "schema" },
67
+
68
+ { section_start: "execution", display: "Server Execution" },
69
+ {
70
+ name: "server_code",
71
+ type: "code",
72
+ display: "Code to Execute (Node.js async)",
73
+ height: "300px",
74
+ comment: "Use `args` as input. Return a string, object, or promise.<br/>e.g. <code>return await joe.search(args.dataset, args.query);</code>"
75
+ },
76
+ { section_end: "execution" },
77
+
78
+ { section_start: "meta", collapsed: true },
79
+ "tags",
80
+ "datasets",
81
+ { section_end: "meta" },
82
+
83
+ { section_start: "system", collapsed: true },
84
+ "_id",
85
+ "created",
86
+ "itemtype",
87
+ { section_end: "system" }
88
+ ];
89
+ },
90
+
91
+ idprop: "_id"
92
+ };
93
+
94
+ module.exports = schema;