Skip to content

refactor: Content filter helper function #441

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

Merged
merged 57 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
e21ec35
refactor: implemement ContentFilters.azure() and deprecate buildAzure…
ZhongpinWang Jan 10, 2025
6f31078
fix: Changes from lint
Jan 10, 2025
1b19566
chore: changeset
ZhongpinWang Jan 10, 2025
6d3b410
fix: vale
ZhongpinWang Jan 10, 2025
c9008cf
chore: update README
ZhongpinWang Jan 10, 2025
1397cc1
Merge branch 'main' into refactor-content-filters
ZhongpinWang Jan 20, 2025
80fbf99
review
ZhongpinWang Jan 20, 2025
b17f12b
fix: Changes from lint
Jan 20, 2025
4ee2f12
Merge branch 'main' into refactor-content-filters
ZhongpinWang Jan 20, 2025
42bd8b5
refactor: rename azure into azureContentSafety
ZhongpinWang Jan 20, 2025
23cd5aa
fix: Changes from lint
Jan 20, 2025
6e20a48
Merge branch 'main' into refactor-content-filters
KavithaSiva Jan 21, 2025
a5fee4b
Merge branch 'main' into refactor-content-filters
ZhongpinWang Jan 23, 2025
07ae577
chore: update changeset
ZhongpinWang Jan 23, 2025
b81fd07
refactor: rename azureContentSafety into buildAzureContentSafety
ZhongpinWang Jan 23, 2025
4dca1b9
fix: Changes from lint
Jan 23, 2025
eee41e0
fix: test
ZhongpinWang Jan 23, 2025
d58970e
fix: Changes from lint
Jan 23, 2025
77af75e
chore: revert behaviour of buildAzureContentFilter
KavithaSiva Jan 24, 2025
220f395
Merge branch 'main' into refactor-content-filters
KavithaSiva Jan 30, 2025
37dc504
Merge branch 'main' into refactor-content-filters
KavithaSiva Jan 31, 2025
8a4abb3
chore: changes from discussion
KavithaSiva Jan 31, 2025
58e4a2d
chore: after discussion updates
KavithaSiva Jan 31, 2025
e86fb1f
chore: fix test
KavithaSiva Jan 31, 2025
ce1ac5a
chore: adjust documentation
KavithaSiva Jan 31, 2025
8190455
Merge branch 'main' into refactor-content-filters
KavithaSiva Feb 3, 2025
cda132b
chore: address review comment
KavithaSiva Feb 3, 2025
5960570
chore: correct deprecation message
KavithaSiva Feb 3, 2025
2908711
feat: Add descriptive constant for Azure content filter configuration…
KavithaSiva Feb 3, 2025
a596608
chore: address review comment
KavithaSiva Feb 3, 2025
e26ced8
Update packages/orchestration/README.md
KavithaSiva Feb 3, 2025
fc6ee9b
chore: update release notes
KavithaSiva Feb 3, 2025
4e394db
Merge branch 'refactor-content-filters' of github.com:SAP/ai-sdk-js i…
KavithaSiva Feb 3, 2025
32c2526
Update .changeset/afraid-cooks-shave.md
KavithaSiva Feb 3, 2025
eaac8d3
Merge branch 'main' into refactor-content-filters
KavithaSiva Feb 3, 2025
ab358c2
chore: minor release note update
KavithaSiva Feb 3, 2025
3a4abcf
Merge branch 'refactor-content-filters' of github.com:SAP/ai-sdk-js i…
KavithaSiva Feb 3, 2025
67b876e
chore: adjust TSDoc
KavithaSiva Feb 3, 2025
3b842cc
chore: update release notes
KavithaSiva Feb 3, 2025
29ff38b
chore: remove test file from tsconfig.json
KavithaSiva Feb 3, 2025
215f754
chore: rename request config
KavithaSiva Feb 3, 2025
bb3b537
chore: fix typo
KavithaSiva Feb 3, 2025
00970f9
Update .changeset/afraid-cooks-shave.md
KavithaSiva Feb 3, 2025
8a51d06
Update packages/orchestration/README.md
KavithaSiva Feb 3, 2025
7caeeb4
Update packages/orchestration/README.md
KavithaSiva Feb 3, 2025
202dddd
fix: Changes from lint
Feb 3, 2025
ac6b143
chore: update TSDoc
KavithaSiva Feb 3, 2025
6d46b4c
Merge branch 'refactor-content-filters' of github.com:SAP/ai-sdk-js i…
KavithaSiva Feb 3, 2025
3ee5f95
Update packages/orchestration/src/util/filtering.ts
KavithaSiva Feb 3, 2025
6d5d65f
Update sample-code/src/orchestration.ts
KavithaSiva Feb 3, 2025
4a0792e
chore: review comment
KavithaSiva Feb 3, 2025
5d439e8
chore: add default value
KavithaSiva Feb 3, 2025
0dfe5ff
Update packages/orchestration/src/orchestration-types.ts
KavithaSiva Feb 3, 2025
56e49e8
chore: rename request-config
KavithaSiva Feb 3, 2025
1b11568
Merge branch 'refactor-content-filters' of github.com:SAP/ai-sdk-js i…
KavithaSiva Feb 3, 2025
8b0913c
chore: rename to module-config
KavithaSiva Feb 3, 2025
fabb56d
Merge branch 'main' into refactor-content-filters
KavithaSiva Feb 3, 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
5 changes: 5 additions & 0 deletions .changeset/afraid-cooks-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sap-ai-sdk/orchestration': minor
---

[Compatibility Note] Deprecate `buildAzureContentFilter()` since it restricts filtering to have only one filter, use `ContentFilters.azure()` instead.
53 changes: 30 additions & 23 deletions packages/orchestration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,24 +244,35 @@

This feature allows filtering both the [input](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/consume-orchestration#content-filtering-on-input) and [output](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/consume-orchestration#content-filtering-on-input) of a model based on content safety criteria.

```ts
import {
OrchestrationClient,
buildAzureContentFilter
} from '@sap-ai-sdk/orchestration';
#### Azure Content Filter

Use `ContentFilters.azure()` to build an Azure content filter.
The Azure content filter supports four categories: `Hate`, `Violence`, `Sexual`, and `SelfHarm`.
Each category can be configured with severity levels of 0, 2, 4, or 6.

Here is a complete example of using an Azure content filter for both input and output:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[q] Is this line even needed? I feel like we're way to verbose sometimes when things are self-explanatory. We can just say (in the line above):

Use buildAzureContentSafetyFilter() function to build an Azure content filter for both input and output.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened the description, but I feel it would be nice to still leave the mapping table in there as previously we had a function which used numbers instead.


const filter = buildAzureContentFilter({ Hate: 2, Violence: 4 });
```ts
import { OrchestrationClient, ContentFilters } from '@sap-ai-sdk/orchestration';
const llm = {
model_name: 'gpt-4o',
model_params: { max_tokens: 50, temperature: 0.1 }
};
const templating = {
template: [{ role: 'user', content: '{{?input}}' }]
};

const filter = ContentFilters.azure({ Hate: 2, Violence: 4 });
const orchestrationClient = new OrchestrationClient({
llm: {
model_name: 'gpt-4o',
model_params: { max_tokens: 50, temperature: 0.1 }
},
templating: {
template: [{ role: 'user', content: '{{?input}}' }]
},
llm,
templating,
filtering: {
input: filter,
output: filter
input: {
filters: [filter]
},
output: {
filters: [filter]
}
}
});

Expand All @@ -275,23 +286,19 @@
}
```

#### Error Handling

Both `chatCompletion()` and `getContent()` methods can throw errors.

- **axios errors**:
- **Axios Errors**:

Check warning on line 293 in packages/orchestration/README.md

View workflow job for this annotation

GitHub Actions / grammar-check

[vale] reported by reviewdog 🐶 [Vale.Terms] Use 'axios' instead of 'Axios'. Raw Output: {"message": "[Vale.Terms] Use 'axios' instead of 'Axios'.", "location": {"path": "packages/orchestration/README.md", "range": {"start": {"line": 293, "column": 5}}}, "severity": "WARNING"}
When the chat completion request fails with a `400` status code, the caught error will be an `Axios` error.
The property `error.response.data.message` may provide additional details about the failure's cause.

- **output content filtered**:
- **Output Content Filtered**:
The method `getContent()` can throw an error if the output filter filters the model output.
This can occur even if the chat completion request responds with a `200` HTTP status code.
The `error.message` property indicates if the output was filtered.

Therefore, handle errors appropriately to ensure meaningful feedback for both types of errors.

`buildAzureContentFilter()` is a convenience function that creates an Azure content filter configuration based on the provided inputs.
The Azure content filter supports four categories: `Hate`, `Violence`, `Sexual`, and `SelfHarm`.
Each category can be configured with severity levels of 0, 2, 4, or 6.

### Data Masking

You can anonymize or pseudonomize the prompt using the data masking capabilities of the orchestration service.
Expand Down
5 changes: 4 additions & 1 deletion packages/orchestration/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ export type {

export { OrchestrationClient } from './orchestration-client.js';

export { buildAzureContentFilter } from './orchestration-utils.js';
export {
buildAzureContentFilter,
ContentFilters
} from './orchestration-filtering.js';

export { OrchestrationResponse } from './orchestration-response.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/orchestration/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './orchestration-client.js';
export * from './orchestration-utils.js';
export * from './orchestration-filtering.js';
export * from './orchestration-types.js';
export * from './orchestration-response.js';
10 changes: 7 additions & 3 deletions packages/orchestration/src/orchestration-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
constructCompletionPostRequest,
OrchestrationClient
} from './orchestration-client.js';
import { buildAzureContentFilter } from './orchestration-utils.js';
import { ContentFilters } from './orchestration-filtering.js';
import { OrchestrationResponse } from './orchestration-response.js';
import type { CompletionPostResponse } from './client/api/schema/index.js';
import type {
Expand Down Expand Up @@ -120,8 +120,12 @@ describe('orchestration service client', () => {
]
},
filtering: {
input: buildAzureContentFilter({ Hate: 4, SelfHarm: 2 }),
output: buildAzureContentFilter({ Sexual: 0, Violence: 4 })
input: {
filters: [ContentFilters.azure({ Hate: 4, SelfHarm: 2 })]
},
output: {
filters: [ContentFilters.azure({ Sexual: 0, Violence: 4 })]
}
}
};
const prompt = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { constructCompletionPostRequest } from './orchestration-client.js';
import { buildAzureContentFilter } from './orchestration-utils.js';
import { ContentFilters } from './orchestration-filtering.js';
import type { CompletionPostRequest } from './client/api/schema';
import type { OrchestrationModuleConfig } from './orchestration-types.js';

Expand Down Expand Up @@ -164,7 +164,9 @@ describe('construct completion post request', () => {
const config: OrchestrationModuleConfig = {
...defaultConfig,
filtering: {
input: buildAzureContentFilter({ Hate: 4, SelfHarm: 0 })
input: {
filters: [ContentFilters.azure({ Hate: 4, SelfHarm: 0 })]
}
}
};
const expectedCompletionPostRequest: CompletionPostRequest = {
Expand Down
194 changes: 194 additions & 0 deletions packages/orchestration/src/orchestration-filtering.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import {
buildAzureContentFilter,
ContentFilters
} from './orchestration-filtering.js';
import { constructCompletionPostRequest } from './orchestration-client.js';
import type { OrchestrationModuleConfig } from './orchestration-types.js';
import type {
CompletionPostRequest,
FilterConfig,
FilteringModuleConfig
} from './client/api/schema/index.js';

describe('Content filters', () => {
// TOOD: Remove this test collection once `buildAzureContentFilter` is removed.
describe('buildAzureContentFilter', () => {
const config: OrchestrationModuleConfig = {
llm: {
model_name: 'gpt-35-turbo-16k',
model_params: { max_tokens: 50, temperature: 0.1 }
},
templating: {
template: [
{ role: 'user', content: 'Create {number} paraphrases of {phrase}' }
]
}
};

const prompt = { inputParams: { phrase: 'I hate you.', number: '3' } };

afterEach(() => {
config.filtering = undefined;
});

it('constructs filter configuration with only input', async () => {
const filtering: FilteringModuleConfig = {
input: buildAzureContentFilter({ Hate: 4, SelfHarm: 0 })
};
const expectedFilterConfig: FilteringModuleConfig = {
input: {
filters: [
{
type: 'azure_content_safety',
config: {
Hate: 4,
SelfHarm: 0
}
}
]
}
};
config.filtering = filtering;
const completionPostRequest: CompletionPostRequest =
constructCompletionPostRequest(config, prompt);
expect(
completionPostRequest.orchestration_config.module_configurations
.filtering_module_config
).toEqual(expectedFilterConfig);
});

it('constructs filter configuration with only output', async () => {
const filtering: FilteringModuleConfig = {
output: buildAzureContentFilter({ Sexual: 2, Violence: 6 })
};
const expectedFilterConfig: FilteringModuleConfig = {
output: {
filters: [
{
type: 'azure_content_safety',
config: {
Sexual: 2,
Violence: 6
}
}
]
}
};
config.filtering = filtering;
const completionPostRequest: CompletionPostRequest =
constructCompletionPostRequest(config, prompt);
expect(
completionPostRequest.orchestration_config.module_configurations
.filtering_module_config
).toEqual(expectedFilterConfig);
});

it('constructs filter configuration with both input and output', async () => {
const filtering: FilteringModuleConfig = {
input: buildAzureContentFilter({
Hate: 4,
SelfHarm: 0,
Sexual: 2,
Violence: 6
}),
output: buildAzureContentFilter({ Sexual: 2, Violence: 6 })
};
const expectedFilterConfig: FilteringModuleConfig = {
input: {
filters: [
{
type: 'azure_content_safety',
config: {
Hate: 4,
SelfHarm: 0,
Sexual: 2,
Violence: 6
}
}
]
},
output: {
filters: [
{
type: 'azure_content_safety',
config: {
Sexual: 2,
Violence: 6
}
}
]
}
};
config.filtering = filtering;
const completionPostRequest: CompletionPostRequest =
constructCompletionPostRequest(config, prompt);
expect(
completionPostRequest.orchestration_config.module_configurations
.filtering_module_config
).toEqual(expectedFilterConfig);
});

it('omits filters if not set', async () => {
const filtering: FilteringModuleConfig = {
input: buildAzureContentFilter(),
output: buildAzureContentFilter()
};
config.filtering = filtering;
const completionPostRequest: CompletionPostRequest =
constructCompletionPostRequest(config, prompt);
const expectedFilterConfig: FilteringModuleConfig = {
input: {
filters: [
{
type: 'azure_content_safety'
}
]
},
output: {
filters: [
{
type: 'azure_content_safety'
}
]
}
};
expect(
completionPostRequest.orchestration_config.module_configurations
.filtering_module_config
).toEqual(expectedFilterConfig);
});

it('omits filter configuration if not set', async () => {
const filtering: FilteringModuleConfig = {};
config.filtering = filtering;
const completionPostRequest: CompletionPostRequest =
constructCompletionPostRequest(config, prompt);
expect(
completionPostRequest.orchestration_config.module_configurations
.filtering_module_config
).toBeUndefined();
});
});

describe('Azure content filter', () => {
it('builds filter config', async () => {
const filterConfig = ContentFilters.azure({ Hate: 4, SelfHarm: 0 });
const expectedFilterConfig: FilterConfig = {
type: 'azure_content_safety',
config: {
Hate: 4,
SelfHarm: 0
}
};
expect(filterConfig).toEqual(expectedFilterConfig);
});

it('builds filter config with no config', async () => {
const filterConfig = ContentFilters.azure();
const expectedFilterConfig: FilterConfig = {
type: 'azure_content_safety'
};
expect(filterConfig).toEqual(expectedFilterConfig);
});
});
});
39 changes: 39 additions & 0 deletions packages/orchestration/src/orchestration-filtering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type {
AzureContentSafety,
FilterConfig,
InputFilteringConfig,
OutputFilteringConfig
} from './client/api/schema/index.js';

/**
* Convenience function to create Azure content filters.
* @param filter - Filtering configuration for Azure filter. If skipped, the default Azure content filter configuration is used.
* @returns An object with the Azure filtering configuration.
* @deprecated Use ContentFilters.azure() instead.
*/
export function buildAzureContentFilter(
filter?: AzureContentSafety
): InputFilteringConfig | OutputFilteringConfig {
return {
filters: [ContentFilters.azure(filter)]
};
}

/**
* Convenience function to create Azure content filters.
* @param config - Filtering configuration for Azure filter. If skipped, the default Azure content filter configuration is used.
* @returns Filter config object.
*/
function azure(config?: AzureContentSafety): FilterConfig {
return {
type: 'azure_content_safety',
...(config && { config })
};
}

/**
* Content filters for orchestration.
*/
export const ContentFilters = {
azure
};
Loading
Loading