API stability
This page defines which parts of AI Context are public (safe to depend on) and which are internal (implementation details that may change without a deprecation period). It exists so contrib developers and site builders can answer one question without reading selector internals: what am I allowed to depend on?
The default rule
If an API is not listed as public on this page, treat it as internal.
- Public APIs follow Drupal's deprecation policy: behavior changes are announced and a deprecation path is provided across minor releases.
- Internal APIs may change, move, or be removed in any release without notice. Depending on them couples your code to implementation details that are expected to evolve.
While the module is in beta, public APIs may still change between releases without a full deprecation cycle — check release notes when upgrading. Once the module reaches a stable release, the boundaries on this page describe the supported surface. See also Upgrade and compatibility policy.
Deprecated APIs
CCC 1.0.x does not currently ship @deprecated public APIs. When a
supported entry point is deprecated, it will be listed here with its
replacement and the release that removes it.
If you depend on classes or services marked @internal in code, migrate to
the public surfaces on this page — those internals are not deprecated; they
were never part of the supported API.
Beta upgrade: entity scope terminology
The entity scope APIs were renamed before the 1.0.0 stable release. There are
no backward-compatibility wrappers for the old names. Deploy the new code and
run drush updatedb in the same release window, then update custom code and
configuration that uses the old identifiers.
| Old | New |
|---|---|
Scope plugin entity_bundle / AiContextScopeEntityBundle |
entity_type / AiContextScopeEntityType |
Scope plugin target_entity / AiContextScopeTargetEntity |
entity_item / AiContextScopeEntityItem |
Cache context ai_context.target_entity |
ai_context.entity_item |
Base field target_entities |
entity_items |
Usage fields target_entity_type / target_entity_id |
entity_item_type / entity_item_id |
Usage field agent_id |
consumer_id plus derived consumer_type |
Views field ai_context_agent_link |
ai_context_consumer_link |
Views field ai_context_target_entity_link |
ai_context_entity_item_link |
Service ai_context.entity_target_resolver / AiContextEntityTargetResolver |
ai_context.current_entity_resolver / AiContextCurrentEntityResolver |
Service ai_context.current_entity_resolver / AiContextCurrentEntityResolver |
ai_context.request_info_resolver / AiContextRequestInfoResolver |
Request factory parameter targetEntity |
requestEntity |
The AiContextItem methods were renamed or replaced as follows:
| Old | New |
|---|---|
getTargetEntities() |
getEntityItems() |
hasTargetEntities() |
hasEntityItems() |
getTargetEntityData() |
getEntityItemData() (internal) |
buildTargetEntityRenderArray() |
buildEntityItemRenderArray() (internal) |
getTargetEntityMarkup() |
buildEntityItemRenderArray() (internal) |
matchesTargetEntity() |
matchesEntityItem() |
The AiContextUsage getters and setters now use EntityItem instead of
TargetEntity, for example getEntityItemType() and setEntityItemId().
getAgentId() / setAgentId() are replaced by getConsumerId() /
setConsumerId() and getConsumerType() / setConsumerType().
getRunnerId() is nullable.
Updates 10015 through 10019 migrate stored scope values and revisions,
scope configuration, usage storage and Views configuration, and the optional
Dynamic Entity Reference field storage. Update hooks cannot rewrite custom PHP
or render arrays, so custom cache-context tokens, plugin IDs, class names, and
method calls must be changed during the deployment.
Beta upgrade: max_items removal
The Max context items to inject limit was removed before the 1.0.0
stable release. The token budget (max_tokens) is now the only
user-facing limit on overall injected context; max_global_items still
caps the Global group. There are no backward-compatibility wrappers.
| Removed | Replacement |
|---|---|
max_items in ai_context.settings and per-agent overrides |
None; use max_tokens |
maxItems parameter on AiContextSelection and AiContextSelectionParams |
None |
AiContextSelectionItemsSelectedEvent::getMaxItems() (constructor no longer takes a max-items argument) |
None |
max_items parameter on the get_relevant_ai_context_items function call |
None |
AiContextLimitsConstraint and its validator |
None; the cross-limit validation is obsolete |
Update 10020 removes the obsolete max_items keys from active
configuration, and legacy configuration exports are sanitized during
import. Custom code that passes maxItems or calls getMaxItems()
must be updated in the same release window.
Update 10023 installs the token_count base field on ai_context_item and
backfills stored estimates. See
Configuration: Upgrading.
Beta upgrade: selection modes
The Match all selection mode (match_all) was removed before the 1.0.0
stable release. Context selection is now a nested ladder of three public
modes. There is no runtime alias for match_all.
| Removed | Replacement |
|---|---|
match_all stored agent selection_mode |
broad |
Default getRenderedContext() / getResult() mode of match_all |
relevant |
Post-update ai_context_post_update_0003_rewrite_match_all_selection_mode()
rewrites stored agent selection_mode: match_all to broad, and
ai_context_post_update_0004_add_site_default_selection_mode() backfills
the new site-wide default key ai_context.settings:selection_mode with
relevant. Deploy the new code and run drush updatedb in the same
release window. Explicit callers that pass an unrecognized mode (including
leftover match_all) through AiContextSelectionParams receive
\InvalidArgumentException. Unrecognized values read from stored
consumer config are logged and resolve to the site-wide default.
See Selection modes below and Services for the full field list.
Beta upgrade: stale scope values
Post-update ai_context_post_update_0006_scrub_stale_scope_values()
removes leftover Entity Types, Language, Taxonomy Terms, Context Tags,
Roles, and Site Section values that are no longer valid options. Live
cleanup now does the same when a bundle, term, language, or role is
deleted, or when a type is removed from Entity Types settings. Use Case
values are not scrubbed. Deploy the new code and run drush updatedb.
Database updates usually run before config import, so 0006 sees the
pre-import environment: a copied production database on a lower
environment can lose values for options that import is about to add.
Beta upgrade: scope field JSON:API shape
The context item scope field was replaced before the 1.0.0 stable
release: a single serialized map became a multi-value scope_id + value
field. This changes the field's shape everywhere the Field API's default
normalization is used, including JSON:API. There is no backward-compatible
wrapper.
// Before: one object, scope_id as key
"scope": {"use_case": ["working_with_text"], "language": ["en"]}
// After: a list of {scope_id, value} pairs, one per delta
"scope": [
{"scope_id": "use_case", "value": "working_with_text"},
{"scope_id": "language", "value": "en"}
]
getScope() / setScope() still return and accept the grouped
scope_id => [value, ...] shape in PHP; only the field's own Field API and
JSON:API representation changed. See
Storing scope values for
the two shapes and which callers use which one.
Update 10026 migrates stored scope values and revisions from the old map
storage; see Configuration: Upgrading.
Beta upgrade: scope availability
Scope plugins gained isAvailable() on AiContextScopeInterface before
the 1.0.0 stable release. AiContextScopeBase defaults it to TRUE.
Direct implementations of the interface (without the base class) must add
the method. Override it when scope usability depends on site configuration
that may be missing; Language returns FALSE when the site has fewer than
two languages.
AiContextScopeCacheableMetadataInterface gained
getAvailabilityCacheableMetadata(). Plugins that implement that
companion interface directly (without AiContextScopeBase) must add the
method. The base class returns empty metadata; Language adds
config:configurable_language_list.
AiContextScopeSubscriptionFormBuilderInterface::extractValues() gained
an optional $previous argument. Direct implementations of the public
form-builder interface must accept it so a save can keep subscriptions
for disabled, unavailable, or missing scopes. Existing callers can omit
it; omitted previous values are not preserved.
Disabled and unavailable scopes are hidden from forms and ignored at
runtime, but stored item values and consumer subscriptions are kept on save
unless the item is Global or a subcontext that inherits parent scope.
Those two cases still strip non-applicable stored scope in preSave().
See Scopes and Scope API.
Beta upgrade: request-context terminology
The scope plugin API and request-factory named parameter were renamed before the 1.0.0 stable release so they describe the request context, which a caller can supply, rather than implying the HTTP current route. There are no backward-compatibility wrappers. Deploy the new code and update custom scope plugins and named-argument callers in the same release window.
| Old | New |
|---|---|
AiContextScopeInterface::matchesCurrentContext() |
matchesRequestContext() |
AiContextScopeInterface::getCurrentValue() |
getDetectedValue() |
AiContextScopeBase::doGetCurrentValue() |
doGetDetectedValue() |
Request factory parameter currentEntity |
requestEntity |
AiContextScopeSiteSection::matchesCurrentPath() |
matchesRequestPath() |
requestEntity is a named parameter. Callers that used
currentEntity: $node must switch to requestEntity: $node. Positional
callers are unaffected.
Direct implementations of AiContextScopeInterface (without
AiContextScopeBase) must rename matchesCurrentContext() and
getCurrentValue(). Plugins that extend the base class must rename
doGetCurrentValue() if they override it, and any override of
matchesCurrentContext().
Internal resolver and manager methods (filterByRequestContext(),
getRequestContextValues()) and protected Taxonomy helpers
(getRequestEntityTermIds()) were renamed in the same sweep and need
no upgrade note.
AiContextScopeBase::isCurrentManageRoute() and $currentUser are
unchanged: those still mean the current admin route and the acting
session user.
Beta upgrade: selection API terminology
The public selector-job value objects and factory were renamed before the 1.0.0 stable release from Request to Selection. This keeps the selector job distinct from request-context matching APIs and provider-call request snapshots. There are no backward-compatibility wrappers. Deploy the new code and update custom callers in the same release window.
| Old | New |
|---|---|
AiContextRequest |
AiContextSelection |
AiContextRequestParamsData |
AiContextSelectionParams |
AiContextResult |
AiContextSelectionResult |
AiContextRequestFactory |
AiContextSelectionFactory |
AiContextRequestFactoryInterface |
AiContextSelectionFactoryInterface |
Service ID ai_context.request_factory |
ai_context.selection_factory |
AiContextSelectionItemsSelectedEvent::getRequest() |
getSelection() |
AiContextSelectionTextRenderedEvent::getRequest() |
getSelection() |
The SELECTION_MODE_* constants now live on AiContextSelection. Method
names that already described selection behavior are unchanged, including
fromParameters(), fromConsumer(), getResult(),
AiContextSelector::select(), and the config keys consumer_id,
selection_mode, and push_enabled.
The request-context APIs intentionally keep their names:
matchesRequestContext(), requestEntity, and
AiContextRequestInfoResolver. Provider-call snapshot APIs such as
AiContextProviderRequestContext, resolveConsumerId(), and request tags are
also unchanged.
Beta upgrade: scope capability methods
Scope plugins gained supportsExactMatch() and
supportsSituationalMatch() on AiContextScopeInterface before the
1.0.0 stable release (#3586419), splitting the former dual-purpose
supportsSubscriptions() flag into explicit capabilities.
AiContextScopeBase defaults both new methods to FALSE — exact-match
and situational-match are opt-in. Direct implementations of the
interface (without the base class) must add both methods.
| Removed or changed | Replacement |
|---|---|
supportsSubscriptions() gating exact-match eligibility and Minimal prefilter candidacy |
supportsExactMatch(); a custom scope that returned supportsSubscriptions(): FALSE and relied on exact-match must now declare supportsExactMatch(): TRUE |
Hardcoded situational scope list (SITUATIONAL_SCOPE_PLUGIN_IDS on the internal AiContextScopeResolverInterface) |
supportsSituationalMatch(); Languages, Site Sections, Entity Types, and Taxonomy Terms declare it |
Internal AiContextScopeResolverInterface::getNonSubscriptionIndexedItemIds() |
getExactMatchIndexedItemIds() |
supportsSubscriptions() keeps its signature with a narrowed, documented
contract: form visibility, subscription scoring, and save-time value
preservation. Behavior is unchanged for every built-in scope (Entity
Item declares supportsExactMatch(): TRUE; Languages, Site Sections,
Entity Types, and Taxonomy Terms declare supportsSituationalMatch():
TRUE); the capability matrix is frozen by a kernel test. The only
migration is for custom scopes that relied on the implicit
non-subscription-implies-exact-match rule, per the table above.
One selection behavior changed in the same release: situational matching now requires the scope to be enabled and available, the same gate every other selection path uses (it previously required only enabled). Concretely, on a monolingual site the Languages scope is unavailable, so items still carrying language values from before a language was removed, or from imports, are no longer situationally included in Relevant mode. They still pass hard filters and remain reachable through Broad mode, Global, Always include, or another matching scope, and they resume matching when a second language makes the scope available again. The budget estimates apply the same gate, so Relevant estimates no longer count scopes that selection ignores.
Beta upgrade: Languages no longer support subscriptions
The Languages scope now returns supportsSubscriptions(): FALSE
(#3586448). It remains a hard filter and still declares
supportsSituationalMatch(): TRUE. This is the first built-in that
combines no subscriptions with situational matching.
| Removed or changed | Replacement |
|---|---|
| Language widgets on consumer subscription forms | Hidden; Languages is listed in the "do not allow subscriptions" note |
| Language credit in subscription scoring | None. Matching the request language no longer boosts an item over an untagged peer |
| Minimal reach via a language subscription | Language-only items are unreachable in Minimal except via Always include. Raise the selection mode to Relevant for situational inclusion |
Stored scope_subscriptions.language values are preserved dormant on
save (#3586412) and stay valid options, so stale-value scrubbing
(#3586416) still removes them if the language itself is uninstalled.
They do not affect ranking.
The capability matrix kernel test now expects
language => [FALSE, FALSE, TRUE].
Minimal's subscription prefilter now drops non-participating scopes before it queries the index, so a language-only subscription is treated like no subscriptions (global, Always include, and exact-match still apply).
Beta upgrade: Specific Entities is availability-gated
Specific Entities was hidden by deleting its plugin definition when
dynamic_entity_reference was absent. It now declares
isAvailable(): FALSE instead (#3586448), matching how Languages behaves
on a single-language site. ai_context_ai_context_scope_info_alter() is
no longer implemented by this module; the alter hook itself is unchanged
and still available to third parties.
| Changed | Effect |
|---|---|
AiContextScopeManager::getScopePlugins() |
Always returns 10 built-ins, including entity_item, on every site |
getAllScopeValues() |
Includes an empty entity_item entry instead of omitting the key |
| Scope overview and settings tab | A Specific Entities row appears with status Unavailable on sites without dynamic_entity_reference |
| Matching, scoring, and forms | Unchanged: every caller already gates on AiContextScopeManager::isEnabledAndAvailable() |
Code that treated a missing entity_item definition as "DER is not
installed" must call isEnabledAndAvailable() (or the plugin's
isAvailable()) instead. Nothing else changes: stored values are still
preserved, the item form still hides the scope, and no update hook is
needed.
Beta upgrade: scope weight split
The single weight attribute on AiContextScope has been replaced by two
independent parameters (#3586438):
display_weight(ascending integer, lower = first) controls form, tab, and listing order.scoring_weight(nullable integer) controls subscription scoring influence.
| Changed | Effect |
|---|---|
AiContextScopeInterface::getWeight() |
Removed. Replace calls with getDisplayWeight() or getSubscriptionScoringWeight(). |
AiContextScopeManager::getScopePluginDisplayWeights() |
Removed. Use $plugin->getDisplayWeight() directly. |
Plugin attribute weight: |
Renamed. Use display_weight: for display order and scoring_weight: for scoring influence. |
| Sort order | Now ascending on display_weight (lower = first), matching Drupal #weight. Previously descending (higher = first). |
Custom scope plugins must rename weight: to display_weight: in their
#[AiContextScope] attribute and re-evaluate whether they need an explicit
scoring_weight:. If no scoring_weight: is declared, scoring influence
defaults to 1 (the floor), not the old display weight.
The direction inverted, so the key rename is not a 1:1 swap. Mechanically
renaming weight: 25 to display_weight: 25 moves a custom scope from
near the bottom of the form to third from the top.
hook_ai_context_scope_info_alter() implementations that still set
$definitions['x']['weight'] fail silently. The definition key is now
display_weight. There is no warning and no fallback.
Ranking of subscribed items changes on existing sites. There is no config migration and none is possible. Use Cases now carry twice the influence of the topical scopes (Context Tags, Site Sections, Entity Types, Taxonomy Terms), which are tied with each other. Form order is not ranking.
Site builders can override either weight per scope via the scope settings page Advanced section — no plugin code change needed. The scoring override is only offered for scopes that support subscriptions.
Beta upgrade: consumer configuration
Per-agent context configuration was replaced by consumers before
the 1.0.0 stable release. Agents remain one consumer type. There are
no backward-compatibility wrappers for the old APIs, config object,
or admin paths. Old /settings/agents URLs are gone.
| Removed | Replacement |
|---|---|
fromAgent() |
fromConsumer('agent:' . $id) |
findAgentConfig() |
loadConsumerConfig() / resolveConsumerConfig() |
Config object ai_context.agents |
ai_context.consumers |
Config key allow_context_injection |
push_enabled |
isInjectionAllowed() |
isPushAllowed() |
Admin paths /settings/agents |
/settings/consumers and /settings/consumers/{consumer_id}/edit |
Usage field agent_id |
consumer_id plus consumer_type |
Views field ai_context_agent_link |
ai_context_consumer_link |
isPushAllowed() is the factory helper for the combined push gate.
It reads push_enabled, the type-wide enablement kill
switch, and isAvailable(). Empty or non-canonical IDs return
FALSE. A valid ID with no stored config uses plugin defaults.
Callers that need push_disabled vs type_disabled should use
isTypeEnabled(), isTypeAvailable(), and
isPushEnabled() on resolveConsumerConfig() instead.
Unavailable types use the same type_disabled invocation
status.
Loop-aware injection is stored as settings.loop_aware on agent
rows. AiContextConsumerTypeAgent::isLoopAware() reads that key
over the type default. The agent subscriber uses the Agent type,
not resolveConsumerConfig(). It is not a factory method.
Update 10028 copies ai_context.agents rows to
ai_context.consumers (canonical IDs agent:{id}, loop_aware →
settings.loop_aware) and deletes the old object. Updates
10029–10031 migrate usage fields and the usage View. After
drush updatedb, re-export site configuration. Importing leftover
ai_context.agents after migration is a validation error.
See Configuration: Update 10028.
Public API
Selection factory service
Service ID ai_context.selection_factory
(Drupal\ai_context\Service\AiContextSelectionFactoryInterface) is the
supported entry point for running context selection from your own code.
Type-hint the interface. The concrete
AiContextSelectionFactory class is @internal.
| API | Purpose |
|---|---|
getRenderedContext() |
One-liner returning the rendered context string for non-agent consumers |
getResult() |
Full AiContextSelectionResult with cache metadata, selected IDs, and token usage |
fromConsumer() |
Build an AiContextSelection from a saved consumer config entry |
fromParameters() |
Build an AiContextSelection from typed parameters |
isPushAllowed() |
Whether automatic push is allowed (push_enabled, type enablement, and availability) |
loadConsumerConfig() |
Stored consumer config, or NULL |
resolveConsumerConfig() |
Row over plugin defaults, plus type gates |
The trailing $selectionMode argument on getRenderedContext() and
getResult() is public; passing NULL (or omitting it) defers to the
site-wide default in ai_context.settings:selection_mode, which ships as
relevant. $consumerId merges canonical {type}:{instance}
config. Pulls without a consumer log as none. See
Selection modes.
See Services for full signatures and examples.
Scope subscription form builder
Service ID ai_context.scope_subscription_form
(Drupal\ai_context\Service\AiContextScopeSubscriptionFormBuilderInterface)
is the supported way to build and process scope subscription widgets on
forms that are not the shared consumer editor.
| API | Purpose |
|---|---|
hasSubscribableScopes() |
Whether any enabled scope supports subscriptions |
getNonSubscribableScopeLabels() |
Labels for scopes that cannot be subscribed to |
buildWidgets() |
Per-scope subscription form elements (same UI as the consumer editor) |
extractValues() |
Normalize submitted widgets into nested scope values; pass stored subscriptions as $previous so hidden scopes are kept |
buildSummary() |
Render-array summary of selected subscriptions |
Do not depend on plugin.manager.ai_context_scope for subscription UI; use
this service instead. See
Services.
Selection and result value objects
Drupal\ai_context\Model\AiContextSelectionParams— validated parameter DTO forfromParameters(). Build instances withfromArray()when passing raw input from forms, plugins, or other loosely typed sources.Drupal\ai_context\Model\AiContextConsumerStoredConfig— storedai_context.consumersconfig fromloadConsumerConfig().Drupal\ai_context\Model\AiContextConsumerResolvedConfig— stored config over plugin defaults fromresolveConsumerConfig().Drupal\ai_context\Model\AiContextSelection— the immutable selection passed into selection.Drupal\ai_context\Model\AiContextSelectionResult— the immutable result returned from selection (rendered text, selected item IDs, token usage, cache metadata).
The supported way to run a selection is through ai_context.selection_factory,
which builds these objects and invokes selection for you. The params DTO
getters, selection fields, and result accessors — such as getRenderedText(),
getSelectedItemIds(), and getCacheableMetadata() — are public.
Selection modes
The three selection mode identifiers and their nested meaning are public 1.0 behavior. After 1.0.0, changing a mode name, default, or what each rung includes requires a deprecation path. Callers may depend on:
| Identifier | Constant on AiContextSelection |
Role |
|---|---|---|
minimal |
SELECTION_MODE_MINIMAL |
Strictest rung |
relevant |
SELECTION_MODE_RELEVANT |
Shipped site-wide default |
broad |
SELECTION_MODE_BROAD |
Most inclusive rung |
Frozen contract:
- The modes are a nested ladder: each rung includes everything the one above it does. Hard context filters apply in every mode.
- An item with no values for a subscribed scope does not earn subscription credit for that scope.
minimal: Global items, always/never overrides, Specific Entities matches, and strict scope subscription matches.relevant(the shipped value of the site-wide defaultai_context.settings:selection_mode, which applies whenfromConsumer(),fromParameters(),getRenderedContext(),getResult(), or the function-callselection_modereceive no mode, and to unrecognized stored consumer config): Minimal, plus items that Languages, Site Sections, Entity Types, or Taxonomy Terms positively match (matchesRequestContext()returnsTRUE) even without a subscription to that scope.broad: Relevant, plus remaining published candidates that pass hard filters, ordered by Priority, appended after the scope-scored bucket to fill leftover token budget. Broad supplements; it does not replace scope-scored or exact-match groups.- Consumer config key
selection_modeand theget_relevant_ai_context_itemsfunction-call parameter use these identifiers.
Candidate ceilings, chunking, and the selector pipeline remain internal. Those ceilings must not drop an item from a higher rung that a lower rung selected. See Services for constructor fields and examples.
Selector pipeline events
The ai_context.selection.* events are the supported way to inspect or alter
selection without coupling to selector internals:
ai_context.selection.items_selected(AiContextSelectionItemsSelectedEvent)ai_context.selection.text_rendered(AiContextSelectionTextRenderedEvent)
Event name constants live on Drupal\ai_context\Event\AiContextSelectionEvents.
See the Supported extension model in
Events for the full contract, subscriber constraints, and examples.
Scope plugins, hooks, and alters
AiContextScopeplugins are a public extension point. Create your own scope plugins as described in Custom scopes and the Scope API. OptionalAiContextScopeValueMatchingInterfaceis the supported way for a custom scope to define wildcard or hierarchical matching. OptionalAiContextScopeStoredValueInterfaceis the supported way for a custom scope to declare which stored values are stale after a delete or settings change.- The documented hooks and alter hooks are public. See Hooks.
AiContextScopeCacheableMetadataInterfaceis an optional companion for dynamic label cache dependencies. It is intentionally separate fromAiContextScopeInterface, so existing direct implementations remain valid.- 1.0 custom scope storage: supported custom scopes store values in the
context item scope field. Use
getScope()/setScope()for the grouped array (scope[plugin_id] => string[]).create(),$entity->set('scope', …), Default Content, and recipes require Field API deltas (scope_id+value). See Storing scope values. Custom entity-field storage, cleanup, and form lifecycle integration are internal module concerns in 1.0; scope-field plugins are indexed automatically. - Entity scope helpers on
AiContextItemare public for reading scope state. Resolve effective scope throughgetScopeItem()first; use stored accessors (getScope(),getScopeValues(),isStoredGlobal(),getStoredEntityItemScopeValues()) only when you need values saved on this item.getEntityItems()andhasEntityItems()are the supported way to read referenced entities. See Scope API.
Do not inject plugin.manager.ai_context_scope from contrib modules; prefer
the subscription form builder service for subscription widgets. The manager
may gain orchestration methods in a future release without a deprecation
period while marked @internal.
Consumer type plugins
AiContextConsumerType plugins are a public extension point. Implement
them in the consuming project when the type must read that project's
internals. Types that only enumerate public config entities and match
emitted request tags may live in AI Context. The plugin attribute,
interface, base class, and AiContextConsumerId are public. The Agent
type's isLoopAware() is public for that type only. AI Context ships
agent and automator. The consumer type manager, router, route
result, route subscriber, and listing forms are internal.
See Consumer type API.
Function call plugins
The context_tools function call plugin IDs
(ai_context:list_ai_context_items,
ai_context:load_ai_context_item_by_id,
ai_context:get_relevant_ai_context_items) are stable identifiers for agent
configuration. See Function calls.
Drupal\ai_context\Plugin\AiFunctionCall\AiContextInheritsAgentIdInterface
is the supported opt-in for tools that should inherit the running agent
ID when agent_id is omitted. Implement it and declare an agent_id
context definition. See
Events.
Generic-path invocation result
On PreGenerateResponseEvent, the generic subscriber writes an
ai_context key to event metadata and chat-input debug data after it
routes a request. Drupal\ai_context\Model\AiContextInvocationResult
is the public API for that key and the status values
(AiContextInvocationResult::KEY, ::PUSHED, and so on). Callers
may depend on this payload shape:
| Key | Meaning |
|---|---|
consumer_id |
Canonical consumer ID, or empty when routing declined or was ambiguous |
status |
One of the AiContextInvocationResult status constants |
item_count |
Number of selected item IDs |
item_ids |
Selected context item IDs |
TYPE_DISABLED covers the type kill switch and isAvailable().
The subscriber is single-use per ChatInput. The debug-data copy of
the key marks the input as processed: when the same input object is
dispatched again (a tool-call inner request reusing the outer
request's ChatInput), the subscriber re-publishes the stored
payload to the new event's metadata and appends no second context
block. A rebuilt or fresh ChatInput gets a full pass; debug data
is in-memory and lives only as long as the input object.
An absent ai_context key means the request was not handled (non-chat,
unrouted tags, or excluded agent/assistant tags). The payload never
includes prompt or rendered text. See
Events.
Internal implementation
The following are internal. They may change without deprecation; do not depend on them, decorate them, or replace them.
Selector and its pipeline
ai_context.selector (Drupal\ai_context\Service\AiContextSelector) and
AiContextSelectorInterface — and the candidate pipeline (loading, scoring,
priority merge, and subcontext resolution) — are internal. Extend selection
through the selector pipeline events, not
by depending on or replacing the selector.
Supporting services
These services support the selector and are not part of the public surface:
ai_context.scope_resolver— scope scoring and hard context filtersai_context.scope_index— denormalized scope index used for prefilteringai_context.subcontext_resolver— child item resolutionai_context.renderer,ai_context.token_estimator, andai_context.subscription_budget— rendering, token budgeting, and consumer settings budget summary internalsai_context.request_info_resolver,ai_context.scope_cleanup,ai_context.children,ai_context.item_validator, and the remaining services in Services
Event subscribers, route subscribers, access checks, cache contexts, and the
scope plugin manager (plugin.manager.ai_context_scope) are likewise internal
infrastructure. All entries under Support services
in the services reference are internal — they are not labeled individually.
Use the public extension points above instead.
Entity display helpers
AiContextItem::getEntityItemData() and
buildEntityItemRenderArray() format Specific Entities for display.
They are leftover UI helpers and may be removed. Use getEntityItems()
to read referenced entities. Use scope plugin labels for admin display.
Service decoration and replacement
Decorating, extending, or replacing internal services (for example
ai_context.selector) in a services.yml file is not supported. The
internal pipeline may change shape, argument order, or service graph between
releases. Use the selector pipeline events
and the selection factory for supported customization.
See also
- Events — Supported extension model
- Services — per-service public/internal status labels
- Access boundaries — permissions versus entity access
- Upgrade and compatibility policy
- Scope API and Custom scopes
- Consumer type API
- Hooks