Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AI-Powered Content Assistance: Auto-Fill, Spell Check, and Content Enhancement for Contentlets [DONT MERGE] #31314

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9071292
feat: create floating dialog component
rjvelazco Feb 6, 2025
79c7fd1
api
nicobytes Feb 6, 2025
c354d23
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
04a631e
add clippy button
zJaaal Feb 6, 2025
d61e05b
save
nicobytes Feb 6, 2025
cabc066
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
f7fc373
chore: improve clippy styles
rjvelazco Feb 6, 2025
b87c436
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
rjvelazco Feb 6, 2025
fe659a5
WIP - Finished service
kevindaviladev Feb 6, 2025
a844475
save
nicobytes Feb 6, 2025
83266f8
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
e3a197d
feat(edit-content) directive for refine inputs
oidacra Feb 6, 2025
5045a85
Integrated service with popup
kevindaviladev Feb 6, 2025
66fb510
feat: update blog editor on open AI response
rjvelazco Feb 6, 2025
f3f073e
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
b412c99
feat(edit-content) hide button when input/textarea is empty
oidacra Feb 6, 2025
ab345bb
chore(e2e): refactor services
nicobytes Feb 6, 2025
049c3ea
chore(e2e): refactor services
nicobytes Feb 6, 2025
8a96767
chore(e2e): refactor services
nicobytes Feb 6, 2025
ec089bd
feat: complete Block Editor and WYSIWYG
rjvelazco Feb 6, 2025
9a7d9b3
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
rjvelazco Feb 6, 2025
fc43e99
chore(e2e): refactor services
nicobytes Feb 6, 2025
3c1c646
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
6eb463f
add language support
zJaaal Feb 6, 2025
acb95b8
fix resize
zJaaal Feb 6, 2025
ea70bcd
chore(e2e): refactor services
nicobytes Feb 6, 2025
8df28e9
Merge branch 'hackathon-2025-q1-feature-ai-content-assistance-autofil…
nicobytes Feb 6, 2025
5171def
chore(e2e): refactor services
nicobytes Feb 6, 2025
2359591
chore(e2e): refactor services
nicobytes Feb 6, 2025
c864e2c
Added revert changes
kevindaviladev Feb 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,9 @@ dotCMS/dependencies.gradle
.nx/
.cursorrules
.cursorignore
**/vite.config.mts.timestamp-*
**/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
2 changes: 2 additions & 0 deletions ai-api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OPENAI_API_KEY=
DEEPSEEK_API_KEY=
144 changes: 144 additions & 0 deletions ai-api/.gitignore
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions ai-api/convert.ts
Original file line number Diff line number Diff line change
@@ -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<any>;

const newSchema = zodSchema.extend({
category: z.enum(["Option 1", "Option 2", "Option 3"])
})



return newSchema;
}
87 changes: 87 additions & 0 deletions ai-api/index.ts
Original file line number Diff line number Diff line change
@@ -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());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

Having default CORS settings is insecure because they often allow overly permissive cross-origin access, exposing your application to unauthorized data sharing, potentially exposing sensitive data to malicious websites. Avoid using wildcard (*) origins, especially for endpoints that handle sensitive data. Use a restrictive CORS policy by explicitly specifying trusted origins in the Access-Control-Allow-Origin header.

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
app.use("*", cors());
const allowedOrigins = process.env.ALLOWED_ORIGINS.split(',');
app.use("*", cors({
origin: allowedOrigins
}));
View step-by-step instructions
  1. Identify the trusted origins that should be allowed to access your application. These are the domains you expect to interact with your API.

  2. Replace the wildcard origin in the cors middleware with an explicit list of trusted origins. Modify the cors configuration as follows:

    app.use("*", cors({
      origin: ["https://trusted-origin1.com", "https://trusted-origin2.com"]
    }));
  3. If you have multiple environments (e.g., development, staging, production), consider using environment variables to manage the list of allowed origins. This can be done by updating your .env file and accessing the variables in your code:

    const allowedOrigins = process.env.ALLOWED_ORIGINS.split(',');
    app.use("*", cors({
      origin: allowedOrigins
    }));

    Ensure your .env file contains a line like: ALLOWED_ORIGINS=https://trusted-origin1.com,https://trusted-origin2.com

By specifying trusted origins, you reduce the risk of unauthorized cross-origin requests and protect sensitive data from being exposed to malicious websites.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by cors-default-config-express.

You can view more details about this finding in the Semgrep AppSec Platform.

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>{text}</text> in this tone: <tone>{tone}</tone> and language: <language>{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,
});
Loading
Loading