Skip to content

Commit da0fd23

Browse files
authored
Merge branch 'main' into chore/video-support-bedrock
2 parents 31bf440 + 425ea8a commit da0fd23

File tree

22 files changed

+601
-70
lines changed

22 files changed

+601
-70
lines changed

plugins/default/regexReplace.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
HookEventType,
3+
PluginContext,
4+
PluginHandler,
5+
PluginParameters,
6+
} from '../types';
7+
import { getCurrentContentPart, setCurrentContentPart } from '../utils';
8+
9+
export const handler: PluginHandler = async (
10+
context: PluginContext,
11+
parameters: PluginParameters,
12+
eventType: HookEventType
13+
) => {
14+
let error = null;
15+
let verdict = true;
16+
let data: any = null;
17+
const transformedData: Record<string, any> = {
18+
request: {
19+
json: null,
20+
},
21+
response: {
22+
json: null,
23+
},
24+
};
25+
let transformed = false;
26+
27+
try {
28+
const regexPattern = parameters.rule;
29+
const redactText = parameters.redactText || '[REDACTED]';
30+
const failOnDetection = parameters.failOnDetection || false;
31+
32+
const { content, textArray } = getCurrentContentPart(context, eventType);
33+
34+
if (!regexPattern) {
35+
throw new Error('Missing regex pattern');
36+
}
37+
if (!content) {
38+
throw new Error('Missing text to match');
39+
}
40+
41+
const regex = new RegExp(regexPattern, 'g');
42+
43+
// Process all text items in the array
44+
let hasMatches = false;
45+
const mappedTextArray: Array<string | null> = [];
46+
textArray.forEach((text) => {
47+
if (!text) {
48+
mappedTextArray.push(null);
49+
return;
50+
}
51+
52+
// Reset regex for each text when using global flag
53+
regex.lastIndex = 0;
54+
55+
const matches = text.match(regex);
56+
if (matches && matches.length > 0) {
57+
hasMatches = true;
58+
}
59+
const replacedText = text.replace(regex, redactText);
60+
mappedTextArray.push(replacedText);
61+
});
62+
63+
// Handle transformation
64+
if (hasMatches) {
65+
setCurrentContentPart(
66+
context,
67+
eventType,
68+
transformedData,
69+
mappedTextArray
70+
);
71+
transformed = true;
72+
}
73+
if (failOnDetection && hasMatches) {
74+
verdict = false;
75+
}
76+
data = {
77+
regexPattern,
78+
verdict,
79+
explanation: transformed
80+
? `Pattern '${regexPattern}' matched and was replaced with '${redactText}'`
81+
: `The regex pattern '${regexPattern}' did not match any text.`,
82+
};
83+
} catch (e: any) {
84+
error = e;
85+
data = {
86+
explanation: `An error occurred while processing the regex: ${e.message}`,
87+
regexPattern: parameters.rule,
88+
};
89+
}
90+
91+
return { error, verdict, data, transformedData, transformed };
92+
};

plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { handler as panwPrismaAirsintercept } from './panw-prisma-airs/intercept
5050
import { handler as defaultjwt } from './default/jwt';
5151
import { handler as defaultrequiredMetadataKeys } from './default/requiredMetadataKeys';
5252
import { handler as walledaiguardrails } from './walledai/guardrails';
53+
import { handler as defaultregexReplace } from './default/regexReplace';
5354

5455
export const plugins = {
5556
default: {
@@ -70,6 +71,7 @@ export const plugins = {
7071
modelWhitelist: defaultmodelWhitelist,
7172
jwt: defaultjwt,
7273
requiredMetadataKeys: defaultrequiredMetadataKeys,
74+
regexReplace: defaultregexReplace,
7375
},
7476
portkey: {
7577
moderateContent: portkeymoderateContent,

src/providers/anthropic/chatComplete.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,9 @@ export const AnthropicChatCompleteStreamChunkTransform: (
606606
streamState,
607607
strictOpenAiCompliance
608608
) => {
609+
if (streamState.toolIndex == undefined) {
610+
streamState.toolIndex = -1;
611+
}
609612
let chunk = responseChunk.trim();
610613
if (
611614
chunk.startsWith('event: ping') ||
@@ -724,9 +727,7 @@ export const AnthropicChatCompleteStreamChunkTransform: (
724727
parsedChunk.type === 'content_block_start' &&
725728
parsedChunk.content_block?.type === 'tool_use';
726729
if (isToolBlockStart) {
727-
streamState.toolIndex = streamState.toolIndex
728-
? streamState.toolIndex + 1
729-
: 0;
730+
streamState.toolIndex = streamState.toolIndex + 1;
730731
}
731732
const isToolBlockDelta: boolean =
732733
parsedChunk.type === 'content_block_delta' &&

src/providers/anthropic/complete.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { ANTHROPIC } from '../../globals';
22
import { Params } from '../../types/requestBody';
33
import { CompletionResponse, ErrorResponse, ProviderConfig } from '../types';
4-
import { generateInvalidProviderResponseError } from '../utils';
4+
import {
5+
generateInvalidProviderResponseError,
6+
transformFinishReason,
7+
} from '../utils';
8+
import {
9+
ANTHROPIC_STOP_REASON,
10+
AnthropicStreamState,
11+
AnthropicErrorResponse,
12+
} from './types';
513
import { AnthropicErrorResponseTransform } from './utils';
6-
import { AnthropicErrorResponse } from './types';
714

815
// TODO: this configuration does not enforce the maximum token limit for the input parameter. If you want to enforce this, you might need to add a custom validation function or a max property to the ParameterConfig interface, and then use it in the input configuration. However, this might be complex because the token count is not a simple length check, but depends on the specific tokenization method used by the model.
916

@@ -57,7 +64,7 @@ export const AnthropicCompleteConfig: ProviderConfig = {
5764

5865
interface AnthropicCompleteResponse {
5966
completion: string;
60-
stop_reason: string;
67+
stop_reason: ANTHROPIC_STOP_REASON;
6168
model: string;
6269
truncated: boolean;
6370
stop: null | string;
@@ -68,10 +75,20 @@ interface AnthropicCompleteResponse {
6875
// TODO: The token calculation is wrong atm
6976
export const AnthropicCompleteResponseTransform: (
7077
response: AnthropicCompleteResponse | AnthropicErrorResponse,
71-
responseStatus: number
72-
) => CompletionResponse | ErrorResponse = (response, responseStatus) => {
73-
if (responseStatus !== 200 && 'error' in response) {
74-
return AnthropicErrorResponseTransform(response);
78+
responseStatus: number,
79+
responseHeaders: Headers,
80+
strictOpenAiCompliance: boolean
81+
) => CompletionResponse | ErrorResponse = (
82+
response,
83+
responseStatus,
84+
_responseHeaders,
85+
strictOpenAiCompliance
86+
) => {
87+
if (responseStatus !== 200) {
88+
const errorResposne = AnthropicErrorResponseTransform(
89+
response as AnthropicErrorResponse
90+
);
91+
if (errorResposne) return errorResposne;
7592
}
7693

7794
if ('completion' in response) {
@@ -86,7 +103,10 @@ export const AnthropicCompleteResponseTransform: (
86103
text: response.completion,
87104
index: 0,
88105
logprobs: null,
89-
finish_reason: response.stop_reason,
106+
finish_reason: transformFinishReason(
107+
response.stop_reason,
108+
strictOpenAiCompliance
109+
),
90110
},
91111
],
92112
};
@@ -96,8 +116,16 @@ export const AnthropicCompleteResponseTransform: (
96116
};
97117

98118
export const AnthropicCompleteStreamChunkTransform: (
99-
response: string
100-
) => string | undefined = (responseChunk) => {
119+
response: string,
120+
fallbackId: string,
121+
streamState: AnthropicStreamState,
122+
strictOpenAiCompliance: boolean
123+
) => string | undefined = (
124+
responseChunk,
125+
fallbackId,
126+
streamState,
127+
strictOpenAiCompliance
128+
) => {
101129
let chunk = responseChunk.trim();
102130
if (chunk.startsWith('event: ping')) {
103131
return;
@@ -110,6 +138,9 @@ export const AnthropicCompleteStreamChunkTransform: (
110138
return chunk;
111139
}
112140
const parsedChunk: AnthropicCompleteResponse = JSON.parse(chunk);
141+
const finishReason = parsedChunk.stop_reason
142+
? transformFinishReason(parsedChunk.stop_reason, strictOpenAiCompliance)
143+
: null;
113144
return (
114145
`data: ${JSON.stringify({
115146
id: parsedChunk.log_id,
@@ -122,7 +153,7 @@ export const AnthropicCompleteStreamChunkTransform: (
122153
text: parsedChunk.completion,
123154
index: 0,
124155
logprobs: null,
125-
finish_reason: parsedChunk.stop_reason,
156+
finish_reason: finishReason,
126157
},
127158
],
128159
})}` + '\n\n'

src/providers/bedrock/complete.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { BEDROCK } from '../../globals';
22
import { Params } from '../../types/requestBody';
33
import { CompletionResponse, ErrorResponse, ProviderConfig } from '../types';
4-
import { generateInvalidProviderResponseError } from '../utils';
4+
import {
5+
generateInvalidProviderResponseError,
6+
transformFinishReason,
7+
} from '../utils';
58
import { BedrockErrorResponseTransform } from './chatComplete';
69
import { BedrockErrorResponse } from './embed';
10+
import { TITAN_STOP_REASON as TITAN_COMPLETION_REASON } from './types';
711

812
export const BedrockAnthropicCompleteConfig: ProviderConfig = {
913
prompt: {
@@ -380,7 +384,7 @@ export interface BedrockTitanCompleteResponse {
380384
results: {
381385
tokenCount: number;
382386
outputText: string;
383-
completionReason: string;
387+
completionReason: TITAN_COMPLETION_REASON;
384388
}[];
385389
}
386390

@@ -420,7 +424,10 @@ export const BedrockTitanCompleteResponseTransform: (
420424
text: generation.outputText,
421425
index: index,
422426
logprobs: null,
423-
finish_reason: generation.completionReason,
427+
finish_reason: transformFinishReason(
428+
generation.completionReason,
429+
strictOpenAiCompliance
430+
),
424431
})),
425432
usage: {
426433
prompt_tokens: response.inputTextTokenCount,
@@ -437,7 +444,7 @@ export interface BedrockTitanStreamChunk {
437444
outputText: string;
438445
index: number;
439446
totalOutputTextTokenCount: number;
440-
completionReason: string | null;
447+
completionReason: TITAN_COMPLETION_REASON | null;
441448
'amazon-bedrock-invocationMetrics': {
442449
inputTokenCount: number;
443450
outputTokenCount: number;
@@ -462,6 +469,12 @@ export const BedrockTitanCompleteStreamChunkTransform: (
462469
let chunk = responseChunk.trim();
463470
chunk = chunk.trim();
464471
const parsedChunk: BedrockTitanStreamChunk = JSON.parse(chunk);
472+
const finishReason = parsedChunk.completionReason
473+
? transformFinishReason(
474+
parsedChunk.completionReason,
475+
_strictOpenAiCompliance
476+
)
477+
: null;
465478

466479
return [
467480
`data: ${JSON.stringify({
@@ -490,7 +503,7 @@ export const BedrockTitanCompleteStreamChunkTransform: (
490503
text: '',
491504
index: 0,
492505
logprobs: null,
493-
finish_reason: parsedChunk.completionReason,
506+
finish_reason: finishReason,
494507
},
495508
],
496509
usage: {

src/providers/bedrock/types.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export interface BedrockChatCompletionResponse {
108108
content: BedrockContentItem[];
109109
};
110110
};
111-
stopReason: BEDROCK_STOP_REASON;
111+
stopReason: BEDROCK_CONVERSE_STOP_REASON;
112112
usage: {
113113
inputTokens: number;
114114
outputTokens: number;
@@ -171,7 +171,7 @@ export type BedrockContentItem = {
171171
};
172172

173173
export interface BedrockStreamState {
174-
stopReason?: BEDROCK_STOP_REASON;
174+
stopReason?: BEDROCK_CONVERSE_STOP_REASON;
175175
currentToolCallIndex?: number;
176176
currentContentBlockIndex?: number;
177177
}
@@ -201,7 +201,7 @@ export interface BedrockChatCompleteStreamChunk {
201201
input?: object;
202202
};
203203
};
204-
stopReason?: BEDROCK_STOP_REASON;
204+
stopReason?: BEDROCK_CONVERSE_STOP_REASON;
205205
metrics?: {
206206
latencyMs: number;
207207
};
@@ -214,13 +214,22 @@ export interface BedrockChatCompleteStreamChunk {
214214
cacheWriteInputTokenCount?: number;
215215
cacheWriteInputTokens?: number;
216216
};
217+
message?: string;
217218
}
218219

219-
export enum BEDROCK_STOP_REASON {
220+
export enum BEDROCK_CONVERSE_STOP_REASON {
220221
end_turn = 'end_turn',
221222
tool_use = 'tool_use',
222223
max_tokens = 'max_tokens',
223224
stop_sequence = 'stop_sequence',
224225
guardrail_intervened = 'guardrail_intervened',
225226
content_filtered = 'content_filtered',
226227
}
228+
229+
export enum TITAN_STOP_REASON {
230+
FINISHED = 'FINISHED',
231+
LENGTH = 'LENGTH',
232+
STOP_CRITERIA_MET = 'STOP_CRITERIA_MET',
233+
RAG_QUERY_WHEN_RAG_DISABLED = 'RAG_QUERY_WHEN_RAG_DISABLED',
234+
CONTENT_FILTERED = 'CONTENT_FILTERED',
235+
}

src/providers/dashscope/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ProviderAPIConfig } from '../types';
22

33
export const dashscopeAPIConfig: ProviderAPIConfig = {
4-
getBaseURL: () => 'https://dashscope.aliyuncs.com/compatible-mode/v1',
4+
getBaseURL: () => 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
55
headers({ providerOptions }) {
66
const { apiKey } = providerOptions;
77
return { Authorization: `Bearer ${apiKey}` };

src/providers/dashscope/index.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,30 @@ import { ProviderConfigs } from '../types';
88
import { dashscopeAPIConfig } from './api';
99

1010
export const DashScopeConfig: ProviderConfigs = {
11-
chatComplete: chatCompleteParams([], { model: 'qwen-turbo' }),
11+
chatComplete: chatCompleteParams(
12+
[],
13+
{ model: 'qwen-turbo' },
14+
{
15+
top_k: {
16+
param: 'top_k',
17+
},
18+
repetition_penalty: {
19+
param: 'repetition_penalty',
20+
},
21+
stop: {
22+
param: 'stop',
23+
},
24+
enable_search: {
25+
param: 'enable_search',
26+
},
27+
enable_thinking: {
28+
param: 'enable_thinking',
29+
},
30+
thinking_budget: {
31+
param: 'thinking_budget',
32+
},
33+
}
34+
),
1235
embed: embedParams([], { model: 'text-embedding-v1' }),
1336
api: dashscopeAPIConfig,
1437
responseTransforms: responseTransformers(DASHSCOPE, {

0 commit comments

Comments
 (0)