Commit c4a90e8e authored by StyleZhang's avatar StyleZhang

fix: audio

parent eadfcb1a
...@@ -6,7 +6,7 @@ bp = Blueprint('console', __name__, url_prefix='/console/api') ...@@ -6,7 +6,7 @@ bp = Blueprint('console', __name__, url_prefix='/console/api')
api = ExternalApi(bp) api = ExternalApi(bp)
# Import other controllers # Import other controllers
from . import setup, version, apikey, admin from . import setup, version, apikey, admin, audio
# Import app controllers # Import app controllers
from .app import app, site, completion, model_config, statistic, conversation, message, generator from .app import app, site, completion, model_config, statistic, conversation, message, generator
......
# -*- coding:utf-8 -*-
import logging
from flask import request
from flask_login import current_user
from flask_restful import Resource
from werkzeug.exceptions import InternalServerError
import services
from services.audio_service import AudioService
from controllers.console import api
from controllers.console.app.error import AppUnavailableError, ProviderNotInitializeError, \
ProviderQuotaExceededError, ProviderModelCurrentlyNotSupportError, CompletionRequestError
from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthorizationError, LLMAPIConnectionError, \
LLMRateLimitError, ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
class AudioApi(Resource):
def post(self):
file = request.files['file']
try:
response = AudioService.transcript(
tenant_id=current_user.current_tenant_id,
file=file,
)
return response
except services.errors.app_model_config.AppModelConfigBrokenError:
logging.exception("App model config broken.")
raise AppUnavailableError()
except ProviderTokenNotInitError:
raise ProviderNotInitializeError()
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
raise CompletionRequestError(str(e))
except ValueError as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
api.add_resource(AudioApi, '/audio-to-text')
\ No newline at end of file
...@@ -3,7 +3,7 @@ import json ...@@ -3,7 +3,7 @@ import json
import logging import logging
from typing import Generator, Union from typing import Generator, Union
from flask import Response, stream_with_context, request from flask import Response, stream_with_context
from flask_login import current_user from flask_login import current_user
from flask_restful import reqparse from flask_restful import reqparse
from werkzeug.exceptions import InternalServerError, NotFound from werkzeug.exceptions import InternalServerError, NotFound
...@@ -20,7 +20,6 @@ from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthor ...@@ -20,7 +20,6 @@ from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthor
from libs.helper import uuid_value from libs.helper import uuid_value
from services.completion_service import CompletionService from services.completion_service import CompletionService
from services.completion_service import CompletionService from services.completion_service import CompletionService
from controllers.console.datasets.error import NoFileUploadedError, TooManyFilesError
# define completion api for user # define completion api for user
...@@ -140,52 +139,6 @@ class ChatStopApi(InstalledAppResource): ...@@ -140,52 +139,6 @@ class ChatStopApi(InstalledAppResource):
PubHandler.stop(current_user, task_id) PubHandler.stop(current_user, task_id)
return {'result': 'success'}, 200 return {'result': 'success'}, 200
class AudioApi(InstalledAppResource):
def post(self, installed_app):
app_model = installed_app.app
if app_model.mode != 'chat':
raise NotChatAppError()
file = request.files['file']
# check file
if 'file' not in request.files:
raise NoFileUploadedError()
if len(request.files) > 1:
raise TooManyFilesError()
from services.audio_service import AudioService
try:
response = AudioService.transcript(
app_model=app_model,
file=file,
)
return response
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
except services.errors.conversation.ConversationCompletedError:
raise ConversationCompletedError()
except services.errors.app_model_config.AppModelConfigBrokenError:
logging.exception("App model config broken.")
raise AppUnavailableError()
except ProviderTokenNotInitError:
raise ProviderNotInitializeError()
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
raise CompletionRequestError(str(e))
except ValueError as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
def compact_response(response: Union[dict | Generator]) -> Response: def compact_response(response: Union[dict | Generator]) -> Response:
...@@ -226,4 +179,3 @@ api.add_resource(CompletionApi, '/installed-apps/<uuid:installed_app_id>/complet ...@@ -226,4 +179,3 @@ api.add_resource(CompletionApi, '/installed-apps/<uuid:installed_app_id>/complet
api.add_resource(CompletionStopApi, '/installed-apps/<uuid:installed_app_id>/completion-messages/<string:task_id>/stop', endpoint='installed_app_stop_completion') api.add_resource(CompletionStopApi, '/installed-apps/<uuid:installed_app_id>/completion-messages/<string:task_id>/stop', endpoint='installed_app_stop_completion')
api.add_resource(ChatApi, '/installed-apps/<uuid:installed_app_id>/chat-messages', endpoint='installed_app_chat_completion') api.add_resource(ChatApi, '/installed-apps/<uuid:installed_app_id>/chat-messages', endpoint='installed_app_chat_completion')
api.add_resource(ChatStopApi, '/installed-apps/<uuid:installed_app_id>/chat-messages/<string:task_id>/stop', endpoint='installed_app_stop_chat_completion') api.add_resource(ChatStopApi, '/installed-apps/<uuid:installed_app_id>/chat-messages/<string:task_id>/stop', endpoint='installed_app_stop_chat_completion')
api.add_resource(AudioApi, '/installed-apps/<uuid:installed_app_id>/audio-to-text')
...@@ -7,6 +7,6 @@ bp = Blueprint('service_api', __name__, url_prefix='/v1') ...@@ -7,6 +7,6 @@ bp = Blueprint('service_api', __name__, url_prefix='/v1')
api = ExternalApi(bp) api = ExternalApi(bp)
from .app import completion, app, conversation, message from .app import completion, app, conversation, message, audio
from .dataset import document from .dataset import document
import logging
from flask import request
from werkzeug.exceptions import InternalServerError
import services
from services.audio_service import AudioService
from controllers.service_api import api
from controllers.service_api.app.error import AppUnavailableError, ProviderNotInitializeError, CompletionRequestError, ProviderQuotaExceededError, \
ProviderModelCurrentlyNotSupportError
from controllers.service_api.wraps import AppApiResource
from core.llm.error import LLMBadRequestError, LLMAuthorizationError, LLMAPIUnavailableError, LLMAPIConnectionError, \
LLMRateLimitError, ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
from models.model import App
class AudioApi(AppApiResource):
def post(self, app_model: App, end_user):
file = request.files['file']
try:
response = AudioService.transcript(
tenant_id=app_model.tenant_id,
file=file,
)
return response
except services.errors.app_model_config.AppModelConfigBrokenError:
logging.exception("App model config broken.")
raise AppUnavailableError()
except ProviderTokenNotInitError:
raise ProviderNotInitializeError()
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
raise CompletionRequestError(str(e))
except ValueError as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
api.add_resource(AudioApi, '/audio-to-text')
\ No newline at end of file
...@@ -7,4 +7,4 @@ bp = Blueprint('web', __name__, url_prefix='/api') ...@@ -7,4 +7,4 @@ bp = Blueprint('web', __name__, url_prefix='/api')
api = ExternalApi(bp) api = ExternalApi(bp)
from . import completion, app, conversation, message, site, saved_message from . import completion, app, conversation, message, site, saved_message, audio
# -*- coding:utf-8 -*-
import logging
from flask import request
from werkzeug.exceptions import InternalServerError
import services
from controllers.web import api
from controllers.web.error import AppUnavailableError, ProviderNotInitializeError, CompletionRequestError, \
ProviderQuotaExceededError, ProviderModelCurrentlyNotSupportError
from controllers.web.wraps import WebApiResource
from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthorizationError, LLMAPIConnectionError, \
LLMRateLimitError, ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
from services.audio_service import AudioService
from models.model import App
class AudioApi(WebApiResource):
def post(self, app_model: App, end_user):
file = request.files['file']
try:
response = AudioService.transcript(
tenant_id=app_model.tenant_id,
file=file,
)
return response
except services.errors.app_model_config.AppModelConfigBrokenError:
logging.exception("App model config broken.")
raise AppUnavailableError()
except ProviderTokenNotInitError:
raise ProviderNotInitializeError()
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
raise CompletionRequestError(str(e))
except ValueError as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
api.add_resource(AudioApi, '/audio-to-text')
\ No newline at end of file
...@@ -3,7 +3,7 @@ import json ...@@ -3,7 +3,7 @@ import json
import logging import logging
from typing import Generator, Union from typing import Generator, Union
from flask import Response, stream_with_context, request from flask import Response, stream_with_context
from flask_restful import reqparse from flask_restful import reqparse
from werkzeug.exceptions import InternalServerError, NotFound from werkzeug.exceptions import InternalServerError, NotFound
...@@ -18,8 +18,6 @@ from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthor ...@@ -18,8 +18,6 @@ from core.llm.error import LLMBadRequestError, LLMAPIUnavailableError, LLMAuthor
LLMRateLimitError, ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError LLMRateLimitError, ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
from libs.helper import uuid_value from libs.helper import uuid_value
from services.completion_service import CompletionService from services.completion_service import CompletionService
from services.audio_service import AudioService
from controllers.console.datasets.error import NoFileUploadedError, TooManyFilesError
# define completion api for user # define completion api for user
class CompletionApi(WebApiResource): class CompletionApi(WebApiResource):
...@@ -134,49 +132,6 @@ class ChatStopApi(WebApiResource): ...@@ -134,49 +132,6 @@ class ChatStopApi(WebApiResource):
PubHandler.stop(end_user, task_id) PubHandler.stop(end_user, task_id)
return {'result': 'success'}, 200 return {'result': 'success'}, 200
class AudioApi(WebApiResource):
def post(self, app_model, end_user):
if app_model.mode != 'chat':
raise NotChatAppError()
file = request.files['file']
# check file
if 'file' not in request.files:
raise NoFileUploadedError()
if len(request.files) > 1:
raise TooManyFilesError()
try:
response = AudioService.transcript(
app_model=app_model,
file=file,
)
return response
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
except services.errors.conversation.ConversationCompletedError:
raise ConversationCompletedError()
except services.errors.app_model_config.AppModelConfigBrokenError:
logging.exception("App model config broken.")
raise AppUnavailableError()
except ProviderTokenNotInitError:
raise ProviderNotInitializeError()
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
raise CompletionRequestError(str(e))
except ValueError as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
def compact_response(response: Union[dict | Generator]) -> Response: def compact_response(response: Union[dict | Generator]) -> Response:
...@@ -217,4 +172,3 @@ api.add_resource(CompletionApi, '/completion-messages') ...@@ -217,4 +172,3 @@ api.add_resource(CompletionApi, '/completion-messages')
api.add_resource(CompletionStopApi, '/completion-messages/<string:task_id>/stop') api.add_resource(CompletionStopApi, '/completion-messages/<string:task_id>/stop')
api.add_resource(ChatApi, '/chat-messages') api.add_resource(ChatApi, '/chat-messages')
api.add_resource(ChatStopApi, '/chat-messages/<string:task_id>/stop') api.add_resource(ChatStopApi, '/chat-messages/<string:task_id>/stop')
api.add_resource(AudioApi, '/audio-to-text')
import openai
import io import io
import openai
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
from core.llm.llm_builder import LLMBuilder from core.llm.llm_builder import LLMBuilder
from core.llm.provider.llm_provider_service import LLMProviderService from core.llm.provider.llm_provider_service import LLMProviderService
from models.model import App from services.errors.audio import NoAudioUploadedError, AudioTooLargeError, UnsupportedAudioTypeError
from controllers.console.datasets.error import FileTooLargeError, UnsupportedFileTypeError
FILE_SIZE_LIMIT = 25 * 1024 * 1024 # 25MB FILE_SIZE_LIMIT = 25 * 1024 * 1024
ALLOWED_EXTENSIONS = ['mp3', 'mp4', 'mpeg', 'mpga', 'm4a', 'wav', 'webm'] ALLOWED_EXTENSIONS = ['mp3', 'mp4', 'mpeg', 'mpga', 'm4a', 'wav', 'webm']
class AudioService: class AudioService:
@classmethod @classmethod
def transcript(cls, app_model: App, file: FileStorage, **params): def transcript(cls, tenant_id: str, file: FileStorage, **params):
if file is None:
raise NoAudioUploadedError()
extension = file.mimetype
if extension not in [f'audio/{ext}' for ext in ALLOWED_EXTENSIONS]:
raise AudioTooLargeError()
file_content = file.read() file_content = file.read()
file_size = len(file_content) file_size = len(file_content)
if file_size > FILE_SIZE_LIMIT: if file_size > FILE_SIZE_LIMIT:
message = f"({file_size} > {FILE_SIZE_LIMIT})" message = f"({file_size} > {FILE_SIZE_LIMIT})"
raise FileTooLargeError(message) raise UnsupportedAudioTypeError(message)
extension = file.mimetype provider_name = LLMBuilder.get_default_provider(tenant_id)
if extension not in [f'audio/{ext}' for ext in ALLOWED_EXTENSIONS]: provider = LLMProviderService(tenant_id, provider_name)
raise UnsupportedFileTypeError()
provider_name = LLMBuilder.get_default_provider(app_model.tenant_id)
provider = LLMProviderService(app_model.tenant_id, provider_name)
credentials = provider.get_credentials(provider_name) credentials = provider.get_credentials(provider_name)
buffer = io.BytesIO(file_content) buffer = io.BytesIO(file_content)
......
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
__all__ = [ __all__ = [
'base', 'conversation', 'message', 'index', 'app_model_config', 'account', 'document', 'dataset', 'base', 'conversation', 'message', 'index', 'app_model_config', 'account', 'document', 'dataset',
'app', 'completion' 'app', 'completion', 'audio'
] ]
from . import * from . import *
from libs.exception import BaseHTTPException
class NoAudioUploadedError(BaseHTTPException):
error_code = 'no_audio_uploaded'
description = "Please upload your audio."
code = 400
class AudioTooLargeError(BaseHTTPException):
error_code = 'audio_too_large'
description = "Audio size exceeded. {message}"
code = 413
class UnsupportedAudioTypeError(BaseHTTPException):
error_code = 'unsupported_audio_type'
description = "Audio type not allowed."
code = 415
\ No newline at end of file
...@@ -63,8 +63,6 @@ export type IChatProps = { ...@@ -63,8 +63,6 @@ export type IChatProps = {
controlFocus?: number controlFocus?: number
isShowSuggestion?: boolean isShowSuggestion?: boolean
suggestionList?: string[] suggestionList?: string[]
isInstalledApp: boolean
installedAppId: string
} }
export type MessageMore = { export type MessageMore = {
...@@ -427,8 +425,6 @@ const Chat: FC<IChatProps> = ({ ...@@ -427,8 +425,6 @@ const Chat: FC<IChatProps> = ({
controlFocus, controlFocus,
isShowSuggestion, isShowSuggestion,
suggestionList, suggestionList,
isInstalledApp,
installedAppId,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { notify } = useContext(ToastContext) const { notify } = useContext(ToastContext)
...@@ -612,8 +608,7 @@ const Chat: FC<IChatProps> = ({ ...@@ -612,8 +608,7 @@ const Chat: FC<IChatProps> = ({
{ {
voiceInputShow && ( voiceInputShow && (
<VoiceInput <VoiceInput
isInstalledApp={isInstalledApp} isPublic={displayScene === 'web'}
installedAppId={installedAppId}
onCancel={() => setVoiceInputShow(false)} onCancel={() => setVoiceInputShow(false)}
onConverted={text => setQuery(text)} onConverted={text => setQuery(text)}
/> />
......
...@@ -386,6 +386,7 @@ const Debug: FC<IDebug> = ({ ...@@ -386,6 +386,7 @@ const Debug: FC<IDebug> = ({
}} }}
isShowSuggestion={doShowSuggestion} isShowSuggestion={doShowSuggestion}
suggestionList={suggestQuestions} suggestionList={suggestQuestions}
displayScene='console'
/> />
</div> </div>
</div> </div>
......
...@@ -8,15 +8,13 @@ import { Loading02, XClose } from '@/app/components/base/icons/src/vender/line/g ...@@ -8,15 +8,13 @@ import { Loading02, XClose } from '@/app/components/base/icons/src/vender/line/g
import { audioToText } from '@/service/share' import { audioToText } from '@/service/share'
type VoiceInputTypes = { type VoiceInputTypes = {
isInstalledApp: boolean isPublic: boolean
installedAppId: string
onConverted: (text: string) => void onConverted: (text: string) => void
onCancel: () => void onCancel: () => void
} }
const VoiceInput = ({ const VoiceInput = ({
isInstalledApp, isPublic,
installedAppId,
onCancel, onCancel,
onConverted, onConverted,
}: VoiceInputTypes) => { }: VoiceInputTypes) => {
...@@ -74,7 +72,7 @@ const VoiceInput = ({ ...@@ -74,7 +72,7 @@ const VoiceInput = ({
formData.append('file', wavFile) formData.append('file', wavFile)
try { try {
const audioResponse = await audioToText(isInstalledApp, installedAppId, formData) const audioResponse = await audioToText(isPublic, formData)
onConverted(audioResponse.text) onConverted(audioResponse.text)
onCancel() onCancel()
} }
......
...@@ -29,7 +29,7 @@ const InstalledApp: FC<IInstalledAppProps> = ({ ...@@ -29,7 +29,7 @@ const InstalledApp: FC<IInstalledAppProps> = ({
<div className='h-full p-2'> <div className='h-full p-2'>
{installedApp?.app.mode === 'chat' {installedApp?.app.mode === 'chat'
? ( ? (
<ChatApp isInstalledApp installedAppInfo={installedApp}/> <ChatApp isInstalledApp installedAppInfo={installedApp} displayScene='console' />
) )
: ( : (
<TextGenerationApp isInstalledApp installedAppInfo={installedApp}/> <TextGenerationApp isInstalledApp installedAppInfo={installedApp}/>
......
...@@ -17,7 +17,7 @@ import Header from '@/app/components/share/header' ...@@ -17,7 +17,7 @@ import Header from '@/app/components/share/header'
import { delConversation, fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, fetchSuggestedQuestions, pinConversation, sendChatMessage, stopChatMessageResponding, unpinConversation, updateFeedback } from '@/service/share' import { delConversation, fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, fetchSuggestedQuestions, pinConversation, sendChatMessage, stopChatMessageResponding, unpinConversation, updateFeedback } from '@/service/share'
import type { ConversationItem, SiteInfo } from '@/models/share' import type { ConversationItem, SiteInfo } from '@/models/share'
import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug' import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug'
import type { Feedbacktype, IChatItem } from '@/app/components/app/chat' import type { DisplayScene, Feedbacktype, IChatItem } from '@/app/components/app/chat'
import Chat from '@/app/components/app/chat' import Chat from '@/app/components/app/chat'
import { changeLanguage } from '@/i18n/i18next-config' import { changeLanguage } from '@/i18n/i18next-config'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
...@@ -28,11 +28,13 @@ import type { InstalledApp } from '@/models/explore' ...@@ -28,11 +28,13 @@ import type { InstalledApp } from '@/models/explore'
import Confirm from '@/app/components/base/confirm' import Confirm from '@/app/components/base/confirm'
export type IMainProps = { export type IMainProps = {
displayScene?: DisplayScene
isInstalledApp?: boolean isInstalledApp?: boolean
installedAppInfo?: InstalledApp installedAppInfo?: InstalledApp
} }
const Main: FC<IMainProps> = ({ const Main: FC<IMainProps> = ({
displayScene = 'web',
isInstalledApp = false, isInstalledApp = false,
installedAppInfo, installedAppInfo,
}) => { }) => {
...@@ -620,8 +622,7 @@ const Main: FC<IMainProps> = ({ ...@@ -620,8 +622,7 @@ const Main: FC<IMainProps> = ({
controlFocus={controlFocus} controlFocus={controlFocus}
isShowSuggestion={doShowSuggestion} isShowSuggestion={doShowSuggestion}
suggestionList={suggestQuestions} suggestionList={suggestQuestions}
isInstalledApp={isInstalledApp} displayScene={displayScene}
installedAppId={installedAppInfo?.id || ''}
/> />
</div> </div>
</div>) </div>)
......
...@@ -115,6 +115,6 @@ export const fetchSuggestedQuestions = (messageId: string, isInstalledApp: boole ...@@ -115,6 +115,6 @@ export const fetchSuggestedQuestions = (messageId: string, isInstalledApp: boole
return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId)) return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId))
} }
export const audioToText = (isInstalledApp: boolean, installedAppId: string, body: FormData) => { export const audioToText = (isPublicAPI: boolean, body: FormData) => {
return (getAction('post', isInstalledApp))(getUrl('/audio-to-text', isInstalledApp, installedAppId), { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ text: string }> return (getAction('post', !isPublicAPI))('/audio-to-text', { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ text: string }>
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment