diff --git a/.gitignore b/.gitignore index 42df66144d0f..f45969e65a08 100644 --- a/.gitignore +++ b/.gitignore @@ -181,4 +181,9 @@ dotCMS/dependencies.gradle .nx/ .cursorrules .cursorignore -**/vite.config.mts.timestamp-* \ No newline at end of file +**/vite.config.mts.timestamp-* + +# DEV PURPOSES +/core-web/apps/dotcms-ui/src/environments/* +/core-web/apps/dotcms-ui/src/environments/environment.ts +/apps/dotcms-ui/src/environments/environment.ts \ No newline at end of file diff --git a/ai-api/.env.example b/ai-api/.env.example new file mode 100644 index 000000000000..9863096d1ff9 --- /dev/null +++ b/ai-api/.env.example @@ -0,0 +1,2 @@ +OPENAI_API_KEY= +DEEPSEEK_API_KEY= \ No newline at end of file diff --git a/ai-api/.gitignore b/ai-api/.gitignore new file mode 100644 index 000000000000..3224a903eae9 --- /dev/null +++ b/ai-api/.gitignore @@ -0,0 +1,144 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node \ No newline at end of file diff --git a/ai-api/convert.ts b/ai-api/convert.ts new file mode 100644 index 000000000000..6c793431e1c6 --- /dev/null +++ b/ai-api/convert.ts @@ -0,0 +1,15 @@ +import { JSONSchemaToZod } from "@dmitryrechkin/json-schema-to-zod"; +import { z, ZodObject } from "zod"; + +export function convertJsonToZodSchema(json: any) { + + const zodSchema = JSONSchemaToZod.convert(json) as ZodObject; + + const newSchema = zodSchema.extend({ + category: z.enum(["Option 1", "Option 2", "Option 3"]) + }) + + + + return newSchema; +} \ No newline at end of file diff --git a/ai-api/index.ts b/ai-api/index.ts new file mode 100644 index 000000000000..56a1ea4f7009 --- /dev/null +++ b/ai-api/index.ts @@ -0,0 +1,87 @@ +import { serve } from "@hono/node-server"; +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { prettyJSON } from "hono/pretty-json"; +import { RunnableSequence } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { ChatPromptTemplate } from "@langchain/core/prompts"; +import { z } from "zod"; + +import "dotenv/config"; + +import { ChatOpenAI } from "@langchain/openai"; +import { convertJsonToZodSchema } from "./convert"; + +const model = new ChatOpenAI({ model: "gpt-4o-mini", }); + +const app = new Hono(); + +app.use("*", cors()); +app.use("*", prettyJSON()); + + +app.get('/', async (c) => { + const response = await model.invoke('Hello') + + return c.json(response.content) + }) + + app.post('/ai/content-generator', async (c) => { + + const { topic, tone, language, schema } = await c.req.json(); + + const promptTemplate = ChatPromptTemplate.fromMessages([ + ["system", "You are a content genetor. You will be given a topic, a tone, and a language. You will need to generate a contentlet based on the topic, tone, and language." ], + ["user", "Generate a contentlet based on the following topic: {topic}, tone: {tone}, and language: {language}"], + ]); + + + + const zodSchema = convertJsonToZodSchema(schema); + + + + const structuredLlm = model.withStructuredOutput(zodSchema); + + const chain = RunnableSequence.from([ + promptTemplate, + structuredLlm, + ]); + + const result = await chain.invoke({topic, tone, language}); + + return c.json({result}) + }) + + app.post('/ai/refine', async (c) => { + const { text, tone, language, system } = await c.req.json(); + + + const promptTemplate = ChatPromptTemplate.fromMessages([ + ["system", system], + ["user", "Refine this text {text} in this tone: {tone} and language: {language}"], + ]); + + const structuredLlm = model.withStructuredOutput(z.object({ + title: z.string(), + })); + + const chain = RunnableSequence.from([ + promptTemplate, + structuredLlm, + ]); + + const result = await chain.invoke({ + text, + tone, + language + }); + + return c.json(result) + }) + + + serve({ + fetch: app.fetch, + port: 3000, + }); \ No newline at end of file diff --git a/ai-api/package-lock.json b/ai-api/package-lock.json new file mode 100644 index 000000000000..cdba0b810100 --- /dev/null +++ b/ai-api/package-lock.json @@ -0,0 +1,1225 @@ +{ + "name": "ai-api", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ai-api", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@dmitryrechkin/json-schema-to-zod": "^1.0.0", + "@hono/node-server": "^1.13.8", + "@langchain/deepseek": "^0.0.1", + "@langchain/openai": "^0.4.2", + "dotenv": "^16.4.7", + "hono": "^4.6.20", + "tsx": "^4.19.2" + }, + "devDependencies": { + "json-schema-to-ts": "^3.1.1" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT", + "peer": true + }, + "node_modules/@dmitryrechkin/json-schema-to-zod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@dmitryrechkin/json-schema-to-zod/-/json-schema-to-zod-1.0.0.tgz", + "integrity": "sha512-avV26RC8CRzhnL6AvQsURlkd071SXlcPURxiYFsRLpsMoDDXBBGJDIsNQTvYmevq31WHYdwGCKGgQKC0YIjDGg==", + "dependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.13.8.tgz", + "integrity": "sha512-fsn8ucecsAXUoVxrUil0m13kOEq4mkX4/4QozCqmY+HpGfKl74OYSn8JcMA8GnG0ClfdRI4/ZSeG7zhFaVg+wg==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@langchain/core": { + "version": "0.3.38", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.38.tgz", + "integrity": "sha512-o7mowk/0oIsYsPxRAJ3TKX6OG674HqcaNRged0sxaTegLAMyZDBDRXEAt3qoe5UfkHnqXAggDLjNVDhpMwECmg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": ">=0.2.8 <0.4.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/deepseek": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@langchain/deepseek/-/deepseek-0.0.1.tgz", + "integrity": "sha512-jgrbitvV4p7Kqo/Fyni9coCliNXUrJ2XChdR8eHvQg3RL+w13DIQjJn2mrkCrb7v6Is1rI7It2x3yIbADL71Yg==", + "license": "MIT", + "dependencies": { + "@langchain/openai": "^0.4.2", + "zod": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.0 <0.4.0" + } + }, + "node_modules/@langchain/openai": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.4.2.tgz", + "integrity": "sha512-Cuj7qbVcycALTP0aqZuPpEc7As8cwiGaU21MhXRyZFs+dnWxKYxZ1Q1z4kcx6cYkq/I+CNwwmk+sP+YruU73Aw==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.77.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.29 <0.4.0" + } + }, + "node_modules/@types/node": { + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT", + "peer": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/console-table-printer": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz", + "integrity": "sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "simple-wcswidth": "^1.0.1" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT", + "peer": true + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hono": { + "version": "4.6.20", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.6.20.tgz", + "integrity": "sha512-5qfNQeaIptMaJKyoJ6N/q4gIq0DBp2FCRaLNuUI3LlJKL4S37DY/rLL1uAxA4wrPB39tJ3s+f7kgI79O4ScSug==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/js-tiktoken": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.18.tgz", + "integrity": "sha512-hFYx4xYf6URgcttcGvGuOBJhTxPYZ2R5eIesqCaNRJmYH8sNmsfTeWg4yu//7u1VD/qIUkgKJTpGom9oHXmB4g==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/langsmith": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.6.tgz", + "integrity": "sha512-FXWbZOZPZsjNfY5DKOO0ORlPhBdysj11cHpO13qf94+R022Rkt+h5YPmiEDqrBI62X4j0mvjLrJ6VN6/HSbPig==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "peer": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai": { + "version": "4.83.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.83.0.tgz", + "integrity": "sha512-fmTsqud0uTtRKsPC7L8Lu55dkaTwYucqncDHzVvO64DKOpNTuiYwjbR/nVgpapXuYy8xSnhQQPUm+3jQaxICgw==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-wcswidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", + "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==", + "license": "MIT", + "peer": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/ai-api/package.json b/ai-api/package.json new file mode 100644 index 000000000000..df2bad95c3a7 --- /dev/null +++ b/ai-api/package.json @@ -0,0 +1,24 @@ +{ + "name": "ai-api", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "dev": "tsx watch index.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@dmitryrechkin/json-schema-to-zod": "^1.0.0", + "@hono/node-server": "^1.13.8", + "@langchain/deepseek": "^0.0.1", + "@langchain/openai": "^0.4.2", + "dotenv": "^16.4.7", + "hono": "^4.6.20", + "tsx": "^4.19.2" + }, + "devDependencies": { + "json-schema-to-ts": "^3.1.1" + } +} diff --git a/core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.html b/core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.html index 88512c519d27..a06f0d6344d0 100644 --- a/core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.html +++ b/core-web/libs/block-editor/src/lib/components/dot-block-editor/dot-block-editor.component.html @@ -11,7 +11,7 @@ (ngModelChange)="onBlockEditorChange($event)" [editor]="editor" [ngClass]="{ 'overflow-hidden': freezeScroll }" - [ngModel]="content" /> + [ngModel]="content()" /> @if (showCharData) { (''); public contentletIdentifier: string; editor: Editor; subject = new Subject(); @@ -487,20 +488,32 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy, ControlValueA } private setEditorJSONContent(content: Content) { - this.content = - this.allowedBlocks?.length > 1 + this.content.set(this.allowedBlocks?.length > 1 ? removeInvalidNodes(content, this.allowedBlocks) - : content; + : content); } private setEditorContent(content: Content) { - if (typeof content === 'string') { - this.content = formatHTML(content); + let isParsable = false + let parsedContent: JSONContent; + + try { + parsedContent = JSON.parse(content as unknown as string); + isParsable = true; + + } catch (e) { + isParsable = false; + parsedContent = content as unknown as JSONContent; + } + + if (!isParsable && typeof content === 'string') { + this.content.set(formatHTML(content)); return; } - this.setEditorJSONContent(content); + this.setEditorJSONContent(parsedContent); + } private setFieldVariable() { diff --git a/core-web/libs/data-access/src/lib/dot-ai/dot-ai.service.ts b/core-web/libs/data-access/src/lib/dot-ai/dot-ai.service.ts index 87e252f9ec0d..9430643e5933 100644 --- a/core-web/libs/data-access/src/lib/dot-ai/dot-ai.service.ts +++ b/core-web/libs/data-access/src/lib/dot-ai/dot-ai.service.ts @@ -167,4 +167,13 @@ export class DotAiService { ) ); } + + refineText(data: { + system: string; + text: string; + tone: string; + language: string; + }) { + return this.#http.post(`http://localhost:3000/ai/refine`, data); + } } diff --git a/core-web/libs/edit-content-bridge/vite.config.ts.timestamp-1738871436722-9f722bf342002.mjs b/core-web/libs/edit-content-bridge/vite.config.ts.timestamp-1738871436722-9f722bf342002.mjs new file mode 100644 index 000000000000..6b4dfbb53369 --- /dev/null +++ b/core-web/libs/edit-content-bridge/vite.config.ts.timestamp-1738871436722-9f722bf342002.mjs @@ -0,0 +1,19 @@ +// libs/edit-content-bridge/vite.config.ts +import { defineConfig } from "file:///Users/nicobytes1/Code/dotcms/core/core-web/node_modules/vite/dist/node/index.js"; +import { resolve } from "path"; +var __vite_injected_original_dirname = "/Users/nicobytes1/Code/dotcms/core/core-web/libs/edit-content-bridge"; +var vite_config_default = defineConfig({ + build: { + lib: { + entry: resolve(__vite_injected_original_dirname, "src/iife.ts"), + name: "DotCustomFieldApi", + formats: ["iife"], + fileName: () => "edit-content-bridge.js" + }, + minify: true + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibGlicy9lZGl0LWNvbnRlbnQtYnJpZGdlL3ZpdGUuY29uZmlnLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL25pY29ieXRlczEvQ29kZS9kb3RjbXMvY29yZS9jb3JlLXdlYi9saWJzL2VkaXQtY29udGVudC1icmlkZ2VcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy9uaWNvYnl0ZXMxL0NvZGUvZG90Y21zL2NvcmUvY29yZS13ZWIvbGlicy9lZGl0LWNvbnRlbnQtYnJpZGdlL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9uaWNvYnl0ZXMxL0NvZGUvZG90Y21zL2NvcmUvY29yZS13ZWIvbGlicy9lZGl0LWNvbnRlbnQtYnJpZGdlL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSc7XG5cbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tICdwYXRoJztcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgICBidWlsZDoge1xuICAgICAgICBsaWI6IHtcbiAgICAgICAgICAgIGVudHJ5OiByZXNvbHZlKF9fZGlybmFtZSwgJ3NyYy9paWZlLnRzJyksXG4gICAgICAgICAgICBuYW1lOiAnRG90Q3VzdG9tRmllbGRBcGknLFxuICAgICAgICAgICAgZm9ybWF0czogWydpaWZlJ10sXG4gICAgICAgICAgICBmaWxlTmFtZTogKCkgPT4gJ2VkaXQtY29udGVudC1icmlkZ2UuanMnXG4gICAgICAgIH0sXG4gICAgICAgIG1pbmlmeTogdHJ1ZVxuICAgIH1cbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE4WCxTQUFTLG9CQUFvQjtBQUUzWixTQUFTLGVBQWU7QUFGeEIsSUFBTSxtQ0FBbUM7QUFJekMsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDeEIsT0FBTztBQUFBLElBQ0gsS0FBSztBQUFBLE1BQ0QsT0FBTyxRQUFRLGtDQUFXLGFBQWE7QUFBQSxNQUN2QyxNQUFNO0FBQUEsTUFDTixTQUFTLENBQUMsTUFBTTtBQUFBLE1BQ2hCLFVBQVUsTUFBTTtBQUFBLElBQ3BCO0FBQUEsSUFDQSxRQUFRO0FBQUEsRUFDWjtBQUNKLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg== diff --git a/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.html b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.html new file mode 100644 index 000000000000..f30cb6b04576 --- /dev/null +++ b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.html @@ -0,0 +1,38 @@ + + + + + + + +
+ Hello. I'm dotAI what do you want me to generate? +
+
+ + +
+
diff --git a/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.scss b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.scss new file mode 100644 index 000000000000..ed4a64a4733c --- /dev/null +++ b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.scss @@ -0,0 +1,36 @@ +@use "variables" as *; + +:host { + position: absolute; + right: 30px; + bottom: 20px; + z-index: 20; + + ::ng-deep { + .ai-button { + width: 3.5rem; + height: 3.5rem; + box-shadow: 0px 10px 20px 0px hsla(230, 13%, 9%, 0.3); + + &:hover { + transform: scale(1.1); + transition: transform 0.3s ease-in-out; + } + } + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.rotate-clippy { + animation: rotate 1s linear infinite; + transform-origin: center; + transform-box: fill-box; +} diff --git a/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.spec.ts b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.spec.ts new file mode 100644 index 000000000000..fffdcb7ffabf --- /dev/null +++ b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DotAiClippyContentGeneratorComponent } from './dot-ai-clippy-content-generator.component'; + +describe('DotAiClippyContentGeneratorComponent', () => { + let component: DotAiClippyContentGeneratorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DotAiClippyContentGeneratorComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(DotAiClippyContentGeneratorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.ts b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.ts new file mode 100644 index 000000000000..4cf70c44af34 --- /dev/null +++ b/core-web/libs/edit-content/src/lib/components/dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component.ts @@ -0,0 +1,32 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, input, model, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ButtonModule } from 'primeng/button'; +import { InputTextareaModule } from 'primeng/inputtextarea'; +import { OverlayPanelModule } from 'primeng/overlaypanel'; + +@Component({ + selector: 'dot-ai-clippy-content-generator', + standalone: true, + imports: [CommonModule, InputTextareaModule, OverlayPanelModule, ButtonModule, FormsModule], + templateUrl: './dot-ai-clippy-content-generator.component.html', + styleUrl: './dot-ai-clippy-content-generator.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DotAiClippyContentGeneratorComponent { + protected readonly messages = [ + { from: 'Chat', message: "Hello! I'm the dotAI CLIPPY Chat, how can I help you today?" } + ]; + + message = model(''); + + isClippyThinking = input(false); + + @Output() generateContent = new EventEmitter(); + + emitGenerateContent() { + this.generateContent.emit(this.message()); + this.message.set(''); + } +} diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.html b/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.html index 733d77734fea..f9e36401c84f 100644 --- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.html +++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.html @@ -72,12 +72,29 @@ pButton (click)="goBack()" icon="pi pi-chevron-left" - class="p-button-tertiary p-button-rounded p-button-sm"> + class="p-button-tertiary p-button-rounded p-button-sm "> + @if ($showAcceptChangesButton()) { + + + + }
+ + diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts b/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts index e872f3829a6e..f71323f30920 100644 --- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts +++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts @@ -8,7 +8,8 @@ import { effect, inject, OnInit, - output + output, + signal } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { @@ -24,6 +25,7 @@ import { ButtonModule } from 'primeng/button'; import { TabViewModule } from 'primeng/tabview'; import { DotCMSContentlet, DotCMSContentTypeField } from '@dotcms/dotcms-models'; +import { OpenAiService } from '@dotcms/edit-content/services/openai/openai.service'; import { DotMessagePipe, DotWorkflowActionsComponent } from '@dotcms/ui'; import { resolutionValue } from './utils'; @@ -43,8 +45,48 @@ import { getFinalCastedValue, isFilteredType } from '../../utils/functions.util'; +import { DotAiClippyContentGeneratorComponent } from '../dot-ai-clippy-content-generator/dot-ai-clippy-content-generator.component'; import { DotEditContentFieldComponent } from '../dot-edit-content-field/dot-edit-content-field.component'; +// CAN BE AN UTIL +const RECORD_OBJECT = { + 'com.dotcms.contenttype.model.field.ImmutableTextField': { + variable: '', + dataType: 'string', + format: '' + }, + 'com.dotcms.contenttype.model.field.ImmutableTagField': { + variable: '', + dataType: 'string[]', + format: '[tag1, tag2, tag3]' + }, + 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField': { + variable: '', + dataType: 'string', + format: 'html' + }, + 'com.dotcms.contenttype.model.field.ImmutableDateTimeField': { + variable: '', + dataType: 'string', + format: 'date: iso string' + }, + 'com.dotcms.contenttype.model.field.ImmutableCustomField': { + variable: '', + dataType: 'string', + format: '' + }, + 'com.dotcms.contenttype.model.field.ImmutableTextAreaField': { + variable: '', + dataType: 'string', + format: 'text' + }, + 'com.dotcms.contenttype.model.field.ImmutableWysiwygField': { + variable: '', + dataType: 'string', + format: 'html' + } +}; + /** * DotEditContentFormComponent * @@ -80,8 +122,10 @@ import { DotEditContentFieldComponent } from '../dot-edit-content-field/dot-edit DotWorkflowActionsComponent, TabViewInsertDirective, NgTemplateOutlet, - DotMessagePipe + DotMessagePipe, + DotAiClippyContentGeneratorComponent ], + providers: [OpenAiService], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('fadeIn', [ @@ -97,6 +141,10 @@ export class DotEditContentFormComponent implements OnInit { readonly #router = inject(Router); readonly #destroyRef = inject(DestroyRef); readonly #fb = inject(FormBuilder); + readonly openAiService = inject(OpenAiService); + + $showAcceptChangesButton = signal(false); + $latestFormAcceptChanges = signal(null); /** * Output event emitter that informs when the form has changed. @@ -160,6 +208,8 @@ export class DotEditContentFormComponent implements OnInit { */ $restOfTabs = computed(() => this.$store.tabs().slice(1)); + isClippyThinking = signal(false); + ngOnInit(): void { if (this.$store.tabs().length) { this.initializeForm(); @@ -418,4 +468,55 @@ export class DotEditContentFormComponent implements OnInit { window.open(realUrl, '_blank'); } + + generateContent(message: string) { + this.isClippyThinking.set(true); + + const formStructure = Object.keys(this.form.controls).reduce((acc, key) => { + const field = this.$formFields().find((f) => f.variable === key); + + const fieldRule = RECORD_OBJECT[field.clazz]; + + if (!fieldRule) { + return acc; + } + + fieldRule.key = field.variable; + + if (field.clazz === 'com.dotcms.contenttype.model.field.ImmutableCustomField') { + fieldRule.type = field.dataType; + fieldRule.format = `${field.regexCheck} ${field.name}`; + } + + acc[key] = fieldRule; + + return acc; + }, {}); + + const body = { + contentType: this.$store.contentType().variable, + structure: JSON.stringify(formStructure), + description: message, + language: this.$store.currentLocale().isoCode + }; + + this.openAiService.sendMessage(body).subscribe((response) => { + this.$latestFormAcceptChanges.set(JSON.stringify(this.form.value)) + this.form.patchValue(JSON.parse(response)); + this.isClippyThinking.set(false); + + // TODO: Show accept changes button + this.$showAcceptChangesButton.set(true); + }); + } + + acceptChanges() { + this.$showAcceptChangesButton.set(false); + this.$latestFormAcceptChanges.set(this.form.value); + } + + resetChanges() { + this.$showAcceptChangesButton.set(false); + this.form.patchValue(JSON.parse(this.$latestFormAcceptChanges())); + } } diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.html b/core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.html index 3f989d4ee916..d2bf71175ae9 100644 --- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.html +++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.html @@ -75,3 +75,5 @@ } + + diff --git a/core-web/libs/edit-content/src/lib/directives/ai-refining-input/ai-refining-input.directive.ts b/core-web/libs/edit-content/src/lib/directives/ai-refining-input/ai-refining-input.directive.ts new file mode 100644 index 000000000000..209be3ea8958 --- /dev/null +++ b/core-web/libs/edit-content/src/lib/directives/ai-refining-input/ai-refining-input.directive.ts @@ -0,0 +1,62 @@ +import { + ComponentRef, + Directive, + ElementRef, + inject, + input, + OnInit, + ViewContainerRef +} from '@angular/core'; + +import { DotAiMenuComponent } from '../dot-ai/dot-ai-menu/dot-ai-menu.component'; + +@Directive({ + selector: '[dotAiRefiningInput]', + standalone: true +}) +export class AiRefiningInputDirective implements OnInit { + private menuComponent!: ComponentRef; + + //** show the ai menu button */ + $showAiMenu = input(false, { alias: 'showAiMenu' }); + + #elementRef = inject(ElementRef); + #viewContainerRef = inject(ViewContainerRef); + + ngOnInit() { + const inputElement = this.#elementRef.nativeElement as HTMLInputElement; + this.menuComponent = this.#viewContainerRef.createComponent(DotAiMenuComponent); + + this.menuComponent.instance.textChanged.subscribe((text) => { + inputElement.value = text; + }); + const parent = inputElement.parentNode as HTMLElement; + const hasContent = inputElement.value.trim().length > 0; + + if (!hasContent) { + parent.removeChild(this.menuComponent.location.nativeElement); + } + + + + inputElement.addEventListener('input', () => { + const hasContent = inputElement.value.trim().length > 0; + + if (this.menuComponent) { + this.menuComponent.setInput('text', inputElement.value); + } + + if (hasContent) { + + if (!parent.style.position) { + parent.style.position = 'relative'; + } + + const menuElement = this.menuComponent.location.nativeElement; + parent.appendChild(menuElement); + } else { + parent.removeChild(this.menuComponent.location.nativeElement); + } + }); + } +} diff --git a/core-web/libs/edit-content/src/lib/directives/dot-ai/dot-ai-menu/dot-ai-menu.component.ts b/core-web/libs/edit-content/src/lib/directives/dot-ai/dot-ai-menu/dot-ai-menu.component.ts new file mode 100644 index 000000000000..3c3d46ba9831 --- /dev/null +++ b/core-web/libs/edit-content/src/lib/directives/dot-ai/dot-ai-menu/dot-ai-menu.component.ts @@ -0,0 +1,184 @@ +import { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core'; +import { DotAiService } from '@dotcms/data-access'; +import { MenuItem } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { MenuModule } from 'primeng/menu'; + +@Component({ + selector: 'dot-ai-menu', + template: ` + + + + + + + + + `, + styles: [ + ` + :host { + display: inline-flex; + align-items: center; + } + :host ::ng-deep .ai-trigger-button { + position: absolute; + right: 0; + top: 0; + margin: 0; + height: 2.5rem; + border: 0 none; + } + :host ::ng-deep .ai-trigger-button .p-button { + width: 2.5rem; + height: 100%; + padding: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + color: var(--text-color-secondary); + background: transparent; + border: none; + display: inline-flex; + align-items: center; + justify-content: center; + } + :host ::ng-deep .ai-trigger-button .p-button:hover { + background: var(--surface-hover); + color: var(--text-color); + } + :host ::ng-deep .ai-trigger-button .p-button:focus { + box-shadow: none; + } + .ai-icon { + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--color-palette-primary-500); + } + :host ::ng-deep .p-menu { + min-width: 200px; + } + ` + ], + standalone: true, + imports: [MenuModule, ButtonModule] +}) +export class DotAiMenuComponent implements OnInit { + @Input() text: string; + items: MenuItem[] | undefined; + + @Output() textChanged = new EventEmitter(); + + #dotAiService = inject(DotAiService); + + ngOnInit() { + this.items = [ + { + label: 'REWRITE', + items: [ + { + label: 'Improve writing', + command: () => { + this.refineText('You are a text refiner. You will be given a text, a tone, and a language. You will need to refine the text to the tone and language.', 'formal', 'en'); + } + }, + { + label: 'Fix spelling and grammar', + command: () => { + this.refineText('You are an expert proofreader. You will be given a text, and you will need to fix the spelling and grammar.', 'formal', 'en'); + } + }, + { + label: 'Convert to English (US)', + command: () => { + this.refineText('You are an expert translator. You will be given a text, and you will need to convert it to US English.', 'formal', 'en'); + } + }, + { + label: 'Simplify language', + command: () => { + this.refineText('You are an expert simplifier. You will be given a text, and you will need to simplify it.', 'formal', 'en'); + } + } + ] + }, + { + label: 'TONE', + items: [ + { + label: 'Write in friendly tone', + command: () => { + this.refineText('You are an expert writer. You will be given a text, and you will need to write it in a friendly tone.', 'friendly', 'en'); + } + }, + { + label: 'Write in casual tone', + command: () => { + this.refineText('You are an expert writer. You will be given a text, and you will need to write it in a casual tone.', 'casual', 'en'); + } + }, + { + label: 'Write in professional tone', + command: () => { + this.refineText('You are an expert writer. You will be given a text, and you will need to write it in a professional tone.', 'professional', 'en'); + } + } + ] + }, + { + label: 'GENERATE', + items: [ + { + label: 'Generate SEO friendly title', + command: () => { + this.refineText('You are an expert SEO writer. You will be given a text, and you will need to generate a SEO friendly title.', 'formal', 'en'); + } + }, + { + label: 'Generate catchy headline', + command: () => { + this.refineText('You are an expert headline writer. You will be given a text, and you will need to generate a catchy headline.', 'formal', 'en'); + } + }, + { + label: 'Generate URL friendly title', + command: () => { + this.refineText('You are an expert URL title writer. You will be given a text, and you will need to generate a URL friendly title.', 'formal', 'en'); + } + } + ] + } + ]; + } + + refineText(system: string, tone: string, language: string) { + this.#dotAiService.refineText({ + system: system, + text: this.text, + tone: tone, + language: language + }) + .subscribe((response: any) => { + this.textChanged.emit(response.title); + }); + } +} diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-text-area/dot-edit-content-text-area.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-text-area/dot-edit-content-text-area.component.html index 95c561ebb364..af05f44d02d9 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-text-area/dot-edit-content-text-area.component.html +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-text-area/dot-edit-content-text-area.component.html @@ -1,4 +1,5 @@