mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 13:17:36 +08:00
Compare commits
2 Commits
fix/handle
...
fix/versio
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d9c8d76b9 | |||
| 9ac6662635 |
14
.github/workflows/build-push.yml
vendored
14
.github/workflows/build-push.yml
vendored
@ -6,7 +6,6 @@ on:
|
||||
- "main"
|
||||
- "deploy/dev"
|
||||
- "plugins/beta"
|
||||
- "dev/plugin-deploy"
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
@ -141,16 +140,3 @@ jobs:
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env[matrix.image_name_env] }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
- name: print context var
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: deploy pod in plugin env
|
||||
if: github.ref == 'refs/heads/dev/plugin-deploy'
|
||||
env:
|
||||
IMAGEHASH: ${{ github.sha }}
|
||||
APICMD: "${{ secrets.PLUGIN_CD_API_CURL }}"
|
||||
WEBCMD: "${{ secrets.PLUGIN_CD_WEB_CURL }}"
|
||||
run: |
|
||||
bash -c "${APICMD/yourNewVersion/$IMAGEHASH}"
|
||||
bash -c "${WEBCMD/yourNewVersion/$IMAGEHASH}"
|
||||
|
||||
23
.github/workflows/deploy-plugin-dev.yml
vendored
23
.github/workflows/deploy-plugin-dev.yml
vendored
@ -1,23 +0,0 @@
|
||||
name: Deploy Plugin Dev
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build and Push API & Web"]
|
||||
branches:
|
||||
- "dev/plugin-deploy"
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: Deploy to server
|
||||
uses: appleboy/ssh-action@v0.1.8
|
||||
with:
|
||||
host: ${{ secrets.SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
script: "echo 123"
|
||||
@ -329,6 +329,7 @@ class BaseAgentRunner(AppRunner):
|
||||
)
|
||||
if not updated_agent_thought:
|
||||
raise ValueError("agent thought not found")
|
||||
agent_thought = updated_agent_thought
|
||||
|
||||
if thought:
|
||||
agent_thought.thought = thought
|
||||
|
||||
@ -384,6 +384,7 @@ class AdvancedChatAppGenerateTaskPipeline:
|
||||
task_id=self._application_generate_entity.task_id,
|
||||
workflow_node_execution=workflow_node_execution,
|
||||
)
|
||||
session.commit()
|
||||
|
||||
if node_finish_resp:
|
||||
yield node_finish_resp
|
||||
|
||||
@ -387,7 +387,6 @@ class WorkflowBasedAppRunner(AppRunner):
|
||||
status=event.status,
|
||||
data=event.data,
|
||||
metadata=event.metadata,
|
||||
node_id=event.node_id,
|
||||
)
|
||||
)
|
||||
elif isinstance(event, ParallelBranchRunStartedEvent):
|
||||
|
||||
@ -331,7 +331,6 @@ class QueueAgentLogEvent(AppQueueEvent):
|
||||
status: str
|
||||
data: Mapping[str, Any]
|
||||
metadata: Optional[Mapping[str, Any]] = None
|
||||
node_id: str
|
||||
|
||||
|
||||
class QueueNodeRetryEvent(QueueNodeStartedEvent):
|
||||
|
||||
@ -719,7 +719,6 @@ class AgentLogStreamResponse(StreamResponse):
|
||||
status: str
|
||||
data: Mapping[str, Any]
|
||||
metadata: Optional[Mapping[str, Any]] = None
|
||||
node_id: str
|
||||
|
||||
event: StreamEvent = StreamEvent.AGENT_LOG
|
||||
data: Data
|
||||
|
||||
@ -864,6 +864,5 @@ class WorkflowCycleManage:
|
||||
status=event.status,
|
||||
data=event.data,
|
||||
metadata=event.metadata,
|
||||
node_id=event.node_id,
|
||||
),
|
||||
)
|
||||
|
||||
@ -65,8 +65,7 @@ def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs):
|
||||
retries += 1
|
||||
if retries <= max_retries:
|
||||
time.sleep(BACKOFF_FACTOR * (2 ** (retries - 1)))
|
||||
raise MaxRetriesExceededError(
|
||||
f"Reached maximum retries ({max_retries}) for URL {url}")
|
||||
raise MaxRetriesExceededError(f"Reached maximum retries ({max_retries}) for URL {url}")
|
||||
|
||||
|
||||
def get(url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs):
|
||||
|
||||
@ -212,9 +212,3 @@ class PluginDependency(BaseModel):
|
||||
|
||||
type: Type
|
||||
value: Github | Marketplace | Package
|
||||
current_identifier: Optional[str] = None
|
||||
|
||||
|
||||
class MissingPluginDependency(BaseModel):
|
||||
plugin_unique_identifier: str
|
||||
current_identifier: Optional[str] = None
|
||||
|
||||
@ -3,7 +3,6 @@ from collections.abc import Sequence
|
||||
from core.plugin.entities.bundle import PluginBundleDependency
|
||||
from core.plugin.entities.plugin import (
|
||||
GenericProviderID,
|
||||
MissingPluginDependency,
|
||||
PluginDeclaration,
|
||||
PluginEntity,
|
||||
PluginInstallation,
|
||||
@ -176,16 +175,14 @@ class PluginInstallationManager(BasePluginManager):
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
def fetch_missing_dependencies(
|
||||
self, tenant_id: str, plugin_unique_identifiers: list[str]
|
||||
) -> list[MissingPluginDependency]:
|
||||
def fetch_missing_dependencies(self, tenant_id: str, plugin_unique_identifiers: list[str]) -> list[str]:
|
||||
"""
|
||||
Fetch missing dependencies
|
||||
"""
|
||||
return self._request_with_plugin_daemon_response(
|
||||
"POST",
|
||||
f"plugin/{tenant_id}/management/installation/missing",
|
||||
list[MissingPluginDependency],
|
||||
list[str],
|
||||
data={"plugin_unique_identifiers": plugin_unique_identifiers},
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
@ -160,8 +160,8 @@ class ToolManager:
|
||||
"""
|
||||
get the tool runtime
|
||||
|
||||
:param provider_type: the type of the provider
|
||||
:param provider_name: the name of the provider
|
||||
:param provider_type: the type of the provider
|
||||
:param provider_name: the name of the provider
|
||||
:param tool_name: the name of the tool
|
||||
|
||||
:return: the tool
|
||||
|
||||
@ -207,7 +207,6 @@ class AgentLogEvent(BaseAgentEvent):
|
||||
status: str = Field(..., description="status")
|
||||
data: Mapping[str, Any] = Field(..., description="data")
|
||||
metadata: Optional[Mapping[str, Any]] = Field(default=None, description="metadata")
|
||||
node_id: str = Field(..., description="agent node id")
|
||||
|
||||
|
||||
InNodeEvent = BaseNodeEvent | BaseParallelBranchEvent | BaseIterationEvent | BaseAgentEvent
|
||||
|
||||
@ -18,7 +18,6 @@ from core.workflow.entities.node_entities import AgentNodeStrategyInit, NodeRunM
|
||||
from core.workflow.entities.variable_pool import VariablePool, VariableValue
|
||||
from core.workflow.graph_engine.condition_handlers.condition_manager import ConditionManager
|
||||
from core.workflow.graph_engine.entities.event import (
|
||||
BaseAgentEvent,
|
||||
BaseIterationEvent,
|
||||
GraphEngineEvent,
|
||||
GraphRunFailedEvent,
|
||||
@ -502,7 +501,7 @@ class GraphEngine:
|
||||
break
|
||||
|
||||
yield event
|
||||
if not isinstance(event, BaseAgentEvent) and event.parallel_id == parallel_id:
|
||||
if event.parallel_id == parallel_id:
|
||||
if isinstance(event, ParallelBranchRunSucceededEvent):
|
||||
succeeded_count += 1
|
||||
if succeeded_count == len(futures):
|
||||
|
||||
@ -8,12 +8,12 @@ from core.model_manager import ModelManager
|
||||
from core.model_runtime.entities.model_entities import ModelType
|
||||
from core.plugin.manager.exc import PluginDaemonClientSideError
|
||||
from core.plugin.manager.plugin import PluginInstallationManager
|
||||
from core.tools.entities.tool_entities import ToolParameter, ToolProviderType
|
||||
from core.tools.entities.tool_entities import ToolProviderType
|
||||
from core.tools.tool_manager import ToolManager
|
||||
from core.workflow.entities.node_entities import NodeRunResult
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
from core.workflow.enums import SystemVariableKey
|
||||
from core.workflow.nodes.agent.entities import AgentNodeData, ParamsAutoGenerated
|
||||
from core.workflow.nodes.agent.entities import AgentNodeData
|
||||
from core.workflow.nodes.base.entities import BaseNodeData
|
||||
from core.workflow.nodes.enums import NodeType
|
||||
from core.workflow.nodes.event.event import RunCompletedEvent
|
||||
@ -156,38 +156,16 @@ class AgentNode(ToolNode):
|
||||
value = cast(list[dict[str, Any]], value)
|
||||
value = [tool for tool in value if tool.get("enabled", False)]
|
||||
|
||||
for tool in value:
|
||||
if "schemas" in tool:
|
||||
tool.pop("schemas")
|
||||
parameters = tool.get("parameters", {})
|
||||
if all(isinstance(v, dict) for _, v in parameters.items()):
|
||||
params = {}
|
||||
for key, param in parameters.items():
|
||||
if param.get("auto", ParamsAutoGenerated.OPEN.value) == ParamsAutoGenerated.CLOSE.value:
|
||||
value_param = param.get("value", {})
|
||||
params[key] = value_param.get("value", "") if value_param is not None else None
|
||||
else:
|
||||
params[key] = None
|
||||
parameters = params
|
||||
tool["settings"] = {k: v.get("value", None) for k, v in tool.get("settings", {}).items()}
|
||||
tool["parameters"] = parameters
|
||||
|
||||
if not for_log:
|
||||
if parameter.type == "array[tools]":
|
||||
value = cast(list[dict[str, Any]], value)
|
||||
tool_value = []
|
||||
for tool in value:
|
||||
provider_type = ToolProviderType(tool.get("type", ToolProviderType.BUILT_IN.value))
|
||||
setting_params = tool.get("settings", {})
|
||||
parameters = tool.get("parameters", {})
|
||||
manual_input_params = [key for key, value in parameters.items() if value is not None]
|
||||
|
||||
parameters = {**parameters, **setting_params}
|
||||
entity = AgentToolEntity(
|
||||
provider_id=tool.get("provider_name", ""),
|
||||
provider_type=provider_type,
|
||||
provider_type=ToolProviderType.BUILT_IN,
|
||||
tool_name=tool.get("tool_name", ""),
|
||||
tool_parameters=parameters,
|
||||
tool_parameters=tool.get("parameters", {}),
|
||||
plugin_unique_identifier=tool.get("plugin_unique_identifier", None),
|
||||
)
|
||||
|
||||
@ -200,27 +178,13 @@ class AgentNode(ToolNode):
|
||||
tool_runtime.entity.description.llm = (
|
||||
extra.get("descrption", "") or tool_runtime.entity.description.llm
|
||||
)
|
||||
for params in tool_runtime.entity.parameters:
|
||||
params.form = (
|
||||
ToolParameter.ToolParameterForm.FORM
|
||||
if params.name in manual_input_params
|
||||
else params.form
|
||||
)
|
||||
if tool_runtime.entity.parameters:
|
||||
manual_input_value = {
|
||||
key: value for key, value in parameters.items() if key in manual_input_params
|
||||
|
||||
tool_value.append(
|
||||
{
|
||||
**tool_runtime.entity.model_dump(mode="json"),
|
||||
"runtime_parameters": tool_runtime.runtime.runtime_parameters,
|
||||
}
|
||||
runtime_parameters = {
|
||||
**tool_runtime.runtime.runtime_parameters,
|
||||
**manual_input_value,
|
||||
}
|
||||
tool_value.append(
|
||||
{
|
||||
**tool_runtime.entity.model_dump(mode="json"),
|
||||
"runtime_parameters": runtime_parameters,
|
||||
"provider_type": provider_type.value,
|
||||
}
|
||||
)
|
||||
)
|
||||
value = tool_value
|
||||
if parameter.type == "model-selector":
|
||||
value = cast(dict[str, Any], value)
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from enum import Enum
|
||||
from typing import Any, Literal, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
@ -17,8 +16,3 @@ class AgentNodeData(BaseNodeData):
|
||||
type: Literal["mixed", "variable", "constant"]
|
||||
|
||||
agent_parameters: dict[str, AgentInput]
|
||||
|
||||
|
||||
class ParamsAutoGenerated(Enum):
|
||||
CLOSE = 0
|
||||
OPEN = 1
|
||||
|
||||
@ -64,7 +64,7 @@ class EndStreamGeneratorRouter:
|
||||
node_type = node.get("data", {}).get("type")
|
||||
if (
|
||||
variable_selector.value_selector not in value_selectors
|
||||
and (node_type in (NodeType.LLM.value, NodeType.AGENT.value))
|
||||
and node_type == NodeType.LLM.value
|
||||
and variable_selector.value_selector[1] == "text"
|
||||
):
|
||||
value_selectors.append(list(variable_selector.value_selector))
|
||||
|
||||
@ -44,13 +44,11 @@ class QuestionClassifierNode(LLMNode):
|
||||
variable_pool = self.graph_runtime_state.variable_pool
|
||||
|
||||
# extract variables
|
||||
variable = variable_pool.get(
|
||||
node_data.query_variable_selector) if node_data.query_variable_selector else None
|
||||
variable = variable_pool.get(node_data.query_variable_selector) if node_data.query_variable_selector else None
|
||||
query = variable.value if variable else None
|
||||
variables = {"query": query}
|
||||
# fetch model config
|
||||
model_instance, model_config = self._fetch_model_config(
|
||||
node_data.model)
|
||||
model_instance, model_config = self._fetch_model_config(node_data.model)
|
||||
# fetch memory
|
||||
memory = self._fetch_memory(
|
||||
node_data_memory=node_data.memory,
|
||||
@ -58,8 +56,7 @@ class QuestionClassifierNode(LLMNode):
|
||||
)
|
||||
# fetch instruction
|
||||
node_data.instruction = node_data.instruction or ""
|
||||
node_data.instruction = variable_pool.convert_template(
|
||||
node_data.instruction).text
|
||||
node_data.instruction = variable_pool.convert_template(node_data.instruction).text
|
||||
|
||||
files = (
|
||||
self._fetch_files(
|
||||
@ -181,15 +178,12 @@ class QuestionClassifierNode(LLMNode):
|
||||
variable_mapping = {"query": node_data.query_variable_selector}
|
||||
variable_selectors = []
|
||||
if node_data.instruction:
|
||||
variable_template_parser = VariableTemplateParser(
|
||||
template=node_data.instruction)
|
||||
variable_selectors.extend(
|
||||
variable_template_parser.extract_variable_selectors())
|
||||
variable_template_parser = VariableTemplateParser(template=node_data.instruction)
|
||||
variable_selectors.extend(variable_template_parser.extract_variable_selectors())
|
||||
for variable_selector in variable_selectors:
|
||||
variable_mapping[variable_selector.variable] = variable_selector.value_selector
|
||||
|
||||
variable_mapping = {node_id + "." + key: value for key,
|
||||
value in variable_mapping.items()}
|
||||
variable_mapping = {node_id + "." + key: value for key, value in variable_mapping.items()}
|
||||
|
||||
return variable_mapping
|
||||
|
||||
@ -210,8 +204,7 @@ class QuestionClassifierNode(LLMNode):
|
||||
context: Optional[str],
|
||||
) -> int:
|
||||
prompt_transform = AdvancedPromptTransform(with_variable_tmpl=True)
|
||||
prompt_template = self._get_prompt_template(
|
||||
node_data, query, None, 2000)
|
||||
prompt_template = self._get_prompt_template(node_data, query, None, 2000)
|
||||
prompt_messages = prompt_transform.get_prompt(
|
||||
prompt_template=prompt_template,
|
||||
inputs={},
|
||||
@ -224,15 +217,13 @@ class QuestionClassifierNode(LLMNode):
|
||||
)
|
||||
rest_tokens = 2000
|
||||
|
||||
model_context_tokens = model_config.model_schema.model_properties.get(
|
||||
ModelPropertyKey.CONTEXT_SIZE)
|
||||
model_context_tokens = model_config.model_schema.model_properties.get(ModelPropertyKey.CONTEXT_SIZE)
|
||||
if model_context_tokens:
|
||||
model_instance = ModelInstance(
|
||||
provider_model_bundle=model_config.provider_model_bundle, model=model_config.model
|
||||
)
|
||||
|
||||
curr_message_tokens = model_instance.get_llm_num_tokens(
|
||||
prompt_messages)
|
||||
curr_message_tokens = model_instance.get_llm_num_tokens(prompt_messages)
|
||||
|
||||
max_tokens = 0
|
||||
for parameter_rule in model_config.model_schema.parameter_rules:
|
||||
@ -273,8 +264,7 @@ class QuestionClassifierNode(LLMNode):
|
||||
prompt_messages: list[LLMNodeChatModelMessage] = []
|
||||
if model_mode == ModelMode.CHAT:
|
||||
system_prompt_messages = LLMNodeChatModelMessage(
|
||||
role=PromptMessageRole.SYSTEM, text=QUESTION_CLASSIFIER_SYSTEM_PROMPT.format(
|
||||
histories=memory_str)
|
||||
role=PromptMessageRole.SYSTEM, text=QUESTION_CLASSIFIER_SYSTEM_PROMPT.format(histories=memory_str)
|
||||
)
|
||||
prompt_messages.append(system_prompt_messages)
|
||||
user_prompt_message_1 = LLMNodeChatModelMessage(
|
||||
@ -315,5 +305,4 @@ class QuestionClassifierNode(LLMNode):
|
||||
)
|
||||
|
||||
else:
|
||||
raise InvalidModelTypeError(
|
||||
f"Model mode {model_mode} not support.")
|
||||
raise InvalidModelTypeError(f"Model mode {model_mode} not support.")
|
||||
|
||||
@ -338,7 +338,6 @@ class ToolNode(BaseNode[ToolNodeData]):
|
||||
data=message.message.data,
|
||||
label=message.message.label,
|
||||
metadata=message.message.metadata,
|
||||
node_id=self.node_id,
|
||||
)
|
||||
|
||||
# check if the agent log is already in the list
|
||||
|
||||
@ -198,7 +198,7 @@ app_site_fields = {
|
||||
"use_icon_as_answer_icon": fields.Boolean,
|
||||
}
|
||||
|
||||
leaked_dependency_fields = {"type": fields.String, "value": fields.Raw, "current_identifier": fields.String}
|
||||
leaked_dependency_fields = {"type": fields.String, "value": fields.Raw}
|
||||
|
||||
app_import_fields = {
|
||||
"id": fields.String,
|
||||
|
||||
@ -748,7 +748,7 @@ class DatasetKeywordTable(db.Model): # type: ignore[name-defined]
|
||||
if keyword_table_text:
|
||||
return json.loads(keyword_table_text.decode("utf-8"), cls=SetDecoder)
|
||||
return None
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logging.exception(f"Failed to load keyword table from file: {file_key}")
|
||||
return None
|
||||
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy.orm import Mapped
|
||||
|
||||
from extensions.ext_database import db
|
||||
from models.base import Base
|
||||
|
||||
from .types import StringUUID
|
||||
|
||||
|
||||
class StagingAccountWhitelist(Base):
|
||||
__tablename__ = "staging_account_whitelists"
|
||||
|
||||
__table_args__ = (
|
||||
db.PrimaryKeyConstraint("id", name="staging_account_whitelist_pkey"),
|
||||
db.Index("account_email_idx", "email"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
|
||||
email: Mapped[str] = db.Column(db.String(255), nullable=False)
|
||||
disabled: Mapped[bool] = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
created_at: Mapped[datetime] = db.Column(
|
||||
db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
|
||||
)
|
||||
updated_at: Mapped[datetime] = db.Column(
|
||||
db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
|
||||
)
|
||||
@ -33,7 +33,6 @@ from models.account import (
|
||||
TenantStatus,
|
||||
)
|
||||
from models.model import DifySetup
|
||||
from models.staging import StagingAccountWhitelist
|
||||
from services.billing_service import BillingService
|
||||
from services.errors.account import (
|
||||
AccountAlreadyInTenantError,
|
||||
@ -349,9 +348,6 @@ class AccountService:
|
||||
|
||||
@staticmethod
|
||||
def login(account: Account, *, ip_address: Optional[str] = None) -> TokenPair:
|
||||
if not AccountService.verify_account_whitelist(account.email):
|
||||
raise ValueError("Account is not whitelisted")
|
||||
|
||||
if ip_address:
|
||||
AccountService.update_login_info(account=account, ip_address=ip_address)
|
||||
|
||||
@ -383,9 +379,6 @@ class AccountService:
|
||||
if not account:
|
||||
raise ValueError("Invalid account")
|
||||
|
||||
if not AccountService.verify_account_whitelist(account.email):
|
||||
raise ValueError("Account is not whitelisted")
|
||||
|
||||
# Generate new access token and refresh token
|
||||
new_access_token = AccountService.get_account_jwt_token(account)
|
||||
new_refresh_token = _generate_refresh_token()
|
||||
@ -395,16 +388,6 @@ class AccountService:
|
||||
|
||||
return TokenPair(access_token=new_access_token, refresh_token=new_refresh_token)
|
||||
|
||||
@staticmethod
|
||||
def verify_account_whitelist(email: str) -> bool:
|
||||
with Session(db.engine) as session:
|
||||
return (
|
||||
session.query(StagingAccountWhitelist)
|
||||
.filter(StagingAccountWhitelist.email == email, StagingAccountWhitelist.disabled == False)
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def load_logged_in_account(*, account_id: str):
|
||||
return AccountService.load_user(account_id)
|
||||
@ -449,13 +432,9 @@ class AccountService:
|
||||
def send_email_code_login_email(
|
||||
cls, account: Optional[Account] = None, email: Optional[str] = None, language: Optional[str] = "en-US"
|
||||
):
|
||||
if email:
|
||||
if not AccountService.verify_account_whitelist(email):
|
||||
raise ValueError("Account is not whitelisted")
|
||||
elif account:
|
||||
if not AccountService.verify_account_whitelist(account.email):
|
||||
raise ValueError("Account is not whitelisted")
|
||||
|
||||
email = account.email if account else email
|
||||
if email is None:
|
||||
raise ValueError("Email must be provided.")
|
||||
if cls.email_code_login_rate_limiter.is_rate_limited(email):
|
||||
from controllers.console.auth.error import EmailCodeLoginRateLimitExceededError
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import logging
|
||||
import uuid
|
||||
from collections.abc import Mapping
|
||||
from enum import StrEnum
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
@ -257,16 +256,6 @@ class AppDslService:
|
||||
check_dependencies_pending_data = None
|
||||
if dependencies:
|
||||
check_dependencies_pending_data = [PluginDependency.model_validate(d) for d in dependencies]
|
||||
elif imported_version <= "0.1.5":
|
||||
if "workflow" in data:
|
||||
graph = data.get("workflow", {}).get("graph", {})
|
||||
dependencies_list = self._extract_dependencies_from_workflow_graph(graph)
|
||||
else:
|
||||
dependencies_list = self._extract_dependencies_from_model_config(data.get("model_config", {}))
|
||||
|
||||
check_dependencies_pending_data = DependenciesAnalysisService.generate_latest_dependencies(
|
||||
dependencies_list
|
||||
)
|
||||
|
||||
# Create or update app
|
||||
app = self._create_or_update_app(
|
||||
@ -569,7 +558,7 @@ class AppDslService:
|
||||
raise ValueError("Missing app configuration, please check.")
|
||||
|
||||
export_data["model_config"] = app_model_config.to_dict()
|
||||
dependencies = cls._extract_dependencies_from_model_config(app_model_config.to_dict())
|
||||
dependencies = cls._extract_dependencies_from_model_config(app_model_config)
|
||||
export_data["dependencies"] = [
|
||||
jsonable_encoder(d.model_dump())
|
||||
for d in DependenciesAnalysisService.generate_dependencies(
|
||||
@ -585,16 +574,6 @@ class AppDslService:
|
||||
:return: dependencies list format like ["langgenius/google"]
|
||||
"""
|
||||
graph = workflow.graph_dict
|
||||
dependencies = cls._extract_dependencies_from_workflow_graph(graph)
|
||||
return dependencies
|
||||
|
||||
@classmethod
|
||||
def _extract_dependencies_from_workflow_graph(cls, graph: Mapping) -> list[str]:
|
||||
"""
|
||||
Extract dependencies from workflow graph
|
||||
:param graph: Workflow graph
|
||||
:return: dependencies list format like ["langgenius/google"]
|
||||
"""
|
||||
dependencies = []
|
||||
for node in graph.get("nodes", []):
|
||||
try:
|
||||
@ -668,24 +647,24 @@ class AppDslService:
|
||||
return dependencies
|
||||
|
||||
@classmethod
|
||||
def _extract_dependencies_from_model_config(cls, model_config: Mapping) -> list[str]:
|
||||
def _extract_dependencies_from_model_config(cls, model_config: AppModelConfig) -> list[str]:
|
||||
"""
|
||||
Extract dependencies from model config
|
||||
:param model_config: model config dict
|
||||
:return: dependencies list format like ["langgenius/google"]
|
||||
:param model_config: AppModelConfig instance
|
||||
:return: dependencies list format like ["langgenius/google:1.0.0@abcdef1234567890"]
|
||||
"""
|
||||
dependencies = []
|
||||
|
||||
try:
|
||||
# completion model
|
||||
model_dict = model_config.get("model", {})
|
||||
model_dict = model_config.model_dict
|
||||
if model_dict:
|
||||
dependencies.append(
|
||||
DependenciesAnalysisService.analyze_model_provider_dependency(model_dict.get("provider", ""))
|
||||
)
|
||||
|
||||
# reranking model
|
||||
dataset_configs = model_config.get("dataset_configs", {})
|
||||
dataset_configs = model_config.dataset_configs_dict
|
||||
if dataset_configs:
|
||||
for dataset_config in dataset_configs.get("datasets", {}).get("datasets", []):
|
||||
if dataset_config.get("reranking_model"):
|
||||
@ -698,7 +677,7 @@ class AppDslService:
|
||||
)
|
||||
|
||||
# tools
|
||||
agent_configs = model_config.get("agent_mode", {})
|
||||
agent_configs = model_config.agent_mode_dict
|
||||
if agent_configs:
|
||||
for agent_config in agent_configs.get("tools", []):
|
||||
dependencies.append(
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from core.helper import marketplace
|
||||
from core.plugin.entities.plugin import GenericProviderID, PluginDependency, PluginInstallationSource
|
||||
from core.plugin.manager.plugin import PluginInstallationManager
|
||||
|
||||
@ -13,8 +12,6 @@ class DependenciesAnalysisService:
|
||||
"""
|
||||
try:
|
||||
tool_provider_id = GenericProviderID(tool_id)
|
||||
if tool_id in ["jina", "siliconflow"]:
|
||||
tool_provider_id.plugin_name = tool_provider_id.plugin_name + "_tool"
|
||||
return tool_provider_id.plugin_id
|
||||
except Exception as e:
|
||||
raise e
|
||||
@ -28,9 +25,6 @@ class DependenciesAnalysisService:
|
||||
"""
|
||||
try:
|
||||
generic_provider_id = GenericProviderID(model_provider_id)
|
||||
if model_provider_id == "google":
|
||||
generic_provider_id.plugin_name = "gemini"
|
||||
|
||||
return generic_provider_id.plugin_id
|
||||
except Exception as e:
|
||||
raise e
|
||||
@ -45,22 +39,15 @@ class DependenciesAnalysisService:
|
||||
required_plugin_unique_identifiers.append(dependency.value.plugin_unique_identifier)
|
||||
|
||||
manager = PluginInstallationManager()
|
||||
|
||||
# get leaked dependencies
|
||||
missing_plugins = manager.fetch_missing_dependencies(tenant_id, required_plugin_unique_identifiers)
|
||||
missing_plugin_unique_identifiers = {plugin.plugin_unique_identifier: plugin for plugin in missing_plugins}
|
||||
missing_plugin_unique_identifiers = manager.fetch_missing_dependencies(
|
||||
tenant_id, required_plugin_unique_identifiers
|
||||
)
|
||||
|
||||
leaked_dependencies = []
|
||||
for dependency in dependencies:
|
||||
unique_identifier = dependency.value.plugin_unique_identifier
|
||||
if unique_identifier in missing_plugin_unique_identifiers:
|
||||
leaked_dependencies.append(
|
||||
PluginDependency(
|
||||
type=dependency.type,
|
||||
value=dependency.value,
|
||||
current_identifier=missing_plugin_unique_identifiers[unique_identifier].current_identifier,
|
||||
)
|
||||
)
|
||||
leaked_dependencies.append(dependency)
|
||||
|
||||
return leaked_dependencies
|
||||
|
||||
@ -111,18 +98,3 @@ class DependenciesAnalysisService:
|
||||
raise ValueError(f"Unknown plugin source: {plugin.source}")
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def generate_latest_dependencies(cls, dependencies: list[str]) -> list[PluginDependency]:
|
||||
"""
|
||||
Generate the latest version of dependencies
|
||||
"""
|
||||
dependencies = list(set(dependencies))
|
||||
deps = marketplace.batch_fetch_plugin_manifests(dependencies)
|
||||
return [
|
||||
PluginDependency(
|
||||
type=PluginDependency.Type.Marketplace,
|
||||
value=PluginDependency.Marketplace(marketplace_plugin_unique_identifier=dep.latest_package_identifier),
|
||||
)
|
||||
for dep in deps
|
||||
]
|
||||
|
||||
@ -255,7 +255,7 @@ class BuiltinToolManageService:
|
||||
include_set=dify_config.POSITION_TOOL_INCLUDES_SET, # type: ignore
|
||||
exclude_set=dify_config.POSITION_TOOL_EXCLUDES_SET, # type: ignore
|
||||
data=provider_controller,
|
||||
name_func=lambda x: x.entity.identity.name,
|
||||
name_func=lambda x: x.identity.name,
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ class ToolTransformService:
|
||||
@staticmethod
|
||||
def repack_provider(tenant_id: str, provider: Union[dict, ToolProviderApiEntity]):
|
||||
"""
|
||||
repack provider
|
||||
repack provider
|
||||
|
||||
:param provider: the provider dict
|
||||
"""
|
||||
|
||||
@ -68,8 +68,7 @@ def test_executor_with_json_body_and_object_variable():
|
||||
system_variables={},
|
||||
user_inputs={},
|
||||
)
|
||||
variable_pool.add(["pre_node_id", "object"], {
|
||||
"name": "John Doe", "age": 30, "email": "john@example.com"})
|
||||
variable_pool.add(["pre_node_id", "object"], {"name": "John Doe", "age": 30, "email": "john@example.com"})
|
||||
|
||||
# Prepare the node data
|
||||
node_data = HttpRequestNodeData(
|
||||
@ -124,8 +123,7 @@ def test_executor_with_json_body_and_nested_object_variable():
|
||||
system_variables={},
|
||||
user_inputs={},
|
||||
)
|
||||
variable_pool.add(["pre_node_id", "object"], {
|
||||
"name": "John Doe", "age": 30, "email": "john@example.com"})
|
||||
variable_pool.add(["pre_node_id", "object"], {"name": "John Doe", "age": 30, "email": "john@example.com"})
|
||||
|
||||
# Prepare the node data
|
||||
node_data = HttpRequestNodeData(
|
||||
|
||||
@ -18,14 +18,6 @@ from models.enums import UserFrom
|
||||
from models.workflow import WorkflowNodeExecutionStatus, WorkflowType
|
||||
|
||||
|
||||
def test_plain_text_to_dict():
|
||||
assert _plain_text_to_dict("aa\n cc:") == {"aa": "", "cc": ""}
|
||||
assert _plain_text_to_dict("aa:bb\n cc:dd") == {"aa": "bb", "cc": "dd"}
|
||||
assert _plain_text_to_dict("aa:bb\n cc:dd\n") == {"aa": "bb", "cc": "dd"}
|
||||
assert _plain_text_to_dict("aa:bb\n\n cc : dd\n\n") == {
|
||||
"aa": "bb", "cc": "dd"}
|
||||
|
||||
|
||||
def test_http_request_node_binary_file(monkeypatch):
|
||||
data = HttpRequestNodeData(
|
||||
title="test",
|
||||
@ -191,8 +183,7 @@ def test_http_request_node_form_with_file(monkeypatch):
|
||||
|
||||
def attr_checker(*args, **kwargs):
|
||||
assert kwargs["data"] == {"name": "test"}
|
||||
assert kwargs["files"] == {
|
||||
"file": (None, b"test", "application/octet-stream")}
|
||||
assert kwargs["files"] == {"file": (None, b"test", "application/octet-stream")}
|
||||
return httpx.Response(200, content=b"")
|
||||
|
||||
monkeypatch.setattr(
|
||||
|
||||
@ -464,8 +464,6 @@ services:
|
||||
environment:
|
||||
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
|
||||
APP_API_URL: ${APP_API_URL:-}
|
||||
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-}
|
||||
MARKETPLACE_URL: ${MARKETPLACE_URL:-}
|
||||
SENTRY_DSN: ${WEB_SENTRY_DSN:-}
|
||||
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
|
||||
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
||||
|
||||
@ -8,7 +8,7 @@ import { logout } from '@/service/common'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
export type IAppSelector = {
|
||||
export interface IAppSelector {
|
||||
isMobile: boolean
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ import { useChatContext } from '@/app/components/base/chat/chat/context'
|
||||
|
||||
const MAX_DEPTH = 3
|
||||
|
||||
export type IGenerationItemProps = {
|
||||
export interface IGenerationItemProps {
|
||||
isWorkflow?: boolean
|
||||
workflowProcessData?: WorkflowProcess
|
||||
className?: string
|
||||
|
||||
@ -12,7 +12,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const maxTopK = (() => {
|
||||
const configValue = Number.parseInt(globalThis.document?.body?.getAttribute('data-public-top-k-max-value') || '', 10)
|
||||
const configValue = parseInt(globalThis.document?.body?.getAttribute('data-public-top-k-max-value') || '', 10)
|
||||
if (configValue && !isNaN(configValue))
|
||||
return configValue
|
||||
return 10
|
||||
@ -33,7 +33,7 @@ const TopKItem: FC<Props> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const handleParamChange = (key: string, value: number) => {
|
||||
let notOutRangeValue = Number.parseFloat(value.toFixed(2))
|
||||
let notOutRangeValue = parseFloat(value.toFixed(2))
|
||||
notOutRangeValue = Math.max(VALUE_LIMIT.min, notOutRangeValue)
|
||||
notOutRangeValue = Math.min(VALUE_LIMIT.max, notOutRangeValue)
|
||||
onChange(key, notOutRangeValue)
|
||||
|
||||
@ -51,11 +51,12 @@ const Toast = ({
|
||||
'top-0',
|
||||
'right-0',
|
||||
)}>
|
||||
<div className={`absolute inset-0 opacity-40 ${(type === 'success' && 'bg-toast-success-bg')
|
||||
<div className={`absolute inset-0 opacity-40 ${
|
||||
(type === 'success' && 'bg-toast-success-bg')
|
||||
|| (type === 'warning' && 'bg-toast-warning-bg')
|
||||
|| (type === 'error' && 'bg-toast-error-bg')
|
||||
|| (type === 'info' && 'bg-toast-info-bg')
|
||||
}`}
|
||||
}`}
|
||||
/>
|
||||
<div className={`flex ${size === 'md' ? 'gap-1' : 'gap-0.5'}`}>
|
||||
<div className={`flex justify-center items-center ${size === 'md' ? 'p-0.5' : 'p-1'}`}>
|
||||
@ -64,11 +65,8 @@ const Toast = ({
|
||||
{type === 'warning' && <RiAlertFill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-warning-secondary`} aria-hidden="true" />}
|
||||
{type === 'info' && <RiInformation2Fill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-accent`} aria-hidden="true" />}
|
||||
</div>
|
||||
<div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} flex-col items-start gap-1 grow z-10`}>
|
||||
<div className='flex items-center gap-1'>
|
||||
<div className='text-text-primary system-sm-semibold'>{message}</div>
|
||||
{customComponent}
|
||||
</div>
|
||||
<div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} flex-col items-start gap-1 grow`}>
|
||||
<div className='text-text-primary system-sm-semibold'>{message}</div>
|
||||
{children && <div className='text-text-secondary system-xs-regular'>
|
||||
{children}
|
||||
</div>
|
||||
@ -80,7 +78,7 @@ const Toast = ({
|
||||
</ActionButton>)
|
||||
}
|
||||
</div>
|
||||
</div >
|
||||
</div>
|
||||
}
|
||||
|
||||
export const ToastProvider = ({
|
||||
@ -132,7 +130,7 @@ Toast.notify = ({
|
||||
|
||||
root.render(
|
||||
<ToastContext.Provider value={{
|
||||
notify: () => { },
|
||||
notify: () => {},
|
||||
close: () => {
|
||||
if (holder) {
|
||||
root.unmount()
|
||||
|
||||
@ -39,7 +39,7 @@ export const DelimiterInput: FC<InputProps & { tooltip?: string }> = (props) =>
|
||||
}
|
||||
|
||||
export const MaxLengthInput: FC<InputNumberProps> = (props) => {
|
||||
const maxValue = Number.parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
|
||||
const maxValue = parseInt(globalThis.document?.body?.getAttribute('data-public-indexing-max-segmentation-tokens-length') || '4000', 10)
|
||||
|
||||
const { t } = useTranslation()
|
||||
return <FormField label={<div className='system-sm-semibold mb-1'>
|
||||
|
||||
@ -80,7 +80,7 @@ export const useSegmentListContext = (selector: (value: SegmentListContextValue)
|
||||
return useContextSelector(SegmentListContext, selector)
|
||||
}
|
||||
|
||||
type ICompletedProps = {
|
||||
interface ICompletedProps {
|
||||
embeddingAvailable: boolean
|
||||
showNewSegmentModal: boolean
|
||||
onNewSegmentModalChange: (state: boolean) => void
|
||||
|
||||
@ -106,8 +106,8 @@ const ModifyRetrievalModal: FC<Props> = ({
|
||||
borderColor: 'rgba(0, 0, 0, 0.05)',
|
||||
}}
|
||||
>
|
||||
<Button className='mr-2 shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||
<Button variant='primary' className='shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
|
||||
<Button className='mr-2 flex-shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||
<Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -15,7 +15,7 @@ import { asyncRunSafe } from '@/utils'
|
||||
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
||||
import promptS from '@/app/components/app/configuration/config-prompt/style.module.css'
|
||||
|
||||
type TextAreaWithButtonIProps = {
|
||||
interface TextAreaWithButtonIProps {
|
||||
datasetId: string
|
||||
onUpdateList: () => void
|
||||
setHitResult: (res: HitTestingResponse) => void
|
||||
|
||||
@ -16,6 +16,7 @@ const WorkplaceSelector = () => {
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { workspaces } = useWorkspacesContext()
|
||||
const currentWorkspace = workspaces.find(v => v.current)
|
||||
const isFreePlan = plan.type === 'sandbox'
|
||||
const handleSwitchWorkspace = async (tenant_id: string) => {
|
||||
try {
|
||||
if (currentWorkspace?.id === tenant_id)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
ModelItem,
|
||||
@ -9,9 +9,11 @@ import {
|
||||
CustomConfigurationStatusEnum,
|
||||
ModelTypeEnum,
|
||||
} from '../declarations'
|
||||
import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types'
|
||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import ConfigurationButton from './configuration-button'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { PluginType } from '@/app/components/plugins/types'
|
||||
import {
|
||||
useModelModalHandler,
|
||||
useUpdateModelList,
|
||||
@ -24,7 +26,8 @@ import StatusIndicators from './status-indicators'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { useModelInList, usePluginInfo } from '@/service/use-plugins'
|
||||
import { fetchPluginInfoFromMarketPlace } from '@/service/plugins'
|
||||
import { fetchModelProviderModelList } from '@/service/common'
|
||||
|
||||
export type AgentModelTriggerProps = {
|
||||
open?: boolean
|
||||
@ -63,14 +66,43 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
needsConfiguration,
|
||||
}
|
||||
}, [modelProviders, providerName])
|
||||
const [pluginInfo, setPluginInfo] = useState<PluginInfoFromMarketPlace | null>(null)
|
||||
const [isPluginChecked, setIsPluginChecked] = useState(false)
|
||||
const [installed, setInstalled] = useState(false)
|
||||
const [inModelList, setInModelList] = useState(false)
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const handleOpenModal = useModelModalHandler()
|
||||
|
||||
const { data: inModelList = false } = useModelInList(currentProvider, modelId)
|
||||
const { data: pluginInfo, isLoading: isPluginLoading } = usePluginInfo(providerName)
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (modelId && currentProvider) {
|
||||
try {
|
||||
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`)
|
||||
if (modelId && modelsData.data.find(item => item.model === modelId))
|
||||
setInModelList(true)
|
||||
}
|
||||
catch (error) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (providerName) {
|
||||
const parts = providerName.split('/')
|
||||
const org = parts[0]
|
||||
const name = parts[1]
|
||||
try {
|
||||
const pluginInfo = await fetchPluginInfoFromMarketPlace({ org, name })
|
||||
if (pluginInfo.data.plugin.category === PluginType.model)
|
||||
setPluginInfo(pluginInfo.data.plugin)
|
||||
}
|
||||
catch (error) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
setIsPluginChecked(true)
|
||||
})()
|
||||
}, [providerName, modelId, currentProvider])
|
||||
|
||||
if (modelId && isPluginLoading)
|
||||
if (modelId && !isPluginChecked)
|
||||
return <Loading />
|
||||
|
||||
return (
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { FC } from 'react'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type ModelTriggerProps = {
|
||||
open: boolean
|
||||
className?: string
|
||||
@ -11,7 +11,6 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
|
||||
open,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -25,13 +24,13 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className='text-[13px] text-text-tertiary truncate'
|
||||
title='Configure model'
|
||||
title='Select model'
|
||||
>
|
||||
{t('plugin.detailPanel.configureModel')}
|
||||
Select model
|
||||
</div>
|
||||
</div>
|
||||
<div className='shrink-0 flex items-center justify-center w-4 h-4'>
|
||||
<RiEqualizer2Line className='w-3.5 h-3.5 text-text-tertiary' />
|
||||
<RiArrowDownSLine className='w-3.5 h-3.5 text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { FC } from 'react'
|
||||
import type { ModelProvider } from '../declarations'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { Openai } from '@/app/components/base/icons/src/vender/other'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { Openai } from '@/app/components/base/icons/src/vender/other'
|
||||
import { AnthropicDark, AnthropicLight } from '@/app/components/base/icons/src/public/llm'
|
||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||
import { Theme } from '@/types/app'
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
import type { PluginDeclaration } from '../types'
|
||||
import { PluginType } from '../types'
|
||||
|
||||
export const toolNeko: PluginDeclaration = {
|
||||
plugin_unique_identifier: 'xxxxxx',
|
||||
version: '0.0.1',
|
||||
author: 'langgenius',
|
||||
name: 'neko',
|
||||
description: {
|
||||
en_US: 'Neko is a cute cat.',
|
||||
zh_Hans: '这是一只可爱的小猫。',
|
||||
pt_BR: 'Neko is a cute cat.',
|
||||
ja_JP: 'Neko is a cute cat.',
|
||||
},
|
||||
icon: '241e5209ecc8b5ce6b7a29a8e50388e9c75b89c3047c6ecd8e552f26de758883.svg',
|
||||
label: {
|
||||
en_US: 'Neko',
|
||||
zh_Hans: 'Neko',
|
||||
pt_BR: 'Neko',
|
||||
ja_JP: 'Neko',
|
||||
},
|
||||
category: 'extension' as any,
|
||||
created_at: '2024-07-12T08:03:44.658609Z',
|
||||
resource: {
|
||||
memory: 1048576,
|
||||
permission: {
|
||||
tool: {
|
||||
enabled: true,
|
||||
},
|
||||
model: {
|
||||
enabled: true,
|
||||
llm: true,
|
||||
text_embedding: false,
|
||||
rerank: false,
|
||||
tts: false,
|
||||
speech2text: false,
|
||||
moderation: false,
|
||||
},
|
||||
node: null,
|
||||
endpoint: {
|
||||
enabled: true,
|
||||
},
|
||||
storage: {
|
||||
enabled: true,
|
||||
size: 1048576,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
tools: null,
|
||||
models: null,
|
||||
endpoints: [
|
||||
'provider/neko.yaml',
|
||||
],
|
||||
},
|
||||
tags: [],
|
||||
verified: false,
|
||||
tool: null,
|
||||
model: null,
|
||||
endpoint: null,
|
||||
}
|
||||
|
||||
export const toolNotion = {
|
||||
type: PluginType.tool,
|
||||
org: 'Notion',
|
||||
name: 'notion page search',
|
||||
version: '1.2.0',
|
||||
latest_version: '1.3.0',
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'Notion Page Search',
|
||||
'zh-Hans': 'Notion 页面搜索',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
|
||||
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
|
||||
},
|
||||
}
|
||||
|
||||
export const toolNotionManifest: PluginDeclaration = {
|
||||
version: '1.2.0',
|
||||
author: 'Notion',
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
name: 'notion page search',
|
||||
category: PluginType.tool,
|
||||
label: {
|
||||
'en-US': 'Notion Page Search',
|
||||
'zh-Hans': 'Notion 页面搜索',
|
||||
},
|
||||
description: {
|
||||
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
|
||||
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
|
||||
},
|
||||
created_at: '2022-01-01',
|
||||
resource: {},
|
||||
plugins: {},
|
||||
verified: true,
|
||||
endpoint: {
|
||||
settings: [],
|
||||
endpoints: [],
|
||||
},
|
||||
tool: {
|
||||
} as any,
|
||||
model: {},
|
||||
}
|
||||
|
||||
export const extensionDallE = {
|
||||
type: PluginType.extension,
|
||||
org: 'OpenAI',
|
||||
name: 'DALL-E',
|
||||
version: '1.1.0',
|
||||
latest_version: '1.2.0',
|
||||
install_count: 1234,
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'DALL-E',
|
||||
'zh-Hans': 'DALL-E',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: A simple plugin to use OpenAI DALL-E model.',
|
||||
'zh-Hans': '一个使用 OpenAI DALL-E 模型的简单插件。',
|
||||
},
|
||||
}
|
||||
|
||||
export const modelGPT4 = {
|
||||
type: PluginType.model,
|
||||
org: 'OpenAI',
|
||||
name: 'GPT-4',
|
||||
version: '1.0.0',
|
||||
latest_version: '1.0.0',
|
||||
install_count: 99999,
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'GPT-4',
|
||||
'zh-Hans': 'GPT-4',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: A simple plugin to use OpenAI GPT-4 model.',
|
||||
'zh-Hans': '一个使用 OpenAI GPT-4 模型的简单插件。',
|
||||
},
|
||||
}
|
||||
|
||||
export const customTool = {
|
||||
type: PluginType.tool,
|
||||
name: 'notion page search',
|
||||
version: '1.2.0',
|
||||
latest_version: '1.3.0',
|
||||
icon: {
|
||||
content: '🕵️',
|
||||
background: '#FEF7C3',
|
||||
},
|
||||
label: {
|
||||
'en-US': 'Notion Page Search',
|
||||
'zh-Hans': 'Notion 页面搜索',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
|
||||
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
|
||||
},
|
||||
}
|
||||
@ -63,7 +63,7 @@ const Description = async ({
|
||||
</>
|
||||
)
|
||||
}
|
||||
</h2 >
|
||||
</h2>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ const Action: FC<Props> = ({
|
||||
hideDeleteConfirm()
|
||||
onDelete()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [installationId, onDelete])
|
||||
return (
|
||||
<div className='flex space-x-1'>
|
||||
|
||||
@ -321,6 +321,7 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
setControlSend(Date.now())
|
||||
// clear run once task status
|
||||
setControlStopResponding(Date.now())
|
||||
|
||||
// eslint-disable-next-line ts/no-use-before-define
|
||||
showResSidebar()
|
||||
}
|
||||
@ -400,6 +401,7 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
transfer_methods: file_upload.allowed_file_upload_methods || file_upload.allowed_upload_methods,
|
||||
// legacy of image upload compatible
|
||||
image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
|
||||
fileUploadConfig: appParams?.system_parameters,
|
||||
})
|
||||
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
|
||||
setPromptConfig({
|
||||
|
||||
@ -27,6 +27,7 @@ import SearchBox from '@/app/components/plugins/marketplace/search-box'
|
||||
import {
|
||||
Plus02,
|
||||
} from '@/app/components/base/icons/src/vender/line/general'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
type NodeSelectorProps = {
|
||||
open?: boolean
|
||||
|
||||
@ -162,7 +162,7 @@ export const useWorkflowRun = () => {
|
||||
else
|
||||
ttsUrl = `/apps/${params.appId}/text-to-audio`
|
||||
}
|
||||
const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', (_: any): any => { })
|
||||
const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', (_: any): any => {})
|
||||
|
||||
ssePost(
|
||||
url,
|
||||
|
||||
@ -61,7 +61,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
showMessageLogModal: state.showMessageLogModal,
|
||||
})))
|
||||
const showSingleRunPanel = useStore(s => s.showSingleRunPanel)
|
||||
const panelWidth = localStorage.getItem('workflow-node-panel-width') ? Number.parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420
|
||||
const panelWidth = localStorage.getItem('workflow-node-panel-width') ? parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420
|
||||
const {
|
||||
setPanelWidth,
|
||||
} = useWorkflow()
|
||||
|
||||
@ -112,10 +112,13 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
|
||||
if (userProfileResponse && !userProfileResponse.bodyUsed) {
|
||||
const result = await userProfileResponse.json()
|
||||
setUserProfile(result)
|
||||
const current_version = userProfileResponse.headers.get('x-version')
|
||||
const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env')
|
||||
const versionData = await fetchLanggeniusVersion({ url: '/version', params: { current_version } })
|
||||
setLangeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env })
|
||||
|
||||
if (process.env.NEXT_PUBLIC_SITE_ABOUT !== 'hide' && document?.body?.getAttribute('data-public-site-about') !== 'hide') {
|
||||
const current_version = userProfileResponse.headers.get('x-version')
|
||||
const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env')
|
||||
const versionData = await fetchLanggeniusVersion({ url: '/version', params: { current_version } })
|
||||
setLangeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env })
|
||||
}
|
||||
}
|
||||
}, [userProfileResponse])
|
||||
|
||||
|
||||
@ -2,6 +2,9 @@ const translation = {
|
||||
common: {
|
||||
welcome: 'आपका स्वागत है',
|
||||
appUnavailable: 'ऐप उपलब्ध नहीं है',
|
||||
appUnknownError: 'अज्ञात त्रुटि, कृपया पुनः प्रयास करें',
|
||||
// @ts-expect-error TODO: fix this
|
||||
// eslint-disable-next-line no-dupe-keys
|
||||
appUnknownError: 'ऐप अनुपलब्ध है',
|
||||
},
|
||||
chat: {
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
import type { I18nText } from '@/i18n/language'
|
||||
|
||||
export type CommonResponse = {
|
||||
export interface CommonResponse {
|
||||
result: 'success' | 'fail'
|
||||
}
|
||||
|
||||
export type OauthResponse = {
|
||||
export interface OauthResponse {
|
||||
redirect_url: string
|
||||
}
|
||||
|
||||
export type SetupStatusResponse = {
|
||||
export interface SetupStatusResponse {
|
||||
step: 'finished' | 'not_started'
|
||||
setup_at?: Date
|
||||
}
|
||||
|
||||
export type InitValidateStatusResponse = {
|
||||
export interface InitValidateStatusResponse {
|
||||
status: 'finished' | 'not_started'
|
||||
}
|
||||
|
||||
export type UserProfileResponse = {
|
||||
export interface UserProfileResponse {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
@ -33,13 +33,13 @@ export type UserProfileResponse = {
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export type UserProfileOriginResponse = {
|
||||
export interface UserProfileOriginResponse {
|
||||
json: () => Promise<UserProfileResponse>
|
||||
bodyUsed: boolean
|
||||
headers: any
|
||||
}
|
||||
|
||||
export type LangGeniusVersionResponse = {
|
||||
export interface LangGeniusVersionResponse {
|
||||
current_version: string
|
||||
latest_version: string
|
||||
version: string
|
||||
@ -49,7 +49,7 @@ export type LangGeniusVersionResponse = {
|
||||
current_env: string
|
||||
}
|
||||
|
||||
export type TenantInfoResponse = {
|
||||
export interface TenantInfoResponse {
|
||||
name: string
|
||||
created_at: string
|
||||
providers: Array<{
|
||||
@ -80,14 +80,14 @@ export enum ProviderName {
|
||||
Tongyi = 'tongyi',
|
||||
ChatGLM = 'chatglm',
|
||||
}
|
||||
export type ProviderAzureToken = {
|
||||
export interface ProviderAzureToken {
|
||||
openai_api_base?: string
|
||||
openai_api_key?: string
|
||||
}
|
||||
export type ProviderAnthropicToken = {
|
||||
export interface ProviderAnthropicToken {
|
||||
anthropic_api_key?: string
|
||||
}
|
||||
export type ProviderTokenType = {
|
||||
export interface ProviderTokenType {
|
||||
[ProviderName.OPENAI]: string
|
||||
[ProviderName.AZURE_OPENAI]: ProviderAzureToken
|
||||
[ProviderName.ANTHROPIC]: ProviderAnthropicToken
|
||||
@ -110,14 +110,14 @@ export type ProviderHosted = Provider & {
|
||||
quota_used: number
|
||||
}
|
||||
|
||||
export type AccountIntegrate = {
|
||||
export interface AccountIntegrate {
|
||||
provider: 'google' | 'github'
|
||||
created_at: number
|
||||
is_bound: boolean
|
||||
link: string
|
||||
}
|
||||
|
||||
export type IWorkspace = {
|
||||
export interface IWorkspace {
|
||||
id: string
|
||||
name: string
|
||||
plan: string
|
||||
@ -137,7 +137,7 @@ export type ICurrentWorkspace = Omit<IWorkspace, 'current'> & {
|
||||
}
|
||||
}
|
||||
|
||||
export type DataSourceNotionPage = {
|
||||
export interface DataSourceNotionPage {
|
||||
page_icon: null | {
|
||||
type: string | null
|
||||
url: string | null
|
||||
@ -156,7 +156,7 @@ export type NotionPage = DataSourceNotionPage & {
|
||||
|
||||
export type DataSourceNotionPageMap = Record<string, DataSourceNotionPage & { workspace_id: string }>
|
||||
|
||||
export type DataSourceNotionWorkspace = {
|
||||
export interface DataSourceNotionWorkspace {
|
||||
workspace_name: string
|
||||
workspace_id: string
|
||||
workspace_icon: string | null
|
||||
@ -166,7 +166,7 @@ export type DataSourceNotionWorkspace = {
|
||||
|
||||
export type DataSourceNotionWorkspaceMap = Record<string, DataSourceNotionWorkspace>
|
||||
|
||||
export type DataSourceNotion = {
|
||||
export interface DataSourceNotion {
|
||||
id: string
|
||||
provider: string
|
||||
is_bound: boolean
|
||||
@ -181,12 +181,12 @@ export enum DataSourceProvider {
|
||||
jinaReader = 'jinareader',
|
||||
}
|
||||
|
||||
export type FirecrawlConfig = {
|
||||
export interface FirecrawlConfig {
|
||||
api_key: string
|
||||
base_url: string
|
||||
}
|
||||
|
||||
export type DataSourceItem = {
|
||||
export interface DataSourceItem {
|
||||
id: string
|
||||
category: DataSourceCategory
|
||||
provider: DataSourceProvider
|
||||
@ -195,15 +195,15 @@ export type DataSourceItem = {
|
||||
updated_at: number
|
||||
}
|
||||
|
||||
export type DataSources = {
|
||||
export interface DataSources {
|
||||
sources: DataSourceItem[]
|
||||
}
|
||||
|
||||
export type GithubRepo = {
|
||||
export interface GithubRepo {
|
||||
stargazers_count: number
|
||||
}
|
||||
|
||||
export type PluginProvider = {
|
||||
export interface PluginProvider {
|
||||
tool_name: string
|
||||
is_enabled: boolean
|
||||
credentials: {
|
||||
@ -211,7 +211,7 @@ export type PluginProvider = {
|
||||
} | null
|
||||
}
|
||||
|
||||
export type FileUploadConfigResponse = {
|
||||
export interface FileUploadConfigResponse {
|
||||
batch_count_limit: number
|
||||
image_file_size_limit?: number | string // default is 10MB
|
||||
file_size_limit: number // default is 15MB
|
||||
@ -234,14 +234,14 @@ export type InvitationResponse = CommonResponse & {
|
||||
invitation_results: InvitationResult[]
|
||||
}
|
||||
|
||||
export type ApiBasedExtension = {
|
||||
export interface ApiBasedExtension {
|
||||
id?: string
|
||||
name?: string
|
||||
api_endpoint?: string
|
||||
api_key?: string
|
||||
}
|
||||
|
||||
export type CodeBasedExtensionForm = {
|
||||
export interface CodeBasedExtensionForm {
|
||||
type: string
|
||||
label: I18nText
|
||||
variable: string
|
||||
@ -252,17 +252,17 @@ export type CodeBasedExtensionForm = {
|
||||
max_length?: number
|
||||
}
|
||||
|
||||
export type CodeBasedExtensionItem = {
|
||||
export interface CodeBasedExtensionItem {
|
||||
name: string
|
||||
label: any
|
||||
form_schema: CodeBasedExtensionForm[]
|
||||
}
|
||||
export type CodeBasedExtension = {
|
||||
export interface CodeBasedExtension {
|
||||
module: string
|
||||
data: CodeBasedExtensionItem[]
|
||||
}
|
||||
|
||||
export type ExternalDataTool = {
|
||||
export interface ExternalDataTool {
|
||||
type?: string
|
||||
label?: string
|
||||
icon?: string
|
||||
@ -274,7 +274,7 @@ export type ExternalDataTool = {
|
||||
} & Partial<Record<string, any>>
|
||||
}
|
||||
|
||||
export type ModerateResponse = {
|
||||
export interface ModerateResponse {
|
||||
flagged: boolean
|
||||
text: string
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ function waitUntilTokenRefreshed() {
|
||||
const isRefreshingSignAvailable = function (delta: number) {
|
||||
const nowTime = new Date().getTime()
|
||||
const lastTime = globalThis.localStorage.getItem('last_refresh_time') || '0'
|
||||
return nowTime - Number.parseInt(lastTime) <= delta
|
||||
return nowTime - parseInt(lastTime) <= delta
|
||||
}
|
||||
|
||||
// only one request can send
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import type {
|
||||
ModelProvider,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { fetchModelProviderModelList } from '@/service/common'
|
||||
import { fetchPluginInfoFromMarketPlace } from '@/service/plugins'
|
||||
import type {
|
||||
DebugInfo as DebugInfoTypes,
|
||||
Dependency,
|
||||
@ -24,7 +19,6 @@ import type {
|
||||
uploadGitHubResponse,
|
||||
} from '@/app/components/plugins/types'
|
||||
import { TaskStatus } from '@/app/components/plugins/types'
|
||||
import { PluginType as PluginTypeEnum } from '@/app/components/plugins/types'
|
||||
import type {
|
||||
PluginsSearchParams,
|
||||
} from '@/app/components/plugins/marketplace/types'
|
||||
@ -456,40 +450,3 @@ export const useMutationCheckDependencies = () => {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useModelInList = (currentProvider?: ModelProvider, modelId?: string) => {
|
||||
return useQuery({
|
||||
queryKey: ['modelInList', currentProvider?.provider, modelId],
|
||||
queryFn: async () => {
|
||||
if (!modelId || !currentProvider) return false
|
||||
try {
|
||||
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`)
|
||||
return !!modelId && !!modelsData.data.find(item => item.model === modelId)
|
||||
}
|
||||
catch (error) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
enabled: !!modelId && !!currentProvider,
|
||||
})
|
||||
}
|
||||
|
||||
export const usePluginInfo = (providerName?: string) => {
|
||||
return useQuery({
|
||||
queryKey: ['pluginInfo', providerName],
|
||||
queryFn: async () => {
|
||||
if (!providerName) return null
|
||||
const parts = providerName.split('/')
|
||||
const org = parts[0]
|
||||
const name = parts[1]
|
||||
try {
|
||||
const response = await fetchPluginInfoFromMarketPlace({ org, name })
|
||||
return response.data.plugin.category === PluginTypeEnum.model ? response.data.plugin : null
|
||||
}
|
||||
catch (error) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
enabled: !!providerName,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user