mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-19 08:28:06 +02:00
feat(context): add warehouse verification tools (#46)
* feat(context): add warehouse dialect dispatch * feat(context): read warehouse scan catalog * feat(context): add entity details verification tool * feat(context): add ingest SQL verification tool * feat(context): add raw warehouse discovery tool * feat(context): expose warehouse verification tools to ingest * docs(context): add ingest identifier verification protocol * test(context): guard ingest identifier verification prompts * chore(context): verify warehouse verification tools * docs: add warehouse verification tools plan and spec * fix(context): expose target warehouses to Notion ingest * fix(context): update ingest prompts for warehouse verification tools * fix(context): scope raw schema discovery to allowed connections * fix(context): verify warehouse column display targets * docs: add notion warehouse verification gap closure plan * fix(context): include raw discovery connection names * fix(context): expose warehouse targets for LookML and MetricFlow * fix(context): pass connection config to ingest query executors * fix(cli): enable read-only SQL probes for local ingest * docs: add warehouse verification final v1 closure plan * fix(context): align warehouse sql probe prompt shape * docs: add warehouse verification prompt shape closure plan * test(context): catch connectionless sql execution prompt examples * fix(context): include connection name in sl capture sql example * docs: add warehouse verification sql example closure plan * fix(context): report structured entity detail misses * docs: add warehouse verification structured target miss closure plan * fix: report untracked squash merge conflicts * feat: require ingest verification ledger * fix: stabilize ingest wiki references
This commit is contained in:
parent
bcb0d2f8f7
commit
c22248dabf
89 changed files with 7818 additions and 191 deletions
|
|
@ -8,22 +8,11 @@ describe('WikiListTagsTool', () => {
|
|||
it("returns distinct sorted tags across the user's visible pages", async () => {
|
||||
const pagesRepository = {
|
||||
listPagesForUser: vi.fn().mockResolvedValue([
|
||||
{ scope: 'GLOBAL', scope_id: null, page_key: 'k1' },
|
||||
{ scope: 'USER', scope_id: 'u', page_key: 'k2' },
|
||||
{ scope: 'GLOBAL', scope_id: null, page_key: 'k1', tags: ['metrics', 'finance'] },
|
||||
{ scope: 'USER', scope_id: 'u', page_key: 'k2', tags: ['metrics'] },
|
||||
]),
|
||||
};
|
||||
const wikiService = {
|
||||
readPage: vi.fn().mockImplementation((_scope, _scopeId, key) => {
|
||||
if (key === 'k1') {
|
||||
return Promise.resolve({ frontmatter: { tags: ['metrics', 'finance'] }, content: '' });
|
||||
}
|
||||
if (key === 'k2') {
|
||||
return Promise.resolve({ frontmatter: { tags: ['metrics'] }, content: '' });
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}),
|
||||
};
|
||||
const tool = new WikiListTagsTool(wikiService as any, pagesRepository as any);
|
||||
const tool = new WikiListTagsTool(pagesRepository as any);
|
||||
|
||||
const result = await tool.call({}, baseContext);
|
||||
expect(result.markdown).toContain('finance');
|
||||
|
|
@ -31,10 +20,23 @@ describe('WikiListTagsTool', () => {
|
|||
expect(result.structured.tags).toEqual(['finance', 'metrics']);
|
||||
});
|
||||
|
||||
it('lists tags from historic-SQL indexed pages with flat wiki keys', async () => {
|
||||
const pagesRepository = {
|
||||
listPagesForUser: vi.fn().mockResolvedValue([
|
||||
{ scope: 'GLOBAL', scope_id: null, page_key: 'company-overview', tags: ['notion'] },
|
||||
{ scope: 'GLOBAL', scope_id: null, page_key: 'historic-sql-revenue-pattern', tags: ['historic-sql', 'pattern'] },
|
||||
]),
|
||||
};
|
||||
const tool = new WikiListTagsTool(pagesRepository as any);
|
||||
|
||||
const result = await tool.call({}, baseContext);
|
||||
|
||||
expect(result.structured.tags).toEqual(['historic-sql', 'notion', 'pattern']);
|
||||
});
|
||||
|
||||
it('returns a friendly message when no pages have tags', async () => {
|
||||
const pagesRepository = { listPagesForUser: vi.fn().mockResolvedValue([]) };
|
||||
const wikiService = { readPage: vi.fn() };
|
||||
const tool = new WikiListTagsTool(wikiService as any, pagesRepository as any);
|
||||
const tool = new WikiListTagsTool(pagesRepository as any);
|
||||
|
||||
const result = await tool.call({}, baseContext);
|
||||
expect(result.markdown).toMatch(/no tags/i);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { z } from 'zod';
|
||||
import type { KnowledgeIndexPort } from '../ports.js';
|
||||
type BlockScope = 'GLOBAL' | 'USER';
|
||||
import { KnowledgeWikiService } from '../index.js';
|
||||
import { BaseTool, type ToolContext, type ToolOutput } from '../../tools/index.js';
|
||||
|
||||
const wikiListTagsInputSchema = z.object({});
|
||||
|
|
@ -11,10 +9,7 @@ type WikiListTagsInput = z.infer<typeof wikiListTagsInputSchema>;
|
|||
export class WikiListTagsTool extends BaseTool<typeof wikiListTagsInputSchema> {
|
||||
readonly name = 'wiki_list_tags';
|
||||
|
||||
constructor(
|
||||
private readonly wikiService: KnowledgeWikiService,
|
||||
private readonly pagesRepository: KnowledgeIndexPort,
|
||||
) {
|
||||
constructor(private readonly pagesRepository: KnowledgeIndexPort) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
|
@ -33,10 +28,7 @@ Call before writing a new page so you can reuse existing tags consistently inste
|
|||
const pages = await this.pagesRepository.listPagesForUser(context.userId);
|
||||
const set = new Set<string>();
|
||||
for (const p of pages) {
|
||||
const scope = p.scope as BlockScope;
|
||||
const scopeId = scope === 'USER' ? p.scope_id : null;
|
||||
const page = await this.wikiService.readPage(scope, scopeId, p.page_key);
|
||||
for (const t of page?.frontmatter.tags ?? []) {
|
||||
for (const t of p.tags) {
|
||||
set.add(t);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ export class WikiWriteTool extends BaseTool<typeof wikiWriteInputSchema> {
|
|||
Create or update a knowledge page. Provide content for create/rewrite, or replacements for targeted edits.
|
||||
For existing pages, you may provide only frontmatter fields such as summary, tags, refs, or sl_refs to update metadata while preserving content.
|
||||
tags/refs/sl_refs use REPLACE semantics: omit to keep existing on update, [] to clear, [values] to set.
|
||||
Keys must be flat file names, not directory paths. Use tags/source frontmatter for grouping.
|
||||
</purpose>`;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue