362 lines
9.2 KiB
JavaScript
362 lines
9.2 KiB
JavaScript
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<parts.length - 1; i++){
|
||
super.send(parts[i]);
|
||
}
|
||
this.data = parts[parts.length - 1];
|
||
}
|
||
async go(){
|
||
var resp = await super.go();
|
||
this.audio.set_source_from_response(resp);
|
||
return resp;
|
||
}
|
||
}
|
||
bricks.AgentOut = class extends bricks.VBox {
|
||
constructor(opts){
|
||
super(opts);
|
||
this.rc_w = null;
|
||
this.c_w = null;
|
||
this.v_w = null;
|
||
this.i_w = null;
|
||
this.a_w = null;
|
||
this.glb_w = null;
|
||
this.images = [];
|
||
this.reasoning_content = '';
|
||
this.content = '';
|
||
this.error = '';
|
||
}
|
||
|
||
update(data){
|
||
if (data.audio){
|
||
var url = data.audio;
|
||
if (! data.audio.startsWith('http')){
|
||
if (! data.audio.startsWith('data:audio/')){
|
||
url = 'data:audio/wav;base64,' + url;
|
||
}
|
||
}
|
||
if (!this.a_w) {
|
||
this.a_w = new bricks.AudioPlayer({
|
||
width: '100%',
|
||
autoplay: true,
|
||
url: url,
|
||
cheight:2
|
||
});
|
||
} else {
|
||
this.a_w.add_url(url);
|
||
}
|
||
}
|
||
if (data.glb){
|
||
this.glb_w = new bricks.GlbViewer({
|
||
url:data.glb,
|
||
width: '100%'
|
||
});
|
||
}
|
||
if (data.video){
|
||
if (!this.v_w){
|
||
this.v_w = new bricks.VideoPlayer({
|
||
width: '100%',
|
||
url: data.video,
|
||
autoplay: true
|
||
});
|
||
} else {
|
||
this.v_w.add_url(data.video);
|
||
}
|
||
}
|
||
if (data.error){
|
||
this.error += data.error;
|
||
}
|
||
if (data.reasoning_content){
|
||
this.reasoning_content += data.reasoning_content;
|
||
}
|
||
if (data.content){
|
||
this.content += data.content;
|
||
}
|
||
if (data.image){
|
||
if (Array.isArray(data.image)){
|
||
this.images.concat(data.image);
|
||
} else {
|
||
this.images.push(data.image);
|
||
}
|
||
}
|
||
this.clear_widgets();
|
||
if (this.error.length) {
|
||
var txt = bricks.escapeSpecialChars(this.error);
|
||
this.c_w = new bricks.Text({
|
||
text: this.error,
|
||
wrap: true,
|
||
halign: 'left',
|
||
css: 'resp-error',
|
||
width: '100%'
|
||
});
|
||
this.add_widget(this.c_w);
|
||
}
|
||
if (this.reasoning_content.length) {
|
||
var txt = bricks.escapeSpecialChars(this.reasoning_content);
|
||
this.rc_w = new bricks.MdWidget({
|
||
mdtext: this.reasoning_content,
|
||
css: 'thinking-content',
|
||
bgcolor: '#f0d0d0',
|
||
width: '100%'
|
||
});
|
||
this.add_widget(this.rc_w);
|
||
}
|
||
if (this.content.length) {
|
||
var txt = bricks.escapeSpecialChars(this.content);
|
||
this.c_w = new bricks.MdWidget({
|
||
mdtext: this.content,
|
||
css: 'resp-content',
|
||
width: '100%'
|
||
});
|
||
this.add_widget(this.c_w);
|
||
}
|
||
if (this.v_w) {
|
||
this.add_widget(this.v_w);
|
||
}
|
||
if (this.glb_w){
|
||
this.add_widget(this.glb_w);
|
||
}
|
||
if (this.a_w) {
|
||
this.add_widget(this.a_w);
|
||
}
|
||
if (this.images.length){
|
||
this.images.forEach( i => {
|
||
var w = new bricks.Image({
|
||
width: '100%',
|
||
url: i
|
||
});
|
||
this.add_widget(w)
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
bricks.AgentOutput = class extends bricks.VBox {
|
||
/* {
|
||
icon:
|
||
reply_url:
|
||
}
|
||
完成模型输出的控件的初始化以及获得数据后的更新, 更新是的数据在流模式下,需要使用累积数据
|
||
*/
|
||
constructor(opts){
|
||
if(! opts){
|
||
opts = {};
|
||
}
|
||
opts.width = '100%';
|
||
opts.height = 'auto';
|
||
super(opts);
|
||
var hb = new bricks.HBox({width:'100%', cheight:2});
|
||
this.img = new bricks.Svg({
|
||
rate:2,
|
||
tip:this.opts.modelname,
|
||
url:this.icon||bricks_resource('imgs/agent.svg')
|
||
});
|
||
hb.add_widget(this.img);
|
||
this.add_widget(hb);
|
||
|
||
this.content = new bricks.HBox({width:'100%'});
|
||
this.add_widget(this.content);
|
||
this.run = new bricks.BaseRunning({target:this, cheight:2, cwidth:2});
|
||
this.content.add_widget(this.run);
|
||
this.filler = new bricks.AgentOut({width: '100%', css: 'card'});
|
||
this.filler.set_css('filler');
|
||
this.content.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
|
||
this.content.add_widget(this.filler);
|
||
// this.content.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
|
||
}
|
||
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
|
||
}
|
||
return;
|
||
}
|
||
finish(){
|
||
console.log('finished')
|
||
}
|
||
}
|
||
|
||
bricks.AgentInputView = class extends bricks.VBox {
|
||
constructor(opts){
|
||
super(opts);
|
||
this.v_w = null;
|
||
this.a_v = null;
|
||
this.show_input(this.data);
|
||
}
|
||
show_input(data){
|
||
var mdtext = bricks.escapeSpecialChars(data.prompt) + '\n';
|
||
if (data.add_files){
|
||
data.add_files.forEach(f =>{
|
||
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 += ``;
|
||
} 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:
|
||
method:
|
||
reply_url:
|
||
}
|
||
*/
|
||
constructor(llmio, opts){
|
||
super(opts);
|
||
this.llmio = llmio;
|
||
}
|
||
async set_inputed(data){
|
||
var mout = new bricks.AgentOutput({
|
||
reply_url: this.opts.reply_url
|
||
});
|
||
this.llmio.msg_box.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){
|
||
if (!opts.height) opts.height = '100%';
|
||
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);
|
||
var agent = new bricks.AgentModel({
|
||
url:this.opts.url,
|
||
params: this.opts.params,
|
||
method: this.opts.method || 'POST',
|
||
reply_url: this.opts.reply_url
|
||
});
|
||
agent.set_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.msg_box.add_widget(box);
|
||
}
|
||
}
|
||
|
||
bricks.Factory.register('AgentIO', bricks.AgentIO);
|