Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Improve error handling by using ErrorWithCause #581

Merged
merged 32 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9cc6014
feat: use ErrorWithCause for getAllDeployments
ZhongpinWang Mar 4, 2025
21b4697
feat: improve error handling in orchestration
ZhongpinWang Mar 4, 2025
6201330
chore: clean up error message
ZhongpinWang Mar 4, 2025
d489426
refactor: throw ErrorWithCause at the root executeRequest
ZhongpinWang Mar 4, 2025
3308d2a
refactor: improve error handling
ZhongpinWang Mar 4, 2025
2cab53d
fix: replace system with user
ZhongpinWang Mar 4, 2025
c22669b
chore: improve sample code
ZhongpinWang Mar 4, 2025
1d4f0d4
fix: lock
ZhongpinWang Mar 4, 2025
26634ec
fix: lint
ZhongpinWang Mar 4, 2025
a492d7c
chore: remove unnecessary error with cause test
ZhongpinWang Mar 4, 2025
f3675c2
fix: import
ZhongpinWang Mar 4, 2025
5d98755
refactor
ZhongpinWang Mar 4, 2025
ad98648
chore: add doc for using ErrorWithCause
ZhongpinWang Mar 4, 2025
14a475c
improve doc
ZhongpinWang Mar 4, 2025
7dfb13e
Merge branch 'main' into improve-error-handling-by-using-error-with-c…
ZhongpinWang Mar 4, 2025
97ef240
fix: lint
ZhongpinWang Mar 4, 2025
b569fed
chore: add changeset
ZhongpinWang Mar 4, 2025
8e991c7
changeset
ZhongpinWang Mar 4, 2025
a57567b
fix: test
ZhongpinWang Mar 4, 2025
24ea6cb
fix: Changes from lint
Mar 4, 2025
87a05c4
changeset
ZhongpinWang Mar 4, 2025
07754a9
fix: changeset
ZhongpinWang Mar 4, 2025
3bed91e
reorder local testing and error handling
ZhongpinWang Mar 5, 2025
c20b4e8
reorder
ZhongpinWang Mar 5, 2025
763d38a
improve error handling doc
ZhongpinWang Mar 5, 2025
01ed7b1
chore: change changeset to Fixed Issue
ZhongpinWang Mar 5, 2025
8fbb1c3
doc: improve orchestration content filtering error handling
ZhongpinWang Mar 5, 2025
c901e8c
changeset
ZhongpinWang Mar 5, 2025
d80c7f1
Merge branch 'main' into improve-error-handling-by-using-error-with-c…
ZhongpinWang Mar 5, 2025
022cb05
Merge branch 'main' into improve-error-handling-by-using-error-with-c…
ZhongpinWang Mar 6, 2025
5b623e7
Merge branch 'main' into improve-error-handling-by-using-error-with-c…
ZhongpinWang Mar 6, 2025
845f448
doc: improve error handling doc
ZhongpinWang Mar 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/itchy-months-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@sap-ai-sdk/ai-api': patch
'@sap-ai-sdk/core': patch
---

[Fixed Issue] Add missing cause in the error object for failing HTTP requests by using `ErrorWithCause`, providing more context for debugging.

[Compatibility Note] Due to the introduction of `ErrorWithCause`, `AxiosError` is now wrapped inside the `cause` property.
For example, use `error.cause.response.data` instead of `error.response.data` to access the error response from the server.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Setup your SAP AI Core instance with SAP Cloud SDK for AI.
- [@sap-ai-sdk/langchain](#sap-ai-sdklangchain)
- [@sap-ai-sdk/orchestration](#sap-ai-sdkorchestration)
- [SAP Cloud SDK for AI Sample Project](#sap-cloud-sdk-for-ai-sample-project)
- [Error Handling](#error-handling)
- [Accessing Error Information](#accessing-error-information)
- [Local Testing](#local-testing)
- [Support, Feedback, Contribution](#support-feedback-contribution)
- [Security / Disclosure](#security--disclosure)
Expand Down Expand Up @@ -93,6 +95,42 @@ For details on orchestration client, refer to this [document](https://github.com
We have created a sample project demonstrating the different clients' usage of the SAP Cloud SDK for AI for TypeScript/JavaScript.
The [project README](https://github.com/SAP/ai-sdk-js/blob/main/sample-code/README.md) outlines the set-up needed to build and run it locally.

## Error Handling

A common error scenario is `Request failed with status code STATUS_CODE` coming from `AxiosError`.
In this case, SAP Cloud SDK for AI uses [`ErrorWithCause`](https://sap.github.io/cloud-sdk/docs/js/features/error-handling) to provide more detailed error information.

### Accessing Error Information

For example, for the following nested `ErrorWithCause`

```ts
const rootCause = new Error('The root cause is a bug!');
const lowerLevelErrorWithCause = new ErrorWithCause('Failed to call function foo().', rootCause);
const upperLevelErrorWithCause = new ErrorWithCause('Process crashed.', lowerLevelErrorWithCause);
throw upperLevelErrorWithCause;
```

The error stack will look like this:

```txt
ErrorWithCause: Process crashed.
at ...
Caused by:
ErrorWithCause: Failed to call function foo().
at ...
Caused by:
Error: The root cause is a bug!
at ...
```

- `error.stack` will contain the above stack trace.
- `error.message` will be `Process crashed.`.
- `error.cause.message` will be `Failed to call function foo().`.
- `error.rootCause.message` will be `The root cause is a bug!`.

In case of `AxiosError`, the response data will be part of the error stack and can be accessed via `error.cause.response.data`.

## Local Testing

To test SAP Cloud SDK for AI features locally during application development, follow these steps:
Expand Down
84 changes: 29 additions & 55 deletions packages/ai-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ We maintain a list of [currently available and tested AI Core APIs](https://gith
- [Create a Deployment](#create-a-deployment)
- [Delete a Deployment](#delete-a-deployment)
- [Custom Destination](#custom-destination)
- [Error Handling](#error-handling)
- [Local Testing](#local-testing)
- [Support, Feedback, Contribution](#support-feedback-contribution)
- [License](#license)
Expand Down Expand Up @@ -73,17 +74,11 @@ async function createArtifact() {
scenarioId: 'foundation-models'
};

try {
const responseData: ArtifactCreationResponse =
await ArtifactApi.artifactCreate(requestBody, {
'AI-Resource-Group': 'default'
}).execute();
return responseData;
} catch (errorData) {
const apiError = errorData.response.data.error as ApiError;
console.error('Status code:', errorData.response.status);
throw new Error(`Artifact creation failed: ${apiError.message}`);
}
const responseData: ArtifactCreationResponse =
await ArtifactApi.artifactCreate(requestBody, {
'AI-Resource-Group': 'default'
}).execute();
return responseData;
}
```

Expand All @@ -108,39 +103,26 @@ async function createConfiguration() {
inputArtifactBindings: []
};

try {
const responseData: ConfigurationCreationResponse =
await ConfigurationApi.configurationCreate(requestBody, {
'AI-Resource-Group': 'default'
}).execute();
return responseData;
} catch (errorData) {
const apiError = errorData.response.data.error as ApiError;
console.error('Status code:', errorData.response.status);
throw new Error(`Configuration creation failed: ${apiError.message}`);
}
const responseData: ConfigurationCreationResponse =
await ConfigurationApi.configurationCreate(requestBody, {
'AI-Resource-Group': 'default'
}).execute();
return responseData;
}
```

### Create a Deployment

```TypeScript
```ts
async function createDeployment() {

const requestBody: DeploymentCreationRequest = {
configurationId: '0a1b2c3d-4e5f6g7h'
};

try{
const responseData: DeploymentCreationResponse = await DeploymentApi
.deploymentCreate(requestBody, {'AI-Resource-Group': 'default'})
.execute();
return responseData;
} catch (errorData) {
const apiError = errorData.response.data.error as ApiError;
console.error('Status code:', errorData.response.status);
throw new Error(`Deployment creation failed: ${apiError.message}`);
}
const requestBody: DeploymentCreationRequest = {
configurationId: '0a1b2c3d-4e5f6g7h'
};
const responseData: DeploymentCreationResponse =
await DeploymentApi.deploymentCreate(requestBody, {
'AI-Resource-Group': 'default'
}).execute();
return responseData;
}
```

Expand All @@ -166,26 +148,14 @@ async function modifyDeployment() {
targetStatus: 'STOPPED'
};

try {
await DeploymentApi.deploymentModify(deploymentId, requestBody, {
'AI-Resource-Group': 'default'
}).execute();
} catch (errorData) {
const apiError = errorData.response.data.error as ApiError;
console.error('Status code:', errorData.response.status);
throw new Error(`Deployment modification failed: ${apiError.message}`);
}
}
// Wait a few seconds for the deployment to stop
try {
return DeploymentApi.deploymentDelete(deploymentId, {
await DeploymentApi.deploymentModify(deploymentId, requestBody, {
'AI-Resource-Group': 'default'
}).execute();
} catch (errorData) {
const apiError = errorData.response.data.error as ApiError;
console.error('Status code:', errorData.response.status);
throw new Error(`Deployment deletion failed: ${apiError.message}`);
}
// Wait a few seconds for the deployment to stop
return DeploymentApi.deploymentDelete(deploymentId, {
'AI-Resource-Group': 'default'
}).execute();
}
```

Expand All @@ -206,6 +176,10 @@ return DeploymentApi.deploymentQuery(queryParams, {
By default, the fetched destination is cached.
To disable caching, set the `useCache` parameter to `false` together with the `destinationName` parameter.

## Error Handling

For error handling instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#error-handling).

## Local Testing

For local testing instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#local-testing).
Expand Down
3 changes: 2 additions & 1 deletion packages/ai-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
"dependencies": {
"@sap-ai-sdk/core": "workspace:^",
"@sap-cloud-sdk/connectivity": "^4.0.1"
"@sap-cloud-sdk/connectivity": "^4.0.1",
"@sap-cloud-sdk/util": "^4.0.1"
}
}
45 changes: 43 additions & 2 deletions packages/ai-api/src/utils/deployment-resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import {
aiCoreDestination,
mockDeploymentsList
} from '../../../../test-util/mock-http.js';
import { type AiDeployment } from '../client/AI_CORE_API';
import { resolveDeploymentId } from './deployment-resolver.js';
import { type AiDeployment } from '../client/AI_CORE_API/index.js';
import {
getAllDeployments,
resolveDeploymentId
} from './deployment-resolver.js';
import { deploymentCache } from './deployment-cache.js';

describe('deployment resolver', () => {
Expand Down Expand Up @@ -114,6 +117,44 @@ describe('deployment resolver', () => {

expect(id).toEqual('5');
});

describe('get all deployments', () => {
it('should return all deployments', async () => {
mockResponse();
const expected = [
{
id: '1',
details: {
resources: {
backendDetails: {
model: {
name: 'gpt-4o',
version: 'latest'
}
}
}
}
},
{
id: '2',
details: {
resources: {
backendDetails: {
model: {
name: 'gpt-4o',
version: '0613'
}
}
}
}
}
];
const deployments = await getAllDeployments({
scenarioId: 'foundation-models'
});
expect(deployments).toStrictEqual(expected);
});
});
});

function mockResponse() {
Expand Down
13 changes: 10 additions & 3 deletions packages/ai-api/src/utils/deployment-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ErrorWithCause } from '@sap-cloud-sdk/util';
import {
type AiDeployment,
DeploymentApi
Expand Down Expand Up @@ -138,7 +139,13 @@ export async function resolveDeploymentId(
return deployments[0].id;
}

async function getAllDeployments(
/**
* Get all deployments that match the given criteria.
* @param opts - The options for the deployment resolution.
* @returns A promise of an array of deployments.
* @internal
*/
export async function getAllDeployments(
opts: DeploymentResolutionOptions
): Promise<AiDeployment[]> {
const {
Expand All @@ -160,8 +167,8 @@ async function getAllDeployments(
deploymentCache.setAll(opts, resources);

return resources;
} catch (error) {
throw new Error('Failed to fetch the list of deployments: ' + error);
} catch (error: any) {
throw new ErrorWithCause('Failed to fetch the list of deployments.', error);
}
}

Expand Down
23 changes: 16 additions & 7 deletions packages/core/src/http-client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ErrorWithCause,
mergeIgnoreCase,
removeLeadingSlashes,
removeTrailingSlashes
Expand Down Expand Up @@ -66,13 +67,21 @@ export async function executeRequest(
data: JSON.stringify(data)
};

return executeHttpRequest(
{ ...aiCoreDestination, url: getTargetUrl(aiCoreDestination.url, url) },
mergedRequestConfig,
{
fetchCsrfToken: false
}
);
try {
const response = await executeHttpRequest(
{ ...aiCoreDestination, url: getTargetUrl(aiCoreDestination.url, url) },
mergedRequestConfig,
{
fetchCsrfToken: false
}
);
return response;
} catch (error: any) {
throw new ErrorWithCause(
`Request failed with status code ${error.status}.`,
error
);
}
}

function mergeWithDefaultRequestConfig(
Expand Down
5 changes: 5 additions & 0 deletions packages/document-grounding/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This package incorporates generative AI document grounding capabilities into you
- [Create a Collection](#create-a-collection)
- [Create a Document](#create-a-document)
- [Custom Destination](#custom-destination)
- [Error Handling](#error-handling)
- [Local Testing](#local-testing)
- [Support, Feedback, Contribution](#support-feedback-contribution)
- [License](#license)
Expand Down Expand Up @@ -106,6 +107,10 @@ const response = await VectorApi.deleteCollectionById(collectionId, {
});
```

## Error Handling

For error handling instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#error-handling).

## Local Testing

For local testing instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#local-testing).
Expand Down
6 changes: 6 additions & 0 deletions packages/foundation-models/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ This package incorporates generative AI foundation models into your AI activitie
- [Azure OpenAI Embedding Client](#azure-openai-embedding-client)
- [Custom Request Configuration](#custom-request-configuration)
- [Custom Destination](#custom-destination)
- [Overwriting API Version](#overwriting-api-version)
- [Error Handling](#error-handling)
- [Local Testing](#local-testing)
- [Support, Feedback, Contribution](#support-feedback-contribution)
- [License](#license)
Expand Down Expand Up @@ -293,6 +295,10 @@ client.run(
);
```

## Error Handling

For error handling instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#error-handling).

## Local Testing

For local testing instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#local-testing).
Expand Down
5 changes: 5 additions & 0 deletions packages/langchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This package provides LangChain clients built on top of the foundation model and
- [Usage](#usage)
- [Orchestration Client](#orchestration-client)
- [Azure OpenAI Client](#azure-openai-client)
- [Error Handling](#error-handling)
- [Local Testing](#local-testing)
- [Support, Feedback, Contribution](#support-feedback-contribution)
- [License](#license)
Expand Down Expand Up @@ -52,6 +53,10 @@ For more information about the Orchestration client, refer to the [documentation

For more information about Azure OpenAI client, refer to the [documentation](https://github.com/SAP/ai-sdk-js/tree/main/packages/langchain/src/openai/README.md).

## Error Handling

For error handling instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#error-handling).

## Local Testing

For local testing instructions, refer to this [section](https://github.com/SAP/ai-sdk-js/blob/main/README.md#local-testing).
Expand Down
4 changes: 2 additions & 2 deletions packages/langchain/src/orchestration/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ describe('orchestration service client', () => {
maxRetries: 1
});

await expect(client.invoke([])).rejects.toThrowErrorMatchingInlineSnapshot(
'"Request failed with status code 500"'
await expect(client.invoke([])).rejects.toThrow(
'Request failed with status code 500'
);
});

Expand Down
Loading
Loading