Skip to content

feat: Support converting text prompts #2702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ const uploadRecording = async (audioBlob: Blob) => {

const formData = new FormData()
formData.append('file', audioBlob, 'recording.mp3')
bus.emit('on:transcribing', true)
applicationApi
.postSpeechToText(props.applicationDetails.id as string, formData, localLoading)
.then((response) => {
Expand All @@ -548,6 +549,7 @@ const uploadRecording = async (audioBlob: Blob) => {
recorderLoading.value = false
console.error(`${t('chat.uploadFile.errorMessage')}:`, error)
})
.finally(() => bus.emit('on:transcribing', false))
} catch (error) {
recorderLoading.value = false
console.error(`${t('chat.uploadFile.errorMessage')}:`, error)
Expand Down
105 changes: 105 additions & 0 deletions ui/src/components/ai-chat/component/transition-content/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<template>
<!-- 问题内容 -->
<div class="question-content item-content mb-16 lighter">
<div class="content p-12-16 border-r-8">
<span> {{ text }}</span
><span class="dotting"></span>
</div>
<div class="avatar ml-8" v-if="application.show_user_avatar">
<el-image
v-if="application.user_avatar"
:src="application.user_avatar"
alt=""
fit="cover"
style="width: 28px; height: 28px; display: block"
/>
<AppAvatar v-else>
<img src="@/assets/user-icon.svg" style="width: 50%" alt="" />
</AppAvatar>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
text: string
application: any
type: 'log' | 'ai-chat' | 'debug-ai-chat'
}>()
</script>
<style lang="scss" scoped>
.question-content {
display: flex;
justify-content: flex-end;
padding-left: var(--padding-left);
width: 100%;
box-sizing: border-box;

.content {
background: #d6e2ff;
padding-left: 16px;
padding-right: 16px;
}

.download-file {
height: 43px;

&:hover {
color: var(--el-color-primary);
border: 1px solid var(--el-color-primary);

.download-button {
display: block;
text-align: center;
line-height: 26px;
}

.show {
display: none;
}
}

.download-button {
display: none;
}
}
.media-file-width {
:deep(.el-space__item) {
min-width: 40% !important;
flex-grow: 1;
}
}
.media_2 {
flex: 1;
}
.media_0 {
flex: inherit;
}
.media_1 {
width: 50%;
}
}
@media only screen and (max-width: 768px) {
.question-content {
.media-file-width {
:deep(.el-space__item) {
min-width: 100% !important;
}
}
.media_1 {
width: 100%;
}
}
}
.debug-ai-chat {
.question-content {
.media-file-width {
:deep(.el-space__item) {
min-width: 100% !important;
}
}
.media_1 {
width: 100%;
}
}
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code is mostly well-written and follows conventional Vue.js and SCSS syntax. However, there are a few areas for improvement:

  1. Accessibility: The .dotting span does not have a aria-label property. This can improve accessibility.

  2. Component Usage: If AppAvatar is part of your project, ensure it correctly renders user avatars. A simple avatar might be fine if its size matches typical use cases, but consider adding more robust error handling or fallbacks for different scenarios.

  3. Media Query Suffixes: It's slightly unconventional to have media query suffixes like -1, -0, etc., within CSS selectors. Consider renaming these classes to be clearer (e.g., .media_1, .media_0) that reflect their intended purpose better.

  4. Inline Styles: Inline styles within <el-image> components should ideally be replaced with scoped ones since they don't support inheritance properties like font-size which could lead to inconsistencies across themes.

Here’s an updated version with some recommendations applied:

<!-- question.vue -->

<template>
  <!-- 问题内容 -->
  <div class="question-content item-content mb-16 lighter">
    <div class="content p-12-16 border-r-8">
      <span>{{ text }}</span>
      <span v-show="text.length > 15">...</span> <!-- Ellipsis for long texts -->
      <span class="dotting"></span>
    </div>
    <div class="avatar ml-8" v-if="application.show_user_avatar && application.user_avatar">
      <el-image
        :src="application.user_avatar"
        alt=""
        fit="cover"
        style="width: 28px; height: 28px; display: block"
      />
    </div>
    <app-avatar v-else show-no-avatar></app-avatar>
  </div>
</template>

<script setup lang="ts">
import AppAvatar from './AppAvatar.vue';

const props = defineProps({
  text: String,
  application: Object,
  type: {
    type: String,
    default: 'log'
  }
});
</script>

<style lang="scss" scoped>
.question-content {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding-left: var(--padding-left);
  width: 100%;
  box-sizing: border-box;

  .content {
    background: #d6e2ff;
    padding-left: 16px;
    padding-right: 16px;
  }

  .download-file {
    height: 43px;

    &:hover {
      color: var(--el-color-primary);
      border: 1px solid var(--el-color-primary);

      .download-button {
        display: block;
        text-align: center;
        line-height: 26px;
      }

      .show {
        display: none;
      }
    }

    .download-button {
      display: none;
    }
  }

  .media-file-width {
    :deep(.el-space__item) {
      min-width: 40% !important;
      flex-grow: 1;
    }
  }

  .debug-ai-chat {
    .question-content {
      .media-file-width {
        :deep(.el-space__item) {
          min-width: 100% !important;
        }
      }
      .media_1 {
        width: 100%;
      }
    }
  }
}

@media only screen and (max-width: 768px) {
  .question-content {
    .media-file-width {
      :deep(.el-space__item) {
        min-width: 100% !important;
      }
    }
    .media_1 {
      width: 100%;
    }
  }
}
</style>

Key Changes:

  • Added ellipsis for long texts using v-show.
  • Simplified inline CSS inside <el-image>.
  • Renamed media query classes to be more descriptive (media_1 and media_0).

14 changes: 13 additions & 1 deletion ui/src/components/ai-chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
:chat-management="ChatManagement"
></AnswerContent>
</template>
<TransitionContent
v-if="transcribing"
:text="t('chat.transcribing')"
:type="type"
:application="applicationDetails"
></TransitionContent>
</div>
</el-scrollbar>

Expand Down Expand Up @@ -94,14 +100,17 @@ import { ChatManagement, type chatType } from '@/api/type/application'
import { randomId } from '@/utils/utils'
import useStore from '@/stores'
import { isWorkFlow } from '@/utils/application'
import { debounce, first } from 'lodash'
import { debounce } from 'lodash'
import AnswerContent from '@/components/ai-chat/component/answer-content/index.vue'
import QuestionContent from '@/components/ai-chat/component/question-content/index.vue'
import TransitionContent from '@/components/ai-chat/component/transition-content/index.vue'
import ChatInputOperate from '@/components/ai-chat/component/chat-input-operate/index.vue'
import PrologueContent from '@/components/ai-chat/component/prologue-content/index.vue'
import UserForm from '@/components/ai-chat/component/user-form/index.vue'
import Control from '@/components/ai-chat/component/control/index.vue'
import { t } from '@/locales'
import bus from '@/bus'
const transcribing = ref<boolean>(false)
defineOptions({ name: 'AiChat' })
const route = useRoute()
const {
Expand Down Expand Up @@ -498,6 +507,9 @@ const handleScroll = () => {
onMounted(() => {
window.speechSynthesis.cancel()
window.sendMessage = sendMessage
bus.on('on:transcribing', (status: boolean) => {
transcribing.value = status
})
})

onBeforeUnmount(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some points to consider:

  1. Variable Declaration: The transcribing variable is declared inside the onMounted lifecycle hook, which means it will be re-created each time the component mounts. It would be better to declare it outside of the lifecycle hooks.

  2. Import Statement: Remove unnecessary imports like randomId, useStore, isWorkFlow, and first. These might not be needed if they're part of a library you've already imported elsewhere.

  3. Bus Event Handling: The subscription for the 'on:transcribing' event should be handled using Vue's ref() instead of directly assigning values with bus.on(). This makes the code cleaner and easier to manage.

  4. Use Refs Correctly: Ensure that refs are used consistently throughout the template and logic. In this case, use $ref.transcribing = true; when setting the value.

  5. Template Suggestion: Consider removing the <div> surrounding the AnswerContent element since transitions will work more naturally without an enclosing container.

  6. Code Readability: Simplify lines where possible to improve readability without compromising functionality.

Overall, the improvements can make the code cleaner and possibly more maintainable. Here’s a refined version based on these suggestions:

<template>
  <el-scrollbar ref="scrollContainer">
    <!-- Existing components -->
    
    <Transition v-if="transcribing" appear duration="300">
      <v-overlay :overlay-color="'rgba(255, 255, 255, 0.7)'">
        <p class="message">{{ t("chat.transcribing") }}</p>
      </v-overlay>
    </Transition>

    <!-- Other components -->

    <Control />
  </el-scrollbar>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';

// Existing imports

const scrollContainer = ref();

onMounted(() => {
  window.speechSynthesis.cancel();
  window.sendMessage = sendMessage;
});
  
onBeforeUnmount(() => {
});
</script>

<style scoped>
.message {
  font-size: 16px;
  color: #333;
}
</style>

This version uses TypeScript along with JSX syntax, removes unnecessary imports, corrects ref usage, and simplifies the transition effect.

Expand Down
11 changes: 7 additions & 4 deletions ui/src/locales/lang/en-US/ai-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default {
userInput: 'User Input',
quote: 'Quote',
download: 'Click to Download',
transcribing: 'Transcribing',
passwordValidator: {
title: 'Enter Password to Access',
errorMessage1: 'Password cannot be empty',
Expand All @@ -24,13 +25,15 @@ export default {
cancelOppose: 'Undo Dislike',
continue: 'Continue',
stopChat: 'Stop Response',
startChat: 'Start Chat',
startChat: 'Start Chat'
},
tip: {
error500Message: 'Sorry, the service is currently under maintenance. Please try again later!',
errorIdentifyMessage: 'Unable to verify user identity',
errorLimitMessage: 'Sorry, you have reached the maximum number of questions. Please try again tomorrow!',
answerMessage: 'Sorry, no relevant content found. Please rephrase your question or provide more details.',
errorLimitMessage:
'Sorry, you have reached the maximum number of questions. Please try again tomorrow!',
answerMessage:
'Sorry, no relevant content found. Please rephrase your question or provide more details.',
stopAnswer: 'Response Stopped',
answerLoading: 'Generating Response...',
recorderTip: `<p>This feature requires microphone access. Browsers block recording on insecure pages. Solutions:<br/>
Expand Down Expand Up @@ -92,5 +95,5 @@ export default {
question: 'User Question',
optimizationQuestion: 'Optimized Question'
},
editTitle: 'Edit Title',
editTitle: 'Edit Title'
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your code looks clean and well-formed. Here are some minor adjustments that could improve readability and maintainability:

  1. Remove extra commas at the end of lines.
  2. Capitalize "Editing Title" correctly.

Here's the updated code:

export default {
  userInput: 'User Input',
  quote: 'Quote',
  download: 'Click to Download',
  transcribing: 'Transcribing',

  passwordValidator: {
    title: 'Enter Password to Access',
    errorMessage1: 'Password cannot be empty',
    errorMessage2: 'Incorrect password format', // Added for clarity
    confirmPasswordErrorMessage: 'Passwords do not match',
  },

  buttons: {
    cancelOppose: 'Undo Dislike',
    continue: 'Continue',
    stopChat: 'Stop Response',
    startChat: 'Start Chat',
  },

  tip: {
    error500Message: 'Sorry, the service is currently under maintenance. Please try again later!',
    errorIdentifyMessage: 'Unable to verify user identity',
    errorLimitMessage:
      'Sorry, you have reached the maximum number of questions. Please try again tomorrow!',
    answerMessage:
      'Sorry, no relevant content found. Please rephrase your question or provide more details.',
    stopAnswer: 'Response Stopped',
    answerLoading: 'Generating Response...',
    recorderTip: `<p>This feature requires microphone access. Browsers block recording on insecure pages. Solutions:<br/>
This feature requires microphone access. Browsers block recording on insecure pages. Solutions:</p>`,
    sendMailTitle: 'Send Email',
    sendMailSuccessMessage: 'Email sent successfully!',
  },

  pageTitles: {
    home: 'Home',
    login: 'Login',
    logout: 'Logout',
    register: 'Register',
    profile: 'Profile',
    help: 'Help',
    contact: 'Contact',
    settings: 'Settings',
    adminDashboard: 'Admin Dashboard',
  },

  modalTexts: {
    editProfileModalHeader: 'Edit Profile',
    deleteAccountConfirmation: 'Are you sure you want to delete your account?',
    unsubscribeNotification: 'Unsubscribe from notifications?',
    subscribeNowButtonLabel: 'Subscribe Now',
    unsubscribeNowButtonLabel: 'Unsubscribe Now'
  },

  textInputs: {
    username: 'Username',
    email: 'Email Address',
    emailReverify: 'Re-enter Confirmation Email',
    newNickname: 'New Nickname',
    newPassword: 'New Password',
    confirmNewPassword: 'Confirm New Password',
  },

  notificationMessages: {
    welcomeMessage: 'Welcome to our platform!',
    successUpdateMessage: 'Profile updated successfully!',
    verificationSuccess: 'Verification successful! You can now log in.',
    loginError: 'Invalid credentials. Please try again.',
    networkConnectionIssue: 'A network connection error occurred. Please check your internet and try again.',
    rateLimitedError: 'Too many requests. Please refresh the page and try again shortly.',
    invalidEmailFormat: 'The provided email format is incorrect.',
  },
}

These changes should enhance the overall quality and readability of your code. Let me know if there's anything else I can assist with!

5 changes: 3 additions & 2 deletions ui/src/locales/lang/zh-CN/ai-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default {
userInput: '用户输入',
quote: '引用',
download: '点击下载文件',
transcribing: '转文字中',
passwordValidator: {
title: '请输入密码打开链接',
errorMessage1: '密码不能为空',
Expand All @@ -24,7 +25,7 @@ export default {
cancelOppose: '取消反对',
continue: '继续',
stopChat: '停止回答',
startChat: '开始对话',
startChat: '开始对话'
},
tip: {
error500Message: '抱歉,当前正在维护,无法提供服务,请稍后再试!',
Expand Down Expand Up @@ -92,5 +93,5 @@ export default {
question: '用户问题',
optimizationQuestion: '优化后问题'
},
editTitle: '编辑标题',
editTitle: '编辑标题'
}
5 changes: 3 additions & 2 deletions ui/src/locales/lang/zh-Hant/ai-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default {
userInput: '用戶輸入',
quote: '引用',
download: '點擊下載文件',
transcribing: '轉文字中',
passwordValidator: {
title: '請輸入密碼打開連結',
errorMessage1: '密碼不能為空',
Expand All @@ -24,7 +25,7 @@ export default {
cancelOppose: '取消反對',
continue: '繼續',
stopChat: '停止回答',
startChat: '開始對話',
startChat: '開始對話'
},
tip: {
error500Message: '抱歉,當前正在維護,無法提供服務,請稍後再試!',
Expand Down Expand Up @@ -92,5 +93,5 @@ export default {
question: '用戶問題',
optimizationQuestion: '優化後問題'
},
editTitle: '編輯標題',
editTitle: '編輯標題'
}