bricks = window.bricks || {} bricks.LlmMsgAudio = class extends bricks.UpStreaming { constructor(opts){ super(opts); this.olddata = ''; this.data = ''; this.cn_p = ["。",",","!","?","\n"]; this.other_p = [".",",","!","?","\n"]; this.audio = AudioPlayer({}) } detectLanguage(text) { try { const detector = new Intl.LocaleDetector(); const locale = detector.detectLocaleOf(text); return locale.language; } catch (error) { console.error('无法检测语言:', error); return '未知'; } } send(data){ var newdata = data.slice(this.olddata.length); this.olddata = data; this.data += newdata; var lang = detectLaguage(this.data); var parts; if (lang='zh'){ parts = this.data.split(this.cn_p).filter(part => part.trim()!== ''); } else { parts = this.data.split(this.oter_p).filter(part => part.trim()!== ''); } for(var i=0;i{ d.append(k, this.llmio.kdb_setting[k]); }); } } else { if (! this.llmio.model_inputed) d.model = this.opts.model; d.llmid = this.opts.llmid; if (this.llmio.enabled_kdb){ Object.keys(this.llmio.kdb_setting).forEach(k =>{ d[k] = this.llmio.kdb_setting[k]; }); } } return d; } async model_inputed(data){ var mout = new bricks.ModelOutput({ textvoice:this.textvoice, tts_url:this.tts_url, icon:this.opts.icon, response_mode: this.opts.response_mode, model:this.opts.model, modelname:this.opts.modelname, estimate_url:this.llmio.estimate_url }); this.llmio.o_w.add_widget(mout); if (this.response_mode == 'stream' || this.response_mode == 'async') { var d = this.inputdata2uploaddata(data); var hr = new bricks.HttpResponseStream(); var resp = await hr.post(this.opts.url, {params:d}); if (! resp) { mout.run_stopped(); return; } await hr.handle_chunk(resp, this.chunk_response.bind(this, mout)); this.chunk_ended(); } else { var d = this.inputdata2uploaddata(data); console.log('data_inouted=', data, 'upload_data=', d); var hj = new bricks.HttpJson() var resp = await hj.post(this.opts.url, {params:d}); if (! resp) { mout.run_stopped(); return; } mout.update_data(resp); } mout.estimate_w.show(); } 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){ l = l.trim(); try { var d = JSON.parse(l); } catch(e){ console.log(l, 'is not a json data'); return } if (this.opts.response_mode == 'async'){ if(d.status != 'SUCCEEDED' && d.status != 'FAILED' ){ console.log('filter all message not successed or failed', d); return; } } console.log('l=', l, 'd=', d); mout.update_data(d); } chunk_ended(){ console.log('chunk end'); } } bricks.LlmIO = class extends bricks.VBox { /* options: { user_icon: list_models_url: input_fields: models: } models:[ { icon: model: modelnmae: 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:3}); var bottom_box = new bricks.HBox({cheight:3}); this.i_w = new bricks.Svg({ rate:2, url:bricks_resource('imgs/input.svg'), margin:'14px', tip:'input data', css:'clickable' }); this.nm_w = new bricks.Svg({ rate:2, url:bricks_resource('imgs/add.svg'), margin:'14px', tip:'add new model', css:'clickable' }); if (this.enabled_kdb){ this.kdb_w = new bricks.Svg({ rate:2, url:bricks_resource('imgs/kdb.svg'), margin:'14px', tip:'setup kdb config', css:'clickable' }); bottom_box.add_widget(this.kdb_w); this.kdb_w.bind('click', this.setup_kdb.bind(this)); } this.input_fields.forEach(f => { if (f.name == 'model') this.model_inputed = true; }); bottom_box.add_widget(this.i_w); bottom_box.add_widget(this.nm_w); this.nm_w.bind('click', this.open_search_models.bind(this)); this.i_w.bind('click', this.open_input_widget.bind(this)); this.o_w = new bricks.Filler({overflow:'auto'}); this.add_widget(this.title_w); this.add_widget(this.o_w); if (this.models.length < 2 && this.tts_url){ this.textvoice = true; } this.add_widget(bottom_box); this.models.forEach( m =>{ this.show_added_model(m); }); } async show_input(params){ var box = new bricks.HBox({width:'100%'}); var data = inputdata2dic(params); console.log('data=', data); var w = new bricks.UserInputView({ width: '100%', input_fields: this.input_fields, data:data }); w.set_css(this.msg_css||'user_msg'); w.set_css('filler'); var img = new bricks.Svg({rate:2,url:this.user_icon||bricks_resource('imgs/chat-user.svg')}); // box.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0})); box.add_widget(w); box.add_widget(img); this.o_w.add_widget(box); } show_added_model(m){ if (this.textvoice){ m.textvoice = true; m.tts_url = this.tts_url; } var lm = new bricks.LlmModel(this, m); this.llmmodels.push(lm); var tw = lm.render_title(); this.title_w.add_widget(tw); } async open_search_models(event){ event.preventDefault(); event.stopPropagation(); var rect = this.showRectage(); var opts = { title:"select model", icon:bricks_resource('imgs/search.png'), auto_destroy:true, auto_open:true, auto_dismiss:false, movable:true, top:rect.top + 'px', left:rect.left + 'px', width: rect.right - rect.left + 'px', height: rect.bottom - rect.top + 'px' } var w = new bricks.PopupWindow(opts); var sopts = { data_url:this.list_models_url, data_params:{ llmid:this.models[0].llmid, llmcatelogid:this.models[0].llmcatelogid }, data_method:'POST', col_cwidth: 24, record_view:{ widgettype:"VBox", options:{ cheight:20, css:"card" }, subwidgets:[ { widgettype: "HBox", options: { cheight: 2 }, subwidgets: [ { widgettype:"Svg", options: { url:"/appbase/show_icon.dspy?id=${iconid}" } }, { widgettype:"Title6", options:{ text:"${name}" } } ] }, { widgettype:"Filler", options:{ css:"scroll" }, subwidgets:[ { widgettype:"VBox", options:{ css:"subcard" }, subwidgets:[ { widgettype:"Text", options:{ text:"模型描述:${description}", wrap:true } }, { widgettype:"Text", options:{ text:"启用日期:${enable_date}" } } ] } ] } ] } }; var cols = new bricks.Cols(sopts); cols.bind('record_click', this.add_new_model.bind(this)); cols.bind('record_click', w.dismiss.bind(w)); w.add_widget(cols); w.open(); } async add_new_model(event){ event.preventDefault(); event.stopPropagation(); var llm = event.params; this.models.push(llm); this.show_added_model(llm); } async setup_kdb(event){ var data = this.kdb_setting if (! this.kdb_setting.prompt_tmpl){ this.kdb_setting.prompt_tmpl = `将下面的提示词按照后面贴出的参考知识改写: {{prompt}} {% for r in records %} 参考{{loop.index}}:{{r.content}} {% endfor %}`; } event.preventDefault(); event.stopPropagation(); var rect = this.showRectage(); var opts = { title:"choose kdb", icon:bricks_resource('imgs/kdb.svg'), auto_destroy:true, auto_open:true, auto_dismiss:false, movable:true, top:rect.top + 'px', left:rect.left + 'px', width: rect.right - rect.left + 'px', height: rect.bottom - rect.top + 'px' } var w = new bricks.PopupWindow(opts); var fopts = { fields:[ { "name": "kdbids", "value":data.kdbids, "uitype":"checkbox", "multicheck": true, "required": true, "label":"知识库", "valueField":"id", "textField":"name", "dataurl":this.get_kdb_url }, { "name":"recall_cnt", "uitype":"int", "value": data.recall_cnt, "label":"召回数量" }, { "name":"prompt_tmpl", "value":data.prompt_tmpl, "uitype":"text", "required": true, "label":"模版" } ] } var fw = new bricks.Form(fopts); fw.bind('submit', this.handle_kdb_setup.bind(this)); fw.bind('submit', w.destroy.bind(w)); w.add_widget(fw); w.open(); } async handle_kdb_setup(event){ event.preventDefault(); event.stopPropagation(); this.kdb_setting = formdata2object(event.params); console.log('kdb_setting=', this.kdb_setting); } async open_input_widget(event){ event.preventDefault(); event.stopPropagation(); var rect = this.showRectage(); var opts = { title:"input data", icon:bricks_resource('imgs/input.svg'), auto_destroy:true, auto_open:true, auto_dismiss:false, movable:true, top:rect.top + 'px', left:rect.left + 'px', width: rect.right - rect.left + 'px', height: rect.bottom - rect.top + 'px' } var w = new bricks.PopupWindow(opts); var fopts = { fields:this.input_fields } var fw = new bricks.Form(fopts); fw.bind('submit', this.handle_input.bind(this)); fw.bind('submit', w.destroy.bind(w)); w.add_widget(fw); w.open(); } async handle_input(event){ var params = event.params; this.show_input(params); for(var i=0;i