generated from polymech/site-template
latest
This commit is contained in:
parent
55dae7814d
commit
1833ce2c2e
@ -23,7 +23,7 @@
|
||||
"format": "unix-time"
|
||||
}
|
||||
],
|
||||
"default": "2025-04-01T07:22:34.370Z"
|
||||
"default": "2025-04-01T16:27:18.441Z"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -670,7 +670,7 @@
|
||||
},
|
||||
"https://scholarworks.uni.edu/cgi/viewcontent.cgi?article=3680%5C&context=grp": {
|
||||
"isValid": false,
|
||||
"timestamp": 1743492156147
|
||||
"timestamp": 1743522204145
|
||||
},
|
||||
"https://pmc.ncbi.nlm.nih.gov/articles/PMC10489002/": {
|
||||
"isValid": true,
|
||||
@ -754,7 +754,7 @@
|
||||
},
|
||||
"https://journals.plos.org/plosone/article?id=10.1371%252Fjournal.pone.0288696": {
|
||||
"isValid": false,
|
||||
"timestamp": 1743492156605
|
||||
"timestamp": 1743522204880
|
||||
},
|
||||
"https://www.youtube.com/watch?v=_a7usMe_K38": {
|
||||
"isValid": true,
|
||||
@ -841,7 +841,7 @@
|
||||
},
|
||||
"https://www.toraytac.com/media/c3feb206-1398-4e0e-bca6-df7780f11745/tcCurg/TenCate%2520Advanced%2520Composites/Documents/Technical%2520papers/TenCate_chopped_fiber_thermoplastics_compression_molding_technical_paper.pdf": {
|
||||
"isValid": false,
|
||||
"timestamp": 1743492156759
|
||||
"timestamp": 1743522205416
|
||||
},
|
||||
"https://youtu.be/qtZv96cifIU": {
|
||||
"isValid": true,
|
||||
@ -10509,5 +10509,15 @@
|
||||
"image": "https://cdn.prod.website-files.com/67055e139de63bfa7ce3b278/670997a273d96fadb9f33c39_INV-HERO-Table%202x.jpg",
|
||||
"favicon": "https://cdn.prod.website-files.com/67055e139de63bfa7ce3b278/67abc6213b55379f6dd37f60_favicon.ico"
|
||||
}
|
||||
},
|
||||
"https://thenounproject.com/": {
|
||||
"isValid": true,
|
||||
"timestamp": 1743523186345,
|
||||
"meta": {
|
||||
"title": "Noun Project: Free Icons & Stock Photos for Everything",
|
||||
"description": "Noun Project has the most diverse collection of free icons and stock photos. Download SVG and PNG. Over 7 million art-quality icons and free photos.",
|
||||
"image": "https://thenounproject.com/_next/static/media/row1-1-freshly-washed-batch-of-cherries-photo.3b89e0de.jpg",
|
||||
"favicon": "https://thenounproject.com/favicon-32x32.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Create a concise description for SEO meta (around 150-160 characters) from the text below. \n Disregard any links or image references. \n Return only the final meta description, no extra commentary.\n\nText to process:\nThis tutorial outlines the process of cutting HDPE sheets with an X-Carve CNC machine.\n\nWatch the full video in Spanish with subtitles: [YouTube](https://www.youtube.com/watch?v=4LrrFz802To)\n\n\nUser Location: Mexico City, Mexico\n\n### Measurement and Setup\n\nMeasure the plastic sheet's height, width, and thickness. The X-Carve CNC machine uses the Easel CAM software, which allows simulation of materials, including HDPE 2-Colors. \n\nFor reference: \n- Height: ___ meters (___ inches) \n- Width: ___ meters (___ inches) \n- Thickness: ___ millimeters (___ inches)\n\nSecure the sheet to the table using the CNC clamps from the X-Carve.\n\nProceed to a vector graphics editor like Inkscape to create or download a vector file from sites such as [The Noun Project](https://thenounproject.com).\n\nDownload the SVG file, an open-source vector format, and import it into Easel.\n\nWith the file ready, select the desired carving width and initiate the cutting process:\n\n- Ensure the sheet is secured.\n- Specify the cutting bit, such as a 1/8 inch (3.175 mm) flat flute bit.\n- Set the machine’s coordinate origin at the lower-left corner.\n- Raise the bit and activate the CNC Router.\n\n**CNC Processing of Various Materials**\n\nTypically, we cut wood, acrylic, and aluminum using the CNC. Transitioning to plastic, specifically HDPE, proved uncomplicated. The CNC Router handles HDPE with ease, outpacing wood and vastly surpassing aluminum in speed.\n\nOur primary challenge with HDPE sheets arises from uneven surfaces due to our production process, leading to inconsistent cuts and engravings. To address this, we often perform an initial CNC pass to level the sheet's surface.\n\n### Final Version\n\nTake your glasses or object, post-process them, and share the results with others."
|
||||
"content": "Return a list of max. 10 keywords that can be used for SEO purposes, separated by commas (dont comment, just the list) : \n\nText to process:\nThis guide outlines the process for cutting HDPE sheets using an X-Carve CNC machine.\n\nFor a detailed demonstration, watch the video with subtitles: [Watch Video](https://www.youtube.com/watch?v=4LrrFz802To)\n\n\nUser Location: Mexico City, Mexico\n\nFor this step, measure the plastic sheet's height, width, and thickness. The X-Carve machine uses the CAM software Easel, which is very user-friendly for CNC milling. Easel allows you to simulate your material and includes HDPE 2-Colors in its material options.\n\nUsing clamps, secure the sheet to the table.\n\nOpen a vector design program like Inkscape to create or download a vector file from a suitable online resource.\n\nDownload the SVG file and import it into Easel.\n\nWith the file ready, select the desired width for carving or cutting. Follow these steps to begin:\n\n- Ensure the sheet is secured.\n- Specify the cutting tool, using a 1/8 inch (3.175 mm) flat flute bit.\n- Set the machine's origin point to the bottom left corner.\n- Raise the bit and activate the CNC Router.\n\nAnd then the process starts!\n\nTake your glasses or object, post-process them, and show the results to friends and family.\n\nYou can attempt this project with various CNC machines, including manual routers or saws, as demonstrated in this video: [YouTube Link](https://youtu.be/gxkcffQD3eQ). Sharing your work supports community growth.\n\nPlease share your ideas and comments."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
Binary file not shown.
@ -2,6 +2,7 @@ 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";
|
||||
import { logger } from "./index.js";
|
||||
|
||||
// Type definitions
|
||||
const InstructionSchema = z.object({
|
||||
@ -18,7 +19,7 @@ export interface TemplateProps extends IKBotTask {
|
||||
disabled?: boolean;
|
||||
template?: string;
|
||||
renderer?: string;
|
||||
|
||||
|
||||
}
|
||||
|
||||
const TemplateConfigSchema = z.object({
|
||||
@ -28,14 +29,13 @@ const TemplateConfigSchema = z.object({
|
||||
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>;
|
||||
|
||||
const LLMConfigSchema = z.object({
|
||||
templates: z.record(TemplateConfigSchema),
|
||||
options: z.record(TemplateConfigSchema),
|
||||
instructions: InstructionSetSchema.optional(),
|
||||
defaults: z.record(z.array(z.string())).optional()
|
||||
});
|
||||
@ -50,120 +50,64 @@ export const enum TemplateContext {
|
||||
}
|
||||
// Default configuration
|
||||
export const DEFAULT_CONFIG: LLMConfig = {
|
||||
templates: {},
|
||||
options: {},
|
||||
instructions: {},
|
||||
defaults: {
|
||||
tone: ["formal"],
|
||||
content: ["spellCheck", "removeEmojis", "removePersonalPrefs", "shorten"],
|
||||
moderation: ["mafiaFilter", "deprogramming"],
|
||||
context: ["makerTutorials", "units"],
|
||||
format: ["markdown"]
|
||||
}
|
||||
defaults: {}
|
||||
};
|
||||
|
||||
// Default template options
|
||||
const DEFAULT_TEMPLATE_OPTIONS = {
|
||||
mode: "completion",
|
||||
preferences: "none",
|
||||
filters: "code"
|
||||
} as const;
|
||||
const getConfigPath = (context: TemplateContext): string => {
|
||||
return `./src/config/templates/${context}.json`;
|
||||
};
|
||||
|
||||
// Load configuration from JSON file
|
||||
export const load = (context: TemplateContext = TemplateContext.COMMONS): LLMConfig => {
|
||||
const configPath = `./src/config/templates/${context}.json`;
|
||||
const configPath = getConfigPath(context);
|
||||
if (exists(configPath)) {
|
||||
try {
|
||||
const content = read(configPath);
|
||||
if (typeof content === 'string') {
|
||||
const parsed = JSON.parse(content);
|
||||
return LLMConfigSchema.parse(parsed);
|
||||
}
|
||||
const content = read(configPath, 'json') || {};
|
||||
return LLMConfigSchema.parse(content)
|
||||
} catch (error) {
|
||||
console.error(`Error loading ${context} config:`, error);
|
||||
logger.error(`Error loading ${context} config:`, error);
|
||||
}
|
||||
} else {
|
||||
logger.error(`Config file ${configPath} not found`);
|
||||
}
|
||||
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[]>
|
||||
defaults: 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] ?? [])
|
||||
[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')
|
||||
format: z.enum(['text', 'json', 'markdown', 'schema']).default('text')
|
||||
});
|
||||
|
||||
type Prompt = z.infer<typeof PromptSchema>;
|
||||
|
||||
const PromptRegistrySchema = z.record(PromptSchema);
|
||||
|
||||
type PromptRegistry = z.infer<typeof PromptRegistrySchema>;
|
||||
|
||||
const DEFAULT_PROMPTS: PromptRegistry = {
|
||||
seo: {
|
||||
template: "Analyze the following content and return a JSON object with these fields: keywords (array of max 10 strings), title (string), description (string), tags (array of max 5 strings). Format as valid JSON only.",
|
||||
format: 'json'
|
||||
},
|
||||
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 references (only with links), as Markdown, grouped : Articles, Books, Papers, Youtube, Opensource Designs, ... Dont comment !",
|
||||
format: 'markdown'
|
||||
},
|
||||
brief: {
|
||||
template: "Create a concise description for SEO meta (around 150-160 characters) from the text below. \n Disregard any links or image references. \n Return only the final meta description, no extra commentary.",
|
||||
format: 'text'
|
||||
},
|
||||
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. per category.",
|
||||
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'
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
const createTemplate = (config: LLMConfig, name: string, defaults: Partial<TemplateConfig>, promptKey?: keyof PromptRegistry) => {
|
||||
const createTemplate = (config: LLMConfig, name: string, defaults: Partial<TemplateConfig>) => {
|
||||
return (opts: Partial<TemplateConfig> = {}) => {
|
||||
const template = config.templates[name] || defaults;
|
||||
const prompt = promptKey ? renderPrompt(DEFAULT_PROMPTS[promptKey], opts.variables) : buildPrompt(
|
||||
const template = config.options[name] || defaults;
|
||||
const prompt = buildPrompt(
|
||||
config.instructions || {},
|
||||
config.defaults || {},
|
||||
opts.overrides || template.overrides
|
||||
)
|
||||
config.defaults || {}
|
||||
);
|
||||
const merged = {
|
||||
...template,
|
||||
...opts,
|
||||
@ -175,86 +119,17 @@ const createTemplate = (config: LLMConfig, name: string, defaults: Partial<Templ
|
||||
|
||||
export const createTemplates = (context: TemplateContext = TemplateContext.COMMONS) => {
|
||||
const config = load(context);
|
||||
|
||||
switch (context) {
|
||||
case TemplateContext.COMMONS:
|
||||
return {
|
||||
seo: createTemplate(config, 'seo', {
|
||||
router: "openai",
|
||||
model: "gpt-4o",
|
||||
...DEFAULT_TEMPLATE_OPTIONS
|
||||
}, 'seo'),
|
||||
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: "gpt-4o",
|
||||
...DEFAULT_TEMPLATE_OPTIONS
|
||||
}, 'keywords'),
|
||||
brief: createTemplate(config, 'keywords', {
|
||||
router: "openai",
|
||||
model: "gpt-4o",
|
||||
...DEFAULT_TEMPLATE_OPTIONS
|
||||
}, 'brief'),
|
||||
references: createTemplate(config, 'references', {
|
||||
model: "perplexity/sonar-deep-research",
|
||||
...DEFAULT_TEMPLATE_OPTIONS
|
||||
}, 'references'),
|
||||
toolsAndHardware: createTemplate(config, 'toolsAndHardware', {
|
||||
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
|
||||
})
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
return Object.keys(config.options).reduce((acc, name) => ({
|
||||
...acc,
|
||||
[name]: createTemplate(config, name, {})
|
||||
}), {});
|
||||
};
|
||||
|
||||
// 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 };
|
||||
export {
|
||||
InstructionSchema,
|
||||
InstructionSetSchema,
|
||||
TemplateConfigSchema,
|
||||
LLMConfigSchema,
|
||||
PromptSchema,
|
||||
PromptRegistrySchema
|
||||
};
|
||||
@ -2,25 +2,20 @@ import { get_cached_object, set_cached_object } from "@polymech/cache"
|
||||
import { run, OptionsSchema } from "@polymech/kbot-d";
|
||||
import { resolveVariables } from "@polymech/commons/variables"
|
||||
import { } from "@polymech/core/objects"
|
||||
import { logger } from "./index.js"
|
||||
const CACHE = true
|
||||
import { logger, env } from "./index.js"
|
||||
import { removeEmptyObjects } from "@/base/objects.js"
|
||||
import { LLM_CACHE } from "@/config/config.js"
|
||||
|
||||
import {
|
||||
TemplateProps,
|
||||
TemplateContext,
|
||||
createTemplates
|
||||
} from "./kbot-templates.js";
|
||||
import { env } from "./index.js";
|
||||
|
||||
export interface Props extends TemplateProps {
|
||||
context?: TemplateContext;
|
||||
}
|
||||
|
||||
const removeEmpty = (obj: any) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([_, v]) => v != null && v !== '')
|
||||
);
|
||||
};
|
||||
|
||||
export const filter = async (content: string, tpl: string = 'howto', opts: Props = {}) => {
|
||||
if (!content || content.length < 20) {
|
||||
return content;
|
||||
@ -40,20 +35,20 @@ export const filter = async (content: string, tpl: string = 'howto', opts: Props
|
||||
filters: [],
|
||||
tools: []
|
||||
};
|
||||
const ca_options = JSON.parse(JSON.stringify(removeEmpty(cache_key_obj)));
|
||||
const ca_options = JSON.parse(JSON.stringify(removeEmptyObjects(cache_key_obj)));
|
||||
let cached = null
|
||||
try {
|
||||
cached = await get_cached_object({ ca_options }, 'kbot');
|
||||
} catch (e) {
|
||||
logger.error(`Failed to get cached object for ${content.substring(0, 20)}`, e);
|
||||
}
|
||||
if (cached && CACHE) {
|
||||
if (cached && LLM_CACHE) {
|
||||
return cached;
|
||||
}
|
||||
logger.info(`kbot: template:${tpl} : context:${context} @ ${options.model} : ${content.substring(0, 20)}`)
|
||||
const result = await run(options);
|
||||
if(!result || !result[0]){
|
||||
logger.error(`No result for ${content.substring(0, 20)}`)
|
||||
if (!result || !result[0]) {
|
||||
logger.error(`No result for ${content.substring(0, 20)}`)
|
||||
return content;
|
||||
}
|
||||
if (template.format === 'json') {
|
||||
@ -70,9 +65,8 @@ export const filter = async (content: string, tpl: string = 'howto', opts: Props
|
||||
logger.debug(`caching result for "${content.substring(0, 20)}..."`)
|
||||
return result[0] as string;
|
||||
};
|
||||
|
||||
export const template_filter = async (text: string, template: string, context: TemplateContext = TemplateContext.COMMONS) => {
|
||||
if(!text || text.length < 20) {
|
||||
if (!text || text.length < 20) {
|
||||
return text;
|
||||
}
|
||||
const templates = createTemplates(context);
|
||||
@ -96,11 +90,10 @@ export const template_filter = async (text: string, template: string, context: T
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const getFilterOptions = (content: string, template: any, opts: Props = {}) => {
|
||||
return OptionsSchema().parse({
|
||||
...template,
|
||||
prompt: `${template.prompt || ""} : ${content}`,
|
||||
...opts,
|
||||
});
|
||||
};
|
||||
};
|
||||
@ -121,7 +121,7 @@ const EditLink = () => {
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{
|
||||
howto.tags.map((tag) => (
|
||||
<span class="bg-orange-500 text-white text-xs px-3 py-1 rounded-full">
|
||||
<span class="bg-orange-400 text-white text-xs px-3 py-1 rounded-full">
|
||||
<Translate>{tag.toUpperCase()}</Translate>
|
||||
</span>
|
||||
))
|
||||
|
||||
@ -4,6 +4,10 @@ import { resolve, template } from '@polymech/commons'
|
||||
import { sync as read } from '@polymech/fs/read'
|
||||
import { sanitizeUri } from 'micromark-util-sanitize-uri'
|
||||
|
||||
|
||||
// LLM
|
||||
export const LLM_CACHE = false
|
||||
|
||||
export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
|
||||
|
||||
export const LOGGING_NAMESPACE = 'polymech-site'
|
||||
@ -33,7 +37,7 @@ export const HOWTO_ADD_REFERENCES = true
|
||||
export const HOWTO_COMPLETE_SKILLS = false
|
||||
export const HOWTO_LOCAL_RESOURCES = false
|
||||
export const HOWTO_SEO_LLM = true
|
||||
export const HOWTO_MAX_ITEMS = 10
|
||||
export const HOWTO_MAX_ITEMS = 1
|
||||
|
||||
export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json"))
|
||||
export const HOWTO_ROOT_INTERN = () => path.resolve(resolve("./public/resources/howtos"))
|
||||
@ -56,7 +60,7 @@ export const DIRECTORY_ADD_REFERENCES = true
|
||||
export const DIRECTORY_COMPLETE_SKILLS = false
|
||||
export const DIRECTORY_LOCAL_RESOURCES = false
|
||||
export const DIRECTORY_SEO_LLM = true
|
||||
export const DIRECTORY_MAX_ITEMS = 20
|
||||
export const DIRECTORY_MAX_ITEMS = 0
|
||||
|
||||
export const DIRECTORY_MIGRATION = () => path.resolve(resolve("./data/last.json"))
|
||||
export const DIRECTORY_ROOT_INTERN = () => path.resolve(resolve("./public/resources/directory"))
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"templates": {
|
||||
"options": {
|
||||
"simple": {
|
||||
"router": "openai",
|
||||
"model": "gpt-4o",
|
||||
@ -7,11 +7,6 @@
|
||||
"mode": "completion",
|
||||
"filters": "code"
|
||||
},
|
||||
"codeSimple": {
|
||||
"model": "gpt-4o",
|
||||
"preferences": "none",
|
||||
"mode": "completion"
|
||||
},
|
||||
"research": {
|
||||
"model": "perplexity/sonar-deep-research",
|
||||
"preferences": "none",
|
||||
@ -21,7 +16,7 @@
|
||||
"instructions": {
|
||||
"tone": [
|
||||
{ "flag": "formal", "text": "use a formal tone" },
|
||||
{ "flag": "friendly", "text": "be friendly and approachable" }
|
||||
{ "flag": "friendly", "text": "Be friendly and approachable!" }
|
||||
],
|
||||
"content": [
|
||||
{ "flag": "spellCheck", "text": "spell & grammar fix the text," },
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"templates": {
|
||||
"options": {
|
||||
"keywords": {
|
||||
"router": "openai",
|
||||
"model": "gpt-4o",
|
||||
@ -25,7 +25,7 @@
|
||||
"model": "perplexity/sonar-deep-research",
|
||||
"preferences": "none",
|
||||
"mode": "completion",
|
||||
"prompt": "Extract the required tools, software hardware from the following tutorial.Return as Markdown chapters (H3) with very short bullet points (not bold), max. 5 per category.",
|
||||
"prompt": "Extract the required tools: software, hardware from the following tutorial.Return as Markdown chapters (H3) with very short bullet points (not bold), max. 5 per category.",
|
||||
"filters": "code"
|
||||
},
|
||||
"requiredSkills": {
|
||||
@ -59,13 +59,10 @@
|
||||
{ "flag": "safety", "text": "Include safety considerations and warnings" },
|
||||
{ "flag": "troubleshooting", "text": "Include common issues and solutions" }
|
||||
],
|
||||
"format": [
|
||||
{ "flag": "stepByStep", "text": "Format as clear step-by-step instructions" },
|
||||
{ "flag": "checklist", "text": "Include progress checkpoints" }
|
||||
]
|
||||
"format": []
|
||||
},
|
||||
"defaults": {
|
||||
"context": ["makerTutorials", "units", "safety", "troubleshooting"],
|
||||
"format": ["stepByStep", "checklist"]
|
||||
"format": []
|
||||
}
|
||||
}
|
||||
@ -3,14 +3,11 @@ import { findUp } from 'find-up'
|
||||
|
||||
import pMap from 'p-map'
|
||||
import { sanitizeFilename } from "@polymech/fs/utils"
|
||||
import { execFileSync, execFile } from "child_process";
|
||||
import { execFile } from "child_process";
|
||||
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 { sync as write } from '@polymech/fs/write'
|
||||
import type { Loader, LoaderContext } from 'astro/loaders'
|
||||
import { resolveVariables } from "@polymech/commons/variables"
|
||||
export * from './howto-model.js'
|
||||
export * from '@/base/filters.js'
|
||||
|
||||
@ -27,7 +24,6 @@ import {
|
||||
HOWTO_FILES_WEB,
|
||||
HOWTO_FILES_ABS,
|
||||
HOWTO_FILTER_LLM,
|
||||
|
||||
default_image,
|
||||
HOWTO_ROOT,
|
||||
HOWTO_GLOB,
|
||||
@ -43,11 +39,7 @@ import {
|
||||
HOWTO_MAX_ITEMS
|
||||
} from "config/config.js"
|
||||
|
||||
|
||||
|
||||
|
||||
import { env, logger } from '@/base/index.js'
|
||||
|
||||
import { logger } from '@/base/index.js'
|
||||
import { applyFilters, default_filters_plain, FilterFunction } from '../../base/filters.js'
|
||||
import { TemplateContext, buildPrompt, LLMConfig, createTemplates } from '@/base/kbot-templates.js';
|
||||
import { template_filter } from '@/base/kbot.js'
|
||||
@ -57,7 +49,6 @@ import { template_filter } from '@/base/kbot.js'
|
||||
// Assets
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export const item_path = (item: IHowto) => `${HOWTO_ROOT()}/${item.slug}`
|
||||
export const asset_local_abs = async (item: IHowto, asset: IImage) => {
|
||||
const sanitizedFilename = sanitizeFilename(asset.name)
|
||||
@ -465,10 +456,7 @@ export function loader(): Loader {
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
export const group_by_cat = (items: IHowto[]) => {
|
||||
return items.reduce((acc: Record<string, IHowto[]>, item: IHowto) => {
|
||||
const category = item?.category?.label || 'uncategorized'
|
||||
if (category === 'uncategorized') {
|
||||
return acc
|
||||
}
|
||||
const category = item?.category?.label || 'Uncategorized'
|
||||
if (!acc[category]) {
|
||||
acc[category] = []
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ const items = (await getCollection(view)).map(
|
||||
const groups = group_by_cat(items as any);
|
||||
const locale = Astro.currentLocale;
|
||||
const test = {};
|
||||
console.log(Object.keys(groups))
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return LANGUAGES.map((lang) => ({
|
||||
@ -46,8 +47,6 @@ export async function getStaticPaths() {
|
||||
<ListItem
|
||||
url={`/${locale}/howtos/${item.slug}`}
|
||||
title={item.title}
|
||||
price={item.price}
|
||||
type={item.type}
|
||||
alt={item.title}
|
||||
model={{item:item}}
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user