json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
173 lines (156 loc) • 7.4 kB
JavaScript
var schema = {
title: "Ai Conversation | ${name}",
display:'Ai Convo',
summary:{
description:'Metadata record for an AI conversation between a user, optional external members, and one or more assistants.',
purpose:'Use ai_conversation to track which user and assistant are involved in a chat, the OpenAI thread_id, high-level status, tags, and a summary. Full message history is fetched from OpenAI at runtime rather than stored in Joe.',
labelField:'name',
defaultSort:{ field:'created', dir:'desc' },
searchableFields:['name','info','summary','thread_id','tags','_id'],
allowedSorts:['created','joeUpdated','name'],
relationships:{
outbound:[
{ field:'user', targetSchema:'user', cardinality:'one' },
{ field:'assistant', targetSchema:'ai_assistant', cardinality:'one' },
{ field:'members', targetSchema:'members', cardinality:'many' },
{ field:'status', targetSchema:'status', cardinality:'one' },
{ field:'tags', targetSchema:'tag', 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_conversation' },
{ name:'name', type:'string', required:true },
{ name:'info', type:'string' },
{ name:'user', type:'string', isReference:true, targetSchema:'user' },
{ name:'assistant', type:'string', isReference:true, targetSchema:'ai_assistant' },
{ name:'members', type:'string', isArray:true, isReference:true, targetSchema:'members' },
{ name:'thread_id', type:'string' },
{ name:'summary', type:'string' },
{ name:'status', type:'string', isReference:true, targetSchema:'status' },
{ name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' },
{ name:'joeUpdated', type:'string', format:'date-time' },
{ name:'created', type:'string', format:'date-time' }
]
},
info: "Tracks AI conversations across users, assistants, and external members, storing only summaries for performance.",
methods:{
chatSpawner:async function(object_id){
await _joe.Ai.spawnContextualChat(object_id);
},
listConversations:function(bus,oneline){
let html ='';
let conversations = _joe.Data.ai_conversation.filter(co=>{
if(co?.context_objects?.includes(bus._id)) return true;
})
$c.sortBy(conversations,'!created');
let temp = oneline?null:'<joe-subtext>${RUN[_joe.Utils.prettyPrintDTS;${created}]}</joe-subtext><joe-subtitle>${name}</joe-subtitle>';
conversations.map(tct=>{
html+=_joe.renderFieldListItem(tct,temp,'ai_conversation');
})
return html;
},
},
listView:{
title: function(chat){
return `
<joe-subtext >${_joe.Utils.prettyPrintDTS(chat.created)}</joe-subtext>
<joe-title>${chat.name}</joe-title>
<joe-title>${_joe.SERVER.User.Render.cubes(chat.user,'fleft')}</joe-title>
<div>${(chat.context_objects||[]).map(function(ref){
var obj = $J.get(ref);
if(obj){
return `<joe-subtext>${obj.itemtype}:<b>${obj.name}</b> - ${obj._id}</joe-subtext>`;
}else{
return `<joe-subtext>${ref}</joe-subtext>`;
}
}).join('')}</div>
`;
},
listWindowTitle: 'Ai Conversations'
},
fields: function() {
return [
"name",
"info",
{ section_start: "participants", display: "Participants", collapsed: false },
{ name: "user", type: "select", values: "user", display: "JOE User", comment: "Select the JOE user who initiated this conversation.",width:'50%'},
{name:'assistant',type:"select",values:'ai_assistant',display:'AI Assistant',width:'50%',
comment:'Select the AI assistant to use for this conversation.',default:function(){
try{
var defAi = Ai.getDefaultAssistant();
return defAi._id;
}catch(e){
console.log('Error getting default assistant',e);
}
},
},
{ name: "members", type: "objectReference", values: "members", display: "External Members"},
// { name: "assistants", type: "group", cssClass:"ai-options", template:function(ass,curObj){
// return `${ass.name} || ${curObj.name}`;
// }, values: function() { return _joe.getDataset('ai_assistant'); }, display: "Assistants", cols:2 },
{ section_end: "participants" },
{ section_start: "thread", display: "Thread", collapsed: true },
{ name: "thread_id", type: "text", display: "OpenAI Thread ID", locked: true },
{ section_end: "thread" },
{ section_start: "summary", display: "Summary", collapsed: false },
{ name: "summary", type: "wysiwyg", display: "Conversation Summary", height: "300px", comment: "Auto-generated after key points or closing." },
{ section_end: "summary" },
{ section_start: "system", collapsed: true },
"_id",
"created",
"joeUpdated",
"itemtype",
{ section_end: "system" },
{ sidebar_start: "right", collapsed: false },
'status',
"tags",
{
name: "openChat",
type: "button",
display: "Continue Conversation",
icon:"ai_assistant",
onclick2:function(object){
if (!object || !object._id) return '';
return `_joe.Ai.spawnChat('${object._id}');`;
},
onclick:function(object){
if (!object || !object._id) return '';
return `_joe.schemas.ai_conversation.methods.chatSpawner('${object._id}');`;
},
},
{name:'context_objects',type:"objectReference",
display:'Context Objects',
comment:'Objects included in this conversation for Ai context.',
locked:true,values:function(obj,prop){
return JOE.getData();
}},
{ sidebar_end: "right" },
];
},
idprop: "_id",
sorter: ["!created"],
};
module.exports = schema;
/**
* AI Conversation Schema
*
* Purpose:
* - Tracks live AI conversations linked to users, assistants, and external members.
* - Stores only metadata and final conversation summaries (not full chat messages).
* - Supports dynamic pulling of live thread content from OpenAI at runtime.
*
* Key Concepts:
* - user: Internal staff or admin account who initiated the conversation.
* - members: External participants (clients, business users), optional and multiple.
* - assistants: One or more AI assistants participating in the conversation.
* - thread_id: The OpenAI thread ID for live runtime message retrieval.
* - summary: Final AI-generated overview of the conversation (saved on close).
*
* Best Practices:
* - Messages are *not* stored permanently in Joe for performance and privacy.
* - Summaries provide lightweight snapshots for audits and reviews.
* - Chat UIs should always fetch full threads from OpenAI at runtime.
*/