diff --git a/api/apps/restful_apis/compilation_template_api.py b/api/apps/restful_apis/compilation_template_api.py index 95ac44b8ab..6a8deeabd4 100644 --- a/api/apps/restful_apis/compilation_template_api.py +++ b/api/apps/restful_apis/compilation_template_api.py @@ -61,7 +61,7 @@ def list_wiki_presets() -> Response: ``api/db/init_data/compilation_templates/wiki/*.yaml``. Each entry carries ``id`` (filename stem) + ``topic`` + - ``instruction`` + ``page_example`` so the artifact-template editor + ``instruction`` + ``example`` so the artifact-template editor can pre-fill its "Page-structure example" / "Global rules" fields from a canned skeleton. Filesystem-fresh per request; no DB seed. """ diff --git a/api/db/init_data/compilation_templates/wiki.yaml b/api/db/init_data/compilation_templates/wiki.yaml index 507d6a0bd7..ffcf91e9e1 100644 --- a/api/db/init_data/compilation_templates/wiki.yaml +++ b/api/db/init_data/compilation_templates/wiki.yaml @@ -3,13 +3,14 @@ display_name: Wiki — Graph-based wiki config: kind: wiki plan: yes - example: | + instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - 1. Opening paragraph (2-4 sentences defining what this is). No heading. - 2. Sections with H2 headings, each starting with prose before sub-bullets. - 3. Bold key terms on first use; link them with [[ ]] wikilinks. - 4. Examples or implications where the source provides them. - 5. ## See also section at the end with wikilinks to highly related pages(less than 12).\n + example: | - Page structure could be as following: (Not provided) entity: description: >- diff --git a/api/db/init_data/compilation_templates/wiki/brand.yaml b/api/db/init_data/compilation_templates/wiki/brand.yaml index 1e5c067488..0b4b6209f2 100644 --- a/api/db/init_data/compilation_templates/wiki/brand.yaml +++ b/api/db/init_data/compilation_templates/wiki/brand.yaml @@ -1,8 +1,8 @@ instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - - 1. Take prefix ">" or "-" in the "page_example" as instructions to generate the content. + - 1. Take prefix ">" or "-" in the "example" as instructions to generate the content. - 2. Sections with H2 headings, each starting with prose before sub-bullets. -page_example: | +example: | # Tone of voice ## Principles @@ -39,9 +39,8 @@ page_example: | ## Dos & Dont's |We say 👍| We don't say 🙅| - | :--- | :---: | + | :--- | :---: | |Example| Example| |Example| Example| topic: marketing - \ No newline at end of file diff --git a/api/db/init_data/compilation_templates/wiki/engineering.yaml b/api/db/init_data/compilation_templates/wiki/engineering.yaml index 5dc014e555..d4dbdd0b92 100644 --- a/api/db/init_data/compilation_templates/wiki/engineering.yaml +++ b/api/db/init_data/compilation_templates/wiki/engineering.yaml @@ -1,9 +1,9 @@ instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - - 1. Take prefix ">" or "-" in the "page_example" as instructions to generate the content. + - 1. Take prefix ">" or "-" in the "example" as instructions to generate the content. - 2. Sections with H2 headings, each starting with prose before sub-bullets. -page_example: | +example: | # Secret feature(Title) ## Background & research @@ -17,7 +17,7 @@ page_example: | - List goals - KPIs - What success looks like - + ## Hypothesis > If we achieve/enable X, then user behavior Y changes in this way leading to positive metrics Z. @@ -32,11 +32,11 @@ page_example: | - Requirements - Future evolutions - Out of scope - + ## Designs and assets > Add any necessary figma project link, hi-fi mockups, SVGs, font files, images, prototypes... ## Open questions [x] E.g. is this part of the free plan? -topic: engineering \ No newline at end of file +topic: engineering diff --git a/api/db/init_data/compilation_templates/wiki/general.yaml b/api/db/init_data/compilation_templates/wiki/general.yaml index bd5e8ac505..fe1cd0692d 100644 --- a/api/db/init_data/compilation_templates/wiki/general.yaml +++ b/api/db/init_data/compilation_templates/wiki/general.yaml @@ -6,5 +6,5 @@ instruction: | - 3. Bold key terms on first use; link them with [[ ]] wikilinks. - 4. Examples or implications where the source provides them. - 5. ## See also section at the end with wikilinks to highly related pages(less than 12).\n -page_example: | - - (Not provided) \ No newline at end of file +example: | + - (Not provided) diff --git a/api/db/init_data/compilation_templates/wiki/market.yaml b/api/db/init_data/compilation_templates/wiki/market.yaml index 01970c23c5..448b248277 100644 --- a/api/db/init_data/compilation_templates/wiki/market.yaml +++ b/api/db/init_data/compilation_templates/wiki/market.yaml @@ -1,9 +1,9 @@ instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - - 1. Take prefix ">" or "-" in the "page_example" as instructions to generate the content. + - 1. Take prefix ">" or "-" in the "example" as instructions to generate the content. - 2. Sections with H2 headings, each starting with prose before sub-bullets. -page_example: | +example: | # Competitor analysis(title) ## Analysis strengths/weaknesses @@ -22,4 +22,4 @@ page_example: | ## Pricing -topic: marketing \ No newline at end of file +topic: marketing diff --git a/api/db/init_data/compilation_templates/wiki/product.yaml b/api/db/init_data/compilation_templates/wiki/product.yaml index b1da7e5672..19b7da0f6c 100644 --- a/api/db/init_data/compilation_templates/wiki/product.yaml +++ b/api/db/init_data/compilation_templates/wiki/product.yaml @@ -1,8 +1,8 @@ instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - - 1. Take prefix ">" or "-" in the "page_example" as instructions to generate the content. + - 1. Take prefix ">" or "-" in the "example" as instructions to generate the content. - 2. Sections with H2 headings, each starting with prose before sub-bullets. -page_example: | +example: | # Onboarding flow update ## Background & research summary(example): 40% of new users aren't activating. By reviewing our onboarding flow we'll help teams better understand the value of the product and activate faster on Slite. @@ -38,4 +38,4 @@ page_example: | Ok("quick_task result") ``` -topic: product \ No newline at end of file +topic: product diff --git a/api/db/init_data/compilation_templates/wiki/user_interview.yaml b/api/db/init_data/compilation_templates/wiki/user_interview.yaml index fc6bae2c7c..e22e00131c 100644 --- a/api/db/init_data/compilation_templates/wiki/user_interview.yaml +++ b/api/db/init_data/compilation_templates/wiki/user_interview.yaml @@ -1,8 +1,8 @@ instruction: | - Each page must be a proper encyclopedic article, NOT a flat bullet list: - - 1. Take prefix ">" or "-" in the "page_example" as instructions to generate the content. + - 1. Take prefix ">" or "-" in the "example" as instructions to generate the content. - 2. Sections with H2 headings, each starting with prose before sub-bullets. -page_example: | +example: | # User interview ## Takeaways - The team is still unaware about features x, y, z @@ -24,4 +24,4 @@ page_example: | ## Resources > Upload screenshots or video recordings of your interview. Any relevant file exchanged during your conversation can be added too. -topic: product \ No newline at end of file +topic: product diff --git a/api/db/services/compilation_template_service.py b/api/db/services/compilation_template_service.py index eb9c210346..db3baa046e 100644 --- a/api/db/services/compilation_template_service.py +++ b/api/db/services/compilation_template_service.py @@ -233,7 +233,7 @@ class CompilationTemplateService(CommonService): ``api/db/init_data/compilation_templates/wiki/*.yaml``. Each file contributes one preset dict with ``topic`` / - ``instruction`` / ``page_example`` fields (plus ``id`` derived + ``instruction`` / ``example`` fields (plus ``id`` derived from the filename stem so the frontend can key list items even when several presets share the same ``topic`` — which is by design; the UI groups presets by topic). @@ -278,7 +278,7 @@ class CompilationTemplateService(CommonService): "id": os.path.splitext(filename)[0], "topic": str(doc.get("topic") or "").strip(), "instruction": str(doc.get("instruction") or ""), - "page_example": str(doc.get("page_example") or ""), + "example": str(doc.get("example") or ""), } ) return presets diff --git a/rag/advanced_rag/knowlege_compile/wiki.py b/rag/advanced_rag/knowlege_compile/wiki.py index fc2437d3fb..e2757d9af3 100644 --- a/rag/advanced_rag/knowlege_compile/wiki.py +++ b/rag/advanced_rag/knowlege_compile/wiki.py @@ -2628,7 +2628,9 @@ WIKI_REFINE_WRITER_SYSTEM_TEMPLATE = ( "- Prose that just rephrases what was already said.\n\n" "# Language\n" "Write in the SAME LANGUAGE as the source text. Never translate content.\n\n" - "# Page structure — CRITICAL\n" + "# Additional writing instructions — CRITICAL\n" + "{template_instruction}\n\n" + "# Page structure example — CRITICAL\n" "{template_example}\n\n" "# What NOT to do\n" "- Do NOT dump raw bullet points from the source as the entire content.\n" @@ -2646,18 +2648,20 @@ WIKI_REFINE_WRITER_SYSTEM_TEMPLATE = ( ) -def _build_refine_writer_system(example: str | None) -> str: - """Return the writer system prompt with the configured page-structure - example (or ``WIKI_TEMPLATE_EXAMPLE`` when ``example`` is empty / - whitespace-only). Used by the REFINE phase to let each compilation - template override just the page-structure section. +def _build_refine_writer_system(instruction: str | None = None, page_example: str | None = None) -> str: + """Return the writer system prompt with separate instruction and page + example overrides. Empty values use the built-in defaults. The default-filled form is also exposed as ``WIKI_REFINE_WRITER_SYSTEM`` for callers that don't have an override to apply. """ - body = (example or "").strip() or WIKI_TEMPLATE_EXAMPLE - return WIKI_REFINE_WRITER_SYSTEM_TEMPLATE.format(template_example=body) + instruction_body = (instruction or "").strip() or "Follow the page structure and writing requirements below." + example_body = (page_example or "").strip() or WIKI_TEMPLATE_EXAMPLE + return WIKI_REFINE_WRITER_SYSTEM_TEMPLATE.format( + template_instruction=instruction_body, + template_example=example_body, + ) WIKI_REFINE_WRITER_SYSTEM = _build_refine_writer_system(None) @@ -3261,13 +3265,15 @@ async def _wiki_write_page_simple( all_plan_slugs: list[str], chat_mdl, llm_timeout: int, + instruction: Optional[str] = None, + page_example: Optional[str] = None, example: Optional[str] = None, ) -> str: """Single LLM call → markdown content. - ``example`` is the per-template ``parser_config.example`` override - for the writer's page-structure section. Falsy / whitespace-only - values fall through to ``WIKI_TEMPLATE_EXAMPLE``. + ``instruction`` and ``page_example`` are the separate per-template + writer overrides. ``example`` is retained as a fallback for callers + using the older combined configuration field. """ own_slug = plan_item.get("slug") or "" available = [s for s in all_plan_slugs if s and s != own_slug] @@ -3292,7 +3298,10 @@ async def _wiki_write_page_simple( content = await _wiki_chat_text( chat_mdl, - _build_refine_writer_system(example), + _build_refine_writer_system( + instruction=instruction, + page_example=page_example or example, + ), user_prompt, temperature=0.15, llm_timeout=llm_timeout, @@ -3532,6 +3541,8 @@ async def wiki_refine_from_plan( merge_shrink_threshold: float = WIKI_MERGE_BODY_SHRINK_THRESHOLD, force_rerun: bool = False, callback: Optional[Callable] = None, + instruction: Optional[str] = None, + page_example: Optional[str] = None, example: Optional[str] = None, ) -> list[dict]: """Phase 4 (REFINE) — KB-scoped. @@ -3726,6 +3737,8 @@ async def wiki_refine_from_plan( all_plan_slugs, chat_mdl, llm_timeout, + instruction=instruction, + page_example=page_example, example=example, ) if not content_md_raw: diff --git a/rag/svr/task_executor_refactor/dataset_wiki_generator.py b/rag/svr/task_executor_refactor/dataset_wiki_generator.py index 7fc6259716..4de3c72411 100644 --- a/rag/svr/task_executor_refactor/dataset_wiki_generator.py +++ b/rag/svr/task_executor_refactor/dataset_wiki_generator.py @@ -1283,12 +1283,15 @@ async def run_wiki( # ``kb_chat_llm_id`` is captured from the first eligible template and # used as the canonical chat model for KB-wide REDUCE/PLAN/REFINE. - # ``kb_writer_example`` follows the same first-template-wins rule. + # Writer instructions and page examples follow the same first-template- + # wins rule. first_parser_cfg = resolved_eligible[0][2] first_parser_cfg = first_parser_cfg if isinstance(first_parser_cfg, dict) else {} kb_chat_llm_id: Optional[str] = (first_parser_cfg.get("llm_id") or "").strip() or None - first_example = first_parser_cfg.get("example") - kb_writer_example: Optional[str] = first_example if isinstance(first_example, str) and first_example.strip() else None + first_instruction = first_parser_cfg.get("instruction") + first_page_example = first_parser_cfg.get("example") + kb_writer_instruction: Optional[str] = first_instruction if isinstance(first_instruction, str) and first_instruction.strip() else None + kb_writer_page_example: Optional[str] = first_page_example if isinstance(first_page_example, str) and first_page_example.strip() else None map_llm_pool = LLMCallPool(WIKI_MAP_LLM_POOL_SIZE, max_pending=WIKI_MAP_MAX_PENDING) n_docs = len(resolved_eligible) map_queue: asyncio.Queue = asyncio.Queue(maxsize=WIKI_MAP_QUEUE_SIZE) @@ -1456,7 +1459,8 @@ async def run_wiki( kb_id=ctx.kb_id, max_workers=WIKI_REFINE_WORKERS, callback=_stage_cb("[wiki REFINE]"), - example=kb_writer_example, + instruction=kb_writer_instruction, + page_example=kb_writer_page_example, ) except Exception: logging.exception("wiki: REDUCE/PLAN/REFINE failed for kb %s", ctx.kb_id) diff --git a/web/src/interfaces/database/compilation-template.ts b/web/src/interfaces/database/compilation-template.ts index 8310a8f2ad..5f36490d14 100644 --- a/web/src/interfaces/database/compilation-template.ts +++ b/web/src/interfaces/database/compilation-template.ts @@ -74,5 +74,5 @@ export interface IWikiPreset { id: string; topic: string; instruction: string; - page_example: string; + example: string; } diff --git a/web/src/lib/editor/raw-markdown-editor.tsx b/web/src/lib/editor/raw-markdown-editor.tsx index e6f7474bf2..22934052f6 100644 --- a/web/src/lib/editor/raw-markdown-editor.tsx +++ b/web/src/lib/editor/raw-markdown-editor.tsx @@ -3,8 +3,11 @@ * Theme follows the app's Nimbalyst theme system. */ -import type { OnChange, OnMount } from '@monaco-editor/react'; -import Editor from '@monaco-editor/react'; +import Editor, { + loader, + type OnChange, + type OnMount, +} from '@monaco-editor/react'; import { Eye } from 'lucide-react'; import type { editor as monacoEditor } from 'monaco-editor'; import type { JSX } from 'react'; @@ -12,6 +15,8 @@ import { useEffect, useRef } from 'react'; import { useTheme } from '@/components/theme-provider'; +loader.config({ paths: { vs: '/vs' } }); + interface Props { content: string; onChange?: (value: string) => void; diff --git a/web/src/pages/user-setting/compilation-templates/create-next/components/blueprints-step.tsx b/web/src/pages/user-setting/compilation-templates/create-next/components/blueprints-step.tsx index f6b6857b7b..bd4d283109 100644 --- a/web/src/pages/user-setting/compilation-templates/create-next/components/blueprints-step.tsx +++ b/web/src/pages/user-setting/compilation-templates/create-next/components/blueprints-step.tsx @@ -72,14 +72,14 @@ export function BlueprintsStep({ const instructionPath = `templates.${selectedTemplateIndex}.config.instruction` as const; - const pageExamplePath = - `templates.${selectedTemplateIndex}.config.page_example` as const; + const examplePath = + `templates.${selectedTemplateIndex}.config.example` as const; const useBlueprintPath = `templates.${selectedTemplateIndex}.config.use_blueprint` as const; - const pageExample = useWatch({ + const example = useWatch({ control: form.control, - name: pageExamplePath, + name: examplePath, }); const instruction = useWatch({ @@ -100,7 +100,7 @@ export function BlueprintsStep({ return selectedItem?.name ?? ''; }, [treeData, selectedItemId]); - const hasTemplateData = Boolean(selectedItemId || instruction || pageExample); + const hasTemplateData = Boolean(selectedItemId || instruction || example); const handleSelect = useCallback( (item?: TreeDataItem) => { @@ -113,11 +113,11 @@ export function BlueprintsStep({ form.setValue(instructionPath, preset.instruction, { shouldValidate: false, }); - form.setValue(pageExamplePath, preset.page_example, { + form.setValue(examplePath, preset.example, { shouldValidate: false, }); }, - [form, instructionPath, pageExamplePath], + [form, instructionPath, examplePath], ); const handleToggleBlueprint = useCallback( @@ -128,11 +128,11 @@ export function BlueprintsStep({ [form, useBlueprintPath], ); - const handlePageExampleChange = useCallback( + const handleExampleChange = useCallback( (value: string) => { - form.setValue(pageExamplePath, value, { shouldValidate: false }); + form.setValue(examplePath, value, { shouldValidate: false }); }, - [form, pageExamplePath], + [form, examplePath], ); return ( @@ -175,8 +175,8 @@ export function BlueprintsStep({