diff --git a/api/core/model_providers/providers/anthropic_provider.py b/api/core/model_providers/providers/anthropic_provider.py
index eab61c60ccc2f0..2f667aed965732 100644
--- a/api/core/model_providers/providers/anthropic_provider.py
+++ b/api/core/model_providers/providers/anthropic_provider.py
@@ -172,7 +172,7 @@ def is_provider_type_system_supported(cls) -> bool:
def should_deduct_quota(self):
if hosted_model_providers.anthropic and \
- hosted_model_providers.anthropic.quota_limit and hosted_model_providers.anthropic.quota_limit > 0:
+ hosted_model_providers.anthropic.quota_limit and hosted_model_providers.anthropic.quota_limit > -1:
return True
return False
diff --git a/api/core/model_providers/providers/azure_openai_provider.py b/api/core/model_providers/providers/azure_openai_provider.py
index a34b463286a76a..242d9109cf28b2 100644
--- a/api/core/model_providers/providers/azure_openai_provider.py
+++ b/api/core/model_providers/providers/azure_openai_provider.py
@@ -329,7 +329,7 @@ def is_provider_type_system_supported(cls) -> bool:
def should_deduct_quota(self):
if hosted_model_providers.azure_openai \
- and hosted_model_providers.azure_openai.quota_limit and hosted_model_providers.azure_openai.quota_limit > 0:
+ and hosted_model_providers.azure_openai.quota_limit and hosted_model_providers.azure_openai.quota_limit > -1:
return True
return False
diff --git a/api/core/model_providers/providers/hosted.py b/api/core/model_providers/providers/hosted.py
index d2dc39b73f15c5..fd90a0a360cfe6 100644
--- a/api/core/model_providers/providers/hosted.py
+++ b/api/core/model_providers/providers/hosted.py
@@ -11,7 +11,7 @@ class HostedOpenAI(BaseModel):
api_organization: str = None
api_key: str
quota_limit: int = 0
- """Quota limit for the openai hosted model. 0 means unlimited."""
+ """Quota limit for the openai hosted model. -1 means unlimited."""
paid_enabled: bool = False
paid_stripe_price_id: str = None
paid_increase_quota: int = 1
@@ -21,14 +21,14 @@ class HostedAzureOpenAI(BaseModel):
api_base: str
api_key: str
quota_limit: int = 0
- """Quota limit for the azure openai hosted model. 0 means unlimited."""
+ """Quota limit for the azure openai hosted model. -1 means unlimited."""
class HostedAnthropic(BaseModel):
api_base: str = None
api_key: str
quota_limit: int = 0
- """Quota limit for the anthropic hosted model. 0 means unlimited."""
+ """Quota limit for the anthropic hosted model. -1 means unlimited."""
paid_enabled: bool = False
paid_stripe_price_id: str = None
paid_increase_quota: int = 1000000
diff --git a/api/core/model_providers/providers/openai_provider.py b/api/core/model_providers/providers/openai_provider.py
index d72ad3c03bc18d..e2e17eab4570f3 100644
--- a/api/core/model_providers/providers/openai_provider.py
+++ b/api/core/model_providers/providers/openai_provider.py
@@ -250,7 +250,7 @@ def is_provider_type_system_supported(cls) -> bool:
def should_deduct_quota(self):
if hosted_model_providers.openai \
- and hosted_model_providers.openai.quota_limit and hosted_model_providers.openai.quota_limit > 0:
+ and hosted_model_providers.openai.quota_limit and hosted_model_providers.openai.quota_limit > -1:
return True
return False
diff --git a/api/core/model_providers/rules/anthropic.json b/api/core/model_providers/rules/anthropic.json
index c0aac8617c05d7..e617842b94d9c0 100644
--- a/api/core/model_providers/rules/anthropic.json
+++ b/api/core/model_providers/rules/anthropic.json
@@ -9,7 +9,7 @@
"trial"
],
"quota_unit": "tokens",
- "quota_limit": 600000
+ "quota_limit": 0
},
"model_flexibility": "fixed",
"price_config": {
diff --git a/third-party/chrome plug-in/README_CN.md b/third-party/chrome plug-in/README_CN.md
new file mode 100644
index 00000000000000..3c1ebabf9b8bcd
--- /dev/null
+++ b/third-party/chrome plug-in/README_CN.md
@@ -0,0 +1,35 @@
+## Chrome Dify ChatBot插件
+
+### 方式1:Chrome插件商店 * [点击访问](https://chrome.google.com/webstore/detail/dify-chatbot/ceehdapohffmjmkdcifjofadiaoeggaf/related?hl=zh-CN&authuser=0) *
+
+### 方式2:本地开发者模式加载
+
+- 进入Chrome浏览器管理扩展程序,可直接访问 [chrome://extensions/](chrome://extensions/)
+- 选择开启 “开发者模式”,并点击 “加载已解压的扩展程序”
+
+![img-1.png](images/img-1.png)
+
+- 然后打开插件源文件所在根目录
+ - third-party
+ - chrome plug-in
+ - content.js 浮动按钮JS脚本
+ - favicon.png 插件图标
+ - manifest.json 插件描述文件
+ - options.css 插件配置页面样式文件
+ - options.html 插件配置静态HTML页面
+ - options.js 插件配置JS脚本
+
+### 插件导入完成后,后续配置无差异
+- 初始化设置Dify 应用配置,分别输入Dify根域名和应用Token,Token可以在Dify应用嵌入中获取,如图:
+
+![img-2.png](images/img-2.png)
+![img-3.png](images/img-3.png)
+
+- 点击保存,确认提示配置成功即可
+
+![img-4.png](images/img-4.png)
+
+- 保险起见重启浏览器确保所有分页刷新成功
+- Chrome打开任意页面均可正常加载DIfy机器人浮动栏,后续如需更换机器人只需要变更Token即可
+
+![img-5.png](images/img-5.png)
\ No newline at end of file
diff --git a/third-party/chrome plug-in/content.js b/third-party/chrome plug-in/content.js
new file mode 100644
index 00000000000000..24d25f330f65e0
--- /dev/null
+++ b/third-party/chrome plug-in/content.js
@@ -0,0 +1,170 @@
+var storage = chrome.storage.sync;
+chrome.storage.sync.get(['baseUrl', 'token'], function(result) {
+ window.difyChatbotConfig = {
+ baseUrl: result.baseUrl,
+ token: result.token
+ };
+});
+
+document.body.onload = embedChatbot;
+
+async function embedChatbot() {
+ const difyChatbotConfig = window.difyChatbotConfig;
+ if (!difyChatbotConfig || !difyChatbotConfig.token) {
+ console.warn('difyChatbotConfig is empty or token is not provided');
+ return;
+ }
+ const baseUrl = difyChatbotConfig.baseUrl
+ const openIcon = ``;
+ const closeIcon = ``;
+
+ // create iframe
+ function createIframe() {
+ const iframe = document.createElement('iframe');
+ iframe.allow = "fullscreen;microphone"
+ iframe.title = "dify chatbot bubble window"
+ iframe.id = 'dify-chatbot-bubble-window'
+ iframe.src = `${baseUrl}/chat/${difyChatbotConfig.token}`
+ iframe.style.cssText = 'border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 6.7rem; right: 1rem; width: 30rem; height: 48rem; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;'
+ document.body.appendChild(iframe);
+ }
+
+ /**
+ * rem to px
+ * @param {*} rem :30rem
+ */
+ function handleRemToPx(rem) {
+ if (!rem) return;
+ let pxValue = 0;
+ try {
+ const regex = /\d+/;
+ // extract the numeric part and convert it to a numeric type
+ const remValue = parseInt(regex.exec(rem)[0], 10);
+ const rootFontSize = parseFloat(
+ window.getComputedStyle(document.documentElement).fontSize
+ );
+ pxValue = remValue * rootFontSize;
+ } catch (error) {
+ console.error(error);
+ }
+ return pxValue;
+ }
+
+ /**
+ * support element drag
+ * @param {*} targetButton entry element
+ */
+ function handleElementDrag(targetButton) {
+ // define a variable to hold the mouse position
+ let mouseX = 0,
+ mouseY = 0,
+ offsetX = 0,
+ offsetY = 0;
+
+ // Listen for mouse press events, get mouse position and element position
+ targetButton.addEventListener("mousedown", function (event) {
+ // calculate mouse position
+ mouseX = event.clientX;
+ mouseY = event.clientY;
+
+ // calculate element position
+ const rect = targetButton.getBoundingClientRect();
+ offsetX = mouseX - rect.left;
+ offsetY = mouseY - rect.top;
+
+ // listen for mouse movement events
+ document.addEventListener("mousemove", onMouseMove);
+ });
+
+ // listen for mouse lift events and stop listening for mouse move events
+ document.addEventListener("mouseup", function () {
+ document.removeEventListener("mousemove", onMouseMove);
+ });
+
+ // the mouse moves the event handler to update the element position
+ function onMouseMove(event) {
+ // calculate element position
+ let newX = event.clientX - offsetX,
+ newY = event.clientY - offsetY;
+
+ // 计算视线边界
+ const viewportWidth = window.innerWidth,
+ viewportHeight = window.innerHeight;
+
+ const maxX = viewportWidth - targetButton.offsetWidth,
+ maxY = viewportHeight - targetButton.offsetHeight;
+
+ // application limitation
+ newX = Math.max(12, Math.min(newX, maxX));
+ newY = Math.max(12, Math.min(newY, maxY));
+
+ // update element position
+ targetButton.style.left = newX + "px";
+ targetButton.style.top = newY + "px";
+ }
+ }
+
+ const targetButton = document.getElementById("dify-chatbot-bubble-button");
+
+ if (!targetButton) {
+ // create button
+ const containerDiv = document.createElement("div");
+ containerDiv.id = 'dify-chatbot-bubble-button';
+ containerDiv.style.cssText = `position: fixed; bottom: 3rem; right: 1rem; width: 50px; height: 50px; border-radius: 25px; background-color: #155EEF; box-shadow: rgba(0, 0, 0, 0.2) 0px 4px 8px 0px; cursor: move; z-index: 2147483647; transition: all 0.2s ease-in-out 0s; left: unset; transform: scale(1); :hover {transform: scale(1.1);}`;
+ const displayDiv = document.createElement('div');
+ displayDiv.style.cssText = "display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;";
+ displayDiv.innerHTML = openIcon;
+ containerDiv.appendChild(displayDiv);
+ document.body.appendChild(containerDiv);
+ handleElementDrag(containerDiv);
+
+ // add click event to control iframe display
+ containerDiv.addEventListener('click', function () {
+ const targetIframe = document.getElementById('dify-chatbot-bubble-window');
+ if (!targetIframe) {
+ createIframe();
+ displayDiv.innerHTML = closeIcon;
+ return;
+ }
+ if (targetIframe.style.display === "none") {
+ targetIframe.style.display = "block";
+ displayDiv.innerHTML = closeIcon;
+ } else {
+ targetIframe.style.display = "none";
+ displayDiv.innerHTML = openIcon;
+ }
+ });
+ } else {
+ // add any drag and drop to the floating icon
+ handleElementDrag(targetButton);
+ }
+}
\ No newline at end of file
diff --git a/third-party/chrome plug-in/images/128.png b/third-party/chrome plug-in/images/128.png
new file mode 100644
index 00000000000000..c88deb377aeefc
Binary files /dev/null and b/third-party/chrome plug-in/images/128.png differ
diff --git a/third-party/chrome plug-in/images/16.png b/third-party/chrome plug-in/images/16.png
new file mode 100644
index 00000000000000..c26df616ffb6e4
Binary files /dev/null and b/third-party/chrome plug-in/images/16.png differ
diff --git a/third-party/chrome plug-in/images/32.png b/third-party/chrome plug-in/images/32.png
new file mode 100644
index 00000000000000..a748ace09fa445
Binary files /dev/null and b/third-party/chrome plug-in/images/32.png differ
diff --git a/third-party/chrome plug-in/images/48.png b/third-party/chrome plug-in/images/48.png
new file mode 100644
index 00000000000000..0cc74d8df053f6
Binary files /dev/null and b/third-party/chrome plug-in/images/48.png differ
diff --git a/third-party/chrome plug-in/images/img-1.png b/third-party/chrome plug-in/images/img-1.png
new file mode 100644
index 00000000000000..92dadaf37ea00a
Binary files /dev/null and b/third-party/chrome plug-in/images/img-1.png differ
diff --git a/third-party/chrome plug-in/images/img-2.png b/third-party/chrome plug-in/images/img-2.png
new file mode 100644
index 00000000000000..ccc7100034b436
Binary files /dev/null and b/third-party/chrome plug-in/images/img-2.png differ
diff --git a/third-party/chrome plug-in/images/img-3.png b/third-party/chrome plug-in/images/img-3.png
new file mode 100644
index 00000000000000..b524df7e11e424
Binary files /dev/null and b/third-party/chrome plug-in/images/img-3.png differ
diff --git a/third-party/chrome plug-in/images/img-4.png b/third-party/chrome plug-in/images/img-4.png
new file mode 100644
index 00000000000000..193171bccf0612
Binary files /dev/null and b/third-party/chrome plug-in/images/img-4.png differ
diff --git a/third-party/chrome plug-in/images/img-5.png b/third-party/chrome plug-in/images/img-5.png
new file mode 100644
index 00000000000000..282a4a68397d0b
Binary files /dev/null and b/third-party/chrome plug-in/images/img-5.png differ
diff --git a/third-party/chrome plug-in/manifest.json b/third-party/chrome plug-in/manifest.json
new file mode 100644
index 00000000000000..fe824c5d836c54
--- /dev/null
+++ b/third-party/chrome plug-in/manifest.json
@@ -0,0 +1,29 @@
+{
+ "manifest_version": 3,
+ "name": "Dify Chatbot",
+ "version": "1.3",
+ "description": "This is a chrome extension to inject a dify chatbot on any pages",
+ "content_scripts": [
+ {
+ "matches": [""],
+ "js": ["content.js"]
+ }
+ ],
+ "permissions": ["webRequest", "storage"],
+ "action": {
+ "default_popup": "options.html",
+ "default_icon": {
+ "16": "images/16.png",
+ "32": "images/32.png",
+ "48": "images/48.png",
+ "128": "images/128.png"
+
+ }
+ },
+ "icons": {
+ "16": "images/16.png",
+ "32": "images/32.png",
+ "48": "images/48.png",
+ "128": "images/128.png"
+ }
+}
\ No newline at end of file
diff --git a/third-party/chrome plug-in/options.html b/third-party/chrome plug-in/options.html
new file mode 100644
index 00000000000000..a0f614a1dc20cb
--- /dev/null
+++ b/third-party/chrome plug-in/options.html
@@ -0,0 +1,56 @@
+
+
+
+
+ Dify Chatbot Extension
+
+
+
+
+
+
+
Dify Chatbot Extension
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/third-party/chrome plug-in/options.js b/third-party/chrome plug-in/options.js
new file mode 100644
index 00000000000000..95895dc05c0182
--- /dev/null
+++ b/third-party/chrome plug-in/options.js
@@ -0,0 +1,39 @@
+
+document.getElementById('save-button').addEventListener('click', function (e) {
+ e.preventDefault();
+ var baseUrl = document.getElementById('base-url').value;
+ var token = document.getElementById('token').value;
+ var errorTip = document.getElementById('error-tip');
+
+ if (baseUrl.trim() === "" || token.trim() === "") {
+ if (baseUrl.trim() === "") {
+ errorTip.textContent = "Base URL cannot be empty.";
+ } else {
+ errorTip.textContent = "Token cannot be empty.";
+ }
+ } else {
+ errorTip.textContent = "";
+
+ chrome.storage.sync.set({
+ 'baseUrl': baseUrl,
+ 'token': token
+ }, function () {
+ alert('Save Success!');
+ });
+ }
+
+});
+
+// Load parameters from chrome.storage when the page loads
+chrome.storage.sync.get(['baseUrl', 'token'], function (result) {
+ const baseUrlInput = document.getElementById('base-url');
+ const tokenInput = document.getElementById('token');
+
+ if (result.baseUrl) {
+ baseUrlInput.value = result.baseUrl;
+ }
+
+ if (result.token) {
+ tokenInput.value = result.token;
+ }
+});
\ No newline at end of file