generated from polymech/site-template
kbot lazy
This commit is contained in:
parent
90a2b1c907
commit
529d754e0a
@ -1,9 +1,9 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"model": "google/gemini-exp-1206:free",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "use a formal tone\nspell check the text, fix any errors\nremove emojis\nremove personal preferences or biases\nshorten text if possible but preserve personality\nremove references to preciousplastic, bazar and Discord\nremove any brain/green washing as well suggestions not related to the craft\nContext: howto tutorials, for makers\ndont comment just return as Markdown : An experience review designing a broom hanger mold: These are some steps to design a mold for the injection machine."
|
||||
"content": "Return a list of useful references (only with links), as Markdown, grouped : Articles, Books, Papers, Youtube, Opensource Designs, ... Dont comment ! : Injection mold design, broom hanger mold, plastic injection, mold making, CAD mold design, DIY injection mold, injection molding process, mold design criteria, plastic product design, custom mold design\n"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
@ -2,6 +2,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 { createMarkdownComponent } from "@/base/index.js";
|
||||
import { filters } from "@/model/howto.js";
|
||||
|
||||
export interface Props extends IKBotTask {
|
||||
language?: string;
|
||||
@ -12,6 +13,10 @@ export interface Props extends IKBotTask {
|
||||
renderer?: string;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Templates
|
||||
|
||||
export const template_simple = () => {
|
||||
return {
|
||||
router: "openai",
|
||||
@ -21,6 +26,27 @@ export const template_simple = () => {
|
||||
};
|
||||
}
|
||||
|
||||
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",
|
||||
@ -36,6 +62,11 @@ export const template_research = () => {
|
||||
mode: "completion",
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filters
|
||||
|
||||
export enum ToneFlags {
|
||||
None = 0,
|
||||
Formal = 1,
|
||||
@ -128,7 +159,7 @@ export function templateLanguage(
|
||||
model: "gpt-4o-mini",
|
||||
preferences: "none",
|
||||
mode: "completion",
|
||||
prompt,
|
||||
prompt,
|
||||
filters: "code"
|
||||
}
|
||||
}
|
||||
@ -137,7 +168,9 @@ export const templates = {
|
||||
simple: template_simple,
|
||||
code_simple: template_code_simple,
|
||||
research: template_research,
|
||||
howto: templateLanguage
|
||||
howto: templateLanguage,
|
||||
keywords: keywords_simple,
|
||||
references: references_simple
|
||||
}
|
||||
|
||||
export const filter = async (content: string, tpl: string = 'howto', opts: any = {}) => {
|
||||
@ -153,4 +186,40 @@ export const filter = async (content: string, tpl: string = 'howto', opts: any =
|
||||
let result: string | unknown[] = [];
|
||||
result = await run(options);
|
||||
return result[0] as string;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Completion
|
||||
export const extract = async (content: string, tpl: string = 'keywords', opts: any = {}) => {
|
||||
if (!content || content.length < 20 || templates[tpl] === undefined) {
|
||||
return content
|
||||
}
|
||||
const template = templates[tpl]();
|
||||
const options = OptionsSchema().parse({
|
||||
...template,
|
||||
prompt: `${template.prompt || ""} : ${content}`,
|
||||
...opts,
|
||||
});
|
||||
let result: string | unknown[] = [];
|
||||
result = await run(options);
|
||||
return result[0] as string;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Completion
|
||||
export const references = async (content: string, tpl: string = 'references', opts: any = {}) => {
|
||||
if (!content || content.length < 20 || templates[tpl] === undefined) {
|
||||
return content
|
||||
}
|
||||
const template = templates[tpl]();
|
||||
const options = OptionsSchema().parse({
|
||||
...template,
|
||||
prompt: `${template.prompt || ""} : ${content}`,
|
||||
...opts,
|
||||
});
|
||||
let result: string | unknown[] = [];
|
||||
result = await run(options);
|
||||
return result[0] as string;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { decode } from 'html-entities'
|
||||
import { decode } from "html-entities";
|
||||
import { IHowto, asset_local_rel } from "@/model/howto.js";
|
||||
import { Img } from "imagetools/components";
|
||||
import { i18n as Translate } from "@polymech/astro-base";
|
||||
@ -12,9 +12,10 @@ import { files, forward_slash } from "@polymech/commons";
|
||||
import pMap from "p-map";
|
||||
import { sync as exists } from "@polymech/fs/exists";
|
||||
import { sync as read } from "@polymech/fs/read";
|
||||
import { createMarkdownComponent } from "@/base/index.js";
|
||||
import { createHTMLComponent, createMarkdownComponent } from "@/base/index.js";
|
||||
import { translate } from "@/base/i18n.js";
|
||||
import { applyFilters, shortenUrl } from "@/model/howto.js";
|
||||
import { extract, references } from "@/base/kbot.js";
|
||||
import {
|
||||
HOWTO_FILES_WEB,
|
||||
HOWTO_FILES_ABS,
|
||||
@ -28,7 +29,7 @@ const { howto } = Astro.props;
|
||||
const howto_abs = HOWTO_FILES_ABS(howto.slug);
|
||||
const howto_resources_default = `# Resources`;
|
||||
const howto_resources_path = path.join(howto_abs, "resources.md");
|
||||
const howto_resources = exists(howto_resources_path)
|
||||
let howto_resources = exists(howto_resources_path)
|
||||
? read(howto_resources_path, "string") || howto_resources_default
|
||||
: howto_resources_default;
|
||||
|
||||
@ -36,15 +37,19 @@ let model_files: any = [...files(howto_abs, "**/**/*.(step|stp)")];
|
||||
model_files = model_files.map((f) =>
|
||||
forward_slash(`${howto.slug}/${path.relative(path.resolve(howto_abs), f)}`),
|
||||
);
|
||||
const content = async (str: string) =>{
|
||||
const content = async (str: string) => {
|
||||
const ret = await translate(
|
||||
await applyFilters(str),
|
||||
I18N_SOURCE_LANGUAGE,
|
||||
Astro.currentLocale,
|
||||
);
|
||||
return ret
|
||||
}
|
||||
const component = async (str: string) => await createMarkdownComponent(await content(str))
|
||||
return ret;
|
||||
};
|
||||
const component = async (str: string) =>
|
||||
await createMarkdownComponent(await content(str));
|
||||
|
||||
const componentHTML = async (str: string) =>
|
||||
await createHTMLComponent(await content(str));
|
||||
|
||||
const stepsWithFilteredMarkdown = await pMap(
|
||||
howto.steps,
|
||||
@ -56,16 +61,21 @@ const stepsWithFilteredMarkdown = await pMap(
|
||||
);
|
||||
|
||||
const Description = component(howto.description);
|
||||
const Resources = component(howto_resources);
|
||||
|
||||
const authorGeo = howto?.user?.geo || {
|
||||
countryName: "Unknown",
|
||||
data: { urls: [] },
|
||||
};
|
||||
|
||||
const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
(l) => !l.url.includes("one_army") && !l.url.includes("bazar")
|
||||
(l) => !l.url.includes("one_army") && !l.url.includes("bazar"),
|
||||
);
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Add references from the extracted keywords
|
||||
const contentAll = `${howto.title} \n Description : ${howto.description} \n Steps: ${howto.steps.map((s) => s.text).join("\n")} \n`;
|
||||
const keywords = await extract(contentAll) as string;
|
||||
const references_extra = await references(keywords);
|
||||
howto_resources = `${howto_resources}\n${references_extra}`;
|
||||
const Resources = component(howto_resources);
|
||||
---
|
||||
|
||||
<BaseLayout class="markdown-content bg-gray-100">
|
||||
@ -75,7 +85,7 @@ const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
<h1 class="text-4xl font-bold text-gray-800 mb-4">
|
||||
<Translate>{howto.title}</Translate>
|
||||
</h1>
|
||||
<GalleryK images={[{ src: howto.cover_image.src, alt:'' }]} />
|
||||
<GalleryK images={[{ src: howto.cover_image.src, alt: "" }]} />
|
||||
<div class="flex gap-2 mb-4">
|
||||
{
|
||||
howto.tags.map((tag) => (
|
||||
@ -105,25 +115,44 @@ const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
<strong><Translate>Creator:</Translate></strong>{howto._createdBy}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Country:</Translate></strong>{authorGeo.countryName}
|
||||
<strong><Translate>Country:</Translate></strong>{
|
||||
authorGeo.countryName
|
||||
}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Email:</Translate></strong>
|
||||
<a class="text-orange-600 underline" href={`mailto:${authorLinks.find((link)=>link.name.toLowerCase()==="email")?.url.replace("mailto:","")}`}>
|
||||
{authorLinks.find((link)=>link.name.toLowerCase()==="email")?.url.replace("mailto:","")}
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={`mailto:${authorLinks.find((link) => link.name.toLowerCase() === "email")?.url.replace("mailto:", "")}`}
|
||||
>
|
||||
{
|
||||
authorLinks
|
||||
.find((link) => link.name.toLowerCase() === "email")
|
||||
?.url.replace("mailto:", "")
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
{
|
||||
authorLinks.filter((l)=>l.name.toLowerCase()!=="email").map((link)=>(
|
||||
<li>
|
||||
<strong>{link.name}:</strong>
|
||||
<a class="text-orange-600 underline" href={link.url} target="_blank">
|
||||
{shortenUrl(link.url)}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
authorLinks
|
||||
.filter((l) => l.name.toLowerCase() !== "email")
|
||||
.map((link) => (
|
||||
<li>
|
||||
<strong>{link.name}:</strong>
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
>
|
||||
{shortenUrl(link.url)}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li><strong><Translate>Downloads:</Translate></strong>{howto.total_downloads}</li>
|
||||
<li>
|
||||
<strong><Translate>Downloads:</Translate></strong>{
|
||||
howto.total_downloads
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@ -131,41 +160,64 @@ const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
<div class="mb-8 markdown-content">
|
||||
<Description />
|
||||
</div>
|
||||
<a href={HOWTO_FILES_WEB(howto.slug)} class="inline-block py-2 px-4 bg-orange-600 hover:bg-orange-700 text-white rounded-full mb-8"><Translate>Browse Files</Translate></a>
|
||||
<a
|
||||
href={HOWTO_FILES_WEB(howto.slug)}
|
||||
class="inline-block py-2 px-4 bg-orange-600 hover:bg-orange-700 text-white rounded-full mb-8"
|
||||
><Translate>Browse Files</Translate></a
|
||||
>
|
||||
</section>
|
||||
|
||||
<section class="px-8 py-8 bg-orange-50">
|
||||
<h2 class="font-bold mb-4 text-xl"><Translate>Table of Contents</Translate></h2>
|
||||
<h2 class="font-bold mb-4 text-xl">
|
||||
<Translate>Table of Contents</Translate>
|
||||
</h2>
|
||||
<ul class="grid grid-cols-1 md:grid-cols-2 gap-2 list-decimal p-4">
|
||||
{stepsWithFilteredMarkdown.map((step, idx)=>(
|
||||
<li>
|
||||
<a href={`#step-${idx+1}`} class="text-orange-600 hover:underline">
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
{
|
||||
stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li>
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="border-t border-gray-300 bg-gray-50 p-8">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6"><Translate>Steps</Translate></h2>
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-6">
|
||||
<Translate>Steps</Translate>
|
||||
</h2>
|
||||
<ol class="space-y-10">
|
||||
{stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li id={`step-${idx + 1}`} class="bg-white shadow-sm rounded-lg p-6">
|
||||
<div class="mb-4 flex items-center">
|
||||
<span class="bg-orange-600 text-xl font-bold text-white rounded-full h-10 w-10 flex items-center justify-center mr-3">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<h3 class="text-xl font-bold">
|
||||
<a href={`#step-${idx + 1}`} class="text-orange-600 hover:underline"><Translate>{step.title}</Translate></a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="markdown-content">
|
||||
<step.filteredMarkdownComponent />
|
||||
</div>
|
||||
{step.images?.length > 0 && <GalleryK images={step.images} />}
|
||||
</li>
|
||||
))}
|
||||
{
|
||||
stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li
|
||||
id={`step-${idx + 1}`}
|
||||
class="bg-white shadow-sm rounded-lg p-6"
|
||||
>
|
||||
<div class="mb-4 flex items-center">
|
||||
<span class="bg-orange-600 text-xl font-bold text-white rounded-full h-10 w-10 flex items-center justify-center mr-3">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<h3 class="text-xl font-bold">
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="markdown-content">
|
||||
<step.filteredMarkdownComponent />
|
||||
</div>
|
||||
{step.images?.length > 0 && <GalleryK images={step.images} />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
@ -173,9 +225,16 @@ const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
|
||||
<footer class="p-8 text-sm border-t bg-white text-gray-600">
|
||||
<div class="flex justify-between">
|
||||
<span><Translate>Created on</Translate>: {new Date(howto._created).toLocaleDateString()}</span>
|
||||
<span>{howto.votedUsefulBy.length} <Translate>people found this useful</Translate></span>
|
||||
<span
|
||||
><Translate>Created on</Translate>: {
|
||||
new Date(howto._created).toLocaleDateString()
|
||||
}</span
|
||||
>
|
||||
<span
|
||||
>{howto.votedUsefulBy.length}
|
||||
<Translate>people found this useful</Translate></span
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</Wrapper>
|
||||
</BaseLayout>
|
||||
</BaseLayout>
|
||||
|
||||
@ -78,6 +78,7 @@ export const asset_local_rel = async (item: IHowto, asset: Image) => {
|
||||
}
|
||||
return default_image().src
|
||||
}
|
||||
|
||||
export const howtos = async () => {
|
||||
const src = HOWTO_MIGRATION()
|
||||
const data = read(src, 'json') as any;
|
||||
@ -99,11 +100,9 @@ export const howtos = async () => {
|
||||
label: 'uncategorized'
|
||||
}
|
||||
})
|
||||
|
||||
howtos = howtos.filter((h) => {
|
||||
return h.steps.length > 0 && !blacklist.includes(h._createdBy);
|
||||
});
|
||||
|
||||
return howtos
|
||||
}
|
||||
|
||||
@ -194,9 +193,11 @@ export function loader(): Loader {
|
||||
load
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//
|
||||
// Utils
|
||||
// Filters
|
||||
|
||||
const urlBlacklist = ["thenounproject.com", "preciousplastic.com"];
|
||||
const bannedWords = ["wizard", "magic2"];
|
||||
const wordReplaceMap: Record<string, string> = {
|
||||
@ -252,6 +253,7 @@ export async function applyFilters(text: string): Promise<string> {
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//
|
||||
// Interfaces - Old
|
||||
|
||||
Loading…
Reference in New Issue
Block a user