├── .eslintrc
├── .gitignore
├── modules
└── @apostrophecms
│ ├── ai-helper-image
│ ├── public
│ │ └── placeholder.jpg
│ ├── ui
│ │ └── apos
│ │ │ └── components
│ │ │ ├── AposAiHelperImageEditor.vue
│ │ │ └── AposAiHelperImageManager.vue
│ └── index.js
│ └── ai-helper-rich-text-widget
│ ├── ui
│ └── apos
│ │ └── components
│ │ └── AposAiHelperTextDialog.vue
│ └── index.js
├── i18n
└── aposAiHelper
│ ├── en.json
│ ├── pt-BR.json
│ ├── es.json
│ ├── sk.json
│ ├── it.json
│ ├── fr.json
│ └── de.json
├── package.json
├── LICENSE.md
├── index.js
├── CHANGELOG.md
└── README.md
/.eslintrc:
--------------------------------------------------------------------------------
1 | { "extends": "apostrophe" }
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .DS_Store
4 | package-lock.json
--------------------------------------------------------------------------------
/modules/@apostrophecms/ai-helper-image/public/placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apostrophecms/ai-helper/main/modules/@apostrophecms/ai-helper-image/public/placeholder.jpg
--------------------------------------------------------------------------------
/i18n/aposAiHelper/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Generate Image",
3 | "generateTextLabel": "Generate Text",
4 | "generateTextDescription": "Generate text with AI",
5 | "imagePlaceholderText": "Describe the image you want to create.",
6 | "textPromptLabel": "Enter a prompt to generate text. Example: \"100 words about cheese, with subheadings\"",
7 | "variations": "Variations",
8 | "select": "Select",
9 | "delete": "Delete",
10 | "generateTextAction": "Generate",
11 | "rateLimitExceeded": "Rate limit exceeded, try again later",
12 | "invalidRequest": "Invalid request, please review OpenAI's rules",
13 | "utilityOperationLabel": "Generate an image with AI",
14 | "errorMessage": "An error occurred."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Gerar Imagem",
3 | "generateTextLabel": "Gerar Texto",
4 | "generateTextDescription": "Gerar texto com IA",
5 | "imagePlaceholderText": "Descreva a imagem que você deseja criar.",
6 | "textPromptLabel": "Digite um prompt para gerar texto. Exemplo: \"100 palavras sobre queijo, no estilo de Anthony Bourdain, com subtítulos\"",
7 | "variations": "Variações",
8 | "select": "Selecionar",
9 | "delete": "Excluir",
10 | "generateTextAction": "Gerar",
11 | "rateLimitExceeded": "Limite de taxa excedido, tente novamente mais tarde",
12 | "invalidRequest": "Pedido inválido, por favor revise as regras da OpenAI",
13 | "utilityOperationLabel": "Gerar uma imagem com IA",
14 | "errorMessage": "Ocorreu um erro."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Generar Imagen",
3 | "generateTextLabel": "Generar Texto",
4 | "generateTextDescription": "Generar texto con IA",
5 | "imagePlaceholderText": "Describe la imagen que deseas crear.",
6 | "textPromptLabel": "Introduce un aviso para generar texto. Ejemplo: \"100 palabras sobre el queso, al estilo de Anthony Bourdain, con subtítulos\"",
7 | "variations": "Variaciones",
8 | "select": "Seleccionar",
9 | "delete": "Eliminar",
10 | "generateTextAction": "Generar",
11 | "rateLimitExceeded": "Límite de tasa excedido, intenta de nuevo más tarde",
12 | "invalidRequest": "Solicitud inválida, revisa las reglas de OpenAI",
13 | "utilityOperationLabel": "Generar una imagen con IA",
14 | "errorMessage": "Ocurrió un error."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Vygenerovať obrázok",
3 | "generateTextLabel": "Vygenerovať text",
4 | "generateTextDescription": "Vygenerovať text s AI",
5 | "imagePlaceholderText": "Opíšte obrázok, ktorý chcete vytvoriť.",
6 | "textPromptLabel": "Zadajte výzvu na vygenerovanie textu. Príklad: \"100 slov o syre, v štýle Anthony Bourdaina, so subnadpismi\"",
7 | "variations": "Variácie",
8 | "select": "Vybrať",
9 | "delete": "Odstrániť",
10 | "generateTextAction": "Vygenerovať",
11 | "rateLimitExceeded": "Prekročený limit požiadaviek, skúste to znova neskôr",
12 | "invalidRequest": "Neplatná požiadavka, prosím skontrolujte pravidlá OpenAI",
13 | "utilityOperationLabel": "Vygenerovať obrázok s AI",
14 | "errorMessage": "Vyskytla sa chyba."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Genera Immagine",
3 | "generateTextLabel": "Genera Testo",
4 | "generateTextDescription": "Genera testo con AI",
5 | "imagePlaceholderText": "Descrivi l'immagine che vuoi creare.",
6 | "textPromptLabel": "Inserisci un prompt per generare testo. Esempio: \"100 parole sul formaggio, nello stile di Anthony Bourdain, con sottotitoli\"",
7 | "variations": "Varianti",
8 | "select": "Seleziona",
9 | "delete": "Elimina",
10 | "generateTextAction": "Genera",
11 | "rateLimitExceeded": "Limite di richieste superato, riprova più tardi",
12 | "invalidRequest": "Richiesta non valida, per favore rivedi le regole di OpenAI",
13 | "utilityOperationLabel": "Genera un'immagine con AI",
14 | "errorMessage": "Si è verificato un errore."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Générer une image",
3 | "generateTextLabel": "Générer du texte",
4 | "generateTextDescription": "Générer du texte avec l'IA",
5 | "imagePlaceholderText": "Décrivez l'image que vous souhaitez créer.",
6 | "textPromptLabel": "Entrez un prompt pour générer du texte. Exemple : \"100 mots sur le fromage, dans le style d'Anthony Bourdain, avec des sous-titres\"",
7 | "variations": "Variations",
8 | "select": "Sélectionner",
9 | "delete": "Supprimer",
10 | "generateTextAction": "Générer",
11 | "rateLimitExceeded": "Limite de taux dépassée, réessayez plus tard",
12 | "invalidRequest": "Demande invalide, veuillez revoir les règles d'OpenAI",
13 | "utilityOperationLabel": "Générer une image avec l'IA",
14 | "errorMessage": "Une erreur s'est produite."
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/aposAiHelper/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "generateImage": "Bild generieren",
3 | "generateTextLabel": "Text generieren",
4 | "generateTextDescription": "Text mit KI generieren",
5 | "imagePlaceholderText": "Beschreiben Sie das Bild, das Sie erstellen möchten.",
6 | "textPromptLabel": "Geben Sie einen Prompt ein, um Text zu generieren. Beispiel: \"100 Wörter über Käse, im Stil von Anthony Bourdain, mit Unterüberschriften\"",
7 | "variations": "Varianten",
8 | "select": "Auswählen",
9 | "delete": "Löschen",
10 | "generateTextAction": "Generieren",
11 | "rateLimitExceeded": "Die Ratebegrenzung wurde überschritten, versuchen Sie es später erneut",
12 | "invalidRequest": "Ungültige Anfrage, bitte überprüfen Sie die Regeln von OpenAI",
13 | "utilityOperationLabel": "Ein Bild mit KI generieren",
14 | "errorMessage": "Ein Fehler ist aufgetreten."
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@apostrophecms/ai-helper",
3 | "version": "1.0.0-beta.10",
4 | "description": "AI helpers for content creation",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "npm run lint",
8 | "lint": "eslint --ext .js,.vue ."
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/apostrophecms/ai-helper.git"
13 | },
14 | "keywords": [
15 | "ApostropheCMS",
16 | "apostrophe"
17 | ],
18 | "author": "Apostrophe Technologies, Inc.",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/apostrophecms/ai-helper/issues"
22 | },
23 | "homepage": "https://github.com/apostrophecms/ai-helper#readme",
24 | "devDependencies": {
25 | "eslint-config-apostrophe": "^5.0.0"
26 | },
27 | "dependencies": {
28 | "@paralleldrive/cuid2": "^3.0.4",
29 | "form-data": "^4.0.0",
30 | "marked": "^4.3.0",
31 | "node-fetch": "^2.6.9",
32 | "sharp": "^0.30.7"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Apostrophe Technologies, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | module.exports = {
5 | init() {
6 | if (!(process.env.APOS_OPENAI_KEY || process.env.APOS_AI_HELPER_MOCK)) {
7 | // We do not document the mock because it is for internal testing
8 | throw new Error('APOS_OPENAI_KEY must be set in your environment');
9 | }
10 | },
11 | i18n: {
12 | aposAiHelper: {
13 | browser: true
14 | }
15 | },
16 | bundle: {
17 | directory: 'modules',
18 | modules: getBundleModuleNames()
19 | },
20 | options: {
21 | textModel: 'gpt-5.1',
22 | textMaxTokens: 1000,
23 | imageModel: 'gpt-image-1-mini'
24 | },
25 | methods(self) {
26 | return {
27 | checkPermissions(req) {
28 | // If the user cannot edit at least one content type, they have
29 | // no business talking to the AI
30 | if (!Object.keys(self.apos.modules).some(type => self.apos.permission.can(req, 'edit', type))) {
31 | throw self.apos.error('forbidden');
32 | }
33 | }
34 | };
35 | }
36 | };
37 |
38 | function getBundleModuleNames() {
39 | const source = path.join(__dirname, './modules/@apostrophecms');
40 | return fs
41 | .readdirSync(source, { withFileTypes: true })
42 | .filter(dirent => dirent.isDirectory())
43 | .map(dirent => `@apostrophecms/${dirent.name}`);
44 | }
45 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.0-beta.10 (2025-11-18)
4 |
5 | * Text generation is fully compatible with the latest versions of Apostrophe 4.x.
6 | * Default models modernized to `gpt-5.1` and `gpt-image-1-mini`.
7 | * Compatible with current OpenAI API.
8 | * Because newer models take much longer to generate each image and also generate much better images, the image generator now generates one image or variant image at a time.
9 | * Bumps `eslint-config-apostrophe` to `5`, fixes linter errors, removes unused dependencies and modernizes dependencies.
10 |
11 | ## 1.0.0-beta.9 (2024-09-05)
12 |
13 | * Add AI and community-reviewed translation strings
14 |
15 | ## 1.0.0-beta.8 (2024-08-09)
16 |
17 | * Security: Apostrophe now checks to make sure the current user can edit at least one type of content on the site before making calls to the OpenAI APIs. Upgrading is recommended.
18 | * Updated to use the gpt4o text model by default.
19 | * Image model can be explicitly specified and defaults to `dall-e-2`. Note that `dall-e-3` does not support variations in the same way, so the current UI will have to change if that feature is not added upstream.
20 |
21 |
22 | ## 1.0.0-beta.7 (2024-07-10)
23 |
24 | * Add missing UI translation keys.
25 |
26 | ## 1.0.0-beta.6 (2024-02-21)
27 |
28 | * Check that the current user `canCreate` images before showing the `Generate an image with AI` button.
29 |
30 | ## 1.0.0-beta.5 (2023-09-20)
31 |
32 | * `text-davinci-003` model was deprecated, update to the `gpt-3.5-turbo-instruct` model as a new default
33 | * Document how to override the default text generation model and token limit (subject to GPT's own limits)
34 |
35 | ## 1.0.0-beta.4 (2023-09-14)
36 |
37 | Just bumping the `latest` tag, no changes.
38 |
39 | ## 1.0.0-beta.3 (2023-08-03)
40 |
41 | Fixed a bug in the "variants" feature caused by premature PNG to JPEG
42 | conversion. This now happens only when inserting into the media library.
43 |
44 | ## 1.0.0-beta.2 (2023-05-26)
45 |
46 | Convert imported PNGs to JPEGs as this is more appropriate to web
47 | delivery of AI-generated, often realistic images. Reduces image
48 | download time to the browser by at least 80%.
49 |
50 | ## 1.0.0-beta (2023-04-27)
51 |
52 | Initial beta release.
53 |
--------------------------------------------------------------------------------
/modules/@apostrophecms/ai-helper-image/ui/apos/components/AposAiHelperImageEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |