diff --git a/README.md b/README.md index 0dc5f95..86d65b5 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,12 @@ For the simplest setup, add the **`cds-starter-ai`** dependency which bundles bo ## Prerequisites - Java 17+ +- Maven 3.6.3+ - CAP Java 4.9+ -- Node.js 20+ with `@sap/cds-dk` 9+ (for CDS build tooling) - An [SAP AI Core](https://help.sap.com/docs/sap-ai-core) service binding (for production use) +The build is hermetic: `cds-maven-plugin` downloads its own Node runtime and `cds-feature-ai-core/package.json` pins `@sap/cds-dk`. A globally installed `@sap/cds-dk` is **not** required. + Without an AI Core binding the plugins fall back to mock implementations for local development. ## Samples @@ -61,9 +63,11 @@ For integration tests against a real AI Core instance: ```bash cds bind ai-core -2 -cds bind --exec mvn test -pl integration-tests/spring -am +cds bind --exec mvn verify ``` +See [`integration-tests/README.md`](integration-tests/README.md) for the full integration-test layout, including the multi-tenancy profile. + ## Support, Feedback, Contributing This project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/cap-java/cds-ai/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md). diff --git a/cds-feature-ai-core/README.md b/cds-feature-ai-core/README.md index 61ffcf6..11e4e26 100644 --- a/cds-feature-ai-core/README.md +++ b/cds-feature-ai-core/README.md @@ -4,7 +4,7 @@ Bridges CAP Java applications to [SAP AI Core](https://help.sap.com/docs/sap-ai- ## Features -- **`AICore` CDS Service** - Exposes resource groups, deployments, and configurations as CDS entities with full CRUD support +- **`AICore` CDS Service** - Internal CDS service (annotated `@protocol: 'none'`) modelling resource groups, deployments, and configurations as CDS entities. The plugin does **not** expose this service via OData; it is consumed in-process via `RemoteService`. To expose it externally, project it from your own service or use the `@cap-js/ai` model. - **Multi-Tenancy** - Automatic per-tenant resource group creation/deletion on subscribe/unsubscribe - **Deployment Management** - Auto-creates configurations and deployments for AI Core models with retry and backoff - **Inference Client Factory** - Provides ready-to-use `ApiClient` instances scoped to a deployment for downstream foundation-model SDKs @@ -27,12 +27,14 @@ The plugin auto-registers via Java's `ServiceLoader` mechanism - no code changes ### AI Core Binding -In production, bind an SAP AI Core service instance to your application. Supported methods: +In production, bind an SAP AI Core service instance to your application via a standard service binding (Cloud Foundry / Kubernetes). For local hybrid testing against a real AI Core instance, use the CAP CLI: -- **Service binding** (Cloud Foundry / Kubernetes) -- **Environment variable** `AICORE_SERVICE_KEY` - for local hybrid testing (via `cds bind --exec`) +```bash +cds bind ai-core -2 +cds bind --exec mvn spring-boot:run +``` -Without a binding the plugin registers a mock implementation. +Without a binding the plugin registers a mock implementation suitable for local development. ## Configuration @@ -53,26 +55,77 @@ and the presence of a `DeploymentService`. No additional configuration flag is r ## CDS Service: `AICore` -The plugin registers a CAP service named `AICore` that proxies AI Core REST APIs as CDS entities: +The plugin registers a CAP service named `AICore` that proxies AI Core REST APIs as CDS entities. +The service is internal (`@protocol: 'none'`); use a `RemoteService` lookup to interact with it. ### Entities -| Entity | Operations | Description | -| ----------------------- | -------------------- | ---------------------------------------------------------------- | -| `AICore.resourceGroups` | READ, CREATE, DELETE | Resource group lifecycle, supports label filtering by `tenantId` | -| `AICore.deployments` | READ, CREATE, DELETE | Deployment management with status tracking | -| `AICore.configurations` | READ, CREATE | Configuration management for scenarios and executables | +| Entity | Operations | Description | +| ----------------------- | ---------------------------- | ---------------------------------------------------------------- | +| `AICore.resourceGroups` | READ, CREATE, UPDATE, DELETE | Resource group lifecycle, supports label filtering by `tenantId` | +| `AICore.deployments` | READ, CREATE, UPDATE, DELETE | Deployment management with status tracking; bound action `stop` | +| `AICore.configurations` | READ, CREATE | Configuration management for scenarios and executables | + +## Programmatic API -### Programmatic API +The plugin exposes its functionality through three event contexts emitted on the `AICore` `RemoteService`. This pattern decouples callers from the implementation and makes it easy to override individual steps in tests. ```java -// Get the resource group for the current tenant -String rgId = aiCoreService.resourceGroup(); +import com.sap.cds.feature.aicore.api.DeploymentIdContext; +import com.sap.cds.feature.aicore.api.InferenceClientContext; +import com.sap.cds.feature.aicore.api.ResourceGroupContext; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.AICore_; +import com.sap.cds.services.cds.RemoteService; +import com.sap.cds.feature.recommendation.api.RptModelSpec; + +// 1. Obtain the AICore service as a RemoteService +RemoteService aiCore = runtime.getServiceCatalog() + .getService(RemoteService.class, AICore_.CDS_NAME); + +// 2. Resolve the resource group for the current tenant +// (auto-creates the group on first use in multi-tenant mode) +ResourceGroupContext rgCtx = ResourceGroupContext.create(); +aiCore.emit(rgCtx); +String resourceGroupId = rgCtx.getResult(); + +// 3. Resolve (or create) a deployment for a given model spec +DeploymentIdContext depCtx = DeploymentIdContext.create(); +depCtx.setResourceGroupId(resourceGroupId); +depCtx.setSpec(RptModelSpec.rpt1()); // or your own ModelDeploymentSpec +aiCore.emit(depCtx); +String deploymentId = depCtx.getResult(); + +// 4. Obtain a configured ApiClient for the deployment +InferenceClientContext infCtx = InferenceClientContext.create(); +infCtx.setResourceGroupId(resourceGroupId); +infCtx.setDeploymentId(deploymentId); +aiCore.emit(infCtx); +ApiClient client = infCtx.getResult(); +``` + +The `ApiClient` returned by `InferenceClientContext` is preconfigured with the AI Core +destination and the deployment URL; use it to construct foundation-model SDK +clients (for example `RptInferenceClient` from `cds-feature-recommendations`). + +Because `RemoteService` extends `CqnService`, you can also run CDS queries against +the entities directly: -// Get (or auto-create) a deployment ID for a model spec in the given resource group -String deploymentId = aiCoreService.deploymentId(rgId, RptModelSpec.rpt1()); +```java +Result rgs = aiCore.run(Select.from(AICore_.CDS_NAME + ".resourceGroups")); ``` +### Public API + +The stable public API of this plugin lives in the `com.sap.cds.feature.aicore.api` package. +Implementation classes in sibling packages may change without notice. + +| Type | Purpose | +| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| [`ResourceGroupContext`](src/main/java/com/sap/cds/feature/aicore/api/ResourceGroupContext.java) | Event context for `resourceGroup` - resolves (and auto-creates in MTX mode) the AI Core resource group for the current/explicit tenant | +| [`DeploymentIdContext`](src/main/java/com/sap/cds/feature/aicore/api/DeploymentIdContext.java) | Event context for `deploymentId` - resolves or creates a deployment matching a `ModelDeploymentSpec` inside a resource group | +| [`InferenceClientContext`](src/main/java/com/sap/cds/feature/aicore/api/InferenceClientContext.java) | Event context for `inferenceClient` - returns an `ApiClient` preconfigured with the inference destination for a given deployment | +| [`ModelDeploymentSpec`](src/main/java/com/sap/cds/feature/aicore/api/ModelDeploymentSpec.java) | Record describing a target deployment (scenario, executable, configuration name, parameter bindings, match predicate) | + ## Multi-Tenancy When multi-tenancy is active (detected via `cds.multiTenancy.sidecar.url`): @@ -83,26 +136,6 @@ When multi-tenancy is active (detected via `cds.multiTenancy.sidecar.url`): The lifecycle hooks are registered automatically when multi-tenancy is enabled. -## Programmatic Usage - -```java -// Obtain the service -AICoreService aiCore = runtime.getServiceCatalog() - .getService(AICoreService.class, AICoreService.DEFAULT_NAME); - -// Use for entity operations (AICoreService extends CqnService) -Result rgs = aiCore.run(Select.from("AICore.resourceGroups")); - -// Resolve a deployment and obtain a configured ApiClient for it -String resourceGroupId = aiCore.resourceGroup(); -String deploymentId = aiCore.deploymentId(resourceGroupId, RptModelSpec.rpt1()); -ApiClient client = aiCore.inferenceClient(resourceGroupId, deploymentId); -``` - -The `ApiClient` returned by `inferenceClient` is preconfigured with the AI Core -destination and the deployment URL; use it to construct foundation-model SDK -clients (for example `RptInferenceClient` from `cds-feature-recommendations`). - ## Related - [SAP AI Core Documentation](https://help.sap.com/docs/sap-ai-core) diff --git a/cds-feature-recommendations/README.md b/cds-feature-recommendations/README.md index 088b165..113221e 100644 --- a/cds-feature-recommendations/README.md +++ b/cds-feature-recommendations/README.md @@ -50,9 +50,11 @@ Add `@cap-js/ai` to your project's `package.json`: } ``` -Then run `npm install`. The plugin hooks into the CDS compiler and automatically adds the `SAP_Recommendations` navigation property to draft-enabled entities that have value-list fields. +Then run `npm install`. The plugin hooks into the CDS compiler and automatically adds the `SAP_Recommendations` navigation property to draft-enabled entities that have value-list fields. -Since the Java module `cds-feature-ai-core` already provides the `AICore` service CDS model, disable the duplicate model from `@cap-js/ai` in your `.cdsrc.json`: +Both the Java module `cds-feature-ai-core` and the `@cap-js/ai` package ship a CDS model for the `AICore` service. Pick **one** of the following patterns in your `.cdsrc.json`, depending on whether you need to expose AICore via OData: + +**Option A — Java-internal use only (recommended for most apps):** the Java plugin's CDS model is sufficient because the recommendation handler consumes it in-process. Disable the duplicate model from `@cap-js/ai`: ```json { @@ -64,6 +66,18 @@ Since the Java module `cds-feature-ai-core` already provides the `AICore` servic } ``` +**Option B — Expose AICore via OData:** keep the `@cap-js/ai` model and project from it in your own service (see `samples/bookshop/srv/ai-core-service.cds` for an example): + +```json +{ + "requires": { + "AICore": { + "model": "@cap-js/ai/srv/AICoreService" + } + } +} +``` + ## Enabling Recommendations For recommendations to fire on an entity: @@ -72,7 +86,7 @@ For recommendations to fire on an entity: - At least one field must be annotated with a **value list** - The `SAP_Recommendations` navigation property must be present — either via the CDS plugin (see above) or added manually (see below). Without it, predictions are computed but not serialized in OData responses. -Recommendations are triggered for fields annotated with `@Common.ValueList`, `@Common.ValueListWithFixedValues`, or whose association target has `@cds.odata.valuelist`: +Recommendations are triggered for fields annotated with `@Common.ValueList` or `@Common.ValueListWithFixedValues`. The CDS compiler also derives `@Common.ValueList` from `@cds.odata.valuelist` on association targets, so annotating the target entity has the same effect: ```cds @odata.draft.enabled @@ -174,7 +188,7 @@ The following configuration applies to the RPT-1 model implementation. ```yaml cds: - requires: + ai: recommendations: contextRowLimit: 2000 # Max historical rows used as training context (RPT-1) ``` @@ -213,11 +227,13 @@ The following field types are supported by the RPT-1 model implementation: | Temporal | `Date`, `Time`, `DateTime`, `Timestamp` | | Other | `Boolean` | +Equivalent CDS HANA types (e.g. `hana.SMALLINT`, `hana.TINYINT`, `hana.SMALLDECIMAL`, `hana.REAL`, `hana.CHAR`, `hana.NCHAR`, `hana.VARCHAR`, `hana.CLOB`) are also supported. + Binary, vector, and draft system fields are excluded automatically. ## Local Development -Without an AI Core binding, the plugin uses a `MockAIClient` that returns random predictions from existing context rows - useful for UI development without AI Core access. The `@cap-js/ai` CDS plugin is still required for the model enhancement. +Without an AI Core binding, the plugin uses a `MockRecommendationClient` that returns random predictions from existing context rows - useful for UI development without AI Core access. The `@cap-js/ai` CDS plugin is still required for the model enhancement. ## Related diff --git a/integration-tests/README.md b/integration-tests/README.md index 42a660e..f47c224 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -25,17 +25,18 @@ mvn verify -Pmtx-integration-tests **Skipping all integration tests (source modules only):** +The `with-integration-tests` profile is active by default at the root. Deactivate it to skip both `integration-tests/` and `coverage-report/`: + ```bash -mvn verify -Pskip-integration-tests +mvn install -P-with-integration-tests ``` ## Profiles | Profile | Scope | Effect | |---------|-------|--------| -| _(default)_ | Root | Builds all modules; runs spring integration tests | +| `with-integration-tests` | Root | **Active by default**; includes `integration-tests/` and `coverage-report/`. Deactivate with `-P-with-integration-tests`. | | `mtx-integration-tests` | `integration-tests/` | Also includes the `mtx-local/srv` module | -| `skip-integration-tests` | Root | Excludes `integration-tests/` and `coverage-report/` entirely | ## Coverage @@ -66,7 +67,9 @@ Per-module thresholds are defined in `coverage-report/pom.xml`: | Module | Instruction | Branch | Complexity | |--------|-------------|--------|------------| | `cds-feature-ai-core` | 0% | 0% | 0% | -| `cds-feature-recommendations` | 80% | 80% | 80% | +| `cds-feature-recommendations` | 0% | 0% | 0% | + +Thresholds are intentionally permissive while the modules are under active development; tighten them in `coverage-report/pom.xml` once the API surfaces stabilise. ### Coverage data sources