diff --git a/api/controllers/console/knowledge_fs/resources.py b/api/controllers/console/knowledge_fs/resources.py index 2a434981b5b..1c111f3c1ed 100644 --- a/api/controllers/console/knowledge_fs/resources.py +++ b/api/controllers/console/knowledge_fs/resources.py @@ -174,6 +174,7 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSPresignedUploadResponse, KnowledgeFSProfileMigrationResponse, KnowledgeFSQualityListQuery, + KnowledgeFSQualityReplayDetailQuery, KnowledgeFSQualityReplayListQuery, KnowledgeFSQualityReplayListResponse, KnowledgeFSQualityReplayPayload, @@ -299,6 +300,7 @@ register_schema_models( KnowledgeFSAdmittedQueryRequest, KnowledgeFSQueryCreatePayload, KnowledgeFSQualityListQuery, + KnowledgeFSQualityReplayDetailQuery, KnowledgeFSQualityReplayListQuery, KnowledgeFSQualityReplayPayload, KnowledgeFSResearchTaskPartialsQuery, @@ -3376,6 +3378,7 @@ class KnowledgeFSSpaceQualityReplayApi(Resource): @console_ns.route("/knowledge-fs/spaces//quality/replay-runs/") class KnowledgeFSSpaceQualityReplayDetailApi(Resource): + @console_ns.doc(params=query_params_from_model(KnowledgeFSQualityReplayDetailQuery)) @console_ns.response( HTTPStatus.OK, "KnowledgeFS quality replay run", @@ -3387,11 +3390,13 @@ class KnowledgeFSSpaceQualityReplayDetailApi(Resource): @_knowledge_fs_errors def get(self, control_space_id: str, run_id: str): actor_id, tenant_id = _actor() + query = KnowledgeFSQualityReplayDetailQuery.model_validate(request.args.to_dict()) result = _console_services().facade.get_quality_replay( tenant_id=tenant_id, account_id=actor_id, control_space_id=control_space_id, run_id=run_id, + evidence_item_id=str(query.evidence_item_id) if query.evidence_item_id else None, ) return dump_response(KnowledgeFSQualityReplayResponse, result) diff --git a/api/knowledge-fs-contract.lock.json b/api/knowledge-fs-contract.lock.json index 102a7d4b20b..ad89bc25797 100644 --- a/api/knowledge-fs-contract.lock.json +++ b/api/knowledge-fs-contract.lock.json @@ -1,7 +1,7 @@ { "schemaVersion": 5, - "subtreeTree": "04565f7a5d363ef4ccb11547e3fa148f99b42ca0", - "openapiSha256": "c9de381284acb183e947fe4d6b019135223511c4fed35b91e966d808ea3a2bf4", + "subtreeTree": "05e402ad162663945d482ebfa7c5221e73347364", + "openapiSha256": "2cf348c68bbe65dd51bbde9a0a4f91398beeebd79e89e9288c9386b26ae09796", "capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7", "capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3", "productOperationManifestSha256": "11f801a41805b250f17766935ee9cc7fc93c3ef6489503ba6297673b73362b7d", diff --git a/api/services/knowledge_fs/data_facade.py b/api/services/knowledge_fs/data_facade.py index 7b878e15d92..740baa15c63 100644 --- a/api/services/knowledge_fs/data_facade.py +++ b/api/services/knowledge_fs/data_facade.py @@ -2198,6 +2198,7 @@ class KnowledgeFSDataFacade: account_id: str, control_space_id: str, run_id: str, + evidence_item_id: str | None = None, ) -> KnowledgeFSQualityReplayResponse: raw = self._interactive_child( tenant_id=tenant_id, @@ -2206,6 +2207,7 @@ class KnowledgeFSDataFacade: operation_id="getQualityReplay", resource_id=run_id, path_parameters=(("runId", run_id),), + query=(("evidenceItemId", evidence_item_id),) if evidence_item_id else (), ) return KnowledgeFSQualityReplayResponse.model_validate(raw) diff --git a/api/services/knowledge_fs/product_dto.py b/api/services/knowledge_fs/product_dto.py index d5b8970dc27..aee1405a948 100644 --- a/api/services/knowledge_fs/product_dto.py +++ b/api/services/knowledge_fs/product_dto.py @@ -3370,7 +3370,35 @@ class KnowledgeFSQualityReplayPayload(BaseModel): return self +class KnowledgeFSQualityReplayDetailQuery(BaseModel): + evidence_item_id: UUID | None = None + + +class KnowledgeFSQualityReplayEvidenceItem(ResponseModel): + available: bool + document_name: str | None = Field( + default=None, + validation_alias=AliasChoices("document_name", "documentName"), + ) + matched: bool + ordinal: int = Field(ge=1) + page_number: int | None = Field( + default=None, + ge=1, + validation_alias=AliasChoices("page_number", "pageNumber"), + ) + section_path: list[str] = Field( + default_factory=list, + validation_alias=AliasChoices("section_path", "sectionPath"), + ) + text: str | None = None + + class KnowledgeFSQualityReplayEvidenceDiff(ResponseModel): + evidence_items: list[KnowledgeFSQualityReplayEvidenceItem] | None = Field( + default=None, + validation_alias=AliasChoices("evidence_items", "evidenceItems"), + ) expected_count: int = Field(ge=0, validation_alias=AliasChoices("expected_count", "expectedCount")) matched_count: int = Field(ge=0, validation_alias=AliasChoices("matched_count", "matchedCount")) missing_count: int = Field(ge=0, validation_alias=AliasChoices("missing_count", "missingCount")) diff --git a/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py b/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py index 9d63d129211..757d99e191f 100644 --- a/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py +++ b/api/tests/unit_tests/services/test_knowledge_fs_data_facade.py @@ -2041,7 +2041,7 @@ def test_logical_document_delete_preserves_initial_row_version() -> None: "get_quality_replay", "KnowledgeFSQualityReplayResponse", "getQualityReplay", - {"run_id": "replay-run-1"}, + {"run_id": "replay-run-1", "evidence_item_id": "replay-item-1"}, "replay-run-1", ), ( @@ -2285,6 +2285,7 @@ def test_facade_public_methods_preserve_the_registered_operation_and_child_bindi ) if operation_id == "getQualityReplay": assert delegated.call_args.kwargs["path_parameters"] == (("runId", "replay-run-1"),) + assert delegated.call_args.kwargs["query"] == (("evidenceItemId", "replay-item-1"),) if operation_id == "getOverviewQueryOutcomes": assert delegated.call_args.kwargs["query"] == (("window", "7d"),) if operation_id == "cancelBackgroundTask": diff --git a/api/tests/unit_tests/services/test_knowledge_fs_product_dto.py b/api/tests/unit_tests/services/test_knowledge_fs_product_dto.py index b37556504ab..f3cdf9d1a4f 100644 --- a/api/tests/unit_tests/services/test_knowledge_fs_product_dto.py +++ b/api/tests/unit_tests/services/test_knowledge_fs_product_dto.py @@ -34,6 +34,8 @@ from services.knowledge_fs.product_dto import ( KnowledgeFSOverviewWindowQuery, KnowledgeFSPublicFailureResponse, KnowledgeFSQualityListQuery, + KnowledgeFSQualityReplayDetailQuery, + KnowledgeFSQualityReplayEvidenceDiff, KnowledgeFSQualityReplayPayload, KnowledgeFSQueryCreatePayload, KnowledgeFSRerankIntent, @@ -78,6 +80,40 @@ def test_quality_replay_payload_requires_exactly_one_selection_mode() -> None: KnowledgeFSQualityReplayPayload(selection="all-active", golden_question_ids=["question-1"]) +def test_quality_replay_detail_query_requires_a_uuid_evidence_item_id() -> None: + value = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c50" + + assert str(KnowledgeFSQualityReplayDetailQuery(evidence_item_id=value).evidence_item_id) == value + with pytest.raises(ValidationError): + KnowledgeFSQualityReplayDetailQuery(evidence_item_id="not-a-uuid") + + +def test_quality_replay_evidence_diff_accepts_safe_detail_aliases() -> None: + diff = KnowledgeFSQualityReplayEvidenceDiff.model_validate( + { + "evidenceItems": [ + { + "available": True, + "documentName": "Permissions.pdf", + "matched": False, + "ordinal": 1, + "pageNumber": 3, + "sectionPath": ["Permissions", "Limits"], + "text": "Editors cannot promote themselves.", + } + ], + "expectedCount": 1, + "matchedCount": 0, + "missingCount": 1, + "retrievedCount": 10, + } + ) + + assert diff.evidence_items is not None + assert diff.evidence_items[0].document_name == "Permissions.pdf" + assert diff.evidence_items[0].section_path == ["Permissions", "Limits"] + + def test_golden_question_evidence_match_requires_exactly_one_lookup_mode() -> None: assert KnowledgeFSGoldenQuestionEvidenceMatchPayload(evidence=" permissions ").evidence == "permissions" assert KnowledgeFSGoldenQuestionEvidenceMatchPayload(node_ids=[" node-1 ", "node-1", "node-2"]).node_ids == [ diff --git a/knowledge-fs/.harness/changes/2026-08-19-quality-replay-evidence-details.md b/knowledge-fs/.harness/changes/2026-08-19-quality-replay-evidence-details.md new file mode 100644 index 00000000000..43aa99a5ffe --- /dev/null +++ b/knowledge-fs/.harness/changes/2026-08-19-quality-replay-evidence-details.md @@ -0,0 +1,47 @@ +# Quality replay evidence details + +## What changed + +- Added an optional, item-scoped evidence-detail expansion to the existing quality replay detail + endpoint. The normal report response remains compact; evidence text is resolved only after the + user opens one question's hit count. +- Resolved immutable expected evidence nodes in their persisted order and classified each passage + as hit or missed from the replay's frozen evidence diff. +- Added document name, section path, page number, and passage text to the expanded response without + exposing internal node identifiers or unrelated retrieved identifiers. +- Made the report's evidence-hit count interactive and added a centered dialog that groups hit and + missed passages, with loading, retry, empty, inaccessible, and close states. +- Added localized UI copy for every supported dataset locale. + +## Why + +The report previously exposed only aggregate counts such as `1/2`. That was enough to determine +that an evaluation failed, but not enough to identify which expected passage was absent or inspect +the evidence that did match. + +## Security and performance + +- Evidence nodes and their backing document assets are rechecked against the current candidate + grants before text or source metadata is returned. Deleted or inaccessible historical evidence + is represented by a redacted placeholder. +- Node resolution is one bounded bulk lookup. Backing asset lookups are deduplicated and bounded by + the expected evidence set, and execute only for the single replay item explicitly requested. +- Replay list and ordinary replay-detail requests do not load evidence text, preventing report + payload growth across large evaluation runs. + +## Verification + +- `@knowledge/api` quality-control handler tests: 25 passed. +- `@knowledge/api` typecheck passed. +- Python KnowledgeFS DTO and facade tests: 155 passed. +- Generated Console contract typecheck passed. +- Quality evaluation panel tests: 4 passed. +- Targeted Biome, Ruff, Ruff format, frontend static checks, locale JSON checks, and + `git diff --check` passed. + +## Known risks and follow-up + +- Historical evidence may have been deleted or may no longer be visible under the current user's + grants; the dialog intentionally redacts it instead of reproducing the old content. +- A future report export feature should use a paginated evidence-detail endpoint rather than + expanding every replay item in one response. diff --git a/knowledge-fs/packages/api/src/quality-control-handlers.test.ts b/knowledge-fs/packages/api/src/quality-control-handlers.test.ts index db1c0bc471b..e576943bc7c 100644 --- a/knowledge-fs/packages/api/src/quality-control-handlers.test.ts +++ b/knowledge-fs/packages/api/src/quality-control-handlers.test.ts @@ -26,6 +26,9 @@ const GOLDEN_QUESTION_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c44"; const TRACE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c46"; const BAD_CASE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c47"; const CAPABILITY_GRANT_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c48"; +const MATCHED_EVIDENCE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c50"; +const MISSING_EVIDENCE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c51"; +const DOCUMENT_ASSET_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c52"; const NOW = "2026-07-14T15:00:00.000Z"; describe("quality-control handlers", () => { @@ -184,6 +187,212 @@ describe("quality-control handlers", () => { }); }); + it("loads permission-safe matched and missing evidence details only when requested", async () => { + const base = replayRun(); + const replayItem = { + ...base.items[0], + expectedEvidenceIds: [MATCHED_EVIDENCE_ID, MISSING_EVIDENCE_ID], + result: { + evidenceDiff: { + matchPolicy: "all", + missingEvidenceIds: [MISSING_EVIDENCE_ID], + retrievedEvidenceIds: [MATCHED_EVIDENCE_ID, "retrieved-evidence-secret"], + }, + metrics: { totalMs: 17 }, + }, + }; + const getManyByIdsAcrossGenerations = vi.fn(async () => [ + { + documentAssetId: DOCUMENT_ASSET_ID, + id: MATCHED_EVIDENCE_ID, + permissionScope: ["subject:editor-1"], + sourceLocation: { pageNumber: 2, sectionPath: ["Permissions", "Roles"] }, + text: "Workspace owners can change member permissions.", + }, + { + documentAssetId: DOCUMENT_ASSET_ID, + id: MISSING_EVIDENCE_ID, + permissionScope: ["subject:editor-1"], + sourceLocation: { pageNumber: 3, sectionPath: ["Permissions", "Limitations"] }, + text: "Editors cannot promote themselves to owner.", + }, + ]); + const getAsset = vi.fn(async () => ({ + filename: "Workspace permissions.pdf", + id: DOCUMENT_ASSET_ID, + metadata: { permissionScope: ["subject:editor-1"] }, + })); + const app = qualityApp( + { + getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })), + } as unknown as QualityControlRepository, + { + assets: { get: getAsset } as never, + nodes: { getManyByIdsAcrossGenerations } as never, + }, + ); + + const response = await app.request( + `/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`, + ); + + expect(response.status).toBe(200); + expect(getManyByIdsAcrossGenerations).toHaveBeenCalledWith({ + ids: [MATCHED_EVIDENCE_ID, MISSING_EVIDENCE_ID], + knowledgeSpaceId: SPACE_ID, + }); + const body = await response.json(); + expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([ + { + available: true, + documentName: "Workspace permissions.pdf", + matched: true, + ordinal: 1, + pageNumber: 2, + sectionPath: ["Permissions", "Roles"], + text: "Workspace owners can change member permissions.", + }, + { + available: true, + documentName: "Workspace permissions.pdf", + matched: false, + ordinal: 2, + pageNumber: 3, + sectionPath: ["Permissions", "Limitations"], + text: "Editors cannot promote themselves to owner.", + }, + ]); + expect(JSON.stringify(body)).not.toContain("retrieved-evidence-secret"); + }); + + it("redacts replay evidence text that is no longer visible to the current grants", async () => { + const base = replayRun(); + const replayItem = { + ...base.items[0], + expectedEvidenceIds: [MISSING_EVIDENCE_ID], + result: { + evidenceDiff: { + missingEvidenceIds: [MISSING_EVIDENCE_ID], + retrievedEvidenceIds: [], + }, + }, + }; + const app = qualityApp( + { + getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })), + } as unknown as QualityControlRepository, + { + assets: { get: vi.fn() } as never, + nodes: { + getManyByIdsAcrossGenerations: vi.fn(async () => [ + { + documentAssetId: DOCUMENT_ASSET_ID, + id: MISSING_EVIDENCE_ID, + permissionScope: ["scope:not-granted"], + sourceLocation: { sectionPath: ["Private"] }, + text: "private-evidence-secret", + }, + ]), + } as never, + }, + ); + + const response = await app.request( + `/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`, + ); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([ + { available: false, matched: false, ordinal: 1, sectionPath: [] }, + ]); + expect(JSON.stringify(body)).not.toContain("private-evidence-secret"); + }); + + it("redacts replay evidence when its document asset is no longer visible", async () => { + const base = replayRun(); + const replayItem = { + ...base.items[0], + expectedEvidenceIds: [MATCHED_EVIDENCE_ID], + result: { + evidenceDiff: { + missingEvidenceIds: [], + retrievedEvidenceIds: [MATCHED_EVIDENCE_ID], + }, + }, + }; + const app = qualityApp( + { + getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })), + } as unknown as QualityControlRepository, + { + assets: { + get: vi.fn(async () => ({ + filename: "private-document.pdf", + id: DOCUMENT_ASSET_ID, + metadata: { permissionScope: ["scope:not-granted"] }, + })), + } as never, + nodes: { + getManyByIdsAcrossGenerations: vi.fn(async () => [ + { + documentAssetId: DOCUMENT_ASSET_ID, + id: MATCHED_EVIDENCE_ID, + permissionScope: ["subject:editor-1"], + sourceLocation: { sectionPath: ["Private"] }, + text: "asset-protected-evidence-secret", + }, + ]), + } as never, + }, + ); + + const response = await app.request( + `/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`, + ); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([ + { available: false, matched: true, ordinal: 1, sectionPath: [] }, + ]); + expect(JSON.stringify(body)).not.toContain("asset-protected-evidence-secret"); + expect(JSON.stringify(body)).not.toContain("private-document.pdf"); + }); + + it("rejects oversized replay evidence details before loading content", async () => { + const base = replayRun(); + const replayItem = { + ...base.items[0], + expectedEvidenceIds: Array.from( + { length: 51 }, + (_, index) => `018f0d60-7a49-7cc2-9c1b-${index.toString(16).padStart(12, "0")}`, + ), + }; + const getManyByIdsAcrossGenerations = vi.fn(); + const getAsset = vi.fn(); + const app = qualityApp( + { + getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })), + } as unknown as QualityControlRepository, + { + assets: { get: getAsset } as never, + nodes: { getManyByIdsAcrossGenerations } as never, + }, + ); + + const response = await app.request( + `/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`, + ); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toEqual({ + error: "Replay evidence detail limit exceeded", + }); + expect(getManyByIdsAcrossGenerations).not.toHaveBeenCalled(); + expect(getAsset).not.toHaveBeenCalled(); + }); + it("serializes optional replay errors, results, embedding, and rerank provenance safely", async () => { const base = replayRun(); const getReplay = vi diff --git a/knowledge-fs/packages/api/src/quality-control-handlers.ts b/knowledge-fs/packages/api/src/quality-control-handlers.ts index 07c6a19b929..bcf59232eb0 100644 --- a/knowledge-fs/packages/api/src/quality-control-handlers.ts +++ b/knowledge-fs/packages/api/src/quality-control-handlers.ts @@ -56,6 +56,7 @@ import { evidenceBundleFromAnswerTrace } from "./query-virtual-entries"; const MAX_QUALITY_REPLAY_QUESTIONS = 1_000; const QUALITY_REPLAY_QUESTION_PAGE_SIZE = 100; +const MAX_QUALITY_REPLAY_EVIDENCE_DETAILS = 50; class QualityReplaySelectionTooLargeError extends Error {} @@ -512,9 +513,22 @@ export function registerQualityControlHandlers({ subjectId: scope.subject.subjectId, tenantId: scope.subject.tenantId, }); - return run - ? context.json(publicReplay(run), 200) - : context.json({ error: "Replay run not found" }, 404); + if (!run) return context.json({ error: "Replay run not found" }, 404); + const evidenceItemId = context.req.valid("query").evidenceItemId; + if (!evidenceItemId) return context.json(publicReplay(run), 200); + const replayItem = run.items.find((item) => item.id === evidenceItemId); + if (!replayItem) return context.json({ error: "Replay item not found" }, 404); + if (replayItem.expectedEvidenceIds.length > MAX_QUALITY_REPLAY_EVIDENCE_DETAILS) { + return context.json({ error: "Replay evidence detail limit exceeded" }, 400); + } + const evidenceItems = await publicReplayEvidenceItems({ + assets, + candidateGrants: scope.candidateGrants, + item: replayItem, + knowledgeSpaceId: scope.knowledgeSpaceId, + nodes, + }); + return context.json(publicReplay(run, { evidenceItemId: replayItem.id, evidenceItems }), 200); }); app.openapi(listQualityReplaysRoute, async (context) => { @@ -537,7 +551,7 @@ export function registerQualityControlHandlers({ }); return context.json( { - items: result.items.map(publicReplay), + items: result.items.map((item) => publicReplay(item)), ...(result.nextCursor ? { nextCursor: encodeQualityCursor(result.nextCursor) } : {}), }, 200, @@ -1004,7 +1018,96 @@ function publicBadCase(value: ProductionBadCase) { return { ...safe, tags: [...value.tags] }; } -function publicReplay(run: QualityReplayRun) { +interface PublicReplayEvidenceItem { + readonly available: boolean; + readonly documentName?: string | undefined; + readonly matched: boolean; + readonly ordinal: number; + readonly pageNumber?: number | undefined; + readonly sectionPath: readonly string[]; + readonly text?: string | undefined; +} + +async function publicReplayEvidenceItems(input: { + readonly assets: Pick; + readonly candidateGrants: readonly string[]; + readonly item: QualityReplayRun["items"][number]; + readonly knowledgeSpaceId: string; + readonly nodes: Pick; +}): Promise { + const resolvedNodes = await input.nodes.getManyByIdsAcrossGenerations({ + ids: input.item.expectedEvidenceIds, + knowledgeSpaceId: input.knowledgeSpaceId, + }); + const nodesById = new Map( + resolvedNodes + .filter((node) => candidatePermissionAllowsNode(node, input.candidateGrants)) + .map((node) => [node.id, node]), + ); + const documentAssetIds = [ + ...new Set([...nodesById.values()].map((node) => node.documentAssetId)), + ]; + const resolvedAssets = await Promise.all( + documentAssetIds.map((id) => + input.assets.get({ id, knowledgeSpaceId: input.knowledgeSpaceId }), + ), + ); + const assetsById = new Map( + resolvedAssets + .filter((asset): asset is NonNullable => + Boolean(asset && candidatePermissionAllowsAsset(asset, input.candidateGrants)), + ) + .map((asset) => [asset.id, asset]), + ); + const diff = + input.item.result && isPlainRecord(input.item.result.evidenceDiff) + ? input.item.result.evidenceDiff + : {}; + const missingEvidenceIds = new Set( + Array.isArray(diff.missingEvidenceIds) + ? diff.missingEvidenceIds.filter((id): id is string => typeof id === "string") + : [], + ); + return input.item.expectedEvidenceIds.map((id, index) => { + const node = nodesById.get(id); + if (!node) { + return { + available: false, + matched: !missingEvidenceIds.has(id), + ordinal: index + 1, + sectionPath: [], + }; + } + const asset = assetsById.get(node.documentAssetId); + if (!asset) { + return { + available: false, + matched: !missingEvidenceIds.has(id), + ordinal: index + 1, + sectionPath: [], + }; + } + return { + available: true, + documentName: asset.filename, + matched: !missingEvidenceIds.has(id), + ordinal: index + 1, + ...(node.sourceLocation.pageNumber === undefined + ? {} + : { pageNumber: node.sourceLocation.pageNumber }), + sectionPath: [...node.sourceLocation.sectionPath], + text: node.text, + }; + }); +} + +function publicReplay( + run: QualityReplayRun, + evidence?: { + readonly evidenceItemId: string; + readonly evidenceItems: readonly PublicReplayEvidenceItem[]; + }, +) { const embedding = run.frozenSnapshot.embeddingProfile; const retrieval = run.frozenSnapshot.retrievalProfile; const passed = run.items.filter((item) => item.state === "passed").length; @@ -1027,6 +1130,7 @@ function publicReplay(run: QualityReplayRun) { item.result, item.expectedEvidenceIds.length, item.matchPolicy, + evidence?.evidenceItemId === item.id ? evidence.evidenceItems : undefined, ), } : {}), @@ -1076,6 +1180,7 @@ function publicReplayResult( value: Readonly>, expectedCount: number, matchPolicy: "all" | "any", + evidenceItems?: readonly PublicReplayEvidenceItem[], ) { const diff = isPlainRecord(value.evidenceDiff) ? value.evidenceDiff : {}; const metrics = isPlainRecord(value.metrics) ? value.metrics : {}; @@ -1105,6 +1210,7 @@ function publicReplayResult( const matchedCount = Math.max(0, expectedCount - missingCount); return { evidenceDiff: { + ...(evidenceItems ? { evidenceItems: evidenceItems.map(publicReplayEvidenceItem) } : {}), expectedCount, matchedCount, missingCount, @@ -1115,6 +1221,18 @@ function publicReplayResult( }; } +function publicReplayEvidenceItem(item: PublicReplayEvidenceItem) { + return { + available: item.available, + ...(item.documentName ? { documentName: item.documentName } : {}), + matched: item.matched, + ordinal: item.ordinal, + ...(item.pageNumber === undefined ? {} : { pageNumber: item.pageNumber }), + sectionPath: [...item.sectionPath], + ...(item.text ? { text: item.text } : {}), + }; +} + function isPlainRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } diff --git a/knowledge-fs/packages/api/src/quality-control-routes.ts b/knowledge-fs/packages/api/src/quality-control-routes.ts index 539abd7651c..b7b6c42abac 100644 --- a/knowledge-fs/packages/api/src/quality-control-routes.ts +++ b/knowledge-fs/packages/api/src/quality-control-routes.ts @@ -10,6 +10,16 @@ const BadCaseParams = SpaceParams.extend({ badCaseId: z.string().uuid() }); const ReplayParams = SpaceParams.extend({ runId: z.string().uuid() }); const DateTime = z.string().datetime(); +const ReplayEvidenceItemSchema = z.object({ + available: z.boolean(), + documentName: z.string().optional(), + matched: z.boolean(), + ordinal: z.number().int().positive(), + pageNumber: z.number().int().positive().optional(), + sectionPath: z.array(z.string()), + text: z.string().optional(), +}); + export const QualityTraceHistoryQuerySchema = z .object({ cursor: z.string().max(1000).optional(), @@ -112,6 +122,7 @@ export const ReplayRunSchema = z result: z .object({ evidenceDiff: z.object({ + evidenceItems: z.array(ReplayEvidenceItemSchema).optional(), expectedCount: z.number().int().nonnegative(), matchedCount: z.number().int().nonnegative(), missingCount: z.number().int().nonnegative(), @@ -514,7 +525,10 @@ export const getQualityReplayRoute = createRoute({ operationId: "getQualityReplay", path: "/knowledge-spaces/{id}/quality/replay-runs/{runId}", "x-knowledge-fs-max-response-bytes": 4 * 1024 * 1024, - request: { params: ReplayParams }, + request: { + params: ReplayParams, + query: z.object({ evidenceItemId: z.string().uuid().optional() }).strict(), + }, responses: { 200: { content: { "application/json": { schema: ReplayRunSchema } }, diff --git a/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts index e80b10a8ead..74afccc9b85 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/orpc.gen.ts @@ -121,6 +121,7 @@ import { zGetKnowledgeFsSpacesByControlSpaceIdQualityBadCasesQuery, zGetKnowledgeFsSpacesByControlSpaceIdQualityBadCasesResponse, zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath, + zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery, zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdResponse, zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsPath, zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsQuery, @@ -1617,7 +1618,12 @@ export const get33 = oc path: '/knowledge-fs/spaces/{control_space_id}/quality/replay-runs/{run_id}', tags: ['console'], }) - .input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath })) + .input( + z.object({ + params: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath, + query: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery.optional(), + }), + ) .output(zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdResponse) export const byRunId = { diff --git a/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts index 56a80a17c93..119916ec003 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/types.gen.ts @@ -2229,6 +2229,7 @@ export type KnowledgeFsTraceStageResponse = { } export type KnowledgeFsQualityReplayEvidenceDiff = { + evidence_items?: Array | null expected_count: number matched_count: number missing_count: number @@ -2248,6 +2249,16 @@ export type KnowledgeFsQualityReplayMetrics = { total_ms?: number | null } +export type KnowledgeFsQualityReplayEvidenceItem = { + available: boolean + document_name?: string | null + matched: boolean + ordinal: number + page_number?: number | null + section_path?: Array + text?: string | null +} + export type GetKnowledgeFsWellKnownJwksJsonData = { body?: never path?: never @@ -3547,7 +3558,9 @@ export type GetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdData = { control_space_id: string run_id: string } - query?: never + query?: { + evidence_item_id?: string + } url: '/knowledge-fs/spaces/{control_space_id}/quality/replay-runs/{run_id}' } diff --git a/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts b/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts index a3d9c5b3e18..8c421213917 100644 --- a/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts +++ b/packages/contracts/generated/api/console/knowledge-fs/zod.gen.ts @@ -2872,16 +2872,6 @@ export const zKnowledgeFsTraceListResponse = z.object({ next_cursor: z.string().nullish(), }) -/** - * KnowledgeFSQualityReplayEvidenceDiff - */ -export const zKnowledgeFsQualityReplayEvidenceDiff = z.object({ - expected_count: z.int().gte(0), - matched_count: z.int().gte(0), - missing_count: z.int().gte(0), - retrieved_count: z.int().gte(0), -}) - /** * KnowledgeFSQualityReplayMetrics */ @@ -2898,6 +2888,30 @@ export const zKnowledgeFsQualityReplayMetrics = z.object({ total_ms: z.number().gte(0).nullish(), }) +/** + * KnowledgeFSQualityReplayEvidenceItem + */ +export const zKnowledgeFsQualityReplayEvidenceItem = z.object({ + available: z.boolean(), + document_name: z.string().nullish(), + matched: z.boolean(), + ordinal: z.int().gte(1), + page_number: z.int().gte(1).nullish(), + section_path: z.array(z.string()).optional(), + text: z.string().nullish(), +}) + +/** + * KnowledgeFSQualityReplayEvidenceDiff + */ +export const zKnowledgeFsQualityReplayEvidenceDiff = z.object({ + evidence_items: z.array(zKnowledgeFsQualityReplayEvidenceItem).nullish(), + expected_count: z.int().gte(0), + matched_count: z.int().gte(0), + missing_count: z.int().gte(0), + retrieved_count: z.int().gte(0), +}) + /** * KnowledgeFSQualityReplayItemResult */ @@ -3912,6 +3926,10 @@ export const zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath = run_id: z.string(), }) +export const zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery = z.object({ + evidence_item_id: z.uuid().optional(), +}) + /** * KnowledgeFS quality replay run */ diff --git a/web/features/new-rag/__tests__/quality-evaluation-panel.spec.tsx b/web/features/new-rag/__tests__/quality-evaluation-panel.spec.tsx index d4d747cb9e0..e65cad1a24b 100644 --- a/web/features/new-rag/__tests__/quality-evaluation-panel.spec.tsx +++ b/web/features/new-rag/__tests__/quality-evaluation-panel.spec.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { screen, waitFor } from '@testing-library/react' +import { screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { render } from '@/test/console/render' import { QualityEvaluationPanel } from '../quality/quality-evaluation-panel' @@ -94,6 +94,41 @@ const completedRun = { updated_at: '2026-08-19T12:00:02.000Z', } as const +const completedRunWithEvidence = { + ...completedRun, + items: [ + { + ...completedRun.items[0], + result: { + ...completedRun.items[0].result, + evidence_diff: { + ...completedRun.items[0].result.evidence_diff, + evidence_items: [ + { + available: true, + document_name: 'Workspace permissions.pdf', + matched: true, + ordinal: 1, + page_number: 2, + section_path: ['Permissions', 'Roles'], + text: 'Workspace owners can change member permissions.', + }, + { + available: true, + document_name: 'Workspace permissions.pdf', + matched: false, + ordinal: 2, + page_number: 3, + section_path: ['Permissions', 'Limitations'], + text: 'Editors cannot promote themselves to owner.', + }, + ], + }, + }, + }, + ], +} as const + function renderPanel() { const queryClient = new QueryClient({ defaultOptions: { @@ -187,4 +222,56 @@ describe('QualityEvaluationPanel', () => { params: { control_space_id: 'space-1', run_id: 'run-1' }, }) }) + + it('opens evidence hit details and identifies matched and missing passages', async () => { + const user = userEvent.setup() + serviceMock.listReplays.mockResolvedValue({ data: [completedRun], next_cursor: null }) + serviceMock.getReplay.mockImplementation(async (input) => + 'query' in input && input.query.evidence_item_id ? completedRunWithEvidence : completedRun, + ) + renderPanel() + + await user.click( + await screen.findByRole('button', { + name: 'dataset.newKnowledge.qualityPage.evaluation.viewReport', + }), + ) + await screen.findByText('Who can change workspace permissions?') + await user.click( + screen.getByRole('button', { + name: /^dataset\.newKnowledge\.qualityPage\.evaluation\.openEvidenceDetails/, + }), + ) + + expect( + await screen.findByRole('dialog', { + name: 'dataset.newKnowledge.qualityPage.evaluation.evidenceDetailsTitle', + }), + ).toBeVisible() + const matchedSection = screen.getByRole('region', { + name: 'dataset.newKnowledge.qualityPage.evaluation.passed', + }) + const missingSection = screen.getByRole('region', { + name: 'dataset.newKnowledge.qualityPage.evaluation.missed', + }) + expect( + within(matchedSection).getByText('Workspace owners can change member permissions.'), + ).toBeVisible() + expect( + within(missingSection).getByText('Editors cannot promote themselves to owner.'), + ).toBeVisible() + expect(serviceMock.getReplay).toHaveBeenCalledWith({ + params: { control_space_id: 'space-1', run_id: 'run-1' }, + query: { evidence_item_id: 'item-1' }, + }) + + await user.click( + screen.getByRole('button', { name: 'dataset.newKnowledge.qualityPage.closeDialog' }), + ) + expect( + screen.queryByRole('dialog', { + name: 'dataset.newKnowledge.qualityPage.evaluation.evidenceDetailsTitle', + }), + ).not.toBeInTheDocument() + }) }) diff --git a/web/features/new-rag/quality/quality-evaluation-panel.tsx b/web/features/new-rag/quality/quality-evaluation-panel.tsx index ae118f687d7..c7fbae606b9 100644 --- a/web/features/new-rag/quality/quality-evaluation-panel.tsx +++ b/web/features/new-rag/quality/quality-evaluation-panel.tsx @@ -1,6 +1,9 @@ 'use client' -import type { KnowledgeFsQualityReplayResponse } from '@dify/contracts/api/console/knowledge-fs/types.gen' +import type { + KnowledgeFsQualityReplayEvidenceItem, + KnowledgeFsQualityReplayResponse, +} from '@dify/contracts/api/console/knowledge-fs/types.gen' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { @@ -65,6 +68,7 @@ function EvaluationReport({ runId: string }) { const { t } = useTranslation('dataset') + const [selectedEvidenceItemId, setSelectedEvidenceItemId] = useState() const detailOptions = consoleQuery.knowledgeFs.spaces.byControlSpaceId.quality.replayRuns.byRunId.get.queryOptions({ input: { params: { control_space_id: knowledgeSpaceId, run_id: runId } }, @@ -74,6 +78,17 @@ function EvaluationReport({ refetchInterval: (query) => query.state.data && activeReplayStates.has(query.state.data.state) ? 1500 : false, }) + const evidenceDetailOptions = + consoleQuery.knowledgeFs.spaces.byControlSpaceId.quality.replayRuns.byRunId.get.queryOptions({ + input: { + params: { control_space_id: knowledgeSpaceId, run_id: runId }, + query: selectedEvidenceItemId ? { evidence_item_id: selectedEvidenceItemId } : {}, + }, + }) + const evidenceDetailQuery = useQuery({ + ...evidenceDetailOptions, + enabled: Boolean(selectedEvidenceItemId), + }) if (detailQuery.isLoading) return ( @@ -102,6 +117,23 @@ function EvaluationReport({ const run = detailQuery.data const progress = run.summary.total === 0 ? 0 : run.summary.completed / run.summary.total + const selectedReportItem = run.items.find((item) => item.id === selectedEvidenceItemId) + const selectedEvidenceItem = evidenceDetailQuery.data?.items.find( + (item) => item.id === selectedEvidenceItemId, + ) + const evidenceItems = selectedEvidenceItem?.result?.evidence_diff.evidence_items ?? [] + const evidenceGroups = [ + { + items: evidenceItems.filter((item) => item.matched), + matched: true, + title: t(($) => $['newKnowledge.qualityPage.evaluation.passed']), + }, + { + items: evidenceItems.filter((item) => !item.matched), + matched: false, + title: t(($) => $['newKnowledge.qualityPage.evaluation.missed']), + }, + ] return (
@@ -200,9 +232,26 @@ function EvaluationReport({ {t(($) => $[`newKnowledge.qualityPage.matchPolicy.${item.match_policy}`])} - - {diff ? `${diff.matched_count}/${diff.expected_count}` : '—'} - + {diff ? ( + + ) : ( + + )} {formatDuration(item.result?.metrics.total_ms)} @@ -236,10 +285,108 @@ function EvaluationReport({
+ + { + if (!open) setSelectedEvidenceItemId(undefined) + }} + > + +
+ + {t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsTitle'])} + + + {t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsDescription'])} + + {selectedReportItem && ( +

+ {selectedReportItem.question} +

+ )} + $['newKnowledge.qualityPage.closeDialog'])} /> +
+ +
+ {evidenceDetailQuery.isLoading ? ( +
+ +
+ ) : evidenceDetailQuery.isError ? ( +
+

+ {t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError'])} +

+ +
+ ) : ( +
+ {evidenceGroups.map((group) => ( +
+
+

{group.title}

+ + {group.items.length} + +
+ {group.items.length === 0 ? ( +

+ {t(($) => $['newKnowledge.qualityPage.evaluation.noEvidenceInGroup'])} +

+ ) : ( +
+ {group.items.map((evidence) => ( + + ))} +
+ )} +
+ ))} +
+ )} +
+
+
) } +function EvidenceDetailCard({ evidence }: { evidence: KnowledgeFsQualityReplayEvidenceItem }) { + const { t } = useTranslation('dataset') + const source = [ + evidence.document_name, + evidence.section_path?.join(' / '), + evidence.page_number + ? t(($) => $['newKnowledge.qualityPage.evaluation.evidencePage'], { + page: evidence.page_number, + }) + : undefined, + ].filter(Boolean) + + return ( +
+ {source.length > 0 && ( +

{source.join(' · ')}

+ )} +

+ {evidence.available && evidence.text + ? evidence.text + : t(($) => $['newKnowledge.qualityPage.evaluation.evidenceUnavailable'])} +

+
+ ) +} + export function QualityEvaluationPanel({ knowledgeSpaceId }: { knowledgeSpaceId: string }) { const { t } = useTranslation('dataset') const queryClient = useQueryClient() diff --git a/web/i18n/ar-TN/dataset.json b/web/i18n/ar-TN/dataset.json index fef258f2d45..a71d7409513 100644 --- a/web/i18n/ar-TN/dataset.json +++ b/web/i18n/ar-TN/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "أسئلة مرجعية لاختبار الاسترجاع مجددًا، وحالات تحتاج إلى تحسين واردة من بيئة الإنتاج.", "newKnowledge.qualityPage.edit": "تحرير", "newKnowledge.qualityPage.editTitle": "تعديل السؤال المرجعي", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "قارن كل مقطع دليل متوقع بالمقاطع التي استرجعها هذا التقييم.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "تعذر تحميل تفاصيل الأدلة. حاول مرة أخرى.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "تفاصيل مطابقة الأدلة", + "newKnowledge.qualityPage.evaluation.evidencePage": "الصفحة {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "لم يعد مقطع الدليل التاريخي هذا متاحًا، أو لم يعد لديك إذن لعرضه.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "لا توجد مقاطع أدلة في هذه المجموعة.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "عرض تفاصيل الأدلة للسؤال «{{question}}»: تمت مطابقة {{matched}} من {{expected}}", "newKnowledge.qualityPage.evidence": "الدليل", "newKnowledge.qualityPage.evidencePlaceholder": "الصق المقطع المتوقع أو صِف الدليل المطلوب العثور عليه", "newKnowledge.qualityPage.evidenceSelected": "عدد مقاطع الدليل المحددة: {{count}}", diff --git a/web/i18n/de-DE/dataset.json b/web/i18n/de-DE/dataset.json index 193bc3c7c47..a9fc17ee74b 100644 --- a/web/i18n/de-DE/dataset.json +++ b/web/i18n/de-DE/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Referenzfragen zum erneuten Testen des Abrufs sowie Problemfälle aus dem Produktivbetrieb.", "newKnowledge.qualityPage.edit": "Bearbeiten", "newKnowledge.qualityPage.editTitle": "Referenzfrage bearbeiten", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Vergleichen Sie jeden erwarteten Belegabschnitt mit den von dieser Auswertung abgerufenen Abschnitten.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Die Belegdetails konnten nicht geladen werden. Versuchen Sie es erneut.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Details zu den Belegtreffern", + "newKnowledge.qualityPage.evaluation.evidencePage": "Seite {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Dieser historische Belegabschnitt ist nicht mehr verfügbar oder Sie sind nicht mehr berechtigt, ihn anzuzeigen.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Diese Gruppe enthält keine Belegabschnitte.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Belegdetails für „{{question}}“ anzeigen: {{matched}} von {{expected}} Treffern", "newKnowledge.qualityPage.evidence": "Beleg", "newKnowledge.qualityPage.evidencePlaceholder": "Erwarteten Textabschnitt einfügen oder den gesuchten Beleg beschreiben", "newKnowledge.qualityPage.evidenceSelected": "{{count}} Belegabschnitte ausgewählt", diff --git a/web/i18n/en-US/dataset.json b/web/i18n/en-US/dataset.json index 0e74e9d7b7e..bd4aa09e39c 100644 --- a/web/i18n/en-US/dataset.json +++ b/web/i18n/en-US/dataset.json @@ -503,7 +503,12 @@ "newKnowledge.qualityPage.evaluation.duration": "Duration", "newKnowledge.qualityPage.evaluation.emptyDescription": "Run an evaluation to see the overall evidence hit rate and the result for every golden question.", "newKnowledge.qualityPage.evaluation.emptyTitle": "No evaluation runs yet", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compare every expected evidence passage with the passages returned by this evaluation.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Evidence details could not be loaded. Try again.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Evidence hit details", "newKnowledge.qualityPage.evaluation.evidenceHit": "Evidence hit", + "newKnowledge.qualityPage.evaluation.evidencePage": "Page {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "This historical evidence passage is no longer available or you no longer have permission to view it.", "newKnowledge.qualityPage.evaluation.executionFailed": "The run could not be completed. Check the knowledge retrieval configuration and run it again.", "newKnowledge.qualityPage.evaluation.hitRate": "Evidence hit rate", "newKnowledge.qualityPage.evaluation.loadError": "Evaluation runs could not be loaded. Try again.", @@ -513,6 +518,8 @@ "newKnowledge.qualityPage.evaluation.mode.fast": "Fast", "newKnowledge.qualityPage.evaluation.mode.research": "Research", "newKnowledge.qualityPage.evaluation.modeLabel": "Retrieval mode", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "No evidence passages in this group.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "View evidence details for {{question}}: {{matched}} of {{expected}} hit", "newKnowledge.qualityPage.evaluation.passed": "Hit", "newKnowledge.qualityPage.evaluation.policy": "Match policy", "newKnowledge.qualityPage.evaluation.profileRevision": "Retrieval profile", diff --git a/web/i18n/es-ES/dataset.json b/web/i18n/es-ES/dataset.json index 9839c082108..3fa22d2e0df 100644 --- a/web/i18n/es-ES/dataset.json +++ b/web/i18n/es-ES/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Preguntas de referencia para volver a probar la recuperación y casos problemáticos notificados desde el entorno de producción.", "newKnowledge.qualityPage.edit": "Editar", "newKnowledge.qualityPage.editTitle": "Editar pregunta de referencia", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compara cada fragmento de evidencia esperado con los fragmentos recuperados por esta evaluación.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "No se pudieron cargar los detalles de la evidencia. Inténtalo de nuevo.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalles de coincidencia de evidencia", + "newKnowledge.qualityPage.evaluation.evidencePage": "Página {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Este fragmento de evidencia histórico ya no está disponible o ya no tienes permiso para verlo.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "No hay fragmentos de evidencia en este grupo.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Ver los detalles de evidencia de «{{question}}»: {{matched}} de {{expected}} coincidencias", "newKnowledge.qualityPage.evidence": "Evidencia", "newKnowledge.qualityPage.evidencePlaceholder": "Pega el fragmento esperado o describe la evidencia que buscas", "newKnowledge.qualityPage.evidenceSelected": "{{count}} fragmentos de evidencia seleccionados", diff --git a/web/i18n/fa-IR/dataset.json b/web/i18n/fa-IR/dataset.json index da98b1f2477..d09254a880c 100644 --- a/web/i18n/fa-IR/dataset.json +++ b/web/i18n/fa-IR/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "پرسش‌های مرجع برای آزمایش دوبارهٔ بازیابی و موارد نیازمند بهبود گزارش‌شده از محیط عملیاتی.", "newKnowledge.qualityPage.edit": "ویرایش", "newKnowledge.qualityPage.editTitle": "ویرایش سوال مرجع", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "هر بخش شاهد مورد انتظار را با بخش‌های بازیابی‌شده در این ارزیابی مقایسه کنید.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "جزئیات شاهد بارگیری نشد. دوباره تلاش کنید.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "جزئیات تطبیق شاهد", + "newKnowledge.qualityPage.evaluation.evidencePage": "صفحه {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "این بخش شاهد تاریخی دیگر در دسترس نیست یا دیگر مجوز مشاهدهٔ آن را ندارید.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "در این گروه هیچ بخش شاهدی وجود ندارد.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "مشاهدهٔ جزئیات شاهد برای «{{question}}»: {{matched}} مورد از {{expected}} مورد تطبیق یافت", "newKnowledge.qualityPage.evidence": "شاهد", "newKnowledge.qualityPage.evidencePlaceholder": "بخش مورد انتظار را جای‌گذاری کنید یا شاهد مورد نظر را توضیح دهید", "newKnowledge.qualityPage.evidenceSelected": "{{count}} بخش شاهد انتخاب شد", diff --git a/web/i18n/fr-FR/dataset.json b/web/i18n/fr-FR/dataset.json index 93fd72222dd..ca0247b450b 100644 --- a/web/i18n/fr-FR/dataset.json +++ b/web/i18n/fr-FR/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Questions de référence pour tester à nouveau la recherche et cas problématiques signalés en production.", "newKnowledge.qualityPage.edit": "Modifier", "newKnowledge.qualityPage.editTitle": "Modifier la question de référence", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Comparez chaque extrait attendu aux extraits retrouvés lors de cette évaluation.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Impossible de charger les détails des extraits. Réessayez.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Détails des extraits retrouvés", + "newKnowledge.qualityPage.evaluation.evidencePage": "Page {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Cet extrait historique n’est plus disponible ou vous n’êtes plus autorisé à le consulter.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Aucun extrait dans ce groupe.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Afficher les détails des extraits pour « {{question}} » : {{matched}} sur {{expected}} retrouvés", "newKnowledge.qualityPage.evidence": "Extrait de référence", "newKnowledge.qualityPage.evidencePlaceholder": "Collez l’extrait attendu ou décrivez l’extrait à rechercher", "newKnowledge.qualityPage.evidenceSelected": "{{count}} extraits de référence sélectionnés", diff --git a/web/i18n/hi-IN/dataset.json b/web/i18n/hi-IN/dataset.json index 94a7b9ebb28..236c7553a7a 100644 --- a/web/i18n/hi-IN/dataset.json +++ b/web/i18n/hi-IN/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "रिट्रीवल को दोबारा जाँचने के लिए संदर्भ प्रश्न और प्रोडक्शन परिवेश से रिपोर्ट किए गए सुधार योग्य मामले।", "newKnowledge.qualityPage.edit": "संपादित करें", "newKnowledge.qualityPage.editTitle": "संदर्भ प्रश्न संपादित करें", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "हर अपेक्षित साक्ष्य अंश की तुलना इस मूल्यांकन में मिले अंशों से करें।", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "साक्ष्य विवरण लोड नहीं हो सका। फिर से कोशिश करें।", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "साक्ष्य मिलान का विवरण", + "newKnowledge.qualityPage.evaluation.evidencePage": "पृष्ठ {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "यह पुराना साक्ष्य अंश अब उपलब्ध नहीं है या अब आपको इसे देखने की अनुमति नहीं है।", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "इस समूह में कोई साक्ष्य अंश नहीं है।", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "“{{question}}” के साक्ष्य विवरण देखें: {{expected}} में से {{matched}} मिले", "newKnowledge.qualityPage.evidence": "साक्ष्य", "newKnowledge.qualityPage.evidencePlaceholder": "अपेक्षित अंश चिपकाएँ या खोजे जाने वाले साक्ष्य का वर्णन करें", "newKnowledge.qualityPage.evidenceSelected": "{{count}} साक्ष्य अंश चुने गए", diff --git a/web/i18n/id-ID/dataset.json b/web/i18n/id-ID/dataset.json index 68f916ebd41..3a795ca6ea7 100644 --- a/web/i18n/id-ID/dataset.json +++ b/web/i18n/id-ID/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Pertanyaan referensi untuk menguji kembali pengambilan, serta kasus bermasalah yang dilaporkan dari lingkungan produksi.", "newKnowledge.qualityPage.edit": "Sunting", "newKnowledge.qualityPage.editTitle": "Edit pertanyaan referensi", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Bandingkan setiap bagian bukti yang diharapkan dengan bagian yang ditemukan oleh evaluasi ini.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Detail bukti tidak dapat dimuat. Coba lagi.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detail kecocokan bukti", + "newKnowledge.qualityPage.evaluation.evidencePage": "Halaman {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Bagian bukti historis ini sudah tidak tersedia atau Anda tidak lagi memiliki izin untuk melihatnya.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Tidak ada bagian bukti dalam grup ini.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Lihat detail bukti untuk “{{question}}”: {{matched}} dari {{expected}} cocok", "newKnowledge.qualityPage.evidence": "Bukti", "newKnowledge.qualityPage.evidencePlaceholder": "Tempel bagian yang diharapkan atau jelaskan bukti yang ingin dicari", "newKnowledge.qualityPage.evidenceSelected": "{{count}} bagian bukti dipilih", diff --git a/web/i18n/it-IT/dataset.json b/web/i18n/it-IT/dataset.json index a4a9ad4eb8e..726a9a976d2 100644 --- a/web/i18n/it-IT/dataset.json +++ b/web/i18n/it-IT/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Domande di riferimento per verificare nuovamente il recupero e casi problematici segnalati nell’ambiente di produzione.", "newKnowledge.qualityPage.edit": "Modifica", "newKnowledge.qualityPage.editTitle": "Modifica la domanda di riferimento", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Confronta ogni passaggio di riscontro previsto con i passaggi recuperati da questa valutazione.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Impossibile caricare i dettagli dei riscontri. Riprova.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Dettagli dei riscontri trovati", + "newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Questo passaggio storico non è più disponibile oppure non disponi più dell’autorizzazione per visualizzarlo.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Nessun passaggio di riscontro in questo gruppo.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Visualizza i dettagli dei riscontri per “{{question}}”: {{matched}} su {{expected}} trovati", "newKnowledge.qualityPage.evidence": "Riscontro", "newKnowledge.qualityPage.evidencePlaceholder": "Incolla il passaggio previsto o descrivi il riscontro da trovare", "newKnowledge.qualityPage.evidenceSelected": "{{count}} passaggi di riscontro selezionati", diff --git a/web/i18n/ja-JP/dataset.json b/web/i18n/ja-JP/dataset.json index 5b0126547e0..75dfb2cb817 100644 --- a/web/i18n/ja-JP/dataset.json +++ b/web/i18n/ja-JP/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "検索を再テストするための参考質問と、本番環境から報告された問題ケースを管理します。", "newKnowledge.qualityPage.edit": "編集", "newKnowledge.qualityPage.editTitle": "参考質問を編集", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "期待される各根拠段落と、この評価で取得された段落を比較します。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "根拠の詳細を読み込めませんでした。もう一度お試しください。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "根拠のヒット詳細", + "newKnowledge.qualityPage.evaluation.evidencePage": "{{page}} ページ", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "この過去の根拠段落は利用できないか、現在は表示する権限がありません。", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "このグループに根拠段落はありません。", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "「{{question}}」の根拠詳細を表示:{{expected}} 件中 {{matched}} 件がヒット", "newKnowledge.qualityPage.evidence": "根拠", "newKnowledge.qualityPage.evidencePlaceholder": "期待する文章を貼り付けるか、検索する根拠を説明してください", "newKnowledge.qualityPage.evidenceSelected": "{{count}} 件の根拠となる文章を選択済み", diff --git a/web/i18n/ko-KR/dataset.json b/web/i18n/ko-KR/dataset.json index f2de82f8178..db88c3f2577 100644 --- a/web/i18n/ko-KR/dataset.json +++ b/web/i18n/ko-KR/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "검색을 다시 테스트하기 위한 참고 질문과 운영 환경에서 보고된 문제 사례를 관리합니다.", "newKnowledge.qualityPage.edit": "편집", "newKnowledge.qualityPage.editTitle": "참고 질문 수정", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "각 예상 근거 문단과 이번 평가에서 검색된 문단을 비교합니다.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "근거 세부 정보를 불러오지 못했습니다. 다시 시도해 주세요.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "근거 적중 세부 정보", + "newKnowledge.qualityPage.evaluation.evidencePage": "{{page}}페이지", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "이 과거 근거 문단은 더 이상 존재하지 않거나 현재 볼 수 있는 권한이 없습니다.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "이 그룹에는 근거 문단이 없습니다.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "‘{{question}}’의 근거 세부 정보 보기: {{expected}}개 중 {{matched}}개 적중", "newKnowledge.qualityPage.evidence": "근거", "newKnowledge.qualityPage.evidencePlaceholder": "예상 문단을 붙여 넣거나 찾을 근거를 설명하세요", "newKnowledge.qualityPage.evidenceSelected": "근거 문단 {{count}}개 선택됨", diff --git a/web/i18n/lo-LA/dataset.json b/web/i18n/lo-LA/dataset.json index 038bcd4e75e..2914ebd6224 100644 --- a/web/i18n/lo-LA/dataset.json +++ b/web/i18n/lo-LA/dataset.json @@ -367,6 +367,13 @@ "newKnowledge.providerNotConfiguredDescription": "ເພີ່ມລະຫັດ API ຫຼື URL ຈຸດສິ້ນສຸດເພື່ອລວບລວມຂໍ້ມູນເວັບໄຊທ໌ດ້ວຍ {{provider}}.", "newKnowledge.providerUnavailable": "ຜູ້ໃຫ້ບໍລິການນີ້ບໍ່ສາມາດໃຊ້ໄດ້.", "newKnowledge.quality": "ຄຸນະພາບ", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "ປຽບທຽບທຸກວັກຫຼັກຖານທີ່ຄາດໄວ້ກັບວັກທີ່ການປະເມີນນີ້ຄົ້ນພົບ.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "ບໍ່ສາມາດໂຫຼດລາຍລະອຽດຫຼັກຖານໄດ້. ລອງໃໝ່.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "ລາຍລະອຽດການພົບຫຼັກຖານ", + "newKnowledge.qualityPage.evaluation.evidencePage": "ໜ້າ {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "ວັກຫຼັກຖານເກົ່ານີ້ບໍ່ມີແລ້ວ ຫຼື ທ່ານບໍ່ມີສິດເບິ່ງອີກຕໍ່ໄປ.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "ບໍ່ມີວັກຫຼັກຖານໃນກຸ່ມນີ້.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "ເບິ່ງລາຍລະອຽດຫຼັກຖານສຳລັບ “{{question}}”: ພົບ {{matched}} ຈາກ {{expected}}", "newKnowledge.qualityPage.ignore": "ບໍ່ສົນໃຈ", "newKnowledge.qualityPage.reasonValues.lowScore": "ຄະແນນຕໍ່າ", "newKnowledge.reCrawl": "ກວາດຄືນ", diff --git a/web/i18n/nl-NL/dataset.json b/web/i18n/nl-NL/dataset.json index 40d59b7e08a..16698b8bc0c 100644 --- a/web/i18n/nl-NL/dataset.json +++ b/web/i18n/nl-NL/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Referentievragen om het ophalen opnieuw te testen en probleemgevallen die vanuit de productieomgeving zijn gemeld.", "newKnowledge.qualityPage.edit": "Bewerken", "newKnowledge.qualityPage.editTitle": "Referentievraag bewerken", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Vergelijk elke verwachte bewijs passage met de passages die door deze evaluatie zijn opgehaald.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "De bewijsdetails konden niet worden geladen. Probeer het opnieuw.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Details van bewijsresultaten", + "newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Deze historische bewijs passage is niet meer beschikbaar of u hebt geen toestemming meer om deze te bekijken.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Deze groep bevat geen bewijs passages.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Bewijsdetails voor ‘{{question}}’ bekijken: {{matched}} van {{expected}} gevonden", "newKnowledge.qualityPage.evidence": "Bewijs", "newKnowledge.qualityPage.evidencePlaceholder": "Plak de verwachte passage of beschrijf het te vinden bewijs", "newKnowledge.qualityPage.evidenceSelected": "{{count}} bewijsfragmenten geselecteerd", diff --git a/web/i18n/pl-PL/dataset.json b/web/i18n/pl-PL/dataset.json index ddf34bf2983..69b71e20a7e 100644 --- a/web/i18n/pl-PL/dataset.json +++ b/web/i18n/pl-PL/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Pytania referencyjne do ponownego testowania wyszukiwania oraz przypadki do poprawy zgłoszone w środowisku produkcyjnym.", "newKnowledge.qualityPage.edit": "Edytuj", "newKnowledge.qualityPage.editTitle": "Edytuj pytanie referencyjne", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Porównaj każdy oczekiwany fragment źródłowy z fragmentami pobranymi w tej ocenie.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Nie udało się wczytać szczegółów fragmentów. Spróbuj ponownie.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Szczegóły trafień fragmentów", + "newKnowledge.qualityPage.evaluation.evidencePage": "Strona {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Ten historyczny fragment nie jest już dostępny albo nie masz już uprawnień do jego wyświetlenia.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "W tej grupie nie ma fragmentów źródłowych.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Wyświetl szczegóły fragmentów dla „{{question}}”: trafiono {{matched}} z {{expected}}", "newKnowledge.qualityPage.evidence": "Fragment źródłowy", "newKnowledge.qualityPage.evidencePlaceholder": "Wklej oczekiwany fragment lub opisz fragment źródłowy do znalezienia", "newKnowledge.qualityPage.evidenceSelected": "Liczba wybranych fragmentów źródłowych: {{count}}", diff --git a/web/i18n/pt-BR/dataset.json b/web/i18n/pt-BR/dataset.json index 07c1a855499..c7f22ee2e82 100644 --- a/web/i18n/pt-BR/dataset.json +++ b/web/i18n/pt-BR/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Perguntas de referência para testar novamente a recuperação e casos problemáticos relatados no ambiente de produção.", "newKnowledge.qualityPage.edit": "Editar", "newKnowledge.qualityPage.editTitle": "Editar pergunta de referência", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compare cada trecho de evidência esperado com os trechos recuperados nesta avaliação.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Não foi possível carregar os detalhes das evidências. Tente novamente.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalhes das evidências encontradas", + "newKnowledge.qualityPage.evaluation.evidencePage": "Página {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Este trecho de evidência histórico não está mais disponível ou você não tem mais permissão para visualizá-lo.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Não há trechos de evidência neste grupo.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Ver detalhes das evidências de “{{question}}”: {{matched}} de {{expected}} encontradas", "newKnowledge.qualityPage.evidence": "Evidência", "newKnowledge.qualityPage.evidencePlaceholder": "Cole o trecho esperado ou descreva a evidência que deseja encontrar", "newKnowledge.qualityPage.evidenceSelected": "{{count}} trechos de evidência selecionados", diff --git a/web/i18n/ro-RO/dataset.json b/web/i18n/ro-RO/dataset.json index b0849821aa4..7bc7198775f 100644 --- a/web/i18n/ro-RO/dataset.json +++ b/web/i18n/ro-RO/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Întrebări de referință pentru a retesta regăsirea și cazuri problematice raportate din mediul de producție.", "newKnowledge.qualityPage.edit": "Editează", "newKnowledge.qualityPage.editTitle": "Editați întrebarea de referință", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compară fiecare fragment de dovadă așteptat cu fragmentele găsite de această evaluare.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Detaliile dovezilor nu au putut fi încărcate. Încearcă din nou.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalii despre dovezile găsite", + "newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Acest fragment istoric nu mai este disponibil sau nu mai ai permisiunea de a-l vedea.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Nu există fragmente de dovadă în acest grup.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Vezi detaliile dovezilor pentru „{{question}}”: {{matched}} din {{expected}} găsite", "newKnowledge.qualityPage.evidence": "Dovadă", "newKnowledge.qualityPage.evidencePlaceholder": "Lipește fragmentul așteptat sau descrie dovada de găsit", "newKnowledge.qualityPage.evidenceSelected": "Au fost selectate {{count}} fragmente de dovadă", diff --git a/web/i18n/ru-RU/dataset.json b/web/i18n/ru-RU/dataset.json index d87c63d5e57..db4a0371cee 100644 --- a/web/i18n/ru-RU/dataset.json +++ b/web/i18n/ru-RU/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Эталонные вопросы для повторной проверки поиска и проблемные случаи из рабочей среды.", "newKnowledge.qualityPage.edit": "Править", "newKnowledge.qualityPage.editTitle": "Редактировать эталонный вопрос", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Сравните каждый ожидаемый подтверждающий фрагмент с фрагментами, найденными при этой оценке.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Не удалось загрузить сведения о фрагментах. Повторите попытку.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Сведения о найденных фрагментах", + "newKnowledge.qualityPage.evaluation.evidencePage": "Страница {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Этот исторический фрагмент больше недоступен или у вас больше нет разрешения на его просмотр.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "В этой группе нет подтверждающих фрагментов.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Показать сведения о фрагментах для «{{question}}»: найдено {{matched}} из {{expected}}", "newKnowledge.qualityPage.evidence": "Подтверждающий фрагмент", "newKnowledge.qualityPage.evidencePlaceholder": "Вставьте ожидаемый фрагмент или опишите, какой подтверждающий фрагмент нужно найти", "newKnowledge.qualityPage.evidenceSelected": "Выбрано подтверждающих фрагментов: {{count}}", diff --git a/web/i18n/sl-SI/dataset.json b/web/i18n/sl-SI/dataset.json index bf1a9567525..515c9f883d8 100644 --- a/web/i18n/sl-SI/dataset.json +++ b/web/i18n/sl-SI/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Referenčna vprašanja za ponovno preverjanje pridobivanja in problematični primeri, prijavljeni v produkcijskem okolju.", "newKnowledge.qualityPage.edit": "Uredi", "newKnowledge.qualityPage.editTitle": "Uredi referenčno vprašanje", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Primerjajte vsak pričakovani referenčni odlomek z odlomki, ki jih je našlo to vrednotenje.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Podrobnosti odlomkov ni bilo mogoče naložiti. Poskusite znova.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Podrobnosti zadetkov odlomkov", + "newKnowledge.qualityPage.evaluation.evidencePage": "Stran {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Ta zgodovinski odlomek ni več na voljo ali pa nimate več dovoljenja za ogled.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "V tej skupini ni referenčnih odlomkov.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Prikaži podrobnosti odlomkov za »{{question}}«: {{matched}} od {{expected}} zadetkov", "newKnowledge.qualityPage.evidence": "Referenčni odlomek", "newKnowledge.qualityPage.evidencePlaceholder": "Prilepite pričakovani odlomek ali opišite iskani referenčni odlomek", "newKnowledge.qualityPage.evidenceSelected": "Izbranih referenčnih odlomkov: {{count}}", diff --git a/web/i18n/th-TH/dataset.json b/web/i18n/th-TH/dataset.json index ef1417ac6b5..77c26100b76 100644 --- a/web/i18n/th-TH/dataset.json +++ b/web/i18n/th-TH/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "คำถามอ้างอิงสำหรับทดสอบการค้นคืนอีกครั้ง และกรณีที่ต้องปรับปรุงซึ่งรายงานจากระบบที่ใช้งานจริง", "newKnowledge.qualityPage.edit": "แก้ไข", "newKnowledge.qualityPage.editTitle": "แก้ไขคำถามอ้างอิง", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "เปรียบเทียบข้อความหลักฐานที่คาดหวังแต่ละรายการกับข้อความที่การประเมินนี้ค้นคืนมา", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "ไม่สามารถโหลดรายละเอียดหลักฐานได้ โปรดลองอีกครั้ง", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "รายละเอียดการพบหลักฐาน", + "newKnowledge.qualityPage.evaluation.evidencePage": "หน้า {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "ข้อความหลักฐานย้อนหลังนี้ไม่มีอยู่แล้ว หรือคุณไม่มีสิทธิ์ดูอีกต่อไป", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "ไม่มีข้อความหลักฐานในกลุ่มนี้", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "ดูรายละเอียดหลักฐานสำหรับ “{{question}}”: พบ {{matched}} จาก {{expected}}", "newKnowledge.qualityPage.evidence": "หลักฐาน", "newKnowledge.qualityPage.evidencePlaceholder": "วางข้อความที่คาดหวังหรืออธิบายหลักฐานที่ต้องการค้นหา", "newKnowledge.qualityPage.evidenceSelected": "เลือกข้อความหลักฐานแล้ว {{count}} รายการ", diff --git a/web/i18n/tr-TR/dataset.json b/web/i18n/tr-TR/dataset.json index 0fbaf8a922a..e6c0814fa41 100644 --- a/web/i18n/tr-TR/dataset.json +++ b/web/i18n/tr-TR/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Getirme işlemini yeniden test eden referans soruları ve canlı ortamdan bildirilen sorunlu durumlar.", "newKnowledge.qualityPage.edit": "Düzenle", "newKnowledge.qualityPage.editTitle": "Referans sorusunu düzenle", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Beklenen her kanıt bölümünü bu değerlendirmede getirilen bölümlerle karşılaştırın.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Kanıt ayrıntıları yüklenemedi. Tekrar deneyin.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Kanıt eşleşme ayrıntıları", + "newKnowledge.qualityPage.evaluation.evidencePage": "Sayfa {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Bu geçmiş kanıt bölümü artık kullanılamıyor veya artık görüntüleme izniniz yok.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Bu grupta kanıt bölümü yok.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "“{{question}}” için kanıt ayrıntılarını görüntüle: {{expected}} kanıttan {{matched}} tanesi eşleşti", "newKnowledge.qualityPage.evidence": "Kanıt", "newKnowledge.qualityPage.evidencePlaceholder": "Beklenen bölümü yapıştırın veya bulunacak kanıtı açıklayın", "newKnowledge.qualityPage.evidenceSelected": "{{count}} kanıt bölümü seçildi", diff --git a/web/i18n/uk-UA/dataset.json b/web/i18n/uk-UA/dataset.json index 785dee3911b..28b7793a3b9 100644 --- a/web/i18n/uk-UA/dataset.json +++ b/web/i18n/uk-UA/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Еталонні запитання для повторної перевірки пошуку та проблемні випадки з робочого середовища.", "newKnowledge.qualityPage.edit": "Редагувати", "newKnowledge.qualityPage.editTitle": "Редагувати еталонне запитання", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Порівняйте кожен очікуваний підтверджувальний уривок з уривками, знайденими під час цього оцінювання.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Не вдалося завантажити відомості про уривки. Спробуйте ще раз.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Відомості про знайдені уривки", + "newKnowledge.qualityPage.evaluation.evidencePage": "Сторінка {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Цей історичний уривок більше недоступний або ви більше не маєте дозволу на його перегляд.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "У цій групі немає підтверджувальних уривків.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Показати відомості про уривки для «{{question}}»: знайдено {{matched}} з {{expected}}", "newKnowledge.qualityPage.evidence": "Підтверджувальний уривок", "newKnowledge.qualityPage.evidencePlaceholder": "Вставте очікуваний уривок або опишіть, який підтверджувальний уривок потрібно знайти", "newKnowledge.qualityPage.evidenceSelected": "Вибрано підтверджувальних уривків: {{count}}", diff --git a/web/i18n/vi-VN/dataset.json b/web/i18n/vi-VN/dataset.json index 255b3948efa..32ac4dad6e7 100644 --- a/web/i18n/vi-VN/dataset.json +++ b/web/i18n/vi-VN/dataset.json @@ -491,6 +491,13 @@ "newKnowledge.qualityPage.description": "Các câu hỏi tham chiếu được chạy lại qua hệ thống truy xuất; các trường hợp cần cải thiện đến từ báo cáo trong môi trường thực tế.", "newKnowledge.qualityPage.edit": "Chỉnh sửa", "newKnowledge.qualityPage.editTitle": "Chỉnh sửa câu hỏi tham chiếu", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "So sánh từng đoạn bằng chứng mong đợi với các đoạn được truy xuất trong lần đánh giá này.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Không thể tải chi tiết bằng chứng. Hãy thử lại.", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Chi tiết bằng chứng được tìm thấy", + "newKnowledge.qualityPage.evaluation.evidencePage": "Trang {{page}}", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Đoạn bằng chứng lịch sử này không còn tồn tại hoặc bạn không còn quyền xem.", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Không có đoạn bằng chứng nào trong nhóm này.", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Xem chi tiết bằng chứng cho “{{question}}”: tìm thấy {{matched}}/{{expected}}", "newKnowledge.qualityPage.evidence": "Bằng chứng", "newKnowledge.qualityPage.evidencePlaceholder": "Dán đoạn văn dự kiến hoặc mô tả bằng chứng cần tìm", "newKnowledge.qualityPage.evidenceSelected": "Đã chọn {{count}} đoạn bằng chứng", diff --git a/web/i18n/zh-Hans/dataset.json b/web/i18n/zh-Hans/dataset.json index 358dadd9978..439f48c6105 100644 --- a/web/i18n/zh-Hans/dataset.json +++ b/web/i18n/zh-Hans/dataset.json @@ -503,7 +503,12 @@ "newKnowledge.qualityPage.evaluation.duration": "耗时", "newKnowledge.qualityPage.evaluation.emptyDescription": "运行一次评测后,可在这里查看整体命中率和每个问题的证据命中结果。", "newKnowledge.qualityPage.evaluation.emptyTitle": "暂无评测任务", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "逐条对比预期证据段落与本次评测实际检索到的段落。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "无法加载证据明细,请重试。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "证据命中明细", "newKnowledge.qualityPage.evaluation.evidenceHit": "证据命中", + "newKnowledge.qualityPage.evaluation.evidencePage": "第 {{page}} 页", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "该历史证据段落已不存在,或你当前没有权限查看。", "newKnowledge.qualityPage.evaluation.executionFailed": "任务执行失败。请检查知识库检索配置后重新运行。", "newKnowledge.qualityPage.evaluation.hitRate": "证据命中率", "newKnowledge.qualityPage.evaluation.loadError": "无法加载评测任务,请重试。", @@ -513,6 +518,8 @@ "newKnowledge.qualityPage.evaluation.mode.fast": "快速检索", "newKnowledge.qualityPage.evaluation.mode.research": "研究检索", "newKnowledge.qualityPage.evaluation.modeLabel": "检索模式", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "该分组中没有证据段落。", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "查看问题「{{question}}」的证据明细:命中 {{matched}}/{{expected}}", "newKnowledge.qualityPage.evaluation.passed": "已命中", "newKnowledge.qualityPage.evaluation.policy": "匹配策略", "newKnowledge.qualityPage.evaluation.profileRevision": "检索配置版本", diff --git a/web/i18n/zh-Hant/dataset.json b/web/i18n/zh-Hant/dataset.json index 2894495153b..cd9b47ddd43 100644 --- a/web/i18n/zh-Hant/dataset.json +++ b/web/i18n/zh-Hant/dataset.json @@ -494,6 +494,13 @@ "newKnowledge.qualityPage.description": "用於重新測試檢索效果的參考問題,以及正式環境回報的問題案例。", "newKnowledge.qualityPage.edit": "編輯", "newKnowledge.qualityPage.editTitle": "編輯參考問題", + "newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "逐條比較預期依據段落與本次評測實際檢索到的段落。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "無法載入依據明細,請重試。", + "newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "依據命中明細", + "newKnowledge.qualityPage.evaluation.evidencePage": "第 {{page}} 頁", + "newKnowledge.qualityPage.evaluation.evidenceUnavailable": "該歷史依據段落已不存在,或你目前沒有權限檢視。", + "newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "此群組中沒有依據段落。", + "newKnowledge.qualityPage.evaluation.openEvidenceDetails": "查看問題「{{question}}」的依據明細:命中 {{matched}}/{{expected}}", "newKnowledge.qualityPage.evidence": "依據", "newKnowledge.qualityPage.evidencePlaceholder": "貼上預期段落,或描述要尋找的依據", "newKnowledge.qualityPage.evidenceSelected": "已選擇 {{count}} 個依據段落",