AI Label integration 

Integrate your TYPO3 extension with AI Foundation AI Label so AI-generated content is recorded, reviewable in the backend module, and labelled on the frontend when rules require it.

Prerequisites 

  • AI Foundation (ns_t3af) installed
  • Your extension persists content on applicable tables (pages, tt_content, sys_file_metadata, or tables registered in AI Label settings)
  • AI requests go through AI Foundation (AiServiceInterface) so capture correlation ids are available

Overview 

Three integration points:

  1. Capture (automatic) — AI Foundation stores each generation in tx_nst3af_ailabel_generation.
  2. Bind (your code) — when you save a record, link the generation to that row.
  3. Optional direct origin — report involvement without a prior capture.

Applicable tables 

Built-in:

  • pages
  • tt_content
  • sys_file_metadata

Additional tables: add via AI Label → Settings → Applicable tables, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ns_t3af']['ailabelApplicableTables'], or listen to CollectApplicableTablesEvent:

use NITSAN\NsT3AF\AiLabel\Event\CollectApplicableTablesEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener]
final class RegisterNewsTableForAiLabel
{
    public function __invoke(CollectApplicableTablesEvent $event): void
    {
        $event->addTable('tx_news_domain_model_news');
    }
}
Copied!

Schema columns for extra tables are added by ApplicableTableSchemaListener as CREATE TABLE fragments. After changing extra tables, run Maintenance → Analyze Database Structure so the columns exist.

Direct origin reporting 

When capture correlation is unavailable:

use NITSAN\NsT3AF\AiLabel\Domain\Involvement;
use NITSAN\NsT3AF\Api\AiLabelRecorderInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;

$recorder = GeneralUtility::makeInstance(AiLabelRecorderInterface::class);
$recorder->recordOrigin(
    'tt_content',
    $uid,
    Involvement::AiGenerated,
    'my_extension',
    aiSystem: 'gpt-4',
    aiVendor: 'openai',
);
Copied!

Public API interface: Classes/Api/AiLabelRecorderInterface.php.

Convenience methods (also reset confirmation):

$recorder->markGenerated('tt_content', $uid, 'my_extension');
$recorder->markModified('tt_content', $uid, 'my_extension');
$recorder->clearInvolvement('tt_content', $uid, 'my_extension');
Copied!

These throw \InvalidArgumentException when $table is not an applicable table.

Involvement values 

Value Use when
not_reviewed Default; no editor decision
no_ai Editor asserts no AI involvement
ai_generated Content substantially created by AI
ai_modified AI changed existing content
origin_unknown Role of AI unclear
suggestion System detected/suggested; awaiting person

Frontend rendering 

Include the TypoScript that hooks into fluid_styled_content (visitor badges on content elements). Listing a Site Set as a dependency does not load its TypoScript automatically — import the file as well.

Site Set (TYPO3 v13.4+): add nitsan/ns-t3af-label to your sitepackage set's dependencies, and import the setup:

dependencies:
  - nitsan/ns-t3af-label
Copied!
@import 'EXT:ns_t3af/Configuration/TypoScript/setup.typoscript'
Copied!

Classic TypoScript templates: include static template “AI Foundation labels”, or @import the same file.

Fluid badge (namespace ail is registered by AI Foundation):

<html xmlns:ail="http://typo3.org/ns/NITSAN/NsT3AF/AiLabel/ViewHelpers"
      data-namespace-typo3-fluid="true">
    <ail:label record="{data}" table="tt_content" />
    <ail:label file="{file}" />
</html>
Copied!

Fluid Styled Content ships an image partial override that already calls <ail:label file="{file}" /> on each rendered file.

Assign state without rendering the badge:

<ail:recordState record="{data}" table="tt_content" as="labelState" />
<f:if condition="{labelState.showLabel}"></f:if>

<ail:fileState file="{image}" as="labelState" />
Copied!

DataProcessor alias nst3af-label (variable labelState by default):

tt_content {
    dataProcessing {
        1550 = nst3af-label
        1550 {
            as = labelState
        }
    }
}
Copied!

Or inject FrontendLabelRenderer / FrontendLabelStateFactory in PHP.

Auto-confirm 

Settings tab drives auto-confirm via AutoConfirmSettingsService:

  • Confirm when OUR extension recorded itns_t3ai, ns_t3aa, ns_t3af for media, text, or both.
  • Confirm when DETECTED on upload — recording source detected_upload (IPTC Digital Source Type already signals AI).
  • Hold — public-interest text stays manual (also EXTCONF ailabelHoldList).

EXTCONF ailabelAutoConfirmSources remains an extra allow-list (used even when the Settings own-toggle is off). Auto-confirm never fills the responsible person field.

Check AutoConfirmSettingsServiceTest and AiLabelRuleMatrixTest before relying on auto-confirm in production.

Module settings consumed on the frontend 

AiLabelSettingsService drives visitor badge appearance:

  • labelSize, labelWording — passed to FrontendLabelRenderer (show_site_language = text only; icon_only = icon only)
  • markImageFile === overlay — enables image partial wrapper and overlay badge
  • markImageFile === written_in — stamps processed image copies (ImageMagick)
  • labelPosition — overlay CSS class and processed-file stamp corner
  • machineReadableiptc / iptc_jsonld / off
  • labelUnknownOrigin — visitor badge for confirmed unknown-origin media
  • secondInfoLayer — expandable <details> on the badge with human-readable involvement wording only (machine reason codes stay in evidence export / backend lists)
  • When markImageFile === overlay, media CTypes (image, textmedia, textpic) skip the content-element After/All drop-in badge so the image overlay is the single visitor mark

Maintainer references 

  • Agent summary: context/features/ai-label.md
  • Deep spec: context/specs/FEATURE_AiLabel.md
  • Unit tests: Tests/Unit/AiLabel/

Run tests:

cd packages/ns_t3af
composer test -- --filter AiLabel
Copied!