diff --git a/bricks/agent.js b/bricks/agent.js new file mode 100644 index 0000000..6b3a562 --- /dev/null +++ b/bricks/agent.js @@ -0,0 +1,232 @@ +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{ + if (f.type.startsWith('video/')) { + var url = URL.createObjectURL(f); + this.v_w = new bricks.VideoPlayer({ + url:url, + autoplay:true, + width: '100%' + }); + } else if (f.type.startsWith('audio')){ + var url = URL.createObjectURL(f); + this.a_w = new bricks.AudioPlayer({ + url:url, + autoplay:true, + width: '100%' + }); + } else if (f.type.startsWith('image')){ + var url = URL.createObjectURL(f); + mdtext += `![${f.name}](${url})`; + } else { + var url = URL.createObjectURL(f); + mdtext += `[${f.name}](${url})`; + } + }); + this.clear_widgets(); + var w = new bricks.MdWidget({ + width: '100%', + mdtext:mdtext + }); + console.log('mdtext=', mdtext); + this.add_widget(w); + if (this.v_w){ + this.add_widget(this.v_w); + } + if (this.a_w){ + this.add_widget(this.a_w); + } + } +} + +bricks.AgentModel = class extends bricks.JsWidget { + /* + { + icon: + url: + params: + reply_url: + } + */ + constructor(llmio, opts){ + super(opts); + this.llmio = llmio; + } + async model_inputed(data){ + var mout = new bricks.AgentOutput({ + }); + this.llmio.o_w.add_widget(mout); + var d = 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(); + mout.estimate_w.show(); + } + chunk_response(mout, l){ + l = l.trim(); + try { + var d = JSON.parse(l); + } catch(e){ + console.log(l, 'is not a json data'); + return + } + console.log('l=', l, 'd=', d); + mout.update_data(d); + } + chunk_ended(){ + console.log('chunk end'); + } +} +bricks.AgentIO = class extends bricks.VBox { + /* + options: + { + user_icon: + agent_icon: + agent_url: + params: + } + */ + constructor(opts){ + super(opts); + this.llmmodels = []; + this.msg_box = new bricks.VScrollPanel({ + width: '100%', + css: 'filler' + }); + this.inputw = new bricks.TextFiles({}); + this.inputw.bind('inputed', this.user_inputed.bind(this)); + this.add_widget(this.msg_box); + this.add_widget(this.inputw); + } + user_inputed(e){ + this.show_input(e.params); + new llmmodel = new bricks.LlmModel(); + llmmodel.model_inputed(e.params); + } + async show_input(params){ + var box = new bricks.HBox({width:'100%'}); + var data = params; + var w = new bricks.AgentInputView({ + width: '100%', + 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); + } +} + +bricks.Factory.register('AgentIO', bricks.AgentIO); diff --git a/bricks/llm.js b/bricks/llm.js index a1813ef..a1facd0 100644 --- a/bricks/llm.js +++ b/bricks/llm.js @@ -113,12 +113,15 @@ bricks.ModelOutput = class extends bricks.VBox { var w = await bricks.widgetBuild(desc, this); this.estimate_w.disabled(true); } - async update_data(data){ + run_stopped(){ if (this.run) { this.run.stop_timepass(); this.content.remove_widget(this.run); this.run = null; } + } + async update_data(data){ + this.run_stopped(); this.filler.update(data); if (data.llmusageid) { this.llmusageid = data.llmusageid @@ -208,7 +211,10 @@ bricks.LlmModel = class extends bricks.JsWidget { var d = this.inputdata2uploaddata(data); var hr = new bricks.HttpResponseStream(); var resp = await hr.post(this.opts.url, {params:d}); - if (! resp) return; + if (! resp) { + mout.run_stopped(); + return; + } await hr.handle_chunk(resp, this.chunk_response.bind(this, mout)); this.chunk_ended(); } else { @@ -216,7 +222,10 @@ bricks.LlmModel = class extends bricks.JsWidget { 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) return; + if (! resp) { + mout.run_stopped(); + return; + } mout.update_data(resp); } mout.estimate_w.show(); diff --git a/bricks/textfiles.js b/bricks/textfiles.js index 3586f46..eb2aaae 100644 --- a/bricks/textfiles.js +++ b/bricks/textfiles.js @@ -50,7 +50,7 @@ bricks.TextFiles = class extends bricks.VBox { this.inputfilew = new bricks.UiFile({name:add_file}); this.filesbar = new bricks.DynamicColumn({}); this.add_files = []; - this.textw = new bricks.UiText({}); + this.textw = new bricks.UiText({name: 'prompt'}); addfilew = new bricks.Svg({ cwidth: 1.5, cheight: 1.5, @@ -76,7 +76,15 @@ bricks.TextFiles = class extends bricks.VBox { this.inputfilew.bind('changed', this.file_added.bind(this)); } input_finished(){ - + var txt = this.textw.getValue(); + if (txt.prompt || txt.prompt.length<1){ + return; + } + var d = { + add_files: self.add_files, + prompt: prompt + } + this.dispatch('inputed', d); } file_added(e){ input_finished.hide();