Ver Fonte

修复语音克隆

ageer há 1 ano atrás
pai
commit
81d64085f1

+ 6 - 9
src/api/openapi.ts

@@ -125,7 +125,7 @@ export const GptUploader =   ( url:string, FormData:FormData )=>{
 }
 
 export const whisperUpload = ( FormData:FormData )=>{
-    const url = gptGetUrl('/v1/audio/transcriptions');
+    const url = gptGetUrl('/audio');
     let headers=   {'Content-Type': 'multipart/form-data' }
     headers={...headers,...getHeaderAuthorization()}
     return new Promise<any>((resolve, reject) => {
@@ -224,13 +224,10 @@ export const subModel= async (opt: subModelType)=>{
             "messages": opt.message
            ,stream:true
         }
-        //
-
-        let  headers ={
-                'Content-Type': 'application/json'
-                //,'Authorization': 'Bearer ' +gptServerStore.myData.OPENAI_API_KEY
-                ,'Accept': 'text/event-stream '
-        }
+        
+        let headers=   {'Content-Type': 'application/json;charset=UTF-8',
+        'Authorization':'Bearer ' + getToken(),
+        'Accept': 'text/event-stream '}
         headers={...headers,...getHeaderAuthorization()}
 
         try {
@@ -279,7 +276,7 @@ export interface ttsType{
 }
 export const subTTS = async (tts:ttsType )=>{
     if(!tts.voice) tts.voice='alloy';
-    let url= getUrl('/v1/audio/speech');
+    let url= getUrl('/speech');
     let headers=  {
         'Content-Type': 'application/json'
       }

+ 2 - 2
src/api/pay.ts

@@ -1,4 +1,3 @@
-import { post} from '@/utils/request/'
 import request from '@/utils/request/req';
 export interface OrderReq {
 	money:string; // 商品价格
@@ -6,8 +5,9 @@ export interface OrderReq {
 }
 
 export function payUrl(params:OrderReq) {
-	return post({
+	return request({
 		url: '/pay/payUrl',
+		method: 'post',
 		data: params,
 	})
 }

+ 18 - 11
src/views/chat/components/Message/Text.vue

@@ -13,7 +13,9 @@ import dallText from '@/views/mj/dallText.vue'
 import ttsText from '@/views/mj/ttsText.vue'
 import whisperText from '@/views/mj/whisperText.vue'
 import MjTextAttr from '@/views/mj/mjTextAttr.vue'
-import { isTTS } from '@/api'
+import aiTextSetting from '@/views/mj/aiTextSetting.vue'
+import aiSetAuth from '@/views/mj/aiSetAuth.vue'
+import { isApikeyError, isAuthSessionError, isTTS } from '@/api'
 
 interface Props {
   inversion?: boolean
@@ -61,9 +63,10 @@ const wrapClass = computed(() => {
 
 const text = computed(() => {
   const value = props.text ?? ''
-  if (!props.asRawText)
-    return mdi.render(value)
-  return value
+  // if (!props.asRawText)
+  //   return mdi.render(value)
+  // return value
+  return mdi.render(value)
 })
 
 function highlightBlock(str: string, lang?: string) {
@@ -115,8 +118,11 @@ onUnmounted(() => {
   <div class="text-black" :class="wrapClass">
     <div ref="textRef" class="leading-relaxed break-words">
       <div v-if="!inversion">
+        <aiTextSetting v-if="!inversion && isApikeyError(text)"/>
+        <aiSetAuth v-if="!inversion && isAuthSessionError(text)" />
+          
         <dallText :chat="chat" v-if="chat.model=='dall-e-3' || chat.model=='dall-e-2'" class="whitespace-pre-wrap" />
-        <mjText v-if="chat.mjID" class="whitespace-pre-wrap" :chat="chat"></mjText>
+        <mjText v-if="chat.mjID" class="whitespace-pre-wrap" :chat="chat" :mdi="mdi"></mjText>
         <ttsText v-else-if="chat.model && isTTS(chat.model) && chat.text=='ok'" :chat="chat"/>
         <template v-else>
           <div v-if="!asRawText" class="markdown-body" :class="{ 'markdown-body-generate': loading }" v-html="text" />
@@ -124,13 +130,14 @@ onUnmounted(() => {
         </template>
       </div>
       <whisperText v-else-if="text=='whisper' && chat.opt?.lkey "  :chat="chat" />
-      <div v-else class="whitespace-pre-wrap" v-text="text" />
+      <div v-else class="markdown-body "  style="--color-fg-default:#24292f"  v-html="text" />
+      <!-- <div v-else class="whitespace-pre-wrap" v-text="text" /> -->
       <MjTextAttr :image="chat.opt?.images[0]" v-if="chat.opt?.images"></MjTextAttr>
-      <!-- <div v-if="chat.opt?.images" class="flex flex-wrap justify-start items-baseline">
-          <div v-for="(img,k ) of chat.opt?.images" :key="k" class="p-1" >
-            <NImage :src="img" preview class="w-[200px] rounded" />
-          </div>
-      </div> -->
+      <whisperText v-if="chat.model && chat.model.indexOf('whisper')>-1 && chat.opt?.lkey " :isW="true"  :chat="chat" class="w-full" />
+      <ttsText v-if="!inversion && chat.opt?.duration && chat.opt?.duration>0 && chat.opt?.lkey " :isW="true"  :chat="chat" class="w-full" />
+
+      
+
     </div>
   </div>
 </template>

+ 22 - 12
src/views/chat/components/Message/index.vue

@@ -9,7 +9,7 @@ import { t } from '@/locales'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
 import { copyToClip } from '@/utils/copy'
 import { homeStore } from '@/store'
-import { getSeed, mlog } from '@/api' 
+import { getSeed, mlog ,mjImgUrl} from '@/api' 
 
 interface Props {
   dateTime?: string
@@ -61,14 +61,22 @@ const options = computed(() => {
       label: asRawText.value ? t('chat.preview') : t('chat.showRawText'),
       key: 'toggleRenderType',
       icon: iconRender({ icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code' }),
+    });
+    common.unshift({
+      label: t('mj.tts'),
+      key: 'tts',
+      icon: iconRender({ icon:'mdi:tts' }),
     })
   }
 
   return common
 })
 
-function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType') {
+function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType' |'tts') {
   switch (key) {
+    case 'tts': 
+      homeStore.setMyData({act:'gpt.ttsv2', actData:{ index:props.index , uuid:props.chat.uuid, text:props.text } });
+      return;
     case 'copyText':
       handleCopy()
       return
@@ -80,14 +88,19 @@ function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType') {
   }
 }
 
+function handleRegenerate() {
+  messageRef.value?.scrollIntoView()
+  emit('regenerate')
+}
+
 
 async function handleCopy(txt?:string) {
   try {
     await copyToClip( txt|| props.text || '')
-    message.success('复制成功')
+    message.success( t('chat.copied'))
   }
   catch {
-    message.error('复制失败')
+    message.error( t('mj.copyFail') )
   }
 }
 
@@ -101,7 +114,7 @@ function handleRegenerate2() {
   mlog('重新发送!');
   homeStore.setMyData({act:'gpt.resubmit', actData:{ index:props.index , uuid:props.chat.uuid } });
 }
-
+ 
 </script>
 
 <template>
@@ -117,7 +130,7 @@ function handleRegenerate2() {
       <AvatarComponent :image="inversion" :logo="chat.logo"/>
     </div>
     <div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
-      <p class="text-xs group  text-[#b4bbc4] flex justify-start items-center space-x-2 " :class="[inversion ? 'text-right' : 'text-left']">
+      <p class="text-xs group  text-[#b4bbc4] flex  items-center space-x-2 " :class="[inversion ? 'justify-end' : 'justify-start']">
         <span>{{ dateTime }}</span>
         <span v-if="chat.model"  class="text-[#b4bbc4]/50">{{ chat.model }}</span>
         <!-- <span>{{ chat.opt?.progress }}</span> -->
@@ -128,15 +141,12 @@ function handleRegenerate2() {
             <span v-if="chat.opt?.seed">Seed:{{ chat.opt?.seed }}</span>
             <span v-else>Seed</span>
           </div>
-          <a :href="chat.opt?.imageUrl" class="hidden group-hover:block active  cursor-pointer underline " target="_blank">原图链接</a>
+          <a :href=" mjImgUrl(chat.opt?.imageUrl)" class="hidden group-hover:block active  cursor-pointer underline " target="_blank">{{ $t('mj.ulink') }}</a>
         </template>
       </p>
       
-      <div
-        class="flex items-end gap-1 mt-2"
-        :class="[inversion ? 'flex-row-reverse' : 'flex-row']"
-      >
-        
+      <div  class="flex items-end gap-1 mt-2"
+        :class="[inversion ? 'flex-row-reverse' : 'flex-row']" > 
         <TextComponent 
           ref="textRef"
           :inversion="inversion"

+ 0 - 4
src/views/mj/aiGptInput.vue

@@ -82,10 +82,6 @@ funt();
             //  const formData = new FormData( ); 
             // formData.append('file', file);
             // formData.append('model', 'whisper-1'); 
-
-            // GptUploader('/v1/audio/transcriptions',formData).then(r=>{
-            //     mlog('语音识别成功', r ); 
-            // }).catch(e=>ms.error('上传失败:'+ ( e.message?? JSON.stringify(e)) ));
             homeStore.setMyData({act:'gpt.whisper', actData:{ file , prompt:'whisper' } });
             return ;