https://github.com/in2code-de/texter
Using AI to generate texts in TYPO3 backend
https://github.com/in2code-de/texter
Last synced: 5 months ago
JSON representation
Using AI to generate texts in TYPO3 backend
- Host: GitHub
- URL: https://github.com/in2code-de/texter
- Owner: in2code-de
- Created: 2025-12-04T15:49:17.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-01-03T16:56:53.000Z (5 months ago)
- Last Synced: 2026-01-07T03:19:32.787Z (5 months ago)
- Language: PHP
- Size: 3.95 MB
- Stars: 2
- Watchers: 0
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# Texter - AI generated texts in TYPO3 with Google Gemini
## Table of Contents
- [Introduction](#introduction)
- [Google Gemini API](#google-gemini-api)
- [Installation](#installation)
- [Custom LLM Integration (like ChatGPT, Claude, Mistral, etc.)](#custom-llm-integration-like-chatgpt-claude-mistral-etc)
- [Changelog and breaking changes](#changelog-and-breaking-changes)
- [Contribution with ddev](#contribution-with-ddev)
## Introduction
Add AI integration to TYPO3 backend. We simply added a CKEditor plugin to generate texts from AI (Gemini).
Example integration into TYPO3 backend.
Example Video

Better quality: https://www.youtube.com/watch?v=yPFrigLah3o
Video image #1

Video image #2

Video image #3

Video image #4

## Google Gemini API
- To use the extension, you need a **Google Gemini API** key. You can register for one
at https://aistudio.google.com/app/api-keys.
- Alternatively, you can implement your own LLM provider (see [Custom LLM Integration](#custom-llm-integration-like-chatgpt-claude-mistral-etc) below).
## Installation
### With composer
```
composer req in2code/texter
```
### Main configuration
After that, you have to set some initial configuration in Extension Manager configuration:
| Title | Default value | Description |
|---------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| promptPrefix | - | Prefix text that should be always added to the prompt at the beginning |
| apiKey | - | Google Gemini API key. You can let this value empty and simply use ENV_VAR "GOOGLE_API_KEY" instead if you want to use CI pipelines for this setting |
Note: It's recommended to use ENV vars for in2code/imager instead of saving the API-Key in Extension Manager configuration
```
GOOGLE_API_KEY=your_api_key_from_google
```
### RTE configuration
Per default, in2code/texter sets a default RTE configuration via Page TSConfig:
```
RTE.default.preset = texter
```
If you want to overrule this default setting, you can require in2code/texter in your sitepackage (to ensure that your
extension is loaded after texter) and define a different default preset.
Check file [Texter.yaml](Configuration/RTE/Texter.yaml) for an example how to add texter to your RTE configuration.
**Hint** You can also use texter for selected RTE fields in backend. Example Page TSConfig:
```
RTE.config.tt_content.bodytext.preset = texter
RTE.config.tx_news_domain_model_news.bodytext.preset = texter
```
## Custom LLM Integration (like ChatGPT, Claude, Mistral, etc.)
Texter uses a factory pattern to allow custom LLM providers. By default, it uses Google Gemini,
but you can easily integrate other AI services (OpenAI, Claude, local models, etc.).
### Implementing a Custom LLM Repository
1. Create a custom repository class implementing `RepositoryInterface` - see example for OpenAI ChatGPT:
```php
apiKey = getenv('OPENAI_API_KEY') ?: '';
}
public function checkApiKey(): void
{
if ($this->apiKey === '') {
throw new ConfigurationException('OpenAI API key not configured', 1735646000);
}
}
public function getApiUrl(): string
{
return $this->apiUrl;
}
public function getText(string $prompt, string $pageId = '0'): string
{
$this->checkApiKey();
$history = $this->conversationHistory->getHistory($pageId);
$this->conversationHistory->addUserMessage($history, $this->extendPrompt($prompt));
$response = $this->connectToChatGpt($history);
$this->conversationHistory->addModelResponse($history, $response);
$this->conversationHistory->saveHistory($history, $pageId);
return $response;
}
protected function connectToChatGpt(array $conversationHistory): string
{
// Convert Gemini format to ChatGPT format
$messages = $this->convertHistoryToChatGptFormat($conversationHistory);
$payload = [
'model' => 'gpt-4o',
'messages' => $messages,
'temperature' => 0.7,
'max_tokens' => 8192,
];
$options = [
'headers' => [
'Authorization' => 'Bearer ' . $this->apiKey,
'Content-Type' => 'application/json',
],
'body' => json_encode($payload),
];
$response = $this->requestFactory->request($this->getApiUrl(), $this->requestMethod, $options);
if ($response->getStatusCode() !== 200) {
throw new ApiException(
'Failed to generate text with ChatGPT: ' . $response->getBody()->getContents(),
1735646001
);
}
$responseData = json_decode($response->getBody()->getContents(), true);
if (isset($responseData['choices'][0]['message']['content']) === false) {
throw new ApiException('Invalid ChatGPT API response structure', 1735646002);
}
return $responseData['choices'][0]['message']['content'];
}
protected function convertHistoryToChatGptFormat(array $geminiHistory): array
{
$messages = [];
foreach ($geminiHistory as $entry) {
$role = $entry['role'] === 'model' ? 'assistant' : $entry['role'];
$content = $entry['parts'][0]['text'] ?? '';
$messages[] = [
'role' => $role,
'content' => $content,
];
}
return $messages;
}
}
```
2. Register your custom repository in `ext_localconf.php`:
```php