Claude/hardcore bassi 1697a9#5251
Closed
youngzs wants to merge 7 commits into
Closed
Conversation
Document the dual-process Django/Vue setup, app boundaries (chat vs. workflow engines under application/, models_provider abstraction, etc.), canonical entry point (main.py), and boot quirks worth knowing (SERVER_NAME settings split, embedded Postgres/Redis 127.0.0.1 gate, migrate retry loop, hmac_signed_serializer Celery tasks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implement the white-label display (theme) settings API in the community edition, so CE users can rebrand the platform (logo, favicon, login page background, title, slogan, theme color, header links) from the existing admin UI at /system/setting/theme without needing X-PACK. The frontend page and ThemeApi client already ship in CE (ui/src/views/system-setting/theme/index.vue, ui/src/api/system-settings/theme.ts) but the backend endpoints they hit (GET /display/info, PUT /display/update) only existed in the closed-source X-PACK. This commit adds them, matching the frontend contract field-for-field. Backend changes: - SystemSetting model: add SettingType.THEME = 3 with a single-row migration (0006) that only widens the choices enum. JSON config lives in SystemSetting(type=THEME).meta. - New DisplayInfoSerializer with one() + Create.update_or_save() mirroring the EmailSettingSerializer pattern. DEFAULTS dict serves first-install / missing-record fallback. Image fields are multi- typed (UploadedFile / kept-old URL / empty) and handled via initial_data because DRF Serializer cannot express that polymorphism cleanly. Old images are best-effort deleted on replacement. - Image uploads land in the existing knowledge.File model (source_type=SYSTEM, source_id='THEME') as PostgreSQL Large Objects, and the public-by-design oss.FileRetrievalView serves them via the './oss/file/<uuid>' relative URL convention used elsewhere in the app. This means the login page (unauthenticated) can render the logo and background without an extra custom asset endpoint. - DisplayInfo.get is intentionally authentication_classes=[] + permission_classes=[] because the login screen, which is shown pre-auth, has to load these values. - DisplayUpdate.put requires TokenAuth + has_permissions( APPEARANCE_SETTINGS_EDIT, ADMIN). It accepts both multipart/form- data (for file uploads) and application/json. The @log audit decorator scrubs file bytes from the body to keep operation_log rows small. - Routes wired at /admin/api/display/info and /admin/api/display/update under the existing system_manage URL include. Frontend gating (router edition check + user.ts CE branch that skips theme.theme()) will be unlocked in a follow-up commit; the backend can be smoke-tested today via curl against the two new endpoints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
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 |
DisplayInfo.get was explicitly setting authentication_classes = [] and permission_classes = [], intending to make the endpoint public. But MaxKB does NOT include django.contrib.auth in INSTALLED_APPS, and DRF's default behavior for an anonymous request hitting an empty authentication_classes is to fall back to api_settings.UNAUTHENTICATED_USER, which lazily imports django.contrib.auth.models.AnonymousUser — and that import then tries to register the Permission model, which fails with: RuntimeError: Model class django.contrib.auth.models.Permission doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS. The fix: omit authentication_classes / permission_classes entirely and inherit REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES, which MaxKB sets to common.auth.authenticate.AnonymousAuthentication — the same pattern oss.FileRetrievalView uses to expose public file URLs. Verified end-to-end against a 1panel/maxkb:v2.9.0 deployment: GET /admin/api/display/info -> HTTP 200, returns DEFAULTS dict PUT /admin/api/display/update (no token) -> HTTP 401 未登录 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… page Now that /display/info and /display/update are open-source backend endpoints (previous commits), let the community edition actually reach them: 1. **Router unlock** (`ui/src/router/modules/system.ts`) Drop `[EditionConst.IS_EE, EditionConst.IS_PE]` from the two `ComplexPermission` blocks that gate the **system → 系统设置 → 外观设置** menu and its `/system/setting/theme` child route. Now ADMIN role + APPEARANCE_SETTINGS_READ perm is sufficient, regardless of edition. 2. **User store CE branch removal** (`ui/src/stores/modules/user.ts`) `asyncGetProfile` was hardcoding `themeInfo = defaultPlatformSetting` for CE and only calling `theme.theme()` (the API) for EE/PE. Replace that branch with an unconditional `await theme.theme()`, so the CE backend's `/display/info` response is actually used. Drop the now-unused `defaultPlatformSetting` import. 3. **Login page preload** (`ui/src/layout/login-layout/LoginLayout.vue`) The login screen is shown pre-authentication and previously had no way to fetch custom theme/logo until after login. Add an `onMounted` that calls `theme.theme()` when `themeInfo` is not yet populated. The backend GET is public for exactly this reason; on failure we silently fall back to defaults so the login page stays usable. Verified end-to-end against 1panel/maxkb:v2.9.0 + public domain: - GET https://maxkb.yangyubo.cn/admin/api/display/info → 200 + defaults - Admin HTML now references the rebuilt bundle (admin-B5sCJGak.js) - PUT /display/update without token → 401 未登录 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the markdown-table chunking for .xlsx / .xls / .csv with
per-row key-value paragraphs that include filename + sheet + row index
+ header→value pairs. Vector retrieval against business questions
("张三的订单金额是多少?") can now hit specific rows, while the
previous output ("| 张三 | SO-0042 | 12000 |") lost semantic context
once chunks were split and headers stopped repeating.
New paragraph shape (xlsx example):
TITLE: 订单明细.xlsx / Q1销售 / 第47行
CONTENT:
文件:订单明细.xlsx
工作表:Q1销售
行号:第 47 行
---
客户名称:张三
订单编号:SO-0042
金额:12000
下单时间:2026-03-15
Design notes:
- New `apps/common/handle/impl/text/excel_kv_common.py` factors the
serialization, so xlsx / xls / csv share `normalize_value`,
`normalize_headers`, `make_kv_paragraph`. This also keeps date /
float-int / image-cell / empty-row / empty-cell handling consistent.
- `normalize_value` collapses `12000.0` → `"12000"`, renders midnight
datetimes as date-only, and preserves the existing image-cell
behavior (``).
- Merged cells in xlsx are now properly resolved on the data path
(previously `handle()` did not call `fill_merged_cells`; only the
unrelated `get_content()` workflow path did).
- Multi-sheet workbooks still split into one document per sheet (old
behavior preserved), but each paragraph's "文件:xxx" line now shows
the real file name regardless of which sheet became the doc name.
- xls cell type is honored via xlrd's `xldate_as_datetime` so dates
stop appearing as float serials.
- CSV omits the 工作表 line (no such concept), uses 1-based row index
matching the file's actual row numbering.
- Empty rows are skipped entirely; empty cells skip just that one key
to avoid "客户名称:" noise that would hurt embedding quality.
- `get_content()` (used by workflow document-extract nodes etc.) is
untouched — it still produces the original markdown-table output,
because changing it would have unintended ripple effects elsewhere.
- The existing QA-flavored parsers under apps/common/handle/impl/qa/
are not touched; standard "question | answer" Excel sheets continue
to flow through that dedicated pipeline.
Verified end-to-end on 1panel/maxkb:v2.9.0 via Django shell:
- Multi-sheet xlsx splits correctly into one doc per sheet
- All 12 design points (file/sheet/row label, dates, floats, empty
rows, empty cells, CSV-no-sheet) confirmed by smoke test
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add an OCR pipeline for the knowledge ingestion path so that:
- Image files (.png/.jpg/.jpeg/.bmp/.tif/.tiff/.webp/.heif/.heic) are
auto-OCR'd and indexed as text instead of being rejected.
- Scanned (image-only) pages inside PDFs fall back to OCR when pypdf
can't extract enough text. Mixed PDFs (text + scan) work correctly —
only the empty pages get OCR'd.
Two OCR backends, switchable in the new "系统设置 → OCR 设置" page:
1. **Vision LLM mode (default, recommended)** —
Reuses any IMAGE-type model already registered in models_provider
(gpt-4o, claude-3-5-sonnet, gemini-1.5-pro, qwen-vl, glm-4v, …).
Zero new Python deps for this mode; the langchain ChatModel
instance is loaded via the existing
`get_model_instance_by_model_workspace_id` factory. Prompt is
customizable; default tells the model to "faithfully transcribe,
not summarize".
2. **Local OCR mode (offline)** —
Backed by rapidocr-onnxruntime, lazy-imported. Not added to
pyproject.toml (CPU build is ~150 MB) — admin installs it manually
inside the container when this mode is selected. The "Test" button
on the settings page surfaces a clean error if the dependency is
missing.
Code layout:
apps/common/handle/impl/ocr/
provider.py — OcrProvider ABC + factory + DEFAULT_PROMPT
vision_llm_provider.py
local_provider.py — lazy-import rapidocr_onnxruntime
apps/common/handle/impl/text/image_ocr_split_handle.py
Routes image extensions through the OCR provider, then through
the same SplitModel as text files so user chunk-size/pattern
config still applies. Registered in
apps/knowledge/serializers/document.py:split_handles BEFORE the
default text handler (which would otherwise reject the extensions).
apps/common/handle/impl/text/pdf_split_handle.py
Adds a _try_ocr_empty_pages helper. After pypdf has visited every
page, pages whose extracted text is below _OCR_PAGE_TEXT_THRESHOLD
(10 chars) are rendered to PNG via pymupdf and sent to the OCR
provider. The pipeline degrades gracefully:
- OCR not configured → log info, skip OCR, keep the empty pages
- pymupdf missing → log warning, skip
- Per-page failure → log error, keep going
System settings:
- SettingType.OCR = 4 (migration 0007, choices-only AlterField,
no schema change)
- OcrSettingSerializer with one() + Create.update_or_save() +
is_valid_config() mirroring EmailSettingSerializer.
- View permission reuses APPEARANCE_SETTINGS_READ/EDIT — no new
permission required.
- Endpoints:
GET /admin/api/ocr_setting — read current config
PUT /admin/api/ocr_setting — write
POST /admin/api/ocr_setting — test (initializes provider
without recognizing an image)
Frontend:
- New page ui/src/views/system-setting/ocr/index.vue with a mode
radio (vision_model / local), an IMAGE-type model picker for the
vision mode, a custom prompt textarea, and a language picker +
install hint for the local mode.
- API client ui/src/api/system-settings/ocr-setting.ts.
- Route registered under /system/setting/ocr with the same
APPEARANCE_SETTINGS_READ permission that unlocks "外观设置" for CE.
Runtime dependency note:
pymupdf is required for scanned-PDF OCR. MaxKB's pip is configured
with PIP_TARGET=/opt/maxkb/python-packages (sandbox path) which is
NOT on the worker's sys.path. To install for the main worker, use:
docker exec <ct> bash -c "PIP_TARGET= pip install \
--target /opt/py3/lib/python3.11/site-packages pymupdf"
For local-OCR mode, the same form applies with
rapidocr-onnxruntime + onnxruntime.
Verified end-to-end on 1panel/maxkb:v2.9.0:
- GET /admin/api/ocr_setting → 401 without token (rest auth works)
- Image upload with no OCR config → handler returns a friendly hint
paragraph instead of crashing
- pymupdf importable from worker
- get_ocr_provider({}) raises clean OcrConfigError
- New /system/setting/ocr menu visible to admin in CE
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem this solves:
Today, starting a chat with an agent requires: top menu "应用" →
agent list → find the card → click the small chat icon. That's 3-4
clicks and not discoverable as the product's main interaction. New
users frequently miss the chat button entirely.
Solution:
A new top-level menu placed at the very front of the navigation
(order: 0), titled "对话" / "Chat", that aggregates every published
agent in one card grid where clicking any card opens the chat
directly in a new tab.
Changes:
1. New route module `ui/src/router/modules/chat-entry.ts` registered
at /chat-entry with the same permission set as /application
(USER role + APPLICATION_READ permission, no edition gating).
Icon: app-user-chat / app-user-chat-active (already registered).
order: 0 so it lands at the leftmost menu position.
2. New page `ui/src/views/chat-entry/index.vue`:
- Pulls the workspace's application list via getAllApplication
- Filters to is_publish === true (only chat-ready agents)
- Renders a responsive card grid (1/2/3/4 cols by breakpoint)
- Each card: icon + name + nick_name + simple/workflow tag
+ description + updated time + "开始对话" button
- The card itself is a button (cursor + hover + keyboard) so the
whole tile is one big click target
- Click → calls getApplicationDetail (for workflow apps) +
getAccessToken (same flow as application/index.vue's toChat),
opens application.location + access_token in a new tab
- Empty state with a "去创建智能体" CTA that routes to /application
3. i18n in three locales (zh-CN, en-US, zh-Hant) under views/chat-entry,
registered in each views/index.ts barrel.
Not in this PR (follow-ups, if needed):
- "Quick chat" mode that lets users pick model + knowledge base and
spawn an ephemeral SIMPLE application on the fly. This needs a
backend POST flow + UX decisions about how to clean up ephemeral
apps; deferred.
- Filter chips for agent type (simple / workflow / by tag).
Verified end-to-end:
- Built admin (admin-BM2GjUoq.js) and chat bundles
- Deployed to 1panel/maxkb:v2.9.0
- Bundle contains chat-entry route definition
- Public URL /admin/ now serves the new bundle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Member
|
感谢贡献,没有计划支持本功能。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does / why we need it?
Summary of your change
Please indicate you've done the following: