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)
- 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.
- 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.
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>
- 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)
- 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.
Removed unnecessary details sections and adjusted formatting for consistency across English, Chinese, Japanese, and Korean README files. Updated headings for features to enhance clarity and readability.
Bump version to v0.5.0 across VERSION, frontend/package.json,
frontend/package-lock.json and helm/Chart.yaml.
Highlights:
- Wiki Mode: agent-driven Wiki knowledge system that distills raw
documents into interlinked markdown pages, with a dedicated
WikiBrowser and an interactive knowledge graph visualizing page
references and relationships.
- Observability: Langfuse tracing across the agent ReAct loop, LLM
token usage, tool calls and the asynq async pipeline.
- Customizable indexing strategy: per-knowledge-base toggles for
vector / keyword / Wiki / knowledge-graph indexing.
- Vector Store UI & per-KB binding.
- Yuque connector with full / incremental sync.
- Agent enhancements: json_repair tool, OpenMAIC Classroom skill,
multi-sheet DuckDB Excel analysis.
- Docs: refreshed READMEs (EN/CN/JA/KO), CHANGELOG, QA, regenerated
Swagger and updated architecture diagram with new Wiki/Langfuse
components.
The existing Langfuse integration covered Chat / Embedding / Rerank / VLM /
ASR generations plus the HTTP + asynq spans, but the agent's own execution
tree was invisible: tool calls never appeared, multi-round ReAct iterations
were flat under the HTTP trace, and there was no single node representing
"one agent run".
This change adds three levels of agent-side spans:
- agent.execute — wraps AgentEngine.Execute, records query preview,
knowledge bases, allowed tools, final-answer length
and totals on finish.
- agent.round.<N> — wraps each ReAct iteration; records finish_reason,
tool-call count, token usage and duration.
- agent.tool.<name> — wraps each tool invocation; records arguments,
success, duration, output preview (rune-safe, 4KB
cap), error, data keys and image count.
To keep the loop's many exit paths (natural stop, stuck loop, empty-content
retry, final_answer, context cancellation) span-safe, the iteration body was
extracted into runReActIteration with a single defer span.Finish() and an
iterOutcome sentinel driving the outer loop. database_query arguments are
redacted (keys only) to avoid leaking raw SQL into the observability
backend, mirroring the existing UI hint policy.
Adds unit tests for the new helpers (truncateForLangfuse, argKeys, dataKeys,
finishToolSpan nil-safety, iterOutcome.String).
Dev-mode repro from the issue reporter's logs:
[Tool][DataAnalysis] Failed to create table from Excel: IO Error:
GDAL Error (4): Failed to open file
'local://10000/.../1777030910246871000.xlsx': No such file or directory
The local file service returns a custom 'local://' URL from GetFileURL,
which the duckdb spatial/excel extensions can't resolve (they expect
plain paths or http(s)/s3:// style URIs). Presigned HTTPS URLs from
cloud backends worked by accident; the 'local://' dev path has always
been broken for the Data Analysis tool.
Instead of per-scheme adapters, stream the file through
FileService.GetFile into a temp file and hand DuckDB the resulting
filesystem path. This works uniformly across every backend (local,
OSS, S3, MinIO, COS) and survives future scheme changes without any
changes to the Data Analysis tool.
- Preserve the original file extension on the temp file so DuckDB's
format auto-detection (csv / xlsx / xls) still kicks in.
- Clean up the temp file when LoadFromKnowledge returns, including on
every error branch. Cleanup is idempotent (double-invoke safe).
- New tests stub FileService to lock in that (a) we never leak a
provider:// URL to DuckDB, (b) GetFile errors propagate, (c) the
extension survives case normalization.
Refs: https://github.com/Tencent/WeKnora/issues/1007
Catching up on a real-world check: when a multi-sheet .xlsx fixture is
fed through LoadFromExcel against an in-memory DuckDB with the spatial
and excel extensions, the first iteration's sheet enumeration quietly
fell back because st_read_meta doesn't expose a scalar 'layer_name'
column — it returns a LIST<STRUCT> column called 'layers'. We now
UNNEST(layers).name so enumeration actually works, and assert the full
data path with three DuckDB-backed tests:
- multi-sheet workbook yields the sum of all rows and per-sheet
breakdown via __sheet_name (including schema drift: columns only
present in one sheet are NULL for the other)
- single-sheet workbook still works and tags rows with its sheet name
- sheet name containing a single quote (Q1'24) survives the SQL
literal round-trip
excelize/v2 is added as a test-time helper to build the fixtures
deterministically. If DuckDB's spatial/excel extensions can't be
installed (offline CI), the tests skip rather than fail.
Refs: https://github.com/Tencent/WeKnora/issues/1007
DuckDB's st_read (spatial) only reads the first layer/sheet of a .xlsx
workbook, so every sheet beyond Sheet1 was silently dropped from the
DuckDB table the Data Analysis tool builds. Users trying to analyse
multi-sheet workbooks could only see the first sheet.
Switch to DuckDB's dedicated 'excel' extension (read_xlsx) for the
actual data load, enumerate sheets via the spatial extension's
st_read_meta, and UNION ALL BY NAME the rows of every sheet into one
table. A synthetic __sheet_name column records the source so the LLM
can still filter/aggregate per sheet; schema drift between sheets is
tolerated via UNION BY NAME. If enumeration fails (older DuckDB, local
filesystem errors, …) we fall back to reading the first sheet so the
tool stays usable.
- Install & LOAD the 'excel' extension alongside 'spatial' both at
startup (internal/container) and in the offline prefetch binary
(cmd/download/duckdb).
- Harden sheet/path handling against single quotes.
- Update the tool description so the agent knows about __sheet_name.
- Add unit tests for the CREATE TABLE SQL builder covering 0 / 1 / N
sheets and quote escaping.
Refs: https://github.com/Tencent/WeKnora/issues/1007
Previously the Langfuse integration only traced in-process HTTP requests
(chat / search / eval), so file uploads and every downstream asynq task
(document parse, chunk embedding, OCR/VLM, summary / question gen, wiki
ingest, datasource sync, etc.) produced either disconnected shallow
traces or no observation at all.
This change threads one trace end-to-end:
- tracer: add SPAN observation type and StartSpan; add ResumeTrace so a
worker can attach to an upstream trace without emitting a duplicate
trace-create; StartGeneration now auto-picks parentObservationId from
ctx so nested trace -> span -> generation trees render correctly.
- types.TracingContext + LangfuseTracingCarrier: embed on all 17 asynq
payloads so trace_id / parent_obs_id / user_id / session_id serialise
into every job.
- langfuse.InjectTracing: injected at 28 enqueue sites before json.Marshal
so the HTTP-layer trace survives the Redis hop.
- langfuse.AsynqMiddleware: mux.Use hook that peeks the payload, either
resumes the upstream trace or opens a standalone asynq.<type> trace
for scheduled jobs, and wraps the handler in a SPAN with task metadata
(id / queue / retry / payload_bytes) plus ERROR level on failure.
- GinMiddleware.shouldTrace: whitelist ingestion / knowledge-mutation /
FAQ / wiki / datasource endpoints so the root trace actually starts.
- Tests: tracer_test.go covers span nesting, error status, and
ResumeTrace no-trace-create guarantee; asynq_test.go covers
InjectTracing round-trip, middleware resume path, and standalone
trace fallback.
- Docs: docs/Langfuse\u96c6\u6210.md now lists the covered task types
and documents the cross-process propagation model.
No behavioural change when Langfuse is disabled (all new code paths are
no-ops and carriers serialise to empty strings with omitempty).
Revised the agent system prompt to clarify the synthesis process and the handling of factual errors. The instructions now emphasize the mandatory use of the `final_answer` tool for submitting responses and the need to flag issues before calling `final_answer`. This enhances the clarity and consistency of the agent's operational guidelines.
Closes#620#497. Add opt-in Langfuse observability covering all five
model types (chat, embedding, rerank, VLM, ASR) with HTTP-request-scoped
traces and Docker Compose support (both cloud and self-hosted).
Core package internal/tracing/langfuse:
- HTTP client with batched async ingestion (non-blocking in request path)
- Sampling, environment / release tagging, and graceful fallback when
LANGFUSE_* env vars are absent (wrappers become no-ops)
- Gin middleware opens one trace per traced request and finishes it after
the handler chain returns, attaching method / path / user / session
- Trace context is stored under a typed key exported from internal/types
so logger.CloneContext can preserve it across handler / goroutine
boundaries (otherwise each LLM call auto-created an orphan trace,
fragmenting one request into many)
Per-model generation wrappers (opt-in via NewChat/NewEmbedder/...):
- chat: captures prompt, streaming output, token usage + TTFT
- embedding: approximates tokens when the provider omits usage
- rerank: previews query/docs, summarizes results to keep payload small
- vlm: records image count and total bytes, never uploads raw pixels
- asr: records file size and audio duration, never uploads audio bytes
Async title generation (GenerateTitleAsync) now forwards the trace key
into the goroutine so title calls appear under the parent chat trace.
Docker Compose:
- LANGFUSE_* env passthrough on the `app` service for cloud deployments
- Optional `langfuse` profile spins up a self-hosted Langfuse stack that
reuses WeKnora's existing PostgreSQL (separate database via an idempotent
init container that fixes ICU collation drift) and Redis (separate DB
number), adding only ClickHouse, MinIO, web and worker containers
- web/worker entrypoints URL-encode DB_PASSWORD / REDIS_PASSWORD at start
to avoid Prisma P1013 when passwords contain @ / # / etc.
Docs: docs/Langfuse集成.md covers cloud vs self-hosted, per-model usage
strategy, code map, and resource footprint.
The share-time validation forced every agent bound to a knowledge base to
have a rerank model configured, which blocked Wiki-type agents
(`wiki_search` / `wiki_read_page` / …) from being shared even though they
never invoke the reranker at runtime.
Align the check with the runtime logic in `session_agent_qa.go`: only
require a rerank model when the agent's allowed_tools include
`knowledge_search` (or rely on the default tool set, which contains it).
Wiki-only agents can now be shared without a rerank model; hybrid / RAG
agents still enforce the requirement.
Also update the handler error message to reflect the new rule.
Allow users to duplicate non-builtin models (chat / embedding / rerank /
VLLM / ASR) from the model card dropdown. The copied model inherits all
parameters (base_url, api_key, provider, embedding params, custom
headers, vision flag, etc.) and gets a non-conflicting name with a
localized suffix, so users can quickly fork and tweak an existing
configuration without re-entering every field.
Adds matching i18n entries (copySuffix / toasts.copied /
toasts.copyFailed / toasts.builtinCannotCopy) for zh-CN, en-US, ko-KR
and ru-RU.
Introduce per-model `custom_headers` config (similar to OpenAI Python SDK's
`extra_headers`) so users can inject gateway auth tokens, trace IDs, etc.
into every outbound API request for chat / embedding / rerank / VLLM / ASR
models. Reserved headers like Authorization / Content-Type are always
preserved to avoid breaking auth or signing flows.
Along the way, unify production and "test connection" paths onto a single
`ConfigFromModel(*types.Model, appID, appSecret)` constructor in each
model package. Both `service.modelService.GetXxxModel` and the four
`handler.initialization.Check*Model` / `TestEmbeddingModel` endpoints now
go through the same field mapping, so new parameters only need to be
added in one place.
Backend:
- Add `CustomHeaders map[string]string` to `types.ModelParameters` with
JSON/YAML `omitempty` tags for forward compatibility.
- New `internal/utils/extraheaders.go` providing `ApplyCustomHeaders`
(for hand-rolled HTTP paths) and `WrapHTTPClientWithHeaders` /
`CustomHeadersRoundTripper` (for SDK paths like go-openai). Reserved
headers (Authorization, api-key, Content-Type, Accept, Host,
Content-Length, User-Agent) are filtered out.
- Inject headers in every remote model path: chat (SDK + raw HTTP),
embedding (openai/aliyun/jina/volcengine/nvidia/azure_openai), rerank
(remote_api/aliyun/jina/nvidia/zhipu — via shared `customHeaderSetter`
interface), VLM and ASR (via http.Client wrapping).
- Add `ConfigFromModel` to each of chat/embedding/rerank/vlm/asr,
consolidating field mapping (ExtraConfig, CustomHeaders, InterfaceType
defaulting, WeKnoraCloud credentials, etc.) and cover it with
dedicated unit tests per package.
- Refactor `service/model.go`: replace ~100 lines of hand-written Config
literals with one-line `ConfigFromModel` calls; drop the now-unused
`stringMapToAnyMap` helper.
- Refactor `handler/initialization.go` test-connection endpoints: merge
four ad-hoc request structs into one `ModelTestRequest`, extract
`buildTestModel` and `resolveTenantWeKnoraCloudCreds` helpers, and
route all four endpoints through `ConfigFromModel` + `NewXxx` so the
test path is now behaviorally identical to production.
Frontend:
- Add `custom_headers` to the `ModelConfig` API type.
- `ModelEditorDialog.vue`: new Key-Value header editor (add/remove rows),
auto-converts map <-> array when loading/saving, and passes the
current header set to the `Test Connection` button so the preview
reflects exactly what production will send.
- `ModelSettings.vue`: serialize the header array back into a map for
the backend (drops empty rows).
- i18n: add custom-header labels / descriptions / placeholders to
zh-CN, en-US, ru-RU, ko-KR.
Tests:
- `internal/utils/extraheaders_test.go` covers reserved-header filtering
and round-tripper wrapping (including nil-client fallback).
- New `config_from_model_test.go` in chat / embedding / rerank / vlm /
asr verifies all fields (CustomHeaders, ExtraConfig, InterfaceType
defaulting, AppID/AppSecret) are propagated end-to-end.
The agent loop previously kept running when the LLM emitted final_answer with
malformed JSON arguments: observe.analyzeResponse strictly json.Unmarshal'd the
raw args and returned isDone=false on failure, while act.runToolCall separately
repaired the JSON and executed the tool. The mismatch made final_answer
non-terminal, so the LLM saw its own answer in the next round's tool result
and re-emitted final_answer, surfacing near-identical answers multiple times
to the user (issue #1008).
Fix by unifying parsing in a new tools.ParseFinalAnswerArgs helper with three
fallbacks (strict -> RepairJSON -> regex extraction) and making final_answer
always terminal in analyzeResponse. When no answer can be recovered, emit a
user-visible fallback message and a Done=true event so the UI still resolves.
Also route final_answer.Execute through the same helper so tool execution and
loop termination stay consistent.
Adds regression tests in observe_test.go and final_answer_test.go covering
valid args, RepairJSON recovery, unrecoverable args, and garbage input.
Refs: Tencent/WeKnora#1008
The <t-icon> component from tdesign-vue-next lazily loads its SVG sprite
from https://tdesign.gtimg.com/icon/<ver>/fonts/index.js at runtime. In
air-gapped or intranet deployments this request fails and the whole UI
loses its icons (agent cards, knowledge-base breadcrumbs/cards, upload
buttons, etc.).
Ship the sprite (tdesign-icons-vue-next@0.4.1) as a static asset under
frontend/public/tdesign-icons/0.4.1/fonts/index.js and preload it from
index.html so every <t-icon name="..."> resolves against local symbols.
An offline guard injects stub <script>/<link> nodes that match tdesign's
dedup selectors, so checkScriptAndLoad / checkLinkAndLoad short-circuit
without ever hitting the CDN (covers both the 0.4.0 and 0.4.1 hardcoded
URLs for forward-compat).
Closes#867Closes#897
- Removed redundant struct definitions for WeKnoraCloud requests and messages, streamlining the customization process.
- Updated the weKnoraCloudRequestCustomizer function to directly modify the request object, enhancing clarity and reducing complexity.
- Improved the convertToWeKnoraCloudMessagesFromOpenAI function to maintain the original message structure while ensuring compatibility with WeKnoraCloud requirements.
- Introduced a unified capability requirements system for agent tools, ensuring consistent filtering of knowledge bases based on their capabilities.
- Implemented derived `kb_filter` logic to streamline compatibility checks between agent tools and knowledge bases, reducing redundancy in configuration.
- Updated frontend components to reflect new filtering logic, including improved handling of empty states and user feedback when no compatible knowledge bases are available.
- Enhanced internationalization support by adding relevant messages for tool compatibility issues across multiple languages.
- Removed header printing from the chatWithRawHTTP method to streamline logging.
- Updated log message to focus on endpoint and model name, enhancing clarity while reducing verbosity.
- Introduced chunkingDirty state to track manual adjustments to chunking settings, preventing automatic overrides during strategy changes.
- Added WIKI_ONLY_CHUNKING_PRESET for specific configurations when using the wiki-only indexing strategy, ensuring optimal settings in creation mode.
- Implemented logic to apply or revert chunking presets based on the current indexing strategy, maintaining user-defined settings in edit mode.
- Updated resetState function to clear chunkingDirty state upon modal reset.
- Introduced UpdateAutoLinkedContent method to persist changes from machine-only link decorators without incrementing the version number, ensuring user-facing revisions remain consistent.
- Updated existing methods to utilize the new auto-linked content update logic, enhancing the handling of cross-link injection and dead-link cleanup.
- Enhanced the KnowledgeBase and WikiBrowser components to improve user experience during wiki operations, including status tracking and indexing indicators.
- Added scheduling for wiki status probes to provide timely feedback on indexing processes after user-triggered actions.
- Added functionality to track and display the wiki indexing status, including pending tasks and active state.
- Introduced a polling mechanism to update the wiki status at regular intervals, enhancing user awareness of background processes.
- Updated the breadcrumb navigation to show indexing indicators, improving the user interface and experience during wiki operations.
- Enhanced the WikiBrowser component to emit status changes to the parent component for better state management.
- Updated the scrubWikiPendingIngest method to accept a reason parameter, improving logging clarity during wiki cleanup and reparse operations.
- Introduced prepareWikiForReparse to maintain the integrity of the pending ingest queue during reparse events, preventing stale operations from executing.
- Enhanced the mapping logic in the wiki ingest process to differentiate between stale and reparse updates, ensuring accurate handling of knowledge contributions.
- Improved logging to provide better insights into the number of reparse and stale slugs processed during wiki ingestion.
- Added line-height to text elements for better readability.
- Enhanced the legend-action-icon class with display properties for improved alignment and consistency in icon presentation.
- Adjusted font sizes and line heights for better visual hierarchy and user experience.
- Upgraded the `mermaid` package from version 11.4.1 to 11.14.0 for improved features and bug fixes.
- Changed the resolved URLs for several `@chevrotain` packages to use Tencent's mirror, enhancing download reliability.
- Added the `@upsetjs/venn.js` package version 2.0.0 to the project, expanding visualization capabilities.
- Updated the `acorn` package from version 8.15.0 to 8.16.0 to incorporate the latest improvements.
- Removed deprecated CGO flags from the build command in the .air.toml file, streamlining the build process for the application.
- This change enhances compatibility and reduces potential build warnings, improving the overall development experience.
- Enhanced the UpdatePage and UpdateMeta methods to differentiate between user-visible content changes and bookkeeping updates, ensuring the version number is only incremented for actual content modifications.
- Updated the WikiPage struct and associated interfaces to reflect the new versioning policy, improving clarity on when the version is modified.
- Improved documentation for methods to clarify their intended use and behavior regarding versioning and metadata updates.
- Deleted migration files for the wiki_pages and wiki_page_issues tables, as well as the indexing_strategy column from knowledge_bases, to clean up the schema.
- This removal streamlines the migration process and ensures that only relevant migrations are retained in the project.
- Changed `kb_selection_mode` in agent type presets from "selected" to "all" to allow agents to access all knowledge bases by default.
- Revised prompt templates to clarify the handling of bound knowledge bases, replacing the `{{knowledge_bases}}` placeholder with a reference to the `<bound_knowledge_bases>` block in the user message's `<runtime_context>`.
- Enhanced internal logic to ensure that only searchable knowledge bases are considered during retrieval, improving the efficiency of knowledge searches.
- Added new functions for better formatting and handling of knowledge base metadata in the runtime context.
- Updated documentation and comments for clarity on the changes made to knowledge base interactions.
- Deleted `AGENT_WIKI_ANALYSIS.md`, `ANALYSIS_SUMMARY.md`, `COMPLETION_REPORT.md`, `DEPLOYMENT_CHECKLIST.md`, `DOCUMENTATION_INDEX.md`, `EXECUTIVE_SUMMARY_WIKI_CONFIG_FIX.md`, and `EXECUTIVE_SUMMARY.md` as they are no longer relevant to the current project scope.
- This cleanup helps streamline the repository and reduces clutter, ensuring that only up-to-date and necessary documentation remains accessible.
- Introduced extraction granularity settings in the wiki configuration, allowing users to control the level of detail in entity and concept extraction (focused, standard, exhaustive).
- Updated the UI to reflect these new options, including tooltips for better user guidance.
- Refactored the knowledge base editor to support the new extraction granularity feature, ensuring a seamless user experience.
- Improved the backend logic for wiki ingest tasks to utilize the new granularity settings, enhancing the accuracy of extracted content.
- Added tests to validate the new extraction granularity functionality and ensure robust performance across different configurations.