mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-16 17:15:59 +08:00
Merge branch 'refs/heads/main' into feat/workflow-parallel-support
# Conflicts: # api/core/workflow/entities/variable_pool.py
This commit is contained in:
commit
e9bfedab9b
@ -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}
|
||||
|
@ -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
|
||||
|
@ -23,6 +23,7 @@
|
||||
- tongyi
|
||||
- wenxin
|
||||
- moonshot
|
||||
- tencent
|
||||
- jina
|
||||
- chatglm
|
||||
- yi
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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
|
@ -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
|
||||
]
|
||||
}
|
@ -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
|
29
api/core/model_runtime/model_providers/tencent/tencent.py
Normal file
29
api/core/model_runtime/model_providers/tencent/tencent.py
Normal 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
|
49
api/core/model_runtime/model_providers/tencent/tencent.yaml
Normal file
49
api/core/model_runtime/model_providers/tencent/tencent.yaml
Normal 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
|
@ -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:
|
||||
|
@ -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], /):
|
||||
"""
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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'}
|
||||
`}
|
||||
>
|
||||
|
@ -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"
|
||||
|
@ -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'
|
||||
/>
|
||||
|
@ -152,6 +152,10 @@ const VoiceInput = ({
|
||||
useEffect(() => {
|
||||
initCanvas()
|
||||
handleStartRecord()
|
||||
const recorderRef = recorder?.current
|
||||
return () => {
|
||||
recorderRef?.stop()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const minutes = parseInt(`${parseInt(`${originDuration}`) / 60}`)
|
||||
|
@ -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
|
||||
|
@ -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' : '',
|
||||
)}
|
@ -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)}
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
|
@ -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(({
|
||||
schema.map((schema, index) => {
|
||||
const {
|
||||
variable,
|
||||
label,
|
||||
type,
|
||||
required,
|
||||
tooltip,
|
||||
}, index) => {
|
||||
} = 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 && (
|
||||
|
@ -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>
|
||||
|
BIN
web/public/logo/logo-site-dark.png
Normal file
BIN
web/public/logo/logo-site-dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -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'),
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user