content-moderation-sdk 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +275 -0
  3. package/content-moderation-sdk.jpg +0 -0
  4. package/dist/azure.d.ts +38 -0
  5. package/dist/azure.d.ts.map +1 -0
  6. package/dist/azure.js +114 -0
  7. package/dist/azure.js.map +1 -0
  8. package/dist/core.d.ts +3 -0
  9. package/dist/core.d.ts.map +1 -0
  10. package/dist/core.js +313 -0
  11. package/dist/core.js.map +1 -0
  12. package/dist/errors.d.ts +41 -0
  13. package/dist/errors.d.ts.map +1 -0
  14. package/dist/errors.js +77 -0
  15. package/dist/errors.js.map +1 -0
  16. package/dist/http.d.ts +9 -0
  17. package/dist/http.d.ts.map +1 -0
  18. package/dist/http.js +87 -0
  19. package/dist/http.js.map +1 -0
  20. package/dist/index.d.ts +4 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +3 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mistral.d.ts +23 -0
  25. package/dist/mistral.d.ts.map +1 -0
  26. package/dist/mistral.js +81 -0
  27. package/dist/mistral.js.map +1 -0
  28. package/dist/openai.d.ts +28 -0
  29. package/dist/openai.d.ts.map +1 -0
  30. package/dist/openai.js +105 -0
  31. package/dist/openai.js.map +1 -0
  32. package/dist/testing.d.ts +18 -0
  33. package/dist/testing.d.ts.map +1 -0
  34. package/dist/testing.js +44 -0
  35. package/dist/testing.js.map +1 -0
  36. package/dist/types.d.ts +164 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +2 -0
  39. package/dist/types.js.map +1 -0
  40. package/dist/utils.d.ts +26 -0
  41. package/dist/utils.d.ts.map +1 -0
  42. package/dist/utils.js +229 -0
  43. package/dist/utils.js.map +1 -0
  44. package/package.json +76 -1
  45. package/src/azure.ts +191 -0
  46. package/src/core.ts +440 -0
  47. package/src/errors.ts +109 -0
  48. package/src/http.ts +94 -0
  49. package/src/index.ts +43 -0
  50. package/src/mistral.ts +124 -0
  51. package/src/openai.ts +162 -0
  52. package/src/testing.ts +85 -0
  53. package/src/types.ts +241 -0
  54. package/src/utils.ts +281 -0
package/src/mistral.ts ADDED
@@ -0,0 +1,124 @@
1
+ import { ModerationAdapterError, ModerationValidationError } from "./errors.js";
2
+ import { requestJson } from "./http.js";
3
+ import type {
4
+ ModerationAdapter,
5
+ ModerationAdapterResult,
6
+ ModerationCategoryAssessment,
7
+ } from "./types.js";
8
+ import { normalizeHttpEndpoint } from "./utils.js";
9
+
10
+ export type MistralModeration = {
11
+ categories: Record<string, boolean>;
12
+ category_scores: Record<string, number>;
13
+ };
14
+
15
+ export type MistralModerationResponse = {
16
+ id: string;
17
+ model: string;
18
+ results: MistralModeration[];
19
+ };
20
+
21
+ export type MistralOptions = {
22
+ apiKey: string;
23
+ model?: string;
24
+ baseUrl?: string;
25
+ headers?: Readonly<Record<string, string>>;
26
+ fetch?: typeof fetch;
27
+ };
28
+
29
+ export type MistralAdapter = ModerationAdapter<
30
+ "mistral",
31
+ { baseUrl: string; model: string },
32
+ MistralModerationResponse
33
+ >;
34
+
35
+ export function mistral(options: MistralOptions): MistralAdapter {
36
+ if (typeof options.apiKey !== "string" || options.apiKey.trim().length === 0) {
37
+ throw new ModerationValidationError("Mistral apiKey is required.");
38
+ }
39
+
40
+ const model = options.model ?? "mistral-moderation-latest";
41
+ if (typeof model !== "string" || model.trim().length === 0) {
42
+ throw new ModerationValidationError("Mistral model must be a non-empty string.");
43
+ }
44
+ const baseUrl = normalizeHttpEndpoint(
45
+ options.baseUrl ?? "https://api.mistral.ai/v1",
46
+ "Mistral baseUrl",
47
+ );
48
+ const fetcher = options.fetch ?? fetch;
49
+
50
+ return {
51
+ name: "mistral",
52
+ capabilities: {
53
+ text: true,
54
+ image: false,
55
+ conversation: "native",
56
+ },
57
+ raw: { baseUrl, model },
58
+ async moderate(input, context) {
59
+ if (input.type === "image") {
60
+ throw new ModerationValidationError("Mistral moderation does not support image input.");
61
+ }
62
+
63
+ const conversation = input.type === "conversation";
64
+ const body = await requestJson<MistralModerationResponse>({
65
+ adapter: "mistral",
66
+ provider: "Mistral",
67
+ fetcher,
68
+ url: `${baseUrl}/${conversation ? "chat/moderations" : "moderations"}`,
69
+ init: {
70
+ method: "POST",
71
+ signal: context.signal,
72
+ headers: {
73
+ Authorization: `Bearer ${options.apiKey}`,
74
+ "Content-Type": "application/json",
75
+ ...options.headers,
76
+ },
77
+ body: JSON.stringify({
78
+ model,
79
+ input: conversation ? input.messages : input.text,
80
+ }),
81
+ },
82
+ });
83
+
84
+ const result = body.results?.[0];
85
+ if (!result || !result.categories || !result.category_scores) {
86
+ throw new ModerationAdapterError("Mistral returned an invalid moderation response.", {
87
+ adapter: "mistral",
88
+ code: "invalid_response",
89
+ details: body,
90
+ });
91
+ }
92
+
93
+ const categories = toCategoryAssessments(result);
94
+ return {
95
+ adapter: "mistral",
96
+ id: body.id,
97
+ model: body.model,
98
+ flagged: Object.values(result.categories).some(Boolean),
99
+ categories,
100
+ raw: body,
101
+ } satisfies ModerationAdapterResult<"mistral", MistralModerationResponse>;
102
+ },
103
+ };
104
+ }
105
+
106
+ function toCategoryAssessments(
107
+ result: MistralModeration,
108
+ ): Readonly<Record<string, ModerationCategoryAssessment>> {
109
+ const names = new Set([
110
+ ...Object.keys(result.categories ?? {}),
111
+ ...Object.keys(result.category_scores ?? {}),
112
+ ]);
113
+ return Object.fromEntries(
114
+ [...names].map((name) => [
115
+ name,
116
+ {
117
+ flagged: result.categories?.[name] ?? null,
118
+ ...(typeof result.category_scores?.[name] === "number"
119
+ ? { score: result.category_scores[name] }
120
+ : {}),
121
+ },
122
+ ]),
123
+ );
124
+ }
package/src/openai.ts ADDED
@@ -0,0 +1,162 @@
1
+ import { ModerationAdapterError, ModerationValidationError } from "./errors.js";
2
+ import { requestJson } from "./http.js";
3
+ import type {
4
+ ModerationAdapter,
5
+ ModerationAdapterResult,
6
+ ModerationCategoryAssessment,
7
+ ModerationImageSource,
8
+ } from "./types.js";
9
+ import { imageSourceToDataUrl, normalizeHttpEndpoint } from "./utils.js";
10
+
11
+ export type OpenAIModerationModel =
12
+ | "omni-moderation-latest"
13
+ | "omni-moderation-2024-09-26"
14
+ | "text-moderation-latest"
15
+ | "text-moderation-stable"
16
+ | (string & {});
17
+
18
+ export type OpenAIModeration = {
19
+ flagged: boolean;
20
+ categories: Record<string, boolean | null>;
21
+ category_scores: Record<string, number>;
22
+ category_applied_input_types?: Record<string, ("text" | "image")[]>;
23
+ };
24
+
25
+ export type OpenAIModerationResponse = {
26
+ id: string;
27
+ model: string;
28
+ results: OpenAIModeration[];
29
+ };
30
+
31
+ export type OpenAIOptions = {
32
+ apiKey: string;
33
+ model?: OpenAIModerationModel;
34
+ baseUrl?: string;
35
+ organization?: string;
36
+ project?: string;
37
+ headers?: Readonly<Record<string, string>>;
38
+ fetch?: typeof fetch;
39
+ };
40
+
41
+ export type OpenAIAdapter = ModerationAdapter<
42
+ "openai",
43
+ { baseUrl: string; model: OpenAIModerationModel },
44
+ OpenAIModerationResponse
45
+ >;
46
+
47
+ export function openai(options: OpenAIOptions): OpenAIAdapter {
48
+ assertCredential(options.apiKey);
49
+ const model = options.model ?? "omni-moderation-latest";
50
+ if (typeof model !== "string" || model.trim().length === 0) {
51
+ throw new ModerationValidationError("OpenAI model must be a non-empty string.");
52
+ }
53
+ const baseUrl = normalizeHttpEndpoint(
54
+ options.baseUrl ?? "https://api.openai.com/v1",
55
+ "OpenAI baseUrl",
56
+ );
57
+ const fetcher = options.fetch ?? fetch;
58
+ const supportsImages = model !== "text-moderation-latest" && model !== "text-moderation-stable";
59
+
60
+ return {
61
+ name: "openai",
62
+ capabilities: {
63
+ text: true,
64
+ image: supportsImages,
65
+ conversation: "flattened",
66
+ },
67
+ raw: { baseUrl, model },
68
+ async moderate(input, context) {
69
+ const body = await requestJson<OpenAIModerationResponse>({
70
+ adapter: "openai",
71
+ provider: "OpenAI",
72
+ fetcher,
73
+ url: `${baseUrl}/moderations`,
74
+ init: {
75
+ method: "POST",
76
+ signal: context.signal,
77
+ headers: {
78
+ Authorization: `Bearer ${options.apiKey}`,
79
+ "Content-Type": "application/json",
80
+ ...(options.organization ? { "OpenAI-Organization": options.organization } : {}),
81
+ ...(options.project ? { "OpenAI-Project": options.project } : {}),
82
+ ...options.headers,
83
+ },
84
+ body: JSON.stringify({
85
+ model,
86
+ input: await toOpenAIInput(input),
87
+ }),
88
+ },
89
+ });
90
+
91
+ const result = body.results?.[0];
92
+ if (!result || typeof result.flagged !== "boolean") {
93
+ throw new ModerationAdapterError("OpenAI returned an invalid moderation response.", {
94
+ adapter: "openai",
95
+ code: "invalid_response",
96
+ details: body,
97
+ });
98
+ }
99
+
100
+ return {
101
+ adapter: "openai",
102
+ id: body.id,
103
+ model: body.model,
104
+ flagged: result.flagged,
105
+ categories: toCategoryAssessments(result),
106
+ raw: body,
107
+ } satisfies ModerationAdapterResult<"openai", OpenAIModerationResponse>;
108
+ },
109
+ };
110
+ }
111
+
112
+ async function toOpenAIInput(input: Parameters<OpenAIAdapter["moderate"]>[0]) {
113
+ if (input.type === "text") return input.text;
114
+ if (input.type === "conversation") {
115
+ throw new ModerationValidationError(
116
+ "OpenAI receives conversations through the client's flattened text path.",
117
+ );
118
+ }
119
+
120
+ return [
121
+ {
122
+ type: "image_url",
123
+ image_url: {
124
+ url: await toImageUrl(input.source),
125
+ },
126
+ },
127
+ ];
128
+ }
129
+
130
+ async function toImageUrl(source: ModerationImageSource): Promise<string> {
131
+ if (source.type === "url") return source.url;
132
+ return imageSourceToDataUrl(source);
133
+ }
134
+
135
+ function toCategoryAssessments(
136
+ result: OpenAIModeration,
137
+ ): Readonly<Record<string, ModerationCategoryAssessment>> {
138
+ const names = new Set([
139
+ ...Object.keys(result.categories ?? {}),
140
+ ...Object.keys(result.category_scores ?? {}),
141
+ ]);
142
+ return Object.fromEntries(
143
+ [...names].map((name) => [
144
+ name,
145
+ {
146
+ flagged: result.categories?.[name] ?? null,
147
+ ...(typeof result.category_scores?.[name] === "number"
148
+ ? { score: result.category_scores[name] }
149
+ : {}),
150
+ ...(result.category_applied_input_types?.[name]
151
+ ? { inputTypes: result.category_applied_input_types[name] }
152
+ : {}),
153
+ },
154
+ ]),
155
+ );
156
+ }
157
+
158
+ function assertCredential(apiKey: string): void {
159
+ if (typeof apiKey !== "string" || apiKey.trim().length === 0) {
160
+ throw new ModerationValidationError("OpenAI apiKey is required.");
161
+ }
162
+ }
package/src/testing.ts ADDED
@@ -0,0 +1,85 @@
1
+ import type {
2
+ MaybePromise,
3
+ ModerationAdapter,
4
+ ModerationAdapterCapabilities,
5
+ ModerationAdapterContext,
6
+ ModerationAdapterResult,
7
+ ModerationInput,
8
+ } from "./types.js";
9
+
10
+ const ALL_CAPABILITIES: ModerationAdapterCapabilities = {
11
+ text: true,
12
+ image: true,
13
+ conversation: "native",
14
+ };
15
+
16
+ export type MemoryModerationRequest<Raw = unknown> = {
17
+ input: ModerationInput;
18
+ context: ModerationAdapterContext;
19
+ result: ModerationAdapterResult<string, Raw>;
20
+ };
21
+
22
+ export type MemoryAdapterOptions<Name extends string, Raw = unknown> = {
23
+ name?: Name;
24
+ capabilities?: ModerationAdapterCapabilities;
25
+ handler?: (
26
+ input: ModerationInput,
27
+ context: ModerationAdapterContext,
28
+ ) => MaybePromise<ModerationAdapterResult<Name, Raw>>;
29
+ };
30
+
31
+ export type MemoryAdapter<Name extends string = "memory", Raw = unknown> = ModerationAdapter<
32
+ Name,
33
+ {
34
+ requests: MemoryModerationRequest<Raw>[];
35
+ clear(): void;
36
+ },
37
+ Raw
38
+ >;
39
+
40
+ export function memoryAdapter<const Name extends string = "memory", Raw = unknown>(
41
+ options: MemoryAdapterOptions<Name, Raw> = {},
42
+ ): MemoryAdapter<Name, Raw> {
43
+ const name = (options.name ?? "memory") as Name;
44
+ const requests: MemoryModerationRequest<Raw>[] = [];
45
+
46
+ return {
47
+ name,
48
+ capabilities: options.capabilities ?? ALL_CAPABILITIES,
49
+ raw: {
50
+ requests,
51
+ clear() {
52
+ requests.length = 0;
53
+ },
54
+ },
55
+ async moderate(input, context) {
56
+ const result = options.handler
57
+ ? await options.handler(input, context)
58
+ : ({
59
+ adapter: name,
60
+ flagged: false,
61
+ categories: {},
62
+ } as ModerationAdapterResult<Name, Raw>);
63
+ requests.push({
64
+ input,
65
+ context,
66
+ result: result as ModerationAdapterResult<string, Raw>,
67
+ });
68
+ return result;
69
+ },
70
+ };
71
+ }
72
+
73
+ export function failingAdapter<const Name extends string>(
74
+ name: Name,
75
+ error: Error = new Error("Moderation adapter failed."),
76
+ capabilities: ModerationAdapterCapabilities = ALL_CAPABILITIES,
77
+ ): ModerationAdapter<Name> {
78
+ return {
79
+ name,
80
+ capabilities,
81
+ moderate() {
82
+ throw error;
83
+ },
84
+ };
85
+ }
package/src/types.ts ADDED
@@ -0,0 +1,241 @@
1
+ import type { ModerationAdapterError, ModerationSdkError } from "./errors.js";
2
+
3
+ export type MaybePromise<T> = T | Promise<T>;
4
+
5
+ export type ModerationInputType = "text" | "image" | "conversation";
6
+
7
+ export type ModerationTextInput = {
8
+ type: "text";
9
+ text: string;
10
+ };
11
+
12
+ export type ModerationImageSource =
13
+ | {
14
+ type: "url";
15
+ url: string;
16
+ }
17
+ | {
18
+ type: "base64";
19
+ data: string;
20
+ mediaType: string;
21
+ }
22
+ | {
23
+ type: "bytes";
24
+ data: Uint8Array | ArrayBuffer | Blob;
25
+ mediaType: string;
26
+ };
27
+
28
+ export type ModerationImageInput = {
29
+ type: "image";
30
+ source: ModerationImageSource;
31
+ };
32
+
33
+ export type ModerationMessageRole = "system" | "user" | "assistant" | "tool";
34
+
35
+ export type ModerationMessage = {
36
+ role: ModerationMessageRole;
37
+ content: string;
38
+ };
39
+
40
+ export type ModerationConversationInput = {
41
+ type: "conversation";
42
+ messages: readonly ModerationMessage[];
43
+ };
44
+
45
+ export type ModerationInput =
46
+ | ModerationTextInput
47
+ | ModerationImageInput
48
+ | ModerationConversationInput;
49
+
50
+ export type ModerationProcessingMode = "native" | "flattened";
51
+
52
+ export type ModerationCategoryAssessment = {
53
+ flagged: boolean | null;
54
+ score?: number;
55
+ severity?: {
56
+ value: number;
57
+ max: number;
58
+ };
59
+ inputTypes?: readonly ("text" | "image")[];
60
+ };
61
+
62
+ export type ModerationAdapterResult<Name extends string = string, Raw = unknown> = {
63
+ adapter: Name;
64
+ flagged: boolean;
65
+ categories: Readonly<Record<string, ModerationCategoryAssessment>>;
66
+ id?: string;
67
+ model?: string;
68
+ raw?: Raw;
69
+ };
70
+
71
+ export type ModerationResult<Name extends string = string, Raw = unknown> = ModerationAdapterResult<
72
+ Name,
73
+ Raw
74
+ > & {
75
+ inputType: ModerationInputType;
76
+ mode: ModerationProcessingMode;
77
+ };
78
+
79
+ export type ModerationAdapterCapabilities = {
80
+ text: boolean;
81
+ image: boolean;
82
+ conversation: "native" | "flattened" | "unsupported";
83
+ };
84
+
85
+ export type ModerationAdapterValidationContext = {
86
+ adapter: string;
87
+ inputType: ModerationInputType;
88
+ mode: ModerationProcessingMode;
89
+ };
90
+
91
+ export type ModerationAdapterContext = ModerationAdapterValidationContext & {
92
+ attempt: number;
93
+ signal?: AbortSignal;
94
+ metadata?: Readonly<Record<string, unknown>>;
95
+ };
96
+
97
+ export type ModerationAdapter<
98
+ Name extends string = string,
99
+ RawClient = unknown,
100
+ RawResult = unknown,
101
+ > = {
102
+ readonly name: Name;
103
+ readonly capabilities: ModerationAdapterCapabilities;
104
+ readonly raw?: RawClient;
105
+ validate?(
106
+ input: ModerationInput,
107
+ context: ModerationAdapterValidationContext,
108
+ ): MaybePromise<void>;
109
+ moderate(
110
+ input: ModerationInput,
111
+ context: ModerationAdapterContext,
112
+ ): MaybePromise<ModerationAdapterResult<Name, RawResult>>;
113
+ };
114
+
115
+ export type ModerationRetryConfig = {
116
+ maxAttempts?: number;
117
+ delay?: (attempt: number, error: ModerationAdapterError) => number;
118
+ shouldRetry?: (error: ModerationAdapterError, attempt: number) => boolean;
119
+ };
120
+
121
+ export type ModerationFallbackConfig<Name extends string = string> = {
122
+ adapters: readonly Name[];
123
+ shouldFallback?: (error: ModerationAdapterError, nextAdapter: Name) => MaybePromise<boolean>;
124
+ };
125
+
126
+ export type ModerationOptions<Name extends string = string> = {
127
+ adapter?: Name;
128
+ fallback?: ModerationFallbackConfig<Name>;
129
+ retry?: ModerationRetryConfig;
130
+ signal?: AbortSignal;
131
+ metadata?: Readonly<Record<string, unknown>>;
132
+ };
133
+
134
+ export type ModerationHookEvent = {
135
+ adapter: string;
136
+ input: ModerationInput;
137
+ inputType: ModerationInputType;
138
+ mode: ModerationProcessingMode;
139
+ attempt: number;
140
+ metadata?: Readonly<Record<string, unknown>>;
141
+ };
142
+
143
+ export type ModerationAfterEvent = ModerationHookEvent & {
144
+ result: ModerationResult;
145
+ };
146
+
147
+ export type ModerationErrorEvent = ModerationHookEvent & {
148
+ error: ModerationAdapterError;
149
+ };
150
+
151
+ export type ModerationHooks = {
152
+ beforeModerate?: (event: ModerationHookEvent) => MaybePromise<void>;
153
+ afterModerate?: (event: ModerationAfterEvent) => MaybePromise<void>;
154
+ onError?: (event: ModerationErrorEvent) => MaybePromise<void>;
155
+ onRetry?: (
156
+ event: ModerationErrorEvent & {
157
+ nextAttempt: number;
158
+ delayMs: number;
159
+ },
160
+ ) => MaybePromise<void>;
161
+ };
162
+
163
+ export type ModerationClientOptions<
164
+ TAdapters extends readonly ModerationAdapter[] = readonly ModerationAdapter[],
165
+ > = {
166
+ adapters: TAdapters;
167
+ defaultAdapter?: TAdapters[number]["name"];
168
+ fallback?: ModerationFallbackConfig<TAdapters[number]["name"]>;
169
+ retry?: ModerationRetryConfig;
170
+ hooks?: ModerationHooks;
171
+ };
172
+
173
+ export type ModerationRouteName<Routes extends readonly ModerationAdapter[]> =
174
+ Routes[number]["name"];
175
+
176
+ export type ModerationAdapterForName<
177
+ Routes extends readonly ModerationAdapter[],
178
+ Name extends ModerationRouteName<Routes>,
179
+ > = Extract<Routes[number], { name: Name }>;
180
+
181
+ export type ModerationResultForAdapter<TAdapter extends ModerationAdapter> =
182
+ TAdapter extends ModerationAdapter<infer Name, unknown, infer Raw>
183
+ ? ModerationResult<Name, Raw>
184
+ : never;
185
+
186
+ export type ModerationValidationRoute<Name extends string = string> = {
187
+ adapter: Name;
188
+ mode: ModerationProcessingMode;
189
+ };
190
+
191
+ export type ModerationValidationResult<Name extends string = string> = {
192
+ adapter: Name;
193
+ routes: readonly ModerationValidationRoute<Name>[];
194
+ };
195
+
196
+ export type ModerationItem<Name extends string = string> = {
197
+ input: ModerationInput;
198
+ options?: ModerationOptions<Name>;
199
+ };
200
+
201
+ export type ModerationSettledResult<Result extends ModerationResult = ModerationResult> =
202
+ | { ok: true; index: number; result: Result }
203
+ | { ok: false; index: number; error: ModerationSdkError };
204
+
205
+ export type BoundModerationClient<TAdapter extends ModerationAdapter> = {
206
+ validate(
207
+ input: ModerationInput,
208
+ options?: Omit<ModerationOptions<TAdapter["name"]>, "adapter">,
209
+ ): Promise<ModerationValidationResult<TAdapter["name"]>>;
210
+ moderate(
211
+ input: ModerationInput,
212
+ options?: Omit<ModerationOptions<TAdapter["name"]>, "adapter">,
213
+ ): Promise<ModerationResultForAdapter<TAdapter>>;
214
+ moderateMany(
215
+ items: readonly ModerationItem<TAdapter["name"]>[],
216
+ options?: Omit<ModerationOptions<TAdapter["name"]>, "adapter">,
217
+ ): Promise<readonly ModerationSettledResult<ModerationResultForAdapter<TAdapter>>[]>;
218
+ };
219
+
220
+ export type ModerationClient<Routes extends readonly ModerationAdapter[]> = {
221
+ readonly adapters: ReadonlyMap<ModerationRouteName<Routes>, Routes[number]>;
222
+ readonly defaultAdapter: ModerationRouteName<Routes>;
223
+ validate(
224
+ input: ModerationInput,
225
+ options?: ModerationOptions<ModerationRouteName<Routes>>,
226
+ ): Promise<ModerationValidationResult<ModerationRouteName<Routes>>>;
227
+ moderate(
228
+ input: ModerationInput,
229
+ options?: ModerationOptions<ModerationRouteName<Routes>>,
230
+ ): Promise<ModerationResultForAdapter<Routes[number]>>;
231
+ moderateMany(
232
+ items: readonly ModerationItem<ModerationRouteName<Routes>>[],
233
+ options?: ModerationOptions<ModerationRouteName<Routes>>,
234
+ ): Promise<readonly ModerationSettledResult<ModerationResultForAdapter<Routes[number]>>[]>;
235
+ adapter<Name extends ModerationRouteName<Routes>>(
236
+ name: Name,
237
+ ): ModerationAdapterForName<Routes, Name>;
238
+ withAdapter<Name extends ModerationRouteName<Routes>>(
239
+ name: Name,
240
+ ): BoundModerationClient<ModerationAdapterForName<Routes, Name>>;
241
+ };