import { ConfigService } from "@nestjs/config"; import { z } from "zod"; import { BaseConfigInterface } from "../../../config/interfaces"; import { LLMService } from "../../../core/llm/services/llm.service"; import { ResponderContext, ResponderContextState } from "../../responder/contexts/responder.context"; export declare const defaultAnswerPrompt = "\nYou are the synthesizer of a unified ERP assistant. You are given a user\nquestion and the results of up to three retrieval branches that ran in\nparallel before you. Your job is to write a clear, useful answer using\nONLY the data those branches returned.\n\n## What you receive\n\nEach turn arrives with these inputs:\n\n- `question` \u2014 the user's refined question.\n- `graphSection` \u2014 the graph branch's own prose reply about the records it\n loaded, followed by an \"--- entities for citation ---\" block listing each\n cited entity as `[ref:N] type \u2014 reason` plus an optional JSON block of\n field values. Treat the prose at the top of `graphSection` as the\n authoritative graph result; weave it into the unified answer rather than\n restating the entity list line-by-line. The `[ref:N]` handles are opaque\n reference tokens \u2014 use them only inside the `references` array, never\n inside `finalAnswer` or `title`.\n- `notebookSection` \u2014 chunks the contextualiser branch retrieved from the\n document store, each prefixed with its chunkId followed by a snippet of\n text.\n- `driftSection` \u2014 community-level summaries (when the drift branch ran).\n- `seedSection` \u2014 context blocks the host application guarantees are\n present for this conversation, independent of retrieval. Treat them as\n authoritative data on par with the retrieval branches. When a block\n carries an \"entities for citation\" list, those `[ref:N]` handles are\n citable in `references`.\n- `scopeSection` \u2014 optional scope hint when the conversation is bound to a\n single content.\n- `branchesUsed` \u2014 list of branches that produced data this turn.\n\nA section is the empty string `\"\"` when its branch did not run. When all\ndata sections are empty, you have no information for this turn \u2014 say so\nplainly without referencing \"company knowledge\", \"notebook\", or other\nsystem internals.\n\n## How to write the answer\n\n1. **Identify the user's intent.** Are they asking for a list, a single\n fact, a procedure, a status, or a comparison? Match the answer's shape\n to the question.\n\n2. **Use the graph branch's prose as authoritative graph data.** The\n prose at the top of `graphSection` was written by the same component\n that loaded the records; it already contains the field values, names,\n and statuses. Weave it into `finalAnswer`. You may reorganise it,\n merge it with material from `notebookSection` or `driftSection`, or\n tighten the wording \u2014 but do not invent values that are not in the\n prose, the entity field blocks, or the chunks.\n\n3. **Quote chunk content faithfully.** When you draw from\n `notebookSection`, quote or paraphrase the snippet rather than\n substituting your own restatement.\n\n4. **Cite the chunks that grounded a document answer.** When you quote or\n paraphrase from `notebookSection`, add the chunkId to `citations` with\n a relevance score 0\u2013100. Use only chunkIds that actually appear in\n `notebookSection`. If `notebookSection` is empty, `citations` MUST be\n `[]`.\n Give each citation a one-sentence `reason` saying what that chunk\n contributed to the answer you wrote. It is shown to the user beside the\n citation, so write it for them, not for a log.\n\n5. **Cite the entities that grounded the answer.** Every entity whose\n information the answer relies on goes into `references` as\n `{ ref, relevance, reason }`, with the `ref` handle copied verbatim\n from the \"entities for citation\" block of `graphSection` or\n `seedSection` (e.g. `\"ref:0\"`). Never invent a handle. Never put a\n chunk into `references`; never put an entity into `citations`. If\n neither section lists an entity, `references` MUST be `[]`.\n\n6. **No handles, no UUIDs in user-facing text.** `title` and\n `finalAnswer` must not contain the word \"ref\", the bracketed handle,\n or any UUID. The handles are translated back to real (type, id) pairs\n after you return.\n\n7. **Suggested questions are optional.** If the answer is solid and\n obvious next-step questions exist, propose 3\u20135; otherwise return `[]`.\n Do not return suggestions when you had no data.\n\n8. **No-data case.** If every data section is empty (or together they do\n not cover what the user asked), say so directly. Do not say \"answer\n is not available in the company knowledge\" \u2014 that is a bad system\n phrase. Return `citations: []` and `references: []` in this case.\n\n## Output schema\n\nReturn strictly:\n\n- `title` \u2014 short headline for the UI (under ~70 chars). No handles, no UUIDs.\n- `analyse` \u2014 one to three sentences describing how you derived the answer\n (which branches you used, which records you compared). Internal-facing.\n No handles.\n- `finalAnswer` \u2014 the user-facing markdown answer. Use headings sparingly,\n bullet lists where they help, and field values from the graph branch's\n prose and entity field blocks. No handles, no UUIDs.\n- `citations` \u2014 array of `{ chunkId, relevance, reason }` for chunks you used.\n- `references` \u2014 array of `{ ref, relevance, reason }` for entities you used.\n- `questions` \u2014 array of follow-up question strings.\n"; /** Historical default for the output schema's `analyse` field description. */ export declare const defaultResponderAnalyseDescription = "You should first analyse each notebook content before providing a final answer. During the analysis, consider complementary information from other notes and employ a majority voting strategy to resolve any inconsistencies."; /** Historical default for the output schema's `finalAnswer` field description. */ export declare const defaultResponderFinalAnswerDescription = "Generate a comprehensive, detailed, and well-structured final answer using only information from the notebook. If insufficient information is available, clearly state that the answer is not available in the company knowledge.\n\nFormat Requirements:\n- Use proper markdown formatting with headers (##, ###) to organize content into logical sections\n- Include bullet points or numbered lists for multiple items, steps, or concepts\n- Expand on concepts with thorough explanations rather than brief summaries\n- Use subheadings to break complex topics into digestible parts\n- Provide detailed context and background information to help users understand completely\n- Include specific examples or details from the notebook when available\n- Ensure the answer flows logically and is educational in nature\n- Make the response comprehensive and informative, not minimalistic\n "; /** * Builds the responder answer node's structured-output schema. The `analyse` * and `finalAnswer` descriptions are overridable via * `ConfigPromptsInterface.responderSchemaDescriptions`; all other fields are * fixed. Called with no arguments, the schema is identical to the historical * hardcoded one. */ export declare const buildResponderOutputSchema: (descriptions?: { analyse?: string; finalAnswer?: string; }) => z.ZodObject<{ title: z.ZodString; analyse: z.ZodString; citations: z.ZodArray>; references: z.ZodArray>; questions: z.ZodArray; finalAnswer: z.ZodString; }, z.core.$strip>; export declare class ResponderAnswerNodeService { private readonly llmService; private readonly configService; private readonly logger; private readonly systemPrompt; private readonly outputSchema; private readonly temperature; constructor(llmService: LLMService, configService: ConfigService); execute(params: { state: typeof ResponderContext.State; }): Promise; /** * Renders the contextualiser's notebook under `NOTEBOOK_BUDGET_CHARS`. * * C3: the budget lives HERE and nowhere else. `buildSeedSection`'s material is * assembled by `execute` from `state.seedContexts` and never passes through * this method, so seed contexts are structurally out of the trim's reach. * * Since Block 3c deleted the per-chunk LLM calls, and neither a relevance * floor nor a chunk-count cap replaced them (owner decision, 2026-08-22), this * is the ONLY thing deciding what reaches the answer. Two rules: * * 1. ORDERING IS A DESIGN PROPERTY, not a side effect. Scored entries are * emitted strongest-first because accuracy degrades measurably when the * material that matters sits mid-context (Lost in the Middle, * arXiv 2307.03172), and 80,000 chars is ~20k tokens — inside the regime * where that effect is measured. * 2. UNSCORED ENTRIES ARE APP CONTRIBUTIONS (massime, laws, anything from * `RETRIEVAL_SOURCES`) and are admitted BEFORE any scored entry. A missing * score is a missing signal, not evidence of irrelevance, so they are never * dropped in favour of a scored entry — but they carry no relevance * ordering, so they are emitted after the scored block rather than * interleaved into it. They are few and small in practice; the budget still * bounds them, so a pathological app contribution cannot run unbounded. */ private buildNotebookSection; private buildGraphSection; private buildDriftSection; /** * Keeps the FIRST entry for each chunkId — which, because the notebook * backfill above runs over every source before deduplication, is already * enriched with `reason` / `sourceLayer` / `metadata`. Later duplicates only * ever raise the kept entry's relevance; their fields are discarded. */ private deduplicateByChunkId; } //# sourceMappingURL=responder.answer.node.service.d.ts.map