Skip to content

Commit

Permalink
feat: custom system prompt (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
liou666 committed May 7, 2023
1 parent 6d6cf10 commit 31c2e9b
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 77 deletions.
1 change: 1 addition & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare module '@vue/runtime-core' {
export interface GlobalComponents {
Avatar: typeof import('./src/components/Avatar.vue')['default']
Button: typeof import('./src/components/Button.vue')['default']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
InputKit: typeof import('./src/components/InputKit.vue')['default']
Expand Down
7 changes: 2 additions & 5 deletions src/hooks/useSpeechService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export const useSpeechService = ({ langs = <const>['fr-FR', 'ja-JP', 'en-US', 'z
mediaRecorder = new MediaRecorder(stream)

mediaRecorder.ondataavailable = (e) => {
console.log(chunks, 'c')
chunks.push(e.data)
}

Expand All @@ -103,12 +102,10 @@ export const useSpeechService = ({ langs = <const>['fr-FR', 'ja-JP', 'en-US', 'z

isRecognizReadying.value = true

recognizer.value.canceled = () => {
console.log('Recognize canceled')
}
recognizer.value.recognized = (s, e) => {
console.log('Recognize result: ', e.result.text)
cb && cb(e.result.text)
// isRecognizing.value = false
}
recognizer.value.recognizing = (s, event) => {
console.log('Recognize recognizing', event.result.text)
Expand All @@ -128,9 +125,9 @@ export const useSpeechService = ({ langs = <const>['fr-FR', 'ja-JP', 'en-US', 'z
isRecognizing.value = false
}
recognizer.value.startContinuousRecognitionAsync(async () => {
await audioRecorder()
isRecognizing.value = true
isRecognizReadying.value = false
await audioRecorder()
console.log('Recognize...')
},
(error) => {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Home/components/Content.vue
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ async function onSubmit(fromRecognize = false) {
...currentChatMessages.value,
{ content: message.value, role: 'user', audioBlob: fromRecognize ? await blobToBase64(audioBlob.value) : '' },
])
const tempCurrentChatMessages = currentChatMessages.value.map(x => ({ content: x.content, role: x.role })) // 发送的请求中需去除audioBlob
const tempCurrentChatMessages = chatMessages.value.map(x => ({ content: x.content, role: x.role })) // 发送的请求中需去除audioBlob
const systemMessage = currentChatMessages.value[0]
const relativeMessage = [...tempCurrentChatMessages, { content: message.value, role: 'user' }].slice(-(Number(chatRememberCount.value))) // 保留最近的几条消息
const relativeMessage = tempCurrentChatMessages.slice(-(Number(chatRememberCount.value))) // 保留最近的几条消息
const prompts = [systemMessage, ...relativeMessage] as ChatMessage[]
message.value = ''
Expand Down
132 changes: 67 additions & 65 deletions src/pages/Home/components/NewChat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,46 @@ const currentAvatarIndex = ref(Math.random() * avatarList.value.length | 0)
const store = useConversationStore()
const { ssmlToSpeak, isSynthesizing, isPlaying } = useSpeechService({ isFetchAllVoice: false })
const allLanguages = computed(() => [...new Set(allVoices.map(v => v.locale))].filter(l => Object.keys(supportLanguageMap).includes(l)))
const selectLanguage = ref('')
const filterVoices = ref<VoiceInfo[]>([])
const selectVoiceName = ref('')
const desc = ref('')
const name = ref('')
const rate = ref('1.0')
const previewText = ref('polyglot is awesome!')
const filterStyles = ref<string[]>([])
const selectStyle = ref('Neural')
const presets = ref('Act as if you are meeting someone for the first time. How would you introduce yourself and start a conversation?')
const canAdd = computed(() => !!(selectLanguage.value && selectVoiceName.value && desc.value && name.value))
onBeforeMount(() => {
selectLanguage.value = allLanguages.value[0]
changeSelectLanguage(selectLanguage.value)
})
const voiceValue = ref<string[]>(['en-US', 'en-US-JennyNeural', 'chat'])
watch(selectLanguage, changeSelectLanguage)
const selectLanguage = computed(() => voiceValue.value[0])
const selectVoiceName = computed(() => voiceValue.value[1])
const selectStyle = computed(() => voiceValue.value[2])
const canAdd = computed(() => !!(selectLanguage.value && selectVoiceName.value && desc.value && name.value))
function changeSelectLanguage(newSelectLanguage: string) {
filterVoices.value = allVoices.filter(v => v.locale === newSelectLanguage)
selectVoiceName.value = filterVoices.value[0]?.shortName
interface Option {
label: string
value: string
children?: Option[]
}
watch(selectVoiceName, (n) => {
filterStyles.value = filterVoices.value.filter(v => v.shortName === n)[0]?.styleList || []
selectStyle.value = filterStyles.value[0] || 'Neural'
const options = ref<Option[] >([])
onMounted(() => {
allLanguages.value.forEach((item) => {
const children: Option[] = []
allVoices.forEach((v) => {
if (v.locale === item) {
children.push({
value: v.shortName,
label: `${v.gender === 1 ? '🧒🏻' : '👦🏻'} ${v.localName}`,
children: v.styleList?.map(x => ({ label: voiceStyleMap[x], value: x })) || [],
})
}
})
options.value.push({
value: item,
label: supportLanguageMap[item],
children,
})
})
})
const randomAvatar = getAvatarUrl(avatarList.value[Math.random() * avatarList.value.length | 0]) // 随机默认选择一个头像
Expand All @@ -60,7 +73,7 @@ const addChat = (event: any) => {
rate: +rate.value,
isDefault: false,
voiceStyle: selectStyle.value,
})
}, presets.value)
store.changeCurrentKey(uid)
emits('close')
}
Expand All @@ -74,16 +87,8 @@ const previewSpeech = () => {
}
</script>

<script>
</script>

<template>
<div flex="~ col gap-3" items-center>
<!-- <div text-lg font-bold>
自定义对话
</div> -->

<div class="wrapper" flex="~ col gap-3" items-center>
<div flex>
<Avatar v-model:image-url="imageUrl" />
</div>
Expand All @@ -96,32 +101,17 @@ const previewSpeech = () => {
<input v-model="desc" type="text">
</div>
<div flex>
<label for="">语言</label>
<select v-model="selectLanguage">
<option v-for="item in allLanguages" :key="item" :value="item">
{{ supportLanguageMap[item] }}
</option>
</select>
</div>
<div flex>
<label for="">音色</label>
<select v-model="selectVoiceName">
<option v-for="item in filterVoices" :key="item.shortName" :value="item.shortName">
{{ `${item.locale} / ${item.gender === 1 ? 'Female' : 'Male'} / ${item.localName}` }}
</option>
</select>
<label for="">语音</label>
<div w-55 flex>
<ElCascader v-model="voiceValue" style="width: 220px;" :options="options" />
</div>
</div>
<div flex>
<label for="">风格</label>
<select v-model="selectStyle">
<option value="Neural">
正常
</option>

<option v-for="item in filterStyles" :key="item" :value="item">
{{ voiceStyleMap[item] }}
</option>
</select>
<label for="">语速</label>
<div w-55 flex>
<input v-model="rate" flex-1 type="range" step="0.1" min="0.1" max="2.0">
<span w-4 ml-1>{{ Number(rate).toFixed(1) }}</span>
</div>
</div>
<div relative center-y>
<div flex>
Expand All @@ -137,18 +127,12 @@ const previewSpeech = () => {
</button>
</div>
</div>

<!-- todo -->
<div flex>
<label for="">语速</label>
<div w-55 flex>
<input v-model="rate" flex-1 type="range" step="0.1" min="0.1" max="2.0">
<span w-4 ml-1>{{ Number(rate).toFixed(1) }}</span>
</div>
<label for="">场景预设</label>
<textarea v-model="presets" :rows="3" placeholder="system prompt" />
</div>
<!-- todo -->
<!-- <div flex>
<label center-y justify-end mr-2 for="">预设</label>
<textarea id="message" resize-none w-50 block p-2 text-sm placeholder="Write your thoughts here..." />
</div> -->
<div center-y text-sm text-gray-500>
<i inline-block w-4 h-4 m-1 cursor-auto i-ic:baseline-lightbulb />
点击头像可更换头像
Expand All @@ -162,12 +146,30 @@ const previewSpeech = () => {

<style scoped>
label{
@apply center-y justify-end mr-2 w-20
@apply center-y justify-center w-20
}
input{
@apply w-50 p-2
@apply w-50 p-2 text-[#222]
}
select{
@apply w-55 select-settings
@apply w-55 select-settings text-[#222]
}
textarea{
@apply resize-none w-50 font-sans block p-2 text-[#222]
}
textArea::-webkit-scrollbar{
width: 0;
height: 0;
}
.wrapper :deep(.el-input__wrapper){
box-shadow: 0 0 0 1px #777 inset;
padding-top: 3px;
padding-bottom: 3px;
}
.wrapper :deep(.el-input__inner){
color: #222;
}
.wrapper :deep(.el-input__wrapper):hover {
box-shadow: 0 0 0 1px #777 inset;
}
</style>
2 changes: 1 addition & 1 deletion src/pages/Home/components/Tool.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ watch([azureKey, azureRegion, ttsPassword], () => {
<span>Setting</span>
</div>
</div>
<Modal v-model:visible="addVisible" :z-index="2" class="dark:bg-[#111111] bg-white" center max-w-120 p6 @close="closeNewChat()">
<Modal v-model:visible="addVisible" h-80vh :z-index="2" class="dark:bg-[#111111] bg-white" center max-w-120 p6 @close="closeNewChat()">
<NewChat :all-voices="tempAllVoices as any" @close="addVisible = false" />
</Modal>

Expand Down
6 changes: 3 additions & 3 deletions src/stores/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid'
import { generatePrompt, getAvatarUrl } from '@/utils'
import { basePrompt, generatePrompt, getAvatarUrl } from '@/utils'

const defaultConversations = [{
key: uuid(),
Expand Down Expand Up @@ -79,12 +79,12 @@ export const useConversationStore = defineStore('conversation', {
cleanCurrentConversations() {
this.chatMessages(this.currentKey)!.chatMessages.length = 1
},
addConversation(conversation: Omit<Conversation, 'chatMessages'>) {
addConversation(conversation: Omit<Conversation, 'chatMessages'>, systemPrompt?: string) {
this.conversations.push({
...conversation,
chatMessages: [{
role: 'system',
content: generatePrompt(conversation.language),
content: systemPrompt ? basePrompt(conversation.language, systemPrompt) : generatePrompt(conversation.language),
}],
})
},
Expand Down
5 changes: 4 additions & 1 deletion src/utils/openAi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export const parseOpenAIStream = (rawResponse: Response) => {
export const verifyOpenKey = (key?: string | null) => key && ([41, 51].includes(key.length))

export const generatePrompt = (language: string) => {
return `I want you to act as an ${language} speaking partner and improver. No matter what language I speak to you, you need to reply me in ${language}. I hope you keep your responses clean and limit your responses to 100 characters. I hope you are humorous and interesting. I hope you will ask me a question from time to time in your reply. Now let\'s start practicing. Remember, I want you reply me in ${language} and you are humorous and interesting`
return `I want you to act as an ${language} speaking partner and improver. No matter what language I speak to you, you need to reply me in ${language}. I hope you keep your responses clean and limit your responses to 80 characters. I hope you are humorous and interesting. I hope you will ask me a question from time to time in your reply. Now let\'s start practicing. Remember, I want you reply me in ${language} and you are humorous and interesting`
}

export const basePrompt = (language: string, prompt?: string) => {
return `${prompt}.Remember, I want you reply me in ${language}, I hope limit your responses to 80 characters. Now let\'s start practicing. `
}

0 comments on commit 31c2e9b

Please sign in to comment.