Skip to content

Commit eb411fe

Browse files
committed
Merge branch 'feat/add-inner-api' into deploy/dev
2 parents b43570b + 859fbf3 commit eb411fe

File tree

185 files changed

+2335
-612
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

185 files changed

+2335
-612
lines changed

.github/actions/setup-poetry/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ inputs:
88
poetry-version:
99
description: Poetry version to set up
1010
required: true
11-
default: '1.8.4'
11+
default: '2.0.1'
1212
poetry-lockfile:
1313
description: Path to the Poetry lockfile to restore cache from
1414
required: true

.github/workflows/api-tests.yml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,23 @@ jobs:
4242
run: poetry install -C api --with dev
4343

4444
- name: Check dependencies in pyproject.toml
45-
run: poetry run -C api bash dev/pytest/pytest_artifacts.sh
45+
run: poetry run -P api bash dev/pytest/pytest_artifacts.sh
4646

4747
- name: Run Unit tests
48-
run: poetry run -C api bash dev/pytest/pytest_unit_tests.sh
48+
run: poetry run -P api bash dev/pytest/pytest_unit_tests.sh
4949

5050
- name: Run ModelRuntime
51-
run: poetry run -C api bash dev/pytest/pytest_model_runtime.sh
51+
run: poetry run -P api bash dev/pytest/pytest_model_runtime.sh
5252

5353
- name: Run dify config tests
54-
run: poetry run -C api python dev/pytest/pytest_config_tests.py
54+
run: poetry run -P api python dev/pytest/pytest_config_tests.py
5555

5656
- name: Run Tool
57-
run: poetry run -C api bash dev/pytest/pytest_tools.sh
57+
run: poetry run -P api bash dev/pytest/pytest_tools.sh
5858

5959
- name: Run mypy
6060
run: |
61-
pushd api
62-
poetry run python -m mypy --install-types --non-interactive .
63-
popd
61+
poetry run -C api python -m mypy --install-types --non-interactive .
6462
6563
- name: Set up dotenvs
6664
run: |
@@ -80,4 +78,4 @@ jobs:
8078
ssrf_proxy
8179
8280
- name: Run Workflow
83-
run: poetry run -C api bash dev/pytest/pytest_workflow.sh
81+
run: poetry run -P api bash dev/pytest/pytest_workflow.sh

.github/workflows/style.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ jobs:
3838
if: steps.changed-files.outputs.any_changed == 'true'
3939
run: |
4040
poetry run -C api ruff --version
41-
poetry run -C api ruff check ./api
42-
poetry run -C api ruff format --check ./api
41+
poetry run -C api ruff check ./
42+
poetry run -C api ruff format --check ./
4343
4444
- name: Dotenv check
4545
if: steps.changed-files.outputs.any_changed == 'true'
46-
run: poetry run -C api dotenv-linter ./api/.env.example ./web/.env.example
46+
run: poetry run -P api dotenv-linter ./api/.env.example ./web/.env.example
4747

4848
- name: Lint hints
4949
if: failure()

.github/workflows/vdb-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ jobs:
7070
tidb
7171
7272
- name: Test Vector Stores
73-
run: poetry run -C api bash dev/pytest/pytest_vdb.sh
73+
run: poetry run -P api bash dev/pytest/pytest_vdb.sh

api/.ruff.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ ignore = [
5353
"FURB152", # math-constant
5454
"UP007", # non-pep604-annotation
5555
"UP032", # f-string
56+
"UP045", # non-pep604-annotation-optional
5657
"B005", # strip-with-multi-characters
5758
"B006", # mutable-argument-default
5859
"B007", # unused-loop-control-variable
5960
"B026", # star-arg-unpacking-after-keyword-arg
61+
"B903", # class-as-data-structure
6062
"B904", # raise-without-from-inside-except
6163
"B905", # zip-without-explicit-strict
6264
"N806", # non-lowercase-variable-in-function

api/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ FROM python:3.12-slim-bookworm AS base
44
WORKDIR /app/api
55

66
# Install Poetry
7-
ENV POETRY_VERSION=1.8.4
7+
ENV POETRY_VERSION=2.0.1
88

99
# if you located in China, you can use aliyun mirror to speed up
1010
# RUN pip install --no-cache-dir poetry==${POETRY_VERSION} -i https://mirrors.aliyun.com/pypi/simple/

api/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@
7979
2. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`
8080

8181
```bash
82-
poetry run -C api bash dev/pytest/pytest_all_tests.sh
82+
poetry run -P api bash dev/pytest/pytest_all_tests.sh
8383
```

api/configs/feature/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class EndpointConfig(BaseSettings):
146146
)
147147

148148
CONSOLE_WEB_URL: str = Field(
149-
description="Base URL for the console web interface," "used for frontend references and CORS configuration",
149+
description="Base URL for the console web interface,used for frontend references and CORS configuration",
150150
default="",
151151
)
152152

api/configs/feature/hosted_service/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class HostedFetchAppTemplateConfig(BaseSettings):
181181
"""
182182

183183
HOSTED_FETCH_APP_TEMPLATES_MODE: str = Field(
184-
description="Mode for fetching app templates: remote, db, or builtin" " default to remote,",
184+
description="Mode for fetching app templates: remote, db, or builtin default to remote,",
185185
default="remote",
186186
)
187187

api/configs/packaging/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings):
99

1010
CURRENT_VERSION: str = Field(
1111
description="Dify version",
12-
default="0.15.1",
12+
default="0.15.2",
1313
)
1414

1515
COMMIT_SHA: str = Field(

api/controllers/console/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def post(self):
5656

5757
app = App.query.filter(App.id == args["app_id"]).first()
5858
if not app:
59-
raise NotFound(f'App \'{args["app_id"]}\' is not found')
59+
raise NotFound(f"App '{args['app_id']}' is not found")
6060

6161
site = app.site
6262
if not site:

api/controllers/console/datasets/datasets.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def post(self):
457457
)
458458
except LLMBadRequestError:
459459
raise ProviderNotInitializeError(
460-
"No Embedding Model available. Please configure a valid provider " "in the Settings -> Model Provider."
460+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
461461
)
462462
except ProviderTokenNotInitError as ex:
463463
raise ProviderNotInitializeError(ex.description)
@@ -619,8 +619,7 @@ def get(self):
619619
vector_type = dify_config.VECTOR_STORE
620620
match vector_type:
621621
case (
622-
VectorType.MILVUS
623-
| VectorType.RELYT
622+
VectorType.RELYT
624623
| VectorType.PGVECTOR
625624
| VectorType.TIDB_VECTOR
626625
| VectorType.CHROMA
@@ -645,6 +644,7 @@ def get(self):
645644
| VectorType.TIDB_ON_QDRANT
646645
| VectorType.LINDORM
647646
| VectorType.COUCHBASE
647+
| VectorType.MILVUS
648648
):
649649
return {
650650
"retrieval_method": [

api/controllers/console/datasets/datasets_document.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,7 @@ def post(self):
350350
)
351351
except InvokeAuthorizationError:
352352
raise ProviderNotInitializeError(
353-
"No Embedding Model available. Please configure a valid provider "
354-
"in the Settings -> Model Provider."
353+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
355354
)
356355
except ProviderTokenNotInitError as ex:
357356
raise ProviderNotInitializeError(ex.description)
@@ -526,8 +525,7 @@ def get(self, dataset_id, batch):
526525
return response.model_dump(), 200
527526
except LLMBadRequestError:
528527
raise ProviderNotInitializeError(
529-
"No Embedding Model available. Please configure a valid provider "
530-
"in the Settings -> Model Provider."
528+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
531529
)
532530
except ProviderTokenNotInitError as ex:
533531
raise ProviderNotInitializeError(ex.description)

api/controllers/console/datasets/datasets_segments.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,7 @@ def patch(self, dataset_id, document_id, action):
168168
)
169169
except LLMBadRequestError:
170170
raise ProviderNotInitializeError(
171-
"No Embedding Model available. Please configure a valid provider "
172-
"in the Settings -> Model Provider."
171+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
173172
)
174173
except ProviderTokenNotInitError as ex:
175174
raise ProviderNotInitializeError(ex.description)
@@ -217,8 +216,7 @@ def post(self, dataset_id, document_id):
217216
)
218217
except LLMBadRequestError:
219218
raise ProviderNotInitializeError(
220-
"No Embedding Model available. Please configure a valid provider "
221-
"in the Settings -> Model Provider."
219+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
222220
)
223221
except ProviderTokenNotInitError as ex:
224222
raise ProviderNotInitializeError(ex.description)
@@ -267,8 +265,7 @@ def patch(self, dataset_id, document_id, segment_id):
267265
)
268266
except LLMBadRequestError:
269267
raise ProviderNotInitializeError(
270-
"No Embedding Model available. Please configure a valid provider "
271-
"in the Settings -> Model Provider."
268+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
272269
)
273270
except ProviderTokenNotInitError as ex:
274271
raise ProviderNotInitializeError(ex.description)
@@ -437,8 +434,7 @@ def post(self, dataset_id, document_id, segment_id):
437434
)
438435
except LLMBadRequestError:
439436
raise ProviderNotInitializeError(
440-
"No Embedding Model available. Please configure a valid provider "
441-
"in the Settings -> Model Provider."
437+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
442438
)
443439
except ProviderTokenNotInitError as ex:
444440
raise ProviderNotInitializeError(ex.description)

api/controllers/inner_api/workspace/workspace.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
from flask_restful import Resource, reqparse # type: ignore
24

35
from controllers.console.wraps import setup_required
@@ -29,4 +31,34 @@ def post(self):
2931
return {"message": "enterprise workspace created."}
3032

3133

34+
class EnterpriseWorkspaceNoOwnerEmail(Resource):
35+
@setup_required
36+
@inner_api_only
37+
def post(self):
38+
parser = reqparse.RequestParser()
39+
parser.add_argument("name", type=str, required=True, location="json")
40+
args = parser.parse_args()
41+
42+
tenant = TenantService.create_tenant(args["name"], is_from_dashboard=True)
43+
44+
tenant_was_created.send(tenant)
45+
46+
resp = {
47+
"id": tenant.id,
48+
"name": tenant.name,
49+
"encrypt_public_key": tenant.encrypt_public_key,
50+
"plan": tenant.plan,
51+
"status": tenant.status,
52+
"custom_config": json.loads(tenant.custom_config) if tenant.custom_config else {},
53+
"created_at": tenant.created_at.isoformat() if tenant.created_at else None,
54+
"updated_at": tenant.updated_at.isoformat() if tenant.updated_at else None,
55+
}
56+
57+
return {
58+
"message": "enterprise workspace created.",
59+
"tenant": resp,
60+
}
61+
62+
3263
api.add_resource(EnterpriseWorkspace, "/enterprise/workspace")
64+
api.add_resource(EnterpriseWorkspaceNoOwnerEmail, "/enterprise/workspace/ownerless")

api/controllers/service_api/dataset/segment.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ def post(self, tenant_id, dataset_id, document_id):
5353
)
5454
except LLMBadRequestError:
5555
raise ProviderNotInitializeError(
56-
"No Embedding Model available. Please configure a valid provider "
57-
"in the Settings -> Model Provider."
56+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
5857
)
5958
except ProviderTokenNotInitError as ex:
6059
raise ProviderNotInitializeError(ex.description)
@@ -95,8 +94,7 @@ def get(self, tenant_id, dataset_id, document_id):
9594
)
9695
except LLMBadRequestError:
9796
raise ProviderNotInitializeError(
98-
"No Embedding Model available. Please configure a valid provider "
99-
"in the Settings -> Model Provider."
97+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
10098
)
10199
except ProviderTokenNotInitError as ex:
102100
raise ProviderNotInitializeError(ex.description)
@@ -175,8 +173,7 @@ def post(self, tenant_id, dataset_id, document_id, segment_id):
175173
)
176174
except LLMBadRequestError:
177175
raise ProviderNotInitializeError(
178-
"No Embedding Model available. Please configure a valid provider "
179-
"in the Settings -> Model Provider."
176+
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
180177
)
181178
except ProviderTokenNotInitError as ex:
182179
raise ProviderNotInitializeError(ex.description)

api/controllers/service_api/wraps.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,11 @@ def validate_and_get_api_token(scope: str | None = None):
195195
with Session(db.engine, expire_on_commit=False) as session:
196196
update_stmt = (
197197
update(ApiToken)
198-
.where(ApiToken.token == auth_token, ApiToken.last_used_at < cutoff_time, ApiToken.type == scope)
198+
.where(
199+
ApiToken.token == auth_token,
200+
(ApiToken.last_used_at.is_(None) | (ApiToken.last_used_at < cutoff_time)),
201+
ApiToken.type == scope,
202+
)
199203
.values(last_used_at=current_time)
200204
.returning(ApiToken)
201205
)

api/core/agent/cot_agent_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: L
172172

173173
self.save_agent_thought(
174174
agent_thought=agent_thought,
175-
tool_name=scratchpad.action.action_name if scratchpad.action else "",
175+
tool_name=(scratchpad.action.action_name if scratchpad.action and not scratchpad.is_final() else ""),
176176
tool_input={scratchpad.action.action_name: scratchpad.action.action_input} if scratchpad.action else {},
177177
tool_invoke_meta={},
178178
thought=scratchpad.thought or "",

api/core/app/apps/base_app_queue_manager.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ def _check_for_sqlalchemy_models(self, data: Any):
167167
else:
168168
if isinstance(data, DeclarativeMeta) or hasattr(data, "_sa_instance_state"):
169169
raise TypeError(
170-
"Critical Error: Passing SQLAlchemy Model instances "
171-
"that cause thread safety issues is not allowed."
170+
"Critical Error: Passing SQLAlchemy Model instances that cause thread safety issues is not allowed."
172171
)
173172

174173

api/core/app/apps/message_based_app_generator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def _get_conversation_by_user(
8989
Conversation.id == conversation_id,
9090
Conversation.app_id == app_model.id,
9191
Conversation.status == "normal",
92+
Conversation.is_deleted.is_(False),
9293
]
9394

9495
if isinstance(user, Account):

api/core/app/task_pipeline/message_cycle_manage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def _message_file_to_stream_response(self, event: QueueMessageFileEvent) -> Opti
145145

146146
# get extension
147147
if "." in message_file.url:
148-
extension = f'.{message_file.url.split(".")[-1]}'
148+
extension = f".{message_file.url.split('.')[-1]}"
149149
if len(extension) > 10:
150150
extension = ".bin"
151151
else:

api/core/external_data_tool/api/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ def query(self, inputs: dict, query: Optional[str] = None) -> str:
6262

6363
if not api_based_extension:
6464
raise ValueError(
65-
"[External data tool] API query failed, variable: {}, "
66-
"error: api_based_extension_id is invalid".format(self.variable)
65+
"[External data tool] API query failed, variable: {}, error: api_based_extension_id is invalid".format(
66+
self.variable
67+
)
6768
)
6869

6970
# decrypt api_key

api/core/file/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def to_dict(self) -> Mapping[str, str | int | None]:
9090
def markdown(self) -> str:
9191
url = self.generate_url()
9292
if self.type == FileType.IMAGE:
93-
text = f'![{self.filename or ""}]({url})'
93+
text = f"![{self.filename or ''}]({url})"
9494
else:
9595
text = f"[{self.filename or url}]({url})"
9696

api/core/llm_generator/prompts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
SUGGESTED_QUESTIONS_AFTER_ANSWER_INSTRUCTION_PROMPT = (
132132
"Please help me predict the three most likely questions that human would ask, "
133133
"and keeping each question under 20 characters.\n"
134-
"MAKE SURE your output is the SAME language as the Assistant's latest response"
134+
"MAKE SURE your output is the SAME language as the Assistant's latest response. "
135135
"The output must be an array in JSON format following the specified schema:\n"
136136
'["question1","question2","question3"]\n'
137137
)

api/core/model_runtime/model_providers/azure_openai/llm/llm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def validate_credentials(self, model: str, credentials: dict) -> None:
108108
ai_model_entity = self._get_ai_model_entity(base_model_name=base_model_name, model=model)
109109

110110
if not ai_model_entity:
111-
raise CredentialsValidateFailedError(f'Base Model Name {credentials["base_model_name"]} is invalid')
111+
raise CredentialsValidateFailedError(f"Base Model Name {credentials['base_model_name']} is invalid")
112112

113113
try:
114114
client = AzureOpenAI(**self._to_credential_kwargs(credentials))

api/core/model_runtime/model_providers/azure_openai/text_embedding/text_embedding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def validate_credentials(self, model: str, credentials: dict) -> None:
130130
raise CredentialsValidateFailedError("Base Model Name is required")
131131

132132
if not self._get_ai_model_entity(credentials["base_model_name"], model):
133-
raise CredentialsValidateFailedError(f'Base Model Name {credentials["base_model_name"]} is invalid')
133+
raise CredentialsValidateFailedError(f"Base Model Name {credentials['base_model_name']} is invalid")
134134

135135
try:
136136
credentials_kwargs = self._to_credential_kwargs(credentials)

0 commit comments

Comments
 (0)