文心一言聊天机器人

show with app
  • global.R
  • server.R
  • ui.R
library(reticulate)
library(stringr)

# Load OpenAI API
print(getwd())
path<- "../../scripts/Wenxin.py"
print(path)
#使用指定的Conda虚拟环境"openai"运行Python程序
use_condaenv(condaenv="base", conda = "/usr/lib64/anaconda3/bin/conda", required = TRUE)
source_python(path)

context<-list()
function(input, output, session) {
  
  ifContext<- reactive({
    input$ifContext
  })
  
  prompt<- reactive({
    input$prompt
  })
  
  observeEvent(input$clearContext,{
    session$userData$context<- context
    updateTextAreaInput(session, "context", value = "")
  })
  
  observeEvent(input$sendout,{
    if(ifContext() && ! is.null(session$userData$context)){
      temp<-session$userData$context
    } else{
      temp<- context
    }
    # Show notification while querying.
    id <- showNotification("正在咨询文心一言...", duration = NULL, closeButton = FALSE)
    on.exit(removeNotification(id), add = TRUE)
    response<- ChatWithWenxin(prompt(), temp)
    #browser()
    temp[[length(temp)+1]]<-list(role="user","content"=prompt())
    temp[[length(temp)+1]]<-list(role="assistant","content"=response)
    session$userData$context<- temp
    # 格式化输出,插入对话者身份前缀。
    outStr<-""
    for(i in 1: length(temp)){
        if (i%%2==1){
        outStr<-paste(outStr,"User: ",temp[[i]][2],"\n",sep="")
        }
        else{
        outStr<-paste(outStr,"Bot: ",temp[[i]][2],"\n\n",sep="")
        }
    }
    updateTextAreaInput(session, "context", value = outStr)
    updateTextAreaInput(session, "prompt", value = "")
    # 对话上下文自动滚动到底部
    session$sendCustomMessage(type = 'scrollToBottom', message = '')
  })
  
  observeEvent(input$restoreContext,{
    if(! is.null(session$userData$context)){
      temp<-session$userData$context
      # 格式化输出,插入对话者身份前缀。
      outStr<-""
      for(i in 1: length(temp)){
        if (i%%2==1){
          outStr<-paste(outStr,"User: ",temp[[i]][2],"\n",sep="")
        }
        else{
          outStr<-paste(outStr,"Bot: ",temp[[i]][2],"\n\n",sep="")
        }
      }
      updateTextAreaInput(session, "context", value = outStr)
      updateTextAreaInput(session, "prompt", value = "")      
    }
  })
  
}
# 在输出文本中包含滚动条的另一种方式。
# https://stackoverflow.com/questions/61007632/container-settings-in-text-output-shiny
fluidPage(
  # 对话上下文自动滚动到底部的JavaScript函数
  tags$script(HTML("
    function scrollToBottom() {
      var textarea = document.getElementById('context');
      console.log('Before scroll:', textarea.scrollTop, textarea.scrollHeight);
      setTimeout(function() {
        textarea.scrollTop = textarea.scrollHeight;
      }, 100); // 可以根据需要调整延迟时间
      console.log('After scroll:', textarea.scrollTop, textarea.scrollHeight);
    }

    Shiny.addCustomMessageHandler('scrollToBottom', function(message) {
      scrollToBottom();
    });
  ")),
  
  sidebarLayout(
    # Sidebar 
    sidebarPanel(
      # Application title
      tags$h3("文心一言聊天机器人"),
      checkboxInput("ifContext","是否包括上下文:", value = TRUE),
      actionButton("clearContext", "清除上下文", class = "btn-cleear"),
      actionButton("restoreContext", "恢复中断对话", class = "btn-success"),
      # 1/6
      width = 3
    ),
    # Main 
    mainPanel(
      textAreaInput("context","对话记录",width="100%", rows =26, resize="vertical", value =""),  
      # 插入javascript,禁止自己修改 context textAreaInput
      tags$script(HTML("
        var context = document.getElementById('context');
        context.readOnly = true;
      ")),
      tags$h6(" "),
      textAreaInput("prompt","输入:",width="100%", rows =4, resize="vertical", value =""),  
      actionButton("sendout", "提交", class = "btn-success"),
      # 5/6
      # width = 10
    )
  )
)