Skip to content

Fix RPT-1 dtype map + composition enhancement; add integrator FAQ#14

Open
sleibach wants to merge 9 commits into
cap-js:mainfrom
sleibach:main
Open

Fix RPT-1 dtype map + composition enhancement; add integrator FAQ#14
sleibach wants to merge 9 commits into
cap-js:mainfrom
sleibach:main

Conversation

@sleibach
Copy link
Copy Markdown

Summary

Three small, independent improvements found while integrating the plugin into a CAP application:

Fix: align CDS→RPT-1 dtype map with the inference API enum

/v2/inference/deployments/{id}/predict only accepts dtype values 'string' | 'numeric' | 'date'. The map emitted 'bool' for
cds.Boolean and 'datetime' for cds.DateTime / cds.Timestamp, which caused HTTP 422 from RPT-1 for any entity carrying those
types. Booleans now map to 'string' (round-trip as 'true' / 'false'), DateTime / Timestamp map to 'string' so the full ISO
value is preserved as an opaque categorical token (declaring 'date' would drop the time portion and may reject inputs that aren't pure
YYYY-MM-DD), and Date keeps 'date'.

Fix: enhance composition children on the current CDS CSN flavour

The CSN enhancer walked entity.compositions[*] only — a legacy shape. In CDS ≥ 7 compositions live in entity.elements[*] with type === 'cds.Composition', so on modern projects the legacy walk was empty and nested entities (e.g. an Approvers composition under a
draft ChangeRequests root) never received @UI.Recommendations. Recommendations were silently missing from any field on a draft
child, even with valid value lists. Replaced the single-level walk with a recursive walk over both shapes with cycle protection.
enhanceEntity is idempotent, so targets reachable through both paths are enhanced once.

Docs: short "how it works" FAQ for integrators

Added a six-question FAQ to the README covering: what annotations the plugin emits, when the READ handler runs, what context goes to
RPT-1 (the 2000-row cap, stripped audit/binary columns, [PREDICT] placeholders), how @Common.Text descriptions are populated, the
RPT-1 deployment lifecycle, and the local-mock behaviour. Includes an [!IMPORTANT] callout pointing at @UI.RecommendationState : 0
for sensitive fields, since everything in the context payload is forwarded to AI Core.

Soeren Leibach added 3 commits April 29, 2026 22:33
RPT-1's /predict endpoint only accepts dtype values 'string', 'numeric'
or 'date' and returns 422 for anything else. The map previously emitted
'bool' for cds.Boolean and 'datetime' for cds.DateTime / cds.Timestamp,
which broke prediction for any entity carrying those types.

- cds.Boolean: 'string' (round-trips as 'true' / 'false')
- cds.DateTime / cds.Timestamp: 'string' so the full ISO value is kept
  as an opaque token (declaring 'date' would drop the time portion and
  may reject non-YYYY-MM-DD inputs)
- cds.Date stays 'date'
Short summary of the runtime behaviour so consumers don't have to read
the source to answer the most common questions: what annotations get
emitted, when the handler runs, what context is sent to RPT-1 (the 2000
active-rows cap, the column stripping, the [PREDICT] placeholder), how
descriptions are filled, the RPT-1 deployment lifecycle, and the local
mock behaviour. Includes a callout reminding integrators to opt sensitive
fields out via @UI.RecommendationState.
The CSN enhancer walked `entity.compositions[*]` only — a legacy CSN
shape. In CDS >= 7 compositions live in `entity.elements[*]` with
`type === 'cds.Composition'`, so the legacy walk was empty and
nested entities (e.g. `Approvers` under a `ChangeRequests` draft)
never received `@UI.Recommendations`. Recommendations would silently
be missing from any field on a child of a draft root, even when the
child's fields had perfectly good value lists.

Replaced the single-level walk with a recursive walk that handles
both the legacy `entity.compositions` map and the modern
`entity.elements` form, with a visited set to break composition
cycles. enhanceEntity is already idempotent, so a target reachable
through both paths is enhanced once.
@sleibach sleibach requested a review from a team as a code owner April 30, 2026 07:56
Until now, the CSN enhancer only emitted SAP_Recommendations for fields
auto-detected as having a value help (`@Common.ValueList.CollectionPath`
or `@cds.odata.valuelist` on the association target). Free-form scalar
fields — typically numerics like measurement ranges or planning
estimates — were silently ignored even when they would be excellent
prediction targets.

Adds an opt-in field-level annotation `@AI.Recommend`. When set on a
scalar element (no association / composition / unmanaged), the field is
included in the entity's `<Entity>_Recommendations` companion alongside
auto-detected value-helped fields and Fiori Elements' soft-fill
placeholder renders the prediction in the empty input.

`task_type` is now chosen per target column: numeric scalars opted in
via `@AI.Recommend` (`cds.Integer*`, `cds.Decimal`, `cds.Double`,
`cds.UInt8`) use `regression` so RPT-1 can interpolate continuous
values; everything else — including value-list FKs and string fields —
uses `classification` (existing behaviour). The opt-in check is what
guards FK columns from being reclassified: those carry no
`@AI.Recommend`, so they remain categorical even though their CDS type
is numeric.

Widens the `task_type` enum on `predictRowColumns` from `{classification}`
to `{classification, regression}` so the action signature accepts both.
@SirSimon04
Copy link
Copy Markdown
Contributor

Hi @sleibach, thank you for your contribution. We will have a look at it. Don't worry about the hybrid integration tests, we will update the credentials soon so they run again.

Soeren Leibach and others added 4 commits April 30, 2026 15:27
Two related stability bugs hit by larger schemas:

1. Empty rows. Reading a draft entity whose composition was empty
   passed `rows: []` to `_fetchPrediction`, whose schema-derivation
   reduce immediately dereferenced `rows[0][ele]` on `undefined` —
   taking the whole server down on a TypeError. `_fetchPrediction`
   now returns `{}` for empty input. The READ handler also
   short-circuits when the response set is empty, avoiding a
   needless AI Core round-trip.

2. RPT-1 inference limits. The model rejects payloads with more than
   10 target columns or more than 100 columns per row, returning a
   noisy HTTP 422. The plugin previously sent through whatever the
   model had collected, so any sufficiently wide entity (think
   denormalised composition children with many value-helped fields)
   produced a 422 on every read. `_fetchPrediction` now checks both
   limits up front, logs a warning with a hint to opt columns out
   via `@UI.RecommendationState : 0`, and returns an empty result —
   the surrounding READ still completes, the page still renders, the
   user just doesn't get recommendations on that particular entity
   until they trim the column set.
@SirSimon04
Copy link
Copy Markdown
Contributor

Just as FYI: We are currently working on setting up our CI Pipeline so that the tests can also be executed from forks, so also the integration tests run for your PR.

@SirSimon04 SirSimon04 deployed to pr-approval May 12, 2026 13:50 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants