Feat plugin init (#10)

* chore: Move directory 'workflows' into init_data.

* feat: Initialize plugin.

* chore: Deepseek plugin.

* chore: Update entrypoint.sh

* fix: Check if plugin already installed.

* chore: Update entrypoint.sh

* chore: Update entrypoint.sh
pull/17608/head
MisluNotFound 1 year ago committed by GitHub
parent 3187c887b8
commit 69928086c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -19,9 +19,10 @@ FILES_URL=http://127.0.0.1:5001
# APO Config # APO Config
APO_BACKEND_URL=http://127.0.0.1:8080 APO_BACKEND_URL=http://127.0.0.1:8080
APO_VM_URL=http://127.0.0.1:8080 APO_VM_URL=http://127.0.0.1:8428
WORKFLOW_DIR=./workflows WORKFLOW_DIR=./inti_data/workflows
INITIAL_LANGUAGE=en-US INITIAL_LANGUAGE=en-US
OFFLINE_MODE=false
# The time in seconds after the signature is rejected # The time in seconds after the signature is rejected
FILES_ACCESS_TIMEOUT=300 FILES_ACCESS_TIMEOUT=300

@ -21,5 +21,9 @@ class APOConfig(BaseSettings):
) )
WORKFLOW_DIR: str = Field( WORKFLOW_DIR: str = Field(
description="Directory of workflows yaml file.", description="Directory of workflows yaml file.",
default="./workflows" default="./init_data/workflows"
)
OFFLINE_MODE: bool = Field(
description="Offline mode",
default=False
) )

@ -2,6 +2,12 @@
set -e set -e
if [[ "${MODE}" == "copy" ]]; then
mkdir -p /app/storage
cp -rf /app/api/init_data/plugins/storage/* /app/storage/
exit 0
fi
if [[ "${MIGRATION_ENABLED}" == "true" ]]; then if [[ "${MIGRATION_ENABLED}" == "true" ]]; then
echo "Running migrations" echo "Running migrations"
flask upgrade-db flask upgrade-db

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="128" height="128" viewBox="0 0 128 128"><g><g style="opacity:0;"><rect x="0" y="0" width="128" height="128" rx="0" fill="#FFFFFF" fill-opacity="1"/></g><g><path d="M100.74,12L93.2335,12C69.21260000000001,12,55.3672,27.3468,55.3672,50.8672L55.3672,54.8988C52.6011,54.1056,49.7377,53.7031,46.8601,53.7031C29.816499999999998,53.7031,16,67.5196,16,84.5632C16,101.6069,29.816499999999998,115.423,46.8601,115.423C63.9037,115.423,77.72030000000001,101.6069,77.72030000000001,84.5632C77.72030000000001,82.4902,77.51140000000001,80.4223,77.0967,78.3911L77.2197,78.3911L100.74,78.3911C106.9654,78.3681,112,73.3151,112,67.08959999999999C112,60.8642,106.9654,55.8111,100.74,55.7882L100.7362,55.7882L100.6985,55.7879L100.6606,55.7882L77.2197,55.7882L77.2195,49.8663C77.2195,40.8584,83.7252,34.352900000000005,93.2335,34.352900000000005L100.5653,34.352900000000005L100.5733,34.352900000000005L100.5812,34.352900000000005L100.74,34.352900000000005L100.74,34.352900000000005C106.8469,34.2605,111.7497,29.284,111.7497,23.1764C111.7497,17.06889,106.8469,12.0923454,100.74,12L100.74,12ZM56.0347,84.5632C56.0347,79.4962,51.9271,75.3885,46.8601,75.3885C41.793099999999995,75.3885,37.6854,79.4962,37.6854,84.5632C37.6854,89.6303,41.793099999999995,93.7378,46.8601,93.7378C51.9271,93.7378,56.0347,89.6303,56.0347,84.5632Z" fill-rule="evenodd" fill="#8358F6" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,22 @@
<svg width="195.000000" height="41.359375" viewBox="0 0 195 41.3594" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip30_2029">
<rect id="_图层_1" width="134.577469" height="25.511124" transform="translate(60.422485 10.022217)" fill="white"/>
</clipPath>
</defs>
<g clip-path="url(#clip30_2029)">
<path id="path" d="M119.508 30.113L117.562 30.113L117.562 27.0967L119.508 27.0967C120.713 27.0967 121.931 26.7961 122.715 25.9614C123.5 25.1265 123.796 23.8464 123.796 22.5664C123.796 21.2864 123.512 20.0063 122.715 19.1716C121.919 18.3369 120.713 18.0364 119.508 18.0364C118.302 18.0364 117.085 18.3369 116.3 19.1716C115.515 20.0063 115.219 21.2864 115.219 22.5664L115.219 34.9551L111.806 34.9551L111.806 15.031L115.219 15.031L115.219 16.2998L115.845 16.2998C115.913 16.2219 115.981 16.1553 116.049 16.0884C116.903 15.3093 118.211 15.031 119.496 15.031C121.51 15.031 123.523 15.532 124.843 16.9233C126.162 18.3145 126.629 20.4517 126.629 22.5776C126.629 24.7036 126.151 26.8296 124.843 28.2319C123.535 29.6345 121.51 30.113 119.508 30.113Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M67.5664 15.5654L69.5117 15.5654L69.5117 18.5818L67.5664 18.5818C66.3606 18.5818 65.1434 18.8823 64.3585 19.717C63.5736 20.552 63.2778 21.832 63.2778 23.1121C63.2778 24.3921 63.5623 25.6721 64.3585 26.5068C65.1548 27.3418 66.3606 27.6423 67.5664 27.6423C68.7722 27.6423 69.9895 27.3418 70.7744 26.5068C71.5593 25.6721 71.8551 24.3921 71.8551 23.1121L71.8551 10.7124L75.2677 10.7124L75.2677 30.6475L71.8551 30.6475L71.8551 29.3787L71.2294 29.3787C71.1611 29.4565 71.0929 29.5234 71.0247 29.5901C70.1715 30.3691 68.8633 30.6475 67.5779 30.6475C65.5643 30.6475 63.5509 30.1467 62.2313 28.7554C60.9117 27.364 60.4453 25.2268 60.4453 23.1008C60.4453 20.9749 60.9231 18.8489 62.2313 17.4465C63.5509 16.0552 65.5643 15.5654 67.5664 15.5654Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M92.3881 22.845L92.3881 24.0581L83.299 24.0581L83.299 21.6428L89.328 21.6428C89.1914 20.7634 88.8729 19.9397 88.3042 19.3386C87.4851 18.4705 86.2224 18.1589 84.9711 18.1589C83.7198 18.1589 82.4572 18.4705 81.6381 19.3386C80.819 20.2068 80.5232 21.5315 80.5232 22.845C80.5232 24.1582 80.819 25.4939 81.6381 26.3511C82.4572 27.208 83.7198 27.531 84.9711 27.531C86.2224 27.531 87.4851 27.2192 88.3042 26.3511C88.418 26.2285 88.5203 26.095 88.6227 25.9614L91.9899 25.9614C91.6941 27.0078 91.2277 27.9539 90.5225 28.6885C89.1573 30.1243 87.0529 30.6475 84.9711 30.6475C82.8894 30.6475 80.7849 30.1355 79.4198 28.6885C78.0547 27.2415 77.5542 25.0376 77.5542 22.845C77.5542 20.6521 78.0433 18.437 79.4198 17.0012C80.7963 15.5654 82.8894 15.0422 84.9711 15.0422C87.0529 15.0422 89.1573 15.5542 90.5225 17.0012C91.8988 18.4482 92.3881 20.6521 92.3881 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M109.52 22.845L109.52 24.0581L100.431 24.0581L100.431 21.6428L106.46 21.6428C106.323 20.7634 106.005 19.9397 105.436 19.3386C104.617 18.4705 103.354 18.1589 102.103 18.1589C100.852 18.1589 99.5889 18.4705 98.7698 19.3386C97.9507 20.2068 97.6549 21.5315 97.6549 22.845C97.6549 24.1582 97.9507 25.4939 98.7698 26.3511C99.5889 27.208 100.852 27.531 102.103 27.531C103.354 27.531 104.617 27.2192 105.436 26.3511C105.55 26.2285 105.652 26.095 105.754 25.9614L109.122 25.9614C108.826 27.0078 108.359 27.9539 107.654 28.6885C106.289 30.1243 104.185 30.6475 102.103 30.6475C100.021 30.6475 97.9166 30.1355 96.5515 28.6885C95.1864 27.2415 94.6859 25.0376 94.6859 22.845C94.6859 20.6521 95.175 18.437 96.5515 17.0012C97.928 15.5654 100.021 15.0422 102.103 15.0422C104.185 15.0422 106.289 15.5542 107.654 17.0012C109.031 18.4482 109.52 20.6521 109.52 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M136.355 30.6475C138.437 30.6475 140.541 30.3469 141.906 29.49C143.271 28.6328 143.772 27.3306 143.772 26.0393C143.772 24.7483 143.282 23.4348 141.906 22.5889C140.541 21.7429 138.437 21.4312 136.355 21.4312C135.467 21.4312 134.648 21.3088 134.068 20.9861C133.488 20.6521 133.272 20.1511 133.272 19.6504C133.272 19.1494 133.477 18.6375 134.068 18.3147C134.648 17.9807 135.547 17.8694 136.434 17.8694C137.322 17.8694 138.22 17.9919 138.801 18.3147C139.381 18.6487 139.597 19.1494 139.597 19.6504L143.066 19.6504C143.066 18.3591 142.623 17.0457 141.383 16.2C140.143 15.354 138.243 15.0422 136.355 15.0422C134.466 15.0422 132.567 15.3428 131.327 16.2C130.087 17.0569 129.643 18.3591 129.643 19.6504C129.643 20.9414 130.087 22.2549 131.327 23.1008C132.567 23.9468 134.466 24.2585 136.355 24.2585C137.333 24.2585 138.414 24.3809 139.062 24.7036C139.711 25.0266 139.938 25.5386 139.938 26.0393C139.938 26.5403 139.711 27.0522 139.062 27.375C138.414 27.6978 137.424 27.8203 136.446 27.8203C135.467 27.8203 134.466 27.6978 133.829 27.375C133.192 27.0522 132.953 26.5403 132.953 26.0393L128.949 26.0393C128.949 27.3306 129.438 28.644 130.815 29.49C132.191 30.3359 134.273 30.6475 136.355 30.6475Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M160.903 22.845L160.903 24.0581L151.814 24.0581L151.814 21.6428L157.843 21.6428C157.707 20.7634 157.388 19.9397 156.82 19.3386C156 18.4705 154.738 18.1589 153.486 18.1589C152.235 18.1589 150.972 18.4705 150.153 19.3386C149.334 20.2068 149.039 21.5315 149.039 22.845C149.039 24.1582 149.334 25.4939 150.153 26.3511C150.972 27.208 152.235 27.531 153.486 27.531C154.738 27.531 156 27.2192 156.82 26.3511C156.933 26.2285 157.036 26.095 157.138 25.9614L160.505 25.9614C160.209 27.0078 159.743 27.9539 159.038 28.6885C157.673 30.1243 155.568 30.6475 153.486 30.6475C151.405 30.6475 149.3 30.1355 147.935 28.6885C146.57 27.2415 146.07 25.0376 146.07 22.845C146.07 20.6521 146.559 18.437 147.935 17.0012C149.312 15.5654 151.405 15.0422 153.486 15.0422C155.568 15.0422 157.673 15.5542 159.038 17.0012C160.414 18.4482 160.903 20.6521 160.903 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M178.035 22.845L178.035 24.0581L168.946 24.0581L168.946 21.6428L174.975 21.6428C174.839 20.7634 174.52 19.9397 173.951 19.3386C173.132 18.4705 171.87 18.1589 170.618 18.1589C169.367 18.1589 168.104 18.4705 167.285 19.3386C166.466 20.2068 166.17 21.5315 166.17 22.845C166.17 24.1582 166.466 25.4939 167.285 26.3511C168.104 27.208 169.367 27.531 170.618 27.531C171.87 27.531 173.132 27.2192 173.951 26.3511C174.065 26.2285 174.167 26.095 174.27 25.9614L177.637 25.9614C177.341 27.0078 176.875 27.9539 176.17 28.6885C174.804 30.1243 172.7 30.6475 170.618 30.6475C168.536 30.6475 166.432 30.1355 165.067 28.6885C163.702 27.2415 163.201 25.0376 163.201 22.845C163.201 20.6521 163.69 18.437 165.067 17.0012C166.443 15.5654 168.536 15.0422 170.618 15.0422C172.7 15.0422 174.804 15.5542 176.17 17.0012C177.546 18.4482 178.035 20.6521 178.035 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<rect id="rect" x="180.321533" y="10.022217" width="3.412687" height="20.625223" fill="#4D6BFE"/>
<path id="polygon" d="M189.559 22.3772L195.155 30.6475L190.935 30.6475L185.338 22.3772L190.935 15.7322L195.155 15.7322L189.559 22.3772Z" fill-rule="nonzero" fill="#4D6BFE"/>
</g>
<path id="path" d="M55.6128 3.47119C55.0175 3.17944 54.7611 3.73535 54.413 4.01782C54.2939 4.10889 54.1932 4.22729 54.0924 4.33667C53.2223 5.26587 52.2057 5.87646 50.8776 5.80347C48.9359 5.69409 47.2781 6.30469 45.8126 7.78979C45.5012 5.9585 44.4663 4.86499 42.8909 4.16357C42.0667 3.79907 41.2332 3.43457 40.6561 2.64185C40.2532 2.07715 40.1432 1.44849 39.9418 0.828857C39.8135 0.455322 39.6853 0.0725098 39.2548 0.00878906C38.7877 -0.0639648 38.6045 0.327637 38.4213 0.655762C37.6886 1.99512 37.4047 3.47119 37.4321 4.96533C37.4962 8.32739 38.9159 11.0059 41.7369 12.9102C42.0575 13.1289 42.1399 13.3474 42.0392 13.6665C41.8468 14.3225 41.6178 14.9602 41.4164 15.6162C41.2881 16.0354 41.0957 16.1265 40.647 15.9441C39.0991 15.2974 37.7618 14.3406 36.5803 13.1836C34.5745 11.2429 32.761 9.10181 30.4988 7.42529C29.9675 7.03345 29.4363 6.66919 28.8867 6.32275C26.5786 4.08154 29.189 2.24097 29.7935 2.02246C30.4254 1.79468 30.0133 1.01099 27.9708 1.02026C25.9283 1.0293 24.0599 1.71265 21.6786 2.62378C21.3306 2.7605 20.9641 2.8606 20.5886 2.94263C18.4271 2.53271 16.1831 2.44141 13.8384 2.70581C9.42371 3.19775 5.89758 5.28418 3.30554 8.84668C0.191406 13.1289 -0.54126 17.9941 0.356323 23.0691C1.29968 28.4172 4.02905 32.8452 8.22388 36.3076C12.5745 39.8972 17.5845 41.6558 23.2997 41.3186C26.771 41.1182 30.6361 40.6536 34.9958 36.9636C36.0948 37.5103 37.2489 37.7288 39.1632 37.8928C40.6378 38.0295 42.0575 37.8201 43.1565 37.5923C44.8784 37.2278 44.7594 35.6333 44.1366 35.3418C39.09 32.9912 40.1981 33.9478 39.1907 33.1733C41.7552 30.1394 45.6204 26.9868 47.1316 16.7732C47.2506 15.9624 47.1499 15.4521 47.1316 14.7961C47.1224 14.3953 47.214 14.2405 47.672 14.1948C48.9359 14.0491 50.1632 13.7029 51.2898 13.0833C54.5596 11.2976 55.8784 8.36377 56.1898 4.84692C56.2357 4.30933 56.1807 3.75342 55.6128 3.47119ZM27.119 35.123C22.2281 31.2783 19.856 30.0117 18.8759 30.0664C17.96 30.1211 18.1249 31.1689 18.3263 31.8523C18.537 32.5264 18.8118 32.9912 19.1964 33.5833C19.462 33.9751 19.6453 34.5581 18.9309 34.9956C17.3555 35.9705 14.6169 34.6675 14.4886 34.6038C11.3014 32.7268 8.63611 30.2485 6.75842 26.8594C4.94495 23.5974 3.89172 20.0989 3.71765 16.3633C3.67188 15.4614 3.9375 15.1423 4.83508 14.9785C6.0166 14.7598 7.23474 14.7141 8.41626 14.8872C13.408 15.6162 17.6577 17.8484 21.2206 21.3835C23.2539 23.397 24.7926 25.8025 26.3772 28.1531C28.0624 30.6494 29.8759 33.0276 32.184 34.9773C32.9991 35.6606 33.6494 36.1799 34.2722 36.5627C32.3947 36.7722 29.2622 36.8179 27.119 35.123ZM29.4637 20.0442C29.4637 19.6433 29.7843 19.3245 30.1874 19.3245C30.2789 19.3245 30.3613 19.3425 30.4346 19.3699C30.5354 19.4065 30.627 19.4612 30.7002 19.543C30.8285 19.6707 30.9017 19.8528 30.9017 20.0442C30.9017 20.4451 30.5812 20.7639 30.1782 20.7639C29.7751 20.7639 29.4637 20.4451 29.4637 20.0442ZM36.7452 23.7798C36.2781 23.9712 35.811 24.135 35.3622 24.1533C34.6661 24.1897 33.9059 23.9072 33.4938 23.561C32.8527 23.0234 32.3947 22.7229 32.2023 21.7844C32.1199 21.3835 32.1656 20.7639 32.239 20.4087C32.4038 19.6433 32.2206 19.1514 31.6803 18.7048C31.2406 18.3403 30.6819 18.2402 30.0682 18.2402C29.8392 18.2402 29.6287 18.1399 29.4729 18.0579C29.2164 17.9304 29.0059 17.6116 29.2073 17.2197C29.2714 17.0923 29.5829 16.7825 29.6561 16.7278C30.4896 16.2539 31.4513 16.4089 32.3397 16.7642C33.1641 17.1013 33.7869 17.7209 34.6844 18.5955C35.6003 19.6523 35.7651 19.9441 36.2872 20.7366C36.6995 21.3562 37.075 21.9939 37.3314 22.7229C37.4871 23.1785 37.2856 23.552 36.7452 23.7798Z" fill-rule="nonzero" fill="#4D6BFE"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,3 @@
<svg width="60" height="50" viewBox="0 0 60 50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path id="path" d="M55.613,3.471C55.018,3.179 54.761,3.735 54.413,4.018C54.294,4.109 54.193,4.227 54.092,4.337C53.222,5.266 52.206,5.876 50.878,5.803C48.936,5.694 47.278,6.305 45.813,7.79C45.501,5.959 44.466,4.865 42.891,4.164C42.067,3.799 41.233,3.435 40.656,2.642C40.253,2.077 40.143,1.448 39.942,0.829C39.814,0.455 39.685,0.073 39.255,0.009C38.788,-0.064 38.605,0.328 38.421,0.656C37.689,1.995 37.405,3.471 37.432,4.965C37.496,8.327 38.916,11.006 41.737,12.91C42.058,13.129 42.14,13.347 42.039,13.666C41.847,14.323 41.618,14.96 41.416,15.616C41.288,16.035 41.096,16.127 40.647,15.944C39.099,15.297 37.762,14.341 36.58,13.184C34.575,11.243 32.761,9.102 30.499,7.425C29.968,7.033 29.436,6.669 28.887,6.323C26.579,4.082 29.189,2.241 29.794,2.022C30.425,1.795 30.013,1.011 27.971,1.02C25.928,1.029 24.06,1.713 21.679,2.624C21.331,2.761 20.964,2.861 20.589,2.943C18.427,2.533 16.183,2.441 13.838,2.706C9.424,3.198 5.898,5.284 3.306,8.847C0.191,13.129 -0.541,17.994 0.356,23.069C1.3,28.417 4.029,32.845 8.224,36.308C12.575,39.897 17.584,41.656 23.3,41.319C26.771,41.118 30.636,40.654 34.996,36.964C36.095,37.51 37.249,37.729 39.163,37.893C40.638,38.03 42.058,37.82 43.157,37.592C44.878,37.228 44.759,35.633 44.137,35.342C39.09,32.991 40.198,33.948 39.191,33.173C41.755,30.139 45.62,26.987 47.132,16.773C47.251,15.962 47.15,15.452 47.132,14.796C47.122,14.395 47.214,14.241 47.672,14.195C48.936,14.049 50.163,13.703 51.29,13.083C54.56,11.298 55.878,8.364 56.19,4.847C56.236,4.309 56.181,3.753 55.613,3.471ZM27.119,35.123C22.228,31.278 19.856,30.012 18.876,30.066C17.96,30.121 18.125,31.169 18.326,31.852C18.537,32.526 18.812,32.991 19.196,33.583C19.462,33.975 19.645,34.558 18.931,34.996C17.356,35.971 14.617,34.667 14.489,34.604C11.301,32.727 8.636,30.249 6.758,26.859C4.945,23.597 3.892,20.099 3.718,16.363C3.672,15.461 3.938,15.142 4.835,14.979C6.017,14.76 7.235,14.714 8.416,14.887C13.408,15.616 17.658,17.848 21.221,21.384C23.254,23.397 24.793,25.802 26.377,28.153C28.062,30.649 29.876,33.028 32.184,34.977C32.999,35.661 33.649,36.18 34.272,36.563C32.395,36.772 29.262,36.818 27.119,35.123ZM29.464,20.044C29.464,19.643 29.784,19.325 30.187,19.325C30.279,19.325 30.361,19.343 30.435,19.37C30.535,19.407 30.627,19.461 30.7,19.543C30.828,19.671 30.902,19.853 30.902,20.044C30.902,20.445 30.581,20.764 30.178,20.764C29.775,20.764 29.464,20.445 29.464,20.044ZM36.745,23.78C36.278,23.971 35.811,24.135 35.362,24.153C34.666,24.19 33.906,23.907 33.494,23.561C32.853,23.023 32.395,22.723 32.202,21.784C32.12,21.384 32.166,20.764 32.239,20.409C32.404,19.643 32.221,19.151 31.68,18.705C31.241,18.34 30.682,18.24 30.068,18.24C29.839,18.24 29.629,18.14 29.473,18.058C29.216,17.93 29.006,17.612 29.207,17.22C29.271,17.092 29.583,16.783 29.656,16.728C30.49,16.254 31.451,16.409 32.34,16.764C33.164,17.101 33.787,17.721 34.684,18.596C35.6,19.652 35.765,19.944 36.287,20.737C36.7,21.356 37.075,21.994 37.331,22.723C37.487,23.179 37.286,23.552 36.745,23.78Z" style="fill:rgb(77,107,254);fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

@ -0,0 +1 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" data-icon="CubeOutline" aria-hidden="true"><g id="cube-outline"><g id="Solid"><path d="M8.26865 1.29003C8.09143 1.25358 7.90866 1.25358 7.73144 1.29003C7.52659 1.33216 7.3435 1.43471 7.19794 1.51624L7.15826 1.53841L6.17628 2.08395C5.85443 2.26276 5.73846 2.66863 5.91727 2.99049C6.09608 3.31234 6.50195 3.4283 6.82381 3.24949L7.80579 2.70395C7.90681 2.64782 7.95839 2.61946 7.99686 2.60091L8.00004 2.59938L8.00323 2.60091C8.0417 2.61946 8.09327 2.64782 8.1943 2.70395L9.17628 3.24949C9.49814 3.4283 9.90401 3.31234 10.0828 2.99048C10.2616 2.66863 10.1457 2.26276 9.82381 2.08395L8.84183 1.53841L8.80215 1.51624C8.65659 1.43471 8.4735 1.33216 8.26865 1.29003Z" fill="currentColor"></path><path d="M12.8238 3.75062C12.5019 3.57181 12.0961 3.68777 11.9173 4.00963C11.7385 4.33148 11.8544 4.73735 12.1763 4.91616L12.6272 5.16668L12.1763 5.41719C11.8545 5.596 11.7385 6.00186 11.9173 6.32372C12.0961 6.64558 12.502 6.76154 12.8238 6.58273L13.3334 6.29966V6.83339C13.3334 7.20158 13.6319 7.50006 14 7.50006C14.3682 7.50006 14.6667 7.20158 14.6667 6.83339V5.79435L14.6668 5.74627C14.6673 5.62441 14.6678 5.48084 14.6452 5.33482C14.6869 5.17472 14.6696 4.99892 14.5829 4.84286C14.4904 4.6764 14.3371 4.56501 14.1662 4.52099C14.0496 4.43038 13.9239 4.36116 13.8173 4.3024L13.7752 4.27915L12.8238 3.75062Z" fill="currentColor"></path><path d="M3.8238 4.91616C4.14566 4.73735 4.26162 4.33148 4.08281 4.00963C3.90401 3.68777 3.49814 3.57181 3.17628 3.75062L2.22493 4.27915L2.18284 4.3024C2.07615 4.36116 1.95045 4.4304 1.83382 4.52102C1.66295 4.56506 1.50977 4.67643 1.41731 4.84286C1.33065 4.99886 1.31323 5.17459 1.35493 5.33464C1.33229 5.48072 1.33281 5.62436 1.33326 5.74627L1.33338 5.79435V6.83339C1.33338 7.20158 1.63185 7.50006 2.00004 7.50006C2.36823 7.50006 2.66671 7.20158 2.66671 6.83339V6.29961L3.17632 6.58273C3.49817 6.76154 3.90404 6.64558 4.08285 6.32372C4.26166 6.00186 4.1457 5.596 3.82384 5.41719L3.3729 5.16666L3.8238 4.91616Z" fill="currentColor"></path><path d="M2.66671 10.1667C2.66671 9.79853 2.36823 9.50006 2.00004 9.50006C1.63185 9.50006 1.33338 9.79853 1.33338 10.1667V11.2058L1.33326 11.2538C1.33262 11.4298 1.33181 11.6509 1.40069 11.8594C1.46024 12.0397 1.55759 12.2051 1.68622 12.3447C1.835 12.5061 2.02873 12.6128 2.18281 12.6977L2.22493 12.721L3.17628 13.2495C3.49814 13.4283 3.90401 13.3123 4.08281 12.9905C4.26162 12.6686 4.14566 12.2628 3.8238 12.084L2.87245 11.5554C2.76582 11.4962 2.71137 11.4656 2.67318 11.4413L2.66995 11.4392L2.66971 11.4354C2.66699 11.3902 2.66671 11.3277 2.66671 11.2058V10.1667Z" fill="currentColor"></path><path d="M14.6667 10.1667C14.6667 9.79853 14.3682 9.50006 14 9.50006C13.6319 9.50006 13.3334 9.79853 13.3334 10.1667V11.2058C13.3334 11.3277 13.3331 11.3902 13.3304 11.4354L13.3301 11.4392L13.3269 11.4413C13.2887 11.4656 13.2343 11.4962 13.1276 11.5554L12.1763 12.084C11.8544 12.2628 11.7385 12.6686 11.9173 12.9905C12.0961 13.3123 12.5019 13.4283 12.8238 13.2495L13.7752 12.721L13.8172 12.6977C13.9713 12.6128 14.1651 12.5061 14.3139 12.3447C14.4425 12.2051 14.5398 12.0397 14.5994 11.8594C14.6683 11.6509 14.6675 11.4298 14.6668 11.2538L14.6667 11.2058V10.1667Z" fill="currentColor"></path><path d="M6.82381 13.7506C6.50195 13.5718 6.09608 13.6878 5.91727 14.0096C5.73846 14.3315 5.85443 14.7374 6.17628 14.9162L7.15826 15.4617L7.19793 15.4839C7.29819 15.54 7.41625 15.6061 7.54696 15.6556C7.66589 15.7659 7.82512 15.8333 8.00008 15.8333C8.17507 15.8333 8.33431 15.7659 8.45324 15.6556C8.58391 15.6061 8.70193 15.54 8.80215 15.4839L8.84183 15.4617L9.82381 14.9162C10.1457 14.7374 10.2616 14.3315 10.0828 14.0096C9.90401 13.6878 9.49814 13.5718 9.17628 13.7506L8.66675 14.0337V13.5C8.66675 13.1318 8.36827 12.8333 8.00008 12.8333C7.63189 12.8333 7.33341 13.1318 7.33341 13.5V14.0337L6.82381 13.7506Z" fill="currentColor"></path><path d="M6.82384 7.08385C6.50199 6.90505 6.09612 7.02101 5.91731 7.34286C5.7385 7.66472 5.85446 8.07059 6.17632 8.2494L7.33341 8.89223V10.1666C7.33341 10.5348 7.63189 10.8333 8.00008 10.8333C8.36827 10.8333 8.66675 10.5348 8.66675 10.1666V8.89223L9.82384 8.2494C10.1457 8.07059 10.2617 7.66472 10.0829 7.34286C9.90404 7.02101 9.49817 6.90505 9.17632 7.08385L8.00008 7.73732L6.82384 7.08385Z" fill="currentColor"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,4 @@
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=ae1aa1c9-0af4-43db-b6d4-4fa9e6bfb646

@ -0,0 +1,24 @@
# Overview
The Agent node in Dify Chatflow/Workflow lets LLMs autonomously use tools. This plugin features two official Dify Agent reasoning strategies, enabling LLMs to dynamically select and run tools during runtime for multi-step problem-solving.
## Strategies
### 1. Function Calling
Function Calling maps user commands to specific functions or tools. The LLM identifies the user's intent, decides which function to call, and extracts the required parameters. It is a straightforward mechanism for invoking external capabilities.
![](./_assets/function_calling.png)
#### Pros:
- **Precise:** Directly calls the right tool for defined tasks, avoiding complex reasoning.
- **Easy External Integration:** Integrates external APIs and tools as callable functions.
- **Structured Output:** Provides structured function call information for easy processing.
### 2. ReAct (Reason + Act)
ReAct alternates between the LLM reasoning about the situation and taking actions. The LLM analyzes the current state and goal, selects and uses a tool, and then uses the tool's output for the next thought and action. This cycle repeats until the problem is resolved.
![](./_assets/react.png)
#### Pros:
- **Leverages External Information:** Effectively uses external tools to gather information for tasks the model cannot handle alone.
- **Explainable Reasoning:** Interwoven reasoning and action steps allow some tracking of the Agent's process.
- **Wide Applicability:** Suitable for tasks requiring external knowledge or specific actions, such as Q&A, information retrieval, and task execution.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

@ -0,0 +1,10 @@
import sys
sys.path.append("../..")
from dify_plugin import DifyPluginEnv, Plugin
plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=240))
if __name__ == "__main__":
plugin.run()

@ -0,0 +1,34 @@
version: 0.0.11
type: plugin
author: "langgenius"
name: "agent"
label:
en_US: "Dify Agent Strategies"
zh_Hans: "Dify Agent 策略"
created_at: "2024-07-12T08:03:44.658609186Z"
icon: icon.svg
description:
en_US: Dify official Agent strategies collection
zh_Hans: Dify 官方 Agent 策略集合
tags:
- "agent"
resource:
memory: 1048576
permission:
tool:
enabled: true
model:
enabled: true
llm: true
plugins:
agent_strategies:
- "provider/agent.yaml"
meta:
version: 0.0.1
arch:
- "amd64"
- "arm64"
runner:
language: "python"
version: "3.12"
entrypoint: "main"

@ -0,0 +1,212 @@
import json
import re
from collections.abc import Generator
from typing import Union
from dify_plugin.entities.model.llm import LLMResultChunk
from dify_plugin.interfaces.agent import AgentScratchpadUnit
class CotAgentOutputParser:
@classmethod
def handle_react_stream_output(
cls, llm_response: Generator[LLMResultChunk, None, None], usage_dict: dict
) -> Generator[Union[str, AgentScratchpadUnit.Action], None, None]:
def parse_action(json_str):
try:
action = json.loads(json_str, strict=False)
action_name = None
action_input = None
# cohere always returns a list
if isinstance(action, list) and len(action) == 1:
action = action[0]
for key, value in action.items():
if "input" in key.lower():
action_input = value
else:
action_name = value
if action_name is not None and action_input is not None:
return AgentScratchpadUnit.Action(
action_name=action_name,
action_input=action_input,
)
else:
return json_str or ""
except:
return json_str or ""
def extra_json_from_code_block(
code_block,
) -> Generator[Union[str, AgentScratchpadUnit.Action], None, None]:
code_blocks = re.findall(r"```(.*?)```", code_block, re.DOTALL)
if not code_blocks:
return
for block in code_blocks:
json_text = re.sub(
r"^[a-zA-Z]+\n", "", block.strip(), flags=re.MULTILINE
)
yield parse_action(json_text)
code_block_cache = ""
code_block_delimiter_count = 0
in_code_block = False
json_cache = ""
json_quote_count = 0
in_json = False
got_json = False
action_cache = ""
action_str = "action:"
action_idx = 0
thought_cache = ""
thought_str = "thought:"
thought_idx = 0
last_character = ""
for response in llm_response:
if response.delta.usage:
usage_dict["usage"] = response.delta.usage
response_content = response.delta.message.content
if not isinstance(response_content, str):
continue
# stream
index = 0
while index < len(response_content):
steps = 1
delta = response_content[index : index + steps]
yield_delta = False
if delta == "`":
last_character = delta
code_block_cache += delta
code_block_delimiter_count += 1
else:
if not in_code_block:
if code_block_delimiter_count > 0:
last_character = delta
yield code_block_cache
code_block_cache = ""
else:
last_character = delta
code_block_cache += delta
code_block_delimiter_count = 0
if not in_code_block and not in_json:
if delta.lower() == action_str[action_idx] and action_idx == 0:
if last_character not in {"\n", " ", ""}:
yield_delta = True
else:
last_character = delta
action_cache += delta
action_idx += 1
if action_idx == len(action_str):
action_cache = ""
action_idx = 0
index += steps
continue
elif delta.lower() == action_str[action_idx] and action_idx > 0:
last_character = delta
action_cache += delta
action_idx += 1
if action_idx == len(action_str):
action_cache = ""
action_idx = 0
index += steps
continue
else:
if action_cache:
last_character = delta
yield action_cache
action_cache = ""
action_idx = 0
if delta.lower() == thought_str[thought_idx] and thought_idx == 0:
if last_character not in {"\n", " ", ""}:
yield_delta = True
else:
last_character = delta
thought_cache += delta
thought_idx += 1
if thought_idx == len(thought_str):
thought_cache = ""
thought_idx = 0
index += steps
continue
elif delta.lower() == thought_str[thought_idx] and thought_idx > 0:
last_character = delta
thought_cache += delta
thought_idx += 1
if thought_idx == len(thought_str):
thought_cache = ""
thought_idx = 0
index += steps
continue
else:
if thought_cache:
last_character = delta
yield thought_cache
thought_cache = ""
thought_idx = 0
if yield_delta:
index += steps
last_character = delta
yield delta
continue
if code_block_delimiter_count == 3:
if in_code_block:
last_character = delta
yield from extra_json_from_code_block(code_block_cache)
code_block_cache = ""
in_code_block = not in_code_block
code_block_delimiter_count = 0
if not in_code_block:
# handle single json
if delta == "{":
json_quote_count += 1
in_json = True
last_character = delta
json_cache += delta
elif delta == "}":
last_character = delta
json_cache += delta
if json_quote_count > 0:
json_quote_count -= 1
if json_quote_count == 0:
in_json = False
got_json = True
index += steps
continue
else:
if in_json:
last_character = delta
json_cache += delta
if got_json:
got_json = False
last_character = delta
yield parse_action(json_cache)
json_cache = ""
json_quote_count = 0
in_json = False
if not in_code_block and not in_json:
last_character = delta
yield delta.replace("`", "")
index += steps
if code_block_cache:
yield code_block_cache
if json_cache:
yield parse_action(json_cache)

@ -0,0 +1,106 @@
ENGLISH_REACT_COMPLETION_PROMPT_TEMPLATES = """Respond to the human as helpfully and accurately as possible.
{{instruction}}
You have access to the following tools:
{{tools}}
Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input).
Valid "action" values: "Final Answer" or {{tool_names}}
Provide only ONE action per $JSON_BLOB, as shown:
```
{
"action": $TOOL_NAME,
"action_input": $ACTION_INPUT
}
```
Follow this format:
Question: input question to answer
Thought: consider previous and subsequent steps
Action:
```
$JSON_BLOB
```
Observation: action result
... (repeat Thought/Action/Observation N times)
Thought: I know what to respond
Action:
```
{
"action": "Final Answer",
"action_input": "Final response to human"
}
```
Begin! Reminder to ALWAYS respond with a valid json blob of a single action. Use tools if necessary. Respond directly if appropriate. Format is Action:```$JSON_BLOB```then Observation:.
{{historic_messages}}
Question: {{query}}
{{agent_scratchpad}}
Thought:""" # noqa: E501
ENGLISH_REACT_COMPLETION_AGENT_SCRATCHPAD_TEMPLATES = """Observation: {{observation}}
Thought:"""
ENGLISH_REACT_CHAT_PROMPT_TEMPLATES = """Respond to the human as helpfully and accurately as possible.
{{instruction}}
You have access to the following tools:
{{tools}}
Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input).
Valid "action" values: "Final Answer" or {{tool_names}}
Provide only ONE action per $JSON_BLOB, as shown:
```
{
"action": $TOOL_NAME,
"action_input": $ACTION_INPUT
}
```
Follow this format:
Question: input question to answer
Thought: consider previous and subsequent steps
Action:
```
$JSON_BLOB
```
Observation: action result
... (repeat Thought/Action/Observation N times)
Thought: I know what to respond
Action:
```
{
"action": "Final Answer",
"action_input": "Final response to human"
}
```
Begin! Reminder to ALWAYS respond with a valid json blob of a single action. Use tools if necessary. Respond directly if appropriate. Format is Action:```$JSON_BLOB```then Observation:.
""" # noqa: E501
ENGLISH_REACT_CHAT_AGENT_SCRATCHPAD_TEMPLATES = ""
REACT_PROMPT_TEMPLATES = {
"english": {
"chat": {
"prompt": ENGLISH_REACT_CHAT_PROMPT_TEMPLATES,
"agent_scratchpad": ENGLISH_REACT_CHAT_AGENT_SCRATCHPAD_TEMPLATES,
},
"completion": {
"prompt": ENGLISH_REACT_COMPLETION_PROMPT_TEMPLATES,
"agent_scratchpad": ENGLISH_REACT_COMPLETION_AGENT_SCRATCHPAD_TEMPLATES,
},
}
}

@ -0,0 +1,5 @@
from dify_plugin.interfaces.agent import AgentProvider
class LanggeniusAgentProvider(AgentProvider):
pass

@ -0,0 +1,18 @@
identity:
author: langgenius
name: agent
label:
en_US: Agent
zh_Hans: Agent
pt_BR: Agent
description:
en_US: Agent
zh_Hans: Agent
pt_BR: Agent
icon: icon.svg
strategies:
- strategies/function_calling.yaml
- strategies/ReAct.yaml
extra:
python:
source: provider/agent.py

@ -0,0 +1,642 @@
import json
import time
from collections.abc import Generator, Mapping
from typing import Any, Optional, cast
from dify_plugin.entities.agent import AgentInvokeMessage
from dify_plugin.entities.model.llm import LLMModelConfig, LLMUsage
from dify_plugin.entities.model.message import (
AssistantPromptMessage,
PromptMessage,
SystemPromptMessage,
ToolPromptMessage,
UserPromptMessage,
)
from dify_plugin.entities.tool import (
LogMetadata,
ToolInvokeMessage,
ToolParameter,
ToolProviderType,
)
from dify_plugin.interfaces.agent import (
AgentModelConfig,
AgentScratchpadUnit,
AgentStrategy,
ToolEntity,
)
from output_parser.cot_output_parser import CotAgentOutputParser
from prompt.template import REACT_PROMPT_TEMPLATES
from pydantic import BaseModel, Field
ignore_observation_providers = ["wenxin"]
class ReActParams(BaseModel):
query: str
instruction: str | None
model: AgentModelConfig
tools: list[ToolEntity] | None
inputs: dict[str, Any] = {}
maximum_iterations: int = 3
class AgentPromptEntity(BaseModel):
"""
Agent Prompt Entity.
"""
first_prompt: str
next_iteration: str
class ToolInvokeMeta(BaseModel):
"""
Tool invoke meta
"""
time_cost: float = Field(..., description="The time cost of the tool invoke")
error: Optional[str] = None
tool_config: Optional[dict] = None
@classmethod
def empty(cls) -> "ToolInvokeMeta":
"""
Get an empty instance of ToolInvokeMeta
"""
return cls(time_cost=0.0, error=None, tool_config={})
@classmethod
def error_instance(cls, error: str) -> "ToolInvokeMeta":
"""
Get an instance of ToolInvokeMeta with error
"""
return cls(time_cost=0.0, error=error, tool_config={})
def to_dict(self) -> dict:
return {
"time_cost": self.time_cost,
"error": self.error,
"tool_config": self.tool_config,
}
class ReActAgentStrategy(AgentStrategy):
def _invoke(self, parameters: dict[str, Any]) -> Generator[AgentInvokeMessage]:
react_params = ReActParams(**parameters)
query = react_params.query
model = react_params.model
agent_scratchpad = []
history_prompt_messages: list[PromptMessage] = []
current_session_messages = []
self._organize_historic_prompt_messages(
history_prompt_messages, current_session_messages=current_session_messages
)
tools = react_params.tools
tool_instances = {tool.identity.name: tool for tool in tools} if tools else {}
react_params.model.completion_params = (
react_params.model.completion_params or {}
)
# check model mode
stop = (
react_params.model.completion_params.get("stop", [])
if react_params.model.completion_params
else []
)
if (
"Observation" not in stop
and model.provider not in ignore_observation_providers
):
stop.append("Observation")
# init instruction
inputs = react_params.inputs
instruction = react_params.instruction or ""
self._instruction = self._fill_in_inputs_from_external_data_tools(
instruction, inputs
)
iteration_step = 1
max_iteration_steps = react_params.maximum_iterations
# convert tools into ModelRuntime Tool format
prompt_messages_tools = self._init_prompt_tools(tools)
self._prompt_messages_tools = prompt_messages_tools
run_agent_state = True
llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
final_answer = ""
prompt_messages = []
while run_agent_state and iteration_step <= max_iteration_steps:
# continue to run until there is not any tool call
run_agent_state = False
round_started_at = time.perf_counter()
round_log = self.create_log_message(
label=f"ROUND {iteration_step}",
data={},
metadata={
LogMetadata.STARTED_AT: round_started_at,
},
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield round_log
if iteration_step == max_iteration_steps:
# the last iteration, remove all tools
self._prompt_messages_tools = []
message_file_ids: list[str] = []
# recalc llm max tokens
prompt_messages = self._organize_prompt_messages(agent_scratchpad, query)
if model.completion_params:
self.recalc_llm_max_tokens(
model.entity, prompt_messages, model.completion_params
)
# invoke model
chunks = self.session.model.llm.invoke(
model_config=LLMModelConfig(**model.model_dump(mode="json")),
prompt_messages=prompt_messages,
stream=True,
stop=stop,
)
usage_dict = {}
react_chunks = CotAgentOutputParser.handle_react_stream_output(
chunks, usage_dict
)
scratchpad = AgentScratchpadUnit(
agent_response="",
thought="",
action_str="",
observation="",
action=None,
)
model_started_at = time.perf_counter()
model_log = self.create_log_message(
label=f"{model.model} Thought",
data={},
metadata={
LogMetadata.STARTED_AT: model_started_at,
LogMetadata.PROVIDER: model.provider,
},
parent=round_log,
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield model_log
for chunk in react_chunks:
if isinstance(chunk, AgentScratchpadUnit.Action):
action = chunk
# detect action
assert scratchpad.agent_response is not None
scratchpad.agent_response += json.dumps(chunk.model_dump())
scratchpad.action_str = json.dumps(chunk.model_dump())
scratchpad.action = action
else:
scratchpad.agent_response = scratchpad.agent_response or ""
scratchpad.thought = scratchpad.thought or ""
scratchpad.agent_response += chunk
scratchpad.thought += chunk
scratchpad.thought = (
scratchpad.thought.strip()
if scratchpad.thought
else "I am thinking about how to help you"
)
agent_scratchpad.append(scratchpad)
# get llm usage
if "usage" in usage_dict:
if usage_dict["usage"] is not None:
self.increase_usage(llm_usage, usage_dict["usage"])
else:
usage_dict["usage"] = LLMUsage.empty_usage()
action = (
scratchpad.action.to_dict()
if scratchpad.action
else {"action": scratchpad.agent_response}
)
yield self.finish_log_message(
log=model_log,
data={"thought": scratchpad.thought, **action},
metadata={
LogMetadata.STARTED_AT: model_started_at,
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter() - model_started_at,
LogMetadata.PROVIDER: model.provider,
LogMetadata.TOTAL_PRICE: usage_dict["usage"].total_price
if usage_dict["usage"]
else 0,
LogMetadata.CURRENCY: usage_dict["usage"].currency
if usage_dict["usage"]
else "",
LogMetadata.TOTAL_TOKENS: usage_dict["usage"].total_tokens
if usage_dict["usage"]
else 0,
},
)
if not scratchpad.action:
final_answer = scratchpad.thought
else:
if scratchpad.action.action_name.lower() == "final answer":
# action is final answer, return final answer directly
try:
if isinstance(scratchpad.action.action_input, dict):
final_answer = json.dumps(scratchpad.action.action_input)
elif isinstance(scratchpad.action.action_input, str):
final_answer = scratchpad.action.action_input
else:
final_answer = f"{scratchpad.action.action_input}"
except json.JSONDecodeError:
final_answer = f"{scratchpad.action.action_input}"
else:
run_agent_state = True
# action is tool call, invoke tool
tool_call_started_at = time.perf_counter()
tool_name = scratchpad.action.action_name
tool_call_log = self.create_log_message(
label=f"CALL {tool_name}",
data={},
metadata={
LogMetadata.STARTED_AT: time.perf_counter(),
LogMetadata.PROVIDER: tool_instances[
tool_name
].identity.provider
if tool_instances.get(tool_name)
else "",
},
parent=round_log,
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield tool_call_log
tool_invoke_response, tool_invoke_parameters = (
self._handle_invoke_action(
action=scratchpad.action,
tool_instances=tool_instances,
message_file_ids=message_file_ids,
)
)
scratchpad.observation = tool_invoke_response
scratchpad.agent_response = tool_invoke_response
yield self.finish_log_message(
log=tool_call_log,
data={
"tool_name": tool_name,
"tool_call_args": tool_invoke_parameters,
"output": tool_invoke_response,
},
metadata={
LogMetadata.STARTED_AT: tool_call_started_at,
LogMetadata.PROVIDER: tool_instances[
tool_name
].identity.provider
if tool_instances.get(tool_name)
else "",
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter()
- tool_call_started_at,
},
)
# update prompt tool message
for prompt_tool in self._prompt_messages_tools:
self.update_prompt_message_tool(
tool_instances[prompt_tool.name], prompt_tool
)
yield self.finish_log_message(
log=round_log,
data={
"action_name": scratchpad.action.action_name
if scratchpad.action
else "",
"action_input": scratchpad.action.action_input
if scratchpad.action
else "",
"thought": scratchpad.thought,
"observation": scratchpad.observation,
},
metadata={
LogMetadata.STARTED_AT: round_started_at,
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter() - round_started_at,
LogMetadata.TOTAL_PRICE: usage_dict["usage"].total_price
if usage_dict["usage"]
else 0,
LogMetadata.CURRENCY: usage_dict["usage"].currency
if usage_dict["usage"]
else "",
LogMetadata.TOTAL_TOKENS: usage_dict["usage"].total_tokens
if usage_dict["usage"]
else 0,
},
)
iteration_step += 1
yield self.create_text_message(final_answer)
yield self.create_json_message(
{
"execution_metadata": {
LogMetadata.TOTAL_PRICE: llm_usage["usage"].total_price
if llm_usage["usage"] is not None
else 0,
LogMetadata.CURRENCY: llm_usage["usage"].currency
if llm_usage["usage"] is not None
else "",
LogMetadata.TOTAL_TOKENS: llm_usage["usage"].total_tokens
if llm_usage["usage"] is not None
else 0,
}
}
)
def _organize_system_prompt(self) -> SystemPromptMessage:
"""
Organize system prompt
"""
prompt_entity = AgentPromptEntity(
first_prompt=REACT_PROMPT_TEMPLATES["english"]["chat"]["prompt"],
next_iteration=REACT_PROMPT_TEMPLATES["english"]["chat"][
"agent_scratchpad"
],
)
if not prompt_entity:
raise ValueError("Agent prompt configuration is not set")
first_prompt = prompt_entity.first_prompt
system_prompt = (
first_prompt.replace("{{instruction}}", self._instruction)
.replace(
"{{tools}}",
json.dumps(
[
tool.model_dump(mode="json")
for tool in self._prompt_messages_tools
]
),
)
.replace(
"{{tool_names}}",
", ".join([tool.name for tool in self._prompt_messages_tools]),
)
)
return SystemPromptMessage(content=system_prompt)
def _organize_user_query(
self, query, prompt_messages: list[PromptMessage]
) -> list[PromptMessage]:
"""
Organize user query
"""
prompt_messages.append(UserPromptMessage(content=query))
return prompt_messages
def _organize_prompt_messages(
self, agent_scratchpad: list, query: str
) -> list[PromptMessage]:
"""
Organize
"""
# organize system prompt
system_message = self._organize_system_prompt()
# organize current assistant messages
agent_scratchpad = agent_scratchpad
if not agent_scratchpad:
assistant_messages = []
else:
assistant_message = AssistantPromptMessage(content="")
assistant_message.content = (
"" # FIXME: type check tell mypy that assistant_message.content is str
)
for unit in agent_scratchpad:
if unit.is_final():
assert isinstance(assistant_message.content, str)
assistant_message.content += f"Final Answer: {unit.agent_response}"
else:
assert isinstance(assistant_message.content, str)
assistant_message.content += f"Thought: {unit.thought}\n\n"
if unit.action_str:
assistant_message.content += f"Action: {unit.action_str}\n\n"
if unit.observation:
assistant_message.content += (
f"Observation: {unit.observation}\n\n"
)
assistant_messages = [assistant_message]
# query messages
query_messages = self._organize_user_query(query, [])
if assistant_messages:
# organize historic prompt messages
historic_messages = self._organize_historic_prompt_messages(
[
system_message,
*query_messages,
*assistant_messages,
UserPromptMessage(content="continue"),
]
)
messages = [
system_message,
*historic_messages,
*query_messages,
*assistant_messages,
UserPromptMessage(content="continue"),
]
else:
# organize historic prompt messages
historic_messages = self._organize_historic_prompt_messages(
[system_message, *query_messages]
)
messages = [system_message, *historic_messages, *query_messages]
# join all messages
return messages
def _handle_invoke_action(
self,
action: AgentScratchpadUnit.Action,
tool_instances: Mapping[str, ToolEntity],
message_file_ids: list[str],
) -> tuple[str, dict[str, Any] | str]:
"""
handle invoke action
:param action: action
:param tool_instances: tool instances
:param message_file_ids: message file ids
:param trace_manager: trace manager
:return: observation, meta
"""
# action is tool call, invoke tool
tool_call_name = action.action_name
tool_call_args = action.action_input
tool_instance = tool_instances.get(tool_call_name)
if not tool_instance:
answer = f"there is not a tool named {tool_call_name}"
return answer, tool_call_args
if isinstance(tool_call_args, str):
try:
tool_call_args = json.loads(tool_call_args)
except json.JSONDecodeError as e:
params = [
param.name
for param in tool_instance.parameters
if param.form == ToolParameter.ToolParameterForm.LLM
]
if len(params) > 1:
raise ValueError("tool call args is not a valid json string") from e
tool_call_args = {params[0]: tool_call_args} if len(params) == 1 else {}
tool_invoke_parameters = {**tool_instance.runtime_parameters, **tool_call_args}
try:
tool_invoke_responses = self.session.tool.invoke(
provider_type=ToolProviderType(tool_instance.provider_type),
provider=tool_instance.identity.provider,
tool_name=tool_instance.identity.name,
parameters=tool_invoke_parameters,
)
result = ""
for response in tool_invoke_responses:
if response.type == ToolInvokeMessage.MessageType.TEXT:
result += cast(ToolInvokeMessage.TextMessage, response.message).text
elif response.type == ToolInvokeMessage.MessageType.LINK:
result += (
f"result link: {cast(ToolInvokeMessage.TextMessage, response.message).text}."
+ " please tell user to check it."
)
elif response.type in {
ToolInvokeMessage.MessageType.IMAGE_LINK,
ToolInvokeMessage.MessageType.IMAGE,
}:
result += (
"image has been created and sent to user already, "
+ "you do not need to create it, just tell the user to check it now."
)
elif response.type == ToolInvokeMessage.MessageType.JSON:
text = json.dumps(
cast(
ToolInvokeMessage.JsonMessage, response.message
).json_object,
ensure_ascii=False,
)
result += f"tool response: {text}."
else:
result += f"tool response: {response.message!r}."
except Exception as e:
result = f"tool invoke error: {str(e)}"
return result, tool_invoke_parameters
def _convert_dict_to_action(self, action: dict) -> AgentScratchpadUnit.Action:
"""
convert dict to action
"""
return AgentScratchpadUnit.Action(
action_name=action["action"], action_input=action["action_input"]
)
def _fill_in_inputs_from_external_data_tools(
self, instruction: str, inputs: Mapping[str, Any]
) -> str:
"""
fill in inputs from external data tools
"""
for key, value in inputs.items():
try:
instruction = instruction.replace(f"{{{{{key}}}}}", str(value))
except Exception:
continue
return instruction
def _format_assistant_message(
self, agent_scratchpad: list[AgentScratchpadUnit]
) -> str:
"""
format assistant message
"""
message = ""
for scratchpad in agent_scratchpad:
if scratchpad.is_final():
message += f"Final Answer: {scratchpad.agent_response}"
else:
message += f"Thought: {scratchpad.thought}\n\n"
if scratchpad.action_str:
message += f"Action: {scratchpad.action_str}\n\n"
if scratchpad.observation:
message += f"Observation: {scratchpad.observation}\n\n"
return message
def _organize_historic_prompt_messages(
self,
history_prompt_messages: list[PromptMessage],
current_session_messages: list[PromptMessage] | None = None,
) -> list[PromptMessage]:
"""
organize historic prompt messages
"""
result: list[PromptMessage] = []
scratchpads: list[AgentScratchpadUnit] = []
current_scratchpad: AgentScratchpadUnit | None = None
for message in history_prompt_messages:
if isinstance(message, AssistantPromptMessage):
if not current_scratchpad:
assert isinstance(message.content, str)
current_scratchpad = AgentScratchpadUnit(
agent_response=message.content,
thought=message.content
or "I am thinking about how to help you",
action_str="",
action=None,
observation=None,
)
scratchpads.append(current_scratchpad)
if message.tool_calls:
try:
current_scratchpad.action = AgentScratchpadUnit.Action(
action_name=message.tool_calls[0].function.name,
action_input=json.loads(
message.tool_calls[0].function.arguments
),
)
current_scratchpad.action_str = json.dumps(
current_scratchpad.action.to_dict()
)
except Exception:
pass
elif isinstance(message, ToolPromptMessage):
if current_scratchpad:
assert isinstance(message.content, str)
current_scratchpad.observation = message.content
else:
raise NotImplementedError("expected str type")
elif isinstance(message, UserPromptMessage):
if scratchpads:
result.append(
AssistantPromptMessage(
content=self._format_assistant_message(scratchpads)
)
)
scratchpads = []
current_scratchpad = None
result.append(message)
if scratchpads:
result.append(
AssistantPromptMessage(
content=self._format_assistant_message(scratchpads)
)
)
return current_session_messages or []

@ -0,0 +1,58 @@
identity:
name: ReAct
author: Dify
label:
en_US: ReAct
zh_Hans: ReAct
pt_BR: ReAct
description:
en_US: ReAct is a basic strategy for agent, model will use the tools provided to perform the task.
zh_Hans: ReAct 是一个基本的 Agent 策略,模型将使用提供的工具来执行任务。
pt_BR: ReAct is a basic strategy for agent, model will use the tools provided to perform the task.
parameters:
- name: model
type: model-selector
scope: tool-call&llm
required: true
label:
en_US: Model
zh_Hans: 模型
pt_BR: Model
- name: tools
type: array[tools]
required: true
label:
en_US: Tool list
zh_Hans: 工具列表
pt_BR: Tool list
- name: instruction
type: string
required: true
label:
en_US: Instruction
zh_Hans: 指令
pt_BR: Instruction
auto_generate:
type: prompt_instruction
template:
enabled: true
- name: query
type: string
required: true
label:
en_US: Query
zh_Hans: 查询
pt_BR: Query
- name: maximum_iterations
type: number
required: true
label:
en_US: Maximum Iterations
zh_Hans: 最大迭代次数
pt_BR: Maximum Iterations
default: 3
min: 1
max: 30
extra:
python:
source: strategies/ReAct.py

@ -0,0 +1,546 @@
import json
import time
from collections.abc import Generator
from copy import deepcopy
from typing import Any, Optional, cast
from dify_plugin.entities.agent import AgentInvokeMessage
from dify_plugin.entities.model.llm import (
LLMModelConfig,
LLMResult,
LLMResultChunk,
LLMUsage,
)
from dify_plugin.entities.model.message import (
AssistantPromptMessage,
PromptMessage,
PromptMessageContentType,
PromptMessageRole,
SystemPromptMessage,
ToolPromptMessage,
UserPromptMessage,
)
from dify_plugin.entities.tool import LogMetadata, ToolInvokeMessage, ToolProviderType
from dify_plugin.interfaces.agent import AgentModelConfig, AgentStrategy, ToolEntity
from pydantic import BaseModel, Field
class FunctionCallingParams(BaseModel):
query: str
instruction: str | None
model: AgentModelConfig
tools: list[ToolEntity] | None
maximum_iterations: int = 3
class ToolInvokeMeta(BaseModel):
"""
Tool invoke meta
"""
time_cost: float = Field(..., description="The time cost of the tool invoke")
error: Optional[str] = None
tool_config: Optional[dict] = None
@classmethod
def empty(cls) -> "ToolInvokeMeta":
"""
Get an empty instance of ToolInvokeMeta
"""
return cls(time_cost=0.0, error=None, tool_config={})
@classmethod
def error_instance(cls, error: str) -> "ToolInvokeMeta":
"""
Get an instance of ToolInvokeMeta with error
"""
return cls(time_cost=0.0, error=error, tool_config={})
def to_dict(self) -> dict:
return {
"time_cost": self.time_cost,
"error": self.error,
"tool_config": self.tool_config,
}
class FunctionCallingAgentStrategy(AgentStrategy):
def __init__(self, session):
super().__init__(session)
self.query = ""
def _invoke(self, parameters: dict[str, Any]) -> Generator[AgentInvokeMessage]:
"""
Run FunctionCall agent application
"""
fc_params = FunctionCallingParams(**parameters)
query = fc_params.query
self.query = query
instruction = fc_params.instruction
init_prompt_messages = [
PromptMessage(role=PromptMessageRole.SYSTEM, content=instruction)
]
tools = fc_params.tools
tool_instances = {tool.identity.name: tool for tool in tools} if tools else {}
model = fc_params.model
stop = (
fc_params.model.completion_params.get("stop", [])
if fc_params.model.completion_params
else []
)
# convert tools into ModelRuntime Tool format
prompt_messages_tools = self._init_prompt_tools(tools)
iteration_step = 1
max_iteration_steps = fc_params.maximum_iterations
current_thoughts: list[PromptMessage] = []
# continue to run until there is not any tool call
function_call_state = True
llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
final_answer = ""
while function_call_state and iteration_step <= max_iteration_steps:
function_call_state = False
round_started_at = time.perf_counter()
round_log = self.create_log_message(
label=f"ROUND {iteration_step}",
data={},
metadata={
LogMetadata.STARTED_AT: round_started_at,
},
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield round_log
if iteration_step == max_iteration_steps:
# the last iteration, remove all tools
prompt_messages_tools = []
# recalc llm max tokens
prompt_messages = self._organize_prompt_messages(
history_prompt_messages=init_prompt_messages,
current_thoughts=current_thoughts,
)
if model.completion_params:
self.recalc_llm_max_tokens(
model.entity, prompt_messages, model.completion_params
)
# invoke model
model_started_at = time.perf_counter()
model_log = self.create_log_message(
label=f"{model.model} Thought",
data={},
metadata={
LogMetadata.STARTED_AT: model_started_at,
LogMetadata.PROVIDER: model.provider,
},
parent=round_log,
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield model_log
chunks: Generator[LLMResultChunk, None, None] | LLMResult = (
self.session.model.llm.invoke(
model_config=LLMModelConfig(**model.model_dump(mode="json")),
prompt_messages=prompt_messages,
stream=True,
stop=stop,
tools=prompt_messages_tools,
)
)
tool_calls: list[tuple[str, str, dict[str, Any]]] = []
# save full response
response = ""
# save tool call names and inputs
tool_call_names = ""
current_llm_usage = None
if isinstance(chunks, Generator):
for chunk in chunks:
# check if there is any tool call
if self.check_tool_calls(chunk):
function_call_state = True
tool_calls.extend(self.extract_tool_calls(chunk) or [])
tool_call_names = ";".join(
[tool_call[1] for tool_call in tool_calls]
)
if chunk.delta.message and chunk.delta.message.content:
if isinstance(chunk.delta.message.content, list):
for content in chunk.delta.message.content:
response += content.data
if (
not function_call_state
or iteration_step == max_iteration_steps
):
yield self.create_text_message(content.data)
else:
response += str(chunk.delta.message.content)
if (
not function_call_state
or iteration_step == max_iteration_steps
):
yield self.create_text_message(
str(chunk.delta.message.content)
)
if chunk.delta.usage:
self.increase_usage(llm_usage, chunk.delta.usage)
current_llm_usage = chunk.delta.usage
else:
result = chunks
# check if there is any tool call
if self.check_blocking_tool_calls(result):
function_call_state = True
tool_calls.extend(self.extract_blocking_tool_calls(result) or [])
tool_call_names = ";".join(
[tool_call[1] for tool_call in tool_calls]
)
if result.usage:
self.increase_usage(llm_usage, result.usage)
current_llm_usage = result.usage
if result.message and result.message.content:
if isinstance(result.message.content, list):
for content in result.message.content:
response += content.data
else:
response += str(result.message.content)
if not result.message.content:
result.message.content = ""
yield self.finish_log_message(
log=model_log,
data={
"output": response,
"tool_name": tool_call_names,
"tool_input": {
tool_call[1]: tool_call[2] for tool_call in tool_calls
},
},
metadata={
LogMetadata.STARTED_AT: model_started_at,
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter() - model_started_at,
LogMetadata.PROVIDER: model.provider,
LogMetadata.TOTAL_PRICE: current_llm_usage.total_price
if current_llm_usage
else 0,
LogMetadata.CURRENCY: current_llm_usage.currency
if current_llm_usage
else "",
LogMetadata.TOTAL_TOKENS: current_llm_usage.total_tokens
if current_llm_usage
else 0,
},
)
assistant_message = AssistantPromptMessage(content="", tool_calls=[])
if tool_calls:
assistant_message.tool_calls = [
AssistantPromptMessage.ToolCall(
id=tool_call[0],
type="function",
function=AssistantPromptMessage.ToolCall.ToolCallFunction(
name=tool_call[1],
arguments=json.dumps(tool_call[2], ensure_ascii=False),
),
)
for tool_call in tool_calls
]
else:
assistant_message.content = response
current_thoughts.append(assistant_message)
final_answer += response + "\n"
# call tools
tool_responses = []
for tool_call_id, tool_call_name, tool_call_args in tool_calls:
tool_instance = tool_instances[tool_call_name]
tool_call_started_at = time.perf_counter()
tool_call_log = self.create_log_message(
label=f"CALL {tool_call_name}",
data={},
metadata={
LogMetadata.STARTED_AT: time.perf_counter(),
LogMetadata.PROVIDER: tool_instance.identity.provider,
},
parent=round_log,
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield tool_call_log
if not tool_instance:
tool_response = {
"tool_call_id": tool_call_id,
"tool_call_name": tool_call_name,
"tool_response": f"there is not a tool named {tool_call_name}",
"meta": ToolInvokeMeta.error_instance(
f"there is not a tool named {tool_call_name}"
).to_dict(),
}
else:
# invoke tool
try:
tool_invoke_responses = self.session.tool.invoke(
provider_type=ToolProviderType(tool_instance.provider_type),
provider=tool_instance.identity.provider,
tool_name=tool_instance.identity.name,
parameters={
**tool_instance.runtime_parameters,
**tool_call_args,
},
)
result = ""
for response in tool_invoke_responses:
if response.type == ToolInvokeMessage.MessageType.TEXT:
result += cast(
ToolInvokeMessage.TextMessage, response.message
).text
elif response.type == ToolInvokeMessage.MessageType.LINK:
result += (
f"result link: {cast(ToolInvokeMessage.TextMessage, response.message).text}."
+ " please tell user to check it."
)
elif response.type in {
ToolInvokeMessage.MessageType.IMAGE_LINK,
ToolInvokeMessage.MessageType.IMAGE,
}:
result += (
"image has been created and sent to user already, "
+ "you do not need to create it, just tell the user to check it now."
)
elif response.type == ToolInvokeMessage.MessageType.JSON:
text = json.dumps(
cast(
ToolInvokeMessage.JsonMessage, response.message
).json_object,
ensure_ascii=False,
)
result += f"tool response: {text}."
else:
result += f"tool response: {response.message!r}."
except Exception as e:
result = f"tool invoke error: {str(e)}"
tool_response = {
"tool_call_id": tool_call_id,
"tool_call_name": tool_call_name,
"tool_call_input": {
**tool_instance.runtime_parameters,
**tool_call_args,
},
"tool_response": result,
}
yield self.finish_log_message(
log=tool_call_log,
data={
"output": tool_response,
},
metadata={
LogMetadata.STARTED_AT: tool_call_started_at,
LogMetadata.PROVIDER: tool_instance.identity.provider,
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter()
- tool_call_started_at,
},
)
tool_responses.append(tool_response)
if tool_response["tool_response"] is not None:
current_thoughts.append(
ToolPromptMessage(
content=str(tool_response["tool_response"]),
tool_call_id=tool_call_id,
name=tool_call_name,
)
)
# update prompt tool
for prompt_tool in prompt_messages_tools:
self.update_prompt_message_tool(
tool_instances[prompt_tool.name], prompt_tool
)
yield self.finish_log_message(
log=round_log,
data={
"output": {
"llm_response": response,
"tool_responses": tool_responses,
},
},
metadata={
LogMetadata.STARTED_AT: round_started_at,
LogMetadata.FINISHED_AT: time.perf_counter(),
LogMetadata.ELAPSED_TIME: time.perf_counter() - round_started_at,
LogMetadata.TOTAL_PRICE: current_llm_usage.total_price
if current_llm_usage
else 0,
LogMetadata.CURRENCY: current_llm_usage.currency
if current_llm_usage
else "",
LogMetadata.TOTAL_TOKENS: current_llm_usage.total_tokens
if current_llm_usage
else 0,
},
)
iteration_step += 1
yield self.create_json_message(
{
"execution_metadata": {
LogMetadata.TOTAL_PRICE: llm_usage["usage"].total_price
if llm_usage["usage"] is not None
else 0,
LogMetadata.CURRENCY: llm_usage["usage"].currency
if llm_usage["usage"] is not None
else "",
LogMetadata.TOTAL_TOKENS: llm_usage["usage"].total_tokens
if llm_usage["usage"] is not None
else 0,
}
}
)
def check_tool_calls(self, llm_result_chunk: LLMResultChunk) -> bool:
"""
Check if there is any tool call in llm result chunk
"""
return bool(llm_result_chunk.delta.message.tool_calls)
def check_blocking_tool_calls(self, llm_result: LLMResult) -> bool:
"""
Check if there is any blocking tool call in llm result
"""
return bool(llm_result.message.tool_calls)
def extract_tool_calls(
self, llm_result_chunk: LLMResultChunk
) -> list[tuple[str, str, dict[str, Any]]]:
"""
Extract tool calls from llm result chunk
Returns:
List[Tuple[str, str, Dict[str, Any]]]: [(tool_call_id, tool_call_name, tool_call_args)]
"""
tool_calls = []
for prompt_message in llm_result_chunk.delta.message.tool_calls:
args = {}
if prompt_message.function.arguments != "":
args = json.loads(prompt_message.function.arguments)
tool_calls.append(
(
prompt_message.id,
prompt_message.function.name,
args,
)
)
return tool_calls
def extract_blocking_tool_calls(
self, llm_result: LLMResult
) -> list[tuple[str, str, dict[str, Any]]]:
"""
Extract blocking tool calls from llm result
Returns:
List[Tuple[str, str, Dict[str, Any]]]: [(tool_call_id, tool_call_name, tool_call_args)]
"""
tool_calls = []
for prompt_message in llm_result.message.tool_calls:
args = {}
if prompt_message.function.arguments != "":
args = json.loads(prompt_message.function.arguments)
tool_calls.append(
(
prompt_message.id,
prompt_message.function.name,
args,
)
)
return tool_calls
def _init_system_message(
self, prompt_template: str, prompt_messages: list[PromptMessage]
) -> list[PromptMessage]:
"""
Initialize system message
"""
if not prompt_messages and prompt_template:
return [
SystemPromptMessage(content=prompt_template),
]
if (
prompt_messages
and not isinstance(prompt_messages[0], SystemPromptMessage)
and prompt_template
):
prompt_messages.insert(0, SystemPromptMessage(content=prompt_template))
return prompt_messages or []
def _organize_user_query(
self, query: str, prompt_messages: list[PromptMessage]
) -> list[PromptMessage]:
"""
Organize user query
"""
prompt_messages.append(UserPromptMessage(content=query))
return prompt_messages
def _clear_user_prompt_image_messages(
self, prompt_messages: list[PromptMessage]
) -> list[PromptMessage]:
"""
As for now, gpt supports both fc and vision at the first iteration.
We need to remove the image messages from the prompt messages at the first iteration.
"""
prompt_messages = deepcopy(prompt_messages)
for prompt_message in prompt_messages:
if isinstance(prompt_message, UserPromptMessage) and isinstance(
prompt_message.content, list
):
prompt_message.content = "\n".join(
[
content.data
if content.type == PromptMessageContentType.TEXT
else "[image]"
if content.type == PromptMessageContentType.IMAGE
else "[file]"
for content in prompt_message.content
]
)
return prompt_messages
def _organize_prompt_messages(
self,
current_thoughts: list[PromptMessage],
history_prompt_messages: list[PromptMessage],
) -> list[PromptMessage]:
prompt_template = ""
history_prompt_messages = self._init_system_message(
prompt_template, history_prompt_messages
)
query_prompt_messages = self._organize_user_query(self.query or "", [])
prompt_messages = [
*history_prompt_messages,
*query_prompt_messages,
*current_thoughts,
]
if len(current_thoughts) != 0:
# clear messages after the first iteration
prompt_messages = self._clear_user_prompt_image_messages(prompt_messages)
return prompt_messages

@ -0,0 +1,58 @@
identity:
name: function_calling
author: Dify
label:
en_US: FunctionCalling
zh_Hans: FunctionCalling
pt_BR: FunctionCalling
description:
en_US: Function Calling is a basic strategy for agent, model will use the tools provided to perform the task.
zh_Hans: Function Calling 是一个基本的 Agent 策略,模型将使用提供的工具来执行任务。
pt_BR: Function Calling is a basic strategy for agent, model will use the tools provided to perform the task.
parameters:
- name: model
type: model-selector
scope: tool-call&llm
required: true
label:
en_US: Model
zh_Hans: 模型
pt_BR: Model
- name: tools
type: array[tools]
required: true
label:
en_US: Tool list
zh_Hans: 工具列表
pt_BR: Tool list
- name: instruction
type: string
required: true
label:
en_US: Instruction
zh_Hans: 指令
pt_BR: Instruction
auto_generate:
type: prompt_instruction
template:
enabled: true
- name: query
type: string
required: true
label:
en_US: Query
zh_Hans: 查询
pt_BR: Query
- name: maximum_iterations
type: number
required: true
label:
en_US: Maximum Iterations
zh_Hans: 最大迭代次数
pt_BR: Maximum Iterations
default: 3
max: 30
min: 1
extra:
python:
source: strategies/function_calling.py

@ -0,0 +1,4 @@
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=********-****-****-****-************

@ -0,0 +1,7 @@
# Overview
DeepSeek provides advanced AI capabilities for chats and completions. This plugin enables developers to integrate DeepSeek's models, including text generation (deepseek-chat and deepseek-code) via the API.
# Configure
After installation, you need to get API keys from [Deepseek](https://platform.deepseek.com/api_keys) and setup in Settings -> Model Provider.
![](_assets/deepseek.PNG)

@ -0,0 +1,22 @@
<svg width="195.000000" height="41.359375" viewBox="0 0 195 41.3594" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip30_2029">
<rect id="_图层_1" width="134.577469" height="25.511124" transform="translate(60.422485 10.022217)" fill="white"/>
</clipPath>
</defs>
<g clip-path="url(#clip30_2029)">
<path id="path" d="M119.508 30.113L117.562 30.113L117.562 27.0967L119.508 27.0967C120.713 27.0967 121.931 26.7961 122.715 25.9614C123.5 25.1265 123.796 23.8464 123.796 22.5664C123.796 21.2864 123.512 20.0063 122.715 19.1716C121.919 18.3369 120.713 18.0364 119.508 18.0364C118.302 18.0364 117.085 18.3369 116.3 19.1716C115.515 20.0063 115.219 21.2864 115.219 22.5664L115.219 34.9551L111.806 34.9551L111.806 15.031L115.219 15.031L115.219 16.2998L115.845 16.2998C115.913 16.2219 115.981 16.1553 116.049 16.0884C116.903 15.3093 118.211 15.031 119.496 15.031C121.51 15.031 123.523 15.532 124.843 16.9233C126.162 18.3145 126.629 20.4517 126.629 22.5776C126.629 24.7036 126.151 26.8296 124.843 28.2319C123.535 29.6345 121.51 30.113 119.508 30.113Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M67.5664 15.5654L69.5117 15.5654L69.5117 18.5818L67.5664 18.5818C66.3606 18.5818 65.1434 18.8823 64.3585 19.717C63.5736 20.552 63.2778 21.832 63.2778 23.1121C63.2778 24.3921 63.5623 25.6721 64.3585 26.5068C65.1548 27.3418 66.3606 27.6423 67.5664 27.6423C68.7722 27.6423 69.9895 27.3418 70.7744 26.5068C71.5593 25.6721 71.8551 24.3921 71.8551 23.1121L71.8551 10.7124L75.2677 10.7124L75.2677 30.6475L71.8551 30.6475L71.8551 29.3787L71.2294 29.3787C71.1611 29.4565 71.0929 29.5234 71.0247 29.5901C70.1715 30.3691 68.8633 30.6475 67.5779 30.6475C65.5643 30.6475 63.5509 30.1467 62.2313 28.7554C60.9117 27.364 60.4453 25.2268 60.4453 23.1008C60.4453 20.9749 60.9231 18.8489 62.2313 17.4465C63.5509 16.0552 65.5643 15.5654 67.5664 15.5654Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M92.3881 22.845L92.3881 24.0581L83.299 24.0581L83.299 21.6428L89.328 21.6428C89.1914 20.7634 88.8729 19.9397 88.3042 19.3386C87.4851 18.4705 86.2224 18.1589 84.9711 18.1589C83.7198 18.1589 82.4572 18.4705 81.6381 19.3386C80.819 20.2068 80.5232 21.5315 80.5232 22.845C80.5232 24.1582 80.819 25.4939 81.6381 26.3511C82.4572 27.208 83.7198 27.531 84.9711 27.531C86.2224 27.531 87.4851 27.2192 88.3042 26.3511C88.418 26.2285 88.5203 26.095 88.6227 25.9614L91.9899 25.9614C91.6941 27.0078 91.2277 27.9539 90.5225 28.6885C89.1573 30.1243 87.0529 30.6475 84.9711 30.6475C82.8894 30.6475 80.7849 30.1355 79.4198 28.6885C78.0547 27.2415 77.5542 25.0376 77.5542 22.845C77.5542 20.6521 78.0433 18.437 79.4198 17.0012C80.7963 15.5654 82.8894 15.0422 84.9711 15.0422C87.0529 15.0422 89.1573 15.5542 90.5225 17.0012C91.8988 18.4482 92.3881 20.6521 92.3881 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M109.52 22.845L109.52 24.0581L100.431 24.0581L100.431 21.6428L106.46 21.6428C106.323 20.7634 106.005 19.9397 105.436 19.3386C104.617 18.4705 103.354 18.1589 102.103 18.1589C100.852 18.1589 99.5889 18.4705 98.7698 19.3386C97.9507 20.2068 97.6549 21.5315 97.6549 22.845C97.6549 24.1582 97.9507 25.4939 98.7698 26.3511C99.5889 27.208 100.852 27.531 102.103 27.531C103.354 27.531 104.617 27.2192 105.436 26.3511C105.55 26.2285 105.652 26.095 105.754 25.9614L109.122 25.9614C108.826 27.0078 108.359 27.9539 107.654 28.6885C106.289 30.1243 104.185 30.6475 102.103 30.6475C100.021 30.6475 97.9166 30.1355 96.5515 28.6885C95.1864 27.2415 94.6859 25.0376 94.6859 22.845C94.6859 20.6521 95.175 18.437 96.5515 17.0012C97.928 15.5654 100.021 15.0422 102.103 15.0422C104.185 15.0422 106.289 15.5542 107.654 17.0012C109.031 18.4482 109.52 20.6521 109.52 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M136.355 30.6475C138.437 30.6475 140.541 30.3469 141.906 29.49C143.271 28.6328 143.772 27.3306 143.772 26.0393C143.772 24.7483 143.282 23.4348 141.906 22.5889C140.541 21.7429 138.437 21.4312 136.355 21.4312C135.467 21.4312 134.648 21.3088 134.068 20.9861C133.488 20.6521 133.272 20.1511 133.272 19.6504C133.272 19.1494 133.477 18.6375 134.068 18.3147C134.648 17.9807 135.547 17.8694 136.434 17.8694C137.322 17.8694 138.22 17.9919 138.801 18.3147C139.381 18.6487 139.597 19.1494 139.597 19.6504L143.066 19.6504C143.066 18.3591 142.623 17.0457 141.383 16.2C140.143 15.354 138.243 15.0422 136.355 15.0422C134.466 15.0422 132.567 15.3428 131.327 16.2C130.087 17.0569 129.643 18.3591 129.643 19.6504C129.643 20.9414 130.087 22.2549 131.327 23.1008C132.567 23.9468 134.466 24.2585 136.355 24.2585C137.333 24.2585 138.414 24.3809 139.062 24.7036C139.711 25.0266 139.938 25.5386 139.938 26.0393C139.938 26.5403 139.711 27.0522 139.062 27.375C138.414 27.6978 137.424 27.8203 136.446 27.8203C135.467 27.8203 134.466 27.6978 133.829 27.375C133.192 27.0522 132.953 26.5403 132.953 26.0393L128.949 26.0393C128.949 27.3306 129.438 28.644 130.815 29.49C132.191 30.3359 134.273 30.6475 136.355 30.6475Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M160.903 22.845L160.903 24.0581L151.814 24.0581L151.814 21.6428L157.843 21.6428C157.707 20.7634 157.388 19.9397 156.82 19.3386C156 18.4705 154.738 18.1589 153.486 18.1589C152.235 18.1589 150.972 18.4705 150.153 19.3386C149.334 20.2068 149.039 21.5315 149.039 22.845C149.039 24.1582 149.334 25.4939 150.153 26.3511C150.972 27.208 152.235 27.531 153.486 27.531C154.738 27.531 156 27.2192 156.82 26.3511C156.933 26.2285 157.036 26.095 157.138 25.9614L160.505 25.9614C160.209 27.0078 159.743 27.9539 159.038 28.6885C157.673 30.1243 155.568 30.6475 153.486 30.6475C151.405 30.6475 149.3 30.1355 147.935 28.6885C146.57 27.2415 146.07 25.0376 146.07 22.845C146.07 20.6521 146.559 18.437 147.935 17.0012C149.312 15.5654 151.405 15.0422 153.486 15.0422C155.568 15.0422 157.673 15.5542 159.038 17.0012C160.414 18.4482 160.903 20.6521 160.903 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<path id="path" d="M178.035 22.845L178.035 24.0581L168.946 24.0581L168.946 21.6428L174.975 21.6428C174.839 20.7634 174.52 19.9397 173.951 19.3386C173.132 18.4705 171.87 18.1589 170.618 18.1589C169.367 18.1589 168.104 18.4705 167.285 19.3386C166.466 20.2068 166.17 21.5315 166.17 22.845C166.17 24.1582 166.466 25.4939 167.285 26.3511C168.104 27.208 169.367 27.531 170.618 27.531C171.87 27.531 173.132 27.2192 173.951 26.3511C174.065 26.2285 174.167 26.095 174.27 25.9614L177.637 25.9614C177.341 27.0078 176.875 27.9539 176.17 28.6885C174.804 30.1243 172.7 30.6475 170.618 30.6475C168.536 30.6475 166.432 30.1355 165.067 28.6885C163.702 27.2415 163.201 25.0376 163.201 22.845C163.201 20.6521 163.69 18.437 165.067 17.0012C166.443 15.5654 168.536 15.0422 170.618 15.0422C172.7 15.0422 174.804 15.5542 176.17 17.0012C177.546 18.4482 178.035 20.6521 178.035 22.845Z" fill-rule="nonzero" fill="#4D6BFE"/>
<rect id="rect" x="180.321533" y="10.022217" width="3.412687" height="20.625223" fill="#4D6BFE"/>
<path id="polygon" d="M189.559 22.3772L195.155 30.6475L190.935 30.6475L185.338 22.3772L190.935 15.7322L195.155 15.7322L189.559 22.3772Z" fill-rule="nonzero" fill="#4D6BFE"/>
</g>
<path id="path" d="M55.6128 3.47119C55.0175 3.17944 54.7611 3.73535 54.413 4.01782C54.2939 4.10889 54.1932 4.22729 54.0924 4.33667C53.2223 5.26587 52.2057 5.87646 50.8776 5.80347C48.9359 5.69409 47.2781 6.30469 45.8126 7.78979C45.5012 5.9585 44.4663 4.86499 42.8909 4.16357C42.0667 3.79907 41.2332 3.43457 40.6561 2.64185C40.2532 2.07715 40.1432 1.44849 39.9418 0.828857C39.8135 0.455322 39.6853 0.0725098 39.2548 0.00878906C38.7877 -0.0639648 38.6045 0.327637 38.4213 0.655762C37.6886 1.99512 37.4047 3.47119 37.4321 4.96533C37.4962 8.32739 38.9159 11.0059 41.7369 12.9102C42.0575 13.1289 42.1399 13.3474 42.0392 13.6665C41.8468 14.3225 41.6178 14.9602 41.4164 15.6162C41.2881 16.0354 41.0957 16.1265 40.647 15.9441C39.0991 15.2974 37.7618 14.3406 36.5803 13.1836C34.5745 11.2429 32.761 9.10181 30.4988 7.42529C29.9675 7.03345 29.4363 6.66919 28.8867 6.32275C26.5786 4.08154 29.189 2.24097 29.7935 2.02246C30.4254 1.79468 30.0133 1.01099 27.9708 1.02026C25.9283 1.0293 24.0599 1.71265 21.6786 2.62378C21.3306 2.7605 20.9641 2.8606 20.5886 2.94263C18.4271 2.53271 16.1831 2.44141 13.8384 2.70581C9.42371 3.19775 5.89758 5.28418 3.30554 8.84668C0.191406 13.1289 -0.54126 17.9941 0.356323 23.0691C1.29968 28.4172 4.02905 32.8452 8.22388 36.3076C12.5745 39.8972 17.5845 41.6558 23.2997 41.3186C26.771 41.1182 30.6361 40.6536 34.9958 36.9636C36.0948 37.5103 37.2489 37.7288 39.1632 37.8928C40.6378 38.0295 42.0575 37.8201 43.1565 37.5923C44.8784 37.2278 44.7594 35.6333 44.1366 35.3418C39.09 32.9912 40.1981 33.9478 39.1907 33.1733C41.7552 30.1394 45.6204 26.9868 47.1316 16.7732C47.2506 15.9624 47.1499 15.4521 47.1316 14.7961C47.1224 14.3953 47.214 14.2405 47.672 14.1948C48.9359 14.0491 50.1632 13.7029 51.2898 13.0833C54.5596 11.2976 55.8784 8.36377 56.1898 4.84692C56.2357 4.30933 56.1807 3.75342 55.6128 3.47119ZM27.119 35.123C22.2281 31.2783 19.856 30.0117 18.8759 30.0664C17.96 30.1211 18.1249 31.1689 18.3263 31.8523C18.537 32.5264 18.8118 32.9912 19.1964 33.5833C19.462 33.9751 19.6453 34.5581 18.9309 34.9956C17.3555 35.9705 14.6169 34.6675 14.4886 34.6038C11.3014 32.7268 8.63611 30.2485 6.75842 26.8594C4.94495 23.5974 3.89172 20.0989 3.71765 16.3633C3.67188 15.4614 3.9375 15.1423 4.83508 14.9785C6.0166 14.7598 7.23474 14.7141 8.41626 14.8872C13.408 15.6162 17.6577 17.8484 21.2206 21.3835C23.2539 23.397 24.7926 25.8025 26.3772 28.1531C28.0624 30.6494 29.8759 33.0276 32.184 34.9773C32.9991 35.6606 33.6494 36.1799 34.2722 36.5627C32.3947 36.7722 29.2622 36.8179 27.119 35.123ZM29.4637 20.0442C29.4637 19.6433 29.7843 19.3245 30.1874 19.3245C30.2789 19.3245 30.3613 19.3425 30.4346 19.3699C30.5354 19.4065 30.627 19.4612 30.7002 19.543C30.8285 19.6707 30.9017 19.8528 30.9017 20.0442C30.9017 20.4451 30.5812 20.7639 30.1782 20.7639C29.7751 20.7639 29.4637 20.4451 29.4637 20.0442ZM36.7452 23.7798C36.2781 23.9712 35.811 24.135 35.3622 24.1533C34.6661 24.1897 33.9059 23.9072 33.4938 23.561C32.8527 23.0234 32.3947 22.7229 32.2023 21.7844C32.1199 21.3835 32.1656 20.7639 32.239 20.4087C32.4038 19.6433 32.2206 19.1514 31.6803 18.7048C31.2406 18.3403 30.6819 18.2402 30.0682 18.2402C29.8392 18.2402 29.6287 18.1399 29.4729 18.0579C29.2164 17.9304 29.0059 17.6116 29.2073 17.2197C29.2714 17.0923 29.5829 16.7825 29.6561 16.7278C30.4896 16.2539 31.4513 16.4089 32.3397 16.7642C33.1641 17.1013 33.7869 17.7209 34.6844 18.5955C35.6003 19.6523 35.7651 19.9441 36.2872 20.7366C36.6995 21.3562 37.075 21.9939 37.3314 22.7229C37.4871 23.1785 37.2856 23.552 36.7452 23.7798Z" fill-rule="nonzero" fill="#4D6BFE"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,3 @@
<svg width="60" height="50" viewBox="0 0 60 50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path id="path" d="M55.613,3.471C55.018,3.179 54.761,3.735 54.413,4.018C54.294,4.109 54.193,4.227 54.092,4.337C53.222,5.266 52.206,5.876 50.878,5.803C48.936,5.694 47.278,6.305 45.813,7.79C45.501,5.959 44.466,4.865 42.891,4.164C42.067,3.799 41.233,3.435 40.656,2.642C40.253,2.077 40.143,1.448 39.942,0.829C39.814,0.455 39.685,0.073 39.255,0.009C38.788,-0.064 38.605,0.328 38.421,0.656C37.689,1.995 37.405,3.471 37.432,4.965C37.496,8.327 38.916,11.006 41.737,12.91C42.058,13.129 42.14,13.347 42.039,13.666C41.847,14.323 41.618,14.96 41.416,15.616C41.288,16.035 41.096,16.127 40.647,15.944C39.099,15.297 37.762,14.341 36.58,13.184C34.575,11.243 32.761,9.102 30.499,7.425C29.968,7.033 29.436,6.669 28.887,6.323C26.579,4.082 29.189,2.241 29.794,2.022C30.425,1.795 30.013,1.011 27.971,1.02C25.928,1.029 24.06,1.713 21.679,2.624C21.331,2.761 20.964,2.861 20.589,2.943C18.427,2.533 16.183,2.441 13.838,2.706C9.424,3.198 5.898,5.284 3.306,8.847C0.191,13.129 -0.541,17.994 0.356,23.069C1.3,28.417 4.029,32.845 8.224,36.308C12.575,39.897 17.584,41.656 23.3,41.319C26.771,41.118 30.636,40.654 34.996,36.964C36.095,37.51 37.249,37.729 39.163,37.893C40.638,38.03 42.058,37.82 43.157,37.592C44.878,37.228 44.759,35.633 44.137,35.342C39.09,32.991 40.198,33.948 39.191,33.173C41.755,30.139 45.62,26.987 47.132,16.773C47.251,15.962 47.15,15.452 47.132,14.796C47.122,14.395 47.214,14.241 47.672,14.195C48.936,14.049 50.163,13.703 51.29,13.083C54.56,11.298 55.878,8.364 56.19,4.847C56.236,4.309 56.181,3.753 55.613,3.471ZM27.119,35.123C22.228,31.278 19.856,30.012 18.876,30.066C17.96,30.121 18.125,31.169 18.326,31.852C18.537,32.526 18.812,32.991 19.196,33.583C19.462,33.975 19.645,34.558 18.931,34.996C17.356,35.971 14.617,34.667 14.489,34.604C11.301,32.727 8.636,30.249 6.758,26.859C4.945,23.597 3.892,20.099 3.718,16.363C3.672,15.461 3.938,15.142 4.835,14.979C6.017,14.76 7.235,14.714 8.416,14.887C13.408,15.616 17.658,17.848 21.221,21.384C23.254,23.397 24.793,25.802 26.377,28.153C28.062,30.649 29.876,33.028 32.184,34.977C32.999,35.661 33.649,36.18 34.272,36.563C32.395,36.772 29.262,36.818 27.119,35.123ZM29.464,20.044C29.464,19.643 29.784,19.325 30.187,19.325C30.279,19.325 30.361,19.343 30.435,19.37C30.535,19.407 30.627,19.461 30.7,19.543C30.828,19.671 30.902,19.853 30.902,20.044C30.902,20.445 30.581,20.764 30.178,20.764C29.775,20.764 29.464,20.445 29.464,20.044ZM36.745,23.78C36.278,23.971 35.811,24.135 35.362,24.153C34.666,24.19 33.906,23.907 33.494,23.561C32.853,23.023 32.395,22.723 32.202,21.784C32.12,21.384 32.166,20.764 32.239,20.409C32.404,19.643 32.221,19.151 31.68,18.705C31.241,18.34 30.682,18.24 30.068,18.24C29.839,18.24 29.629,18.14 29.473,18.058C29.216,17.93 29.006,17.612 29.207,17.22C29.271,17.092 29.583,16.783 29.656,16.728C30.49,16.254 31.451,16.409 32.34,16.764C33.164,17.101 33.787,17.721 34.684,18.596C35.6,19.652 35.765,19.944 36.287,20.737C36.7,21.356 37.075,21.994 37.331,22.723C37.487,23.179 37.286,23.552 36.745,23.78Z" style="fill:rgb(77,107,254);fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,6 @@
from dify_plugin import Plugin, DifyPluginEnv
plugin = Plugin(DifyPluginEnv())
if __name__ == '__main__':
plugin.run()

@ -0,0 +1,37 @@
author: langgenius
created_at: '2024-09-20T00:13:50.29298939-04:00'
description:
en_US: Models provided by deepseek, such as deepseek-chat、deepseek-coder.
zh_Hans: 深度求索提供的模型,例如 deepseek-chat、deepseek-coder 。
icon: icon_s_en.svg
label:
en_US: DeepSeek
zh_Hans: 深度求索
meta:
arch:
- amd64
- arm64
runner:
entrypoint: main
language: python
version: '3.12'
version: 0.0.1
name: deepseek
plugins:
models:
- provider/deepseek.yaml
resource:
memory: 268435456
permission:
model:
enabled: true
llm: true
moderation: false
rerank: true
speech2text: false
text_embedding: true
tts: false
tool:
enabled: true
type: plugin
version: 0.0.5

@ -0,0 +1,83 @@
model: deepseek-chat
label:
zh_Hans: deepseek-chat
en_US: deepseek-chat
model_type: llm
features:
- agent-thought
- tool-call
- multi-tool-call
- stream-tool-call
model_properties:
mode: chat
context_size: 128000
parameter_rules:
- name: temperature
use_template: temperature
type: float
default: 1
min: 0.0
max: 2.0
help:
zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。
en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is.
- name: max_tokens
use_template: max_tokens
type: int
default: 4096
min: 1
max: 8192
help:
zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。
en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter.
- name: top_p
use_template: top_p
type: float
default: 1
min: 0.01
max: 1.00
help:
zh_Hans: 控制生成结果的随机性。数值越小随机性越弱数值越大随机性越强。一般而言top_p 和 temperature 两个参数选择一个进行调整即可。
en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature.
- name: logprobs
label:
en_US: Logprobs
help:
zh_Hans: 是否返回所输出 token 的对数概率。如果为 true则在 message 的 content 中返回每个输出 token 的对数概率。
en_US: Whether to return the log probability of the output token. If true, returns the log probability of each output token in the content of message .
type: boolean
- name: top_logprobs
label:
en_US: Top Logprobs
type: int
default: 0
min: 0
max: 20
help:
zh_Hans: 一个介于 0 到 20 之间的整数 N指定每个输出位置返回输出概率 top N 的 token且返回这些 token 的对数概率。指定此参数时logprobs 必须为 true。
en_US: An integer N between 0 and 20, specifying that each output position returns the top N tokens with output probability, and returns the logarithmic probability of these tokens. When specifying this parameter, logprobs must be true.
- name: frequency_penalty
use_template: frequency_penalty
default: 0
min: -2.0
max: 2.0
help:
zh_Hans: 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。
en_US: A number between -2.0 and 2.0. If the value is positive, new tokens are penalized based on their frequency of occurrence in existing text, reducing the likelihood that the model will repeat the same content.
- name: response_format
label:
zh_Hans: 回复格式
en_US: Response Format
type: string
help:
zh_Hans: 指定模型必须输出的格式
en_US: specifying the format that the model must output
required: false
options:
- text
- json_object
pricing:
input: "2"
output: "8"
unit: "0.000001"
currency: RMB

@ -0,0 +1,29 @@
model: deepseek-coder
label:
zh_Hans: deepseek-coder
en_US: deepseek-coder
model_type: llm
features:
- agent-thought
- tool-call
- multi-tool-call
- stream-tool-call
model_properties:
mode: chat
context_size: 128000
parameter_rules:
- name: temperature
use_template: temperature
min: 0
max: 1
default: 0.5
- name: top_p
use_template: top_p
min: 0
max: 1
default: 1
- name: max_tokens
use_template: max_tokens
min: 1
max: 4096
default: 1024

@ -0,0 +1,21 @@
model: deepseek-reasoner
label:
zh_Hans: deepseek-reasoner
en_US: deepseek-reasoner
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 128000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "4"
output: "16"
unit: "0.000001"
currency: RMB

@ -0,0 +1,33 @@
from collections.abc import Generator
from typing import Optional, Union
from dify_plugin.entities.model.llm import LLMMode, LLMResult
from dify_plugin.entities.model.message import PromptMessage, PromptMessageTool
from yarl import URL
from dify_plugin import OAICompatLargeLanguageModel
class DeepseekLargeLanguageModel(OAICompatLargeLanguageModel):
def _invoke(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
model_parameters: dict,
tools: Optional[list[PromptMessageTool]] = None,
stop: Optional[list[str]] = None,
stream: bool = True,
user: Optional[str] = None,
) -> Union[LLMResult, Generator]:
self._add_custom_parameters(credentials)
return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream)
def validate_credentials(self, model: str, credentials: dict) -> None:
self._add_custom_parameters(credentials)
super().validate_credentials(model, credentials)
@staticmethod
def _add_custom_parameters(credentials) -> None:
credentials["endpoint_url"] = str(URL(credentials.get("endpoint_url", "https://api.deepseek.com")))
credentials["mode"] = LLMMode.CHAT.value
credentials["function_calling_type"] = "tool_call"
credentials["stream_function_calling"] = "support"

@ -0,0 +1,24 @@
import logging
from dify_plugin.entities.model import ModelType
from dify_plugin.errors.model import CredentialsValidateFailedError
from dify_plugin import ModelProvider
logger = logging.getLogger(__name__)
class DeepSeekProvider(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.LLM)
model_instance.validate_credentials(model="deepseek-chat", 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

@ -0,0 +1,51 @@
background: '#c0cdff'
configurate_methods:
- predefined-model
description:
en_US: Models provided by deepseek, such as deepseek-reasoner、deepseek-chat、deepseek-coder.
zh_Hans: 深度求索提供的模型,例如 deepseek-reasoner、deepseek-chat、deepseek-coder 。
extra:
python:
model_sources:
- models/llm/llm.py
provider_source: provider/deepseek.py
help:
title:
en_US: Get your API Key from deepseek
zh_Hans: 从深度求索获取 API Key
url:
en_US: https://platform.deepseek.com/api_keys
icon_large:
en_US: icon_l_en.svg
icon_small:
en_US: icon_s_en.svg
label:
en_US: deepseek
zh_Hans: 深度求索
models:
llm:
position: models/llm/_position.yaml
predefined:
- models/llm/*.yaml
provider: deepseek
provider_credential_schema:
credential_form_schemas:
- label:
en_US: API Key
placeholder:
en_US: Enter your API Key
zh_Hans: 在此输入您的 API Key
required: true
type: secret-input
variable: api_key
- label:
en_US: Custom API endpoint URL
zh_Hans: 自定义 API endpoint 地址
placeholder:
en_US: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com
zh_Hans: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com
required: false
type: text-input
variable: endpoint_url
supported_model_types:
- llm

@ -0,0 +1,4 @@
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=********-****-****-****-************

@ -0,0 +1,50 @@
## Overview
Ollama is a cross-platform inference framework client (MacOS, Windows, Linux) designed for seamless deployment of large language models (LLMs) such as Llama 2, Mistral, Llava, and more. With its one-click setup, Ollama enables local execution of LLMs, providing enhanced data privacy and security by keeping your data on your own machine.
Dify supports integrating LLM and Text Embedding capabilities of large language models deployed with Ollama.
## Configure
#### 1. Download Ollama
Visit [Ollama download page](https://ollama.com/download) to download the Ollama client for your system.
#### 2. Run Ollama and Chat with Llava
````
ollama run llama3.2
````
After successful launch, Ollama starts an API service on local port 11434, which can be accessed at `http://localhost:11434`.
For other models, visit [Ollama Models](https://ollama.com/library) for more details.
#### 3. Install Ollama Plugin
Go to the Dify marketplace and search the Ollama to download it.
![](./_assets/ollama-01.png)
#### 4. Integrate Ollama in Dify
In `Settings > Model Providers > Ollama`, fill in:
![](./_assets/ollama-02.png)
- Model Name`llama3.2`
- Base URL: `http://<your-ollama-endpoint-domain>:11434`
- Enter the base URL where the Ollama service is accessible.
- If Dify is deployed using Docker, consider using the local network IP address, e.g., `http://192.168.1.100:11434` or `http://host.docker.internal:11434` to access the service.
- For local source code deployment, use `http://localhost:11434`.
- Model Type: `Chat`
- Model Context Length: `4096`
- The maximum context length of the model. If unsure, use the default value of 4096.
- Maximum Token Limit: `4096`
- The maximum number of tokens returned by the model. If there are no specific requirements for the model, this can be consistent with the model context length.
- Support for Vision: `Yes`
- Check this option if the model supports image understanding (multimodal), like `llava`.
Click "Save" to use the model in the application after verifying that there are no errors.
The integration method for Embedding models is similar to LLM, just change the model type to Text Embedding.
For more detail, please check [Dify's official document](https://docs.dify.ai/development/models-integration/ollama).

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

@ -0,0 +1,6 @@
from dify_plugin import Plugin, DifyPluginEnv
plugin = Plugin(DifyPluginEnv())
if __name__ == '__main__':
plugin.run()

@ -0,0 +1,35 @@
author: langgenius
created_at: '2024-09-20T00:13:50.29298939-04:00'
description:
en_US: Ollama
icon: icon_s_en.svg
label:
en_US: Ollama
meta:
arch:
- amd64
- arm64
runner:
entrypoint: main
language: python
version: '3.12'
version: 0.0.1
name: ollama
plugins:
models:
- provider/ollama.yaml
resource:
memory: 268435456
permission:
model:
enabled: true
llm: true
moderation: false
rerank: true
speech2text: false
text_embedding: true
tts: false
tool:
enabled: true
type: plugin
version: 0.0.3

@ -0,0 +1,743 @@
import json
import logging
import re
from collections.abc import Generator
from decimal import Decimal
from typing import Any, Optional, Union, cast
from urllib.parse import urljoin
import requests
from dify_plugin.entities.model import (
AIModelEntity,
DefaultParameterName,
FetchFrom,
I18nObject,
ModelFeature,
ModelPropertyKey,
ModelType,
ParameterRule,
ParameterType,
PriceConfig,
)
from dify_plugin.entities.model.llm import (
LLMMode,
LLMResult,
LLMResultChunk,
LLMResultChunkDelta,
)
from dify_plugin.entities.model.message import (
AssistantPromptMessage,
ImagePromptMessageContent,
PromptMessage,
PromptMessageContentType,
PromptMessageTool,
SystemPromptMessage,
TextPromptMessageContent,
ToolPromptMessage,
UserPromptMessage,
)
from dify_plugin.errors.model import (
CredentialsValidateFailedError,
InvokeAuthorizationError,
InvokeBadRequestError,
InvokeConnectionError,
InvokeError,
InvokeRateLimitError,
InvokeServerUnavailableError,
)
from dify_plugin.interfaces.model.large_language_model import LargeLanguageModel
logger = logging.getLogger(__name__)
class OllamaLargeLanguageModel(LargeLanguageModel):
"""
Model class for Ollama large language model.
"""
def _invoke(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
model_parameters: dict,
tools: Optional[list[PromptMessageTool]] = None,
stop: Optional[list[str]] = None,
stream: bool = True,
user: Optional[str] = None,
) -> Union[LLMResult, Generator]:
"""
Invoke large language model
:param model: model name
:param credentials: model credentials
:param prompt_messages: prompt messages
:param model_parameters: model parameters
:param tools: tools for tool calling
:param stop: stop words
:param stream: is stream response
:param user: unique user id
:return: full response or stream response chunk generator result
"""
return self._generate(
model=model,
credentials=credentials,
prompt_messages=prompt_messages,
model_parameters=model_parameters,
tools=tools,
stop=stop,
stream=stream,
user=user,
)
def get_num_tokens(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
tools: Optional[list[PromptMessageTool]] = None,
) -> int:
"""
Get number of tokens for given prompt messages
:param model: model name
:param credentials: model credentials
:param prompt_messages: prompt messages
:param tools: tools for tool calling
:return:
"""
model_mode = self.get_model_mode(model, credentials)
if model_mode == LLMMode.CHAT:
return self._num_tokens_from_messages(prompt_messages)
else:
first_prompt_message = prompt_messages[0]
if isinstance(first_prompt_message.content, str):
text = first_prompt_message.content
elif isinstance(first_prompt_message.content, list):
text = ""
for message_content in first_prompt_message.content:
if message_content.type == PromptMessageContentType.TEXT:
message_content = cast(
TextPromptMessageContent, message_content
)
text = message_content.data
break
return self._get_num_tokens_by_gpt2(text)
def validate_credentials(self, model: str, credentials: dict) -> None:
"""
Validate model credentials
:param model: model name
:param credentials: model credentials
:return:
"""
try:
self._generate(
model=model,
credentials=credentials,
prompt_messages=[UserPromptMessage(content="ping")],
model_parameters={"num_predict": 5},
stream=False,
)
except InvokeError as ex:
raise CredentialsValidateFailedError(
f"An error occurred during credentials validation: {ex.description}"
)
except Exception as ex:
raise CredentialsValidateFailedError(
f"An error occurred during credentials validation: {str(ex)}"
)
def _generate(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
model_parameters: dict,
tools: Optional[list[PromptMessageTool]] = None,
stop: Optional[list[str]] = None,
stream: bool = True,
user: Optional[str] = None,
) -> Union[LLMResult, Generator]:
"""
Invoke llm completion model
:param model: model name
:param credentials: credentials
:param prompt_messages: prompt messages
:param model_parameters: model parameters
:param stop: stop words
:param stream: is stream response
:param user: unique user id
:return: full response or stream response chunk generator result
"""
headers = {"Content-Type": "application/json"}
endpoint_url = credentials["base_url"]
if not endpoint_url.endswith("/"):
endpoint_url += "/"
data = {"model": model, "stream": stream}
if "format" in model_parameters:
data["format"] = model_parameters["format"]
del model_parameters["format"]
if "keep_alive" in model_parameters:
data["keep_alive"] = model_parameters["keep_alive"]
del model_parameters["keep_alive"]
data["options"] = model_parameters or {}
if stop:
data["options"]["stop"] = stop
completion_type = LLMMode.value_of(credentials["mode"])
if completion_type is LLMMode.CHAT:
endpoint_url = urljoin(endpoint_url, "api/chat")
data["messages"] = [
self._convert_prompt_message_to_dict(m) for m in prompt_messages
]
if tools:
data["tools"] = [
self._convert_prompt_message_tool_to_dict(tool) for tool in tools
]
else:
endpoint_url = urljoin(endpoint_url, "api/generate")
first_prompt_message = prompt_messages[0]
if isinstance(first_prompt_message, UserPromptMessage):
first_prompt_message = cast(UserPromptMessage, first_prompt_message)
if isinstance(first_prompt_message.content, str):
data["prompt"] = first_prompt_message.content
elif isinstance(first_prompt_message.content, list):
text = ""
images = []
for message_content in first_prompt_message.content:
if message_content.type == PromptMessageContentType.TEXT:
message_content = cast(
TextPromptMessageContent, message_content
)
text = message_content.data
elif message_content.type == PromptMessageContentType.IMAGE:
message_content = cast(
ImagePromptMessageContent, message_content
)
image_data = re.sub(
"^data:image\\/[a-zA-Z]+;base64,",
"",
message_content.data,
)
images.append(image_data)
data["prompt"] = text
data["images"] = images
response = requests.post(
endpoint_url, headers=headers, json=data, timeout=(10, 300), stream=stream
)
response.encoding = "utf-8"
if response.status_code != 200:
raise InvokeError(
f"API request failed with status code {response.status_code}: {response.text}"
)
if stream:
return self._handle_generate_stream_response(
model, credentials, completion_type, response, prompt_messages
)
return self._handle_generate_response(
model, credentials, completion_type, response, prompt_messages, tools
)
def _handle_generate_response(
self,
model: str,
credentials: dict,
completion_type: LLMMode,
response: requests.Response,
prompt_messages: list[PromptMessage],
tools: Optional[list[PromptMessageTool]],
) -> LLMResult:
"""
Handle llm completion response
:param model: model name
:param credentials: model credentials
:param completion_type: completion type
:param response: response
:param prompt_messages: prompt messages
:return: llm result
"""
response_json = response.json()
tool_calls = []
if completion_type is LLMMode.CHAT:
message = response_json.get("message", {})
response_content = message.get("content", "")
response_tool_calls = message.get("tool_calls", [])
tool_calls = [
self._extract_response_tool_call(tool_call)
for tool_call in response_tool_calls
]
else:
response_content = response_json["response"]
assistant_message = AssistantPromptMessage(
content=response_content, tool_calls=tool_calls
)
if "prompt_eval_count" in response_json and "eval_count" in response_json:
prompt_tokens = response_json["prompt_eval_count"]
completion_tokens = response_json["eval_count"]
else:
prompt_tokens = self._get_num_tokens_by_gpt2(prompt_messages[0].content)
completion_tokens = self._get_num_tokens_by_gpt2(assistant_message.content)
usage = self._calc_response_usage(
model, credentials, prompt_tokens, completion_tokens
)
result = LLMResult(
model=response_json["model"],
prompt_messages=prompt_messages,
message=assistant_message,
usage=usage,
)
return result
def _handle_generate_stream_response(
self,
model: str,
credentials: dict,
completion_type: LLMMode,
response: requests.Response,
prompt_messages: list[PromptMessage],
) -> Generator:
"""
Handle llm completion stream response
:param model: model name
:param credentials: model credentials
:param completion_type: completion type
:param response: response
:param prompt_messages: prompt messages
:return: llm response chunk generator result
"""
full_text = ""
chunk_index = 0
def create_final_llm_result_chunk(
index: int, message: AssistantPromptMessage, finish_reason: str
) -> LLMResultChunk:
prompt_tokens = self._get_num_tokens_by_gpt2(prompt_messages[0].content)
completion_tokens = self._get_num_tokens_by_gpt2(full_text)
usage = self._calc_response_usage(
model, credentials, prompt_tokens, completion_tokens
)
return LLMResultChunk(
model=model,
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=index,
message=message,
finish_reason=finish_reason,
usage=usage,
),
)
for chunk in response.iter_lines(decode_unicode=True, delimiter="\n"):
if not chunk:
continue
try:
chunk_json = json.loads(chunk)
except json.JSONDecodeError as e:
yield create_final_llm_result_chunk(
index=chunk_index,
message=AssistantPromptMessage(content=""),
finish_reason="Non-JSON encountered.",
)
chunk_index += 1
break
if completion_type is LLMMode.CHAT:
if not chunk_json:
continue
if "message" not in chunk_json:
text = ""
else:
text = chunk_json.get("message").get("content", "")
else:
if not chunk_json:
continue
text = chunk_json["response"]
assistant_prompt_message = AssistantPromptMessage(content=text)
full_text += text
if chunk_json["done"]:
if "prompt_eval_count" in chunk_json:
prompt_tokens = chunk_json["prompt_eval_count"]
else:
prompt_message_content = prompt_messages[0].content
if isinstance(prompt_message_content, str):
prompt_tokens = self._get_num_tokens_by_gpt2(
prompt_message_content
)
elif isinstance(prompt_message_content, list):
content_text = ""
for message_content in prompt_message_content:
if message_content.type == PromptMessageContentType.TEXT:
message_content = cast(
TextPromptMessageContent, message_content
)
content_text += message_content.data
prompt_tokens = self._get_num_tokens_by_gpt2(content_text)
completion_tokens = chunk_json.get(
"eval_count", self._get_num_tokens_by_gpt2(full_text)
)
usage = self._calc_response_usage(
model, credentials, prompt_tokens, completion_tokens
)
yield LLMResultChunk(
model=chunk_json["model"],
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=chunk_index,
message=assistant_prompt_message,
finish_reason="stop",
usage=usage,
),
)
else:
yield LLMResultChunk(
model=chunk_json["model"],
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=chunk_index, message=assistant_prompt_message
),
)
chunk_index += 1
def _convert_prompt_message_tool_to_dict(self, tool: PromptMessageTool) -> dict:
"""
Convert PromptMessageTool to dict for Ollama API
:param tool: tool
:return: tool dict
"""
return {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.parameters,
},
}
def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict:
"""
Convert PromptMessage to dict for Ollama API
:param message: prompt message
:return: message dict
"""
if isinstance(message, UserPromptMessage):
message = cast(UserPromptMessage, message)
if isinstance(message.content, str):
message_dict = {"role": "user", "content": message.content}
elif isinstance(message.content, list):
text = ""
images = []
for message_content in message.content:
if message_content.type == PromptMessageContentType.TEXT:
message_content = cast(
TextPromptMessageContent, message_content
)
text = message_content.data
elif message_content.type == PromptMessageContentType.IMAGE:
message_content = cast(
ImagePromptMessageContent, message_content
)
image_data = re.sub(
"^data:image\\/[a-zA-Z]+;base64,", "", message_content.data
)
images.append(image_data)
message_dict = {"role": "user", "content": text, "images": images}
elif isinstance(message, AssistantPromptMessage):
message = cast(AssistantPromptMessage, message)
message_dict = {"role": "assistant", "content": message.content}
elif isinstance(message, SystemPromptMessage):
message = cast(SystemPromptMessage, message)
message_dict = {"role": "system", "content": message.content}
elif isinstance(message, ToolPromptMessage):
message = cast(ToolPromptMessage, message)
message_dict = {"role": "tool", "content": message.content}
else:
raise ValueError(f"Got unknown type {message}")
return message_dict
def _num_tokens_from_messages(self, messages: list[PromptMessage]) -> int:
"""
Calculate num tokens.
:param messages: messages
"""
num_tokens = 0
messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages]
for message in messages_dict:
for key, value in message.items():
num_tokens += self._get_num_tokens_by_gpt2(str(key))
num_tokens += self._get_num_tokens_by_gpt2(str(value))
return num_tokens
def _extract_response_tool_call(
self, response_tool_call: dict
) -> AssistantPromptMessage.ToolCall:
"""
Extract response tool call
"""
tool_call = None
if response_tool_call and "function" in response_tool_call:
arguments = response_tool_call.get("function", {}).get("arguments")
if isinstance(arguments, dict):
arguments = json.dumps(arguments)
function = AssistantPromptMessage.ToolCall.ToolCallFunction(
name=response_tool_call.get("function", {}).get("name"),
arguments=arguments,
)
tool_call = AssistantPromptMessage.ToolCall(
id=response_tool_call.get("function", {}).get("name"),
type="function",
function=function,
)
return tool_call
def get_customizable_model_schema(
self, model: str, credentials: dict
) -> AIModelEntity:
"""
Get customizable model schema.
:param model: model name
:param credentials: credentials
:return: model schema
"""
extras: dict[str, Any] = {"features": []}
if "vision_support" in credentials and credentials["vision_support"] == "true":
extras["features"].append(ModelFeature.VISION)
if (
"function_call_support" in credentials
and credentials["function_call_support"] == "true"
):
extras["features"].append(ModelFeature.TOOL_CALL)
extras["features"].append(ModelFeature.MULTI_TOOL_CALL)
entity = AIModelEntity(
model=model,
label=I18nObject(zh_Hans=model, en_US=model),
model_type=ModelType.LLM,
fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
model_properties={
ModelPropertyKey.MODE: credentials.get("mode"),
ModelPropertyKey.CONTEXT_SIZE: int(
credentials.get("context_size", 4096)
),
},
parameter_rules=[
ParameterRule(
name=DefaultParameterName.TEMPERATURE.value,
use_template=DefaultParameterName.TEMPERATURE.value,
label=I18nObject(en_US="Temperature", zh_Hans="温度"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)",
zh_Hans="模型的温度。增加温度将使模型的回答更具创造性。默认值0.8",
),
default=0.1,
min=0,
max=1,
),
ParameterRule(
name=DefaultParameterName.TOP_P.value,
use_template=DefaultParameterName.TOP_P.value,
label=I18nObject(en_US="Top P", zh_Hans="Top P"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)",
zh_Hans="与top-k一起工作。较高的值例如0.95会导致生成更多样化的文本而较低的值例如0.5会生成更专注和保守的文本。默认值0.9",
),
default=0.9,
min=0,
max=1,
),
ParameterRule(
name="top_k",
label=I18nObject(en_US="Top K", zh_Hans="Top K"),
type=ParameterType.INT,
help=I18nObject(
en_US="Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)",
zh_Hans="减少生成无意义内容的可能性。较高的值例如100将提供更多样化的答案而较低的值例如10将更为保守。默认值40",
),
min=1,
max=100,
),
ParameterRule(
name="repeat_penalty",
label=I18nObject(en_US="Repeat Penalty"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)",
zh_Hans="设置对重复内容的惩罚强度。一个较高的值例如1.5会更强地惩罚重复内容而一个较低的值例如0.9则会相对宽容。默认值1.1",
),
min=-2,
max=2,
),
ParameterRule(
name="num_predict",
use_template="max_tokens",
label=I18nObject(en_US="Num Predict", zh_Hans="最大令牌数预测"),
type=ParameterType.INT,
help=I18nObject(
en_US="Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context)",
zh_Hans="生成文本时预测的最大令牌数。默认值128-1 = 无限生成,-2 = 填充上下文)",
),
default=512
if int(credentials.get("max_tokens", 4096)) >= 768
else 128,
min=-2,
max=int(credentials.get("max_tokens", 4096)),
),
ParameterRule(
name="mirostat",
label=I18nObject(
en_US="Mirostat sampling", zh_Hans="Mirostat 采样"
),
type=ParameterType.INT,
help=I18nObject(
en_US="Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0)",
zh_Hans="启用 Mirostat 采样以控制困惑度。默认值00 = 禁用1 = Mirostat2 = Mirostat 2.0",
),
min=0,
max=2,
),
ParameterRule(
name="mirostat_eta",
label=I18nObject(en_US="Mirostat Eta", zh_Hans="学习率"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)",
zh_Hans="影响算法对生成文本反馈响应的速度。较低的学习率会导致调整速度变慢而较高的学习率会使得算法更加灵敏。默认值0.1",
),
precision=1,
),
ParameterRule(
name="mirostat_tau",
label=I18nObject(en_US="Mirostat Tau", zh_Hans="文本连贯度"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)",
zh_Hans="控制输出的连贯性和多样性之间的平衡。较低的值会导致更专注和连贯的文本。默认值5.0",
),
precision=1,
),
ParameterRule(
name="num_ctx",
label=I18nObject(
en_US="Size of context window", zh_Hans="上下文窗口大小"
),
type=ParameterType.INT,
help=I18nObject(
en_US="Sets the size of the context window used to generate the next token. (Default: 2048)",
zh_Hans="设置用于生成下一个标记的上下文窗口大小。默认值2048",
),
default=2048,
min=1,
),
ParameterRule(
name="num_gpu",
label=I18nObject(en_US="GPU Layers", zh_Hans="GPU 层数"),
type=ParameterType.INT,
help=I18nObject(
en_US="The number of layers to offload to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable.As long as a model fits into one gpu it stays in one. It does not set the number of GPU(s). ",
zh_Hans="加载到 GPU 的层数。在 macOS 上,默认为 1 以启用 Metal 支持,设置为 0 则禁用。只要模型适合一个 GPU它就保留在其中。它不设置 GPU 的数量。",
),
min=-1,
default=1,
),
ParameterRule(
name="num_thread",
label=I18nObject(en_US="Num Thread", zh_Hans="线程数"),
type=ParameterType.INT,
help=I18nObject(
en_US="Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores).",
zh_Hans="设置计算过程中使用的线程数。默认情况下Ollama会检测以获得最佳性能。建议将此值设置为系统拥有的物理CPU核心数而不是逻辑核心数",
),
min=1,
),
ParameterRule(
name="repeat_last_n",
label=I18nObject(en_US="Repeat last N", zh_Hans="回溯内容"),
type=ParameterType.INT,
help=I18nObject(
en_US="Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)",
zh_Hans="设置模型回溯多远的内容以防止重复。默认值640 = 禁用,-1 = num_ctx",
),
min=-1,
),
ParameterRule(
name="tfs_z",
label=I18nObject(en_US="TFS Z", zh_Hans="减少标记影响"),
type=ParameterType.FLOAT,
help=I18nObject(
en_US="Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)",
zh_Hans="用于减少输出中不太可能的标记的影响。较高的值例如2.0会更多地减少这种影响而1.0的值则会禁用此设置。默认值1",
),
precision=1,
),
ParameterRule(
name="seed",
label=I18nObject(en_US="Seed", zh_Hans="随机数种子"),
type=ParameterType.INT,
help=I18nObject(
en_US="Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0)",
zh_Hans="设置用于生成的随机数种子。将此设置为特定数字将使模型对相同的提示生成相同的文本。默认值0",
),
),
ParameterRule(
name="keep_alive",
label=I18nObject(en_US="Keep Alive", zh_Hans="模型存活时间"),
type=ParameterType.STRING,
help=I18nObject(
en_US="Sets how long the model is kept in memory after generating a response. This must be a duration string with a unit (e.g., '10m' for 10 minutes or '24h' for 24 hours). A negative number keeps the model loaded indefinitely, and '0' unloads the model immediately after generating a response. Valid time units are 's','m','h'. (Default: 5m)",
zh_Hans="设置模型在生成响应后在内存中保留的时间。这必须是一个带有单位的持续时间字符串(例如,'10m' 表示10分钟'24h' 表示24小时。负数表示无限期地保留模型'0'表示在生成响应后立即卸载模型。有效的时间单位有 's'(秒)、'm'(分钟)、'h'小时默认值5m",
),
),
ParameterRule(
name="format",
label=I18nObject(en_US="Format", zh_Hans="返回格式"),
type=ParameterType.STRING,
help=I18nObject(
en_US="the format to return a response in. Currently the only accepted value is json.",
zh_Hans="返回响应的格式。目前唯一接受的值是json。",
),
options=["json"],
),
],
pricing=PriceConfig(
input=Decimal(credentials.get("input_price", 0)),
output=Decimal(credentials.get("output_price", 0)),
unit=Decimal(credentials.get("unit", 0)),
currency=credentials.get("currency", "USD"),
),
**extras,
)
return entity
@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 {
InvokeAuthorizationError: [requests.exceptions.InvalidHeader],
InvokeBadRequestError: [
requests.exceptions.HTTPError,
requests.exceptions.InvalidURL,
],
InvokeRateLimitError: [requests.exceptions.RetryError],
InvokeServerUnavailableError: [
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
],
InvokeConnectionError: [
requests.exceptions.ConnectTimeout,
requests.exceptions.ReadTimeout,
],
}

@ -0,0 +1,200 @@
import json
import logging
import time
from decimal import Decimal
from typing import Optional
from urllib.parse import urljoin
from dify_plugin import TextEmbeddingModel
import numpy as np
import requests
from dify_plugin.entities.model import (
AIModelEntity,
EmbeddingInputType,
FetchFrom,
I18nObject,
ModelPropertyKey,
ModelType,
PriceConfig,
PriceType,
)
from dify_plugin.entities.model.text_embedding import (
EmbeddingUsage,
TextEmbeddingResult,
)
from dify_plugin.errors.model import (
CredentialsValidateFailedError,
InvokeAuthorizationError,
InvokeBadRequestError,
InvokeConnectionError,
InvokeError,
InvokeRateLimitError,
InvokeServerUnavailableError,
)
logger = logging.getLogger(__name__)
class OllamaEmbeddingModel(TextEmbeddingModel):
"""
Model class for an Ollama text embedding model.
"""
def _invoke(
self,
model: str,
credentials: dict,
texts: list[str],
user: Optional[str] = None,
input_type: EmbeddingInputType = EmbeddingInputType.DOCUMENT,
) -> TextEmbeddingResult:
"""
Invoke text embedding model
:param model: model name
:param credentials: model credentials
:param texts: texts to embed
:param user: unique user id
:param input_type: input type
:return: embeddings result
"""
headers = {"Content-Type": "application/json"}
endpoint_url = credentials.get("base_url", "")
if endpoint_url and not endpoint_url.endswith("/"):
endpoint_url += "/"
endpoint_url = urljoin(endpoint_url, "api/embed")
context_size = self._get_context_size(model, credentials)
inputs = []
used_tokens = 0
for text in texts:
num_tokens = self._get_num_tokens_by_gpt2(text)
if num_tokens >= context_size:
cutoff = int(np.floor(len(text) * (context_size / num_tokens)))
inputs.append(text[0:cutoff])
else:
inputs.append(text)
payload = {"input": inputs, "model": model, "options": {"use_mmap": True}}
response = requests.post(
endpoint_url, headers=headers, data=json.dumps(payload), timeout=(10, 300)
)
response.raise_for_status()
response_data = response.json()
embeddings = response_data["embeddings"]
embedding_used_tokens = self.get_num_tokens(model, credentials, inputs)
used_tokens += sum(embedding_used_tokens)
usage = self._calc_response_usage(
model=model, credentials=credentials, tokens=used_tokens
)
return TextEmbeddingResult(embeddings=embeddings, usage=usage, model=model)
def get_num_tokens(
self, model: str, credentials: dict, texts: list[str]
) -> list[int]:
"""
Approximate number of tokens for given messages using GPT2 tokenizer
:param model: model name
:param credentials: model credentials
:param texts: texts to embed
:return:
"""
return [self._get_num_tokens_by_gpt2(text) for text in texts]
def validate_credentials(self, model: str, credentials: dict) -> None:
"""
Validate model credentials
:param model: model name
:param credentials: model credentials
:return:
"""
try:
self._invoke(model=model, credentials=credentials, texts=["ping"])
except InvokeError as ex:
raise CredentialsValidateFailedError(
f"An error occurred during credentials validation: {ex.description}"
)
except Exception as ex:
raise CredentialsValidateFailedError(
f"An error occurred during credentials validation: {str(ex)}"
)
def get_customizable_model_schema(
self, model: str, credentials: dict
) -> AIModelEntity:
"""
generate custom model entities from credentials
"""
entity = AIModelEntity(
model=model,
label=I18nObject(en_US=model),
model_type=ModelType.TEXT_EMBEDDING,
fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
model_properties={
ModelPropertyKey.CONTEXT_SIZE: int(
credentials.get("context_size", 512)
),
ModelPropertyKey.MAX_CHUNKS: 1,
},
parameter_rules=[],
pricing=PriceConfig(
input=Decimal(credentials.get("input_price", 0)),
unit=Decimal(credentials.get("unit", 0)),
currency=credentials.get("currency", "USD"),
),
)
return entity
def _calc_response_usage(
self, model: str, credentials: dict, tokens: int
) -> EmbeddingUsage:
"""
Calculate response usage
:param model: model name
:param credentials: model credentials
:param tokens: input tokens
:return: usage
"""
input_price_info = self.get_price(
model=model,
credentials=credentials,
price_type=PriceType.INPUT,
tokens=tokens,
)
usage = EmbeddingUsage(
tokens=tokens,
total_tokens=tokens,
unit_price=input_price_info.unit_price,
price_unit=input_price_info.unit,
total_price=input_price_info.total_amount,
currency=input_price_info.currency,
latency=time.perf_counter() - self.started_at,
)
return usage
@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 {
InvokeAuthorizationError: [requests.exceptions.InvalidHeader],
InvokeBadRequestError: [
requests.exceptions.HTTPError,
requests.exceptions.InvalidURL,
],
InvokeRateLimitError: [requests.exceptions.RetryError],
InvokeServerUnavailableError: [
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
],
InvokeConnectionError: [
requests.exceptions.ConnectTimeout,
requests.exceptions.ReadTimeout,
],
}

@ -0,0 +1,15 @@
import logging
from dify_plugin import ModelProvider
logger = logging.getLogger(__name__)
class OpenAIProvider(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`.
"""
pass

@ -0,0 +1,123 @@
background: '#F9FAFB'
configurate_methods:
- customizable-model
extra:
python:
model_sources:
- models/llm/llm.py
- models/text_embedding/text_embedding.py
provider_source: provider/ollama.py
help:
title:
en_US: How to integrate with Ollama
zh_Hans: 如何集成 Ollama
url:
en_US: https://docs.dify.ai/tutorials/model-configuration/ollama
icon_large:
en_US: icon_l_en.svg
icon_small:
en_US: icon_s_en.svg
label:
en_US: Ollama
model_credential_schema:
credential_form_schemas:
- label:
en_US: Base URL
zh_Hans: 基础 URL
placeholder:
en_US: Base url of Ollama server, e.g. http://192.168.1.100:11434
zh_Hans: Ollama server 的基础 URL例如 http://192.168.1.100:11434
required: true
type: text-input
variable: base_url
- default: chat
label:
en_US: Completion mode
zh_Hans: 模型类型
options:
- label:
en_US: Completion
zh_Hans: 补全
value: completion
- label:
en_US: Chat
zh_Hans: 对话
value: chat
placeholder:
en_US: Select completion mode
zh_Hans: 选择对话类型
required: true
show_on:
- value: llm
variable: __model_type
type: select
variable: mode
- default: '4096'
label:
en_US: Model context size
zh_Hans: 模型上下文长度
placeholder:
en_US: Enter your Model context size
zh_Hans: 在此输入您的模型上下文长度
required: true
type: text-input
variable: context_size
- default: '4096'
label:
en_US: Upper bound for max tokens
zh_Hans: 最大 token 上限
required: true
show_on:
- value: llm
variable: __model_type
type: text-input
variable: max_tokens
- default: 'false'
label:
en_US: Vision support
zh_Hans: 是否支持 Vision
options:
- label:
en_US: 'Yes'
zh_Hans:
value: 'true'
- label:
en_US: 'No'
zh_Hans:
value: 'false'
required: false
show_on:
- value: llm
variable: __model_type
type: radio
variable: vision_support
- default: 'false'
label:
en_US: Function call support
zh_Hans: 是否支持函数调用
options:
- label:
en_US: 'Yes'
zh_Hans:
value: 'true'
- label:
en_US: 'No'
zh_Hans:
value: 'false'
required: false
show_on:
- value: llm
variable: __model_type
type: radio
variable: function_call_support
model:
label:
en_US: Model Name
zh_Hans: 模型名称
placeholder:
en_US: Enter your model name
zh_Hans: 输入模型名称
provider: ollama
supported_model_types:
- llm
- text-embedding

@ -0,0 +1,9 @@
## Overview
This plugin provides access to models that are OpenAI-compatible, including LLMs, reranking, text embedding, speech-to-text (STT), and text-to-speech(TTS) models. Developers can easily add models by providing configuration parameters such as the model name and API key.
## Configure
Configure the OpenAI-API-compatible model by providing its core details (Type, Name, API Key, URL) and adjusting further options like completion, context, and token limits, as well as streaming and vision settings. Save when done.
![](./_assets/openai_api_compatible-01.png)

@ -0,0 +1 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-gray-400" data-icon="CubeOutline" aria-hidden="true"><g id="cube-outline"><g id="Solid"><path d="M8.26865 1.29003C8.09143 1.25358 7.90866 1.25358 7.73144 1.29003C7.52659 1.33216 7.3435 1.43471 7.19794 1.51624L7.15826 1.53841L6.17628 2.08395C5.85443 2.26276 5.73846 2.66863 5.91727 2.99049C6.09608 3.31234 6.50195 3.4283 6.82381 3.24949L7.80579 2.70395C7.90681 2.64782 7.95839 2.61946 7.99686 2.60091L8.00004 2.59938L8.00323 2.60091C8.0417 2.61946 8.09327 2.64782 8.1943 2.70395L9.17628 3.24949C9.49814 3.4283 9.90401 3.31234 10.0828 2.99048C10.2616 2.66863 10.1457 2.26276 9.82381 2.08395L8.84183 1.53841L8.80215 1.51624C8.65659 1.43471 8.4735 1.33216 8.26865 1.29003Z" fill="currentColor"></path><path d="M12.8238 3.75062C12.5019 3.57181 12.0961 3.68777 11.9173 4.00963C11.7385 4.33148 11.8544 4.73735 12.1763 4.91616L12.6272 5.16668L12.1763 5.41719C11.8545 5.596 11.7385 6.00186 11.9173 6.32372C12.0961 6.64558 12.502 6.76154 12.8238 6.58273L13.3334 6.29966V6.83339C13.3334 7.20158 13.6319 7.50006 14 7.50006C14.3682 7.50006 14.6667 7.20158 14.6667 6.83339V5.79435L14.6668 5.74627C14.6673 5.62441 14.6678 5.48084 14.6452 5.33482C14.6869 5.17472 14.6696 4.99892 14.5829 4.84286C14.4904 4.6764 14.3371 4.56501 14.1662 4.52099C14.0496 4.43038 13.9239 4.36116 13.8173 4.3024L13.7752 4.27915L12.8238 3.75062Z" fill="currentColor"></path><path d="M3.8238 4.91616C4.14566 4.73735 4.26162 4.33148 4.08281 4.00963C3.90401 3.68777 3.49814 3.57181 3.17628 3.75062L2.22493 4.27915L2.18284 4.3024C2.07615 4.36116 1.95045 4.4304 1.83382 4.52102C1.66295 4.56506 1.50977 4.67643 1.41731 4.84286C1.33065 4.99886 1.31323 5.17459 1.35493 5.33464C1.33229 5.48072 1.33281 5.62436 1.33326 5.74627L1.33338 5.79435V6.83339C1.33338 7.20158 1.63185 7.50006 2.00004 7.50006C2.36823 7.50006 2.66671 7.20158 2.66671 6.83339V6.29961L3.17632 6.58273C3.49817 6.76154 3.90404 6.64558 4.08285 6.32372C4.26166 6.00186 4.1457 5.596 3.82384 5.41719L3.3729 5.16666L3.8238 4.91616Z" fill="currentColor"></path><path d="M2.66671 10.1667C2.66671 9.79853 2.36823 9.50006 2.00004 9.50006C1.63185 9.50006 1.33338 9.79853 1.33338 10.1667V11.2058L1.33326 11.2538C1.33262 11.4298 1.33181 11.6509 1.40069 11.8594C1.46024 12.0397 1.55759 12.2051 1.68622 12.3447C1.835 12.5061 2.02873 12.6128 2.18281 12.6977L2.22493 12.721L3.17628 13.2495C3.49814 13.4283 3.90401 13.3123 4.08281 12.9905C4.26162 12.6686 4.14566 12.2628 3.8238 12.084L2.87245 11.5554C2.76582 11.4962 2.71137 11.4656 2.67318 11.4413L2.66995 11.4392L2.66971 11.4354C2.66699 11.3902 2.66671 11.3277 2.66671 11.2058V10.1667Z" fill="currentColor"></path><path d="M14.6667 10.1667C14.6667 9.79853 14.3682 9.50006 14 9.50006C13.6319 9.50006 13.3334 9.79853 13.3334 10.1667V11.2058C13.3334 11.3277 13.3331 11.3902 13.3304 11.4354L13.3301 11.4392L13.3269 11.4413C13.2887 11.4656 13.2343 11.4962 13.1276 11.5554L12.1763 12.084C11.8544 12.2628 11.7385 12.6686 11.9173 12.9905C12.0961 13.3123 12.5019 13.4283 12.8238 13.2495L13.7752 12.721L13.8172 12.6977C13.9713 12.6128 14.1651 12.5061 14.3139 12.3447C14.4425 12.2051 14.5398 12.0397 14.5994 11.8594C14.6683 11.6509 14.6675 11.4298 14.6668 11.2538L14.6667 11.2058V10.1667Z" fill="currentColor"></path><path d="M6.82381 13.7506C6.50195 13.5718 6.09608 13.6878 5.91727 14.0096C5.73846 14.3315 5.85443 14.7374 6.17628 14.9162L7.15826 15.4617L7.19793 15.4839C7.29819 15.54 7.41625 15.6061 7.54696 15.6556C7.66589 15.7659 7.82512 15.8333 8.00008 15.8333C8.17507 15.8333 8.33431 15.7659 8.45324 15.6556C8.58391 15.6061 8.70193 15.54 8.80215 15.4839L8.84183 15.4617L9.82381 14.9162C10.1457 14.7374 10.2616 14.3315 10.0828 14.0096C9.90401 13.6878 9.49814 13.5718 9.17628 13.7506L8.66675 14.0337V13.5C8.66675 13.1318 8.36827 12.8333 8.00008 12.8333C7.63189 12.8333 7.33341 13.1318 7.33341 13.5V14.0337L6.82381 13.7506Z" fill="currentColor"></path><path d="M6.82384 7.08385C6.50199 6.90505 6.09612 7.02101 5.91731 7.34286C5.7385 7.66472 5.85446 8.07059 6.17632 8.2494L7.33341 8.89223V10.1666C7.33341 10.5348 7.63189 10.8333 8.00008 10.8333C8.36827 10.8333 8.66675 10.5348 8.66675 10.1666V8.89223L9.82384 8.2494C10.1457 8.07059 10.2617 7.66472 10.0829 7.34286C9.90404 7.02101 9.49817 6.90505 9.17632 7.08385L8.00008 7.73732L6.82384 7.08385Z" fill="currentColor"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,6 @@
from dify_plugin import Plugin, DifyPluginEnv
plugin = Plugin(DifyPluginEnv())
if __name__ == "__main__":
plugin.run()

@ -0,0 +1,31 @@
version: 0.0.11
type: plugin
author: "langgenius"
name: "openai_api_compatible"
description:
en_US: Model providers compatible with OpenAI's API standard, such as LM Studio.
zh_Hans: 兼容 OpenAI API 的模型供应商,例如 LM Studio 。
label:
en_US: "OpenAI-API-compatible"
created_at: "2024-07-12T08:03:44.658609186Z"
icon: icon.svg
resource:
memory: 1048576
permission:
tool:
enabled: true
model:
enabled: true
llm: true
plugins:
models:
- "provider/openai_api_compatible.yaml"
meta:
version: 0.0.1
arch:
- "amd64"
- "arm64"
runner:
language: "python"
version: "3.12"
entrypoint: "main"

@ -0,0 +1,53 @@
from collections.abc import Mapping
import openai
from httpx import Timeout
from dify_plugin.errors.model import InvokeAuthorizationError, InvokeBadRequestError, InvokeConnectionError, InvokeError, InvokeRateLimitError, InvokeServerUnavailableError
class _CommonOpenAI:
def _to_credential_kwargs(self, credentials: Mapping) -> dict:
"""
Transform credentials to kwargs for model instance
:param credentials:
:return:
"""
credentials_kwargs = {
"api_key": credentials['openai_api_key'],
"timeout": Timeout(315.0, read=300.0, write=10.0, connect=5.0),
"max_retries": 1,
}
if credentials.get("openai_api_base"):
openai_api_base = credentials["openai_api_base"].rstrip("/")
credentials_kwargs["base_url"] = openai_api_base + "/v1"
if 'openai_organization' in credentials:
credentials_kwargs['organization'] = credentials['openai_organization']
return credentials_kwargs
@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: [openai.APIConnectionError, openai.APITimeoutError],
InvokeServerUnavailableError: [openai.InternalServerError],
InvokeRateLimitError: [openai.RateLimitError],
InvokeAuthorizationError: [openai.AuthenticationError, openai.PermissionDeniedError],
InvokeBadRequestError: [
openai.BadRequestError,
openai.NotFoundError,
openai.UnprocessableEntityError,
openai.APIError,
],
}

@ -0,0 +1,31 @@
from typing import Mapping
from dify_plugin.entities.model import (
AIModelEntity,
I18nObject,
ModelFeature
)
from dify_plugin.interfaces.model.openai_compatible.llm import (
OAICompatLargeLanguageModel,
)
class OpenAILargeLanguageModel(OAICompatLargeLanguageModel):
def get_customizable_model_schema(self, model: str, credentials: Mapping) -> AIModelEntity:
entity = super().get_customizable_model_schema(model, credentials)
agent_though_support = credentials.get("agent_though_support", "not_supported")
if agent_though_support == "supported":
try:
entity.features.index(ModelFeature.AGENT_THOUGHT)
except ValueError:
entity.features.append(ModelFeature.AGENT_THOUGHT)
if "display_name" in credentials and credentials["display_name"] != "":
entity.label= I18nObject(
en_US=credentials["display_name"],
zh_Hans=credentials["display_name"]
)
return entity

@ -0,0 +1,50 @@
from typing import Mapping
from dify_plugin.entities.model import (
AIModelEntity,
I18nObject
)
from dify_plugin.interfaces.model.openai_compatible.rerank import (
OAICompatRerankModel,
)
from dify_plugin.errors.model import (
CredentialsValidateFailedError,
)
class OpenAIRerankModel(OAICompatRerankModel):
def validate_credentials(self, model: str, credentials: dict) -> None:
"""
Validate model credentials
:param model: model name
:param credentials: model credentials
:return:
"""
try:
self._invoke(
model=model,
credentials=credentials,
query="What is the capital of the United States?",
docs=[
"Carson City is the capital city of the American state of Nevada. At the 2010 United States "
"Census, Carson City had a population of 55,274.",
"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
"are a political division controlled by the United States. Its capital is Saipan.",
],
score_threshold=0.8,
top_n=3,
)
except Exception as ex:
raise CredentialsValidateFailedError(str(ex)) from ex
def get_customizable_model_schema(self, model: str, credentials: Mapping) -> AIModelEntity:
entity = super().get_customizable_model_schema(model, credentials)
if "display_name" in credentials and credentials["display_name"] != "":
entity.label= I18nObject(
en_US=credentials["display_name"],
zh_Hans=credentials["display_name"]
)
return entity

@ -0,0 +1,28 @@
from typing import Optional
from dify_plugin.entities.model import AIModelEntity, FetchFrom, I18nObject, ModelType
from dify_plugin.interfaces.model.openai_compatible.speech2text import (
OAICompatSpeech2TextModel,
)
class OpenAISpeech2TextModel(OAICompatSpeech2TextModel):
def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]:
"""
used to define customizable model schema
"""
entity = AIModelEntity(
model=model,
label=I18nObject(en_US=model),
fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
model_type=ModelType.SPEECH2TEXT,
model_properties={},
parameter_rules=[],
)
if "display_name" in credentials and credentials["display_name"] != "":
entity.label= I18nObject(
en_US=credentials["display_name"],
zh_Hans=credentials["display_name"]
)
return entity

@ -0,0 +1,24 @@
from typing import Mapping
from dify_plugin.entities.model import (
AIModelEntity,
I18nObject
)
from dify_plugin.interfaces.model.openai_compatible.text_embedding import (
OAICompatEmbeddingModel,
)
class OpenAITextEmbeddingModel(OAICompatEmbeddingModel):
def get_customizable_model_schema(self, model: str, credentials: Mapping) -> AIModelEntity:
entity = super().get_customizable_model_schema(model, credentials)
if "display_name" in credentials and credentials["display_name"] != "":
entity.label= I18nObject(
en_US=credentials["display_name"],
zh_Hans=credentials["display_name"]
)
return entity

@ -0,0 +1,22 @@
from typing import Mapping
from dify_plugin.entities.model import (
AIModelEntity,
I18nObject
)
from dify_plugin.interfaces.model.openai_compatible.tts import OAICompatText2SpeechModel
class OpenAIText2SpeechModel(OAICompatText2SpeechModel):
def get_customizable_model_schema(self, model: str, credentials: Mapping) -> AIModelEntity:
entity = super().get_customizable_model_schema(model, credentials)
if "display_name" in credentials and credentials["display_name"] != "":
entity.label= I18nObject(
en_US=credentials["display_name"],
zh_Hans=credentials["display_name"]
)
return entity

@ -0,0 +1,17 @@
import logging
from collections.abc import Mapping
from dify_plugin import ModelProvider
logger = logging.getLogger(__name__)
class OpenAIProvider(ModelProvider):
def validate_provider_credentials(self, credentials: Mapping) -> None:
"""
Validate provider credentials
if validate failed, raise exception
:param credentials: provider credentials, credentials form defined in `provider_credential_schema`.
"""
pass

@ -0,0 +1,240 @@
provider: openai_api_compatible
label:
en_US: OpenAI-API-compatible
description:
en_US: Model providers compatible with OpenAI's API standard, such as LM Studio.
zh_Hans: 兼容 OpenAI API 的模型供应商,例如 LM Studio 。
icon_small:
en_US: icon.svg
supported_model_types:
- llm
- rerank
- text-embedding
- speech2text
- tts
configurate_methods:
- customizable-model
model_credential_schema:
model:
label:
en_US: Model Name
zh_Hans: 模型名称
placeholder:
en_US: Enter full model name
zh_Hans: 输入模型全称
credential_form_schemas:
- variable: display_name
label:
en_US: Model display name
zh_Hans: 模型显示名称
type: text-input
required: false
placeholder:
zh_Hans: 模型在界面的显示名称
en_US: The display name of the model in the interface.
- variable: api_key
label:
en_US: API Key
type: secret-input
required: false
placeholder:
zh_Hans: 在此输入您的 API Key
en_US: Enter your API Key
- variable: endpoint_url
label:
zh_Hans: API endpoint URL
en_US: API endpoint URL
type: text-input
required: true
placeholder:
zh_Hans: Base URL, e.g. https://api.openai.com/v1
en_US: Base URL, e.g. https://api.openai.com/v1
- variable: endpoint_model_name
label:
zh_Hans: API endpoint中的模型名称
en_US: model name for API endpoint
type: text-input
required: false
placeholder:
zh_Hans: endpoint model name, e.g. chatgpt4.0
en_US: endpoint model name, e.g. chatgpt4.0
- variable: mode
show_on:
- variable: __model_type
value: llm
label:
en_US: Completion mode
type: select
required: false
default: chat
placeholder:
zh_Hans: 选择对话类型
en_US: Select completion mode
options:
- value: completion
label:
en_US: Completion
zh_Hans: 补全
- value: chat
label:
en_US: Chat
zh_Hans: 对话
- variable: context_size
label:
zh_Hans: 模型上下文长度
en_US: Model context size
required: true
show_on:
- variable: __model_type
value: llm
type: text-input
default: "4096"
placeholder:
zh_Hans: 在此输入您的模型上下文长度
en_US: Enter your Model context size
- variable: context_size
label:
zh_Hans: 模型上下文长度
en_US: Model context size
required: true
show_on:
- variable: __model_type
value: text-embedding
type: text-input
default: "4096"
placeholder:
zh_Hans: 在此输入您的模型上下文长度
en_US: Enter your Model context size
- variable: context_size
label:
zh_Hans: 模型上下文长度
en_US: Model context size
required: true
show_on:
- variable: __model_type
value: rerank
type: text-input
default: "4096"
placeholder:
zh_Hans: 在此输入您的模型上下文长度
en_US: Enter your Model context size
- variable: max_tokens_to_sample
label:
zh_Hans: 最大 token 上限
en_US: Upper bound for max tokens
show_on:
- variable: __model_type
value: llm
default: "4096"
type: text-input
- variable: agent_though_support
show_on:
- variable: __model_type
value: llm
label:
en_US: Agent Thought
type: select
required: false
default: not_supported
options:
- value: supported
label:
en_US: Support
zh_Hans: 支持
- value: not_supported
label:
en_US: Not Support
zh_Hans: 不支持
- variable: function_calling_type
show_on:
- variable: __model_type
value: llm
label:
en_US: Function calling
type: select
required: false
default: no_call
options:
- value: function_call
label:
en_US: Function Call
zh_Hans: Function Call
- value: tool_call
label:
en_US: Tool Call
zh_Hans: Tool Call
- value: no_call
label:
en_US: Not Support
zh_Hans: 不支持
- variable: stream_function_calling
show_on:
- variable: __model_type
value: llm
label:
en_US: Stream function calling
type: select
required: false
default: not_supported
options:
- value: supported
label:
en_US: Support
zh_Hans: 支持
- value: not_supported
label:
en_US: Not Support
zh_Hans: 不支持
- variable: vision_support
show_on:
- variable: __model_type
value: llm
label:
zh_Hans: Vision 支持
en_US: Vision Support
type: select
required: false
default: no_support
options:
- value: support
label:
en_US: Support
zh_Hans: 支持
- value: no_support
label:
en_US: Not Support
zh_Hans: 不支持
- variable: stream_mode_delimiter
label:
zh_Hans: 流模式返回结果的分隔符
en_US: Delimiter for streaming results
show_on:
- variable: __model_type
value: llm
default: '\n\n'
type: text-input
- variable: voices
show_on:
- variable: __model_type
value: tts
label:
en_US: Available Voices (comma-separated)
zh_Hans: 可用声音(用英文逗号分隔)
type: text-input
required: false
default: "alloy"
placeholder:
en_US: "alloy,echo,fable,onyx,nova,shimmer"
zh_Hans: "alloy,echo,fable,onyx,nova,shimmer"
help:
en_US: "List voice names separated by commas. First voice will be used as default."
zh_Hans: "用英文逗号分隔的声音列表。第一个声音将作为默认值。"
extra:
python:
provider_source: provider/openai_api_compatible.py
model_sources:
- "models/llm/llm.py"
- "models/text_embedding/text_embedding.py"
- "models/rerank/rerank.py"
- "models/speech2text/speech2text.py"
- "models/tts/tts.py"

@ -0,0 +1,4 @@
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=********-****-****-****-************

@ -0,0 +1,15 @@
## Overview
SiliconCloud (MaaS) simplifies AI model deployment with robust performance. This plugin provides access to various models (LLMs, text embedding, reranking, STT, TTS), configurable via model name, API key, and other parameters.
## Configure
Install the SiliconFlow plugin, then configure it by entering your API Key. Get your API Key from [SiliconFlow](https://cloud.siliconflow.cn/account/ak) and save.
![](./_assets/siliconflow-01.png)
## About SiliconFlow 
SiliconFlow is committed to building a scalable, standardized, and high-performance AI Infra platform. It offers SiliconCloud (the model cloud service platform), SiliconLLM (the LLM inference engine), and OneDiff (the high-performance text-to-image/video acceleration library). These solutions help enterprises and individual users deploy AI models efficiently and cost-effectively.
[Website](https://siliconflow.cn/) | [SiliconCloud Quick Start](https://docs.siliconflow.cn/quickstart)

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="128" height="128" viewBox="0 0 128 128"><g><g style="opacity:0;"><rect x="0" y="0" width="128" height="128" rx="0" fill="#FFFFFF" fill-opacity="1"/></g><g><path d="M100.74,12L93.2335,12C69.21260000000001,12,55.3672,27.3468,55.3672,50.8672L55.3672,54.8988C52.6011,54.1056,49.7377,53.7031,46.8601,53.7031C29.816499999999998,53.7031,16,67.5196,16,84.5632C16,101.6069,29.816499999999998,115.423,46.8601,115.423C63.9037,115.423,77.72030000000001,101.6069,77.72030000000001,84.5632C77.72030000000001,82.4902,77.51140000000001,80.4223,77.0967,78.3911L77.2197,78.3911L100.74,78.3911C106.9654,78.3681,112,73.3151,112,67.08959999999999C112,60.8642,106.9654,55.8111,100.74,55.7882L100.7362,55.7882L100.6985,55.7879L100.6606,55.7882L77.2197,55.7882L77.2195,49.8663C77.2195,40.8584,83.7252,34.352900000000005,93.2335,34.352900000000005L100.5653,34.352900000000005L100.5733,34.352900000000005L100.5812,34.352900000000005L100.74,34.352900000000005L100.74,34.352900000000005C106.8469,34.2605,111.7497,29.284,111.7497,23.1764C111.7497,17.06889,106.8469,12.0923454,100.74,12L100.74,12ZM56.0347,84.5632C56.0347,79.4962,51.9271,75.3885,46.8601,75.3885C41.793099999999995,75.3885,37.6854,79.4962,37.6854,84.5632C37.6854,89.6303,41.793099999999995,93.7378,46.8601,93.7378C51.9271,93.7378,56.0347,89.6303,56.0347,84.5632Z" fill-rule="evenodd" fill="#8358F6" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,6 @@
from dify_plugin import Plugin, DifyPluginEnv
plugin = Plugin(DifyPluginEnv())
if __name__ == '__main__':
plugin.run()

@ -0,0 +1,29 @@
meta:
arch:
- amd64
- arm64
runner:
entrypoint: main
language: python
version: "3.12"
version: 0.0.1
name: siliconflow
author: langgenius
label:
en_US: SiliconFlow
zh_Hans: 硅基流动
description:
en_US: SiliconFlow provides access to various models (LLMs, text embedding, reranking, STT, TTS), configurable via model name, API key, and other parameters.
zh_Hans: 硅基流动提供对各种模型LLM、文本嵌入、重排序、STT、TTS的访问可通过模型名称、API密钥和其他参数进行配置。
icon: siliconflow_square.svg
plugins:
models:
- provider/siliconflow.yaml
resource:
memory: 268435456
permission:
model:
enabled: false
type: plugin
version: 0.0.8
created_at: 2024-09-20T00:13:50.29298939-04:00

@ -0,0 +1,84 @@
model: OpenGVLab/InternVL2-26B
label:
en_US: OpenGVLab/InternVL2-26B
model_type: llm
features:
- vision
model_properties:
mode: chat
context_size: 32768
parameter_rules:
- name: temperature
use_template: temperature
type: float
default: 0.3
min: 0.0
max: 2.0
help:
zh_Hans: 用于控制随机性和多样性的程度。具体来说temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值使得更多的低概率词被选择生成结果更加多样化而较低的temperature值则会增强概率分布的峰值使得高概率词更容易被选择生成结果更加确定。
en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain.
- name: max_tokens
use_template: max_tokens
type: int
default: 2000
min: 1
max: 2000
help:
zh_Hans: 用于指定模型在生成内容时token的最大数量它定义了生成的上限但不保证每次都会生成到这个数量。
en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time.
- name: top_p
use_template: top_p
type: float
default: 0.8
min: 0.1
max: 0.9
help:
zh_Hans: 生成过程中核采样方法概率阈值例如取值为0.8时仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。
en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated.
- name: top_k
type: int
min: 0
max: 99
label:
zh_Hans: 取样数量
en_US: Top k
help:
zh_Hans: 生成时采样候选集的大小。例如取值为50时仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大生成的随机性越高取值越小生成的确定性越高。
en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated.
- name: seed
required: false
type: int
default: 1234
label:
zh_Hans: 随机种子
en_US: Random seed
help:
zh_Hans: 生成时使用的随机数种子用户控制模型生成内容的随机性。支持无符号64位整数默认值为 1234。在使用seed时模型将尽可能生成相同或相似的结果但目前不保证每次生成的结果完全相同。
en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time.
- name: repetition_penalty
required: false
type: float
default: 1.1
label:
zh_Hans: 重复惩罚
en_US: Repetition penalty
help:
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
- name: response_format
label:
zh_Hans: 回复格式
en_US: Response Format
type: string
help:
zh_Hans: 指定模型必须输出的格式
en_US: specifying the format that the model must output
required: false
options:
- text
- json_object
pricing:
input: '21'
output: '21'
unit: '0.000001'
currency: RMB

@ -0,0 +1,84 @@
model: Pro/OpenGVLab/InternVL2-8B
label:
en_US: Pro/OpenGVLab/InternVL2-8B
model_type: llm
features:
- vision
model_properties:
mode: chat
context_size: 32768
parameter_rules:
- name: temperature
use_template: temperature
type: float
default: 0.3
min: 0.0
max: 2.0
help:
zh_Hans: 用于控制随机性和多样性的程度。具体来说temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值使得更多的低概率词被选择生成结果更加多样化而较低的temperature值则会增强概率分布的峰值使得高概率词更容易被选择生成结果更加确定。
en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain.
- name: max_tokens
use_template: max_tokens
type: int
default: 2000
min: 1
max: 2000
help:
zh_Hans: 用于指定模型在生成内容时token的最大数量它定义了生成的上限但不保证每次都会生成到这个数量。
en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time.
- name: top_p
use_template: top_p
type: float
default: 0.8
min: 0.1
max: 0.9
help:
zh_Hans: 生成过程中核采样方法概率阈值例如取值为0.8时仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。
en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated.
- name: top_k
type: int
min: 0
max: 99
label:
zh_Hans: 取样数量
en_US: Top k
help:
zh_Hans: 生成时采样候选集的大小。例如取值为50时仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大生成的随机性越高取值越小生成的确定性越高。
en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated.
- name: seed
required: false
type: int
default: 1234
label:
zh_Hans: 随机种子
en_US: Random seed
help:
zh_Hans: 生成时使用的随机数种子用户控制模型生成内容的随机性。支持无符号64位整数默认值为 1234。在使用seed时模型将尽可能生成相同或相似的结果但目前不保证每次生成的结果完全相同。
en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time.
- name: repetition_penalty
required: false
type: float
default: 1.1
label:
zh_Hans: 重复惩罚
en_US: Repetition penalty
help:
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
- name: response_format
label:
zh_Hans: 回复格式
en_US: Response Format
type: string
help:
zh_Hans: 指定模型必须输出的格式
en_US: specifying the format that the model must output
required: false
options:
- text
- json_object
pricing:
input: '21'
output: '21'
unit: '0.000001'
currency: RMB

@ -0,0 +1,44 @@
- Pro/deepseek-ai/DeepSeek-R1
- Pro/deepseek-ai/DeepSeek-V3
- deepseek-ai/DeepSeek-R1
- deepseek-ai/DeepSeek-V3
- deepseek-ai/DeepSeek-V2.5
- deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
- deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
- deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
- deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
- deepseek-ai/DeepSeek-R1-Distill-Llama-70B
- deepseek-ai/DeepSeek-R1-Distill-Llama-8B
- deepseek-ai/DeepSeek-V2.5
- deepseek-ai/Janus-Pro-7B
- Qwen/QwQ-32B-Preview
- Qwen/QVQ-72B-Preview
- Qwen/Qwen2.5-72B-Instruct
- Qwen/Qwen2.5-72B-Instruct-128K
- Qwen/Qwen2.5-32B-Instruct
- Qwen/Qwen2.5-14B-Instruct
- Qwen/Qwen2.5-7B-Instruct
- Qwen/Qwen2.5-Coder-32B-Instruct
- Qwen/Qwen2.5-Coder-7B-Instruct
- Qwen/Qwen2-VL-72B-Instruct
- Qwen/Qwen2-1.5B-Instruct
- Qwen/Qwen2.5-72B-Instruct-128K
- Vendor-A/Qwen/Qwen2.5-72B-Instruct
- Pro/Qwen/Qwen2-VL-7B-Instruct
- OpenGVLab/InternVL2-26B
- Pro/OpenGVLab/InternVL2-8B
- Vendor-A/Qwen/Qwen2.5-72B-Instruct
- Pro/Qwen/Qwen2-VL-7B-Instruct
- Pro/OpenGVLab/InternVL2-8B
- OpenGVLab/InternVL2-26B
- THUDM/glm-4-9b-chat
- 01-ai/Yi-1.5-34B-Chat-16K
- 01-ai/Yi-1.5-9B-Chat-16K
- 01-ai/Yi-1.5-6B-Chat
- internlm/internlm2_5-20b-chat
- internlm/internlm2_5-7b-chat
- meta-llama/Llama-3.3-70B-Instruct
- meta-llama/Meta-Llama-3.1-70B-Instruct
- meta-llama/Meta-Llama-3.1-8B-Instruct
- google/gemma-2-27b-it
- google/gemma-2-9b-it

@ -0,0 +1,40 @@
model: deepseek-ai/DeepSeek-Coder-V2-Instruct
label:
en_US: deepseek-ai/DeepSeek-Coder-V2-Instruct
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32768
parameter_rules:
- name: temperature
use_template: temperature
- name: max_tokens
use_template: max_tokens
type: int
default: 512
min: 1
max: 4096
help:
zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。
en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter.
- name: top_p
use_template: top_p
- name: top_k
label:
zh_Hans: 取样数量
en_US: Top k
type: int
help:
zh_Hans: 仅从每个后续标记的前 K 个选项中采样。
en_US: Only sample from the top K options for each subsequent token.
required: false
- name: frequency_penalty
use_template: frequency_penalty
pricing:
input: '1.33'
output: '1.33'
unit: '0.000001'
currency: RMB
deprecated: true

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
en_US: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "4.3"
unit: "0.000001"
currency: RMB

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
en_US: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "0.00"
unit: "0.000001"
currency: RMB

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "1.26"
unit: "0.000001"
currency: RMB

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "0.70"
unit: "0.000001"
currency: RMB

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "1.26"
unit: "0.000001"
currency: RMB

@ -0,0 +1,21 @@
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
label:
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
model_type: llm
features:
- agent-thought
model_properties:
mode: chat
context_size: 32000
parameter_rules:
- name: max_tokens
use_template: max_tokens
min: 1
max: 8192
default: 4096
pricing:
input: "0.00"
output: "0.00"
unit: "0.000001"
currency: RMB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save