Merge branch 'refs/heads/main' into feat/workflow-parallel-support

# Conflicts:
#	api/core/workflow/entities/variable_pool.py
This commit is contained in:
takatost 2024-07-23 17:28:57 +08:00
commit e9bfedab9b
35 changed files with 571 additions and 53 deletions

View File

@ -281,7 +281,7 @@ class UserSatisfactionRateStatistic(Resource):
SELECT date(DATE_TRUNC('day', m.created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
COUNT(m.id) as message_count, COUNT(mf.id) as feedback_count
FROM messages m
LEFT JOIN message_feedbacks mf on mf.message_id=m.id
LEFT JOIN message_feedbacks mf on mf.message_id=m.id and mf.rating='like'
WHERE m.app_id = :app_id
'''
arg_dict = {'tz': account.timezone, 'app_id': app_model.id}

View File

@ -33,6 +33,15 @@ class Segment(BaseModel):
def markdown(self) -> str:
return str(self.value)
def to_object(self) -> Any:
if isinstance(self.value, Segment):
return self.value.to_object()
if isinstance(self.value, list):
return [v.to_object() for v in self.value]
if isinstance(self.value, dict):
return {k: v.to_object() for k, v in self.value.items()}
return self.value
class StringSegment(Segment):
value_type: SegmentType = SegmentType.STRING

View File

@ -23,6 +23,7 @@
- tongyi
- wenxin
- moonshot
- tencent
- jina
- chatglm
- yi

View File

@ -0,0 +1,13 @@
<svg viewBox="0 83.15545000000002 402.45098039215685 76.44" data-name="图层 1" id="图层_1"
xmlns="http://www.w3.org/2000/svg" style="max-height: 500px" width="402.45098039215685"
height="76.44">
<defs>
<style>.cls-1{fill:#4999d4}</style>
</defs>
<title>tencent-cloud</title>
<path
d="M27.569 113.353a17.56 17.56 0 0 1 33.148-3.743.158.158 0 0 0 .194.105 21.267 21.267 0 0 1 7.008-.729c.235.018.327-.116.25-.33a24.828 24.828 0 0 0-47.933 4.444.082.082 0 0 0 .016 0 18.537 18.537 0 0 0-9.85 31.533 18.007 18.007 0 0 0 10.325 5h-.001a43.066 43.066 0 0 0 5.266.282c1.68.011 33.725.008 35.067.008 2.7 0 4.457-.002 6.345-.14a18.245 18.245 0 0 0 11.723-5.15 18.532 18.532 0 0 0-12.901-31.789 18.06 18.06 0 0 0-11.704 4.285c-1.467 1.196-3.006 2.626-4.944 4.508-.642.625-13.336 12.94-21.67 21.028-1.16-.005-2.828-.021-4.306-.07a11.704 11.704 0 0 1-8.125-3.148A11.275 11.275 0 0 1 23.33 120.1a11.706 11.706 0 0 1 7.646 3.062c1.44 1.192 4.633 4 6.035 5.263a.17.17 0 0 0 .24.002l4.945-4.825a.176.176 0 0 0-.004-.27c-2.378-2.15-5.749-5.158-7.778-6.669a18.874 18.874 0 0 0-6.844-3.31zm46.482 26.094a11.704 11.704 0 0 1-8.125 3.147 168.92 168.92 0 0 1-5.204.073h-22.38c8.142-7.91 15.245-14.808 16.051-15.59.738-.717 2.398-2.306 3.83-3.595 3.145-2.831 5.974-3.4 7.976-3.382a11.275 11.275 0 0 1 7.852 19.347z"
class="cls-1" />
<path
d="M302.794 129.138a.24.24 0 0 0-.419-.163 16.062 16.062 0 0 1-11.961 5.469c-7.7 0-12.674-5.32-12.674-13.552 0-8.388 4.74-13.599 12.37-13.599a17.274 17.274 0 0 1 11.828 4.996.24.24 0 0 0 .414-.168v-4.245a18.595 18.595 0 0 0-12.243-4.502 15.358 15.358 0 0 0-11.733 4.845c-2.857 3.138-4.366 7.52-4.366 12.674 0 10.478 6.592 17.518 16.404 17.518a18.517 18.517 0 0 0 12.38-4.624zM93.47 104.248v3.638h11.506v29.657h3.982v-29.657h11.506v-3.638H93.47zM390.972 115.625c-2.059-2.232-5.501-3.043-7.978-3.043a11.24 11.24 0 0 0-8.363 3.475 13.26 13.26 0 0 0-3.277 9.243c0 8.883 5.894 12.86 11.735 12.86 3.412 0 6.228-1.22 7.883-3.38v2.812h3.971V104.3h-3.97zm-.105 9.769c0 4.545-2.639 9.147-7.683 9.147-3.788 0-7.865-2.803-7.865-8.958 0-4.522 2.388-9.389 7.63-9.389 5.844 0 7.918 4.956 7.918 9.2zM308.064 104.3h4.031v33.292h-4.031zM192.033 131.427a.24.24 0 0 0-.403-.18 13.772 13.772 0 0 1-8.006 3.255c-5.344 0-8.796-3.583-8.796-9.128 0-5.546 3.452-9.129 8.796-9.129a12.973 12.973 0 0 1 8.01 2.8.24.24 0 0 0 .399-.183v-3.668a17.567 17.567 0 0 0-8.621-2.615c-7.38 0-12.34 5.142-12.34 12.795 0 7.652 4.96 12.794 12.34 12.794a14.903 14.903 0 0 0 8.62-2.624zM139.984 130.642a.241.241 0 0 0-.436-.143 12.896 12.896 0 0 1-8.605 4.214 8.725 8.725 0 0 1-6.104-2.141 8.634 8.634 0 0 1-2.372-6.07h18.629v-.285c0-5.254-.896-8.233-3.194-10.625a10.42 10.42 0 0 0-7.849-3.01c-6.969 0-11.472 4.987-11.471 12.715.254 8.04 4.822 12.84 12.218 12.84a13.447 13.447 0 0 0 9.184-3.52zm-2.653-7.564h-14.872l.006-.075a7.37 7.37 0 0 1 7.481-6.93c4.341 0 7.17 2.657 7.38 6.933zM362.877 125.584c0 6.632-3.825 8.985-7.1 8.985a6.287 6.287 0 0 1-6.549-6.263V113.15h-3.79v15.088c0 5.842 4.172 9.922 10.145 9.922 3.308 0 5.89-1.114 7.53-3.235v2.66h3.614V113.15h-3.85zM156.747 112.583a9.79 9.79 0 0 0-7.364 2.887v-2.334h-3.762v24.456h3.846v-13.935a7.592 7.592 0 0 1 1.982-5.51 6.75 6.75 0 0 1 5.197-1.91 5.994 5.994 0 0 1 6.055 6.339v15.016h3.847v-15.197a9.756 9.756 0 0 0-2.767-7.26 9.907 9.907 0 0 0-7.034-2.552zM217.156 130.642a.241.241 0 0 0-.436-.143 12.896 12.896 0 0 1-8.605 4.214 8.725 8.725 0 0 1-6.104-2.141 8.634 8.634 0 0 1-2.372-6.07h18.629v-.285c0-5.254-.895-8.233-3.193-10.625a10.42 10.42 0 0 0-7.85-3.01c-6.968 0-11.471 4.987-11.471 12.715.254 8.04 4.822 12.84 12.218 12.84a13.447 13.447 0 0 0 9.184-3.52zm-2.653-7.564h-14.871l.005-.075a7.37 7.37 0 0 1 7.481-6.93c4.342 0 7.17 2.657 7.381 6.933zM233.857 112.583a9.79 9.79 0 0 0-7.365 2.887v-2.334h-3.762v24.456h3.847v-13.935a7.592 7.592 0 0 1 1.982-5.51 6.75 6.75 0 0 1 5.196-1.91 5.994 5.994 0 0 1 6.056 6.339v15.016h3.846v-15.197a9.756 9.756 0 0 0-2.767-7.26 9.907 9.907 0 0 0-7.033-2.552zM256.236 137.917a19.963 19.963 0 0 0 5.009-1.138v-3.683a.241.241 0 0 0-.321-.229A29.455 29.455 0 0 1 256.8 134c-.402.064-.756.12-1.02-.104a.897.897 0 0 1-.263-.777V116.69h7.04v-3.559h-7.04v-6.516h-3.997v6.516h-4.012v3.558h4.012v16.815a4.207 4.207 0 0 0 1.309 3.327 5.088 5.088 0 0 0 3.406 1.085zM329.224 112.63c-7.093 0-11.859 5.13-11.859 12.764s4.766 12.764 11.859 12.764 11.858-5.13 11.858-12.764-4.765-12.764-11.858-12.764zm-8.18 12.739l-.001-.01c.014-5.41 3.299-9.043 8.18-9.043 4.893 0 8.18 3.648 8.182 9.078-.002 5.429-3.29 9.078-8.181 9.078-4.89 0-8.175-3.678-8.18-9.103z" />
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,13 @@
<svg viewBox="0 83.15545000000002 402.45098039215685 76.44" data-name="图层 1" id="图层_1"
xmlns="http://www.w3.org/2000/svg" style="max-height: 500px" width="402.45098039215685"
height="76.44">
<defs>
<style>.cls-1{fill:#4999d4}</style>
</defs>
<title>tencent-cloud</title>
<path
d="M27.569 113.353a17.56 17.56 0 0 1 33.148-3.743.158.158 0 0 0 .194.105 21.267 21.267 0 0 1 7.008-.729c.235.018.327-.116.25-.33a24.828 24.828 0 0 0-47.933 4.444.082.082 0 0 0 .016 0 18.537 18.537 0 0 0-9.85 31.533 18.007 18.007 0 0 0 10.325 5h-.001a43.066 43.066 0 0 0 5.266.282c1.68.011 33.725.008 35.067.008 2.7 0 4.457-.002 6.345-.14a18.245 18.245 0 0 0 11.723-5.15 18.532 18.532 0 0 0-12.901-31.789 18.06 18.06 0 0 0-11.704 4.285c-1.467 1.196-3.006 2.626-4.944 4.508-.642.625-13.336 12.94-21.67 21.028-1.16-.005-2.828-.021-4.306-.07a11.704 11.704 0 0 1-8.125-3.148A11.275 11.275 0 0 1 23.33 120.1a11.706 11.706 0 0 1 7.646 3.062c1.44 1.192 4.633 4 6.035 5.263a.17.17 0 0 0 .24.002l4.945-4.825a.176.176 0 0 0-.004-.27c-2.378-2.15-5.749-5.158-7.778-6.669a18.874 18.874 0 0 0-6.844-3.31zm46.482 26.094a11.704 11.704 0 0 1-8.125 3.147 168.92 168.92 0 0 1-5.204.073h-22.38c8.142-7.91 15.245-14.808 16.051-15.59.738-.717 2.398-2.306 3.83-3.595 3.145-2.831 5.974-3.4 7.976-3.382a11.275 11.275 0 0 1 7.852 19.347z"
class="cls-1" />
<path
d="M302.794 129.138a.24.24 0 0 0-.419-.163 16.062 16.062 0 0 1-11.961 5.469c-7.7 0-12.674-5.32-12.674-13.552 0-8.388 4.74-13.599 12.37-13.599a17.274 17.274 0 0 1 11.828 4.996.24.24 0 0 0 .414-.168v-4.245a18.595 18.595 0 0 0-12.243-4.502 15.358 15.358 0 0 0-11.733 4.845c-2.857 3.138-4.366 7.52-4.366 12.674 0 10.478 6.592 17.518 16.404 17.518a18.517 18.517 0 0 0 12.38-4.624zM93.47 104.248v3.638h11.506v29.657h3.982v-29.657h11.506v-3.638H93.47zM390.972 115.625c-2.059-2.232-5.501-3.043-7.978-3.043a11.24 11.24 0 0 0-8.363 3.475 13.26 13.26 0 0 0-3.277 9.243c0 8.883 5.894 12.86 11.735 12.86 3.412 0 6.228-1.22 7.883-3.38v2.812h3.971V104.3h-3.97zm-.105 9.769c0 4.545-2.639 9.147-7.683 9.147-3.788 0-7.865-2.803-7.865-8.958 0-4.522 2.388-9.389 7.63-9.389 5.844 0 7.918 4.956 7.918 9.2zM308.064 104.3h4.031v33.292h-4.031zM192.033 131.427a.24.24 0 0 0-.403-.18 13.772 13.772 0 0 1-8.006 3.255c-5.344 0-8.796-3.583-8.796-9.128 0-5.546 3.452-9.129 8.796-9.129a12.973 12.973 0 0 1 8.01 2.8.24.24 0 0 0 .399-.183v-3.668a17.567 17.567 0 0 0-8.621-2.615c-7.38 0-12.34 5.142-12.34 12.795 0 7.652 4.96 12.794 12.34 12.794a14.903 14.903 0 0 0 8.62-2.624zM139.984 130.642a.241.241 0 0 0-.436-.143 12.896 12.896 0 0 1-8.605 4.214 8.725 8.725 0 0 1-6.104-2.141 8.634 8.634 0 0 1-2.372-6.07h18.629v-.285c0-5.254-.896-8.233-3.194-10.625a10.42 10.42 0 0 0-7.849-3.01c-6.969 0-11.472 4.987-11.471 12.715.254 8.04 4.822 12.84 12.218 12.84a13.447 13.447 0 0 0 9.184-3.52zm-2.653-7.564h-14.872l.006-.075a7.37 7.37 0 0 1 7.481-6.93c4.341 0 7.17 2.657 7.38 6.933zM362.877 125.584c0 6.632-3.825 8.985-7.1 8.985a6.287 6.287 0 0 1-6.549-6.263V113.15h-3.79v15.088c0 5.842 4.172 9.922 10.145 9.922 3.308 0 5.89-1.114 7.53-3.235v2.66h3.614V113.15h-3.85zM156.747 112.583a9.79 9.79 0 0 0-7.364 2.887v-2.334h-3.762v24.456h3.846v-13.935a7.592 7.592 0 0 1 1.982-5.51 6.75 6.75 0 0 1 5.197-1.91 5.994 5.994 0 0 1 6.055 6.339v15.016h3.847v-15.197a9.756 9.756 0 0 0-2.767-7.26 9.907 9.907 0 0 0-7.034-2.552zM217.156 130.642a.241.241 0 0 0-.436-.143 12.896 12.896 0 0 1-8.605 4.214 8.725 8.725 0 0 1-6.104-2.141 8.634 8.634 0 0 1-2.372-6.07h18.629v-.285c0-5.254-.895-8.233-3.193-10.625a10.42 10.42 0 0 0-7.85-3.01c-6.968 0-11.471 4.987-11.471 12.715.254 8.04 4.822 12.84 12.218 12.84a13.447 13.447 0 0 0 9.184-3.52zm-2.653-7.564h-14.871l.005-.075a7.37 7.37 0 0 1 7.481-6.93c4.342 0 7.17 2.657 7.381 6.933zM233.857 112.583a9.79 9.79 0 0 0-7.365 2.887v-2.334h-3.762v24.456h3.847v-13.935a7.592 7.592 0 0 1 1.982-5.51 6.75 6.75 0 0 1 5.196-1.91 5.994 5.994 0 0 1 6.056 6.339v15.016h3.846v-15.197a9.756 9.756 0 0 0-2.767-7.26 9.907 9.907 0 0 0-7.033-2.552zM256.236 137.917a19.963 19.963 0 0 0 5.009-1.138v-3.683a.241.241 0 0 0-.321-.229A29.455 29.455 0 0 1 256.8 134c-.402.064-.756.12-1.02-.104a.897.897 0 0 1-.263-.777V116.69h7.04v-3.559h-7.04v-6.516h-3.997v6.516h-4.012v3.558h4.012v16.815a4.207 4.207 0 0 0 1.309 3.327 5.088 5.088 0 0 0 3.406 1.085zM329.224 112.63c-7.093 0-11.859 5.13-11.859 12.764s4.766 12.764 11.859 12.764 11.858-5.13 11.858-12.764-4.765-12.764-11.858-12.764zm-8.18 12.739l-.001-.01c.014-5.41 3.299-9.043 8.18-9.043 4.893 0 8.18 3.648 8.182 9.078-.002 5.429-3.29 9.078-8.181 9.078-4.89 0-8.175-3.678-8.18-9.103z" />
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,11 @@
<svg viewBox="0 83.15545000000002 85 76.44" data-name="图层 1" id="图层_1"
xmlns="http://www.w3.org/2000/svg" style="max-height: 500px" width="85"
height="76.44">
<defs>
<style>.cls-1{fill:#4999d4}</style>
</defs>
<title>tencent-cloud</title>
<path
d="M27.569 113.353a17.56 17.56 0 0 1 33.148-3.743.158.158 0 0 0 .194.105 21.267 21.267 0 0 1 7.008-.729c.235.018.327-.116.25-.33a24.828 24.828 0 0 0-47.933 4.444.082.082 0 0 0 .016 0 18.537 18.537 0 0 0-9.85 31.533 18.007 18.007 0 0 0 10.325 5h-.001a43.066 43.066 0 0 0 5.266.282c1.68.011 33.725.008 35.067.008 2.7 0 4.457-.002 6.345-.14a18.245 18.245 0 0 0 11.723-5.15 18.532 18.532 0 0 0-12.901-31.789 18.06 18.06 0 0 0-11.704 4.285c-1.467 1.196-3.006 2.626-4.944 4.508-.642.625-13.336 12.94-21.67 21.028-1.16-.005-2.828-.021-4.306-.07a11.704 11.704 0 0 1-8.125-3.148A11.275 11.275 0 0 1 23.33 120.1a11.706 11.706 0 0 1 7.646 3.062c1.44 1.192 4.633 4 6.035 5.263a.17.17 0 0 0 .24.002l4.945-4.825a.176.176 0 0 0-.004-.27c-2.378-2.15-5.749-5.158-7.778-6.669a18.874 18.874 0 0 0-6.844-3.31zm46.482 26.094a11.704 11.704 0 0 1-8.125 3.147 168.92 168.92 0 0 1-5.204.073h-22.38c8.142-7.91 15.245-14.808 16.051-15.59.738-.717 2.398-2.306 3.83-3.595 3.145-2.831 5.974-3.4 7.976-3.382a11.275 11.275 0 0 1 7.852 19.347z"
class="cls-1" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,156 @@
import base64
import hashlib
import hmac
import time
import requests
class Credential:
def __init__(self, secret_id, secret_key):
self.secret_id = secret_id
self.secret_key = secret_key
class FlashRecognitionRequest:
def __init__(self, voice_format="mp3", engine_type="16k_zh"):
self.engine_type = engine_type
self.speaker_diarization = 0
self.hotword_id = ""
self.customization_id = ""
self.filter_dirty = 0
self.filter_modal = 0
self.filter_punc = 0
self.convert_num_mode = 1
self.word_info = 0
self.voice_format = voice_format
self.first_channel_only = 1
self.reinforce_hotword = 0
self.sentence_max_length = 0
def set_first_channel_only(self, first_channel_only):
self.first_channel_only = first_channel_only
def set_speaker_diarization(self, speaker_diarization):
self.speaker_diarization = speaker_diarization
def set_filter_dirty(self, filter_dirty):
self.filter_dirty = filter_dirty
def set_filter_modal(self, filter_modal):
self.filter_modal = filter_modal
def set_filter_punc(self, filter_punc):
self.filter_punc = filter_punc
def set_convert_num_mode(self, convert_num_mode):
self.convert_num_mode = convert_num_mode
def set_word_info(self, word_info):
self.word_info = word_info
def set_hotword_id(self, hotword_id):
self.hotword_id = hotword_id
def set_customization_id(self, customization_id):
self.customization_id = customization_id
def set_voice_format(self, voice_format):
self.voice_format = voice_format
def set_sentence_max_length(self, sentence_max_length):
self.sentence_max_length = sentence_max_length
def set_reinforce_hotword(self, reinforce_hotword):
self.reinforce_hotword = reinforce_hotword
class FlashRecognizer:
"""
reponse:
request_id string
status Integer
message String
audio_duration Integer
flash_result Result Array
Result:
text String
channel_id Integer
sentence_list Sentence Array
Sentence:
text String
start_time Integer
end_time Integer
speaker_id Integer
word_list Word Array
Word:
word String
start_time Integer
end_time Integer
stable_flag Integer
"""
def __init__(self, appid, credential):
self.credential = credential
self.appid = appid
def _format_sign_string(self, param):
signstr = "POSTasr.cloud.tencent.com/asr/flash/v1/"
for t in param:
if 'appid' in t:
signstr += str(t[1])
break
signstr += "?"
for x in param:
tmp = x
if 'appid' in x:
continue
for t in tmp:
signstr += str(t)
signstr += "="
signstr = signstr[:-1]
signstr += "&"
signstr = signstr[:-1]
return signstr
def _build_header(self):
header = {"Host": "asr.cloud.tencent.com"}
return header
def _sign(self, signstr, secret_key):
hmacstr = hmac.new(secret_key.encode('utf-8'),
signstr.encode('utf-8'), hashlib.sha1).digest()
s = base64.b64encode(hmacstr)
s = s.decode('utf-8')
return s
def _build_req_with_signature(self, secret_key, params, header):
query = sorted(params.items(), key=lambda d: d[0])
signstr = self._format_sign_string(query)
signature = self._sign(signstr, secret_key)
header["Authorization"] = signature
requrl = "https://"
requrl += signstr[4::]
return requrl
def _create_query_arr(self, req):
return {
'appid': self.appid, 'secretid': self.credential.secret_id, 'timestamp': str(int(time.time())),
'engine_type': req.engine_type, 'voice_format': req.voice_format,
'speaker_diarization': req.speaker_diarization, 'hotword_id': req.hotword_id,
'customization_id': req.customization_id, 'filter_dirty': req.filter_dirty,
'filter_modal': req.filter_modal, 'filter_punc': req.filter_punc,
'convert_num_mode': req.convert_num_mode, 'word_info': req.word_info,
'first_channel_only': req.first_channel_only, 'reinforce_hotword': req.reinforce_hotword,
'sentence_max_length': req.sentence_max_length
}
def recognize(self, req, data):
header = self._build_header()
query_arr = self._create_query_arr(req)
req_url = self._build_req_with_signature(self.credential.secret_key, query_arr, header)
r = requests.post(req_url, headers=header, data=data)
return r.text

View File

@ -0,0 +1,92 @@
import json
from typing import IO, Optional
import requests
from core.model_runtime.errors.invoke import (
InvokeAuthorizationError,
InvokeConnectionError,
InvokeError,
)
from core.model_runtime.errors.validate import CredentialsValidateFailedError
from core.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel
from core.model_runtime.model_providers.tencent.speech2text.flash_recognizer import (
Credential,
FlashRecognitionRequest,
FlashRecognizer,
)
class TencentSpeech2TextModel(Speech2TextModel):
def _invoke(self, model: str, credentials: dict,
file: IO[bytes], user: Optional[str] = None) \
-> str:
"""
Invoke speech2text model
:param model: model name
:param credentials: model credentials
:param file: audio file
:param user: unique user id
:return: text for given audio file
"""
return self._speech2text_invoke(model, credentials, file)
def validate_credentials(self, model: str, credentials: dict) -> None:
"""
Validate model credentials
:param model: model name
:param credentials: model credentials
:return:
"""
try:
audio_file_path = self._get_demo_file_path()
with open(audio_file_path, 'rb') as audio_file:
self._speech2text_invoke(model, credentials, audio_file)
except Exception as ex:
raise CredentialsValidateFailedError(str(ex))
def _speech2text_invoke(self, model: str, credentials: dict, file: IO[bytes]) -> str:
"""
Invoke speech2text model
:param model: model name
:param credentials: model credentials
:param file: audio file
:return: text for given audio file
"""
app_id = credentials["app_id"]
secret_id = credentials["secret_id"]
secret_key = credentials["secret_key"]
voice_format = file.voice_format if hasattr(file, "voice_format") else "mp3"
tencent_voice_recognizer = FlashRecognizer(app_id, Credential(secret_id, secret_key))
resp = tencent_voice_recognizer.recognize(FlashRecognitionRequest(voice_format), file)
resp = json.loads(resp)
code = resp["code"]
message = resp["message"]
if code == 4002:
raise CredentialsValidateFailedError(str(message))
elif code != 0:
return f"Tencent ASR Recognition failed with code {code} and message {message}"
return "\n".join(item["text"] for item in resp["flash_result"])
@property
def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]:
"""
Map model invoke error to unified error
The key is the error type thrown to the caller
The value is the error type thrown by the model,
which needs to be converted into a unified error type for the caller.
:return: Invoke error mapping
"""
return {
InvokeConnectionError: [
requests.exceptions.ConnectionError
],
InvokeAuthorizationError: [
CredentialsValidateFailedError
]
}

View File

@ -0,0 +1,5 @@
model: tencent
model_type: speech2text
model_properties:
file_upload_limit: 25
supported_file_extensions: flac,mp3,mp4,mpeg,mpga,m4a,ogg,wav,webm

View File

@ -0,0 +1,29 @@
import logging
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.errors.validate import CredentialsValidateFailedError
from core.model_runtime.model_providers.__base.model_provider import ModelProvider
logger = logging.getLogger(__name__)
class TencentProvider(ModelProvider):
def validate_provider_credentials(self, credentials: dict) -> None:
"""
Validate provider credentials
if validate failed, raise exception
:param credentials: provider credentials, credentials form defined in `provider_credential_schema`.
"""
try:
model_instance = self.get_model_instance(ModelType.SPEECH2TEXT)
model_instance.validate_credentials(
model='tencent',
credentials=credentials
)
except CredentialsValidateFailedError as ex:
raise ex
except Exception as ex:
logger.exception(f'{self.get_provider_schema().provider} credentials validate failed')
raise ex

View File

@ -0,0 +1,49 @@
provider: tencent
label:
zh_Hans: 腾讯云
en_US: Tencent
icon_small:
en_US: icon_s_en.svg
icon_large:
zh_Hans: icon_l_zh.svg
en_US: icon_l_en.svg
background: "#E5E7EB"
help:
title:
en_US: Get your API key from Tencent AI
zh_Hans: 从腾讯云获取 API Key
url:
en_US: https://cloud.tencent.com/product/asr
supported_model_types:
- speech2text
configurate_methods:
- predefined-model
provider_credential_schema:
credential_form_schemas:
- variable: app_id
label:
zh_Hans: APPID
en_US: APPID
type: text-input
required: true
placeholder:
zh_Hans: 在此输入您的腾讯语音识别服务的 APPID
en_US: Enter the APPID of your Tencent Cloud ASR service
- variable: secret_id
label:
zh_Hans: SecretId
en_US: SecretId
type: secret-input
required: true
placeholder:
zh_Hans: 在此输入您的腾讯语音识别服务的 SecretId
en_US: Enter the SecretId of your Tencent Cloud ASR service
- variable: secret_key
label:
zh_Hans: SecretKey
en_US: SecretKey
type: secret-input
required: true
placeholder:
zh_Hans: 在此输入您的腾讯语音识别服务的 SecretKey
en_US: Enter the SecretKey of your Tencent Cloud ASR service

View File

@ -53,7 +53,7 @@ class ToolParameterConverter:
case ToolParameter.ToolParameterType.NUMBER:
if isinstance(value, int) | isinstance(value, float):
return value
elif isinstance(value, str):
elif isinstance(value, str) and value != '':
if '.' in value:
return float(value)
else:

View File

@ -5,7 +5,7 @@ from typing import Any, Union
from pydantic import BaseModel, Field, model_validator
from typing_extensions import deprecated
from core.app.segments import ArrayVariable, ObjectVariable, Variable, factory
from core.app.segments import Variable, factory
from core.file.file_obj import FileVar
from core.workflow.entities.node_entities import SystemVariable
@ -122,14 +122,7 @@ class VariablePool(BaseModel):
raise ValueError('Invalid selector')
hash_key = hash(tuple(selector[1:]))
value = self.variable_dictionary[selector[0]].get(hash_key)
if value is None:
return value
if isinstance(value, ArrayVariable):
return [element.value for element in value.value]
if isinstance(value, ObjectVariable):
return {k: v.value for k, v in value.value.items()}
return value.value if value else None
return value.to_object() if value else None
def remove(self, selector: Sequence[str], /):
"""

View File

@ -405,7 +405,7 @@ class LLMNode(BaseNode):
if 'content' not in item:
raise ValueError(f'Invalid context structure: {item}')
context_str += item['content'].text + '\n'
context_str += item['content'] + '\n'
retriever_resource = self._convert_to_original_retriever_resource(item)
if retriever_resource:

View File

@ -4,6 +4,7 @@ from os import path
from typing import Optional
import requests
from flask import current_app
from configs import dify_config
from constants.languages import languages

View File

@ -9,6 +9,7 @@ from core.app.segments import (
StringVariable,
factory,
)
from core.app.segments.variables import ArrayVariable, ObjectVariable
def test_string_variable():
@ -89,3 +90,47 @@ def test_build_a_blank_string():
)
assert isinstance(result, StringVariable)
assert result.value == ''
def test_object_variable_to_object():
var = ObjectVariable(
name='object',
value={
'key1': ObjectVariable(
name='object',
value={
'key2': StringVariable(name='key2', value='value2'),
},
),
'key2': ArrayVariable(
name='array',
value=[
StringVariable(name='key5_1', value='value5_1'),
IntegerVariable(name='key5_2', value=42),
ObjectVariable(name='key5_3', value={}),
],
),
},
)
assert var.to_object() == {
'key1': {
'key2': 'value2',
},
'key2': [
'value5_1',
42,
{},
],
}
def test_variable_to_object():
var = StringVariable(name='text', value='text')
assert var.to_object() == 'text'
var = IntegerVariable(name='integer', value=42)
assert var.to_object() == 42
var = FloatVariable(name='float', value=3.14)
assert var.to_object() == 3.14
var = SecretVariable(name='secret', value='secret_value')
assert var.to_object() == 'secret_value'

View File

@ -3,7 +3,7 @@ import type { ReactNode } from 'react'
import SwrInitor from '@/app/components/swr-initor'
import { AppContextProvider } from '@/context/app-context'
import GA, { GaType } from '@/app/components/base/ga'
import HeaderWrapper from '@/app/components/header/HeaderWrapper'
import HeaderWrapper from '@/app/components/header/header-wrapper'
import Header from '@/app/components/header'
import { EventEmitterContextProvider } from '@/context/event-emitter'
import { ProviderContextProvider } from '@/context/provider-context'

View File

@ -218,7 +218,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
</div>
{expand && (
<div className="grow w-0">
<div className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900'>
<div className='flex justify-between items-center text-sm leading-5 font-medium text-text-secondary'>
<div className='truncate' title={appDetail.name}>{appDetail.name}</div>
{isCurrentWorkspaceEditor && <RiArrowDownSLine className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
</div>

View File

@ -49,7 +49,7 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
return (
<div
className={`
shrink-0 flex flex-col bg-white border-r border-gray-200 transition-all
shrink-0 flex flex-col bg-background-default-subtle border-r border-divider-burn transition-all
${expand ? 'w-[216px]' : 'w-14'}
`}
>
@ -60,7 +60,7 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
`}
>
{iconType === 'app' && (
<AppInfo expand={expand}/>
<AppInfo expand={expand} />
)}
{iconType !== 'app' && (
<AppBasic
@ -74,11 +74,11 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
)}
</div>
{!expand && (
<div className='mt-1 mx-auto w-6 h-[1px] bg-gray-100'/>
<div className='mt-1 mx-auto w-6 h-[1px] bg-divider-subtle' />
)}
<nav
className={`
grow space-y-1 bg-white
grow space-y-1
${expand ? 'p-4' : 'px-2.5 py-4'}
`}
>

View File

@ -44,7 +44,7 @@ export default function NavLink({
key={name}
href={href}
className={classNames(
isActive ? 'bg-primary-50 text-primary-600 font-semibold' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-700',
isActive ? 'bg-state-accent-active text-text-accent font-semibold' : 'text-components-menu-item-text hover:bg-gray-100 hover:text-components-menu-item-text-hover',
'group flex items-center h-9 rounded-md py-2 text-sm font-normal',
mode === 'expand' ? 'px-3' : 'px-2.5',
)}
@ -53,7 +53,6 @@ export default function NavLink({
<NavIcon
className={classNames(
'h-4 w-4 flex-shrink-0',
isActive ? 'text-primary-600' : 'text-gray-700',
mode === 'expand' ? 'mr-2' : 'mr-0',
)}
aria-hidden="true"

View File

@ -1,5 +1,7 @@
'use client'
import type { FC } from 'react'
import classNames from '@/utils/classnames'
import { useSelector } from '@/context/app-context'
type LogoSiteProps = {
className?: string
@ -8,9 +10,16 @@ type LogoSiteProps = {
const LogoSite: FC<LogoSiteProps> = ({
className,
}) => {
const { theme } = useSelector((s) => {
return {
theme: s.theme,
}
})
const src = theme === 'light' ? '/logo/logo-site.png' : `/logo/logo-site-${theme}.png`
return (
<img
src='/logo/logo-site.png'
src={src}
className={classNames('block w-auto h-10', className)}
alt='logo'
/>

View File

@ -152,6 +152,10 @@ const VoiceInput = ({
useEffect(() => {
initCanvas()
handleStartRecord()
const recorderRef = recorder?.current
return () => {
recorderRef?.stop()
}
}, [])
const minutes = parseInt(`${parseInt(`${originDuration}`) / 60}`)

View File

@ -22,8 +22,8 @@ const ExploreNav = ({
return (
<Link href="/explore/apps" className={classNames(
className, 'group',
actived && 'bg-white shadow-md',
actived ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
actived && 'bg-components-main-nav-nav-button-bg-active shadow-md',
actived ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text hover:bg-components-main-nav-nav-button-bg-hover',
)}>
{
actived

View File

@ -15,7 +15,7 @@ const HeaderWrapper = ({
return (
<div className={classNames(
'sticky top-0 left-0 right-0 z-30 flex flex-col bg-gray-100 grow-0 shrink-0 basis-auto min-h-[56px]',
'sticky top-0 left-0 right-0 z-30 flex flex-col grow-0 shrink-0 basis-auto min-h-[56px]',
s.header,
isBordered ? 'border-b border-gray-200' : '',
)}

View File

@ -39,16 +39,16 @@ const Nav = ({
return (
<div className={`
flex items-center h-8 mr-0 sm:mr-3 px-0.5 rounded-xl text-sm shrink-0 font-medium
${isActived && 'bg-white shadow-md font-semibold'}
${!curNav && !isActived && 'hover:bg-gray-200'}
${isActived && 'bg-components-main-nav-nav-button-bg-active shadow-md font-semibold'}
${!curNav && !isActived && 'hover:bg-components-main-nav-nav-button-bg-hover'}
`}>
<Link href={link}>
<div
onClick={() => setAppDetail()}
className={classNames(`
flex items-center h-7 px-2.5 cursor-pointer rounded-[10px]
${isActived ? 'text-primary-600' : 'text-gray-500'}
${curNav && isActived && 'hover:bg-primary-50'}
${isActived ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text'}
${curNav && isActived && 'hover:bg-components-main-nav-nav-button-bg-active-hover'}
`)}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}

View File

@ -55,8 +55,8 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
{({ open }) => (
<>
<Menu.Button className={cn(
'group inline-flex items-center w-full h-7 justify-center rounded-[10px] pl-2 pr-2.5 text-[14px] font-semibold text-primary-600 hover:bg-primary-50',
open && 'bg-primary-50',
'group inline-flex items-center w-full h-7 justify-center rounded-[10px] pl-2 pr-2.5 text-[14px] font-semibold text-components-main-nav-nav-button-text-active hover:hover:bg-components-main-nav-nav-button-bg-active-hover',
open && 'bg-components-main-nav-nav-button-bg-active',
)}>
<div className='max-w-[180px] truncate' title={curNav?.name}>{curNav?.name}</div>
<RiArrowDownSLine

View File

@ -0,0 +1,62 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import type { Var } from '@/app/components/workflow/types'
import { SimpleSelect } from '@/app/components/base/select'
type Props = {
schema: CredentialFormSchema
readonly: boolean
value: string
onChange: (value: string | number, varKindType: VarKindType, varInfo?: Var) => void
}
const ConstantField: FC<Props> = ({
schema,
readonly,
value,
onChange,
}) => {
const language = useLanguage()
const placeholder = (schema as CredentialFormSchemaSelect).placeholder
const handleStaticChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value === '' ? '' : parseFloat(e.target.value)
onChange(value, VarKindType.constant)
}, [onChange])
const handleSelectChange = useCallback((value: string | number) => {
value = value === null ? '' : value
onChange(value as string, VarKindType.constant)
}, [onChange])
return (
<>
{schema.type === FormTypeEnum.select && (
<SimpleSelect
wrapperClassName='w-full !h-8'
className='flex items-center'
disabled={readonly}
items={(schema as CredentialFormSchemaSelect).options.map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
onSelect={item => handleSelectChange(item.value)}
placeholder={placeholder?.[language] || placeholder?.en_US}
/>
)}
{schema.type === FormTypeEnum.textNumber && (
<input
type='number'
className='w-full h-8 leading-8 pl-0.5 bg-transparent text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
value={value}
onChange={handleStaticChange}
readOnly={readonly}
placeholder={placeholder?.[language] || placeholder?.en_US}
min={(schema as CredentialFormSchemaNumberInput).min}
max={(schema as CredentialFormSchemaNumberInput).max}
/>
)}
</>
)
}
export default React.memo(ConstantField)

View File

@ -10,8 +10,10 @@ import produce from 'immer'
import { useStoreApi } from 'reactflow'
import VarReferencePopup from './var-reference-popup'
import { getNodeInfoById, isENV, isSystemVar } from './utils'
import ConstantField from './constant-field'
import cn from '@/utils/classnames'
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { BlockEnum } from '@/app/components/workflow/types'
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
import { Line3 } from '@/app/components/base/icons/src/public/common'
@ -47,6 +49,7 @@ type Props = {
availableNodes?: Node[]
availableVars?: NodeOutPutVar[]
isAddBtnTrigger?: boolean
schema?: CredentialFormSchema
}
const VarReferencePicker: FC<Props> = ({
@ -64,6 +67,7 @@ const VarReferencePicker: FC<Props> = ({
availableNodes: passedInAvailableNodes,
availableVars,
isAddBtnTrigger,
schema,
}) => {
const { t } = useTranslation()
const store = useStoreApi()
@ -192,10 +196,6 @@ const VarReferencePicker: FC<Props> = ({
setOpen(false)
}, [onChange, varKindType])
const handleStaticChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value as string, varKindType)
}, [onChange, varKindType])
const handleClearVar = useCallback(() => {
if (varKindType === VarKindType.constant)
onChange('', varKindType)
@ -265,14 +265,11 @@ const VarReferencePicker: FC<Props> = ({
</div>)}
{isConstant
? (
<input
type='text'
className='w-full h-8 leading-8 pl-0.5 bg-transparent text-[13px] font-normal text-gray-900 placeholder:text-gray-400 focus:outline-none overflow-hidden'
value={isConstant ? value : ''}
onChange={handleStaticChange}
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
readOnly={readonly}
<ConstantField
value={value as string}
onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)}
schema={schema as CredentialFormSchema}
readonly={readonly}
/>
)
: (

View File

@ -48,6 +48,8 @@ const InputVarList: FC<Props> = ({
return 'Number'
else if (type === FormTypeEnum.files)
return 'Files'
else if (type === FormTypeEnum.select)
return 'Options'
else
return 'String'
}
@ -114,17 +116,19 @@ const InputVarList: FC<Props> = ({
return (
<div className='space-y-3'>
{
schema.map(({
variable,
label,
type,
required,
tooltip,
}, index) => {
schema.map((schema, index) => {
const {
variable,
label,
type,
required,
tooltip,
} = schema
const varInput = value[variable]
const isNumber = type === FormTypeEnum.textNumber
const isSelect = type === FormTypeEnum.select
const isFile = type === FormTypeEnum.files
const isString = type !== FormTypeEnum.textNumber && type !== FormTypeEnum.files
const isString = type !== FormTypeEnum.textNumber && type !== FormTypeEnum.files && type !== FormTypeEnum.select
return (
<div key={variable} className='space-y-1'>
<div className='flex items-center h-[18px] space-x-2'>
@ -145,7 +149,7 @@ const InputVarList: FC<Props> = ({
placeholderClassName='!leading-[21px]'
/>
)}
{isNumber && (
{(isNumber || isSelect) && (
<VarReferencePicker
readonly={readOnly}
isShowNodeName
@ -155,7 +159,9 @@ const InputVarList: FC<Props> = ({
onOpen={handleOpen(index)}
isSupportConstantValue={isSupportConstantValue}
defaultVarKindType={varInput?.type}
filterVar={filterVar}
filterVar={isNumber ? filterVar : undefined}
availableVars={isSelect ? availableVars : undefined}
schema={schema}
/>
)}
{isFile && (

View File

@ -8,10 +8,13 @@ import { fetchAppList } from '@/service/apps'
import Loading from '@/app/components/base/loading'
import { fetchCurrentWorkspace, fetchLanggeniusVersion, fetchUserProfile } from '@/service/common'
import type { App } from '@/types/app'
import { Theme } from '@/types/app'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
export type AppContextValue = {
theme: Theme
setTheme: (theme: Theme) => void
apps: App[]
mutateApps: VoidFunction
userProfile: UserProfileResponse
@ -49,6 +52,8 @@ const initialWorkspaceInfo: ICurrentWorkspace = {
}
const AppContext = createContext<AppContextValue>({
theme: Theme.light,
setTheme: () => { },
apps: [],
mutateApps: () => { },
userProfile: {
@ -112,11 +117,24 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
setCurrentWorkspace(currentWorkspaceResponse)
}, [currentWorkspaceResponse])
const [theme, setTheme] = useState<Theme>(Theme.light)
const handleSetTheme = useCallback((theme: Theme) => {
setTheme(theme)
globalThis.document.documentElement.setAttribute('data-theme', theme)
}, [])
useEffect(() => {
globalThis.document.documentElement.setAttribute('data-theme', theme)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
if (!appList || !userProfile)
return <Loading type='app' />
return (
<AppContext.Provider value={{
theme,
setTheme: handleSetTheme,
apps: appList.data,
mutateApps,
userProfile,
@ -133,7 +151,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
}}>
<div className='flex flex-col h-full overflow-y-auto'>
{globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />}
<div ref={pageContainerRef} className='grow relative flex flex-col overflow-y-auto overflow-x-hidden bg-gray-100'>
<div ref={pageContainerRef} className='grow relative flex flex-col overflow-y-auto overflow-x-hidden bg-background-body'>
{children}
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -4,6 +4,7 @@ module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./context/**/*.{js,ts,jsx,tsx}',
],
theme: {
typography: require('./typography'),

View File

@ -3,6 +3,11 @@ import type { CollectionType } from '@/app/components/tools/types'
import type { LanguagesSupported } from '@/i18n/language'
import type { Tag } from '@/app/components/base/tag-management/constant'
export enum Theme {
light = 'light',
dark = 'dark',
}
export enum ProviderType {
openai = 'openai',
anthropic = 'anthropic',