json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
323 lines (288 loc) • 17 kB
JavaScript
var schema = {
display:'Ai Prompt',
title : 'Ai Prompt | ${name}',
info:"An ai prompt that can be sent to openAi, etc",
summary:{
description:'Reusable AI prompt definition that describes how to call the chatgpt plugin or Responses API.',
purpose:'Use ai_prompt to define prompt methods, helper functions, and instructions for generating structured AI responses or content. Prompts can be tagged, scoped to datasets, and linked to ai_response records.',
labelField:'name',
defaultSort:{ field:'joeUpdated', dir:'desc' },
searchableFields:['name','info','prompt_method','ai_model','tags','datasets'],
allowedSorts:['joeUpdated','created','name','ai_model'],
relationships:{
outbound:[
{ field:'status', targetSchema:'status', cardinality:'one' },
{ field:'tags', targetSchema:'tag', cardinality:'many' },
{ field:'datasets', targetSchema:'<schemaName>', cardinality:'many' }
],
inbound:{ graphRef:'server/relationships.graph.json' }
},
joeManagedFields:['created','joeUpdated'],
fields:[
{ name:'_id', type:'string', required:true },
{ name:'itemtype', type:'string', required:true, const:'ai_prompt' },
{ name:'name', type:'string', required:true },
{ name:'info', type:'string' },
{ name:'prompt_method', type:'string' },
{ name:'content_items', type:'objectList' },
{ name:'functions', type:'string' },
{ name:'instructions_format', type:'string' },
{ name:'instructions', type:'string' },
{ name:'user_prompt', type:'string' },
{ name:'status', type:'string', isReference:true, targetSchema:'status' },
{ name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' },
{ name:'ai_model', type:'string' },
{ name:'temperature', type:'number' },
{ name:'datasets', type:'string', isArray:true },
{ name:'joeUpdated', type:'string', format:'date-time' },
{ name:'created', type:'string', format:'date-time' }
]
},
menuicon:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="-128 -128 1280 1280">
<path d="M356.52 692.84v31.84c0 11.07.47 17.51 1.4 19.31 1.19 2.44 3.42 4.2 6.07 4.8 4.53 1.05 9.18 1.49 13.83 1.31h8.55c9.29 0 13.93 2.98 13.93 8.94 0 6.89-4.9 11.11-14.71 12.66-12.05 1.35-24.18 1.91-36.31 1.66-14.75 0-25.19-2.86-31.32-8.58-6.13-5.72-9.21-15.51-9.23-29.36v-40.03c.42-5.59-.57-11.2-2.87-16.31-2.81-3.78-6.95-6.36-11.58-7.21-5.48-1.37-9.15-6.51-8.68-12.14-.53-5.64 3.17-10.81 8.68-12.14 4.65-.96 8.77-3.61 11.58-7.44 2.32-5.11 3.31-10.72 2.87-16.31v-39.93c0-13.64 3.07-23.32 9.2-29.04 6.13-5.72 16.57-8.57 31.32-8.55 12.9-.36 25.81.33 38.6 2.06 3.42.52 6.6 2.05 9.14 4.4 2.11 2.02 3.29 4.82 3.26 7.73 0 5.96-4.64 8.94-13.93 8.94h-8.81c-7.92 0-13.35.85-16.31 2.54-2.92 1.96-4.62 5.29-4.47 8.81v45.77c-.17 15.42-4.31 25.69-12.4 30.8-.64.43-1.02 1.16-1.01 1.92 0 1.2 1.45 2.99 4.34 5.38 3.23 2.94 5.58 6.73 6.79 10.93 1.58 5.6 2.28 11.41 2.09 17.23Zm90.28-100.23c16.42.07 29.72 13.37 29.79 29.79.25 8.33-3.05 16.37-9.07 22.12-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.5 14.16-10.15 22.87-9.98Zm0 73.9c16.42.07 29.72 13.37 29.79 29.79.25 8.34-3.06 16.4-9.1 22.15-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.52 14.17-10.18 22.9-10.02Zm89.36-40.03v-31.71c0-11.07-.47-17.51-1.4-19.31-1.19-2.44-3.42-4.2-6.07-4.8-4.53-1.05-9.18-1.49-13.83-1.31h-8.48c-9.29 0-13.93-2.98-13.93-8.94 0-6.89 4.9-11.07 14.71-12.53 12.05-1.35 24.18-1.9 36.31-1.66 14.75 0 25.19 2.86 31.32 8.58 6.13 5.72 9.2 15.4 9.2 29.04v40.13c-.44 5.59.55 11.2 2.87 16.31 2.84 3.76 6.96 6.35 11.58 7.28 5.51 1.34 9.2 6.53 8.65 12.17.37 4.53-1.99 8.85-6 10.99-2.02.92-4.11 1.65-6.26 2.19-4.2 1.33-7.58 4.48-9.2 8.58-1.34 4.52-1.9 9.23-1.66 13.93v40c0 13.72-3.07 23.44-9.2 29.13-6.13 5.7-16.57 8.56-31.32 8.58-12.9.36-25.81-.32-38.6-2.06-3.42-.52-6.6-2.05-9.14-4.4-2.13-2.06-3.31-4.9-3.26-7.86 0-5.96 4.64-8.94 13.93-8.94 1.11 0 4.05.09 8.81.26 5.55.28 11.11-.59 16.31-2.54 2.02-1.05 3.47-2.95 3.92-5.19.66-5.88.9-11.8.72-17.72v-31.84c-.25-6.36.68-12.71 2.74-18.73 2.07-4.82 5.46-8.96 9.79-11.94.64-.43 1.02-1.16 1.01-1.92 0-1.2-1.45-2.99-4.34-5.38-3.23-2.94-5.58-6.73-6.79-10.93-1.69-5.66-2.5-11.55-2.38-17.45Zm164.47-327.03 197.56 40.87-197.56 40.86-40.86 197.57-40.87-197.57-197.56-40.86 197.56-40.87 40.87-197.56 40.86 197.56z"/>
<path d="m516.05 158.53 81.14 16.79-81.14 16.78-16.78 81.15-16.79-81.15-81.14-16.78 81.14-16.79 16.79-81.14 16.78 81.14zm43.7 281.88 41.02 8.48-41.02 8.48-8.48 41.02-8.49-41.02-41.01-8.48 41.01-8.48 8.49-41.02 8.48 41.02z"/>
<path d="M698.87 563.96c16.22 26.17 25.29 55.26 25.29 85.9 0 117.09-105.49 205.38-245.39 205.38-3.01 0-6.03-.04-9.07-.12-13.15-.34-27.52-2.1-42.74-3.96-16.83-2.06-34.23-4.19-52.19-4.67l-8.81-.24-8.65 1.71-138.94 27.52 12.32-30.18 18.2-44.57-32.74-35.61c-22.03-23.96-48.3-63.4-48.3-115.27 0-113.76 124.78-206.3 278.15-206.3 10.26 0 20.38.43 30.35 1.23l-48.12-48.12C259.65 403.99 125.8 514.97 125.8 650.82c0 56.11 22.89 107.94 61.57 150.01l-59.5 145.77 252.16-49.95c26.34.7 55.86 6.99 85.42 7.76 3.35.09 6.69.13 10.01.13 171.44 0 290.73-115.81 290.73-253.72 0-42.53-13.14-82.62-36.34-117.86l-30.99 30.99Z"/>
</svg>`,
listView:{
title: function(aip){
return `
<joe-title>${aip.name}</joe-title>
<joe-subtitle>${aip.info}</joe-subtitle><br/>
<joe-subtext class="fright"><div><b>${aip.ai_model}</b></div>${aip.prompt_method}</joe-subtext>
<joe-subtext>${aip.content_items.map(i=>{
return `<b>${i.itemtype}</b>: ${i.reference}`;
}).join('')}</joe-subtext>
${_joe.schemas.tag.methods.renderTags(aip.tags,'fleft')}
`;
},
listWindowTitle: 'Ai Prompts'
},
subsets:function(i){
return _joe.Filter.Options.status();
},
filters:function(i){
return _joe.Filter.Options.tags();
},
stripeColor: function(item) {
//use the stripe color from ai_assistant status object if it has one
if (item.status){
var status = _joe.Cache.get(item.status);
if (status && status.color) {
return {color:status.color,title:status.name};
}
}
},
onload:function(dataset){
//replace the blank instructions field with the populated template field && the blank instrctions format field with the populated template type field
dataset.map(function(item){
if(item.template){
item.instructions = item.template;
delete item.template;
}
if(item.template_type){
item.instructions_format = item.template_type;
delete item.template_type;
}
})
},
fields:[
'name',
'info',
{name:"prompt_method",placeholder:"name of method to call in plugin", comment:'use executeJOEAiPrompt to use the JOE ui here for all smarts', default:"executeJOEAiPrompt",display:"Prompt Plugin Method",type:'text'},
{name:'content_items',type:'objectList',
properties:['itemtype','reference']
},
{section_start:'Input'},
{name: 'functions', type: 'code', display: 'Helper Functions', language:'javascript',comment: `
<div>AI Prompt Helper Function
<h4>Usage:</h4>
<ul>
<li>Must export an async function.</li>
<li>Receives: { instructions, params, ai_prompt } as a single argument.</li>
<li>Must return a final modified instructions string.</li>
</ul>
<h4>Example:</h4>
<div class="pad10">
<pre>
module.exports = async function({ instructions, params, ai_prompt })
// Your code here to modify the instructions based off params
const object = $J.get(params.idToLookup);
trackObject(params.idToLookup); // Tell parent we used this
return {
instructions: "Updated system-level instructions string",
input: "Specific input text (URL, query, etc)"
};
};
</pre>
</div>
</div>`},
{section_end:'Input'},
{section_start:'Instruct'},
{label:'Instructions'},
"instructions_format",
"instructions",
{name:"user_prompt",display:'User Prompt',type:'rendering',comment:'speciific input to the prompt if any, such as campaign intent'},
// 'template_type',
// {extend:'template',specs:{comment:'legacy field, use instructions instead',
// // comment:`<div>use $\\{this.AI_PROMPT.name\\} $\\{this.CONTENT\\} <br/></div><div>for <b>module</b>: export function(data) that returns an html string.
// // data.request, data.REPORT,
// // <br/>shorthand: $J.get(itemid) $J.schema(schemaname)</div>`,
// language:function(item){
// switch(item.template_type){
// case 'module':
// return 'javascript';
// break;
// default:
// return '';
// }
// }
// }},
{section_end:'Instruct'},
{section_start:'system',collapsed:true},
'_id','created','itemtype',
{section_end:'system'},
{sidebar_start:'right',collapsed:true},
{section_start:'workflow'},
'status',
'tags',
// {name:'merge_status',comment:'status to add when this data is merged in', allowMultiple:false,type:"objectReference",display:'Merge Status',values:function(){
// return _joe.getDataset('status');
// }},
{section_end:'workflow'},
{section_start:'openAi',collapsed:true},
'ai_model',
{name:'temperature', type:'number',display:'Temperature', default:.7, step:"0.1",comment:'0-1, 0 is deterministic, 1 is random'},
//{name:'max_tokens', type:'number',display:'Max Tokens',comment:'max tokens to return',default:4096},
{section_end:'openAi'},
{section_start:'examples',collapsed:true},
{name:'examples', display:'prompt examples', comment:'runs the prompt to create a response',type:'content', reloadable:true,run:function(item){
return $J.schema('ai_prompt').methods.listExamples(item);
} },
{section_end:'examples'},
{sidebar_end:'right'},
{sidebar_start:'left',collapsed:function(aip){
return aip.datasets.length != 0;
}},
{section_start:'categorization',collapsed:false},
'datasets',
{section_end:'categorization'},
{sidebar_end:'left'},
],
idprop : "_id",
sorter:['name','!joeUpdated'],
methods:{
buildURL: function(prompt, items = []) {
const method = prompt.prompt_method || '';
let url = `/API/plugin/chatgpt/${method}?ai_prompt=${prompt._id}`;
if (!Array.isArray(items)) {
console.warn('Expected items to be an array.');
return url;
}
if (prompt.content_items) {
prompt.content_items.forEach(ci => {
const matchingItem = items.find(it => it.itemtype === ci.itemtype );
if (matchingItem) {
url += `&${ci.reference}=${matchingItem._id}`;
}
});
}
return url;
},
// listItemClickHandler:function(itemid,promptid){
// var item = $J.get(itemid);
// var prompt;
// if(promptid){
// prompt = $J.get(promptid);
// }else{
// prompt = _jco();
// }
// window.open(this.buildURL(prompt,[item]));
// },
listItemClickHandler: function (itemMap, promptId) {
const prompt = promptId ? $J.get(promptId,'ai_prompt') : _jco();
const items = [];
for (const [itemtype, id] of Object.entries(itemMap)) {
const obj = $J.get(id,itemtype);
if (obj) {
items.push(obj);
}
}
window.open(this.buildURL(prompt, items));
},
listExamples:function(prompt){
var examples = [];
var html ='';
var ref0;
var it0;
if(!prompt.content_items){
return 'select content items to send with this prompt';
}
prompt.content_items.map(function(ci){
if(_joe.Data[ci.itemtype]){
examples = examples.concat(_joe.Data[ci.itemtype]);
it0 = ci.itemtype;
ref0 = ci.reference;
}
})
var finalExamples = [];
while(finalExamples.length < 5 && finalExamples.length < examples.length){
let ci = examples[Math.floor(Math.random()*examples.length)];
if(finalExamples.indexOf(ci) == -1){
finalExamples.push(ci);
}
}
var rep_url;
var method =
finalExamples.map(function(ex){
//rep_url = '/API/plugin/chatgpt'+'?ai_prompt='+prompt._id+'&'+ref0+'='+ex._id;
html += _joe.renderFieldListItem(ex,
'<joe-subtext>'+it0+'</joe-subtext>'+
'<joe-title>${name} ${date}</joe-title>\
<joe-subtext>${website}</joe-subtext>',
it0,
{
//action:'onclick="window.open(\''+rep_url+'\');"',
action:`onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler('${ex._id}');"`,
icon:'ai_prompt'
}
);
})
return html;
},
listPrompts: function (objectMap) {
if (!objectMap || typeof objectMap !== 'object') return '';
const activeStatuses = _joe.getDataset('status')
.filter(f => f.datasets.includes('ai_prompt') && f.active)
.map(f => f._id);
const prompts = _joe.getDataset('ai_prompt').filter(prompt =>
activeStatuses.includes(prompt.status)
);
const context_items = {}
// Inline helper: reduce objects to { itemtype: _id }
const extractIds = map => {
const idMap = {};
for (const [type, obj] of Object.entries(map)) {
if (obj && obj._id) idMap[type] = obj._id;
}
return idMap;
};
const objectIdMap = extractIds(objectMap);
const paramStr = JSON.stringify(objectIdMap).replace(/"/g, '"');
let html = '';
const temp = '<joe-title>${name}</joe-title>';
let itemtype = _joe.current.object.itemtype;
const matchingPrompts = prompts.filter(prompt =>
(prompt.content_items || []).some(ci => ci.itemtype === itemtype)
);
matchingPrompts.forEach(prompt => {
html += _joe.renderFieldListItem(prompt, temp, 'ai_prompt', {
icon: 'ai_prompt',
action: `onclick="(function(){ _joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, '${prompt._id}'); })()"`
//action: `onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, "${prompt._id}");"`
});
});
return html;
}
}
};
module.exports = schema;