1404 Commits
Author SHA1 Message Date
wizardchen fe0d24ae87 fix(sessions): review fixes for keyword search / pinning / IM titles
- Apply the session-list additions to the SQLite init migration (Lite build)
  and make QueryPaged dialect-aware: LOWER(..) LIKE on SQLite, ILIKE on
  Postgres, drop NULLS LAST on SQLite. Escape LIKE wildcards in the keyword
  via the existing escapeLikeKeyword helper.
- SetPinned now returns rowsAffected so the handler can respond 404 on
  unknown / unauthorized session IDs instead of a misleading 200.
- GET /sessions always returns the enriched shape (pin state + IM origin
  fields) so the frontend never needs a second roundtrip; the dual-path
  legacy branch in the handler is gone.
- Register pin routes with the wildcard name that matches each verb's
  existing radix tree (POST :session_id, DELETE :id) and accept either
  param name in the handler; avoids gin's "wildcard conflicts" panic.
- Drop the redundant [platform] prefix from IM session titles now that
  the list renders a platform icon alongside the title; add unit tests
  for shortID / buildUserSessionTitle / buildThreadSessionTitle.
- Frontend: remove the submenu search input and its i18n/keyword wiring
  (search lives elsewhere in the app); pin icon uses the TDesign `pin`
  glyph and inherits color so the active session turns green; optimistic
  pin moves the item to the top of the list so it shows up at the top
  of the Pinned group; IM text badge replaced by the platform SVG icons
  from assets/img/im, desaturated by default and full-color on hover
  or when the session is active.
v0.5.1
2026-04-30 16:23:00 +08:00
wizardchen dbd804d6e3 feat(sessions): add keyword search, user-scoped pinning, and IM source visibility
Sessions today are a flat list ordered by updated_at. Two gaps showed up in
practice:
- Users cannot find specific chats as the list grows beyond a screen.
- IM-created sessions (WeCom/Feishu/Slack/...) are indistinguishable:
  every title was "IM-<platform>" or "IM-<platform>-<username>" and the
  list API hid the underlying im_channel_sessions mapping, so admins had
  no way to tell which Feishu group a session came from.

Backend
- Migration 000039 adds sessions.user_id (owner), is_pinned, pinned_at
  plus a composite index for the list query. Existing rows keep user_id
  NULL and stay visible at the tenant level for backward compatibility.
- CreateSession now writes the caller's user_id from auth context.
- GET /sessions accepts keyword / source / agent_id. When any filter is
  set, the response switches to enriched items that LEFT JOIN
  im_channel_sessions and expose im_platform / im_chat_id / im_thread_id
  / im_user_id / im_agent_id / im_channel_id. No filters => legacy shape,
  existing clients unaffected.
- Ordering: is_pinned DESC, pinned_at DESC NULLS LAST, updated_at DESC.
- POST/DELETE /sessions/:id/pin for user-scoped pin/unpin.
- IM session titles: "[platform] <user|chat|thread>" with short ID
  suffixes so group/DM/thread sessions are visually distinct without
  needing a round-trip to fetch a display name from the IM adapter.

Frontend
- Search input debounced at 300ms drives the keyword filter.
- Pinned chats render in a dedicated group above the time-based groups,
  with a pin icon and a pin/unpin entry in the per-chat dropdown.
- IM chats get a short [platform] badge in the list.
- Pin toggle is optimistic and guards against double-clicks.
- zh/en/ko/ru i18n keys added for the new strings.
2026-04-30 16:23:00 +08:00
wizardchen e029d31f86 feat(im): tenant-wide IM channels overview under the user menu
Until now the IM channels a tenant has connected were only visible from
inside each agent's editor (AgentEditorModal → IMChannelPanel). Finding
out which bots are live across all agents meant clicking through every
one of them. Add a tenant-scoped overview so the set of connected IMs is
always one hop away from the avatar.

Backend

- im.Service.ListChannelsByTenant(tenantID) returns all non-deleted
  channels in the tenant, LEFT JOIN'd with custom_agents.name so
  built-in agents (which don't have rows in custom_agents) still show
  up with an empty agent_name; the frontend substitutes a localized
  "built-in agent" label.
- Credentials are intentionally stripped from this response — the list
  view is read-only and the editor route (GET /agents/:id/im-channels)
  remains the only source for secrets.
- New ChannelWithAgent DTO + IMHandler.ListAllIMChannels wired at
  GET /api/v1/im-channels under the existing RegisterIMChannelRoutes
  group, so auth middleware is inherited.

Frontend

- IMChannelsOverviewPanel.vue: floating submenu pane, stacked rows
  (agent on top, channel below) with identical 20px avatars and 12px
  labels so neither identity dominates. Each row is the whole click
  target (jumps to the agent editor); the switch uses @click.stop to
  toggle in place without triggering navigation.
- UserMenu.vue: new hover-driven submenu entry ("已接入的 IM" + link
  icon). The pane is teleported to <body> because the sidebar container
  has overflow:hidden that would otherwise clip right-flying content.
  Position is computed from the menu item's rect and clamps/flips
  against the viewport edges.
- "Live" indicator: a pulsing green dot on the menu item when at least
  one channel is enabled. UserMenu prefetches the list once on mount;
  the panel re-emits channels-changed after every load/toggle so the
  dot stays in sync. Respects prefers-reduced-motion.
- AgentList.vue: supplement the existing onMounted-only
  checkAndOpenEditModal with a watch(route.query.edit) so
  router.push({ path: '/platform/agents', query: { edit, section } })
  from an already-mounted AgentList (the common case when navigating
  from the overview) opens the editor immediately instead of only
  after a hard refresh.

Assets

- Seven platform SVGs under assets/img/im/, pulled from iconify
  (simple-icons / logos / tdesign / icon-park / remix icon) and baked
  with brand colors so monochrome sources (wecom/wechat/dingtalk/
  feishu) don't render black.

i18n

- New imOverview namespace in zh-CN / en-US / ko-KR / ru-RU covering
  the menu label, panel title/subtitle, column headers, builtin-agent
  fallback, and the live-indicator tooltip.

Tested

- go build ./...
- go test ./internal/im/...
- vue-tsc --noEmit
- npm run build
2026-04-30 15:29:14 +08:00
sqkstwj a5f80b747c fix(web-search): normalize default tenant web search config at runtime 2026-04-30 15:28:35 +08:00
wizardchen 3b23713c54 fix(crypto): fail loudly when encrypted DB fields cannot be decrypted
GORM Scan / AfterFind hooks for every AES-encrypted column followed
the same lenient pattern:

    if decrypted, err := utils.DecryptAESGCM(c.APIKey, key); err == nil {
        c.APIKey = decrypted
    }

When SYSTEM_AES_KEY was missing, rotated, or the wrong length the
decryption error was swallowed and the in-memory struct kept the raw
"enc:v1:..." ciphertext. The application then happily forwarded the
ciphertext upstream as the actual API key / password, surfacing as
401/403/SignatureDoesNotMatch from third-party vendors. Worse, a
ciphertext snippet of a customer credential was leaking into the
external provider's request logs.

Introduce utils.DecryptStoredSecret that:
  - returns "" / legacy plaintext untouched (no false positives for
    pre-encryption rows);
  - returns ErrEncryptedDataMissingKey when the value carries the
    enc:v1: prefix but no AES key is configured;
  - propagates any GCM auth-tag failure from a rotated key.

Wire it into the five Scan / AfterFind sites that currently swallow
the error:
  - Tenant.AfterFind (tenants.api_key)
  - CredentialsConfig.Scan (tenants.we_knora_cloud.app_secret)
  - ModelParameters.Scan (models.parameters.api_key, .app_secret)
  - ConnectionConfig.Scan (vector_store_connections.password, .api_key)
  - WebSearchProviderParameters.Scan (web_search_providers.api_key)

The error is wrapped with the originating column so the failure is
diagnosable in the logs. Operators must restore the previous
SYSTEM_AES_KEY (or rotate the affected secrets) instead of receiving
silent vendor 401s.

Add table-driven tests covering empty input, legacy plaintext,
round-trip, missing key, wrong-length key, and rotated key. The
strict path explicitly asserts that ciphertext does NOT leak in the
returned plaintext on any error path.
2026-04-30 15:17:56 +08:00
wizardchen 097c9d0ad5 fix(im): revert tenant-from-context for presigned URL
Reverts the tenant-ID-from-context change in da5fcd3. The storage path
encodes the resource owner's tenant, and the presigned-URL verifier
uses that ID to look up the owning tenant's StorageEngineConfig. Using
the caller's tenant ID from context would break cross-tenant shared
resources — e.g. when tenant Y reads an image from a KB shared by
tenant X, signing with Y would cause the verifier to open Y's storage
backend and 404 on X's file.

Keep:
- presignDefaultTTL shortened from 24h to 2h (independent improvement).
- godoc note on ParseTenantIDFromStoragePath flagging the ambiguity
  for cloud paths with numeric bucket/region names.
- Unit tests for path-based tenant extraction and the no-external-URL
  backward-compat path.
2026-04-30 11:40:30 +08:00
wizardchen f27a1e083c fix(im): prefer tenant ID from context, shorten presigned URL TTL
Addresses review feedback on the IM storage URL rewrite:

- localFileService.GetFileURL now reads tenant ID from request context
  first, falling back to ParseTenantIDFromStoragePath only when context
  is absent. Fixes ambiguity for cloud providers whose paths embed
  numeric bucket/region names before the tenant segment, which could
  mint presigned URLs bound to the wrong tenant ID.
- Shorten presigned URL default TTL from 24h to 2h. A leaked HMAC key
  authorizes cross-tenant file reads, so URLs should expire quickly;
  IM clients fetch referenced images within seconds anyway.
- Document ParseTenantIDFromStoragePath as a best-effort fallback.
- Add unit tests covering context-first, path-fallback, and the
  no-external-URL backward-compat path.
2026-04-30 11:40:30 +08:00
wizardchen 7fd566bc15 fix(im): rewrite private storage URLs to HTTP in IM channel replies
IM platforms (Feishu, Slack, Telegram, DingTalk, Mattermost, WeCom) cannot
render provider:// URLs (local://, minio://, s3://, etc.) that appear in
LLM answers containing knowledge base images. The web frontend handles
these via the authenticated /files endpoint, but IM clients need publicly
resolvable HTTP URLs.

Changes:
- Add HMAC-SHA256 presigned URL utility (internal/utils/presign.go) for
  generating time-limited, signature-verified file access URLs
- Add GET /api/v1/files/presigned endpoint that serves files without
  session auth, verified by HMAC signature and expiry
- Update localFileService.GetFileURL() to return presigned HTTP URLs
  when APP_EXTERNAL_URL is configured (cloud backends already return
  presigned URLs via their SDKs)
- Add IM content rewriting pipeline: strip <image> XML tags, strip
  citation tags, rewrite storage URLs to HTTP — applied at all IM
  output points (streaming flush, non-streaming reply, fallback)
- Add holdback buffer in streaming flush to prevent URL/tag splitting
  across chunk boundaries

Closes #1058
2026-04-30 11:40:30 +08:00
c 8c62a066af fix(frontend): route chat drag-and-drop uploads correctly 2026-04-30 10:43:03 +08:00
wizardchen 5ff11dce38 fix(frontend): hide card checkbox when not selecting
Card grid showed a faded checkbox at all times after multi-select was
added, which felt visually noisy and misaligned the title column. Hide
the checkbox column by default and let it expand on hover / when any
card is selected / when this card itself is selected. Drop
:focus-within so a cleared selection doesn't get stuck visible due to
the retained input focus.
2026-04-29 22:35:23 +08:00
wizardchen 679e40de82 refactor(frontend): unify Model/WebSearch/MCP settings pages with shared card + drawer
Aligns the three list-style settings pages (ModelSettings, WebSearchSettings,
McpSettings) under a consistent visual language, and replaces the modal editors
with right-side drawers for a calmer editing flow.

- Extract shared building blocks under frontend/src/components/settings/:
  SettingCard (list-item card), SettingDrawer (500px right drawer with a
  pinned footer), and useConfirmDelete (a single DialogPlugin.confirm path
  that replaces the mix of window.confirm / t-popconfirm / ad-hoc dialogs).
- Swap the old bespoke overlay / t-dialog shells in ModelEditorDialog and
  McpServiceDialog for SettingDrawer. Form logic is untouched.
- Model settings now use a single t-tabs filter (All/Chat/Embedding/ReRank/
  Vision/Speech with per-type counts) and decouple "Add model" from the tab
  state via a dropdown in the header. Type tags get a 5-color palette so
  categories read at a glance. The list is a two-column grid.
- Web search and MCP lists are likewise rendered as two-column grids, with
  richer cards: transport type / provider, on-off and default state, plus a
  compact meta row showing base URL, proxy or service URL.
- Introduce modelSettings.typeShort.{chat,embedding,rerank,vllm,asr} in all
  four locales for the shortened category labels.
2026-04-29 22:34:39 +08:00
Mao Meng b48adde3e6 fix:attachment_document_failed 2026-04-29 22:34:13 +08:00
wizardchen b2975f7d1d refactor(im): move adapter factories into per-platform subpackages
registerIMAdapterFactories in internal/container/container.go had grown
to ~290 lines of platform-specific config parsing and runtime start/stop
logic across 7 IM platforms, plus three private credential helpers. The
container package is meant to wire dependencies, not own each platform's
credentials schema and websocket lifecycle.

Extract each platform's factory into its own subpackage file and expose
the credential helpers on the im package:

- New im.ParseCredentials / im.GetString / im.GetBool (formerly the
  unexported parseCredentials / getString / credentialBool in container).
- New im.ResolveMode helper collapses the six identical "default to
  websocket when Mode is empty" blocks.
- New {wecom,feishu,slack,telegram,dingtalk,mattermost,wechat}.NewFactory
  returns an im.AdapterFactory; container.go now calls each one in a
  single line.

Behavior is preserved: adapter construction, goroutine start/stop,
log messages, default modes (mattermost still defaults to "webhook";
others to "websocket"), and error strings are unchanged. The WeCom
corp_agent_id float64/int switch and the WeChat long-poll-only path
are preserved as-is.

Net: container.go drops ~320 lines and no longer needs to be touched
when adding or tuning an IM platform.
2026-04-29 20:08:22 +08:00
wizardchen 9af5fb30ac fix(frontend): polish knowledge list sticky header and floating batch bar
- Detect sticky state via IntersectionObserver and flatten the table
  header's top corners when stuck so the rounded cut-outs no longer
  reveal the list container's background while scrolling.
- Lift the batch-action toolbar out of flex flow and float it 12px above
  the scroll container bottom so selecting rows no longer shrinks the
  visible list; strengthen its shadow to match the floating treatment.
2026-04-29 20:07:55 +08:00
wizardchen 7fcd92994b feat(tenant): expose API Key reset from the API Info page
Add a danger-themed reset button next to the existing copy/eye buttons
on the API Info settings page. Confirmation dialog warns that the old
key is revoked immediately; on success the new plaintext key replaces
the displayed value and is auto-revealed so the user can copy it.

Backend: new POST /api/v1/tenants/:id/api-key handler that wraps the
existing TenantService.UpdateAPIKey; access is gated by the same
authorizeTenantAccess check as other tenant endpoints. The handler
returns the freshly generated plaintext key, while the encrypted form
is persisted to the database.

Frontend: new resetTenantApiKey API client, reset button + confirm
dialog wiring in ApiInfo.vue, plus matching i18n entries for zh-CN,
en-US, ko-KR, and ru-RU.
2026-04-29 20:07:38 +08:00
lyingbug cbdfba0075 Merge pull request #1086 from Windfarer/storage-allow-list
feat: add STORAGE_ALLOW_LIST env var
2026-04-29 19:49:46 +08:00
cn-kali-team 1e170793b3 客户端添加切换租户 2026-04-29 19:47:34 +08:00
wizardchen b3898eb101 fix(helm): preserve SYSTEM_AES_KEY/TENANT_AES_KEY across upgrades
The Secret template defaulted both keys to randAlphaNum 32, which Helm
re-rolls on every template render. As a result, any `helm upgrade`
without explicit secrets.systemAesKey / secrets.tenantAesKey rotated
the keys, breaking decryption of every previously encrypted field
(tenants.api_key, model API keys, vector store credentials, web
search provider keys, WeKnoraCloud.AppSecret) and surfacing
"enc:v1:..." ciphertext in the UI.

Use Helm's `lookup` to reuse the values stored in the existing Secret
when one is already present, falling back to randAlphaNum only on
first install. Also document the recovery caveat in values.yaml so
operators understand the risk of relying on the auto-generated value.
2026-04-29 19:46:21 +08:00
wizardchen c89fd5da98 fix(tenant): return plaintext API key after create/reset
CreateTenant and UpdateAPIKey both overwrote tenant.APIKey with the
AES-encrypted ciphertext before returning, causing the UI to display
"enc:v1:..." right after generating or resetting an API key. Save the
plaintext in a local variable, encrypt only the value handed to
UpdateTenant, and return the plaintext to callers.

Subsequent reads via GetTenantByID continue to rely on the AfterFind
hook for transparent decryption, so this only affects the initial
write-back path.
2026-04-29 19:46:21 +08:00
Windfarer 20be935fe0 Merge branch 'main' into storage-allow-list 2026-04-29 19:35:13 +08:00
Windfarer 6601a483be feat: add STORAGE_ALLOW_LIST env var 2026-04-29 18:58:09 +08:00
wizardchen 1fe1f853d5 fix(chat): keep input visible when conversation overflows viewport
The chat layout is a flex column (.chat) inside .platform-route-outlet,
with .chat_scroll_box {flex:1; overflow-y:auto} above .input-container.
Flex items default to min-height:auto, so when messages outgrew the
viewport the scroll box was stretched by its content instead of
scrolling internally. That pushed the input container out of view and
broke page-level scrolling because the parent clips overflow.

- .chat: add min-height:0 so its flex:1 child can shrink
- .chat_scroll_box: add min-height:0 to enable overflow-y scrolling
- .input-container: add flex-shrink:0 so a tall scroll box cannot
  squeeze it to zero height
2026-04-29 17:34:20 +08:00
wizardchen da90867dd4 feat(ui): refactor IM channels list layout and form\n\n- Change IM channel form from dialog to drawer for better UX\n- Convert channel list from vertical layout to responsive grid cards\n- Move edit/delete actions into a dropdown menu for cleaner card design\n- Replace platform radio group with a select dropdown in the add channel form 2026-04-29 17:34:20 +08:00
wizardchen c62ff4c906 fix(frontend): knowledge document list layout, batch bar, and selection
- Platform route outlet: flex min-height so document area is not collapsed
- Card grid: stable :key=item.id; clamp selection anchor after list reload
- DocumentListView: TDesign checkbox, sticky header, neutral file icons
- DocumentBatchBar: column-footer placement; toned style vs floating pill
- Clear selection i18n: less ambiguous wording (e.g. 取消选择)
- Batch delete: poll list refresh after async queue; getKnowled returns Promise
- files.ts: use file icon for text types; align chat/upload icon usage
2026-04-29 15:20:41 +08:00
nullkey a40f191430 fix(knowledge-base): restore hover feedback on selected list rows
The previous list-view tone refinement set the selected-row hover
background to var(--td-brand-color-light), which is aliased to
var(--td-brand-color-1) — the same value used as the static selected
background. Hovering a selected row produced no visible change, leaving
users without feedback for what was clickable.

Use color-mix() to blend brand-color-1 with a touch of brand-color so
the hover delta is visible in both light and dark themes, without
reverting to the saturated brand-color-2 the original code used.
2026-04-29 14:28:56 +08:00
nullkey 48a764b866 refactor(knowledge-base): address review feedback
Backend
- BatchDeleteKnowledge handler now enqueues an asynq
  TypeKnowledgeListDelete task instead of calling DeleteKnowledgeList
  synchronously, matching the pattern used by ClearKnowledgeBaseContents.
  Avoids long HTTP timeouts on large batches and shares the existing
  async cleanup pipeline.
- Extract a small enqueueKnowledgeListDelete helper on KnowledgeHandler
  so BatchDeleteKnowledge and ClearKnowledgeBaseContents share the
  payload/marshal/enqueue boilerplate.

Frontend (DocumentListView)
- softer outer border and lighter row separators
- header background switched to the page tone with placeholder-color
  text, reducing institutional gray
- selected row uses an inset left accent bar and a quieter brand tint
  instead of a saturated background; hover stays neutral
- consolidate the .row-more-btn { opacity: 1 } rule that was duplicated
  across the hover and selected blocks
2026-04-29 12:01:38 +08:00
nullkey be8e53bf08 feat(knowledge-base): add document batch delete and list view
Add a list-view alongside the existing card grid (issue #957) and
multi-select batch delete (issue #1045) for documents inside a knowledge
base. The two views are toggled from the toolbar and the preference is
persisted in localStorage. Selection state is shared between views with
shift-range support and a sticky batch-action bar.

Backend
- POST /api/v1/knowledge/batch-delete: validates KB scope and editor
  permission, then delegates to the existing DeleteKnowledgeList service
  which already cascades vector/file/graph cleanup. Caps batch size at
  200 and uses a single GetKnowledgeBatch call for membership validation.

Frontend
- DocumentListView: table layout with sticky header, checkbox column
  (with indeterminate select-all), file-type icons, status badges,
  per-row action menu.
- DocumentBatchBar: floating pill bar with selected count and delete
  action; appears when items are selected.
- KnowledgeBase.vue: view-mode toggle, selection state, batch-delete
  confirmation dialog, hover/active checkbox overlay on existing cards.
- Extracted formatFileSize and getFileIcon to utils/files.ts to share
  with the new list view and avoid further drift from the existing
  inline copies.
- i18n keys added for zh-CN, en-US, ko-KR, ru-RU.

Closes #1045
Closes #957
2026-04-29 12:01:38 +08:00
wolfkill abd188d344 fix(miniprogram): improve knowledge base selection 2026-04-29 12:00:02 +08:00
wolfkill d06111e5f7 feat(miniprogram): add WeChat mini program plugin 2026-04-29 12:00:02 +08:00
wizardchen d55b52652c fix(docparser): preserve standalone image uploads from icon filter
When the uploaded file is itself an image, the image reference now carries
an IsOriginal flag so ResolveAndStore skips the small-icon size filter.
Otherwise small standalone images (e.g. avatars below 64x64) were silently
dropped before reaching multimodal OCR/caption processing.
2026-04-29 11:58:02 +08:00
hjz 535aec87e3 feat: data_analysis sql validation & type processing 2026-04-28 23:47:34 +08:00
wizardchen d812770806 fix: inject KB document listing on fallback for broad queries (#959)
When users ask broad queries like "请整理知识库中的数据" in RAG mode,
vector/keyword search returns nothing because the query has no specific
content to match. The user needs to see what documents exist, not search
results.

- Add buildKBDocumentListing() to fetch document titles/filenames from
  the knowledge base and inject them into the fallback prompt via
  {{kb_documents}} placeholder
- Update fallback prompt templates (model_fallback, default_fallback_prompt)
  to include document listing context so the LLM can guide users
- Improve rewrite prompt: tighten summarize vs kb_search classification,
  expand kb_search to cover browse/organize/list operations, add examples

Closes #959
2026-04-28 23:31:04 +08:00
wizardchen d5f6c7ba21 fix(docreader): remove default 100-page limit for DOCX parsing
The default DOCREADER_DOCX_MAX_PAGES=100 silently truncates large
documents, causing users to see at most ~1000 chunks regardless of
document length. Change the default to 0 (no limit) so all pages are
processed. Operators who need a cap can still set the env var.

Fixes #719
2026-04-28 21:50:15 +08:00
wizardchen 80a007cc06 fix(im): remove pipeline-level timeout that kills multi-round agent reasoning
The IM service applied a hard 120s deadline (`qaTimeout`) to the entire
QA pipeline context. Multi-round ReAct agents easily exceed this — when
the deadline fires mid-stream the LLM provider returns "context deadline
exceeded" which leaks into the user-visible answer (fixes #1000).

Changes:
- Replace `context.WithTimeout(ctx, qaTimeout)` with `context.WithCancel(ctx)`
  in both `handleMessageStream` and `runQA`, matching the web handler's
  approach. Each agent round still has its own per-call LLMCallTimeout
  (default 120s) which is sufficient.
- Filter `ResponseTypeError` stream chunks in `streamLLMToEventBus` so
  error messages (e.g. "context deadline exceeded") are never appended
  to the LLM content. Instead they are captured in `StreamError` and
  surfaced as a Go error when no usable content was produced.
- Bump Redis inflight mapping TTL from qaTimeout+30s to 10 minutes to
  accommodate longer agent runs.
- Remove the now-unused `qaTimeout` constant.
2026-04-28 21:17:56 +08:00
wizardchen c578fdbad6 fix(im): isolate IM sessions per agent and recover from deleted sessions
Three bugs fixed:

1. Session cross-contamination (#1066): resolveUserSession and
   resolveThreadSession looked up ChannelSession by (platform, user_id,
   chat_id, tenant_id) without agent_id. When the same user talked to
   two bots bound to different agents under the same tenant, they shared
   a session — mixing knowledge base context. Add agent_id to all four
   WHERE clauses and update the DB unique indexes to match.

2. Swapped parameters in resolveThreadSession: the SQL expected
   (chat_id, thread_id) but Go args passed (threadID, msg.ChatID).

3. Orphaned ChannelSession crash (#1046): deleting a session from the
   WeKnora UI soft-deletes it (GORM), but the ChannelSession row
   survives because soft-delete doesn't trigger SQL ON DELETE CASCADE.
   Subsequent IM messages hit "record not found" on GetSession and the
   bot becomes permanently unresponsive. Now detect this case, recycle
   the stale mapping, and transparently create a fresh session.
2026-04-28 20:53:06 +08:00
wizardchen a4c0832007 fix(search): align rerank priority between Execute and rerankResults
The outer if/else in Execute claimed to prefer chatModel (LLM-based
reranking), but both branches called rerankResults which internally
prefers rerankModel first. This made the outer branching dead code
with a misleading comment.

Consolidate into a single branch that delegates entirely to
rerankResults, whose actual priority is: rerankModel → chatModel → none.
2026-04-28 20:23:57 +08:00
goodnight ca1c8074bc fix(container): aggregate registration errors in connector registry initialization 2026-04-28 20:02:13 +08:00
wizardchen 4b87239929 fix: LaTeX formulas flash and disappear during streaming response (#1056)
Replace token-by-token rendering (marked.lexer + v-for + marked.parser)
with single-pass marked.parse() in botmsg.vue and AgentStreamDisplay.vue.
This prevents Vue's v-for keyed diffing from destroying KaTeX DOM nodes
on each streaming update.

Also adds a dev-only markdown test page (/platform/dev/markdown) with
LaTeX, code, tables, Mermaid, and streaming simulation for visual
regression testing.
2026-04-28 19:34:33 +08:00
wizardchen 1f03741462 fix(wiki): Lite ingest lock, failed-op requeue, sync task retry parity
- Add per-KB sync.Map lock in Lite mode to mirror Redis SetNX concurrency.
- Requeue failed wiki ops in Lite by enqueueing asynq tasks with delay/tracing.
- Teach SyncTaskExecutor to honor ProcessIn delay and MaxRetry like Redis asynq.
2026-04-28 18:27:00 +08:00
wizardchen 48c032ccbe chore(i18n): align en-US/ko-KR/ru-RU locales with zh-CN
Sync key structure of non-Chinese locale files with zh-CN.ts as the
source of truth: add missing keys (translated into the target language)
and remove keys that no longer exist in zh-CN.

- en-US: +1 (knowledgeEditor.wikiBrowser.aliases)
- ko-KR: +110 / -9 (drop confluence/github/web_crawler entries that
  don't exist in zh-CN; add chat.wiki*, auth.oidc*, agent.editor.web*,
  knowledgeEditor.indexing.*, agentEditor.{im.wechat*,desc.web*,
  llmCallTimeout.*}, datasource.* prereq/scheduleHuman/resourceType/
  relative-time keys, etc.)
- ru-RU: +108 / -9 (same shape as ko-KR, minus auth.oidc which already
  existed)
2026-04-28 18:01:22 +08:00
wizardchen dd524ffb1c feat: enhance Scan method in RetrieverEngines to support legacy and current formats
- Update Scan method to handle both legacy bare-array format (e.g. [{...}, {...}]) and current object-wrapped format (e.g. {"engines": [{...}, {...}]}).
- Improve error handling for unmarshalling failures, providing clearer feedback on format issues.

This change ensures backward compatibility while allowing for the new data structure.
2026-04-28 17:21:56 +08:00
wizardchen c34f7b6254 Enhance wiki ingest process to handle failed operations
- Introduced a mechanism to track and requeue failed operations during the wiki ingest process.
- Added a new `requeueFailedOps` function to append failed operations back to the Redis pending list for retry in subsequent batches.
- Updated the `ProcessWikiIngest` method to collect failed operations and ensure they are retried after trimming the pending list.

This change improves the robustness of the ingest process by preventing data loss from transient failures.
2026-04-28 17:21:42 +08:00
wizardchenandClaude Opus 4.6 13260b831c Fix wiki ingest silent data loss from malformed JSON in Redis queue
Two critical fixes to the peekPendingList function:

1. Add error logging for JSON unmarshal failures (line 303)
   - Previously: Malformed JSON items were silently skipped with no indication
   - Now: Each failure is logged with the error and raw item content (first 100 chars)
   - This makes data loss visible and debuggable

2. Fix trim count mismatch (line 325)
   - Previously: Returned len(result) - the count of raw Redis items peeked
   - Now: Returns len(ops) - the count of successfully parsed items
   - Impact: Malformed items now stay in Redis for retry instead of being discarded
   - This prevents silent data loss when JSON encoding issues occur

Root cause of the reported issue:
- When 8 files are uploaded in a batch, if one has encoding issues that cause
  JSON.Unmarshal to fail, it was silently dropped from processing
- The file was trimmed from Redis without being processed
- Result: Only 7 of 8 files appeared in the Wiki

Fix approach:
- Make unmarshal failures visible in logs
- Retry malformed items in the next batch instead of discarding them
- Admin can see the errors and take corrective action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 17:21:42 +08:00
wizardchen b1fc0e18d9 feat: Add LLM call timeout configuration to agent frontend
- Add llm_call_timeout field to CustomAgentConfig TypeScript interface
- Add thinking field to CustomAgentConfig (for extended thinking support)
- Add UI control (number input) in AgentEditorModal for timeout configuration
  - Range: 0-600 seconds (0 = use global default)
  - Supports 60-600 seconds recommended range
- Add i18n translations in Chinese (zh-CN) and English (en-US)
  - Label: "LLM 调用超时" / "LLM Call Timeout"
  - Description with explanation of timeout behavior
  - Hint about default and unlimited wait implications
  - Placeholder with recommended range
- Initialize llm_call_timeout: 0 in formData (uses global default)

This completes the frontend support for per-agent LLM timeout configuration that was previously missing. The setting integrates with the existing three-tier configuration hierarchy:
1. Per-agent config (llm_call_timeout in custom_agents table)
2. Global config (agent.llm_call_timeout in config.yaml)
3. Hardcoded default (120 seconds)
2026-04-28 17:21:17 +08:00
wizardchen 29a65cc250 docs(readme): update tracing references from Jaeger to Langfuse across multiple languages 2026-04-27 15:35:15 +08:00
wizardchen 9fa60c7e7d docs(readme): enhance layout for Agent Mode and Observability sections across multiple languages 2026-04-27 15:27:24 +08:00
wizardchen 99c17a0548 docs(readme): adjust layout for Agent Mode and Knowledge Base Management sections v0.5.0 2026-04-27 13:39:08 +08:00
wizardchen a888bc2dfe docs(images): update agent-qa.png to improve visual content 2026-04-27 13:39:08 +08:00
wizardchen ddd4234e64 docs(readme): move Interface Showcase up and rebalance layout
- Promote Interface Showcase to right after Latest Updates / before
  Architecture so users see the product UI early.
- Rebalance grid: hero Q&A (full width) -> Wiki Browser + Wiki Graph
  (50/50) -> Agent Mode (full width) -> KB Management + Settings
  (50/50). Forces equal-width columns via width=50% so screenshots
  no longer look uneven.
- Add small emoji prefixes to subsection labels for visual rhythm.
2026-04-27 13:39:08 +08:00
hjz efae3d466f feat: correct data analysis errors using fileService. 2026-04-27 13:18:08 +08:00