0


vue通过ollama接口调用开源模型实现人机对话

vue通过ollama接口调用开源模型实现人机对话

先展示下最终效果:

第一步:先安装ollama,并配置对应的开源大模型。

安装步骤可以查看上一篇博客:

ollama搭建本地ai大模型并应用调用

第二步:需要注意两个配置,页面才可以调用

1)OLLAMA_HOST= "0.0.0.0:11434"

2)若应用部署服务器后想调用,需要配置:OLLAMA_ORIGINS=*

第三步:js流式调用大模型接口方法

async startStreaming(e) {

  if(e.ctrkey&&e.keyCode==13){

    this.form.desc+='\n';

  }

  document.getElementById("txt_suiwen").disabled="true";

  // 如果已经有一个正在进行的流式请求,则中止它

  if (this.controller) {

    this.controller.abort();

  }

  setTimeout(()=>{

    this.scrollToBottom();

   },50);

    var mymsg=this.form.desc.trim(); 

    if(mymsg.length>0){

           this.form.desc='';

           this.message.push({

               user:this.username,

               msg:mymsg

           })           

           this.message.push({

                   user:'GPT', 

                   msg:'',

                   dot:''

           });

    

         // 创建一个新的 AbortController 实例

         this.controller = new AbortController();

         const signal = this.controller.signal;

         this.arequestData.messages.push({role:"user",content:mymsg});

         try {

           const response = await fetch('http://127.0.0.1:11434/api/chat', {

             method: 'POST',

             headers: {

               'Content-Type': 'application/json'

             },

             body:JSON.stringify(this.arequestData),

             signal

           });

   

           if (!response.body) {

             this.message[this.message.length-1].msg='ReadableStream not yet supported in this browser.';

             throw new Error('ReadableStream not yet supported in this browser.');

           }

   

           const reader = response.body.getReader();

           const decoder = new TextDecoder();

           let result = '';

           this.message[this.message.length-1].dot='⚪';

           while (true) {

             const { done, value } = await reader.read();

             if (done) {

               break;

             }

             result += decoder.decode(value, { stream: true });

    

             // 处理流中的每一块数据,这里假设每块数据都是完整的 JSON 对象

             const jsonChunks = result.split('\n').filter(line => line.trim());

             //console.log(result)

             for (const chunk of jsonChunks) {

               try {

                 const data = JSON.parse(chunk);

                 //console.log(data.message.content) 

                 this.message[this.message.length-1].msg+=data.message.content;

                 setTimeout(()=>{

                   this.scrollToBottom();

                  },50); 

               } catch (e) {

                 //this.message[this.message.length-1].msg=e;

                 // 处理 JSON 解析错误

                 //console.error('Failed to parse JSON:', e); }

             }

   

             // 清空 result 以便处理下一块数据

             result = '';

           }

         } catch (error) {

           if (error.name === 'AbortError') {

             console.log('Stream aborted');

             this.message[this.message.length-1].msg='Stream aborted';

           } else {

             console.error('Streaming error:', error);

             this.message[this.message.length-1].msg='Stream error'+error;

           }

         }

         this.message[this.message.length-1].dot='';

         this.arequestData.messages.push({

                 role: 'assistant',//this.message[this.message.length-1].user,//"GPT",

                 content: this.message[this.message.length-1].msg

               })

         setTimeout(()=>{

            this.scrollToBottom();

           },50); 

           

}else{

      this.form.desc='';

    }

    document.getElementById("txt_suiwen").disabled="";

    document.getElementById("txt_suiwen").focus();

}  

}

vue完整代码如下:

<template> <el-row :gutter="12" class="demo-radius">
<div

    class="radius"

    :style="{

      borderRadius: 'base'

    }">

    <div class="messge" id="messgebox" ref="scrollDiv"> 

        <ul>
  • <div v-if="item.user == username" class="mymsginfo" style="float:right">
    
        <div>
    
          <el-avatar style="float: right;margin-right: 30px;background: #01bd7e;">  
    
            <!-- {{ item.user.substring(0, 2) }} -->
    
            <img :alt="item.user.substring(0, 2)" :src=userphoto /> 
    
          </el-avatar>  
    
    </div><div style="float: right;margin-right: 10px;margin-top:10px;width:80%;text-align: right;"> {{ item.msg }} </div> 
    
     </div>
    
     <div v-else class="chatmsginfo" >
    
        <div>
    
        <el-avatar style="float: left;margin-right: 10px;"> {{ item.user }} </el-avatar> 
    
    </div>
    
      <div style="float: left;margin-top:10px;width:80%;">
    
        <img alt="loading" v-if="item.msg == ''" class="loading" src="../../assets/loading.gif"/>
    
        <MdPreview style="margin-top:-20px;" :autoFoldThreshold="9999" :editorId="id" :modelValue=" item.msg + item.dot " />
    
        <!-- {{ item.msg }} -->
    
        </div> 
    
      </div>
    
  •     </div>
    
        <div class="inputmsg">
    
            <el-form :model="form" >
    
                <el-form-item > 
    
                   <el-avatar style="float: left;background: #01bd7e;margin-bottom: -44px;margin-left: 4px;z-index: 999;width: 30px;height: 30px;">  
    
                     <img alt="jin" :src=userphoto /> 
    
                    </el-avatar>  
    
                    <el-input id="txt_suiwen" :prefix-icon="userphoto" resize="none" autofocus="true" :autosize="{ minRows: 1, maxRows: 2 }" v-model="form.desc" placeholder="说说你想问点啥....按Enter键可直接发送" @keydown.enter.native.prevent="startStreaming($event)" type="textarea" />
    
                </el-form-item>
    
            </el-form>
    
        </div>
    
      </div>    
    
    </el-row>

    </template><script setup>

    import { MdPreview, MdCatalog } from 'md-editor-v3';

    import 'md-editor-v3/lib/preview.css';

    const id = 'preview-only';

    </script><script>

    export default {

    data() {

    return {
    
      form: {
    
        desc: ''
    
      },
    
      message:[],
    
      username:sessionStorage.name,
    
      userphoto:sessionStorage.photo, loadingtype:false, 
    
      controller: null, 
    
      arequestData : {
    
          model: "qwen2",//"llama3.1", messages: []
    
      }
    
    }
    

    },

    mounted() {

    },

    methods: {

    scrollToBottom() {
    
        let elscroll=this.$refs["scrollDiv"];
    
        elscroll.scrollTop = elscroll.scrollHeight+30
    
       
    
    }, 
    
    clearForm(formName){
    
      this.form.desc='';
    
    }, 
    
    async startStreaming(e) {
    
      if(e.ctrkey&&e.keyCode==13){
    
        this.form.desc+='\n';
    
      }
    
      document.getElementById("txt_suiwen").disabled="true";
    
      // 如果已经有一个正在进行的流式请求,则中止它
    
      if (this.controller) {
    
        this.controller.abort();
    
      }
    
      setTimeout(()=>{
    
        this.scrollToBottom();
    
       },50);
    
        var mymsg=this.form.desc.trim(); 
    
        if(mymsg.length>0){
    
               this.form.desc='';
    
               this.message.push({
    
                   user:this.username, msg:mymsg
    
               })           
    
               this.message.push({
    
                       user:'GPT', msg:'',
    
                       dot:''
    
               });
    
        
    
             // 创建一个新的 AbortController 实例
    
             this.controller = new AbortController();
    
             const signal = this.controller.signal;
    
             this.arequestData.messages.push({role:"user",content:mymsg});
    
             try {
    
               const response = await fetch('http://127.0.0.1:11434/api/chat', {
    
                 method: 'POST',
    
                 headers: {
    
                   'Content-Type': 'application/json'
    
                 },
    
                 body:JSON.stringify(this.arequestData),
    
                 signal
    
               });
    
       
    
               if (!response.body) {
    
                 this.message[this.message.length-1].msg='ReadableStream not yet supported in this browser.';
    
                 throw new Error('ReadableStream not yet supported in this browser.');
    
               }
    
       
    
               const reader = response.body.getReader();
    
               const decoder = new TextDecoder();
    
               let result = '';
    
               this.message[this.message.length-1].dot='⚪';
    
               while (true) {
    
                 const { done, value } = await reader.read();
    
                 if (done) {
    
                   break;
    
                 }
    
                 result += decoder.decode(value, { stream: true });
    
                 // 处理流中的每一块数据,这里假设每块数据都是完整的 JSON 对象 const jsonChunks = result.split('\n').filter(line => line.trim());
    
                 //console.log(result)
    
                 for (const chunk of jsonChunks) {
    
                   try {
    
                     const data = JSON.parse(chunk);
    
                     //console.log(data.message.content) 
    
                     this.message[this.message.length-1].msg+=data.message.content;
    
                     setTimeout(()=>{
    
                       this.scrollToBottom();
    
                      },50); 
    
                   } catch (e) {
    
                     //this.message[this.message.length-1].msg=e;
    
                     // 处理 JSON 解析错误
    
                     //console.error('Failed to parse JSON:', e); }
    
                 }
    
                 // 清空 result 以便处理下一块数据 result = '';
    
               }
    
             } catch (error) {
    
               if (error.name === 'AbortError') {
    
                 console.log('Stream aborted');
    
                 this.message[this.message.length-1].msg='Stream aborted';
    
               } else {
    
                 console.error('Streaming error:', error);
    
                 this.message[this.message.length-1].msg='Stream error'+error;
    
               }
    
             }
    
             this.message[this.message.length-1].dot='';
    
             this.arequestData.messages.push({
    
                     role: 'assistant',//this.message[this.message.length-1].user,//"GPT", content: this.message[this.message.length-1].msg
    
                   })
    
             setTimeout(()=>{
    
                this.scrollToBottom();
    
               },50); 
    
               
    
    }else{
    
          this.form.desc='';
    
        }
    
        document.getElementById("txt_suiwen").disabled="";
    
        document.getElementById("txt_suiwen").focus();
    
    }  
    

    },

    beforeDestroy() {

    // 组件销毁时中止流式请求
    
    if (this.controller) {
    
      this.controller.abort();
    
    }
    

    }

    }

    </script><style scoped>

    .radius{

    margin:0 auto;}

    .demo-radius .title {

    color: var(--el-text-color-regular);

    font-size: 18px;

    margin: 10px 0;}

    .demo-radius .value {

    color: var(--el-text-color-primary);

    font-size: 16px;

    margin: 10px 0;}

    .demo-radius .radius {

    min-height: 580px;

    height: 85vh;

    width: 70%;

    border: 1px solid var(--el-border-color);

    border-radius: 14px;

    margin-top: 10px;}

    .messge{

    width:96%;
    
    height:84%;
    
    /* border:1px solid red; */
    
    margin: 6px auto;
    
    overflow: hidden;
    
    overflow-y: auto;}
    

    .inputmsg{

    width:96%;
    
    height:12%;
    
    /* border:1px solid blue; */
    
    border-top:2px solid #ccc;
    
    margin: 4px auto;
    
    padding-top: 10px;}
    

    .mymsginfo{

    width:100%;
    
    height:auto;
    
    min-height:50px;}
    

    ::-webkit-scrollbar {width: 6px;height: 5px;}

    ::-webkit-scrollbar-track {background-color: rgba(0, 0, 0, 0.2);border-radius: 10px;}

    ::-webkit-scrollbar-thumb {background-c

    olor: rgba(0, 0, 0, 0.5);border-radius: 10px;}

    ::-webkit-scrollbar-button {background-color: #7c2929;height: 0;width: 0px;}

    ::-webkit-scrollbar-corner {background-color: black;}

    标签: vue.js 开源 前端

    本文转载自: https://blog.csdn.net/weixin_55010563/article/details/143654632
    版权归原作者 牛马程序员24 所有, 如有侵权,请联系我们删除。

    “vue通过ollama接口调用开源模型实现人机对话”的评论:

    还没有评论