generated from polymech/site-template
262 lines
9.5 KiB
TypeScript
262 lines
9.5 KiB
TypeScript
import { IKBotTask } from "@polymech/kbot-d";
|
|
import { sync as read } from "@polymech/fs/read";
|
|
import { sync as exists } from "@polymech/fs/exists";
|
|
import { z } from "zod";
|
|
|
|
// Type definitions
|
|
const InstructionSchema = z.object({
|
|
flag: z.string(),
|
|
text: z.string()
|
|
});
|
|
|
|
const InstructionSetSchema = z.record(z.array(InstructionSchema));
|
|
|
|
export interface TemplateProps extends IKBotTask {
|
|
language?: string;
|
|
clazz?: string;
|
|
cache?: boolean;
|
|
disabled?: boolean;
|
|
template?: string;
|
|
renderer?: string;
|
|
}
|
|
|
|
const TemplateConfigSchema = z.object({
|
|
router: z.string().optional(),
|
|
_router: z.string().optional(),
|
|
model: z.string(),
|
|
preferences: z.string(),
|
|
mode: z.string(),
|
|
filters: z.string().optional(),
|
|
overrides: z.record(z.array(z.string())).optional(),
|
|
variables: z.record(z.string()).optional()
|
|
});
|
|
|
|
type TemplateConfig = z.infer<typeof TemplateConfigSchema>;
|
|
|
|
interface TemplateFunction {
|
|
(opts?: Partial<TemplateConfig>): TemplateConfig;
|
|
}
|
|
|
|
interface TemplateMap {
|
|
[key: string]: TemplateFunction | TemplateConfig;
|
|
}
|
|
|
|
const LLMConfigSchema = z.object({
|
|
templates: z.record(TemplateConfigSchema),
|
|
instructions: InstructionSetSchema.optional(),
|
|
defaults: z.record(z.array(z.string())).optional()
|
|
});
|
|
|
|
type LLMConfig = z.infer<typeof LLMConfigSchema>;
|
|
|
|
// Context enum for template switching
|
|
export const enum TemplateContext {
|
|
COMMONS = 'commons',
|
|
HOWTO = 'howto',
|
|
MARKETPLACE = 'marketplace'
|
|
}
|
|
|
|
// Default configuration
|
|
export const DEFAULT_CONFIG: LLMConfig = {
|
|
templates: {},
|
|
instructions: {},
|
|
defaults: {
|
|
tone: ["formal"],
|
|
content: ["spellCheck", "removeEmojis", "removePersonalPrefs", "shorten"],
|
|
moderation: ["mafiaFilter", "deprogramming"],
|
|
context: ["makerTutorials", "units"],
|
|
format: ["markdown"]
|
|
}
|
|
};
|
|
|
|
// Default template options
|
|
const DEFAULT_TEMPLATE_OPTIONS = {
|
|
mode: "completion",
|
|
preferences: "none",
|
|
filters: "code"
|
|
} as const;
|
|
|
|
// Load configuration from JSON file
|
|
const loadConfig = (context: TemplateContext = TemplateContext.COMMONS): LLMConfig => {
|
|
const configPath = `./src/config/templates/${context}.json`;
|
|
if (exists(configPath)) {
|
|
try {
|
|
const content = read(configPath);
|
|
if (typeof content === 'string') {
|
|
const parsed = JSON.parse(content);
|
|
return LLMConfigSchema.parse(parsed);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error loading ${context} config:`, error);
|
|
}
|
|
}
|
|
return DEFAULT_CONFIG;
|
|
};
|
|
|
|
// Helper function to build prompt from instruction sets
|
|
export const buildPrompt = (
|
|
instructions: z.infer<typeof InstructionSetSchema>,
|
|
defaults: Record<string, string[]>,
|
|
overrides?: Record<string, string[]>
|
|
): string => {
|
|
const getInstructions = (category: string, flags: string[]) => {
|
|
const set = instructions[category] || [];
|
|
return set.filter(x => flags.includes(x.flag)).map(x => x.text);
|
|
};
|
|
|
|
const merged = Object.keys(instructions).reduce((acc, category) => ({
|
|
...acc,
|
|
[category]: (overrides?.[category] ?? defaults[category] ?? [])
|
|
}), {} as Record<string, string[]>);
|
|
|
|
return Object.entries(merged)
|
|
.flatMap(([category, flags]) => getInstructions(category, flags))
|
|
.join("\n");
|
|
};
|
|
|
|
// Prompt system
|
|
const PromptSchema = z.object({
|
|
template: z.string(),
|
|
variables: z.record(z.string()).optional(),
|
|
format: z.enum(['text', 'json', 'markdown']).default('text')
|
|
});
|
|
|
|
type Prompt = z.infer<typeof PromptSchema>;
|
|
|
|
const PromptRegistrySchema = z.record(PromptSchema);
|
|
|
|
type PromptRegistry = z.infer<typeof PromptRegistrySchema>;
|
|
|
|
const DEFAULT_PROMPTS: PromptRegistry = {
|
|
keywords: {
|
|
template: "Return a list of max. 10 keywords that can be used for SEO purposes, separated by commas (dont comment, just the list) : ",
|
|
format: 'text'
|
|
},
|
|
references: {
|
|
template: "Return a list of useful references (only with links), as Markdown, grouped : Articles, Books, Papers, Youtube, Opensource Designs, ... Dont comment !",
|
|
format: 'markdown'
|
|
},
|
|
toolsAndHardware: {
|
|
template: "Extract the required tools, software hardware from the following tutorial. Return as Markdown chapters (H3) with very short bullet points (not bold), with links, max. 5.",
|
|
format: 'markdown'
|
|
},
|
|
learnedSkills: {
|
|
template: "Analyze the following tutorial and identify all the skills that a person would learn or improve by completing this project, modest, humble, simple - dont enflate (sustainable, recylcing, ...), Return as Markdown chapter (H2) with very short bullet points (not bold), max. 5.",
|
|
format: 'markdown'
|
|
},
|
|
local: {
|
|
template: "Markdown chapter (h4) with a list of local resources, services & suppliers, max. 5, with links, group by category. dont comment, just the list",
|
|
format: 'markdown'
|
|
}
|
|
};
|
|
|
|
// Helper function to render prompt
|
|
const renderPrompt = (prompt: Prompt, variables: Record<string, string> = {}): string => {
|
|
let result = prompt.template;
|
|
for (const [key, value] of Object.entries(variables)) {
|
|
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
// Template definitions
|
|
const createTemplate = (config: LLMConfig, name: string, defaults: Partial<TemplateConfig>, promptKey?: keyof PromptRegistry) => {
|
|
return (opts: Partial<TemplateConfig> = {}) => {
|
|
const template = config.templates[name] || defaults;
|
|
const prompt = promptKey ? renderPrompt(DEFAULT_PROMPTS[promptKey], opts.variables) : buildPrompt(
|
|
config.instructions || {},
|
|
config.defaults || {},
|
|
opts.overrides || template.overrides
|
|
);
|
|
|
|
const merged = {
|
|
...template,
|
|
...opts,
|
|
prompt
|
|
};
|
|
return merged;
|
|
};
|
|
};
|
|
|
|
export const createTemplates = (context: TemplateContext = TemplateContext.COMMONS) => {
|
|
const config = loadConfig(context);
|
|
|
|
switch (context) {
|
|
case TemplateContext.COMMONS:
|
|
return {
|
|
simple: createTemplate(config, 'simple', {
|
|
router: "openai",
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}),
|
|
codeSimple: createTemplate(config, 'codeSimple', {
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}),
|
|
research: createTemplate(config, 'research', {
|
|
router: "openai",
|
|
model: "gpt-4.5-preview",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
})
|
|
};
|
|
case TemplateContext.HOWTO:
|
|
return {
|
|
keywords: createTemplate(config, 'keywords', {
|
|
_router: "openai",
|
|
model: "google/gemini-exp-1206:free",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}, 'keywords'),
|
|
references: createTemplate(config, 'references', {
|
|
_router: "openai",
|
|
model: "google/gemini-exp-1206:free",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}, 'references'),
|
|
toolsAndHardware: createTemplate(config, 'toolsAndHardware', {
|
|
router: "openai",
|
|
model: "perplexity/sonar-deep-research",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}, 'toolsAndHardware'),
|
|
requiredSkills: createTemplate(config, 'requiredSkills', {
|
|
router: "openai",
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}),
|
|
learnedSkills: createTemplate(config, 'learnedSkills', {
|
|
router: "openai",
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}, 'learnedSkills'),
|
|
local: createTemplate(config, 'local', {
|
|
model: "perplexity/sonar-deep-research",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}, 'local')
|
|
};
|
|
case TemplateContext.MARKETPLACE:
|
|
return {
|
|
productDescription: createTemplate(config, 'productDescription', {
|
|
router: "openai",
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}),
|
|
pricingAnalysis: createTemplate(config, 'pricingAnalysis', {
|
|
router: "openai",
|
|
model: "gpt-4o",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
}),
|
|
marketResearch: createTemplate(config, 'marketResearch', {
|
|
router: "openai",
|
|
model: "perplexity/sonar-deep-research",
|
|
...DEFAULT_TEMPLATE_OPTIONS
|
|
})
|
|
};
|
|
default:
|
|
return {};
|
|
}
|
|
};
|
|
|
|
// Export default templates with default config (commons context)
|
|
export const templates = createTemplates(TemplateContext.COMMONS);
|
|
|
|
// Export types
|
|
export type { TemplateConfig, LLMConfig, Prompt, PromptRegistry };
|
|
export { InstructionSchema, InstructionSetSchema, TemplateConfigSchema, LLMConfigSchema, PromptSchema, PromptRegistrySchema };
|