bricks/bricks/llm.js
2024-07-31 16:06:49 +08:00

272 lines
7.0 KiB
JavaScript

bricks = window.bricks || {}
bricks.ModelOutput = class extends bricks.HBox {
/* {
icon:
output_view:
}
完成模型输出的控件的初始化以及获得数据后的更新, 更新是的数据在流模式下,需要使用累积数据
*/
constructor(opts){
if(! opts){
opts = {};
}
opts.width = '100%';
super(opts);
this.logid = null;
this.img = new bricks.Icon({rate:2,url:this.icon||bricks_resource('imgs/llm.png')});
this.run = new bricks.BaseRunning({target:this});
this.add_widget(this.img);
this.add_widget(this.run);
this.filler = new bricks.VBox({});
this.filler.set_css('filler');
this.add_widget(this.filler);
this.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
this.build_estimate_widgets();
}
build_estimate_widgets(){
if (!this.estimate_url) return;
this.estimate_w = new bricks.HBox({width:'100%', cheight:2});
var txtw = new bricks.Text({
otext:'结果满意吗?',
i18n:true,
});
var likew = new bricks.Icon({rate:2, url:bricks_resource('imgs/like.png')});
var unlikew = new bricks.Icon({rate:2, url:bricks_resource('imgs/unlike.png')});
likew.bind('click', this.estimate_llm.bind(this, 1));
unlikew.bind('click', this.estimate_llm.bind(this, -1))
this.estimate_w.add_widget(txtw);
this.estimate_w.add_widget(likew);
this.estimate_w.add_widget(new bricks.BlankIcon({rate:1, flexShrink:0}));
this.estimate_w.add_widget(unlikew);
likew.set_style('cursor', 'pointer');
unlikew.set_style('cursor', 'pointer');
this.estimate_w.hide();
}
async estimate_llm(val, event){
var desc = {
"widgettype":"urlwidget",
"options":{
"params":{
"logid":this.logid,
"value":val
},
"url":this.estimate_url
}
};
var icon = event.target.bricks_widget;
icon.rate = 2;
icon.charsize_sizing();
var w = await bricks.widgetBuild(desc, this);
this.estimate_w.disabled(true);
}
async update_data(data){
if (this.run) {
this.run.stop_timepass();
this.remove_widget(this.run);
}
console.log('update_data():data=', data);
this.run = null;
var w = await bricks.widgetBuild(this.output_view, this.llmio, data);
w.set_css('llm_msg');
this.filler.clear_widgets();
this.filler.add_widget(w);
this.filler.add_widget(this.estimate_w);
if (data.logid){
this.logid = data.logid;
if (this.estimate_url){
this.estimate_w.show();
}
}
}
}
bricks.LlmModel = class extends bricks.JsWidget {
/*
{
icon:
model:
mapi:
url:
output_view:
params:
user_message_format:
system_message_format:
llm_message_format:
use_session:
input_from:
response_mode:stream, sync or async
}
*/
constructor(llmio, opts){
super(opts);
this.llmio = llmio;
if (opts.use_session){
this.messages = [];
}
}
render_title(){
var w = new bricks.HBox({});
w.bind('click', this.show_setup_panel.bind(this))
var img = new bricks.Icon({rate:2,url:this.opts.icon||bricks_resource('imgs/llm.png')});
var txt = new bricks.Text({text:this.opts.model});
w.add_widget(img);
w.add_widget(txt);
return w;
}
show_setup_panel(event){
}
async model_inputed(data){
var mout = new bricks.ModelOutput({
icon:this.opts.icon,
estimate_url:this.llmio.estimate_url,
output_view:this.opts.output_view});
this.llmio.o_w.add_widget(mout);
var fmt = this.opts.user_message_format || { role:'user', content:'${prompt}'};
var umsg = bricks.apply_data(fmt, data);
var d = data;
if (this.messages){
this.messages.push(umsg);
d = bricks.extend({}, data);
d.messages = this.messages;
d.model = this.opts.model;
d.modelinstanceid = this.opts.modelinstanceid;
d.modeltypeid = this.opts.modeltypeid;
} else {
d = bricks.extend({}, data);
d.model = this.opts.model;
d.mapi = this.mapi;
d.modelinstanceid = this.opts.modelinstanceid;
d.modeltypeid = this.opts.modeltypeid;
}
console.log('upload data=', d, this.options);
if (this.response_mode == 'stream' || this.response_mode == 'async') {
var hr = new bricks.HttpResponseStream();
var resp = await hr.post(this.opts.url, {params:d});
await hr.handle_chunk(resp, this.chunk_response.bind(this, mout));
this.chunk_ended();
} else {
var hj = new bricks.HttpJson()
var resp = await hj.post(this.opts.url, {params:d});
if (this.response_mode == 'sync'){
mout.update_data(resp);
if (this.messages){
var msg = this.llm_msg_format();
var lmsg = bricks.apply_data(msg, resp);
this.messages.push(lmsg)
}
} else {
;
}
}
}
is_accept_source(source){
if (this.opts.input_from == source){
return true;
}
return false;
}
llm_msg_format(){
return this.llm_message_format || {role:'assistant', content:"${content}"}
}
chunk_response(mout, l){
var d = JSON.parse(l);
if (! d.content || d.content == ''){
return;
}
d.content = bricks.escapeSpecialChars(d.content);
this.resp_data = d;
mout.update_data(d);
console.log('stream data=', d);
}
chunk_ended(){
if (! this.messages) {
console.log('this.messages is null !!!!!!!!!');
return;
}
var msg = this.llm_msg_format();
var txt = bricks.escapeSpecialChars(this.resp_data.content)
this.resp_data.content = txt;
var lmsg = bricks.apply_data(msg, this.resp_data);
this.messages.push(lmsg);
}
}
bricks.LlmIO = class extends bricks.VBox {
/*
options:
{
user_icon:
input_fields:
input_view:
output_view:
models:
}
models:[
{
icon:
model:
mapi:
url:
params:
use_session:
system_prompt:
user_parmpt:
input_from:
io_mode:stream, sync or async
}
]
*/
constructor(opts){
super(opts);
this.llmmodels = [];
this.title_w = new bricks.HBox({cheight:2});
this.i_w = new bricks.VBox({cheight:5});
this.o_w = new bricks.Filler({overflow:'auto'});
this.add_widget(this.title_w);
this.add_widget(this.o_w);
this.add_widget(this.i_w);
this.models.forEach( m =>{
var lm = new bricks.LlmModel(this, m);
this.llmmodels.push(lm);
var tw = lm.render_title();
this.title_w.add_widget(tw);
});
this.build_input();
}
async handle_input(event){
var params = event.params;
if (params.prompt)
params.prompt = bricks.escapeSpecialChars(params.prompt);
await this.show_input(params);
for(var i=0;i<this.llmmodels.length;i++){
var lm = this.llmmodels[i];
if (lm.is_accept_source('userinput')){
schedule_once(lm.model_inputed.bind(lm, params), 0.01);
// await lm.model_inputed(params);
}
};
}
async show_input(params){
var box = new bricks.HBox({width:'100%'});
var w = await bricks.widgetBuild(this.input_view, this.o_w, params);
w.set_css(this.msg_css||'user_msg');
w.set_css('filler');
var img = new bricks.Icon({rate:2,url:this.user_icon||bricks_resource('imgs/user.png')});
box.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
box.add_widget(w);
box.add_widget(img);
this.o_w.add_widget(box);
}
build_input(){
var fopts = {
fields:this.input_fields
};
var fw = new bricks.InlineForm(fopts);
fw.bind('submit', this.handle_input.bind(this));
this.i_w.add_widget(fw);
}
}
bricks.Factory.register('LlmIO', bricks.LlmIO);