mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-06 02:25:05 +08:00
Compare commits
5 Commits
3ee9653170
...
618d6bc924
| Author | SHA1 | Date | |
|---|---|---|---|
| 618d6bc924 | |||
| 762aa4b8c4 | |||
| 9cd09488ca | |||
| f2806a8332 | |||
| b6e34e3aa7 |
@ -108,7 +108,9 @@ class Retrieval(ToolBase, ABC):
|
|||||||
if self._param.rerank_id:
|
if self._param.rerank_id:
|
||||||
rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id)
|
rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id)
|
||||||
|
|
||||||
query = kwargs["query"]
|
vars = self.get_input_elements_from_text(kwargs["query"])
|
||||||
|
vars = {k:o["value"] for k,o in vars.items()}
|
||||||
|
query = self.string_format(kwargs["query"], vars)
|
||||||
if self._param.cross_languages:
|
if self._param.cross_languages:
|
||||||
query = cross_languages(kbs[0].tenant_id, None, query, self._param.cross_languages)
|
query = cross_languages(kbs[0].tenant_id, None, query, self._param.cross_languages)
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,7 @@ dependencies = [
|
|||||||
"pyclipper==1.3.0.post5",
|
"pyclipper==1.3.0.post5",
|
||||||
"pycryptodomex==3.20.0",
|
"pycryptodomex==3.20.0",
|
||||||
"pymysql>=1.1.1,<2.0.0",
|
"pymysql>=1.1.1,<2.0.0",
|
||||||
"pypdf>=5.0.0,<6.0.0",
|
"pypdf==6.0.0",
|
||||||
"python-dotenv==1.0.1",
|
"python-dotenv==1.0.1",
|
||||||
"python-dateutil==2.8.2",
|
"python-dateutil==2.8.2",
|
||||||
"python-pptx>=1.0.2,<2.0.0",
|
"python-pptx>=1.0.2,<2.0.0",
|
||||||
|
|||||||
@ -68,7 +68,7 @@ class Base(ABC):
|
|||||||
pmpt.append({
|
pmpt.append({
|
||||||
"type": "image_url",
|
"type": "image_url",
|
||||||
"image_url": {
|
"image_url": {
|
||||||
"url": f"data:image/jpeg;base64,{img}" if img[:4] != "data" else img
|
"url": img if isinstance(img, str) and img.startswith("data:") else f"data:image/png;base64,{img}"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return pmpt
|
return pmpt
|
||||||
@ -109,16 +109,33 @@ class Base(ABC):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def image2base64(image):
|
def image2base64(image):
|
||||||
|
# Return a data URL with the correct MIME to avoid provider mismatches
|
||||||
if isinstance(image, bytes):
|
if isinstance(image, bytes):
|
||||||
return base64.b64encode(image).decode("utf-8")
|
# Best-effort magic number sniffing
|
||||||
|
mime = "image/png"
|
||||||
|
if len(image) >= 2 and image[0] == 0xFF and image[1] == 0xD8:
|
||||||
|
mime = "image/jpeg"
|
||||||
|
b64 = base64.b64encode(image).decode("utf-8")
|
||||||
|
return f"data:{mime};base64,{b64}"
|
||||||
if isinstance(image, BytesIO):
|
if isinstance(image, BytesIO):
|
||||||
return base64.b64encode(image.getvalue()).decode("utf-8")
|
data = image.getvalue()
|
||||||
|
mime = "image/png"
|
||||||
|
if len(data) >= 2 and data[0] == 0xFF and data[1] == 0xD8:
|
||||||
|
mime = "image/jpeg"
|
||||||
|
b64 = base64.b64encode(data).decode("utf-8")
|
||||||
|
return f"data:{mime};base64,{b64}"
|
||||||
buffered = BytesIO()
|
buffered = BytesIO()
|
||||||
|
fmt = "JPEG"
|
||||||
try:
|
try:
|
||||||
image.save(buffered, format="JPEG")
|
image.save(buffered, format="JPEG")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
buffered = BytesIO() # reset buffer before saving PNG
|
||||||
image.save(buffered, format="PNG")
|
image.save(buffered, format="PNG")
|
||||||
return base64.b64encode(buffered.getvalue()).decode("utf-8")
|
fmt = "PNG"
|
||||||
|
data = buffered.getvalue()
|
||||||
|
b64 = base64.b64encode(data).decode("utf-8")
|
||||||
|
mime = f"image/{fmt.lower()}"
|
||||||
|
return f"data:{mime};base64,{b64}"
|
||||||
|
|
||||||
def prompt(self, b64):
|
def prompt(self, b64):
|
||||||
return [
|
return [
|
||||||
@ -372,6 +389,16 @@ class OllamaCV(Base):
|
|||||||
self.keep_alive = kwargs.get("ollama_keep_alive", int(os.environ.get("OLLAMA_KEEP_ALIVE", -1)))
|
self.keep_alive = kwargs.get("ollama_keep_alive", int(os.environ.get("OLLAMA_KEEP_ALIVE", -1)))
|
||||||
Base.__init__(self, **kwargs)
|
Base.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_img(self, img):
|
||||||
|
if not isinstance(img, str):
|
||||||
|
return img
|
||||||
|
|
||||||
|
#remove the header like "data/*;base64,"
|
||||||
|
if img.startswith("data:") and ";base64," in img:
|
||||||
|
img = img.split(";base64,")[1]
|
||||||
|
return img
|
||||||
|
|
||||||
def _clean_conf(self, gen_conf):
|
def _clean_conf(self, gen_conf):
|
||||||
options = {}
|
options = {}
|
||||||
if "temperature" in gen_conf:
|
if "temperature" in gen_conf:
|
||||||
@ -390,9 +417,12 @@ class OllamaCV(Base):
|
|||||||
hist.insert(0, {"role": "system", "content": system})
|
hist.insert(0, {"role": "system", "content": system})
|
||||||
if not images:
|
if not images:
|
||||||
return hist
|
return hist
|
||||||
|
temp_images = []
|
||||||
|
for img in images:
|
||||||
|
temp_images.append(self._clean_img(img))
|
||||||
for his in hist:
|
for his in hist:
|
||||||
if his["role"] == "user":
|
if his["role"] == "user":
|
||||||
his["images"] = images
|
his["images"] = temp_images
|
||||||
break
|
break
|
||||||
return hist
|
return hist
|
||||||
|
|
||||||
@ -661,8 +691,8 @@ class AnthropicCV(Base):
|
|||||||
"type": "image",
|
"type": "image",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "base64",
|
"type": "base64",
|
||||||
"media_type": "image/jpeg" if img[:4] != "data" else img.split(":")[1].split(";")[0],
|
"media_type": (img.split(":")[1].split(";")[0] if isinstance(img, str) and img[:4] == "data" else "image/png"),
|
||||||
"data": img if img[:4] != "data" else img.split(",")[1]
|
"data": (img.split(",")[1] if isinstance(img, str) and img[:4] == "data" else img)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
87
uv.lock
generated
87
uv.lock
generated
@ -1,5 +1,4 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 1
|
|
||||||
requires-python = ">=3.10, <3.13"
|
requires-python = ">=3.10, <3.13"
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.12' and sys_platform == 'darwin'",
|
"python_full_version >= '3.12' and sys_platform == 'darwin'",
|
||||||
@ -1190,6 +1189,9 @@ name = "datrie"
|
|||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d" }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://mirrors.aliyun.com/pypi/packages/44/02/53f0cf0bf0cd629ba6c2cc13f2f9db24323459e9c19463783d890a540a96/datrie-0.8.2-pp273-pypy_73-win32.whl", hash = "sha256:b07bd5fdfc3399a6dab86d6e35c72b1dbd598e80c97509c7c7518ab8774d3fda" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "debugpy"
|
name = "debugpy"
|
||||||
@ -2162,37 +2164,37 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "greenlet"
|
name = "greenlet"
|
||||||
version = "3.2.3"
|
version = "3.2.4"
|
||||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365" }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/92/db/b4c12cff13ebac2786f4f217f06588bccd8b53d260453404ef22b121fc3a/greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/52/61/75b4abd8147f13f70986df2801bf93735c1bd87ea780d70e3b3ecda8c165/greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/35/aa/6894ae299d059d26254779a5088632874b80ee8cf89a88bca00b0709d22f/greenlet-3.2.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a433dbc54e4a37e4fff90ef34f25a8c00aed99b06856f0119dcf09fbafa16392" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/30/64/e01a8261d13c47f3c082519a5e9dbf9e143cc0498ed20c911d04e54d526c/greenlet-3.2.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:72e77ed69312bab0434d7292316d5afd6896192ac4327d44f3d613ecb85b037c" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/47/48/ff9ca8ba9772d083a4f5221f7b4f0ebe8978131a9ae0909cf202f94cd879/greenlet-3.2.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:68671180e3849b963649254a882cd544a3c75bfcd2c527346ad8bb53494444db" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/e9/45/626e974948713bc15775b696adb3eb0bd708bec267d6d2d5c47bb47a6119/greenlet-3.2.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49c8cfb18fb419b3d08e011228ef8a25882397f3a859b9fe1436946140b6756b" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/b1/8e/8b6f42c67d5df7db35b8c55c9a850ea045219741bb14416255616808c690/greenlet-3.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:efc6dc8a792243c31f2f5674b670b3a95d46fa1c6a912b8e310d6f542e7b0712" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/05/46/ab58828217349500a7ebb81159d52ca357da747ff1797c29c6023d79d798/greenlet-3.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:731e154aba8e757aedd0781d4b240f1225b075b4409f1bb83b05ff410582cf00" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/68/7f/d1b537be5080721c0f0089a8447d4ef72839039cdb743bdd8ffd23046e9a/greenlet-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:96c20252c2f792defe9a115d3287e14811036d51e78b3aaddbee23b69b216302" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/fc/2e/d4fcb2978f826358b673f779f78fa8a32ee37df11920dc2bb5589cbeecef/greenlet-3.2.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/16/24/929f853e0202130e4fe163bc1d05a671ce8dcd604f790e14896adac43a52/greenlet-3.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/d1/b2/0320715eb61ae70c25ceca2f1d5ae620477d246692d9cc284c13242ec31c/greenlet-3.2.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/bd/49/445fd1a210f4747fedf77615d941444349c6a3a4a1135bba9701337cd966/greenlet-3.2.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/7e/c8/ca19760cf6eae75fa8dc32b487e963d863b3ee04a7637da77b616703bc37/greenlet-3.2.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/65/89/77acf9e3da38e9bcfca881e43b02ed467c1dedc387021fc4d9bd9928afb8/greenlet-3.2.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/97/c6/ae244d7c95b23b7130136e07a9cc5aadd60d59b5951180dc7dc7e8edaba7/greenlet-3.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/89/5f/b16dec0cbfd3070658e0d744487919740c6d45eb90946f6787689a7efbce/greenlet-3.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/66/77/d48fb441b5a71125bcac042fc5b1494c806ccb9a1432ecaa421e72157f77/greenlet-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2877,7 +2879,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litellm"
|
name = "litellm"
|
||||||
version = "1.75.0"
|
version = "1.75.5.post1"
|
||||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
@ -2892,9 +2894,9 @@ dependencies = [
|
|||||||
{ name = "tiktoken" },
|
{ name = "tiktoken" },
|
||||||
{ name = "tokenizers" },
|
{ name = "tokenizers" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1b/28/50837cb0246c42a8caac45610572883de7f478543cf4d143e84f099c0234/litellm-1.75.0.tar.gz", hash = "sha256:ec7fbfe79e1b9cd4a2b36ca9e71e71959d8fc43305b222e5f257aced1a0d1d63" }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/10/97/6091a020895102a20f1da204ebe68c1293123555476b38e749f95ba5981c/litellm-1.75.5.post1.tar.gz", hash = "sha256:e40a0e4b25032755dc5df7f02742abe9e3b8836236363f605f3bdd363cb5a0d0" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/db/43/e10905870d42e927de3b095a9248f2764156c7eb45ec172d72be35cd2bb4/litellm-1.75.0-py3-none-any.whl", hash = "sha256:1657472f37d291b366050dd2035e3640eebd96142d6fa0f935ceb290a0e1d5ad" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/8f/76/780f68a3b26227136a5147c76860aacedcae9bf1b7afc1c991ec9cad11bc/litellm-1.75.5.post1-py3-none-any.whl", hash = "sha256:1c72809a9c8f6e132ad06eb7e628f674c775b0ce6bfb58cbd37e8b585d929cb7" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3803,7 +3805,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openai"
|
name = "openai"
|
||||||
version = "1.99.1"
|
version = "1.99.9"
|
||||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
@ -3815,9 +3817,9 @@ dependencies = [
|
|||||||
{ name = "tqdm" },
|
{ name = "tqdm" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/30/f0fb7907a77e733bb801c7bdcde903500b31215141cdb261f04421e6fbec/openai-1.99.1.tar.gz", hash = "sha256:2c9d8e498c298f51bb94bcac724257a3a6cac6139ccdfc1186c6708f7a93120f" }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8a/d2/ef89c6f3f36b13b06e271d3cc984ddd2f62508a0972c1cbcc8485a6644ff/openai-1.99.9.tar.gz", hash = "sha256:f2082d155b1ad22e83247c3de3958eb4255b20ccf4a1de2e6681b6957b554e92" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/54/15/9c85154ffd283abfc43309ff3aaa63c3fd02f7767ee684e73670f6c5ade2/openai-1.99.1-py3-none-any.whl", hash = "sha256:8eeccc69e0ece1357b51ca0d9fb21324afee09b20c3e5b547d02445ca18a4e03" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/e8/fb/df274ca10698ee77b07bff952f302ea627cc12dac6b85289485dd77db6de/openai-1.99.9-py3-none-any.whl", hash = "sha256:9dbcdb425553bae1ac5d947147bebbd630d91bbfc7788394d4c4f3a35682ab3a" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4662,6 +4664,8 @@ wheels = [
|
|||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c" },
|
||||||
|
{ url = "https://mirrors.aliyun.com/pypi/packages/9f/7c/f5b0556590e7b4e710509105e668adb55aa9470a9f0e4dea9c40a4a11ce1/pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56" },
|
||||||
|
{ url = "https://mirrors.aliyun.com/pypi/packages/33/38/dcc795578d610ea1aaffef4b148b8cafcfcf4d126b1e58231ddc4e475c70/pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630" },
|
||||||
@ -4685,6 +4689,8 @@ wheels = [
|
|||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc" },
|
||||||
|
{ url = "https://mirrors.aliyun.com/pypi/packages/e7/c5/9140bb867141d948c8e242013ec8a8011172233c898dfdba0a2417c3169a/pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458" },
|
||||||
|
{ url = "https://mirrors.aliyun.com/pypi/packages/5e/6a/04acb4978ce08ab16890c70611ebc6efd251681341617bbb9e53356dee70/pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/eb/df/3f1ea084e43b91e6d2b6b3493cc948864c17ea5d93ff1261a03812fbfd1a/pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/eb/df/3f1ea084e43b91e6d2b6b3493cc948864c17ea5d93ff1261a03812fbfd1a/pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/f3/83ffbdfa0c8f9154bcd8866895f6cae5a3ec749da8b0840603cf936c4412/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/f3/83ffbdfa0c8f9154bcd8866895f6cae5a3ec749da8b0840603cf936c4412/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea" },
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/9d/c113e640aaf02af5631ae2686b742aac5cd0e1402b9d6512b1c7ec5ef05d/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/9d/c113e640aaf02af5631ae2686b742aac5cd0e1402b9d6512b1c7ec5ef05d/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781" },
|
||||||
@ -4920,14 +4926,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pypdf"
|
name = "pypdf"
|
||||||
version = "5.9.0"
|
version = "6.0.0"
|
||||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/89/3a/584b97a228950ed85aec97c811c68473d9b8d149e6a8c155668287cf1a28/pypdf-5.9.0.tar.gz", hash = "sha256:30f67a614d558e495e1fbb157ba58c1de91ffc1718f5e0dfeb82a029233890a1" }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/20/ac/a300a03c3b34967c050677ccb16e7a4b65607ee5df9d51e8b6d713de4098/pypdf-6.0.0.tar.gz", hash = "sha256:282a99d2cc94a84a3a3159f0d9358c0af53f85b4d28d76ea38b96e9e5ac2a08d" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://mirrors.aliyun.com/pypi/packages/48/d9/6cff57c80a6963e7dd183bf09e9f21604a77716644b1e580e97b259f7612/pypdf-5.9.0-py3-none-any.whl", hash = "sha256:be10a4c54202f46d9daceaa8788be07aa8cd5ea8c25c529c50dd509206382c35" },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/2c/83/2cacc506eb322bb31b747bc06ccb82cc9aa03e19ee9c1245e538e49d52be/pypdf-6.0.0-py3-none-any.whl", hash = "sha256:56ea60100ce9f11fc3eec4f359da15e9aec3821b036c1f06d2b660d35683abb8" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5486,7 +5492,7 @@ requires-dist = [
|
|||||||
{ name = "pyicu", specifier = ">=2.13.1,<3.0.0" },
|
{ name = "pyicu", specifier = ">=2.13.1,<3.0.0" },
|
||||||
{ name = "pymysql", specifier = ">=1.1.1,<2.0.0" },
|
{ name = "pymysql", specifier = ">=1.1.1,<2.0.0" },
|
||||||
{ name = "pyodbc", specifier = ">=5.2.0,<6.0.0" },
|
{ name = "pyodbc", specifier = ">=5.2.0,<6.0.0" },
|
||||||
{ name = "pypdf", specifier = ">=5.0.0,<6.0.0" },
|
{ name = "pypdf", specifier = "===6.0.0" },
|
||||||
{ name = "pypdf2", specifier = ">=3.0.1,<4.0.0" },
|
{ name = "pypdf2", specifier = ">=3.0.1,<4.0.0" },
|
||||||
{ name = "python-calamine", specifier = ">=0.4.0" },
|
{ name = "python-calamine", specifier = ">=0.4.0" },
|
||||||
{ name = "python-dateutil", specifier = "==2.8.2" },
|
{ name = "python-dateutil", specifier = "==2.8.2" },
|
||||||
@ -5533,7 +5539,6 @@ requires-dist = [
|
|||||||
{ name = "yfinance", specifier = "==0.2.65" },
|
{ name = "yfinance", specifier = "==0.2.65" },
|
||||||
{ name = "zhipuai", specifier = "==2.0.1" },
|
{ name = "zhipuai", specifier = "==2.0.1" },
|
||||||
]
|
]
|
||||||
provides-extras = ["full"]
|
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
test = [
|
test = [
|
||||||
|
|||||||
@ -202,7 +202,7 @@ export const useSendMessageWithSse = (
|
|||||||
[Authorization]: getAuthorization(),
|
[Authorization]: getAuthorization(),
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(omit(body, 'chatBoxId')),
|
||||||
signal: controller?.signal || sseRef.current?.signal,
|
signal: controller?.signal || sseRef.current?.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -228,6 +228,7 @@ export const useSendMessageWithSse = (
|
|||||||
setAnswer({
|
setAnswer({
|
||||||
...d,
|
...d,
|
||||||
conversationId: body?.conversation_id,
|
conversationId: body?.conversation_id,
|
||||||
|
chatBoxId: body.chatBoxId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -82,6 +82,7 @@ export interface Message {
|
|||||||
audio_binary?: string;
|
audio_binary?: string;
|
||||||
data?: any;
|
data?: any;
|
||||||
files?: File[];
|
files?: File[];
|
||||||
|
chatBoxId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IReferenceChunk {
|
export interface IReferenceChunk {
|
||||||
@ -117,6 +118,7 @@ export interface IAnswer {
|
|||||||
id?: string;
|
id?: string;
|
||||||
audio_binary?: string;
|
audio_binary?: string;
|
||||||
data?: any;
|
data?: any;
|
||||||
|
chatBoxId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Docagg {
|
export interface Docagg {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export default {
|
|||||||
previousPage: '上一页',
|
previousPage: '上一页',
|
||||||
nextPage: '下一页',
|
nextPage: '下一页',
|
||||||
add: '添加',
|
add: '添加',
|
||||||
|
promptPlaceholder: '请输入或使用 / 快速插入变量。',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
|||||||
56
web/src/pages/agent/canvas/context.tsx
Normal file
56
web/src/pages/agent/canvas/context.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
interface DropdownContextType {
|
||||||
|
canShowDropdown: () => boolean;
|
||||||
|
setActiveDropdown: (type: 'handle' | 'drag') => void;
|
||||||
|
clearActiveDropdown: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownContext = createContext<DropdownContextType | null>(null);
|
||||||
|
|
||||||
|
export const useDropdownManager = () => {
|
||||||
|
const context = useContext(DropdownContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useDropdownManager must be used within DropdownProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DropdownProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DropdownProvider = ({ children }: DropdownProviderProps) => {
|
||||||
|
const activeDropdownRef = useRef<'handle' | 'drag' | null>(null);
|
||||||
|
|
||||||
|
const canShowDropdown = useCallback(() => {
|
||||||
|
const current = activeDropdownRef.current;
|
||||||
|
return !current;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setActiveDropdown = useCallback((type: 'handle' | 'drag') => {
|
||||||
|
activeDropdownRef.current = type;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const clearActiveDropdown = useCallback(() => {
|
||||||
|
activeDropdownRef.current = null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const value: DropdownContextType = {
|
||||||
|
canShowDropdown,
|
||||||
|
setActiveDropdown,
|
||||||
|
clearActiveDropdown,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</DropdownContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -4,17 +4,20 @@ import {
|
|||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip';
|
} from '@/components/ui/tooltip';
|
||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
|
Connection,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
ControlButton,
|
ControlButton,
|
||||||
Controls,
|
Controls,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
Position,
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
import { NotebookPen } from 'lucide-react';
|
import { NotebookPen } from 'lucide-react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ChatSheet } from '../chat/chat-sheet';
|
import { ChatSheet } from '../chat/chat-sheet';
|
||||||
import { AgentBackground } from '../components/background';
|
import { AgentBackground } from '../components/background';
|
||||||
@ -22,7 +25,9 @@ import {
|
|||||||
AgentChatContext,
|
AgentChatContext,
|
||||||
AgentChatLogContext,
|
AgentChatLogContext,
|
||||||
AgentInstanceContext,
|
AgentInstanceContext,
|
||||||
|
HandleContext,
|
||||||
} from '../context';
|
} from '../context';
|
||||||
|
|
||||||
import FormSheet from '../form-sheet/next';
|
import FormSheet from '../form-sheet/next';
|
||||||
import {
|
import {
|
||||||
useHandleDrop,
|
useHandleDrop,
|
||||||
@ -33,6 +38,8 @@ import { useAddNode } from '../hooks/use-add-node';
|
|||||||
import { useBeforeDelete } from '../hooks/use-before-delete';
|
import { useBeforeDelete } from '../hooks/use-before-delete';
|
||||||
import { useCacheChatLog } from '../hooks/use-cache-chat-log';
|
import { useCacheChatLog } from '../hooks/use-cache-chat-log';
|
||||||
import { useMoveNote } from '../hooks/use-move-note';
|
import { useMoveNote } from '../hooks/use-move-note';
|
||||||
|
import { useDropdownManager } from './context';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useHideFormSheetOnNodeDeletion,
|
useHideFormSheetOnNodeDeletion,
|
||||||
useShowDrawer,
|
useShowDrawer,
|
||||||
@ -46,6 +53,7 @@ import { RagNode } from './node';
|
|||||||
import { AgentNode } from './node/agent-node';
|
import { AgentNode } from './node/agent-node';
|
||||||
import { BeginNode } from './node/begin-node';
|
import { BeginNode } from './node/begin-node';
|
||||||
import { CategorizeNode } from './node/categorize-node';
|
import { CategorizeNode } from './node/categorize-node';
|
||||||
|
import { InnerNextStepDropdown } from './node/dropdown/next-step-dropdown';
|
||||||
import { GenerateNode } from './node/generate-node';
|
import { GenerateNode } from './node/generate-node';
|
||||||
import { InvokeNode } from './node/invoke-node';
|
import { InvokeNode } from './node/invoke-node';
|
||||||
import { IterationNode, IterationStartNode } from './node/iteration-node';
|
import { IterationNode, IterationStartNode } from './node/iteration-node';
|
||||||
@ -96,7 +104,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
onConnect,
|
onConnect: originalOnConnect,
|
||||||
onEdgesChange,
|
onEdgesChange,
|
||||||
onNodesChange,
|
onNodesChange,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
@ -147,14 +155,6 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
const onPaneClick = useCallback(() => {
|
|
||||||
hideFormDrawer();
|
|
||||||
if (imgVisible) {
|
|
||||||
addNoteNode(mouse);
|
|
||||||
hideImage();
|
|
||||||
}
|
|
||||||
}, [addNoteNode, hideFormDrawer, hideImage, imgVisible, mouse]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chatVisible) {
|
if (!chatVisible) {
|
||||||
clearEventList();
|
clearEventList();
|
||||||
@ -172,6 +172,73 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
|
|
||||||
useHideFormSheetOnNodeDeletion({ hideFormDrawer });
|
useHideFormSheetOnNodeDeletion({ hideFormDrawer });
|
||||||
|
|
||||||
|
const { visible, hideModal, showModal } = useSetModalState();
|
||||||
|
const [dropdownPosition, setDropdownPosition] = useState({ x: 0, y: 0 });
|
||||||
|
|
||||||
|
const isConnectedRef = useRef(false);
|
||||||
|
const connectionStartRef = useRef<{
|
||||||
|
nodeId: string;
|
||||||
|
handleId: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
const preventCloseRef = useRef(false);
|
||||||
|
|
||||||
|
const { setActiveDropdown, clearActiveDropdown } = useDropdownManager();
|
||||||
|
|
||||||
|
const onPaneClick = useCallback(() => {
|
||||||
|
hideFormDrawer();
|
||||||
|
if (visible && !preventCloseRef.current) {
|
||||||
|
hideModal();
|
||||||
|
clearActiveDropdown();
|
||||||
|
}
|
||||||
|
if (imgVisible) {
|
||||||
|
addNoteNode(mouse);
|
||||||
|
hideImage();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
hideFormDrawer,
|
||||||
|
visible,
|
||||||
|
hideModal,
|
||||||
|
imgVisible,
|
||||||
|
addNoteNode,
|
||||||
|
mouse,
|
||||||
|
hideImage,
|
||||||
|
clearActiveDropdown,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const onConnect = (connection: Connection) => {
|
||||||
|
originalOnConnect(connection);
|
||||||
|
isConnectedRef.current = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OnConnectStart = (event: any, params: any) => {
|
||||||
|
isConnectedRef.current = false;
|
||||||
|
|
||||||
|
if (params && params.nodeId && params.handleId) {
|
||||||
|
connectionStartRef.current = {
|
||||||
|
nodeId: params.nodeId,
|
||||||
|
handleId: params.handleId,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
connectionStartRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const OnConnectEnd = (event: MouseEvent | TouchEvent) => {
|
||||||
|
if ('clientX' in event && 'clientY' in event) {
|
||||||
|
const { clientX, clientY } = event;
|
||||||
|
setDropdownPosition({ x: clientX, y: clientY });
|
||||||
|
if (!isConnectedRef.current) {
|
||||||
|
setActiveDropdown('drag');
|
||||||
|
showModal();
|
||||||
|
preventCloseRef.current = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
preventCloseRef.current = false;
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.canvasWrapper}>
|
<div className={styles.canvasWrapper}>
|
||||||
<svg
|
<svg
|
||||||
@ -206,6 +273,8 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
edgeTypes={edgeTypes}
|
edgeTypes={edgeTypes}
|
||||||
onDrop={onDrop}
|
onDrop={onDrop}
|
||||||
|
onConnectStart={OnConnectStart}
|
||||||
|
onConnectEnd={OnConnectEnd}
|
||||||
onDragOver={onDragOver}
|
onDragOver={onDragOver}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
@ -243,6 +312,27 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
</ControlButton>
|
</ControlButton>
|
||||||
</Controls>
|
</Controls>
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
{visible && (
|
||||||
|
<HandleContext.Provider
|
||||||
|
value={{
|
||||||
|
nodeId: connectionStartRef.current?.nodeId || '',
|
||||||
|
id: connectionStartRef.current?.handleId || '',
|
||||||
|
type: 'source',
|
||||||
|
position: Position.Right,
|
||||||
|
isFromConnectionDrag: true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InnerNextStepDropdown
|
||||||
|
hideModal={() => {
|
||||||
|
hideModal();
|
||||||
|
clearActiveDropdown();
|
||||||
|
}}
|
||||||
|
position={dropdownPosition}
|
||||||
|
>
|
||||||
|
<span></span>
|
||||||
|
</InnerNextStepDropdown>
|
||||||
|
</HandleContext.Provider>
|
||||||
|
)}
|
||||||
</AgentInstanceContext.Provider>
|
</AgentInstanceContext.Provider>
|
||||||
<NotebookPen
|
<NotebookPen
|
||||||
className={cn('hidden absolute size-6', { block: imgVisible })}
|
className={cn('hidden absolute size-6', { block: imgVisible })}
|
||||||
|
|||||||
@ -20,55 +20,111 @@ import { IModalProps } from '@/interfaces/common';
|
|||||||
import { Operator } from '@/pages/agent/constant';
|
import { Operator } from '@/pages/agent/constant';
|
||||||
import { AgentInstanceContext, HandleContext } from '@/pages/agent/context';
|
import { AgentInstanceContext, HandleContext } from '@/pages/agent/context';
|
||||||
import OperatorIcon from '@/pages/agent/operator-icon';
|
import OperatorIcon from '@/pages/agent/operator-icon';
|
||||||
|
import { Position } from '@xyflow/react';
|
||||||
import { lowerFirst } from 'lodash';
|
import { lowerFirst } from 'lodash';
|
||||||
import { PropsWithChildren, createContext, memo, useContext } from 'react';
|
import {
|
||||||
|
PropsWithChildren,
|
||||||
|
createContext,
|
||||||
|
memo,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
type OperatorItemProps = { operators: Operator[] };
|
type OperatorItemProps = {
|
||||||
|
operators: Operator[];
|
||||||
|
isCustomDropdown?: boolean;
|
||||||
|
mousePosition?: { x: number; y: number };
|
||||||
|
};
|
||||||
|
|
||||||
const HideModalContext = createContext<IModalProps<any>['showModal']>(() => {});
|
const HideModalContext = createContext<IModalProps<any>['showModal']>(() => {});
|
||||||
|
const OnNodeCreatedContext = createContext<
|
||||||
|
((newNodeId: string) => void) | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
function OperatorItemList({ operators }: OperatorItemProps) {
|
function OperatorItemList({
|
||||||
|
operators,
|
||||||
|
isCustomDropdown = false,
|
||||||
|
mousePosition,
|
||||||
|
}: OperatorItemProps) {
|
||||||
const { addCanvasNode } = useContext(AgentInstanceContext);
|
const { addCanvasNode } = useContext(AgentInstanceContext);
|
||||||
const { nodeId, id, position } = useContext(HandleContext);
|
const handleContext = useContext(HandleContext);
|
||||||
const hideModal = useContext(HideModalContext);
|
const hideModal = useContext(HideModalContext);
|
||||||
|
const onNodeCreated = useContext(OnNodeCreatedContext);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
const handleClick = (operator: Operator) => {
|
||||||
<ul className="space-y-2">
|
const contextData = handleContext || {
|
||||||
{operators.map((x) => {
|
nodeId: '',
|
||||||
return (
|
id: '',
|
||||||
<Tooltip key={x}>
|
type: 'source' as const,
|
||||||
<TooltipTrigger asChild>
|
position: Position.Right,
|
||||||
<DropdownMenuItem
|
isFromConnectionDrag: true,
|
||||||
key={x}
|
};
|
||||||
className="hover:bg-bg-card py-1 px-3 cursor-pointer rounded-sm flex gap-2 items-center justify-start"
|
|
||||||
onClick={addCanvasNode(x, {
|
const mockEvent = mousePosition
|
||||||
nodeId,
|
? {
|
||||||
id,
|
clientX: mousePosition.x,
|
||||||
position,
|
clientY: mousePosition.y,
|
||||||
})}
|
}
|
||||||
onSelect={() => hideModal?.()}
|
: undefined;
|
||||||
>
|
|
||||||
<OperatorIcon name={x}></OperatorIcon>
|
const newNodeId = addCanvasNode(operator, contextData)(mockEvent);
|
||||||
{t(`flow.${lowerFirst(x)}`)}
|
|
||||||
</DropdownMenuItem>
|
if (onNodeCreated && newNodeId) {
|
||||||
</TooltipTrigger>
|
onNodeCreated(newNodeId);
|
||||||
<TooltipContent side="right">
|
}
|
||||||
<p>{t(`flow.${lowerFirst(x)}Description`)}</p>
|
|
||||||
</TooltipContent>
|
hideModal?.();
|
||||||
</Tooltip>
|
};
|
||||||
);
|
|
||||||
})}
|
const renderOperatorItem = (operator: Operator) => {
|
||||||
</ul>
|
const commonContent = (
|
||||||
);
|
<div className="hover:bg-background-card py-1 px-3 cursor-pointer rounded-sm flex gap-2 items-center justify-start">
|
||||||
|
<OperatorIcon name={operator} />
|
||||||
|
{t(`flow.${lowerFirst(operator)}`)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip key={operator}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
{isCustomDropdown ? (
|
||||||
|
<li onClick={() => handleClick(operator)}>{commonContent}</li>
|
||||||
|
) : (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={operator}
|
||||||
|
className="hover:bg-background-card py-1 px-3 cursor-pointer rounded-sm flex gap-2 items-center justify-start"
|
||||||
|
onClick={() => handleClick(operator)}
|
||||||
|
onSelect={() => hideModal?.()}
|
||||||
|
>
|
||||||
|
<OperatorIcon name={operator} />
|
||||||
|
{t(`flow.${lowerFirst(operator)}`)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="right">
|
||||||
|
<p>{t(`flow.${lowerFirst(operator)}Description`)}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ul className="space-y-2">{operators.map(renderOperatorItem)}</ul>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AccordionOperators() {
|
function AccordionOperators({
|
||||||
|
isCustomDropdown = false,
|
||||||
|
mousePosition,
|
||||||
|
}: {
|
||||||
|
isCustomDropdown?: boolean;
|
||||||
|
mousePosition?: { x: number; y: number };
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<Accordion
|
<Accordion
|
||||||
type="multiple"
|
type="multiple"
|
||||||
className="px-2 text-text-primary max-h-[45vh] overflow-auto"
|
className="px-2 text-text-title max-h-[45vh] overflow-auto"
|
||||||
defaultValue={['item-1', 'item-2', 'item-3', 'item-4', 'item-5']}
|
defaultValue={['item-1', 'item-2', 'item-3', 'item-4', 'item-5']}
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1">
|
<AccordionItem value="item-1">
|
||||||
@ -76,6 +132,8 @@ function AccordionOperators() {
|
|||||||
<AccordionContent className="flex flex-col gap-4 text-balance">
|
<AccordionContent className="flex flex-col gap-4 text-balance">
|
||||||
<OperatorItemList
|
<OperatorItemList
|
||||||
operators={[Operator.Agent, Operator.Retrieval]}
|
operators={[Operator.Agent, Operator.Retrieval]}
|
||||||
|
isCustomDropdown={isCustomDropdown}
|
||||||
|
mousePosition={mousePosition}
|
||||||
></OperatorItemList>
|
></OperatorItemList>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@ -84,6 +142,8 @@ function AccordionOperators() {
|
|||||||
<AccordionContent className="flex flex-col gap-4 text-balance">
|
<AccordionContent className="flex flex-col gap-4 text-balance">
|
||||||
<OperatorItemList
|
<OperatorItemList
|
||||||
operators={[Operator.Message, Operator.UserFillUp]}
|
operators={[Operator.Message, Operator.UserFillUp]}
|
||||||
|
isCustomDropdown={isCustomDropdown}
|
||||||
|
mousePosition={mousePosition}
|
||||||
></OperatorItemList>
|
></OperatorItemList>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@ -96,6 +156,8 @@ function AccordionOperators() {
|
|||||||
Operator.Iteration,
|
Operator.Iteration,
|
||||||
Operator.Categorize,
|
Operator.Categorize,
|
||||||
]}
|
]}
|
||||||
|
isCustomDropdown={isCustomDropdown}
|
||||||
|
mousePosition={mousePosition}
|
||||||
></OperatorItemList>
|
></OperatorItemList>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@ -106,6 +168,8 @@ function AccordionOperators() {
|
|||||||
<AccordionContent className="flex flex-col gap-4 text-balance">
|
<AccordionContent className="flex flex-col gap-4 text-balance">
|
||||||
<OperatorItemList
|
<OperatorItemList
|
||||||
operators={[Operator.Code, Operator.StringTransform]}
|
operators={[Operator.Code, Operator.StringTransform]}
|
||||||
|
isCustomDropdown={isCustomDropdown}
|
||||||
|
mousePosition={mousePosition}
|
||||||
></OperatorItemList>
|
></OperatorItemList>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@ -129,6 +193,8 @@ function AccordionOperators() {
|
|||||||
Operator.Invoke,
|
Operator.Invoke,
|
||||||
Operator.WenCai,
|
Operator.WenCai,
|
||||||
]}
|
]}
|
||||||
|
isCustomDropdown={isCustomDropdown}
|
||||||
|
mousePosition={mousePosition}
|
||||||
></OperatorItemList>
|
></OperatorItemList>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@ -139,9 +205,69 @@ function AccordionOperators() {
|
|||||||
export function InnerNextStepDropdown({
|
export function InnerNextStepDropdown({
|
||||||
children,
|
children,
|
||||||
hideModal,
|
hideModal,
|
||||||
}: PropsWithChildren & IModalProps<any>) {
|
position,
|
||||||
|
onNodeCreated,
|
||||||
|
}: PropsWithChildren &
|
||||||
|
IModalProps<any> & {
|
||||||
|
position?: { x: number; y: number };
|
||||||
|
onNodeCreated?: (newNodeId: string) => void;
|
||||||
|
}) {
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (position && hideModal) {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
hideModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [position, hideModal]);
|
||||||
|
|
||||||
|
if (position) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={dropdownRef}
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
left: position.x,
|
||||||
|
top: position.y + 10,
|
||||||
|
zIndex: 1000,
|
||||||
|
}}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="w-[300px] font-semibold bg-white border border-border rounded-md shadow-lg">
|
||||||
|
<div className="px-3 py-2 border-b border-border">
|
||||||
|
<div className="text-sm font-medium">Next Step</div>
|
||||||
|
</div>
|
||||||
|
<HideModalContext.Provider value={hideModal}>
|
||||||
|
<OnNodeCreatedContext.Provider value={onNodeCreated}>
|
||||||
|
<AccordionOperators
|
||||||
|
isCustomDropdown={true}
|
||||||
|
mousePosition={position}
|
||||||
|
></AccordionOperators>
|
||||||
|
</OnNodeCreatedContext.Provider>
|
||||||
|
</HideModalContext.Provider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu open onOpenChange={hideModal}>
|
<DropdownMenu
|
||||||
|
open={true}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open && hideModal) {
|
||||||
|
hideModal();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Handle, HandleProps } from '@xyflow/react';
|
|||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { HandleContext } from '../../context';
|
import { HandleContext } from '../../context';
|
||||||
|
import { useDropdownManager } from '../context';
|
||||||
import { InnerNextStepDropdown } from './dropdown/next-step-dropdown';
|
import { InnerNextStepDropdown } from './dropdown/next-step-dropdown';
|
||||||
|
|
||||||
export function CommonHandle({
|
export function CommonHandle({
|
||||||
@ -13,12 +14,16 @@ export function CommonHandle({
|
|||||||
}: HandleProps & { nodeId: string }) {
|
}: HandleProps & { nodeId: string }) {
|
||||||
const { visible, hideModal, showModal } = useSetModalState();
|
const { visible, hideModal, showModal } = useSetModalState();
|
||||||
|
|
||||||
|
const { canShowDropdown, setActiveDropdown, clearActiveDropdown } =
|
||||||
|
useDropdownManager();
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
nodeId,
|
nodeId,
|
||||||
id: props.id,
|
id: props.id || undefined,
|
||||||
type: props.type,
|
type: props.type,
|
||||||
position: props.position,
|
position: props.position,
|
||||||
|
isFromConnectionDrag: false,
|
||||||
}),
|
}),
|
||||||
[nodeId, props.id, props.position, props.type],
|
[nodeId, props.id, props.position, props.type],
|
||||||
);
|
);
|
||||||
@ -33,12 +38,23 @@ export function CommonHandle({
|
|||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!canShowDropdown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveDropdown('handle');
|
||||||
showModal();
|
showModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Plus className="size-3 pointer-events-none text-text-title-invert" />
|
<Plus className="size-3 pointer-events-none text-text-title-invert" />
|
||||||
{visible && (
|
{visible && (
|
||||||
<InnerNextStepDropdown hideModal={hideModal}>
|
<InnerNextStepDropdown
|
||||||
|
hideModal={() => {
|
||||||
|
hideModal();
|
||||||
|
clearActiveDropdown();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span></span>
|
<span></span>
|
||||||
</InnerNextStepDropdown>
|
</InnerNextStepDropdown>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export type HandleContextType = {
|
|||||||
id?: string;
|
id?: string;
|
||||||
type: HandleType;
|
type: HandleType;
|
||||||
position: Position;
|
position: Position;
|
||||||
|
isFromConnectionDrag: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HandleContext = createContext<HandleContextType>(
|
export const HandleContext = createContext<HandleContextType>(
|
||||||
|
|||||||
@ -163,9 +163,9 @@ export function PromptEditor({
|
|||||||
'absolute top-1 left-2 text-text-secondary pointer-events-none',
|
'absolute top-1 left-2 text-text-secondary pointer-events-none',
|
||||||
{
|
{
|
||||||
'truncate w-[90%]': !multiLine,
|
'truncate w-[90%]': !multiLine,
|
||||||
|
'translate-y-10': multiLine,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
data-xxx
|
|
||||||
>
|
>
|
||||||
{placeholder || t('common.promptPlaceholder')}
|
{placeholder || t('common.promptPlaceholder')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Collapse } from '@/components/collapse';
|
|||||||
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
|
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
|
||||||
import { FormContainer } from '@/components/form-container';
|
import { FormContainer } from '@/components/form-container';
|
||||||
import { KnowledgeBaseFormField } from '@/components/knowledge-base-item';
|
import { KnowledgeBaseFormField } from '@/components/knowledge-base-item';
|
||||||
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
import { RerankFormFields } from '@/components/rerank';
|
import { RerankFormFields } from '@/components/rerank';
|
||||||
import { SimilaritySliderFormField } from '@/components/similarity-slider';
|
import { SimilaritySliderFormField } from '@/components/similarity-slider';
|
||||||
import { TopNFormField } from '@/components/top-n-item';
|
import { TopNFormField } from '@/components/top-n-item';
|
||||||
@ -25,7 +26,7 @@ import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
|||||||
import { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { FormWrapper } from '../components/form-wrapper';
|
import { FormWrapper } from '../components/form-wrapper';
|
||||||
import { Output } from '../components/output';
|
import { Output } from '../components/output';
|
||||||
import { QueryVariable } from '../components/query-variable';
|
import { PromptEditor } from '../components/prompt-editor';
|
||||||
import { useValues } from './use-values';
|
import { useValues } from './use-values';
|
||||||
|
|
||||||
export const RetrievalPartialSchema = {
|
export const RetrievalPartialSchema = {
|
||||||
@ -74,6 +75,8 @@ export function EmptyResponseField() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function RetrievalForm({ node }: INextOperatorForm) {
|
function RetrievalForm({ node }: INextOperatorForm) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const outputList = useMemo(() => {
|
const outputList = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -96,7 +99,9 @@ function RetrievalForm({ node }: INextOperatorForm) {
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<FormWrapper>
|
<FormWrapper>
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<QueryVariable></QueryVariable>
|
<RAGFlowFormItem name="query" label={t('flow.query')}>
|
||||||
|
<PromptEditor></PromptEditor>
|
||||||
|
</RAGFlowFormItem>
|
||||||
<KnowledgeBaseFormField showVariable></KnowledgeBaseFormField>
|
<KnowledgeBaseFormField showVariable></KnowledgeBaseFormField>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
<Collapse title={<div>Advanced Settings</div>}>
|
<Collapse title={<div>Advanced Settings</div>}>
|
||||||
|
|||||||
@ -208,7 +208,7 @@ function useAddToolNode() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const addToolNode = useCallback(
|
const addToolNode = useCallback(
|
||||||
(newNode: Node<any>, nodeId?: string) => {
|
(newNode: Node<any>, nodeId?: string): boolean => {
|
||||||
const agentNode = getNode(nodeId);
|
const agentNode = getNode(nodeId);
|
||||||
|
|
||||||
if (agentNode) {
|
if (agentNode) {
|
||||||
@ -222,7 +222,7 @@ function useAddToolNode() {
|
|||||||
childToolNodeIds.length > 0 &&
|
childToolNodeIds.length > 0 &&
|
||||||
nodes.some((x) => x.id === childToolNodeIds[0])
|
nodes.some((x) => x.id === childToolNodeIds[0])
|
||||||
) {
|
) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
newNode.position = {
|
newNode.position = {
|
||||||
@ -239,7 +239,9 @@ function useAddToolNode() {
|
|||||||
targetHandle: NodeHandleId.End,
|
targetHandle: NodeHandleId.End,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
[addEdge, addNode, edges, getNode, nodes],
|
[addEdge, addNode, edges, getNode, nodes],
|
||||||
);
|
);
|
||||||
@ -295,13 +297,17 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
|
|||||||
const addCanvasNode = useCallback(
|
const addCanvasNode = useCallback(
|
||||||
(
|
(
|
||||||
type: string,
|
type: string,
|
||||||
params: { nodeId?: string; position: Position; id?: string } = {
|
params: {
|
||||||
|
nodeId?: string;
|
||||||
|
position: Position;
|
||||||
|
id?: string;
|
||||||
|
isFromConnectionDrag?: boolean;
|
||||||
|
} = {
|
||||||
position: Position.Right,
|
position: Position.Right,
|
||||||
},
|
},
|
||||||
) =>
|
) =>
|
||||||
(event?: CanvasMouseEvent) => {
|
(event?: CanvasMouseEvent): string | undefined => {
|
||||||
const nodeId = params.nodeId;
|
const nodeId = params.nodeId;
|
||||||
|
|
||||||
const node = getNode(nodeId);
|
const node = getNode(nodeId);
|
||||||
|
|
||||||
// reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition
|
// reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition
|
||||||
@ -312,7 +318,11 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
|
|||||||
y: event?.clientY || 0,
|
y: event?.clientY || 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (params.position === Position.Right && type !== Operator.Note) {
|
if (
|
||||||
|
params.position === Position.Right &&
|
||||||
|
type !== Operator.Note &&
|
||||||
|
!params.isFromConnectionDrag
|
||||||
|
) {
|
||||||
position = calculateNewlyBackChildPosition(nodeId, params.id);
|
position = calculateNewlyBackChildPosition(nodeId, params.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,6 +381,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
|
|||||||
targetHandle: NodeHandleId.End,
|
targetHandle: NodeHandleId.End,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return newNode.id;
|
||||||
} else if (
|
} else if (
|
||||||
type === Operator.Agent &&
|
type === Operator.Agent &&
|
||||||
params.position === Position.Bottom
|
params.position === Position.Bottom
|
||||||
@ -406,8 +417,10 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
|
|||||||
targetHandle: NodeHandleId.AgentTop,
|
targetHandle: NodeHandleId.AgentTop,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return newNode.id;
|
||||||
} else if (type === Operator.Tool) {
|
} else if (type === Operator.Tool) {
|
||||||
addToolNode(newNode, params.nodeId);
|
const toolNodeAdded = addToolNode(newNode, params.nodeId);
|
||||||
|
return toolNodeAdded ? newNode.id : undefined;
|
||||||
} else {
|
} else {
|
||||||
addNode(newNode);
|
addNode(newNode);
|
||||||
addChildEdge(params.position, {
|
addChildEdge(params.position, {
|
||||||
@ -416,6 +429,8 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
|
|||||||
sourceHandle: params.id,
|
sourceHandle: params.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newNode.id;
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
addChildEdge,
|
addChildEdge,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import { ComponentPropsWithoutRef, useCallback } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
import AgentCanvas from './canvas';
|
import AgentCanvas from './canvas';
|
||||||
|
import { DropdownProvider } from './canvas/context';
|
||||||
import EmbedDialog from './embed-dialog';
|
import EmbedDialog from './embed-dialog';
|
||||||
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
|
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
|
||||||
import { useFetchDataOnMount } from './hooks/use-fetch-data';
|
import { useFetchDataOnMount } from './hooks/use-fetch-data';
|
||||||
@ -185,10 +186,12 @@ export default function Agent() {
|
|||||||
</div>
|
</div>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<AgentCanvas
|
<DropdownProvider>
|
||||||
drawerVisible={chatDrawerVisible}
|
<AgentCanvas
|
||||||
hideDrawer={hideChatDrawer}
|
drawerVisible={chatDrawerVisible}
|
||||||
></AgentCanvas>
|
hideDrawer={hideChatDrawer}
|
||||||
|
></AgentCanvas>
|
||||||
|
</DropdownProvider>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
{fileUploadVisible && (
|
{fileUploadVisible && (
|
||||||
<UploadAgentDialog
|
<UploadAgentDialog
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
|
import { LargeModelFormFieldWithoutFilter } from '@/components/large-model-form-field';
|
||||||
|
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||||
import { NextMessageInput } from '@/components/message-input/next';
|
import { NextMessageInput } from '@/components/message-input/next';
|
||||||
import MessageItem from '@/components/message-item';
|
import MessageItem from '@/components/message-item';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Form } from '@/components/ui/form';
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip';
|
} from '@/components/ui/tooltip';
|
||||||
import { MessageType } from '@/constants/chat';
|
import { MessageType } from '@/constants/chat';
|
||||||
|
import { useScrollToBottom } from '@/hooks/logic-hooks';
|
||||||
import {
|
import {
|
||||||
useFetchConversation,
|
useFetchConversation,
|
||||||
useFetchDialog,
|
useFetchDialog,
|
||||||
@ -15,16 +19,20 @@ import {
|
|||||||
} from '@/hooks/use-chat-request';
|
} from '@/hooks/use-chat-request';
|
||||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { ListCheck, Plus, Trash2 } from 'lucide-react';
|
import { ListCheck, Plus, Trash2 } from 'lucide-react';
|
||||||
import { useCallback } from 'react';
|
import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
useGetSendButtonDisabled,
|
useGetSendButtonDisabled,
|
||||||
useSendButtonDisabled,
|
useSendButtonDisabled,
|
||||||
} from '../../hooks/use-button-disabled';
|
} from '../../hooks/use-button-disabled';
|
||||||
import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation';
|
import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation';
|
||||||
import { useSendMessage } from '../../hooks/use-send-chat-message';
|
import { useSendMessage } from '../../hooks/use-send-chat-message';
|
||||||
|
import { useSendMultipleChatMessage } from '../../hooks/use-send-multiple-message';
|
||||||
import { buildMessageItemReference } from '../../utils';
|
import { buildMessageItemReference } from '../../utils';
|
||||||
import { LLMSelectForm } from '../llm-select-form';
|
import { IMessage } from '../interface';
|
||||||
import { useAddChatBox } from '../use-add-box';
|
import { useAddChatBox } from '../use-add-box';
|
||||||
|
|
||||||
type MultipleChatBoxProps = {
|
type MultipleChatBoxProps = {
|
||||||
@ -35,31 +43,42 @@ type MultipleChatBoxProps = {
|
|||||||
'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type ChatCardProps = { id: string; idx: number } & Pick<
|
type ChatCardProps = {
|
||||||
|
id: string;
|
||||||
|
idx: number;
|
||||||
|
derivedMessages: IMessage[];
|
||||||
|
} & Pick<
|
||||||
MultipleChatBoxProps,
|
MultipleChatBoxProps,
|
||||||
'controller' | 'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
'controller' | 'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
function ChatCard({
|
const ChatCard = forwardRef(function ChatCard(
|
||||||
controller,
|
{
|
||||||
removeChatBox,
|
controller,
|
||||||
id,
|
removeChatBox,
|
||||||
idx,
|
id,
|
||||||
addChatBox,
|
idx,
|
||||||
chatBoxIds,
|
addChatBox,
|
||||||
}: ChatCardProps) {
|
chatBoxIds,
|
||||||
const {
|
|
||||||
value,
|
|
||||||
// scrollRef,
|
|
||||||
messageContainerRef,
|
|
||||||
sendLoading,
|
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
handleInputChange,
|
}: ChatCardProps,
|
||||||
handlePressEnter,
|
ref,
|
||||||
regenerateMessage,
|
) {
|
||||||
removeMessageById,
|
const { sendLoading, regenerateMessage, removeMessageById } =
|
||||||
stopOutputMessage,
|
useSendMessage(controller);
|
||||||
} = useSendMessage(controller);
|
|
||||||
|
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const { scrollRef } = useScrollToBottom(derivedMessages, messageContainerRef);
|
||||||
|
|
||||||
|
const FormSchema = z.object(LlmSettingSchema);
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
llm_id: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { data: currentDialog } = useFetchDialog();
|
const { data: currentDialog } = useFetchDialog();
|
||||||
@ -71,13 +90,19 @@ function ChatCard({
|
|||||||
removeChatBox(id);
|
removeChatBox(id);
|
||||||
}, [id, removeChatBox]);
|
}, [id, removeChatBox]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
getFormData: () => form.getValues(),
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-transparent border flex-1">
|
<Card className="bg-transparent border flex-1 flex flex-col">
|
||||||
<CardHeader className="border-b px-5 py-3">
|
<CardHeader className="border-b px-5 py-3">
|
||||||
<CardTitle className="flex justify-between items-center">
|
<CardTitle className="flex justify-between items-center">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-base">{idx + 1}</span>
|
<span className="text-base">{idx + 1}</span>
|
||||||
<LLMSelectForm></LLMSelectForm>
|
<Form {...form}>
|
||||||
|
<LargeModelFormFieldWithoutFilter></LargeModelFormFieldWithoutFilter>
|
||||||
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@ -102,8 +127,8 @@ function ChatCard({
|
|||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="flex-1 min-h-0">
|
||||||
<div ref={messageContainerRef} className="flex-1 overflow-auto min-h-0">
|
<div ref={messageContainerRef} className="h-full overflow-auto">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{derivedMessages?.map((message, i) => {
|
{derivedMessages?.map((message, i) => {
|
||||||
return (
|
return (
|
||||||
@ -134,12 +159,12 @@ function ChatCard({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{/* <div ref={scrollRef} /> */}
|
<div ref={scrollRef} />
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
export function MultipleChatBox({
|
export function MultipleChatBox({
|
||||||
controller,
|
controller,
|
||||||
@ -150,10 +175,12 @@ export function MultipleChatBox({
|
|||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
sendLoading,
|
sendLoading,
|
||||||
|
messageRecord,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
handlePressEnter,
|
handlePressEnter,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
} = useSendMessage(controller);
|
setFormRef,
|
||||||
|
} = useSendMultipleChatMessage(controller, chatBoxIds);
|
||||||
|
|
||||||
const { createConversationBeforeUploadDocument } =
|
const { createConversationBeforeUploadDocument } =
|
||||||
useCreateConversationBeforeUploadDocument();
|
useCreateConversationBeforeUploadDocument();
|
||||||
@ -163,7 +190,7 @@ export function MultipleChatBox({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="h-full flex flex-col px-5">
|
<section className="h-full flex flex-col px-5">
|
||||||
<div className="flex gap-4 flex-1 px-5 pb-14">
|
<div className="flex gap-4 flex-1 px-5 pb-14 min-h-0">
|
||||||
{chatBoxIds.map((id, idx) => (
|
{chatBoxIds.map((id, idx) => (
|
||||||
<ChatCard
|
<ChatCard
|
||||||
key={id}
|
key={id}
|
||||||
@ -173,6 +200,8 @@ export function MultipleChatBox({
|
|||||||
chatBoxIds={chatBoxIds}
|
chatBoxIds={chatBoxIds}
|
||||||
removeChatBox={removeChatBox}
|
removeChatBox={removeChatBox}
|
||||||
addChatBox={addChatBox}
|
addChatBox={addChatBox}
|
||||||
|
derivedMessages={messageRecord[id]}
|
||||||
|
ref={setFormRef(id)}
|
||||||
></ChatCard>
|
></ChatCard>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
48
web/src/pages/next-chats/hooks/use-build-form-refs.ts
Normal file
48
web/src/pages/next-chats/hooks/use-build-form-refs.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export function useBuildFormRefs(chatBoxIds: string[]) {
|
||||||
|
const formRefs = useRef<Record<string, { getFormData: () => any }>>({});
|
||||||
|
|
||||||
|
const setFormRef = (id: string) => (ref: { getFormData: () => any }) => {
|
||||||
|
formRefs.current[id] = ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanupFormRefs = useCallback(() => {
|
||||||
|
const currentIds = new Set(chatBoxIds);
|
||||||
|
Object.keys(formRefs.current).forEach((id) => {
|
||||||
|
if (!currentIds.has(id)) {
|
||||||
|
delete formRefs.current[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [chatBoxIds]);
|
||||||
|
|
||||||
|
const getLLMConfigById = useCallback(
|
||||||
|
(chatBoxId?: string) => {
|
||||||
|
const llmConfig = chatBoxId
|
||||||
|
? formRefs.current[chatBoxId].getFormData()
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return llmConfig;
|
||||||
|
},
|
||||||
|
[formRefs],
|
||||||
|
);
|
||||||
|
|
||||||
|
const isLLMConfigEmpty = useCallback(
|
||||||
|
(chatBoxId?: string) => {
|
||||||
|
return isEmpty(getLLMConfigById(chatBoxId)?.llm_id);
|
||||||
|
},
|
||||||
|
[getLLMConfigById],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cleanupFormRefs();
|
||||||
|
}, [cleanupFormRefs]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
formRefs,
|
||||||
|
setFormRef,
|
||||||
|
getLLMConfigById,
|
||||||
|
isLLMConfigEmpty,
|
||||||
|
};
|
||||||
|
}
|
||||||
235
web/src/pages/next-chats/hooks/use-send-multiple-message.ts
Normal file
235
web/src/pages/next-chats/hooks/use-send-multiple-message.ts
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import { MessageType } from '@/constants/chat';
|
||||||
|
import {
|
||||||
|
useHandleMessageInputChange,
|
||||||
|
useSendMessageWithSse,
|
||||||
|
} from '@/hooks/logic-hooks';
|
||||||
|
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
|
||||||
|
import { IAnswer, Message } from '@/interfaces/database/chat';
|
||||||
|
import api from '@/utils/api';
|
||||||
|
import { buildMessageUuid } from '@/utils/chat';
|
||||||
|
import { trim } from 'lodash';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { IMessage } from '../chat/interface';
|
||||||
|
import { useBuildFormRefs } from './use-build-form-refs';
|
||||||
|
|
||||||
|
export function useSendMultipleChatMessage(
|
||||||
|
controller: AbortController,
|
||||||
|
chatBoxIds: string[],
|
||||||
|
) {
|
||||||
|
const [messageRecord, setMessageRecord] = useState<
|
||||||
|
Record<string, IMessage[]>
|
||||||
|
>({});
|
||||||
|
|
||||||
|
const { conversationId } = useGetChatSearchParams();
|
||||||
|
|
||||||
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
|
const { send, answer, done } = useSendMessageWithSse(
|
||||||
|
api.completeConversation,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { setFormRef, getLLMConfigById, isLLMConfigEmpty } =
|
||||||
|
useBuildFormRefs(chatBoxIds);
|
||||||
|
|
||||||
|
const stopOutputMessage = useCallback(() => {
|
||||||
|
controller.abort();
|
||||||
|
}, [controller]);
|
||||||
|
|
||||||
|
const addNewestQuestion = useCallback(
|
||||||
|
(message: Message, answer: string = '') => {
|
||||||
|
setMessageRecord((pre) => {
|
||||||
|
const currentRecord = { ...pre };
|
||||||
|
const chatBoxId = message.chatBoxId;
|
||||||
|
if (typeof chatBoxId === 'string') {
|
||||||
|
const currentChatMessages = currentRecord[chatBoxId];
|
||||||
|
|
||||||
|
const nextChatMessages = [
|
||||||
|
...currentChatMessages,
|
||||||
|
{
|
||||||
|
...message,
|
||||||
|
id: buildMessageUuid(message), // The message id is generated on the front end,
|
||||||
|
// and the message id returned by the back end is the same as the question id,
|
||||||
|
// so that the pair of messages can be deleted together when deleting the message
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
content: answer,
|
||||||
|
id: buildMessageUuid({ ...message, role: MessageType.Assistant }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
currentRecord[chatBoxId] = nextChatMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRecord;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the streaming message to the last item in the message list
|
||||||
|
const addNewestAnswer = useCallback((answer: IAnswer) => {
|
||||||
|
setMessageRecord((pre) => {
|
||||||
|
const currentRecord = { ...pre };
|
||||||
|
const chatBoxId = answer.chatBoxId;
|
||||||
|
if (typeof chatBoxId === 'string') {
|
||||||
|
const currentChatMessages = currentRecord[chatBoxId];
|
||||||
|
|
||||||
|
const nextChatMessages = [
|
||||||
|
...(currentChatMessages?.slice(0, -1) ?? []),
|
||||||
|
{
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
content: answer.answer,
|
||||||
|
reference: answer.reference,
|
||||||
|
id: buildMessageUuid({
|
||||||
|
id: answer.id,
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
}),
|
||||||
|
prompt: answer.prompt,
|
||||||
|
audio_binary: answer.audio_binary,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
currentRecord[chatBoxId] = nextChatMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRecord;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const removeLatestMessage = useCallback((chatBoxId?: string) => {
|
||||||
|
setMessageRecord((pre) => {
|
||||||
|
const currentRecord = { ...pre };
|
||||||
|
if (chatBoxId) {
|
||||||
|
const currentChatMessages = currentRecord[chatBoxId];
|
||||||
|
if (currentChatMessages) {
|
||||||
|
currentRecord[chatBoxId] = currentChatMessages.slice(0, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentRecord;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const adjustRecordByChatBoxIds = useCallback(() => {
|
||||||
|
setMessageRecord((pre) => {
|
||||||
|
const currentRecord = { ...pre };
|
||||||
|
chatBoxIds.forEach((chatBoxId) => {
|
||||||
|
if (!currentRecord[chatBoxId]) {
|
||||||
|
currentRecord[chatBoxId] = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.keys(currentRecord).forEach((chatBoxId) => {
|
||||||
|
if (!chatBoxIds.includes(chatBoxId)) {
|
||||||
|
delete currentRecord[chatBoxId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return currentRecord;
|
||||||
|
});
|
||||||
|
}, [chatBoxIds, setMessageRecord]);
|
||||||
|
|
||||||
|
const sendMessage = useCallback(
|
||||||
|
async ({
|
||||||
|
message,
|
||||||
|
currentConversationId,
|
||||||
|
messages,
|
||||||
|
chatBoxId,
|
||||||
|
}: {
|
||||||
|
message: Message;
|
||||||
|
currentConversationId?: string;
|
||||||
|
chatBoxId: string;
|
||||||
|
messages?: Message[];
|
||||||
|
}) => {
|
||||||
|
let derivedMessages: IMessage[] = [];
|
||||||
|
|
||||||
|
derivedMessages = messageRecord[chatBoxId];
|
||||||
|
|
||||||
|
const res = await send(
|
||||||
|
{
|
||||||
|
chatBoxId,
|
||||||
|
conversation_id: currentConversationId ?? conversationId,
|
||||||
|
messages: [...(messages ?? derivedMessages ?? []), message],
|
||||||
|
...getLLMConfigById(chatBoxId),
|
||||||
|
},
|
||||||
|
controller,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res && (res?.response.status !== 200 || res?.data?.code !== 0)) {
|
||||||
|
// cancel loading
|
||||||
|
setValue(message.content);
|
||||||
|
console.info('removeLatestMessage111');
|
||||||
|
removeLatestMessage(chatBoxId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
send,
|
||||||
|
conversationId,
|
||||||
|
getLLMConfigById,
|
||||||
|
controller,
|
||||||
|
messageRecord,
|
||||||
|
setValue,
|
||||||
|
removeLatestMessage,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePressEnter = useCallback(() => {
|
||||||
|
if (trim(value) === '') return;
|
||||||
|
const id = uuid();
|
||||||
|
|
||||||
|
chatBoxIds.forEach((chatBoxId) => {
|
||||||
|
if (!isLLMConfigEmpty(chatBoxId)) {
|
||||||
|
addNewestQuestion({
|
||||||
|
content: value,
|
||||||
|
id,
|
||||||
|
role: MessageType.User,
|
||||||
|
chatBoxId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
// TODO:
|
||||||
|
setValue('');
|
||||||
|
chatBoxIds.forEach((chatBoxId) => {
|
||||||
|
if (!isLLMConfigEmpty(chatBoxId)) {
|
||||||
|
sendMessage({
|
||||||
|
message: {
|
||||||
|
id,
|
||||||
|
content: value.trim(),
|
||||||
|
role: MessageType.User,
|
||||||
|
},
|
||||||
|
chatBoxId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
value,
|
||||||
|
chatBoxIds,
|
||||||
|
done,
|
||||||
|
isLLMConfigEmpty,
|
||||||
|
addNewestQuestion,
|
||||||
|
setValue,
|
||||||
|
sendMessage,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (answer.answer && conversationId) {
|
||||||
|
addNewestAnswer(answer);
|
||||||
|
}
|
||||||
|
}, [answer, addNewestAnswer, conversationId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
adjustRecordByChatBoxIds();
|
||||||
|
}, [adjustRecordByChatBoxIds]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
messageRecord,
|
||||||
|
sendMessage,
|
||||||
|
handleInputChange,
|
||||||
|
handlePressEnter,
|
||||||
|
stopOutputMessage,
|
||||||
|
sendLoading: false,
|
||||||
|
setFormRef,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user