Back to skills
SkillHub ClubWrite Technical DocsFull StackData / AITech Writer

sap-cloud-sdk-ai

Integrates SAP Cloud SDK for AI into JavaScript/TypeScript and Java applications. Use when building applications with SAP AI Core, Generative AI Hub, or Orchestration Service. Covers chat completion, embedding, streaming, function calling, content filtering, data masking, document grounding, prompt registry, and LangChain/Spring AI integration. Supports OpenAI GPT-4o, Claude, Gemini, Amazon Nova, and other foundation models via SAP BTP.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
152
Hot score
96
Updated
March 20, 2026
Overall rating
C2.9
Composite score
2.9
Best-practice grade
F39.6

Install command

npx @skill-hub/cli install secondsky-sap-skills-sap-cloud-sdk-ai

Repository

secondsky/sap-skills

Skill path: plugins/sap-cloud-sdk-ai/skills/sap-cloud-sdk-ai

Integrates SAP Cloud SDK for AI into JavaScript/TypeScript and Java applications. Use when building applications with SAP AI Core, Generative AI Hub, or Orchestration Service. Covers chat completion, embedding, streaming, function calling, content filtering, data masking, document grounding, prompt registry, and LangChain/Spring AI integration. Supports OpenAI GPT-4o, Claude, Gemini, Amazon Nova, and other foundation models via SAP BTP.

Open repository

Best for

Primary workflow: Write Technical Docs.

Technical facets: Full Stack, Data / AI, Tech Writer, Integration.

Target audience: everyone.

License: GPL-3.0.

Original source

Catalog source: SkillHub Club.

Repository owner: secondsky.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install sap-cloud-sdk-ai into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/secondsky/sap-skills before adding sap-cloud-sdk-ai to shared team environments
  • Use sap-cloud-sdk-ai for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: sap-cloud-sdk-ai
description: |
  Integrates SAP Cloud SDK for AI into JavaScript/TypeScript and Java applications. Use when building applications with SAP AI Core, Generative AI Hub, or Orchestration Service. Covers chat completion, embedding, streaming, function calling, content filtering, data masking, document grounding, prompt registry, and LangChain/Spring AI integration. Supports OpenAI GPT-4o, Claude, Gemini, Amazon Nova, and other foundation models via SAP BTP.
license: GPL-3.0
metadata:
  version: 2.0.0
  last_verified: 2025-11-27
---

# SAP Cloud SDK for AI

The official SDK for SAP AI Core, SAP Generative AI Hub, and Orchestration Service.

## When to Use This Skill

Use this skill when:
- Integrating AI/LLM capabilities into SAP BTP applications
- Building chat completion or embedding features
- Using GPT-4o, Claude, Gemini, or other models via SAP AI Core
- Implementing content filtering, data masking, or document grounding
- Creating agentic workflows with LangChain or Spring AI
- Managing prompts via Prompt Registry
- Deploying AI models on SAP AI Core

## Table of Contents
- [Quick Start](#quick-start)
- [Prerequisites](#prerequisites)
- [Connection Setup](#connection-setup)
- [Available Packages](#available-packages)
- [Supported Models](#supported-models)
- [Core Features](#core-features)
- [Bundled Resources](#bundled-resources)

## Quick Start

> **Note**: This skill uses SAP Cloud SDK for AI v2.2.0+. If you're migrating from v1.x, see [V1 to V2 Migration Guide](references/v1-to-v2-migration.md) for breaking changes.

### JavaScript/TypeScript

```bash
npm install @sap-ai-sdk/orchestration@^2
```

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [{ role: 'user', content: '{{?question}}' }]
  }
});

const response = await client.chatCompletion({
  placeholderValues: { question: 'What is SAP?' }
});
console.log(response.getContent());
```

### Java

```xml
<dependency>
  <groupId>com.sap.ai.sdk</groupId>
  <artifactId>orchestration</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
```

```java
var client = new OrchestrationClient();
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O);
var prompt = new OrchestrationPrompt("What is SAP?");
var result = client.chatCompletion(prompt, config);
System.out.println(result.getContent());
```

## Prerequisites

- **Node.js 20+** (JavaScript) or **Java 17+** (Java)
- **SAP AI Core** service instance (extended or sap-internal plan)
- **Orchestration deployment** in AI Core (default resource group has this)

## Connection Setup

### BTP Runtime (Cloud Foundry/Kyma)
Bind AI Core service instance to your application. SDK auto-detects via `VCAP_SERVICES` or mounted secrets.

### Local Development
Set environment variable:
```bash
export AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"...","serviceurls":{"AI_API_URL":"..."}}'
```

Or use CAP hybrid mode:
```bash
# JavaScript
cds bind -2 <AICORE_INSTANCE> && cds-tsx watch --profile hybrid

# Java
cds bind --to aicore --exec mvn spring-boot:run
```

For detailed connection options, see `references/connecting-to-ai-core.md`

## Available Packages

### JavaScript/TypeScript
| Package | Purpose |
|---------|---------|
| `@sap-ai-sdk/orchestration` | Chat completion, filtering, grounding |
| `@sap-ai-sdk/foundation-models` | Direct model access (OpenAI) |
| `@sap-ai-sdk/langchain` | LangChain integration |
| `@sap-ai-sdk/ai-api` | Deployments, artifacts, configurations |
| `@sap-ai-sdk/document-grounding` | Pipeline, Vector, Retrieval APIs |
| `@sap-ai-sdk/prompt-registry` | Prompt template management |

### Java
| Artifact | Purpose |
|----------|---------|
| `orchestration` | Chat completion, filtering, grounding |
| `openai` (foundationmodels) | Direct OpenAI model access |
| `core` | Base connectivity |
| `document-grounding` | Pipeline, Vector, Retrieval APIs |
| `prompt-registry` | Prompt template management |

## Supported Models

### Recommended
- **OpenAI**: gpt-4o, gpt-4o-mini, o1, o3-mini
- **Anthropic (AWS)**: Claude 3.5 Sonnet, Claude 4
- **Amazon**: Nova Pro, Nova Lite, Nova Micro
- **Google**: Gemini 2.5 Flash, Gemini 2.0 Flash
- **Mistral**: Medium, Large

### Deprecated Models (Use Replacements)
| Deprecated | Use Instead |
|------------|-------------|
| text-embedding-ada-002 | text-embedding-3-small/large |
| gpt-35-turbo (all variants) | gpt-4o-mini |
| gpt-4-32k | gpt-4o |
| gpt-4 (base) | gpt-4o or gpt-4.1 |
| gemini-1.0-pro | gemini-2.0-flash |
| gemini-1.5-pro/flash | gemini-2.5-flash |
| mistralai--mixtral-8x7b | mistralai--mistral-small-instruct |

## Core Features

### Chat Completion with Streaming

```typescript
// JavaScript
const stream = client.stream({
  placeholderValues: { question: 'Explain SAP CAP' }
});

for await (const chunk of stream.toContentStream()) {
  process.stdout.write(chunk);
}
```

```java
// Java
client.streamChatCompletion(prompt, config)
    .forEach(chunk -> System.out.print(chunk.getDeltaContent()));
```

### Function/Tool Calling

```typescript
// JavaScript
const tools = [{
  type: 'function',
  function: {
    name: 'get_weather',
    parameters: { type: 'object', properties: { city: { type: 'string' } } }
  }
}];

const response = await client.chatCompletion({
  placeholderValues: { question: 'Weather in Berlin?' }
}, { tools });

const toolCalls = response.getToolCalls();
```

### Content Filtering

```typescript
// JavaScript
import { buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildAzureContentSafetyFilter({ Hate: 'ALLOW_SAFE' }),
    output: buildAzureContentSafetyFilter({ Violence: 'ALLOW_SAFE' })
  }
});
```

### Data Masking

```typescript
// JavaScript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  masking: {
    masking_providers: [{
      type: 'sap_data_privacy_integration',
      method: 'anonymization',
      entities: [{ type: 'profile-email' }, { type: 'profile-person' }]
    }]
  }
});
```

### Document Grounding

```typescript
// JavaScript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  grounding: {
    grounding_input: ['{{?question}}'],
    grounding_output: ['{{?context}}'],
    data_repositories: [{ type: 'vector', id: 'my-repo-id' }]
  }
});
```

## Response Helpers

JavaScript SDK provides helper methods:

```typescript
const response = await client.chatCompletion({ placeholderValues });

response.getContent();          // Model output string
response.getTokenUsage();       // { prompt_tokens, completion_tokens, total_tokens }
response.getFinishReason();     // 'stop', 'length', 'tool_calls', etc.
response.getToolCalls();        // Array of function calls
response.getDeltaToolCalls();   // Partial tool calls (streaming)
response.getAllMessages();      // Full message history
response.getAssistantMessage(); // Assistant response only
response.getRefusal();          // Refusal message if blocked
```

Streaming response methods:

```typescript
const stream = client.stream({ placeholderValues });
for await (const chunk of stream.toContentStream()) {
  process.stdout.write(chunk);
}
// After stream ends:
stream.getFinishReason();
stream.getTokenUsage();
```

## Advanced Topics

For detailed guidance:
- **Orchestration features**: `references/orchestration-guide.md`
- **Foundation models (direct OpenAI)**: `references/foundation-models-guide.md`
- **LangChain integration**: `references/langchain-guide.md`
- **Spring AI integration**: `references/spring-ai-guide.md`
- **AI Core management**: `references/ai-core-api-guide.md`
---

## Bundled Resources

### Reference Documentation
- `references/foundation-models-guide.md` - Foundation models and pricing
- `references/ai-core-api-guide.md` - AI Core service API reference
- `references/orchestration-guide.md` - Orchestration service guide
- `references/langchain-guide.md` - LangChain.js integration
- `references/spring-ai-guide.md` - Spring AI integration
- `references/agentic-workflows.md` - Agentic workflow patterns
- `references/connecting-to-ai-core.md` - Connection setup guide
- `references/error-handling.md` - Error handling patterns
- `references/v1-to-v2-migration.md` - V1 to V2 migration guide

## Version Information

| SDK | Current Version | Node/Java Requirement |
|-----|-----------------|----------------------|
| JavaScript | 2.2.0+ | Node.js 20+ |
| Java | 1.13.0 (Core) / 1.12.0 (Latest orchestration) | Java 17+ (21 LTS recommended) |

**Note**: Generated model classes (in `...model` packages) may change in minor releases but are safe to use.

## Common Errors

| Error | Cause | Solution |
|-------|-------|----------|
| "Could not find service bindings for 'aicore'" | Missing AI Core binding | Bind AI Core service or set AICORE_SERVICE_KEY |
| "Orchestration deployment not found" | No deployment in resource group | Deploy orchestration in AI Core or use different resource group |
| Content filter violation | Input/output blocked | Adjust filter thresholds or modify content |
| Token limit exceeded | Response too long | Set max_tokens parameter |

## Documentation Sources

Keep this skill updated using these sources:
- **JS Docs**: [https://github.com/SAP/ai-sdk/tree/main/docs-js](https://github.com/SAP/ai-sdk/tree/main/docs-js)
- **Java Docs**: [https://github.com/SAP/ai-sdk/tree/main/docs-java](https://github.com/SAP/ai-sdk/tree/main/docs-java)
- **JS SDK**: [https://github.com/SAP/ai-sdk-js](https://github.com/SAP/ai-sdk-js)
- **Java SDK**: [https://github.com/SAP/ai-sdk-java](https://github.com/SAP/ai-sdk-java)
- **Release Notes**: Check docs-js/release-notes.mdx and docs-java/release-notes.mdx


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/v1-to-v2-migration.md

```markdown
# V1 to V2 Migration Guide

Complete migration guide for upgrading SAP Cloud SDK for AI from version 1.x to 2.x.

**Note**: As of November 2025, only the JavaScript/TypeScript SDK has been updated to v2.2.0. The Java SDK remains at v1.12.0 for orchestration and v1.13.0 for core.

## Table of Contents
1. [Overview](#overview)
2. [Dependency Update](#dependency-update)
3. [Foundation Models Changes](#foundation-models-changes)
4. [Orchestration Changes](#orchestration-changes)
5. [LangChain Changes](#langchain-changes)
6. [Type Import Changes](#type-import-changes)

---

## Overview

Version 2.0 introduces breaking changes across all packages. Key themes:
- Consolidated `promptTemplating` module (replaces separate `llm` and `templating`)
- `AbortSignal` instead of `AbortController` for streaming
- Renamed properties for consistency
- Type imports restructured

---

## Dependency Update

Update `package.json`:

```json
{
  "dependencies": {
    "@sap-ai-sdk/orchestration": "^2",
    "@sap-ai-sdk/foundation-models": "^2",
    "@sap-ai-sdk/langchain": "^2",
    "@sap-ai-sdk/ai-api": "^2",
    "@sap-ai-sdk/document-grounding": "^2",
    "@sap-ai-sdk/prompt-registry": "^2"
  }
}
```

Then reinstall:
```bash
npm install
```

---

## Foundation Models Changes

### Stream Method Parameter

**Before (v1):**
```typescript
const controller = new AbortController();
const stream = client.stream(config, controller);
// Cancel: controller.abort()
```

**After (v2):**
```typescript
const controller = new AbortController();
const stream = client.stream(config, controller.signal);
// Cancel: controller.abort()
```

### Chat Completion Request Type

**Before (v1):**
```typescript
import { AzureOpenAiCreateChatCompletionRequest } from '@sap-ai-sdk/foundation-models';

const request: AzureOpenAiCreateChatCompletionRequest = { ... };
```

**After (v2):**
```typescript
import { AzureOpenAiChatCompletionParameters } from '@sap-ai-sdk/foundation-models';

const request: AzureOpenAiChatCompletionParameters = { ... };
```

### Response Object Properties

**Before (v1):**
```typescript
const response = await client.run(config);
console.log(response.data); // Direct access
```

**After (v2):**
```typescript
const response = await client.run(config);
console.log(response.getContent());     // Use getter
console.log(response.getTokenUsage());  // Use getter
// Internal: response._data (renamed from .data)
```

### Type Imports

**Before (v1):**
```typescript
import {
  AzureOpenAiChatClient,
  SomeGeneratedType
} from '@sap-ai-sdk/foundation-models';
```

**After (v2):**
```typescript
// Public types (frequently used)
import { AzureOpenAiChatClient } from '@sap-ai-sdk/foundation-models';

// Generated types (internal)
import { SomeGeneratedType } from '@sap-ai-sdk/foundation-models/internal.js';
```

---

## Orchestration Changes

### Module Configuration (MAJOR CHANGE)

**Before (v1):**
```typescript
const client = new OrchestrationClient({
  llm: {
    model_name: 'gpt-4o',
    model_params: { max_tokens: 1000 }
  },
  templating: {
    template: [
      { role: 'system', content: 'You are helpful' },
      { role: 'user', content: '{{?question}}' }
    ]
  }
});
```

**After (v2):**
```typescript
const client = new OrchestrationClient({
  promptTemplating: {
    model: {
      name: 'gpt-4o',
      params: { max_tokens: 1000 }
    },
    prompt: [
      { role: 'system', content: 'You are helpful' },
      { role: 'user', content: '{{?question}}' }
    ]
  }
});
```

### Placeholder Values

**Before (v1):**
```typescript
const response = await client.chatCompletion({
  inputParams: { question: 'Hello' }
});
```

**After (v2):**
```typescript
const response = await client.chatCompletion({
  placeholderValues: { question: 'Hello' }
});
```

### Streaming Configuration

**Before (v1):**
```typescript
const client = new OrchestrationClient({
  llm: { model_name: 'gpt-4o' },
  stream: true
});
```

**After (v2):**
```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  streamOptions: { enabled: true }
});
```

### Response Properties

**Before (v1):**
```typescript
const result = response.orchestration_result;
const modules = response.module_results;
```

**After (v2):**
```typescript
const result = response.final_result;
const modules = response.intermediate_results;
```

### Content Filter Functions

**Before (v1):**
```typescript
import { buildAzureContentFilter } from '@sap-ai-sdk/orchestration';

const filter = buildAzureContentFilter({
  Hate: 'ALLOW_SAFE',
  Violence: 'ALLOW_SAFE'
});
```

**After (v2):**
```typescript
import { buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';

// Input filter
const inputFilter = buildAzureContentSafetyFilter({
  type: 'input',  // Required type parameter
  hate: 'ALLOW_SAFE',     // lowercase property names
  violence: 'ALLOW_SAFE'
});

// Output filter
const outputFilter = buildAzureContentSafetyFilter({
  type: 'output',
  hate: 'ALLOW_SAFE'
});
```

### Llama Guard Filter

**Before (v1):**
```typescript
import { buildLlamaGuardFilter } from '@sap-ai-sdk/orchestration';

const filter = buildLlamaGuardFilter({
  enabledCategories: ['violent_crimes', 'hate']
});
```

**After (v2):**
```typescript
import { buildLlamaGuard38BFilter } from '@sap-ai-sdk/orchestration';

const inputFilter = buildLlamaGuard38BFilter({
  type: 'input',  // Required type parameter
  enabledCategories: ['violent_crimes', 'hate']
});

const outputFilter = buildLlamaGuard38BFilter({
  type: 'output',
  enabledCategories: ['violent_crimes']
});
```

### Translation Configuration

**Before (v1):**
```typescript
import { buildTranslationConfig } from '@sap-ai-sdk/orchestration';

const translation = buildTranslationConfig({
  inputLanguage: 'auto',
  outputLanguage: 'en'
});
```

**After (v2):**
```typescript
import { buildTranslationConfig } from '@sap-ai-sdk/orchestration';

const inputTranslation = buildTranslationConfig({
  type: 'input',  // Required type parameter
  inputLanguage: 'auto',
  targetLanguage: 'en'
});

const outputTranslation = buildTranslationConfig({
  type: 'output',
  targetLanguage: 'de'
});
```

### Grounding Configuration

**Before (v1):**
```typescript
const client = new OrchestrationClient({
  grounding: {
    input_params: ['{{?question}}'],
    output_param: '{{?context}}',
    data_repositories: [...]
  }
});
```

**After (v2):**
```typescript
const client = new OrchestrationClient({
  grounding: {
    grounding_input: ['{{?question}}'],
    grounding_output: ['{{?context}}'],
    data_repositories: [...]
  }
});
```

### Stream Method (Same as Foundation Models)

**Before (v1):**
```typescript
const stream = client.stream(config, controller);
```

**After (v2):**
```typescript
const stream = client.stream(config, { signal: controller.signal });
```

---

## LangChain Changes

### Configuration

LangChain clients follow the same changes as core orchestration:

**Before (v1):**
```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';

const client = new OrchestrationClient({
  llm: { model_name: 'gpt-4o' },
  templating: { template: [...] }
});
```

**After (v2):**
```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';

const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [...]
  }
});
```

### Invoke with Placeholder Values

**Before (v1):**
```typescript
const response = await client.invoke(messages, {
  inputParams: { question: 'Hello' }
});
```

**After (v2):**
```typescript
const response = await client.invoke(messages, {
  placeholderValues: { question: 'Hello' }
});
```

### Response Properties

**Before (v1):**
```typescript
const moduleResults = response.module_results;
```

**After (v2):**
```typescript
const intermediateResults = response.intermediate_results;
```

---

## Type Import Changes

### Summary Table

| Package | Public Types | Internal Types |
|---------|--------------|----------------|
| @sap-ai-sdk/foundation-models | Main export | `internal.js` |
| @sap-ai-sdk/orchestration | Main export | `internal.js` |
| @sap-ai-sdk/langchain | Main export | (follows orchestration) |

### Example

```typescript
// Public (frequently used) - always available
import {
  OrchestrationClient,
  buildAzureContentSafetyFilter
} from '@sap-ai-sdk/orchestration';

// Internal (generated) - import from internal.js
import {
  SomeGeneratedType,
  AnotherGeneratedType
} from '@sap-ai-sdk/orchestration/internal.js';
```

---

## Migration Checklist

- [ ] Update all package versions to `^2`
- [ ] Replace `llm` + `templating` with `promptTemplating`
- [ ] Rename `inputParams` to `placeholderValues`
- [ ] Update stream method to use `signal` instead of controller
- [ ] Rename `orchestration_result` to `final_result`
- [ ] Rename `module_results` to `intermediate_results`
- [ ] Replace `buildAzureContentFilter` with `buildAzureContentSafetyFilter`
- [ ] Add `type` parameter to filter/translation builders
- [ ] Update property names to lowercase (Hate → hate)
- [ ] Move generated type imports to `internal.js`
- [ ] Update grounding `input_params`/`output_param` to `grounding_input`/`grounding_output`
- [ ] Test all functionality after migration

---

## Documentation Links

- Upgrade Guide: [https://github.com/SAP/ai-sdk/blob/main/docs-js/upgrade-guide.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/upgrade-guide.mdx)
- Release Notes: [https://github.com/SAP/ai-sdk/blob/main/docs-js/release-notes.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/release-notes.mdx)

```

### references/connecting-to-ai-core.md

```markdown
# Connecting to AI Core Guide

Complete guide for configuring connectivity to SAP AI Core.

## Table of Contents
1. [Overview](#overview)
2. [Service Binding (Default)](#service-binding-default)
3. [Environment Variable](#environment-variable)
4. [CAP Hybrid Mode](#cap-hybrid-mode)
5. [BTP Destination Service](#btp-destination-service)
6. [Custom Destination](#custom-destination)
7. [Resource Groups](#resource-groups)
8. [Troubleshooting](#troubleshooting)

---

## Overview

SAP Cloud SDK for AI uses the SAP Cloud SDK Destination concept for AI Core connectivity. The SDK automatically detects credentials in this order:

1. **Service Binding** - Bound AI Core service instance
2. **Environment Variable** - `AICORE_SERVICE_KEY`
3. **BTP Destination** - Named destination in BTP
4. **Custom Destination** - Programmatically provided

---

## Service Binding (Default)

### Cloud Foundry

Create and bind AI Core service instance:

```bash
# Create service instance
cf create-service aicore extended my-aicore-instance

# Bind to application
cf bind-service my-app my-aicore-instance

# Restage application
cf restage my-app
```

The SDK automatically reads credentials from `VCAP_SERVICES`:

```json
{
  "aicore": [{
    "credentials": {
      "clientid": "...",
      "clientsecret": "...",
      "url": "[https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com",](https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com",)
      "serviceurls": {
        "AI_API_URL": "[https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2"](https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2")
      }
    }
  }]
}
```

### Kubernetes/Kyma

Mount service binding as secret:

```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        - name: my-app
          volumeMounts:
            - name: aicore-binding
              mountPath: /etc/secrets/sapcp/aicore/my-aicore-instance
              readOnly: true
      volumes:
        - name: aicore-binding
          secret:
            secretName: my-aicore-binding
```

---

## Environment Variable

Set `AICORE_SERVICE_KEY` with service credentials JSON.

### Get Credentials

1. Open SAP BTP Cockpit
2. Navigate to your subaccount > Service Instances
3. Click on your AI Core instance
4. Click "View Credentials" or create a service key
5. Copy the JSON credentials

### JavaScript - Using .env File

Create `.env` file:

```bash
AICORE_SERVICE_KEY='{"clientid":"sb-abc123","clientsecret":"secret123","url":"[https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com","serviceurls":{"AI_API_URL":"https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2"}}'](https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com","serviceurls":{"AI_API_URL":"https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2"}}')
```

Load in application:

```typescript
// Option 1: dotenv package
import 'dotenv/config';

// Option 2: Node.js built-in (v20.6+)
// Run with: node --env-file=.env app.js

// SDK will automatically use AICORE_SERVICE_KEY
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';
const client = new OrchestrationClient({ /* config */ });
```

### Java - Environment Variable

Set environment variable:

```bash
# macOS/Linux
export AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"..."}'

# Windows PowerShell
$env:AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"..."}'
```

Or in `.env` file with Spring Boot:

```properties
# .env
AICORE_SERVICE_KEY={"clientid":"...","clientsecret":"...","url":"..."}
```

Run application:

```bash
# Spring Boot
mvn spring-boot:run

# Or with explicit env file
java -jar app.jar --spring.config.import=optional:file:.env
```

### IntelliJ Run Configuration

1. Edit Run Configuration
2. Under "Environment variables", add:
   - Name: `AICORE_SERVICE_KEY`
   - Value: `{"clientid":"...","clientsecret":"...",...}`

---

## CAP Hybrid Mode

Use CAP CLI to bind credentials for local development.

### JavaScript (CAP Node.js)

```bash
# Bind to AI Core instance
cds bind -2 my-aicore-instance

# Run in hybrid mode
cds-tsx watch --profile hybrid

# Or with npm
npm run watch:hybrid
```

Add to `package.json`:

```json
{
  "scripts": {
    "watch:hybrid": "cds-tsx watch --profile hybrid"
  }
}
```

### Java (CAP Java)

```bash
# Bind and run Maven
cds bind --to aicore --exec mvn spring-boot:run
```

Add to `pom.xml`:

```xml
<profiles>
  <profile>
    <id>hybrid</id>
    <properties>
      <spring.profiles.active>hybrid</spring.profiles.active>
    </properties>
  </profile>
</profiles>
```

---

## BTP Destination Service

Create a destination in SAP BTP for centralized credential management.

### Create Destination

1. Open SAP BTP Cockpit
2. Navigate to Connectivity > Destinations
3. Create new destination:

| Property | Value |
|----------|-------|
| Name | `my-aicore-destination` |
| Type | HTTP |
| URL | `<url from service key>` |
| Proxy Type | Internet |
| Authentication | OAuth2ClientCredentials |
| Client ID | `<clientid from service key>` |
| Client Secret | `<clientsecret from service key>` |
| Token Service URL | `<url>/oauth/token` |

### Use in JavaScript

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient(
  { promptTemplating: { model: { name: 'gpt-4o' } } },
  { destinationName: 'my-aicore-destination' }
);
```

Disable caching if needed:

```typescript
const client = new OrchestrationClient(
  { /* config */ },
  {
    destinationName: 'my-aicore-destination',
    useCache: false // Refresh destination on each request
  }
);
```

### Use in Java

```java
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.ai.sdk.core.AiCoreService;

// Get destination
Destination destination = DestinationAccessor
    .getDestination("my-aicore-destination")
    .asHttp();

// Use with AI Core service
AiCoreService aiCoreService = new AiCoreService()
    .withBaseDestination(destination);

// Create client
var client = new OrchestrationClient(aiCoreService);
```

---

## Custom Destination

Build destinations programmatically for custom authentication flows.

### JavaScript

```typescript
import { registerDestination } from '@sap-cloud-sdk/connectivity';

// Register custom destination
registerDestination({
  name: 'custom-aicore',
  url: '[https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com',](https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com',)
  authentication: 'OAuth2ClientCredentials',
  clientId: 'my-client-id',
  clientSecret: 'my-client-secret',
  tokenServiceUrl: '[https://auth.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/oauth/token'](https://auth.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/oauth/token')
});

// Use registered destination
const client = new OrchestrationClient(
  { promptTemplating: { model: { name: 'gpt-4o' } } },
  { destinationName: 'custom-aicore' }
);
```

### Java - OAuth2DestinationBuilder

```java
import com.sap.cloud.sdk.cloudplatform.connectivity.OAuth2DestinationBuilder;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;

// Build custom destination
HttpDestination destination = OAuth2DestinationBuilder.forTargetUrl(
    "[https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com"](https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com")
)
    .withClient("my-client-id", "my-client-secret")
    .withTokenEndpoint("[https://auth.ai.../oauth/token](https://auth.ai.../oauth/token)")
    .build();

// Use with AI Core service
AiCoreService aiCoreService = new AiCoreService()
    .withBaseDestination(destination);
```

### Java - With Client Certificate

```java
HttpDestination destination = OAuth2DestinationBuilder.forTargetUrl(aiCoreUrl)
    .withCertificate(certificate, privateKey)
    .withTokenEndpoint(tokenUrl)
    .build();
```

---

## Resource Groups

### Default Resource Group

By default, the SDK uses the `default` resource group which has orchestration deployed.

### Custom Resource Group

```typescript
// JavaScript
const client = new OrchestrationClient(
  { promptTemplating: { model: { name: 'gpt-4o' } } },
  { resourceGroup: 'my-custom-group' }
);
```

```java
// Java
AiCoreService aiCoreService = new AiCoreService()
    .getInferenceDestination("my-custom-group");

var client = new OrchestrationClient(aiCoreService);
```

### Verify Deployment Exists

Ensure orchestration is deployed in your resource group:

```typescript
import { DeploymentApi } from '@sap-ai-sdk/ai-api';

const deployments = await DeploymentApi.deploymentQuery(
  { scenarioId: 'orchestration' },
  { 'AI-Resource-Group': 'my-custom-group' }
).execute();

if (deployments.count === 0) {
  console.error('No orchestration deployment found in resource group');
}
```

---

## Troubleshooting

### Error: "Could not find any matching service bindings for service identifier 'aicore'"

**Cause**: No AI Core service binding detected.

**Solutions**:
1. Bind AI Core service to your application (Cloud Foundry/Kyma)
2. Set `AICORE_SERVICE_KEY` environment variable
3. Configure BTP destination

### Error: "Orchestration deployment not found"

**Cause**: No orchestration deployment in the resource group.

**Solutions**:
1. Use `default` resource group (has orchestration by default)
2. Deploy orchestration in your custom resource group
3. Check deployment status in AI Launchpad

### Error: "401 Unauthorized"

**Cause**: Invalid or expired credentials.

**Solutions**:
1. Verify service key credentials are correct
2. Check token service URL includes `/oauth/token`
3. Ensure client ID and secret are from correct service instance
4. Regenerate service key if expired

### Error: "403 Forbidden"

**Cause**: Insufficient permissions or wrong service plan.

**Solutions**:
1. Verify service plan is `extended` or `sap-internal`
2. Check user has required roles
3. Ensure resource group access is granted

### Destination Caching

Destinations are cached by default. To refresh:

```typescript
// JavaScript
{ useCache: false }
```

```java
// Java - destinations refresh tokens automatically
// but you can force new destination lookup
DestinationAccessor.setLoader(new DefaultDestinationLoader());
```

### Debug Logging

Enable debug logging to troubleshoot connectivity:

```typescript
// JavaScript - set DEBUG environment variable
// DEBUG=sap-cloud-sdk:* node app.js
```

```java
// Java - application.properties
logging.level.com.sap.cloud.sdk=DEBUG
logging.level.com.sap.ai.sdk=DEBUG
```

---

## Documentation Links

- JS Connectivity: [https://github.com/SAP/ai-sdk/blob/main/docs-js/connecting-to-ai-core.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/connecting-to-ai-core.mdx)
- Java Connectivity: [https://github.com/SAP/ai-sdk/blob/main/docs-java/connecting-to-ai-core.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/connecting-to-ai-core.mdx)
- SAP Cloud SDK Destinations: [https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations](https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations)

```

### references/orchestration-guide.md

```markdown
# Orchestration Service Guide

Complete guide for SAP AI Core Orchestration Service using SAP Cloud SDK for AI.

## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [Chat Completion](#chat-completion)
4. [Streaming](#streaming)
5. [Prompt Templating](#prompt-templating)
6. [Message History](#message-history)
7. [Content Filtering](#content-filtering)
8. [Data Masking](#data-masking)
9. [Document Grounding](#document-grounding)
10. [Translation](#translation)
11. [Function Calling](#function-calling)
12. [Response Formatting](#response-formatting)
13. [Image Recognition](#image-recognition)
14. [Embedding](#embedding)
15. [Custom Configuration](#custom-configuration)

---

## Overview

The Orchestration Service provides a harmonized API for multiple LLM providers through SAP AI Core. Switch between models without code changes.

### Supported Models

| Provider | Models |
|----------|--------|
| OpenAI | gpt-4o, gpt-4o-mini, o1, o3-mini |
| Anthropic (AWS) | claude-3-5-sonnet, claude-4 |
| Amazon | amazon-nova-pro, amazon-nova-lite, amazon-nova-micro |
| Google | gemini-2.5-flash, gemini-2.0-flash |
| Mistral | mistral-medium-instruct, mistral-large |
| Cohere | command-a-reasoning |
| Perplexity | sonar variants |

---

## Installation

### JavaScript/TypeScript

```bash
npm install @sap-ai-sdk/orchestration
```

### Java (Maven)

```xml
<dependency>
  <groupId>com.sap.ai.sdk</groupId>
  <artifactId>orchestration</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
```

---

## Chat Completion

### JavaScript

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [{ role: 'user', content: '{{?question}}' }]
  }
});

const response = await client.chatCompletion({
  placeholderValues: { question: 'What is SAP CAP?' }
});

// Response helpers
console.log(response.getContent());           // Model output
console.log(response.getTokenUsage());        // Token metrics
console.log(response.getFinishReason());      // Completion reason
console.log(response.getAllMessages());       // Message history
console.log(response.getAssistantMessage()); // Assistant response
```

### Java

```java
var client = new OrchestrationClient();
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O);

var prompt = new OrchestrationPrompt("What is SAP CAP?");
var result = client.chatCompletion(prompt, config);

String content = result.getContent();
```

### Model Parameters

```typescript
// JavaScript
const client = new OrchestrationClient({
  promptTemplating: {
    model: {
      name: 'gpt-4o',
      version: 'latest',
      parameters: {
        max_tokens: 1000,
        temperature: 0.7,
        top_p: 0.9,
        frequency_penalty: 0.5,
        presence_penalty: 0.5
      }
    }
  }
});
```

```java
// Java - using parameter constants
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.*;

var customGPT4O = OrchestrationAiModel.GPT_4O
    .withParam(MAX_TOKENS, 50)
    .withParam(TEMPERATURE, 0.1)
    .withParam(FREQUENCY_PENALTY, 0)
    .withParam(PRESENCE_PENALTY, 0)
    .withVersion("2024-05-13");

var config = new OrchestrationModuleConfig()
    .withLlmConfig(customGPT4O);
```

### Custom Headers (Java)

```java
// Add headers to individual requests
var result = client.withHeader("foo", "bar").chatCompletion(prompt, config);
```

---

## Streaming

### JavaScript

```typescript
// Basic streaming
const stream = client.stream({
  placeholderValues: { question: 'Explain CAP in detail' }
});

for await (const chunk of stream.toContentStream()) {
  process.stdout.write(chunk);
}

// After streaming completes
console.log(stream.getFinishReason());
console.log(stream.getTokenUsage());

// Abort streaming
const controller = new AbortController();
const stream = client.stream(
  { placeholderValues: { question: 'Long response...' } },
  { signal: controller.signal }
);

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
```

### JavaScript Stream Options

```typescript
// Advanced streaming with options
const stream = client.stream(
  { placeholderValues: { question: 'Explain SAP CAP' } },
  {
    signal: controller.signal,
    streamOptions: {
      llm: {
        include_usage: true  // Include token usage in stream (default: true)
      },
      global: {
        chunk_size: 100  // Maximum characters per chunk
      },
      outputFiltering: {
        overlap: 50  // Overlap for content filtering
      }
    }
  }
);
```

### Java

```java
// Blocking stream with try-with-resources
var msg = "Can you give me the first 100 numbers of the Fibonacci sequence?";
var prompt = new OrchestrationPrompt(msg);
try (Stream<String> stream = client.streamChatCompletion(prompt, config)) {
    stream.forEach(deltaString -> {
        System.out.print(deltaString);
        System.out.flush();
    });
}

// Non-blocking with callback
AtomicReference<String> result = new AtomicReference<>("");
client.streamChatCompletion(prompt, config)
    .forEach(chunk -> result.updateAndGet(s -> s + chunk.getDeltaContent()));
```

### Stream Configuration (Java)

```java
// Configure stream behavior
var streamConfig = new OrchestrationStreamConfig()
    .withFilterOverlap(100)        // preceding characters for content filtering
    .withChunkSize(500)            // tokens per chunk
    .withDelimiters(List.of("\n")); // custom chunk delimiters

var configWithStream = config.withStreamConfig(streamConfig);
```

---

## Prompt Templating

### Inline Templates (JavaScript)

```typescript
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [
      { role: 'system', content: 'You are a helpful assistant for {{?domain}}.' },
      { role: 'user', content: '{{?question}}' }
    ]
  }
});

const response = await client.chatCompletion({
  placeholderValues: {
    domain: 'SAP development',
    question: 'What is CDS?'
  }
});
```

### Prompt Registry Reference

```typescript
// JavaScript - by ID
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: { ref: { id: 'template-uuid-here' } }
  }
});

// JavaScript - by name/scenario/version
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: {
      ref: {
        name: 'my-template',
        scenario: 'customer-support',
        version: '1.0.0'
      }
    }
  }
});
```

```java
// Java - by ID using TemplateConfig
var template = TemplateConfig.reference()
    .byId("21cb1358-0bf1-4f43-870b-00f14d0f9f16");
var configWithTemplate = config.withTemplateConfig(template);

// Java - by name/scenario/version
var template = TemplateConfig.reference()
    .byName("my-template")
    .scenario("customer-support")
    .version("1.0.0");
```

### Java Template Configuration

```java
// Inline template with placeholders
var message = Message.user("Reply with 'Orchestration Service is working!' in {{?language}}");
var templatingConfig = TemplateConfig.create().withMessages(message);
var configWithTemplate = config.withTemplateConfig(templatingConfig);

// Execute with placeholder values
var inputParams = Map.of("language", "German");
var prompt = new OrchestrationPrompt(inputParams);
var result = client.chatCompletion(prompt, configWithTemplate);
```

### Local YAML Templates (Java)

```java
// Load template from YAML file (useful for testing before registry upload)
String promptTemplate = Files.readString(Path.of("path/to/prompt-template.yaml"));
var template = TemplateConfig.create().fromYaml(promptTemplate);
var configWithTemplate = config.withTemplateConfig(template);
```

### Multiple Text Inputs (Java)

```java
// Add additional context to messages
var message = Message.user("What is chess about?");
var newMessage = message.withText("Answer in two sentences.");

// Note: User and system messages support multiple text inputs
```

---

## Message History

### JavaScript

```typescript
const response = await client.chatCompletion({
  placeholderValues: { question: 'What is CAP?' },
  messagesHistory: [
    { role: 'user', content: 'I am learning SAP development' },
    { role: 'assistant', content: 'Great! SAP offers many frameworks.' }
  ]
});
```

### Java

```java
var history = List.of(
    OrchestrationMessage.user("I am learning SAP development"),
    OrchestrationMessage.assistant("Great! SAP offers many frameworks.")
);

var prompt = new OrchestrationPrompt("What is CAP?")
    .messageHistory(history);
```

---

## Content Filtering

### Azure Content Safety Filter (JavaScript)

```typescript
import { buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildAzureContentSafetyFilter({
      Hate: 'ALLOW_SAFE',
      SelfHarm: 'ALLOW_SAFE',
      Sexual: 'ALLOW_SAFE',
      Violence: 'ALLOW_SAFE'
    }),
    output: buildAzureContentSafetyFilter({
      Hate: 'ALLOW_SAFE_LOW_MEDIUM',
      Violence: 'ALLOW_SAFE'
    })
  }
});

// Filter severity levels:
// ALLOW_SAFE - Only safe content
// ALLOW_SAFE_LOW - Safe + low severity
// ALLOW_SAFE_LOW_MEDIUM - Safe + low + medium
// ALLOW_ALL - No filtering
```

### Prompt Shield (Jailbreak Detection)

```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildAzureContentSafetyFilter({
      promptShield: { detectJailbreakAttempts: true }
    })
  }
});
```

### Llama Guard Filter (JavaScript)

```typescript
import { buildLlamaGuard38BFilter } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildLlamaGuard38BFilter({
      type: 'input',
      enabledCategories: ['violent_crimes', 'hate', 'sexual_content']
    }),
    output: buildLlamaGuard38BFilter({
      type: 'output',
      enabledCategories: ['violent_crimes', 'hate']
    })
  }
});
```

### Java Content Filtering

```java
// Azure Content Filter with Prompt Shield
var filterStrict = new AzureContentFilter()
    .hate(AzureThreshold.ALLOW_SAFE)
    .selfHarm(AzureThreshold.ALLOW_SAFE)
    .sexual(AzureThreshold.ALLOW_SAFE)
    .violence(AzureThreshold.ALLOW_SAFE)
    .promptShield(true);  // Enable jailbreak detection

// Llama Guard Filter
var llamaGuardFilter = new LlamaGuardFilter()
    .config(LlamaGuard38b.create().selfHarm(true));

// Combined filtering
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withInputFiltering(filterStrict)
    .withOutputFiltering(filterStrict, llamaGuardFilter);
```

### Filter Exception Handling (Java)

```java
try {
    var result = client.chatCompletion(prompt, configWithFilter);

    // Output filter violations throw on getContent()
    try {
        String content = result.getContent();
    } catch (OrchestrationFilterException.Output e) {
        System.err.println("Output blocked by filter");
        // Access filter details
        var details = result.getFilterDetails();
        var azureInput = result.getAzureContentSafetyInput();
        var azureOutput = result.getAzureContentSafetyOutput();
    }
} catch (OrchestrationFilterException.Input e) {
    // Input filter violations throw immediately
    System.err.println("Input blocked by filter: " + e.getMessage());
}
```

---

## Data Masking

### JavaScript (SAP DPI)

```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  masking: {
    masking_providers: [{
      type: 'sap_data_privacy_integration',
      method: 'anonymization', // or 'pseudonymization'
      entities: [
        { type: 'profile-email' },
        { type: 'profile-person' },
        { type: 'profile-phone' },
        { type: 'profile-address' },
        { type: 'profile-org' },
        { type: 'profile-location' }
      ]
    }]
  }
});
```

### Using buildDpiMaskingProvider (Recommended)

```typescript
import { buildDpiMaskingProvider } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  masking: {
    masking_providers: [
      buildDpiMaskingProvider({
        method: 'anonymization', // or 'pseudonymization'
        entities: ['profile-email', 'profile-person', 'profile-phone'],
        // Optional: replacement strategy
        replacement_strategy: {
          method: 'constant', // or 'fabricated_data'
          value: '[REDACTED]'
        },
        // Optional: allow specific terms to remain unmasked
        allowlist: ['SAP', 'Joule'],
        // Optional: mask grounding input as well
        mask_grounding_input: true
      })
    ]
  }
});
```

### Custom Regex Masking

```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  masking: {
    masking_providers: [{
      type: 'sap_data_privacy_integration',
      method: 'anonymization',
      entities: [
        {
          type: 'custom',
          regex: '\\b[A-Z]{2}\\d{6}\\b', // Custom ID pattern
          replacement: 'REDACTED_ID'
        }
      ]
    }]
  }
});
```

### Java Data Masking

```java
// Standard entity masking
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withMasking(DpiMasking.anonymization()
        .withEntities(
            DPIEntities.EMAIL,
            DPIEntities.PERSON,
            DPIEntities.PHONE,
            DPIEntities.ADDRESS,
            DPIEntities.LOCATION,
            DPIEntities.SENSITIVE_DATA
        ));

// Custom regex masking
var maskingConfig = DpiMasking.anonymization()
    .withRegex("patient_id_[0-9]+", "REDACTED_PATIENT_ID");

// Pseudonymization (reversible)
var pseudoMasking = DpiMasking.pseudonymization()
    .withEntities(DPIEntities.EMAIL, DPIEntities.PERSON);
```

---

## Document Grounding

### Vector Repository (JavaScript)

```typescript
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [
      { role: 'system', content: 'Answer based on context: {{?context}}' },
      { role: 'user', content: '{{?question}}' }
    ]
  },
  grounding: {
    grounding_input: ['{{?question}}'],
    grounding_output: ['{{?context}}'],
    data_repositories: [{
      type: 'vector',
      id: 'my-vector-repo-id',
      metadata: [{ key: 'category', value: 'technical' }]
    }],
    max_chunks: 5
  }
});
```

### SAP Help Portal Grounding

```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  grounding: {
    grounding_input: ['{{?question}}'],
    data_repositories: [{
      type: 'help.sap.com',
      configurations: [{ product_url: 'cloud-alm' }]
    }]
  }
});
```

### Java Grounding

```java
// Vector repository with metadata
var documentMetadata = SearchDocumentKeyValueListPair.create()
    .key("my-collection")
    .value("value");

var databaseFilter = DocumentGroundingFilter.create()
    .id("")
    .dataRepositoryType(DataRepositoryType.VECTOR)
    .addDocumentMetadataItem(documentMetadata);

var groundingConfig = Grounding.create().filter(databaseFilter);
var prompt = groundingConfig.createGroundingPrompt("What does Joule do?");
```

### SharePoint Integration (Java)

```java
var dataRepositoryId = "SharePoint ID here";
var filter = DocumentGroundingFilter.create()
    .dataRepositoryType(DataRepositoryType.VECTOR)
    .dataRepositories(List.of(dataRepositoryId));

var groundingConfig = Grounding.create().filters(filter);
```

### SAP Help Portal Grounding (Java)

```java
var groundingHelpSapCom = DocumentGroundingFilter.create()
    .dataRepositoryType(DataRepositoryType.HELP_SAP_COM);

var groundingConfig = Grounding.create().filters(groundingHelpSapCom);
```

### Masked Grounding with Allowlist (Java)

```java
// Mask sensitive data in grounding but allow specific terms
var maskingConfig = DpiMasking.anonymization()
    .withEntities(DPIEntities.SENSITIVE_DATA)
    .withMaskGroundingEnabled()
    .withAllowList(List.of("SAP", "Joule"));  // Terms to NOT mask

var maskedGroundingConfig = groundingConfig.withMaskingConfig(maskingConfig);
```

---

## Translation

### JavaScript

```typescript
import { buildTranslationConfig } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  translation: buildTranslationConfig({
    inputLanguage: 'auto', // auto-detect
    outputLanguage: 'en'   // translate output to English
  })
});
```

### Java

```java
// Simple translation
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withTranslation("auto", "en"); // input auto-detect, output English
```

### Advanced Translation Configuration (Java)

```java
// Full translation configuration
var prompt = new OrchestrationPrompt("Quelle est la couleur de la tour Eiffel?");

// Input translation config
var inputConfig = SAPDocumentTranslationInputConfig.create()
    .targetLanguage("en-US")
    .applyTo(null);  // Apply to all

var inputTranslation = SAPDocumentTranslationInput.create()
    .type(SAPDocumentTranslationInput.TypeEnum.SAP_DOCUMENT_TRANSLATION)
    .config(inputConfig);

// Output translation config
var outputConfig = SAPDocumentTranslationOutputConfig.create()
    .targetLanguage(SAPDocumentTranslationOutputTargetLanguage.create("de-DE"));

var outputTranslation = SAPDocumentTranslationOutput.create()
    .type(SAPDocumentTranslationOutput.TypeEnum.SAP_DOCUMENT_TRANSLATION)
    .config(outputConfig);

// Apply to config
var configWithTranslation = config
    .withInputTranslationConfig(inputTranslation)
    .withOutputTranslationConfig(outputTranslation);
```

---

## Function Calling

### JavaScript

```typescript
const tools = [{
  type: 'function',
  function: {
    name: 'get_weather',
    description: 'Get current weather for a city',
    parameters: {
      type: 'object',
      properties: {
        city: { type: 'string', description: 'City name' },
        unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }
      },
      required: ['city']
    }
  }
}];

const response = await client.chatCompletion(
  { placeholderValues: { question: 'Weather in Berlin?' } },
  { tools }
);

const toolCalls = response.getToolCalls();
if (toolCalls?.length) {
  for (const call of toolCalls) {
    console.log('Function:', call.function.name);
    console.log('Arguments:', JSON.parse(call.function.arguments));

    // Execute function and continue conversation
    const result = await executeFunction(call);
    // Send result back to model...
  }
}
```

### Java

```java
var tools = List.of(
    OpenAiTool.builder()
        .function(OpenAiFunction.builder()
            .name("get_weather")
            .description("Get current weather for a city")
            .parameters(Map.of(
                "type", "object",
                "properties", Map.of(
                    "city", Map.of("type", "string"),
                    "unit", Map.of("type", "string", "enum", List.of("celsius", "fahrenheit"))
                ),
                "required", List.of("city")
            ))
            .build())
        .build()
);

var result = client.chatCompletion(prompt, config.withTools(tools));
var toolCalls = result.getToolCalls();
```

---

## Response Formatting

### JSON Schema (JavaScript)

```typescript
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    response_format: {
      type: 'json_schema',
      json_schema: {
        name: 'weather_response',
        strict: true,
        schema: {
          type: 'object',
          properties: {
            city: { type: 'string' },
            temperature: { type: 'number' },
            conditions: { type: 'string' }
          },
          required: ['city', 'temperature', 'conditions']
        }
      }
    }
  }
});
```

### Zod Schema (JavaScript)

```typescript
import { z } from 'zod';
import { toJsonSchema } from '@sap-ai-sdk/orchestration';

const WeatherSchema = z.object({
  city: z.string(),
  temperature: z.number(),
  conditions: z.string()
});

const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    response_format: {
      type: 'json_schema',
      json_schema: {
        name: 'weather_response',
        strict: true,
        schema: toJsonSchema(WeatherSchema)
      }
    }
  }
});
```

### Java Response Format

```java
// Using Java class with annotations
@JsonProperty
public record WeatherResponse(
    @JsonProperty(required = true) String city,
    @JsonProperty(required = true) double temperature,
    @JsonProperty(required = true) String conditions
) {}

var schema = ResponseJsonSchema.fromType(WeatherResponse.class)
    .withDescription("Output schema for weather data.")
    .withStrict(true);

var configWithSchema = config.withTemplateConfig(
    TemplateConfig.create().withJsonSchemaResponse(schema)
);

// Parse response to object
WeatherResponse response = client.chatCompletion(prompt, configWithSchema)
    .asEntity(WeatherResponse.class);
```

### JSON Schema from Map (Java)

```java
// Define schema using Map (without Java class)
var schemaMap = Map.ofEntries(
    entry("type", "object"),
    entry("properties", Map.ofEntries(
        entry("language", Map.of("type", "string")),
        entry("translation", Map.of("type", "string"))
    )),
    entry("required", List.of("language", "translation")),
    entry("additionalProperties", false)
);

var schemaFromMap = ResponseJsonSchema.fromMap(schemaMap, "Translator-Schema");
var configWithMapSchema = config.withTemplateConfig(
    TemplateConfig.create().withJsonSchemaResponse(schemaFromMap)
);
```

### JSON Object Response (Unstructured)

```java
// Return valid JSON without strict schema
var configWithJsonResponse = config.withTemplateConfig(
    TemplateConfig.create().withJsonResponse()
);
```

---

## Image Recognition

### JavaScript

```typescript
// URL-based image
const response = await client.chatCompletion({
  placeholderValues: { question: 'Describe this image' },
  messagesHistory: [{
    role: 'user',
    content: [
      { type: 'text', text: 'What is in this image?' },
      {
        type: 'image_url',
        image_url: {
          url: '[https://example.com/image.jpg',](https://example.com/image.jpg',)
          detail: 'auto' // 'auto', 'low', or 'high'
        }
      }
    ]
  }]
});

// Base64 encoded image
const imageData = fs.readFileSync('image.jpg').toString('base64');
const response = await client.chatCompletion({
  messagesHistory: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Describe this image' },
      {
        type: 'image_url',
        image_url: { url: `data:image/jpeg;base64,${imageData}` }
      }
    ]
  }]
});
```

### Java

```java
// Basic image message
var message = Message.user("Describe the following image");
var newMessage = message.withImage("[https://url.to/image.jpg](https://url.to/image.jpg)");

// With detail level (LOW, HIGH, AUTO)
var messageWithDetail = message.withImage(
    "[https://url.to/image.jpg",](https://url.to/image.jpg",)
    ImageItem.DetailLevel.LOW
);

// Using ImageItem directly
var imageMessage = Message.user(new ImageItem("[https://url.to/image.jpg](https://url.to/image.jpg)"));

// Note: Only user messages support image attachments currently
```

---

## Embedding

### JavaScript (v2.2.0+)

```typescript
import { OrchestrationEmbeddingClient } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationEmbeddingClient({
  embeddings: {
    model: {
      name: 'text-embedding-3-large',
      version: 'latest',           // optional
      params: { dimensions: 512 }  // optional, model-specific
    }
  }
});

// Single text with type parameter
const response = await client.embed({
  input: 'SAP is an enterprise software company',
  type: 'document'  // 'text' | 'document' | 'query'
});
const embedding = response.getEmbeddings()[0];

// Batch embedding
const batchResponse = await client.embed({
  input: ['First text', 'Second text', 'Third text']
});
const embeddings = batchResponse.getEmbeddings();

// Response methods
response.getTokenUsage();           // Token consumption
response.getIntermediateResults();  // Module results (masking diagnostics, etc.)
```

### Supported Embedding Models
- `text-embedding-3-small`, `text-embedding-3-large` (OpenAI)
- `amazon--titan-embed-text` (Amazon)
- `nvidia--llama-3.2-nv-embedqa-1b` (NVIDIA)
- See SAP Note 3437766 for complete list

### Java (v1.11.0+)

```java
var request = OrchestrationEmbeddingRequest
    .forModel(OrchestrationAiModel.TEXT_EMBEDDING_3_SMALL)
    .forInputs("SAP is an enterprise software company")
    .asDocument();

var response = client.embed(request);
float[] embedding = response.getEmbeddingVectors().get(0);
```

---

## Custom Configuration

### Resource Group

```typescript
// JavaScript
const client = new OrchestrationClient(config, {
  resourceGroup: 'my-custom-group'
});
```

```java
// Java
var service = new AiCoreService()
    .getInferenceDestination("my-custom-group");
var client = new OrchestrationClient(service);
```

### Deployment ID

```typescript
// JavaScript
const client = new OrchestrationClient(config, {
  deploymentId: 'd1234567'
});
```

### Custom Destination

```typescript
// JavaScript
const client = new OrchestrationClient(config, {
  destinationName: 'my-aicore-destination',
  useCache: false // disable destination caching
});
```

### Load from AI Launchpad JSON

```typescript
// JavaScript - from file
const client = await OrchestrationClient.fromJsonConfig('config.json');

// JavaScript - from string
const jsonString = fs.readFileSync('config.json', 'utf-8');
const client = OrchestrationClient.fromJsonConfigString(jsonString);
```

```java
// Java
var client = OrchestrationClient.fromJsonConfig(
    new File("config.json")
);
```

---

## Documentation Links

- JS Orchestration: [https://github.com/SAP/ai-sdk/tree/main/docs-js/orchestration](https://github.com/SAP/ai-sdk/tree/main/docs-js/orchestration)
- Java Orchestration: [https://github.com/SAP/ai-sdk/tree/main/docs-java/orchestration](https://github.com/SAP/ai-sdk/tree/main/docs-java/orchestration)

```

### references/foundation-models-guide.md

```markdown
# Foundation Models Guide

Direct access to OpenAI models via SAP AI Core without orchestration layer.

## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [JavaScript Client](#javascript-client)
4. [Java Client](#java-client)
5. [Chat Completion](#chat-completion)
6. [Streaming](#streaming)
7. [Function Calling](#function-calling)
8. [Embedding](#embedding)
9. [Model Versioning](#model-versioning)
10. [Custom Configuration](#custom-configuration)

---

## Overview

The Foundation Models package provides direct access to Azure OpenAI models deployed on SAP AI Core. Use this when you need:
- Direct model access without orchestration features
- Specific OpenAI API compatibility
- Lower latency (no orchestration overhead)

**When to use Orchestration instead:**
- Need content filtering, data masking, or grounding
- Want harmonized API across multiple model providers
- Need prompt templating or translation

---

## Installation

### JavaScript/TypeScript

```bash
npm install @sap-ai-sdk/foundation-models
```

### Java (Maven)

```xml
<dependency>
  <groupId>com.sap.ai.sdk.foundationmodels</groupId>
  <artifactId>openai</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
```

---

## JavaScript Client

### Client Initialization

```typescript
import { AzureOpenAiChatClient } from '@sap-ai-sdk/foundation-models';

// Basic initialization
const client = new AzureOpenAiChatClient('gpt-4o');

// With model version
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  modelVersion: '2024-05-13'
});

// With resource group
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  resourceGroup: 'my-resource-group'
});

// With deployment ID (bypasses model lookup)
const client = new AzureOpenAiChatClient({
  deploymentId: 'd1234567'
});
```

### API Version

The client uses Azure OpenAI API version `2024-10-21` by default. Override via `CustomRequestConfig`:

```typescript
const response = await client.run(
  { messages: [...] },
  {
    params: { 'api-version': '2024-08-01-preview' }
  }
);
```

### Available Clients

| Client | Purpose |
|--------|---------|
| `AzureOpenAiChatClient` | Chat completions |
| `AzureOpenAiEmbeddingClient` | Text embeddings |

---

## Java Client

### Client Initialization

```java
import com.sap.ai.sdk.foundationmodels.openai.*;

// Basic initialization
var client = OpenAiClient.forModel(OpenAiModel.GPT_4O);

// With system prompt
var client = OpenAiClient.forModel(OpenAiModel.GPT_4O)
    .withSystemPrompt("You are a helpful assistant");

// With custom resource group
var destination = new AiCoreService()
    .getInferenceDestination("my-resource-group");
var client = OpenAiClient.forModel(OpenAiModel.GPT_4O)
    .withDestination(destination);
```

### Version History

| Version | Features |
|---------|----------|
| v1.8.0+ | Tool calling with `OpenAiTool` |
| v1.4.0+ | New interface with `OpenAiChatCompletionRequest`, `OpenAiMessage` |
| v1.0.0 | Deprecated interface (still available) |

### v1.4.0+ Interface (Recommended)

```java
// Using new interface
var request = OpenAiChatCompletionRequest.create()
    .addMessage(OpenAiMessage.system("You are helpful"))
    .addMessage(OpenAiMessage.user("What is SAP?"));

var response = client.chatCompletion(request);
String content = response.getContent();
```

### v1.0.0 Interface (Deprecated)

```java
// Legacy interface - avoid for new code
var params = new OpenAiChatCompletionParameters()
    .addMessages(new OpenAiChatSystemMessage().setContent("System prompt"))
    .addMessages(new OpenAiChatUserMessage().setContent("User message"));

var response = client.chatCompletion(params);
```

---

## Chat Completion

### JavaScript

```typescript
// Basic request
const response = await client.run({
  messages: [
    { role: 'user', content: 'What is SAP CAP?' }
  ]
});

console.log(response.getContent());

// With parameters
const response = await client.run({
  messages: [
    { role: 'system', content: 'You are a helpful assistant' },
    { role: 'user', content: 'Explain briefly' }
  ],
  max_tokens: 500,
  temperature: 0.7
});

// Token usage
const usage = response.getTokenUsage();
console.log('Prompt tokens:', usage.prompt_tokens);
console.log('Completion tokens:', usage.completion_tokens);
console.log('Total tokens:', usage.total_tokens);
```

### Java

```java
// Simple request
var response = client.chatCompletion("What is SAP CAP?");
System.out.println(response.getContent());

// With message history
var request = OpenAiChatCompletionRequest.create()
    .addMessage(OpenAiMessage.system("You are a SAP expert"))
    .addMessage(OpenAiMessage.user("What is CAP?"))
    .addMessage(OpenAiMessage.assistant("CAP is..."))
    .addMessage(OpenAiMessage.user("Tell me more"));

var response = client.chatCompletion(request);
```

---

## Streaming

### JavaScript

```typescript
// Basic streaming
const stream = client.stream({
  messages: [{ role: 'user', content: 'Explain SAP in detail' }]
});

for await (const chunk of stream.toContentStream()) {
  process.stdout.write(chunk);
}

// Get metadata after streaming
console.log('Finish reason:', stream.getFinishReason());
console.log('Token usage:', stream.getTokenUsage());

// With abort controller
const controller = new AbortController();
const stream = client.stream(config, controller.signal);

setTimeout(() => controller.abort(), 5000);

try {
  for await (const chunk of stream.toContentStream()) {
    process.stdout.write(chunk);
  }
} catch (e) {
  if (e.name === 'AbortError') {
    console.log('Stream cancelled');
  }
}
```

### Java

```java
// Blocking stream
client.streamChatCompletion("Explain SAP")
    .forEach(chunk -> System.out.print(chunk.getDeltaContent()));

// Non-blocking with deltas (v1.4.0+)
client.streamChatCompletionDeltas(request)
    .forEach(delta -> {
        if (delta.getDeltaContent() != null) {
            System.out.print(delta.getDeltaContent());
        }
    });

// With AtomicReference for result collection
AtomicReference<String> result = new AtomicReference<>("");
client.streamChatCompletion("Query")
    .forEach(chunk -> result.updateAndGet(s -> s + chunk.getDeltaContent()));

System.out.println("Full response: " + result.get());
```

---

## Function Calling

### JavaScript

```typescript
const tools = [{
  type: 'function',
  function: {
    name: 'get_weather',
    description: 'Get weather for a location',
    parameters: {
      type: 'object',
      properties: {
        location: { type: 'string' },
        unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }
      },
      required: ['location']
    },
    strict: true  // Enforce schema compliance
  }
}];

const response = await client.run({
  messages: [{ role: 'user', content: 'Weather in Berlin?' }],
  tools
});

const toolCalls = response.getToolCalls();
if (toolCalls?.length) {
  for (const call of toolCalls) {
    const { name, arguments: args } = call.function;
    const parsedArgs = JSON.parse(args);

    // Execute function
    const result = await executeWeatherLookup(parsedArgs.location);

    // Continue conversation with result
    const followUp = await client.run({
      messages: [
        { role: 'user', content: 'Weather in Berlin?' },
        response.getAssistantMessage(),
        { role: 'tool', tool_call_id: call.id, content: result }
      ],
      tools
    });
  }
}
```

### Java (v1.8.0+)

```java
// Define tool
var weatherTool = OpenAiTool.builder()
    .function(OpenAiFunction.builder()
        .name("get_weather")
        .description("Get weather for a location")
        .parameters(Map.of(
            "type", "object",
            "properties", Map.of(
                "location", Map.of("type", "string"),
                "unit", Map.of("type", "string", "enum", List.of("celsius", "fahrenheit"))
            ),
            "required", List.of("location")
        ))
        .build())
    .build();

// Request with tools
var request = OpenAiChatCompletionRequest.create()
    .addMessage(OpenAiMessage.user("Weather in Berlin?"))
    .withTools(List.of(weatherTool));

var response = client.chatCompletion(request);
var toolCalls = response.getToolCalls();

// Process tool calls
if (toolCalls != null && !toolCalls.isEmpty()) {
    for (var call : toolCalls) {
        String functionName = call.getFunction().getName();
        String arguments = call.getFunction().getArguments();
        // Execute and continue...
    }
}
```

---

## Embedding

### JavaScript

```typescript
import { AzureOpenAiEmbeddingClient } from '@sap-ai-sdk/foundation-models';

const client = new AzureOpenAiEmbeddingClient('text-embedding-3-small');

// Single embedding
const response = await client.run({
  input: 'SAP is an enterprise software company'
});
const embedding = response.getEmbedding();
console.log('Dimensions:', embedding.length);

// Batch embeddings
const response = await client.run({
  input: ['First text', 'Second text', 'Third text']
});
const embeddings = response.getEmbeddings();
```

### Java

```java
// v1.4.0+ interface (recommended)
var client = OpenAiClient.forModel(OpenAiModel.TEXT_EMBEDDING_3_SMALL);

var request = new OpenAiEmbeddingRequest(List.of("SAP is an enterprise software company"));
var response = client.embedding(request);
float[] embedding = response.getEmbeddingVectors().get(0);
```

```java
// v1.0.0 interface (legacy, deprecated)
var request = new OpenAiEmbeddingParameters().setInput("Hello World");
var response = OpenAiClient.forModel(OpenAiModel.TEXT_EMBEDDING_3_SMALL)
    .embedding(request);
float[] embedding = response.getData().get(0).getEmbedding();
```

---

## Model Versioning

### JavaScript

```typescript
// Specify version at initialization
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  modelVersion: '2024-05-13'
});
```

### Java

```java
// Using withVersion()
var model = OpenAiModel.GPT_4O.withVersion("2024-05-13");
var client = OpenAiClient.forModel(model);

// Custom model
var customModel = new OpenAiModel("gpt-4o-custom", "2024-05-13");
var client = OpenAiClient.forModel(customModel);
```

### Available Models

| Model Constant | Description |
|----------------|-------------|
| `GPT_4O` | GPT-4o latest |
| `GPT_4O_MINI` | GPT-4o mini |
| `TEXT_EMBEDDING_3_SMALL` | Small embedding model |
| `TEXT_EMBEDDING_3_LARGE` | Large embedding model |

---

## Custom Configuration

### Custom Headers

```java
// Java
var response = client.withHeader("X-Custom-Header", "value")
    .chatCompletion("Query");
```

### Custom Destination

```typescript
// JavaScript
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  destinationName: 'my-aicore-destination',
  useCache: false  // Disable destination caching
});
```

```java
// Java
var destination = DestinationAccessor.getDestination("my-aicore-destination").asHttp();
var client = OpenAiClient.forModel(OpenAiModel.GPT_4O)
    .withDestination(destination);
```

### Response Format (JSON Schema)

```typescript
// JavaScript
const response = await client.run({
  messages: [...],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'response_schema',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          answer: { type: 'string' },
          confidence: { type: 'number' }
        },
        required: ['answer', 'confidence']
      }
    }
  }
});
```

---

## Important Notes

1. **Deployment Cache**: Deployment information (ID, model name, version) is cached for 5 minutes by default
2. **API Version**: Default is `2024-10-21`, can be overridden
3. **Generated Classes**: Model classes in `...model` packages may change in minor releases
4. **No Filtering**: Foundation models don't include content filtering - use Orchestration if needed

---

## Documentation Links

- JS Foundation Models: [https://github.com/SAP/ai-sdk/tree/main/docs-js/foundation-models](https://github.com/SAP/ai-sdk/tree/main/docs-js/foundation-models)
- Java Foundation Models: [https://github.com/SAP/ai-sdk/tree/main/docs-java/foundation-models](https://github.com/SAP/ai-sdk/tree/main/docs-java/foundation-models)

```

### references/langchain-guide.md

```markdown
# LangChain Integration Guide

Guide for using SAP Cloud SDK for AI with LangChain framework.

## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [Available Clients](#available-clients)
4. [OrchestrationClient](#orchestrationclient)
5. [AzureOpenAiChatClient](#azureopenaichatclient)
6. [AzureOpenAiEmbeddingClient](#azureopenaiembeddingclient)
7. [Streaming](#streaming)
8. [Tool Binding](#tool-binding)
9. [Structured Output](#structured-output)
10. [LangGraph Agents](#langgraph-agents)
11. [Resilience Configuration](#resilience-configuration)

---

## Overview

The `@sap-ai-sdk/langchain` package provides LangChain-compatible clients built on SAP Cloud SDK for AI foundation models and orchestration clients.

**Important**: Use the same `@langchain/core` version as the `@sap-ai-sdk/langchain` package. Check the package.json for the correct version.

---

## Installation

```bash
npm install @sap-ai-sdk/langchain @langchain/core langchain
```

For agents with LangGraph:
```bash
npm install @sap-ai-sdk/langchain @langchain/core @langchain/langgraph langchain zod
```

---

## Available Clients

| Client | Purpose | Package |
|--------|---------|---------|
| `OrchestrationClient` | Orchestration service with LangChain | @sap-ai-sdk/langchain |
| `AzureOpenAiChatClient` | OpenAI chat via LangChain | @sap-ai-sdk/langchain |
| `AzureOpenAiEmbeddingClient` | OpenAI embeddings via LangChain | @sap-ai-sdk/langchain |

---

## OrchestrationClient

### Basic Usage

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

const config = {
  promptTemplating: {
    model: { name: 'gpt-4o' }
  }
};

const client = new OrchestrationClient(config);

// Simple invocation
const response = await client.invoke([
  new HumanMessage('What is SAP CAP?')
]);

console.log(response.content);
```

### With Placeholder Values

```typescript
const config = {
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [
      { role: 'system', content: 'You are an expert in {{?domain}}' },
      { role: 'user', content: '{{?question}}' }
    ]
  }
};

const client = new OrchestrationClient(config);

const response = await client.invoke([], {
  placeholderValues: {
    domain: 'SAP development',
    question: 'What is CDS?'
  }
});
```

### With Content Filtering

```typescript
import { buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';

const config = {
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildAzureContentSafetyFilter({ Hate: 'ALLOW_SAFE' }),
    output: buildAzureContentSafetyFilter({ Violence: 'ALLOW_SAFE' })
  }
};

const client = new OrchestrationClient(config);
```

### With Data Masking

```typescript
const config = {
  promptTemplating: { model: { name: 'gpt-4o' } },
  masking: {
    masking_providers: [{
      type: 'sap_data_privacy_integration',
      method: 'anonymization',
      entities: [
        { type: 'profile-email' },
        { type: 'profile-person' }
      ]
    }]
  }
};

const client = new OrchestrationClient(config);
```

---

## AzureOpenAiChatClient

### Basic Usage

```typescript
import { AzureOpenAiChatClient } from '@sap-ai-sdk/langchain';
import { HumanMessage } from '@langchain/core/messages';

const client = new AzureOpenAiChatClient({ modelName: 'gpt-4o' });

const response = await client.invoke([
  new HumanMessage('What is SAP CAP?')
]);

console.log(response.content);
```

### With Model Parameters

```typescript
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  modelVersion: 'latest',
  temperature: 0.7,
  maxTokens: 1000
});
```

### With Chains

```typescript
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

const client = new AzureOpenAiChatClient({ modelName: 'gpt-4o' });

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful SAP expert'],
  ['human', '{question}']
]);

const chain = prompt.pipe(client).pipe(new StringOutputParser());

const result = await chain.invoke({
  question: 'What is SAP CAP?'
});
```

---

## AzureOpenAiEmbeddingClient

### Basic Usage

```typescript
import { AzureOpenAiEmbeddingClient } from '@sap-ai-sdk/langchain';

const client = new AzureOpenAiEmbeddingClient({
  modelName: 'text-embedding-3-small'
});

// Single embedding
const embedding = await client.embedQuery('What is SAP?');
console.log(embedding.length); // Vector dimension

// Multiple embeddings
const embeddings = await client.embedDocuments([
  'SAP is an enterprise software company',
  'CAP is a framework for building business applications'
]);
```

### For RAG with Vector Stores

```typescript
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200
});

const docs = await splitter.createDocuments([documentText]);

const embeddings = new AzureOpenAiEmbeddingClient({
  modelName: 'text-embedding-3-large'
});

// Use with your vector store
// const vectorStore = await HanaVectorStore.fromDocuments(docs, embeddings);
```

---

## Streaming

### OrchestrationClient Streaming

```typescript
const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } }
});

const stream = await client.stream([
  new HumanMessage('Explain SAP CAP in detail')
]);

for await (const chunk of stream) {
  process.stdout.write(chunk.content);
}

// Note: Orchestration currently doesn't support multiple choices during streaming
```

### AzureOpenAiChatClient Streaming

```typescript
const client = new AzureOpenAiChatClient({ modelName: 'gpt-4o' });

const stream = await client.stream([
  new HumanMessage('Explain SAP CAP')
]);

for await (const chunk of stream) {
  process.stdout.write(chunk.content);
}

// Get finish reason and usage after streaming
console.log('\nFinish reason:', stream.getFinishReason());
console.log('Token usage:', stream.getTokenUsage());
```

### Abort Streaming

```typescript
const controller = new AbortController();

const stream = await client.stream(
  [new HumanMessage('Long explanation...')],
  { signal: controller.signal }
);

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
  for await (const chunk of stream) {
    process.stdout.write(chunk.content);
  }
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Stream cancelled');
  }
}
```

---

## Tool Binding

### Define and Bind Tools

```typescript
import { tool } from '@langchain/core/tools';
import { z } from 'zod';

const weatherTool = tool(
  async ({ city }) => {
    // Implement weather lookup
    return `Weather in ${city}: 20°C, sunny`;
  },
  {
    name: 'get_weather',
    description: 'Get current weather for a city',
    schema: z.object({
      city: z.string().describe('City name')
    })
  }
);

const client = new AzureOpenAiChatClient({ modelName: 'gpt-4o' });
const clientWithTools = client.bindTools([weatherTool]);

const response = await clientWithTools.invoke([
  new HumanMessage('What is the weather in Berlin?')
]);
```

### Process Tool Calls

```typescript
import { ToolMessage } from '@langchain/core/messages';

const response = await clientWithTools.invoke([
  new HumanMessage('What is the weather in Berlin?')
]);

if (response.tool_calls?.length) {
  const toolResults = [];

  for (const call of response.tool_calls) {
    const result = await weatherTool.invoke(call.args);
    toolResults.push(
      new ToolMessage({
        tool_call_id: call.id,
        content: result
      })
    );
  }

  // Continue conversation with tool results
  const finalResponse = await clientWithTools.invoke([
    new HumanMessage('What is the weather in Berlin?'),
    response,
    ...toolResults
  ]);
}
```

---

## Structured Output

### With Zod Schema

```typescript
import { z } from 'zod';

const WeatherSchema = z.object({
  city: z.string().describe('City name'),
  temperature: z.number().describe('Temperature in Celsius'),
  conditions: z.string().describe('Weather conditions')
});

const client = new AzureOpenAiChatClient({ modelName: 'gpt-4o' });
const structuredClient = client.withStructuredOutput(WeatherSchema);

const result = await structuredClient.invoke([
  new HumanMessage('What is the weather in Berlin?')
]);

// result is typed as { city: string, temperature: number, conditions: string }
console.log(result.city, result.temperature, result.conditions);
```

### With Strict Mode

```typescript
const structuredClient = client.withStructuredOutput(WeatherSchema, {
  strict: true // Enforce exact schema compliance
});
```

---

## LangGraph Agents

### Travel Assistant Agent Example

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';
import { StateGraph, START, END, MemorySaver } from '@langchain/langgraph';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { ToolNode } from '@langchain/langgraph/prebuilt';

// Define tools
const getWeather = tool(
  async ({ city }) => {
    const response = await fetch(
      `[https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true`](https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true`)
    );
    const data = await response.json();
    return JSON.stringify(data.current_weather);
  },
  {
    name: 'get_weather',
    description: 'Get current weather for a city',
    schema: z.object({ city: z.string() })
  }
);

const getRestaurants = tool(
  async ({ city }) => {
    return JSON.stringify([
      { name: 'Restaurant A', cuisine: 'French' },
      { name: 'Restaurant B', cuisine: 'Italian' }
    ]);
  },
  {
    name: 'get_restaurants',
    description: 'Get restaurant recommendations',
    schema: z.object({ city: z.string() })
  }
);

// Build tools and client
const tools = [getWeather, getRestaurants];
const toolNode = new ToolNode(tools);

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } }
});
const boundClient = client.bindTools(tools);

// Define state and graph
const graph = new StateGraph({
  channels: {
    messages: { value: (x, y) => x.concat(y), default: () => [] }
  }
})
  .addNode('agent', async (state) => {
    const response = await boundClient.invoke(state.messages);
    return { messages: [response] };
  })
  .addNode('tools', toolNode)
  .addEdge(START, 'agent')
  .addConditionalEdges('agent', (state) => {
    const lastMessage = state.messages[state.messages.length - 1];
    return lastMessage.tool_calls?.length ? 'tools' : END;
  })
  .addEdge('tools', 'agent');

const app = graph.compile({ checkpointer: new MemorySaver() });

// Run agent
const result = await app.invoke({
  messages: [new HumanMessage('Plan a day trip to Paris with restaurants')]
});
```

---

## Resilience Configuration

### Retries

```typescript
const client = new AzureOpenAiChatClient({
  modelName: 'gpt-4o',
  maxRetries: 3 // Default is 6
});
```

### Timeout

```typescript
const response = await client.invoke(
  [new HumanMessage('Query')],
  { timeout: 30000 } // 30 seconds
);
```

### Combined

```typescript
const client = new OrchestrationClient(config, {
  maxRetries: 3
});

const response = await client.invoke(messages, {
  timeout: 60000,
  signal: controller.signal
});
```

**Note**: Content filtering errors throw immediately without retry.

---

## Documentation Links

- LangChain SDK: [https://github.com/SAP/ai-sdk/tree/main/docs-js/langchain](https://github.com/SAP/ai-sdk/tree/main/docs-js/langchain)
- LangGraph Tutorial: [https://github.com/SAP/ai-sdk/blob/main/docs-js/tutorials/getting-started-with-agents.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/tutorials/getting-started-with-agents.mdx)

```

### references/spring-ai-guide.md

```markdown
# Spring AI Integration Guide

Guide for using SAP Cloud SDK for AI with Spring AI framework (Java).

## Table of Contents
1. [Overview](#overview)
2. [Dependencies](#dependencies)
3. [OpenAI Integration](#openai-integration)
4. [Orchestration Integration](#orchestration-integration)
5. [Embedding](#embedding)
6. [Streaming](#streaming)
7. [Tool Calling](#tool-calling)
8. [Chat Memory](#chat-memory)
9. [Response Format](#response-format)
10. [Prompt Registry](#prompt-registry)
11. [Data Protection](#data-protection)
12. [Content Filtering](#content-filtering)
13. [MCP Integration](#mcp-integration)

---

## Overview

SAP Cloud SDK for AI provides Spring AI integration through wrapper classes that bridge SAP's AI Core with Spring AI's ChatModel and EmbeddingModel interfaces.

**Requirements:**
- Spring AI 1.0.0+
- Spring Boot 3.0+
- SAP Cloud SDK for AI 1.10.0+

---

## Dependencies

### Maven Configuration

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-bom</artifactId>
      <version>1.0.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <!-- Spring AI Core -->
  <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-commons</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-model</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-client-chat</artifactId>
  </dependency>

  <!-- SAP SDK - Choose one or both -->
  <dependency>
    <groupId>com.sap.ai.sdk.foundationmodels</groupId>
    <artifactId>openai</artifactId>
    <version>${ai-sdk.version}</version>
  </dependency>
  <dependency>
    <groupId>com.sap.ai.sdk</groupId>
    <artifactId>orchestration</artifactId>
    <version>${ai-sdk.version}</version>
  </dependency>
</dependencies>
```

---

## OpenAI Integration

### Chat Completion

```java
import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient;
import com.sap.ai.sdk.foundationmodels.openai.OpenAiModel;
import com.sap.ai.sdk.foundationmodels.openai.spring.OpenAiChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.model.ChatResponse;

// Initialize client
var openAiClient = OpenAiClient.forModel(OpenAiModel.GPT_4O);
var chatModel = new OpenAiChatModel(openAiClient);

// Chat completion
var prompt = new Prompt("What is SAP CAP?");
ChatResponse response = chatModel.call(prompt);

System.out.println(response.getResult().getOutput().getContent());
```

### With Spring Bean

```java
@Configuration
public class AiConfig {
    @Bean
    public ChatModel chatModel() {
        var client = OpenAiClient.forModel(OpenAiModel.GPT_4O);
        return new OpenAiChatModel(client);
    }
}

@Service
public class AiService {
    private final ChatModel chatModel;

    public AiService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    public String askQuestion(String question) {
        return chatModel.call(new Prompt(question))
            .getResult()
            .getOutput()
            .getContent();
    }
}
```

---

## Orchestration Integration

### Basic Chat Completion

```java
import com.sap.ai.sdk.orchestration.OrchestrationClient;
import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig;
import com.sap.ai.sdk.orchestration.OrchestrationAiModel;
import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel;

var orchestrationClient = new OrchestrationClient();
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O);

var chatModel = new OrchestrationChatModel(orchestrationClient, config);

var prompt = new Prompt("What is SAP CAP?");
ChatResponse response = chatModel.call(prompt);

System.out.println(response.getResult().getOutput().getContent());
```

### With Configuration

```java
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O
        .withParam("max_tokens", 1000)
        .withParam("temperature", 0.7));

var chatModel = new OrchestrationChatModel(orchestrationClient, config);
```

---

## Embedding

### OpenAI Embedding

```java
import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient;
import com.sap.ai.sdk.foundationmodels.openai.OpenAiModel;
import com.sap.ai.sdk.foundationmodels.openai.spring.OpenAiSpringEmbeddingModel;

var client = OpenAiClient.forModel(OpenAiModel.TEXT_EMBEDDING_3_SMALL);
var embeddingModel = new OpenAiSpringEmbeddingModel(client);

// Generate embeddings
float[] embedding = embeddingModel.embed("SAP is an enterprise software company");
```

### Orchestration Embedding

```java
import com.sap.ai.sdk.orchestration.spring.OrchestrationSpringEmbeddingModel;
import com.sap.ai.sdk.orchestration.OrchestrationAiModel;

var embeddingModel = new OrchestrationSpringEmbeddingModel(
    orchestrationClient,
    OrchestrationAiModel.TEXT_EMBEDDING_3_LARGE
);

float[] embedding = embeddingModel.embed("SAP CAP framework");
```

### For RAG with Vector Stores

```java
// Use with HANA Vector Store (CAP integration)
import com.sap.cds.services.persistence.HanaVectorStore;

var documents = List.of(
    new Document("SAP CAP is a framework for building business applications"),
    new Document("Cloud Foundry is a platform for deploying applications")
);

// Store documents with embeddings
vectorStore.add(documents, embeddingModel);
```

---

## Streaming

### OpenAI Streaming

```java
import reactor.core.publisher.Flux;

var chatModel = new OpenAiChatModel(openAiClient);

Flux<ChatResponse> stream = chatModel.stream(
    new Prompt("Explain SAP CAP in detail")
);

stream.subscribe(response -> {
    String content = response.getResult().getOutput().getContent();
    System.out.print(content);
});
```

### Orchestration Streaming

```java
var chatModel = new OrchestrationChatModel(orchestrationClient, config);

Flux<ChatResponse> stream = chatModel.stream(
    new Prompt("Explain SAP CAP in detail")
);

stream.doOnComplete(() -> System.out.println("\nDone"))
    .subscribe(response -> {
        String delta = response.getResult().getOutput().getContent();
        if (delta != null) {
            System.out.print(delta);
        }
    });
```

### Blocking Stream Collection

```java
String fullResponse = stream
    .map(r -> r.getResult().getOutput().getContent())
    .filter(Objects::nonNull)
    .collect(Collectors.joining())
    .block();
```

---

## Tool Calling

### Define Tools with Annotations

```java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

public class WeatherTools {
    @Tool(description = "Get current weather for a city")
    public String getWeather(
        @ToolParam(description = "City name") String city,
        @ToolParam(description = "Unit: celsius or fahrenheit") String unit
    ) {
        // Implement weather lookup
        return String.format("Weather in %s: 20°C, sunny", city);
    }

    @Tool(description = "Get restaurant recommendations")
    public String getRestaurants(
        @ToolParam(description = "City name") String city
    ) {
        return "Restaurant A, Restaurant B";
    }
}
```

### Register Tools with ChatModel

```java
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.ToolCallAdvisor;

var tools = new WeatherTools();
var toolCallbacks = ToolCallbacks.from(tools);

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(new ToolCallAdvisor(toolCallbacks))
    .build();

String response = chatClient.prompt()
    .user("What's the weather in Berlin and recommend some restaurants?")
    .call()
    .content();
```

### With DefaultToolCallingChatOptions

```java
import org.springframework.ai.chat.prompt.DefaultToolCallingChatOptions;

var options = DefaultToolCallingChatOptions.builder()
    .tools(ToolCallbacks.from(new WeatherTools()))
    .build();

var prompt = new Prompt("What's the weather in Berlin?", options);
ChatResponse response = chatModel.call(prompt);
```

---

## Chat Memory

### InMemory Chat Memory

```java
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;

// Create memory
var memoryRepository = new InMemoryChatMemoryRepository();
var chatMemory = MessageWindowChatMemory.builder()
    .chatMemoryRepository(memoryRepository)
    .maxMessages(10)
    .build();

// Create chat client with memory
var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
    .build();

// Conversation
String response1 = chatClient.prompt()
    .user("My name is John")
    .call()
    .content();

String response2 = chatClient.prompt()
    .user("What is my name?") // Will remember John
    .call()
    .content();
```

### With Conversation ID

```java
String conversationId = "user-123";

String response = chatClient.prompt()
    .user("Hello")
    .advisors(spec -> spec.param("chat_memory_conversation_id", conversationId))
    .call()
    .content();
```

---

## Response Format

### JSON Schema Response

```java
import com.fasterxml.jackson.annotation.JsonProperty;

public record WeatherResponse(
    @JsonProperty(required = true) String city,
    @JsonProperty(required = true) double temperature,
    @JsonProperty(required = true) String conditions
) {}

var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withResponseFormat(ResponseFormat.jsonSchema(WeatherResponse.class));

var chatModel = new OrchestrationChatModel(orchestrationClient, config);

var response = chatModel.call(new Prompt("Weather in Berlin?"));
// Response will be valid JSON matching WeatherResponse schema
```

### Parse Response to Object

```java
import com.fasterxml.jackson.databind.ObjectMapper;

var objectMapper = new ObjectMapper();
var content = response.getResult().getOutput().getContent();
WeatherResponse weather = objectMapper.readValue(content, WeatherResponse.class);

System.out.println(weather.city() + ": " + weather.temperature() + "°C");
```

---

## Prompt Registry

### Use Template from Registry

```java
import com.sap.ai.sdk.orchestration.spring.SpringAiConverter;

// Reference template by scenario/name/version
var prompt = new OrchestrationPrompt()
    .withTemplateRef("my-template", "customer-support", "1.0.0");

// Use with Spring AI ChatClient
var chatClient = ChatClient.builder(chatModel).build();

String response = chatClient.prompt()
    .user(SpringAiConverter.toSpringAiMessage(prompt))
    .call()
    .content();
```

### Create Template

```java
import com.sap.ai.sdk.promptregistry.PromptClient;
import com.sap.ai.sdk.promptregistry.PromptTemplateSpec;

var promptClient = new PromptClient();

var templateSpec = PromptTemplateSpec.builder()
    .name("greeting-template")
    .scenario("customer-support")
    .version("1.0.0")
    .template("Hello {{name}}, how can I help you today?")
    .build();

promptClient.createOrUpdateTemplate(templateSpec);
```

---

## Data Protection

### DPI Masking

```java
import com.sap.ai.sdk.orchestration.DpiMasking;
import com.sap.ai.sdk.orchestration.DpiEntity;

var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withMasking(DpiMasking.anonymization()
        .withEntities(
            DpiEntity.EMAIL,
            DpiEntity.PERSON,
            DpiEntity.PHONE,
            DpiEntity.ADDRESS,
            DpiEntity.LOCATION
        ));

var chatModel = new OrchestrationChatModel(orchestrationClient, config);

// Personal data in input will be anonymized before sending to LLM
var response = chatModel.call(
    new Prompt("Process this: John Doe, [email protected], +1-555-1234")
);
```

### Pseudonymization

```java
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withMasking(DpiMasking.pseudonymization()
        .withEntities(DpiEntity.EMAIL, DpiEntity.PERSON));
```

---

## Content Filtering

### Azure Content Filter

```java
import com.sap.ai.sdk.orchestration.AzureContentFilter;
import com.sap.ai.sdk.orchestration.AzureThreshold;

var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withInputFiltering(AzureContentFilter.builder()
        .hate(AzureThreshold.ALLOW_SAFE)
        .selfHarm(AzureThreshold.ALLOW_SAFE)
        .sexual(AzureThreshold.ALLOW_SAFE)
        .violence(AzureThreshold.ALLOW_SAFE)
        .build())
    .withOutputFiltering(AzureContentFilter.builder()
        .hate(AzureThreshold.ALLOW_SAFE_LOW_MEDIUM)
        .violence(AzureThreshold.ALLOW_SAFE)
        .build());

var chatModel = new OrchestrationChatModel(orchestrationClient, config);
```

### Llama Guard Filter

```java
import com.sap.ai.sdk.orchestration.LlamaGuardFilter;

var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O)
    .withInputFiltering(LlamaGuardFilter.builder()
        .enabledCategories("violent_crimes", "hate", "sexual_content")
        .build());
```

---

## MCP Integration

### Model Context Protocol with Spring

```java
import org.springframework.ai.mcp.client.autoconfigure.McpAutoConfiguration;
import org.springframework.ai.tool.ToolCallbackProvider;

@Configuration
@Import(McpAutoConfiguration.class)
public class McpConfig {
    @Bean
    public ChatClient chatClient(
        ChatModel chatModel,
        ToolCallbackProvider toolCallbackProvider
    ) {
        return ChatClient.builder(chatModel)
            .defaultTools(toolCallbackProvider.getToolCallbacks())
            .build();
    }
}
```

### MCP Server Connection

```yaml
# application.yml
spring:
  ai:
    mcp:
      client:
        enabled: true
        servers:
          - name: "my-tools"
            url: "[http://localhost:3000/mcp"](http://localhost:3000/mcp")
```

---

## Documentation Links

- Spring AI OpenAI: [https://github.com/SAP/ai-sdk/blob/main/docs-java/spring-ai/openai.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/spring-ai/openai.mdx)
- Spring AI Orchestration: [https://github.com/SAP/ai-sdk/blob/main/docs-java/spring-ai/orchestration.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/spring-ai/orchestration.mdx)

```

### references/ai-core-api-guide.md

```markdown
# AI Core API Guide

Guide for managing SAP AI Core resources using SAP Cloud SDK for AI.

## Table of Contents
1. [Overview](#overview)
2. [Installation](#installation)
3. [AI API Package](#ai-api-package)
4. [Document Grounding](#document-grounding)
5. [Prompt Registry](#prompt-registry)
6. [Deployment Management](#deployment-management)
7. [Custom Destinations](#custom-destinations)

---

## Overview

The AI Core APIs provide programmatic access to manage:
- **Deployments**: Create, start, stop, delete model deployments
- **Artifacts**: Register datasets and models
- **Configurations**: Define execution parameters
- **Document Grounding**: Manage vector stores and pipelines
- **Prompt Registry**: Create and manage prompt templates

---

## Installation

### JavaScript/TypeScript

```bash
npm install @sap-ai-sdk/ai-api
npm install @sap-ai-sdk/document-grounding
npm install @sap-ai-sdk/prompt-registry
```

**Important**: Use tilde (`~`) version ranges instead of caret (`^`) as these packages contain generated code that may have breaking changes:

```json
{
  "dependencies": {
    "@sap-ai-sdk/ai-api": "~2.2.0",
    "@sap-ai-sdk/document-grounding": "~2.2.0",
    "@sap-ai-sdk/prompt-registry": "~2.2.0"
  }
}
```

### Java (Maven)

```xml
<dependency>
  <groupId>com.sap.ai.sdk</groupId>
  <artifactId>core</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
<dependency>
  <groupId>com.sap.ai.sdk</groupId>
  <artifactId>document-grounding</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
<dependency>
  <groupId>com.sap.ai.sdk</groupId>
  <artifactId>prompt-registry</artifactId>
  <version>${ai-sdk.version}</version>
</dependency>
```

---

## AI API Package

### Create Artifact

```typescript
import { ArtifactApi } from '@sap-ai-sdk/ai-api';

const artifact = await ArtifactApi.artifactCreate(
  {
    name: 'my-dataset',
    kind: 'dataset',
    url: 's3://bucket/path/to/data',
    scenarioId: 'my-scenario'
  },
  { 'AI-Resource-Group': 'default' }
).execute();

console.log('Artifact ID:', artifact.id);
```

### Create Configuration

```typescript
import { ConfigurationApi } from '@sap-ai-sdk/ai-api';

const config = await ConfigurationApi.configurationCreate(
  {
    name: 'my-config',
    executableId: 'my-executable',
    scenarioId: 'my-scenario',
    parameterBindings: [
      { key: 'learning_rate', value: '0.001' }
    ],
    inputArtifactBindings: [
      { key: 'training_data', artifactId: 'artifact-id' }
    ]
  },
  { 'AI-Resource-Group': 'default' }
).execute();
```

### Create Deployment

```typescript
import { DeploymentApi } from '@sap-ai-sdk/ai-api';

const deployment = await DeploymentApi.deploymentCreate(
  { configurationId: config.id },
  { 'AI-Resource-Group': 'default' }
).execute();

console.log('Deployment ID:', deployment.id);
console.log('Status:', deployment.status);
```

### Modify and Delete Deployment

```typescript
// Stop deployment (only RUNNING can be stopped)
await DeploymentApi.deploymentModify(
  deployment.id,
  { targetStatus: 'STOPPED' },
  { 'AI-Resource-Group': 'default' }
).execute();

// Delete deployment (only STOPPED or UNKNOWN can be deleted)
await DeploymentApi.deploymentDelete(
  deployment.id,
  { 'AI-Resource-Group': 'default' }
).execute();
```

### Query Deployments

```typescript
// Query deployments with filters
const deployments = await DeploymentApi.deploymentQuery(
  {
    status: 'RUNNING',
    scenarioId: 'orchestration'
  },
  { 'AI-Resource-Group': 'default' }
).execute();

for (const deployment of deployments.resources) {
  console.log(`ID: ${deployment.id}, Status: ${deployment.status}`);
}
```

### Resolve Deployment URL

```typescript
import { resolveDeploymentUrl } from '@sap-ai-sdk/ai-api';

const url = await resolveDeploymentUrl({
  scenarioId: 'orchestration',
  modelName: 'gpt-4o'
});

console.log('Deployment URL:', url);
```

---

## Document Grounding

### Prerequisites

Custom resource groups require the label `document-grounding: true`:

```typescript
// Via AI Launchpad UI or AI API
await ResourceGroupApi.resourceGroupModify(
  'my-resource-group',
  { labels: [{ key: 'document-grounding', value: 'true' }] }
).execute();
```

### Pipeline API

```typescript
import { PipelinesApi } from '@sap-ai-sdk/document-grounding';

// List pipelines
const pipelines = await PipelinesApi.listPipelines({
  'AI-Resource-Group': 'default'
}).execute();

// Create SharePoint pipeline
const pipeline = await PipelinesApi.createPipeline(
  {
    type: 'MSSharePoint',
    configuration: {
      siteUrl: '[https://company.sharepoint.com/sites/docs',](https://company.sharepoint.com/sites/docs',)
      clientId: 'client-id',
      clientSecret: 'client-secret',
      tenantId: 'tenant-id'
    }
  },
  { 'AI-Resource-Group': 'default' }
).execute();

// Get pipeline status
const status = await PipelinesApi.getPipelineStatus(
  pipeline.id,
  { 'AI-Resource-Group': 'default' }
).execute();
```

### Vector API

```typescript
import { VectorApi } from '@sap-ai-sdk/document-grounding';

// Create collection
const collection = await VectorApi.createCollection(
  {
    name: 'my-collection',
    embeddingModel: 'text-embedding-3-large'
  },
  { 'AI-Resource-Group': 'default' }
).execute();

// Add documents
await VectorApi.createDocuments(
  collection.id,
  {
    documents: [
      {
        chunks: [
          { content: 'SAP CAP is a framework...', metadata: { topic: 'cap' } },
          { content: 'Cloud Foundry provides...', metadata: { topic: 'cf' } }
        ]
      }
    ]
  },
  { 'AI-Resource-Group': 'default' }
).execute();

// Delete collection
await VectorApi.deleteCollectionById(
  collection.id,
  { 'AI-Resource-Group': 'default' }
).execute();
```

### Retrieval API

```typescript
import { RetrievalApi } from '@sap-ai-sdk/document-grounding';

// Search documents
const results = await RetrievalApi.search(
  {
    query: 'What is SAP CAP?',
    dataRepositories: [{ type: 'vector', id: collection.id }],
    maxChunks: 5
  },
  { 'AI-Resource-Group': 'default' }
).execute();

for (const chunk of results.chunks) {
  console.log('Content:', chunk.content);
  console.log('Score:', chunk.score);
}
```

### Java Document Grounding

```java
import com.sap.ai.sdk.documentgrounding.*;

// Create client
var pipelineClient = new PipelinesApi();
var vectorClient = new VectorApi();
var retrievalClient = new RetrievalApi();

// List pipelines
var pipelines = pipelineClient.listPipelines()
    .withHeader("AI-Resource-Group", "default")
    .execute();

// Search documents
var results = retrievalClient.search(
    SearchRequest.builder()
        .query("What is SAP CAP?")
        .dataRepositories(List.of(
            VectorRepository.create("collection-id")
        ))
        .maxChunks(5)
        .build()
).execute();
```

---

## Prompt Registry

### JavaScript

```typescript
import { PromptTemplatesApi } from '@sap-ai-sdk/prompt-registry';

// List templates
const templates = await PromptTemplatesApi.listPromptTemplates(
  { scenarioId: 'customer-support' },
  { 'AI-Resource-Group': 'default' }
).execute();

// Get template by ID
const template = await PromptTemplatesApi.getPromptTemplate(
  'template-id',
  { 'AI-Resource-Group': 'default' }
).execute();
```

### Java Prompt Registry

```java
import com.sap.ai.sdk.promptregistry.PromptClient;
import com.sap.ai.sdk.promptregistry.PromptTemplateSpec;

var promptClient = new PromptClient();

// Create template
var templateSpec = PromptTemplateSpec.builder()
    .name("greeting-template")
    .scenario("customer-support")
    .version("1.0.0")
    .template("Hello {{name}}, welcome to {{company}}!")
    .build();

promptClient.createOrUpdateTemplate(templateSpec);

// Retrieve template
var template = promptClient.getTemplate("template-id");

// Or by name/scenario/version
var template = promptClient.getTemplate("greeting-template", "customer-support", "1.0.0");

// List templates
var templates = promptClient.listTemplates("customer-support");

// Delete template (only imperatively managed)
promptClient.deleteTemplate("template-id", "1.0.0");

// List template edit history
var history = promptClient.listPromptTemplateHistory(
    "customer-support", // scenario
    "1.0.0",            // version
    "greeting-template" // name
);
```

### Import YAML Templates

```java
// Import declarative templates from YAML
promptClient.importTemplates(new File("templates.yaml"));
```

```yaml
# templates.yaml
templates:
  - name: support-greeting
    scenario: customer-support
    version: "1.0.0"
    template: |
      You are a helpful support agent for {{company}}.
      Greet the customer: {{customer_name}}
```

### Use with Orchestration

```typescript
// JavaScript - reference template in orchestration
const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: {
      ref: {
        name: 'support-greeting',
        scenario: 'customer-support',
        version: '1.0.0'
      }
    }
  }
});

const response = await client.chatCompletion({
  placeholderValues: {
    company: 'SAP',
    customer_name: 'John'
  }
});
```

```java
// Java - reference template in orchestration
var prompt = new OrchestrationPrompt()
    .withTemplateRef("support-greeting", "customer-support", "1.0.0");

var result = client.chatCompletion(prompt, config);
```

### Spring AI Integration with Prompt Registry

```java
import com.sap.ai.sdk.orchestration.spring.SpringAiConverter;

// Get template and convert to Spring AI messages
var templateResponse = promptClient.parsePromptTemplateByNameVersion(
    "customer-support", "1.0.0", "support-greeting",
    "default",
    false, // includeHistory
    new PromptTemplateSubstitutionRequest()
        .inputParams(Map.of("company", "SAP", "customer_name", "John"))
);

// Convert to Spring AI message list
var messages = SpringAiConverter.promptTemplateToMessages(templateResponse);

// Use with Spring AI ChatClient
var chatClient = ChatClient.builder(chatModel).build();
var response = chatClient.prompt()
    .messages(messages)
    .call()
    .content();
```

---

## Deployment Management

### Java Deployment API

```java
import com.sap.ai.sdk.core.DeploymentApi;
import com.sap.ai.sdk.core.model.*;

var deploymentApi = new DeploymentApi();

// Create deployment
var deploymentRequest = new AiDeploymentCreationRequest()
    .configurationId("config-id");

var deployment = deploymentApi.create(
    "default", // resource group
    deploymentRequest
).execute();

System.out.println("Deployment ID: " + deployment.getId());
System.out.println("Status: " + deployment.getStatus());

// Check deployment status
var status = deploymentApi.get(
    "default",
    deployment.getId()
).execute();

// Stop deployment (only RUNNING deployments)
if (status.getStatus() == AiDeploymentStatus.RUNNING) {
    deploymentApi.modify(
        "default",
        deployment.getId(),
        new AiDeploymentModificationRequest()
            .targetStatus(AiDeploymentTargetStatus.STOPPED)
    ).execute();
}

// Delete deployment (only STOPPED or UNKNOWN)
if (status.getStatus() == AiDeploymentStatus.STOPPED ||
    status.getStatus() == AiDeploymentStatus.UNKNOWN) {
    deploymentApi.delete("default", deployment.getId()).execute();
}
```

---

## Custom Destinations

### JavaScript

```typescript
// Use custom destination
const artifact = await ArtifactApi.artifactCreate(
  { name: 'my-artifact', kind: 'dataset', url: 's3://bucket/data' },
  { 'AI-Resource-Group': 'default' }
).execute({
  destinationName: 'my-aicore-destination',
  useCache: false // disable destination caching
});
```

### Java

```java
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.ai.sdk.core.AiCoreService;

// Get destination
var destination = DestinationAccessor.getDestination("my-aicore-destination").asHttp();

// Use with AI Core service
var aiCoreService = new AiCoreService().withBaseDestination(destination);

// APIs will now use this destination
var deploymentApi = new DeploymentApi(aiCoreService);
```

---

## Documentation Links

- AI API JS: [https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/ai-api.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/ai-api.mdx)
- Document Grounding JS: [https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/document-grounding.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/document-grounding.mdx)
- Prompt Registry JS: [https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/prompt-registry.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/ai-core/prompt-registry.mdx)
- AI Core Deployment Java: [https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/ai-core-deployment.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/ai-core-deployment.mdx)
- Document Grounding Java: [https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/document-grounding.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/document-grounding.mdx)
- Prompt Registry Java: [https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/prompt-registry.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/ai-core/prompt-registry.mdx)

```

### references/agentic-workflows.md

```markdown
# Agentic Workflows Guide

Guide for building AI agents and workflows using SAP Cloud SDK for AI.

## Table of Contents
1. [Overview](#overview)
2. [JavaScript with LangGraph](#javascript-with-langgraph)
3. [Java with Spring AI](#java-with-spring-ai)
4. [Tool Definition Patterns](#tool-definition-patterns)
5. [State Management](#state-management)
6. [Human-in-the-Loop](#human-in-the-loop)
7. [MCP Integration](#mcp-integration)

---

## Overview

Agentic workflows enable AI models to:
- Execute multi-step tasks autonomously
- Call external tools and APIs
- Maintain conversation state across turns
- Request human confirmation when needed

**Frameworks:**
- **JavaScript**: LangGraph with `@sap-ai-sdk/langchain`
- **Java**: Spring AI with `com.sap.ai.sdk:orchestration`

---

## JavaScript with LangGraph

### Complete Travel Assistant Example

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';
import { StateGraph, START, END, MemorySaver, Annotation } from '@langchain/langgraph';
import { HumanMessage, AIMessage, BaseMessage } from '@langchain/core/messages';
import { tool } from '@langchain/core/tools';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { z } from 'zod';

// 1. Define Tools
const getWeather = tool(
  async ({ city }) => {
    // Call weather API
    const response = await fetch(
      `[https://api.open-meteo.com/v1/forecast?latitude=48.85&longitude=2.35&current_weather=true`](https://api.open-meteo.com/v1/forecast?latitude=48.85&longitude=2.35&current_weather=true`)
    );
    const data = await response.json();
    return JSON.stringify({
      city,
      temperature: data.current_weather.temperature,
      conditions: data.current_weather.weathercode < 3 ? 'sunny' : 'cloudy'
    });
  },
  {
    name: 'get_weather',
    description: 'Get current weather for a city',
    schema: z.object({
      city: z.string().describe('City name')
    })
  }
);

const getRestaurants = tool(
  async ({ city, cuisine }) => {
    // Mock restaurant data
    const restaurants = {
      Paris: [
        { name: 'Le Comptoir', cuisine: 'French', rating: 4.5 },
        { name: 'Chez Georges', cuisine: 'French', rating: 4.3 }
      ],
      Berlin: [
        { name: 'Nobelhart & Schmutzig', cuisine: 'German', rating: 4.7 },
        { name: 'Einsunternull', cuisine: 'Modern', rating: 4.4 }
      ]
    };
    return JSON.stringify(restaurants[city] || []);
  },
  {
    name: 'get_restaurants',
    description: 'Get restaurant recommendations for a city',
    schema: z.object({
      city: z.string().describe('City name'),
      cuisine: z.string().optional().describe('Preferred cuisine type')
    })
  }
);

// 2. Configure Client with Tools
const tools = [getWeather, getRestaurants];
const toolNode = new ToolNode(tools);

const client = new OrchestrationClient({
  promptTemplating: {
    model: { name: 'gpt-4o' },
    prompt: [
      {
        role: 'system',
        content: 'You are a helpful travel assistant. Create detailed one-day itineraries.'
      }
    ]
  }
});
const boundClient = client.bindTools(tools);

// 3. Define State Schema
const StateAnnotation = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (x, y) => x.concat(y),
    default: () => []
  })
});

// 4. Define Agent Node
async function agentNode(state: typeof StateAnnotation.State) {
  const response = await boundClient.invoke(state.messages);
  return { messages: [response] };
}

// 5. Define Routing Logic
function shouldContinue(state: typeof StateAnnotation.State) {
  const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
  if (lastMessage.tool_calls && lastMessage.tool_calls.length > 0) {
    return 'tools';
  }
  return END;
}

// 6. Build Graph
const graph = new StateGraph(StateAnnotation)
  .addNode('agent', agentNode)
  .addNode('tools', toolNode)
  .addEdge(START, 'agent')
  .addConditionalEdges('agent', shouldContinue, {
    tools: 'tools',
    [END]: END
  })
  .addEdge('tools', 'agent');

// 7. Compile with Memory
const app = graph.compile({
  checkpointer: new MemorySaver()
});

// 8. Run Agent
async function runAgent() {
  const threadId = 'user-session-123';

  const result = await app.invoke(
    {
      messages: [
        new HumanMessage('Plan a day trip to Paris with lunch recommendations')
      ]
    },
    { configurable: { thread_id: threadId } }
  );

  const lastMessage = result.messages[result.messages.length - 1];
  console.log('Agent Response:', lastMessage.content);
}

runAgent();
```

### Streaming Agent Responses

```typescript
const stream = await app.streamEvents(
  { messages: [new HumanMessage('Plan trip to Berlin')] },
  { configurable: { thread_id: 'user-123' }, version: 'v2' }
);

for await (const event of stream) {
  if (event.event === 'on_chat_model_stream') {
    const chunk = event.data.chunk;
    if (chunk.content) {
      process.stdout.write(chunk.content);
    }
  }
}
```

---

## Java with Spring AI

### Complete Travel Assistant Example

```java
import com.sap.ai.sdk.orchestration.*;
import com.sap.ai.sdk.orchestration.spring.*;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.*;
import org.springframework.ai.tool.annotation.*;
import org.springframework.ai.tool.ToolCallbacks;

// 1. Define Tool Methods
public class TravelTools {

    @Tool(description = "Get current weather for a city")
    public String getWeather(
        @ToolParam(description = "City name") String city
    ) {
        // Mock weather data based on city hash
        int temp = Math.abs(city.hashCode() % 20) + 10;
        return String.format(
            "{\"city\":\"%s\",\"temperature\":%d,\"conditions\":\"sunny\"}",
            city, temp
        );
    }

    @Tool(description = "Get restaurant recommendations")
    public String getRestaurants(
        @ToolParam(description = "City name") String city
    ) {
        if (city.equalsIgnoreCase("Paris")) {
            return "[{\"name\":\"Le Comptoir\",\"cuisine\":\"French\"}," +
                   "{\"name\":\"Chez Georges\",\"cuisine\":\"Bistro\"}]";
        }
        return "[{\"name\":\"Local Restaurant\",\"cuisine\":\"International\"}]";
    }
}

// 2. Configure Chat Client
@Service
public class TravelAgentService {

    private final ChatClient chatClient;
    private final MessageWindowChatMemory chatMemory;

    public TravelAgentService() {
        // Create orchestration client
        var orchestrationClient = new OrchestrationClient();
        var config = new OrchestrationModuleConfig()
            .withLlmConfig(OrchestrationAiModel.GPT_4O);

        var chatModel = new OrchestrationChatModel(orchestrationClient, config);

        // Create memory
        var memoryRepository = new InMemoryChatMemoryRepository();
        this.chatMemory = MessageWindowChatMemory.builder()
            .chatMemoryRepository(memoryRepository)
            .maxMessages(20)
            .build();

        // Create tool callbacks
        var tools = new TravelTools();
        var toolCallbacks = ToolCallbacks.from(tools);

        // Build chat client
        this.chatClient = ChatClient.builder(chatModel)
            .defaultAdvisors(
                new MessageChatMemoryAdvisor(chatMemory),
                new ToolCallAdvisor(toolCallbacks)
            )
            .defaultSystem("You are a helpful travel assistant. " +
                          "Create detailed one-day itineraries with weather and dining.")
            .build();
    }

    public String planTrip(String destination, String conversationId) {
        return chatClient.prompt()
            .user("Plan a one-day trip to " + destination +
                  " with weather info and restaurant recommendations")
            .advisors(spec -> spec
                .param("chat_memory_conversation_id", conversationId))
            .call()
            .content();
    }
}

// 3. Run Agent Workflow
public class TravelAgentRunner {
    public static void main(String[] args) {
        var agent = new TravelAgentService();

        // Multi-turn conversation
        String conversationId = "user-session-123";

        String response1 = agent.planTrip("Paris", conversationId);
        System.out.println("Agent: " + response1);

        // Follow-up uses same conversation memory
        String response2 = agent.planTrip("Berlin", conversationId);
        System.out.println("Agent: " + response2);
    }
}
```

### Streaming in Java

```java
import reactor.core.publisher.Flux;

public Flux<String> planTripStreaming(String destination) {
    return chatClient.prompt()
        .user("Plan a trip to " + destination)
        .stream()
        .content();
}

// Usage
planTripStreaming("Tokyo")
    .doOnNext(System.out::print)
    .doOnComplete(() -> System.out.println("\n--- Complete ---"))
    .blockLast();
```

---

## Tool Definition Patterns

### JavaScript - Zod Schema

```typescript
import { z } from 'zod';
import { tool } from '@langchain/core/tools';

const searchFlights = tool(
  async ({ origin, destination, date }) => {
    // Implementation
    return JSON.stringify([
      { flight: 'LH123', price: 299, departure: '08:00' },
      { flight: 'BA456', price: 349, departure: '10:30' }
    ]);
  },
  {
    name: 'search_flights',
    description: 'Search for available flights',
    schema: z.object({
      origin: z.string().describe('Departure airport code (e.g., FRA)'),
      destination: z.string().describe('Arrival airport code (e.g., LHR)'),
      date: z.string().describe('Travel date in YYYY-MM-DD format')
    })
  }
);
```

### Java - Annotation-Based

```java
public class BookingTools {

    @Tool(description = "Search for available flights between cities")
    public String searchFlights(
        @ToolParam(description = "Departure airport code") String origin,
        @ToolParam(description = "Arrival airport code") String destination,
        @ToolParam(description = "Travel date (YYYY-MM-DD)") String date
    ) {
        // Implementation
        return "[{\"flight\":\"LH123\",\"price\":299}]";
    }

    @Tool(description = "Book a hotel room")
    public String bookHotel(
        @ToolParam(description = "City name") String city,
        @ToolParam(description = "Check-in date") String checkIn,
        @ToolParam(description = "Check-out date") String checkOut,
        @ToolParam(description = "Number of guests") int guests
    ) {
        return "{\"confirmation\":\"HTL-" + System.currentTimeMillis() + "\"}";
    }
}
```

---

## State Management

### JavaScript - Custom State

```typescript
const StateAnnotation = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (x, y) => x.concat(y),
    default: () => []
  }),
  tripPlan: Annotation<object>({
    reducer: (_, y) => y,
    default: () => ({})
  }),
  userPreferences: Annotation<object>({
    reducer: (x, y) => ({ ...x, ...y }),
    default: () => ({})
  })
});

// Access state in nodes
async function plannerNode(state: typeof StateAnnotation.State) {
  const preferences = state.userPreferences;
  // Use preferences in planning...
  return {
    tripPlan: { destination: 'Paris', days: 3 },
    messages: [new AIMessage('Trip planned!')]
  };
}
```

### Java - Conversation Memory

```java
// Per-user conversation memory
var memoryRepository = new InMemoryChatMemoryRepository();

// Get or create user memory
String userId = "user-123";
var userMemory = MessageWindowChatMemory.builder()
    .chatMemoryRepository(memoryRepository)
    .conversationId(userId)
    .maxMessages(50)
    .build();
```

---

## Human-in-the-Loop

### JavaScript - Graph Interrupts

```typescript
import { interrupt, Command } from '@langchain/langgraph';

// Define node that requires confirmation
async function confirmationNode(state: typeof StateAnnotation.State) {
  const plan = state.tripPlan;

  // Request human confirmation
  const approved = interrupt({
    question: 'Do you approve this trip plan?',
    plan: plan
  });

  if (!approved) {
    return { messages: [new AIMessage('Trip cancelled.')] };
  }

  return { messages: [new AIMessage('Trip confirmed! Proceeding with booking.')] };
}

// Build graph with interrupt
const graph = new StateGraph(StateAnnotation)
  .addNode('planner', plannerNode)
  .addNode('confirm', confirmationNode)
  .addNode('booker', bookingNode)
  .addEdge(START, 'planner')
  .addEdge('planner', 'confirm')
  .addEdge('confirm', 'booker')
  .addEdge('booker', END);

const app = graph.compile({
  checkpointer: new MemorySaver(),
  interruptBefore: ['confirm'] // Pause before confirmation
});

// Run until interrupt
let result = await app.invoke(
  { messages: [new HumanMessage('Plan trip to Paris')] },
  { configurable: { thread_id: 'trip-123' } }
);

// Check if interrupted
const state = await app.getState({ configurable: { thread_id: 'trip-123' } });
if (state.next.includes('confirm')) {
  // Get user input, then resume
  const userApproved = true; // From user input

  result = await app.invoke(
    new Command({ resume: userApproved }),
    { configurable: { thread_id: 'trip-123' } }
  );
}
```

---

## MCP Integration

### JavaScript - MCP Adapter

```typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { loadMcpTools } from '@langchain/mcp-adapters';

// Connect to MCP server
const transport = new StdioClientTransport({
  command: 'npx',
  args: ['-y', '@anthropic/mcp-server-weather']
});

const mcpClient = new Client({ name: 'travel-agent', version: '1.0.0' });
await mcpClient.connect(transport);

// Load tools from MCP server
const mcpTools = await loadMcpTools({ client: mcpClient });

// Combine with local tools
const allTools = [...localTools, ...mcpTools];

const boundClient = client.bindTools(allTools);
```

### Java - Spring MCP

```java
@Configuration
@Import(McpAutoConfiguration.class)
public class McpConfig {

    @Bean
    public ChatClient agentChatClient(
        ChatModel chatModel,
        ToolCallbackProvider mcpToolProvider
    ) {
        // Get tools from MCP servers
        var mcpTools = mcpToolProvider.getToolCallbacks();

        // Combine with local tools
        var localTools = ToolCallbacks.from(new TravelTools());
        var allTools = new ArrayList<>(mcpTools);
        allTools.addAll(Arrays.asList(localTools));

        return ChatClient.builder(chatModel)
            .defaultTools(allTools.toArray(new ToolCallback[0]))
            .build();
    }
}
```

```yaml
# application.yml
spring:
  ai:
    mcp:
      client:
        enabled: true
        servers:
          - name: weather
            command: npx
            args: ["-y", "@anthropic/mcp-server-weather"]
```

---

## Documentation Links

- LangGraph Tutorial (JS): [https://github.com/SAP/ai-sdk/blob/main/docs-js/tutorials/getting-started-with-agents.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/tutorials/getting-started-with-agents.mdx)
- Agentic Workflows (Java): [https://github.com/SAP/ai-sdk/blob/main/docs-java/tutorials/agentic-workflows.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/tutorials/agentic-workflows.mdx)
- LangGraph Documentation: [https://langchain-ai.github.io/langgraphjs/](https://langchain-ai.github.io/langgraphjs/)
- Spring AI Agents: [https://docs.spring.io/spring-ai/reference/](https://docs.spring.io/spring-ai/reference/)

```

### references/error-handling.md

```markdown
# Error Handling Guide

Guide for handling errors when using SAP Cloud SDK for AI.

## Table of Contents
1. [Overview](#overview)
2. [JavaScript Error Handling](#javascript-error-handling)
3. [Java Error Handling](#java-error-handling)
4. [Common Errors](#common-errors)
5. [Content Filter Errors](#content-filter-errors)
6. [Retry Strategies](#retry-strategies)

---

## Overview

SAP Cloud SDK for AI provides structured error handling with:
- **ErrorWithCause** (JS) - Nested error chains with root cause access
- **Exceptions** (Java) - Standard Java exception hierarchy
- **Detailed error messages** - HTTP status, response body, context

---

## JavaScript Error Handling

### ErrorWithCause Pattern

The SDK uses `ErrorWithCause` to provide detailed error context:

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } }
});

try {
  const response = await client.chatCompletion({
    placeholderValues: { question: 'Hello' }
  });
  console.log(response.getContent());
} catch (error) {
  if (error instanceof Error) {
    // Top-level error message
    console.error('Error:', error.message);

    // Access cause chain
    if ('cause' in error && error.cause instanceof Error) {
      console.error('Caused by:', error.cause.message);

      // HTTP response details
      if ('response' in error.cause) {
        const response = (error.cause as any).response;
        console.error('Status:', response?.status);
        console.error('Data:', response?.data);
      }
    }

    // Root cause
    if ('rootCause' in error) {
      console.error('Root cause:', (error as any).rootCause.message);
    }
  }
}
```

### Error Stack Representation

```
Error: Chat completion request failed
    at OrchestrationClient.chatCompletion (...)
Caused by: Request to orchestration service failed
    at sendRequest (...)
Caused by: AxiosError: Request failed with status code 401
    at Axios.request (...)
```

### Handling Specific Error Types

```typescript
import axios from 'axios';

try {
  await client.chatCompletion({ placeholderValues });
} catch (error) {
  // Check for HTTP errors
  if (axios.isAxiosError(error) || error.cause?.code === 'ECONNREFUSED') {
    console.error('Network error - check connectivity');
    return;
  }

  // Check for timeout
  if (error.message?.includes('timeout')) {
    console.error('Request timed out');
    return;
  }

  // Check for auth errors
  if (error.cause?.response?.status === 401) {
    console.error('Authentication failed - check credentials');
    return;
  }

  // Check for rate limiting
  if (error.cause?.response?.status === 429) {
    console.error('Rate limited - retry after delay');
    return;
  }

  throw error;
}
```

---

## Java Error Handling

### Standard Exception Handling

```java
import com.sap.ai.sdk.orchestration.*;

var client = new OrchestrationClient();
var config = new OrchestrationModuleConfig()
    .withLlmConfig(OrchestrationAiModel.GPT_4O);

try {
    var prompt = new OrchestrationPrompt("Hello");
    var result = client.chatCompletion(prompt, config);
    System.out.println(result.getContent());
} catch (OrchestrationException e) {
    System.err.println("Orchestration error: " + e.getMessage());

    // Access HTTP details
    if (e.getCause() != null) {
        System.err.println("Caused by: " + e.getCause().getMessage());
    }
} catch (Exception e) {
    System.err.println("Unexpected error: " + e.getMessage());
    e.printStackTrace();
}
```

### Checking Response Status

```java
try {
    var result = client.chatCompletion(prompt, config);

    // Check for filter violations (output)
    try {
        String content = result.getContent();
    } catch (ContentFilterException e) {
        System.err.println("Output blocked by content filter");
    }
} catch (ContentFilterException e) {
    // Input was blocked
    System.err.println("Input blocked by content filter: " + e.getMessage());
}
```

---

## Common Errors

### Connection Errors

| Error | Cause | Solution |
|-------|-------|----------|
| `Could not find any matching service bindings for service identifier 'aicore'` | No AI Core binding | Bind service or set AICORE_SERVICE_KEY |
| `ECONNREFUSED` | AI Core unreachable | Check network, VPN, proxy settings |
| `ETIMEDOUT` | Request timeout | Increase timeout, check network |
| `ENOTFOUND` | DNS resolution failed | Verify URL in service key |

### Authentication Errors

| Error | Cause | Solution |
|-------|-------|----------|
| `401 Unauthorized` | Invalid credentials | Verify clientid/clientsecret |
| `401 - Token expired` | OAuth token expired | SDK should auto-refresh; check token URL |
| `403 Forbidden` | Insufficient permissions | Check service plan, roles |

### Deployment Errors

| Error | Cause | Solution |
|-------|-------|----------|
| `Orchestration deployment not found` | No deployment | Use 'default' group or deploy orchestration |
| `404 Not Found` | Invalid deployment ID | Verify deployment exists |
| `Model not available` | Model not deployed | Check available models in AI Launchpad |

### Request Errors

| Error | Cause | Solution |
|-------|-------|----------|
| `400 Bad Request` | Invalid request body | Check prompt format, parameters |
| `413 Payload Too Large` | Input too long | Reduce input size |
| `422 Unprocessable Entity` | Schema validation failed | Fix request structure |
| `429 Too Many Requests` | Rate limited | Implement backoff, reduce frequency |
| `500 Internal Server Error` | Server-side issue | Retry, contact SAP support |

### Token Limit Errors

```typescript
// Handle token limit errors
try {
  await client.chatCompletion({ placeholderValues });
} catch (error) {
  if (error.message?.includes('context_length_exceeded')) {
    console.error('Input too long - reduce context or use summarization');
  }
  if (error.message?.includes('max_tokens')) {
    console.error('Increase max_tokens parameter or expect truncated response');
  }
}
```

---

## Content Filter Errors

### Input Filter Violation (JavaScript)

```typescript
import { buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';

const client = new OrchestrationClient({
  promptTemplating: { model: { name: 'gpt-4o' } },
  filtering: {
    input: buildAzureContentSafetyFilter({ Hate: 'ALLOW_SAFE' })
  }
});

try {
  await client.chatCompletion({
    placeholderValues: { question: 'potentially harmful content' }
  });
} catch (error) {
  if (error.message?.includes('content filter') ||
      error.message?.includes('ContentFilterViolation')) {
    console.error('Input blocked by content filter');
    // Handle gracefully - request user to rephrase
  }
}
```

### Output Filter Violation (JavaScript)

```typescript
const response = await client.chatCompletion({ placeholderValues });

try {
  const content = response.getContent();
} catch (error) {
  if (error.message?.includes('content filter')) {
    console.error('Model output was blocked');
    // May need to adjust filter thresholds or prompt
  }
}
```

### Java Content Filter Handling

```java
try {
    var result = client.chatCompletion(prompt, config);
    var content = result.getContent(); // May throw if output filtered
} catch (ContentFilterException e) {
    System.err.println("Content filter violation");
    System.err.println("Category: " + e.getCategory());
    System.err.println("Severity: " + e.getSeverity());
}
```

### Filter Categories

| Category | Description |
|----------|-------------|
| Hate | Hateful or discriminatory content |
| SelfHarm | Self-harm related content |
| Sexual | Sexual content |
| Violence | Violent content |

### Filter Severity Levels

| Level | Threshold Constant |
|-------|-------------------|
| Safe only | `ALLOW_SAFE` |
| Safe + Low | `ALLOW_SAFE_LOW` |
| Safe + Low + Medium | `ALLOW_SAFE_LOW_MEDIUM` |
| All (no filtering) | `ALLOW_ALL` |

---

## Retry Strategies

### JavaScript - LangChain Retry

LangChain clients have built-in retry:

```typescript
import { OrchestrationClient } from '@sap-ai-sdk/langchain';

const client = new OrchestrationClient(config, {
  maxRetries: 3 // Default is 6
});
```

**Note**: Content filter errors do NOT trigger retry.

### JavaScript - Manual Retry

```typescript
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  baseDelay: number = 1000
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      // Don't retry client errors (4xx except 429)
      const status = (error as any).cause?.response?.status;
      if (status >= 400 && status < 500 && status !== 429) {
        throw error;
      }

      // Exponential backoff
      const delay = baseDelay * Math.pow(2, attempt);
      console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

// Usage
const response = await withRetry(() =>
  client.chatCompletion({ placeholderValues })
);
```

### Java - Resilience4j

```java
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;

var retryConfig = RetryConfig.custom()
    .maxAttempts(3)
    .waitDuration(Duration.ofSeconds(1))
    .exponentialBackoff(2, Duration.ofSeconds(10))
    .retryOnException(e ->
        !(e instanceof ContentFilterException) &&
        !(e instanceof IllegalArgumentException)
    )
    .build();

var retry = Retry.of("aicore", retryConfig);

var result = Retry.decorateSupplier(retry, () ->
    client.chatCompletion(prompt, config)
).get();
```

### Rate Limit Handling

```typescript
async function handleRateLimit<T>(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    const status = (error as any).cause?.response?.status;
    const retryAfter = (error as any).cause?.response?.headers?.['retry-after'];

    if (status === 429) {
      const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
      console.log(`Rate limited. Waiting ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
      return fn(); // Retry once after waiting
    }

    throw error;
  }
}
```

---

## Best Practices

1. **Always wrap SDK calls in try-catch** - Errors can occur at any layer
2. **Log error chains** - Use `error.cause` to get full context
3. **Handle content filters gracefully** - Don't expose raw filter errors to users
4. **Implement retry with backoff** - For transient errors (5xx, 429)
5. **Don't retry client errors** - 4xx errors (except 429) indicate request problems
6. **Set appropriate timeouts** - Prevent hanging requests
7. **Monitor error rates** - Track patterns to identify systemic issues

---

## Documentation Links

- JS Error Handling: [https://github.com/SAP/ai-sdk/blob/main/docs-js/error-handling.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/error-handling.mdx)
- JS FAQ: [https://github.com/SAP/ai-sdk/blob/main/docs-js/faq.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-js/faq.mdx)
- Java FAQ: [https://github.com/SAP/ai-sdk/blob/main/docs-java/faq.mdx](https://github.com/SAP/ai-sdk/blob/main/docs-java/faq.mdx)

```

sap-cloud-sdk-ai | SkillHub