json-object-editor 0.10.653 → 0.10.654
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/CHANGELOG.md +33 -0
- package/css/joe-ai.css +1 -1
- package/css/joe-styles.css +50 -1
- package/css/joe.css +51 -2
- package/css/joe.min.css +1 -1
- package/js/JsonObjectEditor.jquery.craydent.js +96 -5
- package/js/joe-ai.js +229 -2
- package/js/joe.js +97 -6
- package/js/joe.min.js +1 -1
- package/package.json +1 -1
- package/readme.md +44 -3
- package/server/fields/core.js +54 -6
- package/server/modules/MCP.js +2 -2
- package/server/modules/ThoughtPipeline.js +6 -0
- package/server/plugins/awsConnect.js +31 -1
- package/server/plugins/chatgpt.js +159 -3
- package/server/schemas/ai_prompt.js +2 -0
- package/server/schemas/ai_response.js +21 -12
- package/server/schemas/status.js +12 -2
- package/server/schemas/task.js +9 -3
|
@@ -5351,7 +5351,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5351
5351
|
'<div class="joe-uploader-preview">'+dz_message+'</div>'+
|
|
5352
5352
|
'</div>'+
|
|
5353
5353
|
'<div class="joe-uploader-message">add a file</div>'+
|
|
5354
|
-
'<div class="joe-button joe-green-button joe-upload-cofirm-button hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
|
|
5354
|
+
'<div class="joe-button joe-green-button joe-upload-cofirm-button ui-helper-hidden hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
|
|
5355
5355
|
'</div>'
|
|
5356
5356
|
;
|
|
5357
5357
|
//var idprop = prop.idprop || '_id';
|
|
@@ -5378,7 +5378,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5378
5378
|
var jup_template,html= '';
|
|
5379
5379
|
var alink ="<a href='${url}${base64}' class='file-link' target='_blank'></a>";
|
|
5380
5380
|
var delete_btn = '<joe-uploader-file-delete-btn class="svg-shadow" onclick="_joe.Fields.uploader.remove(\''+cuid+'\',\'${filename}\');">'+self.SVG.icon.close+'</joe-uploader-delete-btn>';
|
|
5381
|
-
var label = '
|
|
5381
|
+
var label = '';
|
|
5382
5382
|
|
|
5383
5383
|
|
|
5384
5384
|
files.map(function(file){
|
|
@@ -5403,6 +5403,12 @@ this.renderHTMLContent = function(specs){
|
|
|
5403
5403
|
|
|
5404
5404
|
|
|
5405
5405
|
|
|
5406
|
+
// Build the label with filename and optional OpenAI file id on a new line
|
|
5407
|
+
label = '<joe-uploader-file-label>'+(file.filename || '')+'</joe-uploader-file-label>';
|
|
5408
|
+
if(file.openai_file_id){
|
|
5409
|
+
label += '<joe-uploader-file-oaid >'+file.openai_file_id+'</joe-uploader-file-oaid>';
|
|
5410
|
+
}
|
|
5411
|
+
|
|
5406
5412
|
if((file.type && file.type.contains('image')) || (!file.type && (file.url||file.base64).split('.').contains(['jpg','jpeg','png','gif','svg']))){
|
|
5407
5413
|
jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+filesize+delete_btn+'</joe-uploader-file>';
|
|
5408
5414
|
}else if (docTypes.includes((file.url || file.base64).split('.').pop())) {
|
|
@@ -5414,7 +5420,16 @@ this.renderHTMLContent = function(specs){
|
|
|
5414
5420
|
'<joe-uploader-file-extension >.'+(file.type.split('/')[1] || '???')+'</joe-uploader-file-extension>'+
|
|
5415
5421
|
alink+label+filesize+delete_btn+'</joe-uploader-file>';
|
|
5416
5422
|
}
|
|
5417
|
-
|
|
5423
|
+
// Build optional OpenAI retry/upload button
|
|
5424
|
+
var openaiBtn = '';
|
|
5425
|
+
var needRetry = (!file.openai_file_id || file.openai_status == 'error');
|
|
5426
|
+
var hasUrl = !!file.url;
|
|
5427
|
+
if(hasUrl && needRetry){
|
|
5428
|
+
var openaiLabel = (file.openai_status == 'error') ? 'Retry OpenAI' : 'Upload to OpenAI';
|
|
5429
|
+
var safeFilename = (file.filename || '').replace(/'/g,"\\'");
|
|
5430
|
+
openaiBtn = '<joe-uploader-openai-btn class="joe-button" style="margin-left:6px;" onclick="_joe.SERVER.Plugins.openaiRetryFromUrl(\''+cuid+'\',\''+safeFilename+'\');">'+openaiLabel+'</joe-uploader-openai-btn>';
|
|
5431
|
+
}
|
|
5432
|
+
html += fillTemplate(jup_template,file) + openaiBtn;
|
|
5418
5433
|
})
|
|
5419
5434
|
return html+'<div class="clear"></div>';
|
|
5420
5435
|
}
|
|
@@ -5431,7 +5446,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5431
5446
|
$(joe_uploader.dropzone).find('img,div').replaceWith('<img src="' + base64 + '">')
|
|
5432
5447
|
//joe_uploader.dropzone.html('<img src="' + base64 + '">');
|
|
5433
5448
|
joe_uploader.message.html('<b>'+file.name+'</b> selected');
|
|
5434
|
-
joe_uploader.confirmBtn.removeClass('hidden');
|
|
5449
|
+
joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5435
5450
|
}else {
|
|
5436
5451
|
results.innerHTML = 'Nothing to upload.';
|
|
5437
5452
|
}
|
|
@@ -5462,7 +5477,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5462
5477
|
}
|
|
5463
5478
|
joe_uploader.files.push(temp_file);
|
|
5464
5479
|
$(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
|
|
5465
|
-
joe_uploader.confirmBtn.removeClass('hidden');
|
|
5480
|
+
joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5466
5481
|
}else {
|
|
5467
5482
|
results.innerHTML = 'Nothing to upload.';
|
|
5468
5483
|
}
|
|
@@ -5530,6 +5545,20 @@ this.renderHTMLContent = function(specs){
|
|
|
5530
5545
|
var fileobj = joe_uploader.files.where({filename:filename})[0];
|
|
5531
5546
|
fileobj.uploaded = new Date();
|
|
5532
5547
|
fileobj.url = url;
|
|
5548
|
+
// Apply captured OpenAI info if present
|
|
5549
|
+
var key = self.current.object._id + '/' + filename;
|
|
5550
|
+
var oi = _joe._lastOpenAI && _joe._lastOpenAI[key];
|
|
5551
|
+
fileobj.openai_purpose = 'assistants';
|
|
5552
|
+
if(oi){
|
|
5553
|
+
fileobj.openai_file_id = oi.openai_file_id || null;
|
|
5554
|
+
fileobj.openai_status = (oi.openai_file_id && 'ok') || (oi.openai_error && 'error') || 'not_uploaded';
|
|
5555
|
+
fileobj.openai_error = oi.openai_error || null;
|
|
5556
|
+
if(oi.openai_error){
|
|
5557
|
+
joe_uploader.message.append('<div>OpenAI error: '+oi.openai_error+'</div>');
|
|
5558
|
+
}
|
|
5559
|
+
}else{
|
|
5560
|
+
fileobj.openai_status = fileobj.openai_status || 'not_uploaded';
|
|
5561
|
+
}
|
|
5533
5562
|
delete fileobj.base64;
|
|
5534
5563
|
}
|
|
5535
5564
|
|
|
@@ -5600,6 +5629,14 @@ this.renderHTMLContent = function(specs){
|
|
|
5600
5629
|
self.uploaders[id].dropzone = $(this).find('.joe-uploader-dropzone');
|
|
5601
5630
|
self.uploaders[id].confirmBtn = $(this).find('.joe-upload-cofirm-button');
|
|
5602
5631
|
|
|
5632
|
+
// If there are pending files (not uploaded or missing url), show the confirm button
|
|
5633
|
+
var pending = (self.uploaders[id].files || []).some(function(file){
|
|
5634
|
+
return $c.isObject(file) && (!file.uploaded || !file.url);
|
|
5635
|
+
});
|
|
5636
|
+
if(pending){
|
|
5637
|
+
self.uploaders[id].confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5603
5640
|
});
|
|
5604
5641
|
};
|
|
5605
5642
|
encapsulateFieldType('uploader',self.renderUploaderField,
|
|
@@ -9936,6 +9973,13 @@ logit(intent)
|
|
|
9936
9973
|
url = prefix+response.Key;
|
|
9937
9974
|
}
|
|
9938
9975
|
var filename = response.Key.replace(self.current.object._id+'/','');
|
|
9976
|
+
// Stash OpenAI info for use during finalization
|
|
9977
|
+
_joe._lastOpenAI = _joe._lastOpenAI || {};
|
|
9978
|
+
_joe._lastOpenAI[response.Key] = {
|
|
9979
|
+
openai_file_id: response.openai_file_id,
|
|
9980
|
+
openai_purpose: response.openai_purpose,
|
|
9981
|
+
openai_error: response.openai_error
|
|
9982
|
+
};
|
|
9939
9983
|
if(field.url_field){
|
|
9940
9984
|
var nprop = {};
|
|
9941
9985
|
_joe.current.object[field.url_field] = url;
|
|
@@ -9960,6 +10004,53 @@ logit(intent)
|
|
|
9960
10004
|
file: { base64:data.base64, extension:data.extension, type:data.contentType, filename: (_joe.current.object._id+data.extension), name: (_joe.current.object._id+data.extension) },
|
|
9961
10005
|
field: data.field
|
|
9962
10006
|
}, callback);
|
|
10007
|
+
},
|
|
10008
|
+
// Retry uploading a specific file to OpenAI using its URL.
|
|
10009
|
+
// Invoked by the uploader row action.
|
|
10010
|
+
openaiRetryFromUrl:function(uploader_id, filename){
|
|
10011
|
+
try{
|
|
10012
|
+
var joe_uploader = self.uploaders[uploader_id];
|
|
10013
|
+
if(!joe_uploader){ return alert('Uploader not found'); }
|
|
10014
|
+
var file = joe_uploader.files.where({filename:filename})[0];
|
|
10015
|
+
if(!file){ return alert('File not found'); }
|
|
10016
|
+
if(!file.url){ return alert('No URL to upload to OpenAI'); }
|
|
10017
|
+
var btn = $(joe_uploader.preview).find("joe-uploader-openai-btn:contains('"+filename+"')");
|
|
10018
|
+
joe_uploader.message.append('<div>Uploading to OpenAI...</div>');
|
|
10019
|
+
$.ajax({
|
|
10020
|
+
url: (location.port && '//'+__jsc.hostname+':'+__jsc.port+'/API/plugin/chatgpt/filesRetryFromUrl/') || '//'+__jsc.hostname+'/API/plugin/chatgpt/filesRetryFromUrl/',
|
|
10021
|
+
type:'POST',
|
|
10022
|
+
data:{
|
|
10023
|
+
url:file.url,
|
|
10024
|
+
filename:file.filename,
|
|
10025
|
+
contentType:file.type
|
|
10026
|
+
},
|
|
10027
|
+
success:function(resp){
|
|
10028
|
+
if(resp && resp.success && resp.openai_file_id){
|
|
10029
|
+
file.openai_file_id = resp.openai_file_id;
|
|
10030
|
+
file.openai_purpose = resp.openai_purpose || 'assistants';
|
|
10031
|
+
file.openai_status = 'ok';
|
|
10032
|
+
file.openai_error = null;
|
|
10033
|
+
joe_uploader.message.append('<div>OpenAI file attached</div>');
|
|
10034
|
+
}else{
|
|
10035
|
+
file.openai_status = 'error';
|
|
10036
|
+
file.openai_error = (resp && resp.error) || 'Upload failed';
|
|
10037
|
+
joe_uploader.message.append('<div>OpenAI error: '+(file.openai_error)+'</div>');
|
|
10038
|
+
}
|
|
10039
|
+
// Refresh preview to reflect new status/button
|
|
10040
|
+
$(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
|
|
10041
|
+
// Save object so file metadata is persisted
|
|
10042
|
+
self.updateObject(null, null, true);
|
|
10043
|
+
},
|
|
10044
|
+
error:function(xhr,status,err){
|
|
10045
|
+
var message = (xhr && (xhr.responseJSON && (xhr.responseJSON.error || xhr.responseJSON.code) || xhr.responseText)) || err || status || 'Retry failed';
|
|
10046
|
+
file.openai_status = 'error';
|
|
10047
|
+
file.openai_error = message;
|
|
10048
|
+
joe_uploader.message.append('<div>OpenAI error: '+message+'</div>');
|
|
10049
|
+
}
|
|
10050
|
+
});
|
|
10051
|
+
}catch(e){
|
|
10052
|
+
alert(e && e.message || e);
|
|
10053
|
+
}
|
|
9963
10054
|
}
|
|
9964
10055
|
},
|
|
9965
10056
|
ready:{
|
package/js/joe-ai.js
CHANGED
|
@@ -1679,26 +1679,54 @@
|
|
|
1679
1679
|
* Run the Thought agent for the current schema object.
|
|
1680
1680
|
* Used by the core `proposeThought` field.
|
|
1681
1681
|
*/
|
|
1682
|
-
Ai.runProposeThought = function(
|
|
1682
|
+
Ai.runProposeThought = function(btnOrScopeId, textareaIdOrModel, maybeModel){
|
|
1683
1683
|
try{
|
|
1684
|
+
// Back-compat: allow old signature (scopeId, textareaId, model)
|
|
1685
|
+
var btn = (btnOrScopeId && btnOrScopeId.tagName) ? btnOrScopeId : null;
|
|
1686
|
+
var scopeId = btn ? textareaIdOrModel : btnOrScopeId;
|
|
1687
|
+
var textareaId = btn ? maybeModel : textareaIdOrModel;
|
|
1688
|
+
var model = btn ? undefined : maybeModel;
|
|
1689
|
+
|
|
1690
|
+
if (btn) {
|
|
1691
|
+
if (btn.dataset.running === '1') { return; }
|
|
1692
|
+
btn.dataset.running = '1';
|
|
1693
|
+
btn.classList.add('joe-loading');
|
|
1694
|
+
btn.setAttribute('disabled','disabled');
|
|
1695
|
+
}
|
|
1684
1696
|
if (!scopeId) {
|
|
1685
1697
|
alert('Save the item before proposing thoughts.');
|
|
1698
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1686
1699
|
return;
|
|
1687
1700
|
}
|
|
1688
1701
|
var ta = document.getElementById(textareaId);
|
|
1689
1702
|
if (!ta) {
|
|
1690
1703
|
alert('Prompt field not found.');
|
|
1704
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1691
1705
|
return;
|
|
1692
1706
|
}
|
|
1693
1707
|
var prompt = (ta.value || '').trim();
|
|
1694
1708
|
if (!prompt) {
|
|
1695
1709
|
alert('Please enter a prompt for the Thought run.');
|
|
1710
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1696
1711
|
return;
|
|
1697
1712
|
}
|
|
1713
|
+
// Collect selected OpenAI file ids (trim to 10)
|
|
1714
|
+
var selId = 'propose_thought_files_' + scopeId;
|
|
1715
|
+
var fileIds = [];
|
|
1716
|
+
try{
|
|
1717
|
+
var sel = document.getElementById(selId);
|
|
1718
|
+
if(sel){
|
|
1719
|
+
var selected = Array.prototype.slice.call(sel.selectedOptions || []);
|
|
1720
|
+
fileIds = selected.map(function(o){ return o.value; }).filter(Boolean);
|
|
1721
|
+
if(fileIds.length > 10){ fileIds = fileIds.slice(0,10); }
|
|
1722
|
+
}
|
|
1723
|
+
}catch(_e){/*noop*/}
|
|
1724
|
+
|
|
1698
1725
|
var params = {
|
|
1699
1726
|
agent_id: 'thought_agent_default',
|
|
1700
1727
|
scope_id: scopeId,
|
|
1701
|
-
user_input: prompt
|
|
1728
|
+
user_input: prompt,
|
|
1729
|
+
openai_file_ids: fileIds
|
|
1702
1730
|
};
|
|
1703
1731
|
if (model) {
|
|
1704
1732
|
params.model = model;
|
|
@@ -1723,12 +1751,15 @@
|
|
|
1723
1751
|
} else {
|
|
1724
1752
|
alert('Thought agent run complete. Proposed ' + count + ' thoughts.');
|
|
1725
1753
|
}
|
|
1754
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1726
1755
|
}).catch(function(err){
|
|
1727
1756
|
console.error('Ai.runProposeThought error', err);
|
|
1728
1757
|
alert('Failed to run Thought agent: ' + (err && err.message || err));
|
|
1758
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1729
1759
|
});
|
|
1730
1760
|
}catch(e){
|
|
1731
1761
|
console.error('Ai.runProposeThought error', e);
|
|
1762
|
+
try{ if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); } }catch(_e){}
|
|
1732
1763
|
}
|
|
1733
1764
|
};
|
|
1734
1765
|
|
|
@@ -1813,7 +1844,203 @@
|
|
|
1813
1844
|
return false;
|
|
1814
1845
|
}
|
|
1815
1846
|
};
|
|
1847
|
+
|
|
1848
|
+
// --------------------------------------------------
|
|
1849
|
+
// File helpers for AI integrations
|
|
1850
|
+
// --------------------------------------------------
|
|
1851
|
+
Ai.getFilesForObject = function(options = {}){
|
|
1852
|
+
try{
|
|
1853
|
+
var onlyWithOpenAI = options.onlyWithOpenAI === true;
|
|
1854
|
+
var includeNested = options.includeNested === true; // not used in v1
|
|
1855
|
+
var obj = (_joe && _joe.current && _joe.current.object) || {};
|
|
1856
|
+
var schema = (_joe && _joe.current && _joe.current.schema) || {};
|
|
1857
|
+
// Prefer the live, constructed field defs; fall back to schema.fields
|
|
1858
|
+
var liveFieldDefs = (_joe && _joe.current && Array.isArray(_joe.current.fields) && _joe.current.fields) || [];
|
|
1859
|
+
var schemaFields = (schema && schema.fields) || [];
|
|
1860
|
+
var fieldNames = [];
|
|
1861
|
+
// collect from live constructed defs
|
|
1862
|
+
liveFieldDefs.forEach(function(fd){
|
|
1863
|
+
if(fd && fd.name && fd.type === 'uploader'){ fieldNames.push(fd.name); }
|
|
1864
|
+
});
|
|
1865
|
+
// also collect from raw schema (string or object), dedupe
|
|
1866
|
+
schemaFields.forEach(function(f){
|
|
1867
|
+
var fname = (typeof f === 'string') ? f : (f && (f.name || f.field || f.prop));
|
|
1868
|
+
if(!fname){ return; }
|
|
1869
|
+
try{
|
|
1870
|
+
var fd = _joe.getField(fname);
|
|
1871
|
+
if(fd && fd.type === 'uploader' && fieldNames.indexOf(fname) === -1){
|
|
1872
|
+
fieldNames.push(fname);
|
|
1873
|
+
}
|
|
1874
|
+
}catch(_e){ /* ignore */ }
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
var out = [];
|
|
1878
|
+
fieldNames.forEach(function(fname){
|
|
1879
|
+
var val = obj[fname];
|
|
1880
|
+
if(!val){ return; }
|
|
1881
|
+
var list = Array.isArray(val) ? val : [val];
|
|
1882
|
+
list.forEach(function(file){
|
|
1883
|
+
if(!file || typeof file !== 'object'){ return; }
|
|
1884
|
+
var item = {
|
|
1885
|
+
fieldName: fname,
|
|
1886
|
+
filename: file.filename || (file.url && String(file.url).split('/').pop()) || '',
|
|
1887
|
+
url: file.url || null,
|
|
1888
|
+
openai_file_id: file.openai_file_id || null,
|
|
1889
|
+
openai_status: file.openai_status || null,
|
|
1890
|
+
size: file.size || null,
|
|
1891
|
+
type: file.type || null,
|
|
1892
|
+
path: fname
|
|
1893
|
+
};
|
|
1894
|
+
if(onlyWithOpenAI && !item.openai_file_id){ return; }
|
|
1895
|
+
out.push(item);
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
return out;
|
|
1899
|
+
}catch(e){
|
|
1900
|
+
console.warn('Ai.getFilesForObject error', e);
|
|
1901
|
+
return [];
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
|
|
1905
|
+
Ai.renderFilesSelector = function(selectId, opts = {}){
|
|
1906
|
+
try{
|
|
1907
|
+
var cap = typeof opts.cap === 'number' ? opts.cap : 10;
|
|
1908
|
+
var disableWithoutOpenAI = opts.disableWithoutOpenAI !== false; // default true
|
|
1909
|
+
var onlyWithOpenAI = false; // show all; disable those missing ids
|
|
1910
|
+
var files = Ai.getFilesForObject({ onlyWithOpenAI, includeNested:false });
|
|
1911
|
+
// Fallback: scan top-level arrays for file-shaped objects if nothing found
|
|
1912
|
+
if(!files || !files.length){
|
|
1913
|
+
try{
|
|
1914
|
+
var obj = (_joe && _joe.current && _joe.current.object) || {};
|
|
1915
|
+
Object.keys(obj || {}).forEach(function(k){
|
|
1916
|
+
var v = obj[k];
|
|
1917
|
+
if(Array.isArray(v)){
|
|
1918
|
+
v.forEach(function(it){
|
|
1919
|
+
if(it && typeof it === 'object' && (it.filename || it.url)){
|
|
1920
|
+
files.push({
|
|
1921
|
+
fieldName: k,
|
|
1922
|
+
filename: it.filename || (it.url && String(it.url).split('/').pop()) || '',
|
|
1923
|
+
url: it.url || null,
|
|
1924
|
+
openai_file_id: it.openai_file_id || null,
|
|
1925
|
+
openai_status: it.openai_status || null,
|
|
1926
|
+
size: it.size || null,
|
|
1927
|
+
type: it.type || null,
|
|
1928
|
+
path: k
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
}catch(_e){ /* noop */ }
|
|
1935
|
+
}
|
|
1936
|
+
var sel = document.getElementById(selectId);
|
|
1937
|
+
if(!sel){ return; }
|
|
1938
|
+
sel.innerHTML = '';
|
|
1939
|
+
files.forEach(function(f){
|
|
1940
|
+
var opt = document.createElement('option');
|
|
1941
|
+
opt.value = f.openai_file_id || ''; // empty when missing
|
|
1942
|
+
opt.textContent = f.filename || f.openai_file_id || '(unnamed file)';
|
|
1943
|
+
if(disableWithoutOpenAI && !f.openai_file_id){
|
|
1944
|
+
opt.disabled = true;
|
|
1945
|
+
opt.textContent += ' (not uploaded to OpenAI)';
|
|
1946
|
+
}
|
|
1947
|
+
sel.appendChild(opt);
|
|
1948
|
+
});
|
|
1949
|
+
// Enforce cap on submit: caller will trim again before sending
|
|
1950
|
+
// If still empty, retry shortly in case the panel hasn't fully constructed fields yet
|
|
1951
|
+
if(!files.length){
|
|
1952
|
+
setTimeout(function(){ try{ Ai.renderFilesSelector(selectId, opts); }catch(_e){} }, 200);
|
|
1953
|
+
}
|
|
1954
|
+
}catch(e){
|
|
1955
|
+
console.warn('Ai.renderFilesSelector error', e);
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1816
1958
|
|
|
1959
|
+
Ai.runPromptSelection = function(btnOrObjectId, selectId, filesSelectId){
|
|
1960
|
+
try{
|
|
1961
|
+
// Back-compat: allow old signature (objectId, selectId, filesSelectId)
|
|
1962
|
+
var btn = (btnOrObjectId && btnOrObjectId.tagName) ? btnOrObjectId : null;
|
|
1963
|
+
var objectId = btn ? selectId : btnOrObjectId;
|
|
1964
|
+
var realSelectId = btn ? filesSelectId : selectId;
|
|
1965
|
+
var realFilesSelectId = btn ? arguments[3] : filesSelectId; // handle both signatures
|
|
1966
|
+
|
|
1967
|
+
if (btn) {
|
|
1968
|
+
if (btn.dataset.running === '1') { return; }
|
|
1969
|
+
btn.dataset.running = '1';
|
|
1970
|
+
btn.classList.add('joe-loading');
|
|
1971
|
+
btn.setAttribute('disabled','disabled');
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
var sel = document.getElementById(realSelectId);
|
|
1975
|
+
if(!sel || !sel.value){
|
|
1976
|
+
alert('Select a prompt first.');
|
|
1977
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
var promptId = sel.value;
|
|
1981
|
+
// Collect selected OpenAI file ids (trim to 10)
|
|
1982
|
+
var fileIds = [];
|
|
1983
|
+
try{
|
|
1984
|
+
var fsel = document.getElementById(realFilesSelectId);
|
|
1985
|
+
if(fsel){
|
|
1986
|
+
var selected = Array.prototype.slice.call(fsel.selectedOptions || []);
|
|
1987
|
+
fileIds = selected.map(function(o){ return o.value; }).filter(Boolean);
|
|
1988
|
+
if(fileIds.length > 10){ fileIds = fileIds.slice(0,10); }
|
|
1989
|
+
}
|
|
1990
|
+
}catch(_e){}
|
|
1991
|
+
|
|
1992
|
+
// Build references for this object when the prompt expects the current itemtype
|
|
1993
|
+
var obj = (_joe && _joe.current && _joe.current.object) || {};
|
|
1994
|
+
var prompt = $J.get(promptId,'ai_prompt');
|
|
1995
|
+
var params = {};
|
|
1996
|
+
try{
|
|
1997
|
+
(prompt && prompt.content_items || []).forEach(function(ci){
|
|
1998
|
+
if(ci && ci.itemtype === obj.itemtype && ci.reference){
|
|
1999
|
+
params[ci.reference] = obj._id;
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
}catch(_e){}
|
|
2003
|
+
|
|
2004
|
+
$.ajax('/API/plugin/chatgpt/executeJOEAiPrompt',{
|
|
2005
|
+
method:'POST',
|
|
2006
|
+
data:{
|
|
2007
|
+
ai_prompt: promptId,
|
|
2008
|
+
params: params,
|
|
2009
|
+
openai_file_ids: fileIds
|
|
2010
|
+
},
|
|
2011
|
+
success:function(resp){
|
|
2012
|
+
try{
|
|
2013
|
+
// Try to apply a patch if the response contains one
|
|
2014
|
+
var text = (resp && resp.response) || '';
|
|
2015
|
+
if(text){
|
|
2016
|
+
try{
|
|
2017
|
+
var parsed = JSON.parse(text);
|
|
2018
|
+
if(parsed && parsed.patch){
|
|
2019
|
+
_joe.Ai.applyAutofillPatch(parsed.patch);
|
|
2020
|
+
}
|
|
2021
|
+
}catch(_e){ /* ignore parse errors */ }
|
|
2022
|
+
}
|
|
2023
|
+
}catch(_e){}
|
|
2024
|
+
// Refresh AI responses field
|
|
2025
|
+
try{ _joe.Fields.rerender && _joe.Fields.rerender('ai_responses'); }catch(_e){}
|
|
2026
|
+
if(_joe.toast){
|
|
2027
|
+
_joe.toast('AI prompt run complete.');
|
|
2028
|
+
}
|
|
2029
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
2030
|
+
},
|
|
2031
|
+
error:function(xhr,status,err){
|
|
2032
|
+
var message = (xhr && (xhr.responseJSON && (xhr.responseJSON.error || xhr.responseJSON.code) || xhr.responseText)) || err || status || 'Prompt run failed';
|
|
2033
|
+
alert(message);
|
|
2034
|
+
if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); }
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
2037
|
+
}catch(e){
|
|
2038
|
+
console.warn('runPromptSelection error', e);
|
|
2039
|
+
alert('Failed to run prompt.');
|
|
2040
|
+
try{ if (btn) { btn.dataset.running=''; btn.classList.remove('joe-loading'); btn.removeAttribute('disabled'); } }catch(_e){}
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
|
|
1817
2044
|
|
|
1818
2045
|
// Attach AI to _joe
|
|
1819
2046
|
if (window._joe) {
|
package/js/joe.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* --------------------------------------------------------
|
|
2
2
|
*
|
|
3
|
-
* json-object-editor - v0.10.
|
|
3
|
+
* json-object-editor - v0.10.654
|
|
4
4
|
* Created by: Corey Hadden
|
|
5
5
|
*
|
|
6
6
|
* -------------------------------------------------------- */
|
|
@@ -5357,7 +5357,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5357
5357
|
'<div class="joe-uploader-preview">'+dz_message+'</div>'+
|
|
5358
5358
|
'</div>'+
|
|
5359
5359
|
'<div class="joe-uploader-message">add a file</div>'+
|
|
5360
|
-
'<div class="joe-button joe-green-button joe-upload-cofirm-button hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
|
|
5360
|
+
'<div class="joe-button joe-green-button joe-upload-cofirm-button ui-helper-hidden hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
|
|
5361
5361
|
'</div>'
|
|
5362
5362
|
;
|
|
5363
5363
|
//var idprop = prop.idprop || '_id';
|
|
@@ -5384,7 +5384,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5384
5384
|
var jup_template,html= '';
|
|
5385
5385
|
var alink ="<a href='${url}${base64}' class='file-link' target='_blank'></a>";
|
|
5386
5386
|
var delete_btn = '<joe-uploader-file-delete-btn class="svg-shadow" onclick="_joe.Fields.uploader.remove(\''+cuid+'\',\'${filename}\');">'+self.SVG.icon.close+'</joe-uploader-delete-btn>';
|
|
5387
|
-
var label = '
|
|
5387
|
+
var label = '';
|
|
5388
5388
|
|
|
5389
5389
|
|
|
5390
5390
|
files.map(function(file){
|
|
@@ -5409,6 +5409,12 @@ this.renderHTMLContent = function(specs){
|
|
|
5409
5409
|
|
|
5410
5410
|
|
|
5411
5411
|
|
|
5412
|
+
// Build the label with filename and optional OpenAI file id on a new line
|
|
5413
|
+
label = '<joe-uploader-file-label>'+(file.filename || '')+'</joe-uploader-file-label>';
|
|
5414
|
+
if(file.openai_file_id){
|
|
5415
|
+
label += '<joe-uploader-file-oaid >'+file.openai_file_id+'</joe-uploader-file-oaid>';
|
|
5416
|
+
}
|
|
5417
|
+
|
|
5412
5418
|
if((file.type && file.type.contains('image')) || (!file.type && (file.url||file.base64).split('.').contains(['jpg','jpeg','png','gif','svg']))){
|
|
5413
5419
|
jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+filesize+delete_btn+'</joe-uploader-file>';
|
|
5414
5420
|
}else if (docTypes.includes((file.url || file.base64).split('.').pop())) {
|
|
@@ -5420,7 +5426,16 @@ this.renderHTMLContent = function(specs){
|
|
|
5420
5426
|
'<joe-uploader-file-extension >.'+(file.type.split('/')[1] || '???')+'</joe-uploader-file-extension>'+
|
|
5421
5427
|
alink+label+filesize+delete_btn+'</joe-uploader-file>';
|
|
5422
5428
|
}
|
|
5423
|
-
|
|
5429
|
+
// Build optional OpenAI retry/upload button
|
|
5430
|
+
var openaiBtn = '';
|
|
5431
|
+
var needRetry = (!file.openai_file_id || file.openai_status == 'error');
|
|
5432
|
+
var hasUrl = !!file.url;
|
|
5433
|
+
if(hasUrl && needRetry){
|
|
5434
|
+
var openaiLabel = (file.openai_status == 'error') ? 'Retry OpenAI' : 'Upload to OpenAI';
|
|
5435
|
+
var safeFilename = (file.filename || '').replace(/'/g,"\\'");
|
|
5436
|
+
openaiBtn = '<joe-uploader-openai-btn class="joe-button" style="margin-left:6px;" onclick="_joe.SERVER.Plugins.openaiRetryFromUrl(\''+cuid+'\',\''+safeFilename+'\');">'+openaiLabel+'</joe-uploader-openai-btn>';
|
|
5437
|
+
}
|
|
5438
|
+
html += fillTemplate(jup_template,file) + openaiBtn;
|
|
5424
5439
|
})
|
|
5425
5440
|
return html+'<div class="clear"></div>';
|
|
5426
5441
|
}
|
|
@@ -5437,7 +5452,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5437
5452
|
$(joe_uploader.dropzone).find('img,div').replaceWith('<img src="' + base64 + '">')
|
|
5438
5453
|
//joe_uploader.dropzone.html('<img src="' + base64 + '">');
|
|
5439
5454
|
joe_uploader.message.html('<b>'+file.name+'</b> selected');
|
|
5440
|
-
joe_uploader.confirmBtn.removeClass('hidden');
|
|
5455
|
+
joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5441
5456
|
}else {
|
|
5442
5457
|
results.innerHTML = 'Nothing to upload.';
|
|
5443
5458
|
}
|
|
@@ -5468,7 +5483,7 @@ this.renderHTMLContent = function(specs){
|
|
|
5468
5483
|
}
|
|
5469
5484
|
joe_uploader.files.push(temp_file);
|
|
5470
5485
|
$(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
|
|
5471
|
-
joe_uploader.confirmBtn.removeClass('hidden');
|
|
5486
|
+
joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5472
5487
|
}else {
|
|
5473
5488
|
results.innerHTML = 'Nothing to upload.';
|
|
5474
5489
|
}
|
|
@@ -5536,6 +5551,20 @@ this.renderHTMLContent = function(specs){
|
|
|
5536
5551
|
var fileobj = joe_uploader.files.where({filename:filename})[0];
|
|
5537
5552
|
fileobj.uploaded = new Date();
|
|
5538
5553
|
fileobj.url = url;
|
|
5554
|
+
// Apply captured OpenAI info if present
|
|
5555
|
+
var key = self.current.object._id + '/' + filename;
|
|
5556
|
+
var oi = _joe._lastOpenAI && _joe._lastOpenAI[key];
|
|
5557
|
+
fileobj.openai_purpose = 'assistants';
|
|
5558
|
+
if(oi){
|
|
5559
|
+
fileobj.openai_file_id = oi.openai_file_id || null;
|
|
5560
|
+
fileobj.openai_status = (oi.openai_file_id && 'ok') || (oi.openai_error && 'error') || 'not_uploaded';
|
|
5561
|
+
fileobj.openai_error = oi.openai_error || null;
|
|
5562
|
+
if(oi.openai_error){
|
|
5563
|
+
joe_uploader.message.append('<div>OpenAI error: '+oi.openai_error+'</div>');
|
|
5564
|
+
}
|
|
5565
|
+
}else{
|
|
5566
|
+
fileobj.openai_status = fileobj.openai_status || 'not_uploaded';
|
|
5567
|
+
}
|
|
5539
5568
|
delete fileobj.base64;
|
|
5540
5569
|
}
|
|
5541
5570
|
|
|
@@ -5606,6 +5635,14 @@ this.renderHTMLContent = function(specs){
|
|
|
5606
5635
|
self.uploaders[id].dropzone = $(this).find('.joe-uploader-dropzone');
|
|
5607
5636
|
self.uploaders[id].confirmBtn = $(this).find('.joe-upload-cofirm-button');
|
|
5608
5637
|
|
|
5638
|
+
// If there are pending files (not uploaded or missing url), show the confirm button
|
|
5639
|
+
var pending = (self.uploaders[id].files || []).some(function(file){
|
|
5640
|
+
return $c.isObject(file) && (!file.uploaded || !file.url);
|
|
5641
|
+
});
|
|
5642
|
+
if(pending){
|
|
5643
|
+
self.uploaders[id].confirmBtn.removeClass('ui-helper-hidden hidden');
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5609
5646
|
});
|
|
5610
5647
|
};
|
|
5611
5648
|
encapsulateFieldType('uploader',self.renderUploaderField,
|
|
@@ -9942,6 +9979,13 @@ logit(intent)
|
|
|
9942
9979
|
url = prefix+response.Key;
|
|
9943
9980
|
}
|
|
9944
9981
|
var filename = response.Key.replace(self.current.object._id+'/','');
|
|
9982
|
+
// Stash OpenAI info for use during finalization
|
|
9983
|
+
_joe._lastOpenAI = _joe._lastOpenAI || {};
|
|
9984
|
+
_joe._lastOpenAI[response.Key] = {
|
|
9985
|
+
openai_file_id: response.openai_file_id,
|
|
9986
|
+
openai_purpose: response.openai_purpose,
|
|
9987
|
+
openai_error: response.openai_error
|
|
9988
|
+
};
|
|
9945
9989
|
if(field.url_field){
|
|
9946
9990
|
var nprop = {};
|
|
9947
9991
|
_joe.current.object[field.url_field] = url;
|
|
@@ -9966,6 +10010,53 @@ logit(intent)
|
|
|
9966
10010
|
file: { base64:data.base64, extension:data.extension, type:data.contentType, filename: (_joe.current.object._id+data.extension), name: (_joe.current.object._id+data.extension) },
|
|
9967
10011
|
field: data.field
|
|
9968
10012
|
}, callback);
|
|
10013
|
+
},
|
|
10014
|
+
// Retry uploading a specific file to OpenAI using its URL.
|
|
10015
|
+
// Invoked by the uploader row action.
|
|
10016
|
+
openaiRetryFromUrl:function(uploader_id, filename){
|
|
10017
|
+
try{
|
|
10018
|
+
var joe_uploader = self.uploaders[uploader_id];
|
|
10019
|
+
if(!joe_uploader){ return alert('Uploader not found'); }
|
|
10020
|
+
var file = joe_uploader.files.where({filename:filename})[0];
|
|
10021
|
+
if(!file){ return alert('File not found'); }
|
|
10022
|
+
if(!file.url){ return alert('No URL to upload to OpenAI'); }
|
|
10023
|
+
var btn = $(joe_uploader.preview).find("joe-uploader-openai-btn:contains('"+filename+"')");
|
|
10024
|
+
joe_uploader.message.append('<div>Uploading to OpenAI...</div>');
|
|
10025
|
+
$.ajax({
|
|
10026
|
+
url: (location.port && '//'+__jsc.hostname+':'+__jsc.port+'/API/plugin/chatgpt/filesRetryFromUrl/') || '//'+__jsc.hostname+'/API/plugin/chatgpt/filesRetryFromUrl/',
|
|
10027
|
+
type:'POST',
|
|
10028
|
+
data:{
|
|
10029
|
+
url:file.url,
|
|
10030
|
+
filename:file.filename,
|
|
10031
|
+
contentType:file.type
|
|
10032
|
+
},
|
|
10033
|
+
success:function(resp){
|
|
10034
|
+
if(resp && resp.success && resp.openai_file_id){
|
|
10035
|
+
file.openai_file_id = resp.openai_file_id;
|
|
10036
|
+
file.openai_purpose = resp.openai_purpose || 'assistants';
|
|
10037
|
+
file.openai_status = 'ok';
|
|
10038
|
+
file.openai_error = null;
|
|
10039
|
+
joe_uploader.message.append('<div>OpenAI file attached</div>');
|
|
10040
|
+
}else{
|
|
10041
|
+
file.openai_status = 'error';
|
|
10042
|
+
file.openai_error = (resp && resp.error) || 'Upload failed';
|
|
10043
|
+
joe_uploader.message.append('<div>OpenAI error: '+(file.openai_error)+'</div>');
|
|
10044
|
+
}
|
|
10045
|
+
// Refresh preview to reflect new status/button
|
|
10046
|
+
$(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
|
|
10047
|
+
// Save object so file metadata is persisted
|
|
10048
|
+
self.updateObject(null, null, true);
|
|
10049
|
+
},
|
|
10050
|
+
error:function(xhr,status,err){
|
|
10051
|
+
var message = (xhr && (xhr.responseJSON && (xhr.responseJSON.error || xhr.responseJSON.code) || xhr.responseText)) || err || status || 'Retry failed';
|
|
10052
|
+
file.openai_status = 'error';
|
|
10053
|
+
file.openai_error = message;
|
|
10054
|
+
joe_uploader.message.append('<div>OpenAI error: '+message+'</div>');
|
|
10055
|
+
}
|
|
10056
|
+
});
|
|
10057
|
+
}catch(e){
|
|
10058
|
+
alert(e && e.message || e);
|
|
10059
|
+
}
|
|
9969
10060
|
}
|
|
9970
10061
|
},
|
|
9971
10062
|
ready:{
|