mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-19 08:28:06 +02:00
154 lines
4.7 KiB
TypeScript
154 lines
4.7 KiB
TypeScript
import type { ZodType } from 'zod';
|
|
import type { GitAuthorResolverPort, ToolContext, ToolOutput } from '../../tools/index.js';
|
|
import { BaseTool } from '../../tools/index.js';
|
|
import { sourceDefinitionSchema } from '../schemas.js';
|
|
import { SemanticLayerService } from '../semantic-layer.service.js';
|
|
import { SlSearchService } from '../sl-search.service.js';
|
|
|
|
export { sourceDefinitionSchema };
|
|
|
|
// ── Shared output types ──
|
|
|
|
export interface SemanticLayerStructured {
|
|
success: boolean;
|
|
sourceName: string;
|
|
yaml?: string;
|
|
commitHash?: string;
|
|
errors?: string[];
|
|
validationErrors?: string[];
|
|
validationWarnings?: string[];
|
|
actionRequiredWarnings?: string[];
|
|
}
|
|
|
|
export interface BaseSemanticLayerToolDeps {
|
|
semanticLayerService: SemanticLayerService;
|
|
slSearchService: SlSearchService;
|
|
authorResolver: GitAuthorResolverPort;
|
|
}
|
|
|
|
// ── Abstract base class ──
|
|
|
|
export abstract class BaseSemanticLayerTool<TInput extends ZodType = ZodType> extends BaseTool<TInput> {
|
|
protected readonly semanticLayerService: SemanticLayerService;
|
|
protected readonly slSearchService: SlSearchService;
|
|
protected readonly authorResolver: GitAuthorResolverPort;
|
|
|
|
constructor(deps: BaseSemanticLayerToolDeps) {
|
|
super();
|
|
this.semanticLayerService = deps.semanticLayerService;
|
|
this.slSearchService = deps.slSearchService;
|
|
this.authorResolver = deps.authorResolver;
|
|
}
|
|
|
|
protected async readSourceYaml(
|
|
connectionId: string,
|
|
sourceName: string,
|
|
context?: ToolContext,
|
|
): Promise<string | null> {
|
|
const semanticLayerService = context?.session?.semanticLayerService ?? this.semanticLayerService;
|
|
|
|
try {
|
|
const { content } = await semanticLayerService.readSourceFile(connectionId, sourceName);
|
|
return content;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected buildMarkdown(
|
|
success: boolean,
|
|
errors: string[],
|
|
sourceName: string,
|
|
extra?: {
|
|
yaml?: string;
|
|
commitHash?: string;
|
|
validationErrors?: string[];
|
|
validationWarnings?: string[];
|
|
actionRequiredWarnings?: string[];
|
|
editCount?: number;
|
|
},
|
|
): string {
|
|
const parts: string[] = [];
|
|
|
|
if (success) {
|
|
const verb = extra?.editCount != null ? `applied ${extra.editCount} edit(s) to` : 'saved';
|
|
parts.push(`Source **${sourceName}** ${verb} successfully.`);
|
|
} else {
|
|
parts.push(`Source **${sourceName}** update completed with ${errors.length} error(s):`);
|
|
for (const err of errors) {
|
|
parts.push(`- ${err}`);
|
|
}
|
|
}
|
|
|
|
if (extra?.commitHash) {
|
|
parts.push(`Commit: \`${extra.commitHash}\``);
|
|
}
|
|
|
|
if (extra?.actionRequiredWarnings && extra.actionRequiredWarnings.length > 0) {
|
|
parts.push('\n**Action required:**');
|
|
for (const warning of extra.actionRequiredWarnings) {
|
|
parts.push(`- ${warning}`);
|
|
}
|
|
}
|
|
|
|
if (extra?.validationErrors && extra.validationErrors.length > 0) {
|
|
parts.push('\n**Validation errors:**');
|
|
for (const ve of extra.validationErrors) {
|
|
parts.push(`- ${ve}`);
|
|
}
|
|
}
|
|
|
|
if (extra?.validationWarnings && extra.validationWarnings.length > 0) {
|
|
parts.push('\n**Validation warnings:**');
|
|
for (const vw of extra.validationWarnings) {
|
|
parts.push(`- ${vw}`);
|
|
}
|
|
}
|
|
|
|
if (extra?.yaml) {
|
|
const yaml = extra.yaml;
|
|
const MAX_YAML = 2000;
|
|
if (yaml.length > MAX_YAML) {
|
|
parts.push(`\n**YAML** (${yaml.length} chars, truncated):\n\`\`\`yaml\n${yaml.slice(0, MAX_YAML)}...\n\`\`\``);
|
|
} else {
|
|
parts.push(`\n**YAML**:\n\`\`\`yaml\n${yaml}\n\`\`\``);
|
|
}
|
|
}
|
|
|
|
return parts.join('\n');
|
|
}
|
|
|
|
protected buildOutput(
|
|
success: boolean,
|
|
errors: string[],
|
|
sourceName: string,
|
|
extra?: {
|
|
yaml?: string;
|
|
commitHash?: string;
|
|
validationErrors?: string[];
|
|
validationWarnings?: string[];
|
|
actionRequiredWarnings?: string[];
|
|
editCount?: number;
|
|
},
|
|
): ToolOutput<SemanticLayerStructured> {
|
|
return {
|
|
markdown: this.buildMarkdown(success, errors, sourceName, extra),
|
|
structured: {
|
|
success,
|
|
sourceName,
|
|
yaml: extra?.yaml,
|
|
commitHash: extra?.commitHash,
|
|
...(errors.length > 0 ? { errors } : {}),
|
|
...(extra?.validationErrors && extra.validationErrors.length > 0
|
|
? { validationErrors: extra.validationErrors }
|
|
: {}),
|
|
...(extra?.validationWarnings && extra.validationWarnings.length > 0
|
|
? { validationWarnings: extra.validationWarnings }
|
|
: {}),
|
|
...(extra?.actionRequiredWarnings && extra.actionRequiredWarnings.length > 0
|
|
? { actionRequiredWarnings: extra.actionRequiredWarnings }
|
|
: {}),
|
|
},
|
|
};
|
|
}
|
|
}
|