-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: moved OpenAI API call from client to server side by extending t…
…he Headless CMS GraphQL API (#171)
- Loading branch information
1 parent
f303e26
commit 0636eec
Showing
3 changed files
with
161 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
headless-cms/smart-seo-open-ai/5.41.x/extensions/smartSeoOpenAi/api/src/generateSeo.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { CmsGraphQLSchemaPlugin, ContentContextParams } from "@webiny/api-headless-cms"; | ||
import { Context } from "@webiny/api-serverless-cms" | ||
import OpenAI from "openai"; | ||
|
||
/* | ||
* This file adds a GraphQL schema for generating SEO metadata. | ||
* It defines a generateSeo query to analyze input content. | ||
* The query outputs an SEO title, description, and keywords | ||
* and uses OpenAI’s GPT model to processes the content to generate metadata. | ||
* It uses Webiny’s CmsGraphQLSchemaPlugin to extend the Headless CMS GraphQL API. | ||
*/ | ||
|
||
|
||
const OPENAI_API_KEY = process.env["WEBINY_API_OPEN_AI_API_KEY"]; | ||
|
||
const openai = new OpenAI({ apiKey: OPENAI_API_KEY }); | ||
|
||
export const generateSeo = () => [ | ||
new CmsGraphQLSchemaPlugin<Context>({ | ||
typeDefs: ` | ||
type SeoData { | ||
title: String | ||
description: String | ||
keywords: [String] | ||
} | ||
input GenerateSeoInput { | ||
content: String! | ||
} | ||
type Query { | ||
generateSeo(input: GenerateSeoInput!): SeoData | ||
} | ||
`, | ||
resolvers: { | ||
Query: { | ||
generateSeo: async (_, { input }) => { | ||
try { | ||
const { content } = input; | ||
|
||
const response = await openai.chat.completions.create({ | ||
model: "gpt-3.5-turbo", | ||
messages: [ | ||
{ | ||
role: "system", | ||
content: `You will be provided with one or more paragraphs of HTML, and you need to extract an SEO optimized page title, a page summary, and up to 5 keywords. Response should be returned as a plain JSON object, with "title" field for the page title, "description" field for page summary, and "keywords" field as an array of keywords.` | ||
}, | ||
{ | ||
role: "user", | ||
content | ||
} | ||
], | ||
temperature: 0.5, | ||
max_tokens: 128, | ||
top_p: 1 | ||
}); | ||
|
||
const messageContent = response?.choices?.[0]?.message?.content; | ||
|
||
if (typeof messageContent !== "string") { | ||
console.error("Invalid or null content received from OpenAI."); | ||
throw new Error("Failed to get a valid response from OpenAI."); | ||
} | ||
|
||
const seoData = JSON.parse(messageContent); | ||
return { | ||
title: seoData.title, | ||
description: seoData.description, | ||
keywords: seoData.keywords | ||
}; | ||
} catch (error) { | ||
console.error("Error generating SEO data:", error); | ||
throw new Error("Failed to generate SEO data."); | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
]; |
5 changes: 4 additions & 1 deletion
5
headless-cms/smart-seo-open-ai/5.41.x/extensions/smartSeoOpenAi/api/src/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import { Article } from "./Article" | ||
import { generateSeo } from "./generateSeo"; | ||
|
||
export const createExtension = () => { | ||
return [ | ||
Article | ||
Article, | ||
generateSeo | ||
]; | ||
}; |