Skip to content

Commit 4c074a8

Browse files
authored
Merge branch 'main' into feat/model-provider-api
2 parents e35c62a + 213273d commit 4c074a8

File tree

13 files changed

+201
-12
lines changed

13 files changed

+201
-12
lines changed

.github/workflows/run_tests.yml

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1-
name: Run Gateway tests
1+
name: Gateway Tests
22

33
on:
4-
pull_request:
5-
types: [opened, synchronize, reopened]
4+
issue_comment:
5+
types: [created]
66

77
jobs:
88
gateway-tests:
9+
if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, 'run tests') }}
910
runs-on: ubuntu-latest
1011
environment: production
1112
steps:
1213
- name: Checkout head
1314
uses: actions/checkout@v4
1415
with:
1516
fetch-depth: 0
17+
1618
- name: Install dependencies
1719
run: npm ci
20+
1821
- name: Build
1922
run: npm run build
23+
2024
- name: Start gateway and run tests
25+
id: run-tests
26+
continue-on-error: true
2127
run: |
2228
npm run build/start-server.js &
2329
echo "Waiting for gateway to start..."
@@ -27,6 +33,43 @@ jobs:
2733
echo "Gateway is ready. Running tests..."
2834
npm run test:gateway
2935
36+
- name: Update PR Check
37+
uses: actions/github-script@v6
38+
with:
39+
github-token: ${{secrets.GITHUB_TOKEN}}
40+
script: |
41+
const { owner, repo } = context.repo;
42+
const issue_number = context.issue.number;
43+
44+
try {
45+
const { data: pull_request } = await github.rest.pulls.get({
46+
owner,
47+
repo,
48+
pull_number: issue_number,
49+
});
50+
51+
const sha = pull_request.head.sha;
52+
53+
await github.rest.checks.create({
54+
owner,
55+
repo,
56+
name: 'Gateway Tests (Comment Triggered)',
57+
head_sha: sha,
58+
status: 'completed',
59+
conclusion: '${{ steps.run-tests.outcome }}',
60+
output: {
61+
title: 'Gateway Test Results',
62+
summary: 'Gateway tests have completed.',
63+
text: 'These tests were triggered by a comment on the PR.'
64+
},
65+
});
66+
67+
console.log('Check run created successfully');
68+
} catch (error) {
69+
console.error('Error creating check run:', error);
70+
core.setFailed(`Action failed with error: ${error}`);
71+
}
72+
3073
env:
3174
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
3275
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ The AI Gateway's enterprise version offers enterprise-ready capabilities for **o
6767

6868
The enterprise deployment architecture, supported platforms is available here - [**Enterprise Private Cloud Deployments**](https://docs.portkey.ai/docs/product/enterprise-offering/private-cloud-deployments)
6969

70-
<a href="https://app.portkey.ai/signup"><img src="https://portkey.ai/blog/content/images/2024/08/Get-API-Key--5-.png" height=50 alt="Book an enterprise AI gateway demo" /></a><br>
70+
<a href="https://portkey.sh/demo-22"><img src="https://portkey.ai/blog/content/images/2024/08/Get-API-Key--5-.png" height=50 alt="Book an enterprise AI gateway demo" /></a><br>
7171

7272
<br>
7373

@@ -268,7 +268,7 @@ Make your AI app more <ins>reliable</ins> and <ins>forward compatible</ins>, whi
268268
&nbsp; SOC2, ISO, HIPAA, GDPR Compliances - for best security practices <br>
269269
&nbsp; Professional Support - along with feature prioritization <br>
270270

271-
[Schedule a call to discuss enterprise deployments](https://calendly.com/rohit-portkey/noam)
271+
[Schedule a call to discuss enterprise deployments](https://portkey.sh/demo-22)
272272

273273
<br>
274274

Lines changed: 0 additions & 1 deletion
This file was deleted.

plugins/default/default.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { handler as logHandler } from './log';
1010
import { handler as allUppercaseHandler } from './alluppercase';
1111
import { handler as endsWithHandler } from './endsWith';
1212
import { handler as allLowerCaseHandler } from './alllowercase';
13+
import { handler as modelWhitelistHandler } from './modelWhitelist';
1314

1415
import { z } from 'zod';
1516
import { PluginContext, PluginParameters } from '../types';
@@ -802,3 +803,36 @@ describe('allLowercase handler', () => {
802803
expect(result.verdict).toBe(false);
803804
});
804805
});
806+
807+
describe('modelWhitelist handler', () => {
808+
it('should return true verdict when the model requested is part of the whitelist', async () => {
809+
const context: PluginContext = {
810+
request: { json: { model: 'gemini-1.5-flash-001' } },
811+
};
812+
813+
const parameters: PluginParameters = {
814+
models: ['gemini-1.5-flash-001'],
815+
};
816+
const eventType = 'beforeRequestHook';
817+
818+
const result = await modelWhitelistHandler(context, parameters, eventType);
819+
820+
expect(result.error).toBe(null);
821+
expect(result.verdict).toBe(true);
822+
});
823+
it('should return false verdict when the model requested is not part of the whitelist', async () => {
824+
const context: PluginContext = {
825+
request: { json: { model: 'gemini-1.5-pro-001' } },
826+
};
827+
828+
const parameters: PluginParameters = {
829+
models: ['gemini-1.5-flash-001'],
830+
};
831+
const eventType = 'beforeRequestHook';
832+
833+
const result = await modelWhitelistHandler(context, parameters, eventType);
834+
835+
expect(result.error).toBe(null);
836+
expect(result.verdict).toBe(false);
837+
});
838+
});

plugins/default/manifest.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,37 @@
475475
}
476476
],
477477
"parameters": {}
478+
},
479+
{
480+
"name": "Model whitelisting",
481+
"id": "modelwhitelist",
482+
"type": "guardrail",
483+
"supportedHooks": ["beforeRequestHook"],
484+
"description": [
485+
{
486+
"type": "subHeading",
487+
"text": "Check if the model in the request is part of the allowed model list."
488+
}
489+
],
490+
"parameters": {
491+
"type": "object",
492+
"properties": {
493+
"models": {
494+
"type": "array",
495+
"label": "Model list",
496+
"description": [
497+
{
498+
"type": "subHeading",
499+
"text": "Enter the allowed models."
500+
}
501+
],
502+
"items": {
503+
"type": "string"
504+
}
505+
}
506+
},
507+
"required": ["models"]
508+
}
478509
}
479510
]
480511
}

plugins/default/modelWhitelist.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {
2+
HookEventType,
3+
PluginContext,
4+
PluginHandler,
5+
PluginParameters,
6+
} from '../types';
7+
8+
export const handler: PluginHandler = async (
9+
context: PluginContext,
10+
parameters: PluginParameters,
11+
eventType: HookEventType
12+
) => {
13+
let error = null;
14+
let verdict = false;
15+
16+
try {
17+
const modelList = parameters.models;
18+
let requestModel = context.request?.json.model;
19+
verdict = modelList.includes(requestModel);
20+
} catch (e) {
21+
error = e as Error;
22+
}
23+
24+
return { error, verdict };
25+
};

plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { handler as defaultcontainsCode } from './default/containsCode';
1212
import { handler as defaultalluppercase } from './default/alluppercase';
1313
import { handler as defaultalllowercase } from './default/alllowercase';
1414
import { handler as defaultendsWith } from './default/endsWith';
15+
import { handler as defaultmodelWhitelist } from './default/modelWhitelist';
1516
import { handler as portkeymoderateContent } from './portkey/moderateContent';
1617
import { handler as portkeylanguage } from './portkey/language';
1718
import { handler as portkeypii } from './portkey/pii';
@@ -48,6 +49,7 @@ export const plugins = {
4849
alluppercase: defaultalluppercase,
4950
alllowercase: defaultalllowercase,
5051
endsWith: defaultendsWith,
52+
modelWhitelist: defaultmodelWhitelist,
5153
},
5254
portkey: {
5355
moderateContent: portkeymoderateContent,

src/handlers/handlerUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,8 @@ export async function tryPost(
642642

643643
// Prerequest validator (For virtual key budgets)
644644
const preRequestValidator = c.get('preRequestValidator');
645-
let preRequestValidatorResponse = preRequestValidator
646-
? await preRequestValidator(env(c), providerOption, requestHeaders)
645+
const preRequestValidatorResponse = preRequestValidator
646+
? await preRequestValidator(c, providerOption, requestHeaders, params)
647647
: undefined;
648648
if (!!preRequestValidatorResponse) {
649649
return createResponse(preRequestValidatorResponse, undefined, false);

src/providers/azure-openai/chatComplete.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,24 @@ export const AzureOpenAIChatCompleteConfig: ProviderConfig = {
8787
response_format: {
8888
param: 'response_format',
8989
},
90+
store: {
91+
param: 'store',
92+
},
93+
metadata: {
94+
param: 'metadata',
95+
},
96+
modalities: {
97+
param: 'modalities',
98+
},
99+
audio: {
100+
param: 'audio',
101+
},
102+
seed: {
103+
param: 'seed',
104+
},
105+
prediction: {
106+
param: 'prediction',
107+
},
90108
};
91109

92110
interface AzureOpenAIChatCompleteResponse extends ChatCompletionResponse {}

src/providers/google-vertex-ai/transformGenerationConfig.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export function transformGenerationConfig(params: Params) {
3535
schema = derefer(schema);
3636
delete schema['$defs'];
3737
}
38+
if (Object.hasOwn(schema, '$schema')) {
39+
delete schema['$schema'];
40+
}
3841
generationConfig['responseSchema'] = schema;
3942
}
4043

0 commit comments

Comments
 (0)