Conversation
--bug=1054683 --user=刘瑞斌 【github#2831】知识库上传excel、应用编排文档内容提取节点中上传excel,单元格中有换行,导入后没有在一个单元格里显示 https://www.tapd.cn/57709429/s/1685274
--bug=1054775 --user=刘瑞斌 【应用编排】MCP调用节点,自定义工具参数建议支持选择数据类型 https://www.tapd.cn/57709429/s/1685483
--bug=1054724 --user=王孝刚 【github#2855】【应用】修改应用名称后,演示界面及访问链接的web标签页名称还是旧名称 https://www.tapd.cn/57709429/s/1685555
…esponse streaming
…ttribute handling
|
Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Pull request overview
This is a large feature/fix PR for the MaxKB platform that introduces: (1) a CAPTCHA + RSA-encrypted login flow, (2) "other file type" upload support in workflows, (3) per-dataset HNSW vector search indexes for improved embedding performance, (4) a new Regolo AI model provider, and numerous bug fixes, dependency upgrades, and UI polish improvements.
Changes:
- Login security hardening: captcha image generation, RSA-encrypted credential submission, and LDAP login separation
- Workflow and chat improvements: "other" file type support end-to-end,
is_resultflag on step nodes, form node field list exposure, hit-test API changed from GET to PUT - Infrastructure upgrades: PostgreSQL 15.8 → 15.14, psycopg2 → psycopg3, pinned Python dependencies, new Regolo model provider, per-dataset embedding HNSW indexes
Reviewed changes
Copilot reviewed 197 out of 197 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
ui/src/views/login/index.vue |
Adds captcha UI, RSA encryption of login payload, separates LDAP flow |
ui/src/views/login/forgot-password/index.vue |
Loads theme slogan before rendering |
ui/src/views/login/reset-password/index.vue |
Loads theme slogan, separates send/page loading |
ui/src/stores/modules/user.ts |
Adds rasKey, separates login/ldapLogin, fixes localStorage token key bug |
apps/users/serializers/user_serializers.py |
New CaptchaSerializer, reworks LoginSerializer with captcha + RSA decryption |
apps/users/views/user.py / apps/users/urls.py |
New /user/captcha endpoint |
ui/src/workflow/nodes/start-node/index.vue |
Adds "other" file type to file upload fields |
ui/src/workflow/nodes/base-node/index.vue |
Adds other and otherExtensions to file upload config |
ui/src/workflow/nodes/form-node/index.ts |
Adds get_node_field_list() for form field exposure |
ui/src/workflow/nodes/function-lib-node/index.vue |
Adds reference validation pre-check in validate() |
apps/application/flow/workflow_manage.py |
Passes other_list, fixes edge-based dependency evaluation, improves field sorting |
apps/application/flow/step_node/** |
All step nodes now gate answer_text on is_result node param |
apps/dataset/serializers/common_serializers.py |
Adds per-dataset HNSW index creation/deletion helpers |
apps/embedding/sql/*.sql |
Updates SQL to use cast-based vector comparison (::vector(N)) |
apps/setting/models_provider/impl/regolo_model_provider/ |
New Regolo AI provider (LLM, embedding, TTI, image) |
pyproject.toml |
Pins all Python dependencies to specific versions; adds captcha, migrates to psycopg |
ui/package.json |
Upgrades element-plus, md-editor-v3; adds nanoid, node-forge |
installer/Dockerfile* |
PostgreSQL 15.14, trixie base images |
| Locale files (en-US, zh-CN, zh-Hant) | New captcha, other file type, similarity threshold wording, MCP label updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| showQrCodeTab.value = false | ||
| loginForm.value = { | ||
| username: '', | ||
| password: '' | ||
| password: '', | ||
| captcha: '' | ||
| } |
There was a problem hiding this comment.
The changeMode function resets loginForm but omits the encryptedData field that was added to the form object. This means after a mode change, encryptedData retains whatever value it had from a previous login attempt instead of being reset to an empty string.
| } | ||
| try: | ||
| # 建立连接 | ||
| conn = psycopg2.connect(**conn_params) |
There was a problem hiding this comment.
The pgsql_template embedded in the migration 0004_functionlib_decimal_date.py still uses psycopg2 (which is not installed in the new requirements — it was replaced with psycopg), so any user executing this PostgreSQL function template at runtime will encounter an ImportError. The template should be updated to use psycopg (psycopg3) instead of psycopg2.
| .append_default_model_info( | ||
| ModelInfo('gpt-3.5-turbo', _('The latest gpt-3.5-turbo, updated with OpenAI adjustments'), ModelTypeConst.LLM, | ||
| openai_llm_model_credential, RegoloChatModel | ||
| )) |
There was a problem hiding this comment.
In regolo_model_provider.py, the default model info appended via append_default_model_info references a gpt-3.5-turbo model with a description "The latest gpt-3.5-turbo, updated with OpenAI adjustments". This is an OpenAI model, not a Regolo model, and it was apparently copied from another provider by mistake. The default model should be one of the Regolo-specific models listed above.
| # coding=utf-8 | ||
| import base64 | ||
| import os | ||
| import traceback | ||
| from typing import Dict | ||
|
|
||
| from langchain_core.messages import HumanMessage | ||
|
|
||
| from common import forms | ||
| from common.exception.app_exception import AppApiException | ||
| from common.forms import BaseForm, TooltipLabel | ||
| from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode | ||
| from django.utils.translation import gettext_lazy as _, gettext | ||
|
|
||
|
|
||
| class RegoloImageModelParams(BaseForm): | ||
| temperature = forms.SliderField(TooltipLabel(_('Temperature'), | ||
| _('Higher values make the output more random, while lower values make it more focused and deterministic')), | ||
| required=True, default_value=0.7, | ||
| _min=0.1, | ||
| _max=1.0, | ||
| _step=0.01, | ||
| precision=2) | ||
|
|
||
| max_tokens = forms.SliderField( | ||
| TooltipLabel(_('Output the maximum Tokens'), | ||
| _('Specify the maximum number of tokens that the model can generate')), | ||
| required=True, default_value=800, | ||
| _min=1, | ||
| _max=100000, | ||
| _step=1, | ||
| precision=0) | ||
|
|
||
|
|
||
| class RegoloImageModelCredential(BaseForm, BaseModelCredential): | ||
| api_base = forms.TextInputField('API URL', required=True) | ||
| api_key = forms.PasswordInputField('API Key', required=True) | ||
|
|
||
| def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider, | ||
| raise_exception=False): | ||
| model_type_list = provider.get_model_type_list() | ||
| if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))): | ||
| raise AppApiException(ValidCode.valid_error.value, | ||
| gettext('{model_type} Model type is not supported').format(model_type=model_type)) | ||
|
|
||
| for key in ['api_key']: | ||
| if key not in model_credential: | ||
| if raise_exception: | ||
| raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key)) | ||
| else: | ||
| return False | ||
| try: | ||
| model = provider.get_model(model_type, model_name, model_credential, **model_params) | ||
| res = model.stream([HumanMessage(content=[{"type": "text", "text": gettext('Hello')}])]) | ||
| for chunk in res: | ||
| print(chunk) | ||
| except Exception as e: | ||
| traceback.print_exc() | ||
| if isinstance(e, AppApiException): | ||
| raise e | ||
| if raise_exception: | ||
| raise AppApiException(ValidCode.valid_error.value, | ||
| gettext( | ||
| 'Verification failed, please check whether the parameters are correct: {error}').format( | ||
| error=str(e))) | ||
| else: | ||
| return False | ||
| return True | ||
|
|
||
| def encryption_dict(self, model: Dict[str, object]): | ||
| return {**model, 'api_key': super().encryption(model.get('api_key', ''))} | ||
|
|
||
| def get_model_params_setting_form(self, model_name): | ||
| return RegoloImageModelParams() |
There was a problem hiding this comment.
The regolo_model_provider.py registers a RegoloImage model in the image.py credential file (RegoloImageModelCredential) that requires an api_base field, but the model implementation hardcodes openai_api_base="https://api.regolo.ai/v1" and the RegoloLLMModelCredential (used for LLM and image models in the provider) only requires api_key. The RegoloImageModelCredential in credential/image.py is never referenced from regolo_model_provider.py - the image model entries in the provider use openai_llm_model_credential (which is RegoloLLMModelCredential). The credential/image.py file appears to be unused dead code.
| try: | ||
| model = provider.get_model(model_type, model_name, model_credential, **model_params) | ||
| res = model.check_auth() | ||
| print(res) |
There was a problem hiding this comment.
In regolo_model_provider/credential/tti.py, the is_valid method contains a bare print(res) debug statement that will output the model check result to stdout in production environments.
| lengthMessage: 'Length must be between 6 and 20 words' | ||
| }, | ||
| captcha: { | ||
| label: 'captcha', |
There was a problem hiding this comment.
The English locale captcha label is lowercase ('captcha') while the equivalent zh-CN and zh-Hant labels are capitalized ("验证码" / "驗證碼"). For consistency the English label should be capitalized as 'Captcha' to match the style of other labels in the same file (e.g., 'Username', 'Name', etc.).
| alreadyTurnedOn: 'Enabled', | ||
| notEnabled: 'Disabled', |
There was a problem hiding this comment.
There is an indentation inconsistency at lines 80–81 — alreadyTurnedOn is indented with 3 spaces while notEnabled uses the standard 6 spaces. This breaks the consistent indentation pattern in the surrounding code.
|
|
||
| # 关闭数据库连接 | ||
| db.close() |
There was a problem hiding this comment.
There is an unreachable code block in the mysql_template string embedded in the migration: db.close() is called after a return json_data statement (line 51-52 of the template). The database connection is never closed on the success path. This is a resource leak in the generated function template.
| <login-layout v-if="!loading" v-loading="loading || sendLoading"> | ||
| <LoginContainer | ||
| :subTitle=" | ||
| user.themeInfo?.slogan ? user.themeInfo?.slogan : $t('views.system.theme.defaultSlogan') | ||
| " | ||
| > |
There was a problem hiding this comment.
The onBeforeMount hook calls user.asyncGetProfile() to fetch the theme slogan before displaying the page. However, onBeforeMount runs before mounting and loading is a regular ref — the page template uses v-if="!loading" so the page stays hidden until the profile loads. The issue is that user.asyncGetProfile() likely calls the full profile endpoint (which requires authentication) and may fail for unauthenticated users on the login/forgot-password page, leaving the page permanently blank. A check should be added to handle auth failures gracefully on the guest pages.
| @action(methods="PUT", detail=False) | ||
| @swagger_auto_schema(operation_summary=_("Hit Test List"), operation_id=_("Hit Test List"), | ||
| manual_parameters=CommonApi.HitTestApi.get_request_params_api(), |
There was a problem hiding this comment.
In application/views/application_views.py, the HitTest view still references manual_parameters=CommonApi.HitTestApi.get_request_params_api() in the swagger decorator, but the get_request_params_api method was replaced with get_request_body_api() in common_api.py. The swagger decorator should use request_body=CommonApi.HitTestApi.get_request_body_api() to match the now-PUT method and the equivalent change made in dataset/views/dataset.py.
What this PR does / why we need it?
Summary of your change
Please indicate you've done the following: