Skip to content

Latest commit

 

History

History
559 lines (446 loc) · 15.3 KB

File metadata and controls

559 lines (446 loc) · 15.3 KB

SAP AI Core API - Manual curl Testing Guide

This guide shows how to make direct API calls to SAP AI Core using curl for testing and debugging. For production code, use the SAP AI SDK with AICORE_SERVICE_KEY.


Table of Contents

Overview

Complete OAuth2 authentication → API call → Tool calling flow.


Prerequisites

  • SAP AI Core instance + service key (from BTP cockpit) - see Environment Setup for credential configuration
  • curl and base64 utilities

Step-by-Step Guide

Step 1: Prepare Credentials

Service key contains: clientid, clientsecret, url (auth server), serviceurls.AI_API_URL

⚠️ Important: Never commit credentials. Use environment variables.

Extracting Values from AICORE_SERVICE_KEY

Your AICORE_SERVICE_KEY environment variable contains a JSON object. Here's how to extract the required values:

Example AICORE_SERVICE_KEY structure:

{
  "clientid": "sb-abc123!a12345|aicore!b123",
  "clientsecret": "AbCdEf123456+ghIjKl==",
  "url": "https://mysubaccount.authentication.eu10.hana.ondemand.com",
  "serviceurls": {
    "AI_API_URL": "https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com"
  }
}

Extract values using jq (recommended):

# Parse AICORE_SERVICE_KEY and extract individual values
CLIENT_ID=$(echo "$AICORE_SERVICE_KEY" | jq -r '.clientid')
CLIENT_SECRET=$(echo "$AICORE_SERVICE_KEY" | jq -r '.clientsecret')
AUTH_URL=$(echo "$AICORE_SERVICE_KEY" | jq -r '.url')
AI_API_URL=$(echo "$AICORE_SERVICE_KEY" | jq -r '.serviceurls.AI_API_URL')

# Verify extraction succeeded
echo "Client ID: ${CLIENT_ID:0:20}..."  # Show first 20 chars only
echo "Auth URL: $AUTH_URL"
echo "API URL: $AI_API_URL"

Alternative: Extract using grep/sed (no jq required):

# Extract values without jq (less robust but works in minimal environments)
CLIENT_ID=$(echo "$AICORE_SERVICE_KEY" | grep -o '"clientid":"[^"]*' | cut -d'"' -f4)
CLIENT_SECRET=$(echo "$AICORE_SERVICE_KEY" | grep -o '"clientsecret":"[^"]*' | cut -d'"' -f4)
AUTH_URL=$(echo "$AICORE_SERVICE_KEY" | grep -o '"url":"[^"]*' | cut -d'"' -f4)
AI_API_URL=$(echo "$AICORE_SERVICE_KEY" | grep -o '"AI_API_URL":"[^"]*' | cut -d'"' -f4)

Tip: The jq approach is more reliable, especially when credentials contain special characters like +, =, or |. Install jq via apt install jq, brew install jq, or your package manager.

Step 2: Get OAuth Token

#!/bin/bash

# Your credentials (replace with actual values)
CLIENT_ID="your-client-id-here"
CLIENT_SECRET="your-client-secret-here"
AUTH_URL="https://your-subdomain.authentication.region.hana.ondemand.com"

# Encode credentials to Base64
CREDENTIALS=$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64 | tr -d '\n')

# Request OAuth token
TOKEN_RESPONSE=$(curl -s --request POST \
  --url "${AUTH_URL}/oauth/token" \
  --header "Authorization: Basic ${CREDENTIALS}" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=client_credentials")

# Extract access token from JSON response
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')

# Verify token was obtained
if [ -z "$ACCESS_TOKEN" ]; then
  echo "❌ Failed to get OAuth token"
  echo "Response: $TOKEN_RESPONSE"
  exit 1
fi

echo "✅ OAuth token obtained"

Key Points: Tokens expire after 12h.

Step 3: Call SAP AI Core API

Endpoint: https://{AI_API_URL}/v2/inference/deployments/{DEPLOYMENT_ID}/v2/completion

Note: The /v2 appears twice (base path + completion endpoint).

# Configuration
AI_API_URL="https://api.ai.prod.region.aws.ml.hana.ondemand.com"
DEPLOYMENT_ID="your-deployment-id"
RESOURCE_GROUP="default"

# Build endpoint URL
API_ENDPOINT="${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/v2/completion"

# Make API call
curl --request POST \
  --url "${API_ENDPOINT}" \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header "AI-Resource-Group: ${RESOURCE_GROUP}" \
  --header "Content-Type: application/json" \
  --data '{
  "config": {
    "modules": {
      "prompt_templating": {
        "prompt": {
          "template": [
            {
              "role": "system",
              "content": "You are a helpful assistant."
            },
            {
              "role": "user",
              "content": "What is 2+2?"
            }
          ]
        },
        "model": {
          "name": "gpt-4.1",
          "version": "latest"
        }
      }
    }
  }
}'

Request Body Structure (Orchestration v2)

Basic Structure

{
  "config": {
    "modules": {
      "prompt_templating": {
        "prompt": {
          /* Prompt configuration */
        },
        "model": {
          /* Model configuration */
        }
      }
    }
  }
}

Request Structure

The SAP AI Core v2 API uses a modular configuration structure with prompt (messages, tools, response_format) and model (name, version, params) sections. See complete working examples below for the full structure.

💡 For detailed parameter documentation, see API Reference


Tool Calling Example

For complete tool calling documentation including all models, parallel execution, error handling, and best practices, see API Reference - Tool Calling.

⚠️ Model-Specific Limitations

For complete model capabilities and tool calling support, see API Reference - Model-Specific Tool Limitations.


Complete Working Example

#!/bin/bash

# ============================================
# Configuration (REPLACE WITH YOUR VALUES)
# ============================================

CLIENT_ID="your-client-id"
CLIENT_SECRET="your-client-secret"
AUTH_URL="https://your-auth-url.authentication.region.hana.ondemand.com"
AI_API_URL="https://api.ai.prod.region.aws.ml.hana.ondemand.com"
DEPLOYMENT_ID="your-deployment-id"
RESOURCE_GROUP="default"

# ============================================
# Get OAuth Token
# ============================================

echo "🔐 Getting OAuth token..."

CREDENTIALS=$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)

TOKEN_RESPONSE=$(curl -s --request POST \
  --url "${AUTH_URL}/oauth/token" \
  --header "Authorization: Basic ${CREDENTIALS}" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=client_credentials")

ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)

if [ -z "$ACCESS_TOKEN" ]; then
  echo "❌ Failed to get OAuth token"
  exit 1
fi

echo "✅ OAuth token obtained"

# ============================================
# Call SAP AI Core API
# ============================================

echo "🚀 Calling SAP AI Core..."

API_ENDPOINT="${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/v2/completion"

curl --request POST \
  --url "${API_ENDPOINT}" \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header "AI-Resource-Group: ${RESOURCE_GROUP}" \
  --header "Content-Type: application/json" \
  --data '{
  "config": {
    "modules": {
      "prompt_templating": {
        "prompt": {
          "template": [
            {
              "role": "system",
              "content": "You are a helpful assistant."
            },
            {
              "role": "user",
              "content": "Calculate the total price: 100 per unit, 10 units."
            }
          ],
          "tools": [
            {
              "type": "function",
              "function": {
                "name": "calculate_total_price",
                "description": "Calculate total price",
                "parameters": {
                  "type": "object",
                  "properties": {
                    "price_per_unit": {
                      "type": "number",
                      "description": "Price per unit"
                    },
                    "quantity": {
                      "type": "number",
                      "description": "Number of units"
                    }
                  },
                  "required": ["price_per_unit", "quantity"],
                  "additionalProperties": false
                }
              }
            }
          ]
        },
        "model": {
          "name": "gpt-4.1",
          "version": "latest"
        }
      }
    }
  }
}'

echo ""
echo "✅ Request completed"

Response Format

Success Response (HTTP 200)

{
  "request_id": "uuid",
  "final_result": {
    "id": "chatcmpl-xxx",
    "object": "chat.completion",
    "created": 1234567890,
    "model": "gpt-4.1-2024-08-06",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "The total price is $1000",
          "tool_calls": [
            {
              "id": "call_xxx",
              "type": "function",
              "function": {
                "name": "calculate_total_price",
                "arguments": "{\"price_per_unit\": 100, \"quantity\": 10}"
              }
            }
          ]
        },
        "finish_reason": "tool_calls"
      }
    ],
    "usage": {
      "completion_tokens": 59,
      "prompt_tokens": 129,
      "total_tokens": 188
    }
  }
}

Error Response (HTTP 400)

{
  "error": {
    "request_id": "uuid",
    "code": 400,
    "message": "Error description",
    "location": "Module name"
  }
}

Common Issues

Error Cause Solution
Missing Tenant Id Expired token, wrong endpoint, missing AI-Resource-Group header, misconfigured service key Regenerate token, verify /v2 paths, ensure AI-Resource-Group header is set, check service key contains tenant information
Bad Credentials Wrong client ID/secret, bad Base64 encoding Check credentials, verify Base64 output has no newlines
Deployment Not Found Wrong deployment ID, wrong region, wrong resource group Verify deployment exists, check resource group
Multiple Tools Error Gemini model with >1 tool Use 1 tool OR switch to OpenAI/Claude models

Debugging Tips

Verbose output:

curl --verbose --fail-with-body --show-error ...

Decode JWT token:

echo "$ACCESS_TOKEN" | cut -d. -f2 | base64 -d | jq .

Check: exp (expiration), subaccountid, scope

Minimal test request:

{
  "config": {
    "modules": {
      "prompt_templating": {
        "prompt": {
          "template": [{ "role": "user", "content": "Hello" }]
        },
        "model": { "name": "gpt-4.1", "version": "latest" }
      }
    }
  }
}

Foundation Models API

The Foundation Models API provides direct model access with additional parameters like logprobs, seed, and logit_bias. Use a different endpoint path.

Endpoint

${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/chat/completions

Note: Replace completion (Orchestration) with chat/completions (Foundation Models).

Basic Request

curl --request POST \
  --url "${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/chat/completions" \
  --header "Authorization: Bearer ${ACCESS_TOKEN}" \
  --header "AI-Resource-Group: ${RESOURCE_GROUP}" \
  --header "Content-Type: application/json" \
  --data '{
  "model": "gpt-4.1",
  "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"}
  ],
  "temperature": 0.7,
  "max_tokens": 100
}'

Foundation Models-Specific Parameters

{
  "model": "gpt-4.1",
  "messages": [...],
  "logprobs": true,
  "top_logprobs": 5,
  "seed": 42,
  "logit_bias": {"50256": -100},
  "user": "user-123"
}

Response Format

{
  "id": "chatcmpl-xxx",
  "object": "chat.completion",
  "model": "gpt-4.1",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?"
      },
      "finish_reason": "stop",
      "logprobs": {
        "content": [
          {"token": "Hello", "logprob": -0.5, "top_logprobs": [...]}
        ]
      }
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 10,
    "total_tokens": 30
  }
}

Note: The Foundation Models API uses standard OpenAI-compatible format, while the Orchestration API uses SAP's orchestration envelope format.


Security Best Practices

See Environment Setup - Security Best Practices for detailed guidance on credential management, key rotation, and secure deployment.


Additional Resources


TypeScript Examples

See examples/ directory: example-generate-text.ts, example-streaming-chat.ts, example-chat-completion-tool.ts, example-image-recognition.ts, example-data-masking.ts. More in README.


For production, use the TypeScript provider package for better error handling and type safety.


Related Documentation