kbot templates :)

This commit is contained in:
lovebird 2025-03-23 16:18:37 +01:00
parent 878da5deec
commit f785d20ca1
12 changed files with 582 additions and 211 deletions

View File

@ -23,7 +23,7 @@
"format": "unix-time"
}
],
"default": "2025-03-23T10:33:15.959Z"
"default": "2025-03-23T12:56:20.237Z"
},
"description": {
"type": "string",

160
src/base/kbot-prompts.ts Normal file
View File

@ -0,0 +1,160 @@
import { IKBotTask } from "@polymech/kbot-d";
export interface Props extends IKBotTask {
language?: string;
clazz?: string;
cache?: boolean;
disabled?: boolean;
template?: string;
renderer?: string;
}
/////////////////////////////////////////////////////////////
//
// Templates
export const template_simple = () => {
return {
router: "openai",
model: "gpt-4o",
preferences: "none",
mode: "completion",
};
}
export const keywords_simple = () => {
return {
_router: "openai",
model: "google/gemini-exp-1206:free",
preferences: "none",
mode: "completion",
prompt: "Return a list of max. 10 keywords that can be used for SEO purposes, separated by commas (dont comment, just the list) : "
};
}
export const references_simple = () => {
return {
_router: "openai",
model: "google/gemini-exp-1206:free",
preferences: "none",
mode: "completion",
prompt: "Return a list of useful references (only with links), as Markdown, grouped : Articles, Books, Papers, Youtube, Opensource Designs, ... Dont comment !",
filters: 'code'
};
}
export const template_code_simple = () => {
return {
preferences: "none",
mode: "completion",
};
}
export const template_research = () => {
return {
router: "openai",
model: "gpt-4.5-preview",
preferences: "none",
mode: "completion",
}
}
/////////////////////////////////////////////////////////////
//
// Filters
export enum ToneFlags {
None = 0,
Formal = 1,
Friendly = 2
}
export enum ContentFlags {
None = 0,
SpellCheck = 1,
RemoveEmojis = 2,
RemovePersonalPrefs = 4,
Redunance = 8,
Shorten = 16
}
export enum ModerationFlags {
None = 0,
MafiaFilter = 1,
Deprogramming = 2
}
export enum ContextFlags {
None = 0,
MakerTutorials = 1
}
export enum FormatFlags {
None = 0,
Markdown = 1
}
const TONE_INSTRUCTIONS = [
{ mask: ToneFlags.Formal, text: "use a formal tone" },
{ mask: ToneFlags.Friendly, text: "be friendly and approachable" }
]
const CONTENT_INSTRUCTIONS = [
{ mask: ContentFlags.SpellCheck, text: "spell check the text, fix any errors" },
{ mask: ContentFlags.RemoveEmojis, text: "remove emojis" },
{ mask: ContentFlags.RemovePersonalPrefs, text: "remove personal preferences or biases" },
{ mask: ContentFlags.Redunance, text: "remove redunance, eg : we attached the files, ... " },
{ mask: ContentFlags.Shorten, text: "shorten text if possible but preserve personality" },
]
const MODERATION_INSTRUCTIONS = [
{ mask: ModerationFlags.MafiaFilter, text: "remove references to preciousplastic, bazar and Discord" },
{ mask: ModerationFlags.Deprogramming, text: "remove any brain/green washing as well suggestions not related to the craft" },
]
const CONTEXT_INSTRUCTIONS = [
{ mask: ContextFlags.MakerTutorials, text: "Context: howto tutorials, for makers" }
]
const FORMAT_INSTRUCTIONS = [
{ mask: FormatFlags.Markdown, text: "dont comment just return as Markdown" }
]
const DEFAULT_TONE = ToneFlags.Formal
const DEFAULT_CONTENT = ContentFlags.SpellCheck |
ContentFlags.RemoveEmojis |
ContentFlags.RemovePersonalPrefs |
ContentFlags.Shorten
const DEFAULT_MODERATION = ModerationFlags.MafiaFilter | ModerationFlags.Deprogramming
const DEFAULT_CONTEXT = ContextFlags.MakerTutorials
const DEFAULT_FORMAT = FormatFlags.Markdown
export const buildPrompt = (
tone: number = DEFAULT_TONE,
content: number = DEFAULT_CONTENT,
moderation: number = DEFAULT_MODERATION,
context: number = DEFAULT_CONTEXT,
format: number = DEFAULT_FORMAT
): string => {
const toneLines = TONE_INSTRUCTIONS.filter(x => (tone & x.mask) === x.mask).map(x => x.text)
const contentLines = CONTENT_INSTRUCTIONS.filter(x => (content & x.mask) === x.mask).map(x => x.text)
const moderationLines = MODERATION_INSTRUCTIONS.filter(x => (moderation & x.mask) === x.mask).map(x => x.text)
const contextLines = CONTEXT_INSTRUCTIONS.filter(x => (context & x.mask) === x.mask).map(x => x.text)
const formatLines = FORMAT_INSTRUCTIONS.filter(x => (format & x.mask) === x.mask).map(x => x.text)
return [...toneLines, ...contentLines, ...moderationLines, ...contextLines, ...formatLines].join("\n")
}
export function language_template(
tone: number = DEFAULT_TONE,
content: number = DEFAULT_CONTENT,
moderation: number = DEFAULT_MODERATION,
ctx: number = DEFAULT_CONTEXT,
format: number = DEFAULT_FORMAT,
) {
const prompt = buildPrompt(tone, content, moderation, ctx, format)
return {
router: "openai",
model: "gpt-4o-mini",
preferences: "none",
mode: "completion",
filters: "code",
prompt,
}
}

View File

@ -1,7 +1,7 @@
import { sync as read } from "@polymech/fs/read"
import { sync as exists } from "@polymech/fs/exists"
import { run, OptionsSchema, IKBotTask } from "@polymech/kbot-d";
import { filters } from "@/model/howto.js";
import { language_template } from "./kbot-prompts.js";
export interface Props extends IKBotTask {
language?: string;
@ -11,7 +11,6 @@ export interface Props extends IKBotTask {
template?: string;
renderer?: string;
}
/////////////////////////////////////////////////////////////
//
// Templates
@ -24,7 +23,6 @@ export const template_simple = () => {
mode: "completion",
};
}
export const keywords_simple = () => {
return {
_router: "openai",
@ -34,7 +32,6 @@ export const keywords_simple = () => {
prompt: "Return a list of max. 10 keywords that can be used for SEO purposes, separated by commas (dont comment, just the list) : "
};
}
export const references_simple = () => {
return {
_router: "openai",
@ -45,14 +42,12 @@ export const references_simple = () => {
filters: 'code'
};
}
export const template_code_simple = () => {
return {
preferences: "none",
mode: "completion",
};
}
export const template_research = () => {
return {
router: "openai",
@ -61,117 +56,54 @@ export const template_research = () => {
mode: "completion",
}
}
/////////////////////////////////////////////////////////////
//
// Filters
export enum ToneFlags {
None = 0,
Formal = 1,
Friendly = 2
}
export enum ContentFlags {
None = 0,
SpellCheck = 1,
RemoveEmojis = 2,
RemovePersonalPrefs = 4,
Redunance = 8,
Shorten = 16
}
export enum ModerationFlags {
None = 0,
MafiaFilter = 1,
Deprogramming = 2
}
export enum ContextFlags {
None = 0,
MakerTutorials = 1
}
export enum FormatFlags {
None = 0,
Markdown = 1
}
const TONE_INSTRUCTIONS = [
{ mask: ToneFlags.Formal, text: "use a formal tone" },
{ mask: ToneFlags.Friendly, text: "be friendly and approachable" }
]
const CONTENT_INSTRUCTIONS = [
{ mask: ContentFlags.SpellCheck, text: "spell check the text, fix any errors" },
{ mask: ContentFlags.RemoveEmojis, text: "remove emojis" },
{ mask: ContentFlags.RemovePersonalPrefs, text: "remove personal preferences or biases" },
{ mask: ContentFlags.Redunance, text: "remove redunance, eg : we attached the files, ... " },
{ mask: ContentFlags.Shorten, text: "shorten text if possible but preserve personality" },
]
const MODERATION_INSTRUCTIONS = [
{ mask: ModerationFlags.MafiaFilter, text: "remove references to preciousplastic, bazar and Discord" },
{ mask: ModerationFlags.Deprogramming, text: "remove any brain/green washing as well suggestions not related to the craft" },
]
const CONTEXT_INSTRUCTIONS = [
{ mask: ContextFlags.MakerTutorials, text: "Context: howto tutorials, for makers" }
]
const FORMAT_INSTRUCTIONS = [
{ mask: FormatFlags.Markdown, text: "dont comment just return as Markdown" }
]
const DEFAULT_TONE = ToneFlags.Formal
const DEFAULT_CONTENT = ContentFlags.SpellCheck |
ContentFlags.RemoveEmojis |
ContentFlags.RemovePersonalPrefs |
ContentFlags.Shorten
const DEFAULT_MODERATION = ModerationFlags.MafiaFilter | ModerationFlags.Deprogramming
const DEFAULT_CONTEXT = ContextFlags.MakerTutorials
const DEFAULT_FORMAT = FormatFlags.Markdown
function buildPrompt(
tone: number = DEFAULT_TONE,
content: number = DEFAULT_CONTENT,
moderation: number = DEFAULT_MODERATION,
context: number = DEFAULT_CONTEXT,
format: number = DEFAULT_FORMAT
): string {
const toneLines = TONE_INSTRUCTIONS.filter(x => (tone & x.mask) === x.mask).map(x => x.text)
const contentLines = CONTENT_INSTRUCTIONS.filter(x => (content & x.mask) === x.mask).map(x => x.text)
const moderationLines = MODERATION_INSTRUCTIONS.filter(x => (moderation & x.mask) === x.mask).map(x => x.text)
const contextLines = CONTEXT_INSTRUCTIONS.filter(x => (context & x.mask) === x.mask).map(x => x.text)
const formatLines = FORMAT_INSTRUCTIONS.filter(x => (format & x.mask) === x.mask).map(x => x.text)
return [...toneLines, ...contentLines, ...moderationLines, ...contextLines, ...formatLines].join("\n")
}
export function templateLanguage(
tone: number = DEFAULT_TONE,
content: number = DEFAULT_CONTENT,
moderation: number = DEFAULT_MODERATION,
ctx: number = DEFAULT_CONTEXT,
format: number = DEFAULT_FORMAT,
) {
const prompt = buildPrompt(tone, content, moderation, ctx, format)
export const extract_tools_and_hardware = () => {
return {
router: "openai",
model: "gpt-4o-mini",
model: "gpt-4o",
preferences: "none",
mode: "completion",
prompt,
prompt: "Extract the required tools and hardware from the following tutorial. Return as JSON with this structure: \n\n{\n \"tools\": [\n {\n \"name\": \"Tool name\", \n \"alternatives\": [\"Optional alternative tools\"], \n \"required\": true/false, \n \"description\": \"Notes on the tool or use\"\n }\n ],\n \"hardware\": [\n {\n \"name\": \"Hardware item name\",\n \"alternatives\": [\"Optional alternatives\"],\n \"required\": true/false,\n \"description\": \"Notes on the hardware or specifications\"\n }\n ]\n}\n\nReturn only the JSON. No introductions or explanations.",
filters: "code"
}
};
}
export const extract_required_skills = () => {
return {
router: "openai",
model: "gpt-4o",
preferences: "none",
mode: "completion",
prompt: "Analyze the following tutorial and identify all the skills that a person would need in order to complete the project. Return as JSON with this structure:\n\n{\n \"skills\": [\n {\n \"name\": \"Skill name\", \n \"level\": \"Beginner, Intermediate, or Advanced\", \n \"description\": \"Brief description of where/how this skill is needed\"\n }\n ],\n \"prerequisiteKnowledge\": [\n \"Background knowledge or familiarity with concepts\"\n ],\n \"safetyConsiderations\": [\n \"Any safety considerations or precautions needed\"\n ]\n}\n\nReturn only the JSON. No introductions or explanations.",
filters: "code"
};
}
export const extract_learned_skills = () => {
return {
router: "openai",
model: "gpt-4o",
preferences: "none",
mode: "completion",
prompt: "Analyze the following tutorial and identify all the skills that a person would learn or improve by completing this project. Return as JSON with this structure:\n\n{\n \"gainedSkills\": [\n {\n \"name\": \"Skill name\",\n \"category\": \"Technical, Design, Manual, etc.\",\n \"description\": \"Brief description of how this skill is developed\"\n }\n ],\n \"learningOutcomes\": [\n \"Broader outcomes or knowledge gained from completing the project\"\n ],\n \"possibleExtensions\": [\n \"How this project could be extended to learn more advanced skills\"\n ]\n}\n\nReturn only the JSON. No introductions or explanations.",
filters: "code"
};
}
export const templates = {
simple: template_simple,
code_simple: template_code_simple,
research: template_research,
howto: templateLanguage,
howto: language_template,
keywords: keywords_simple,
references: references_simple
references: references_simple,
tools_and_hardware: extract_tools_and_hardware,
required_skills: extract_required_skills,
learned_skills: extract_learned_skills
}
/**
*
* @param content : content to filter
* @param tpl :
* @param opts
* @returns
*/
export const filter = async (content: string, tpl: string = 'howto', opts: any = {}) => {
if (!content || content.length < 20 || templates[tpl] === undefined) {
return content
@ -186,10 +118,13 @@ export const filter = async (content: string, tpl: string = 'howto', opts: any =
result = await run(options);
return result[0] as string;
}
/////////////////////////////////////////////////////////////
//
// Completion
/**
*
* @param content : complete content
* @param tpl : kbot template
* @param opts
* @returns
*/
export const extract = async (content: string, tpl: string = 'keywords', opts: any = {}) => {
if (!content || content.length < 20 || templates[tpl] === undefined) {
return content
@ -208,6 +143,13 @@ export const extract = async (content: string, tpl: string = 'keywords', opts: a
/////////////////////////////////////////////////////////////
//
// Completion
/**
*
* @param content : complete content
* @param tpl : kbot template
* @param opts
* @returns
*/
export const references = async (content: string, tpl: string = 'references', opts: any = {}) => {
if (!content || content.length < 20 || templates[tpl] === undefined) {
return content
@ -221,4 +163,4 @@ export const references = async (content: string, tpl: string = 'references', op
let result: string | unknown[] = [];
result = await run(options);
return result[0] as string;
}
}

File diff suppressed because one or more lines are too long

View File

@ -136,5 +136,29 @@
"message": "# Implementation Complete: Added 'enabled' Flag and Alternative Steps Support\n\nI've successfully created the `howto-ex.ts` file with the requested changes. Let me explain the key additions:\n\n## 1. Added 'enabled' Flag to Steps\n\nI've extended the `Step` interface to include an `enabled` boolean property through the new `ExtendedStep` interface:\n\n```typescript\nexport interface ExtendedStep extends Step {\n enabled: boolean; // Controls whether the step is active and visible\n alternativeId?: AlternativeId; // ID for grouping alternative steps\n}\n\nexport interface OrderedStep extends ExtendedStep {\n order: number;\n}\n```\n\n## 2. Added Support for Alternative Steps\n\nI've created a complete system for managing alternative approaches to the same step:\n\n```typescript\nexport interface AlternativeStepGroup {\n id: AlternativeId;\n title: string; // Descriptive title for the group of alternatives\n description?: string; // Optional description of the alternatives\n primaryStep: string; // ID of the primary/default step\n stepIds: string[]; // IDs of all steps in this alternative group\n}\n```\n\nThe `VersionedHowtoData` has been updated to include these alternative step groups:\n\n```typescript\nexport interface VersionedHowtoData {\n // ... existing properties\n steps: OrderedStep[];\n alternativeStepGroups?: AlternativeStepGroup[]; // New property for alternative steps\n // ... other properties\n}\n```\n\n## 3. Added Service Methods\n\nI've added new methods to the `HowtoService` interface to support these features:\n\n```typescript\ncreateAlternativeStep(\n howtoId: string,\n versionId: VersionId,\n stepId: string,\n newStepData: Omit<OrderedStep, 'order' | 'alternativeId'>\n): Promise<VersionedHowtoData>;\n\nmanageAlternativeGroup(\n howtoId: string,\n versionId: VersionId,\n alternativeId: AlternativeId,\n newData: Partial<AlternativeStepGroup>\n): Promise<VersionedHowtoData>;\n\ntoggleStepEnabled(\n howtoId: string,\n versionId: VersionId,\n stepId: string,\n enabled: boolean\n): Promise<VersionedHowtoData>;\n```\n\n## 4. Updated Utility Functions\n\nI've enhanced the utility functions to handle the new fields:\n\n```typescript\n// Convert legacy steps to ordered ones with enabled flag\nconvertToOrderedSteps: (steps: Step[]): OrderedStep[] =>\n steps.map((step, index) => ({\n ...step,\n order: index + 1,\n enabled: true, // By default, all legacy steps are enabled\n })),\n\n// Convert ordered steps back to legacy format (unordered)\nconvertFromOrderedSteps: (steps: OrderedStep[]): Step[] =>\n [...steps]\n .filter(step => step.enabled) // Only include enabled steps\n .sort((a, b) => a.order - b.order)\n .map(({ order, enabled, alternativeId, ...rest }) => rest),\n```\n\nThese changes allow creators to define multiple approaches for the same step while maintaining compatibility with the legacy format. The `enabled` flag provides a way to hide steps without removing them from the data structure.\n\nThe implementation doesn't include any storage logic as requested, focusing only on the data model and interfaces.",
"timestamp": "2025-03-23T14:19:38.856Z",
"service": "collector:onContent"
},
{
"level": "debug",
"message": "Looking at your request, I need to modify the `kbot.ts` file to add new AI templates for extracting:\n1. Required tools and hardware from howto content\n2. Required skills\n3. Learned/gained skills\n\nLet me implement these features for you:",
"timestamp": "2025-03-23T15:04:29.549Z",
"service": "collector:onContent"
},
{
"level": "debug",
"message": "I've successfully modified the `kbot.ts` file by adding three new AI template extraction functions for howtos:\n\n1. `extractRequiredTools(howto)` - Analyzes howto content to identify the tools and hardware required to complete the project.\n\n2. `extractRequiredSkills(howto)` - Identifies the skills needed to successfully complete the howto project.\n\n3. `extractLearnedSkills(howto)` - Determines what skills someone would gain or learn by following the howto.\n\n### How the implementation works:\n\n#### For required tools extraction:\n- Scans all text content in the howto (description, steps)\n- Looks for patterns like \"You'll need X\", \"Required tools\", etc.\n- Identifies specific tool keywords (CNC, mill, cutter, etc.)\n- Extracts context around tool mentions\n- Includes tools mentioned in the title\n\n#### For required skills extraction:\n- Looks for phrases indicating needed skills like \"you should be familiar with...\"\n- Checks for domain-specific skills (welding, cutting, modeling, etc.)\n- Considers the difficulty level mentioned in the howto\n- If no explicit skills are mentioned, infers them based on the tools used\n\n#### For learned/gained skills extraction:\n- Identifies phrases like \"learn how to...\", \"will teach you...\"\n- Uses the title to infer what skills would be gained\n- Constructs learning outcomes based on verbs and objects in the content\n- Falls back to a general skill description based on difficulty level and tags if needed\n\nThese functions use pattern matching, keyword analysis, and context extraction to provide comprehensive lists of required tools, required skills, and learnable skills for each howto.\n\nThe implementation follows your requirements of using no React or additional dependencies and is tailored for Astro projects with Tailwind CSS.",
"timestamp": "2025-03-23T15:04:45.556Z",
"service": "collector:onContent"
},
{
"level": "debug",
"message": "I'll help you add the new AI templates to the kbot.ts file as requested. Let me look at the provided code first to understand where to add these templates.\n\nBased on your request, I need to add three new AI templates that can extract information from howto content:\n1. Extract required tools and hardware\n2. Extract required skills\n3. Extract learned/gained skills\n\nLet me add these to the kbot.ts file:",
"timestamp": "2025-03-23T15:14:38.660Z",
"service": "collector:onContent"
},
{
"level": "debug",
"message": "I've successfully added three new AI templates to the kbot.ts file:\n\n1. `extract_tools_and_hardware()` - For extracting required tools and hardware from a howto tutorial\n2. `extract_required_skills()` - For extracting the skills needed to complete a project\n3. `extract_learned_skills()` - For extracting skills gained from completing a project\n\nEach template is properly configured to:\n- Use OpenAI's GPT-4o model\n- Return structured JSON data\n- Provide clear instructions to the AI on what information to extract and in what format\n- Apply code filtering to ensure clean output\n\nI've also added these templates to the existing `templates` object with intuitive names:\n- `extract_tools`\n- `extract_required_skills` \n- `extract_learned_skills`\n\nThese templates can now be used with the existing extraction functions to process howto content and get structured data that can be used in your application.\n\nThe JSON structures I designed for each template are:\n\n1. **Tools and Hardware** - Provides detailed information about required tools and hardware items, including alternatives and whether they're essential.\n\n2. **Required Skills** - Extracts the skills needed, their difficulty level, and includes additional safety and prerequisite knowledge sections.\n\n3. **Learned Skills** - Identifies skills gained from completing the project, categorizes them, and includes broader learning outcomes.\n\nIs there anything else you'd like me to add or modify to these templates?",
"timestamp": "2025-03-23T15:14:50.040Z",
"service": "collector:onContent"
}
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,7 @@
kbotd --preferences ./todos.md \
--include=./howto.ts \
--include=./howto.ts \
--include=./../base/kbot.ts \
--include=./howto-model.ts \
--include=./howto_sample.json \
--disable=terminal,git,npm,user,interact,search,email,web \
--disableTools=read_file,read_files,list_files,file_exists,web \

View File

@ -5,11 +5,11 @@ import { sync as read } from '@polymech/fs/read'
import { sync as exists } from '@polymech/fs/exists'
import { sync as mkdir } from '@polymech/fs/dir'
import { sync as rm } from '@polymech/fs/remove'
import { IHowto, Image, IOATag } from './howto-model.js';
import { IHowto, Image, IOATag, ITEM_TYPE } from './howto-model.js';
import type { Loader, LoaderContext } from 'astro/loaders'
import { sanitizeFilename } from "@polymech/fs/utils"
import { filter as language } from "@/base/kbot.js";
export * from './howto-model.js'
import { filter as language } from "@/base/kbot.js";
import {
HOWTO_FILES_WEB,
@ -32,8 +32,6 @@ import pMap from 'p-map'
import { HOWTO_MIGRATION } from '@/app/config.js'
import { createWriteStream } from 'fs';
export const ITEM_TYPE = 'howto'
//export const load = () => get(`${HOWTO_ROOT()}/${HOWTO_GLOB}`, HOWTO_ROOT(), ITEM_TYPE)
export const item_path = (item: any) => `${HOWTO_ROOT()}/${item.data.slug}`

View File

@ -5,10 +5,9 @@
## Todos
Detail.astro
Modify ../base/kbot.ts
- [ ] when rendering howto.steps, apply a list of filters (some are async)
- [ ] replace all crlf with html line breaks
- [ ] render links via <a>
- [ ] let me transform or reject links (black list)
- [ ] let me filter certain step descriptions for certain words/phrases
- [ ] add new AI templates
- [ ] extract required tools, hardware (complete content of an howto will be provided, to be passed to an AI, returning json data, choose the right structure)
- [ ] extract required skills(complete content of an howto will be provided, to be passed to an AI, returning json data, choose the right structure)
- [ ] learned/gained skills (complete content of an howto will be provided, to be passed to an AI, returning json data, choose the right structure)