Skip to content

Commit 1f783b6

Browse files
authored
UI Initial Model Management Changes
1 parent 87b2085 commit 1f783b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+8172
-6544
lines changed

.eslintrc.json

+29-25
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
{
2-
"env": {
3-
"node": true
4-
},
52
"parser": "@typescript-eslint/parser",
6-
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
73
"parserOptions": {
8-
"ecmaVersion": 2020,
4+
"ecmaVersion": 12,
95
"sourceType": "module"
106
},
11-
"plugins": ["@typescript-eslint", "import"],
7+
"plugins": ["@stylistic", "@typescript-eslint"],
8+
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"],
129
"rules": {
13-
"max-len": [
10+
"eqeqeq": ["error", "smart"],
11+
"@stylistic/indent": "error",
12+
"@stylistic/quotes": ["error", "single"],
13+
"@stylistic/arrow-parens": "error",
14+
"@stylistic/arrow-spacing": "error",
15+
"@stylistic/brace-style": "error",
16+
"@stylistic/computed-property-spacing": ["error", "never"],
17+
"@stylistic/jsx-quotes": ["error", "prefer-single"],
18+
"@stylistic/keyword-spacing": [
1419
"error",
1520
{
16-
"code": 120,
17-
"ignoreComments": false,
18-
"ignoreStrings": false,
19-
"ignoreTemplateLiterals": false,
20-
"ignoreRegExpLiterals": false,
21-
"ignoreUrls": false
21+
"before": true
2222
}
2323
],
24-
"@typescript-eslint/no-explicit-any": "warn",
25-
"import/order": [
26-
"error",
27-
{
28-
"newlines-between": "always",
29-
"groups": ["builtin", "external", "internal", ["parent", "sibling", "index"]],
30-
"alphabetize": {
31-
"order": "asc",
32-
"caseInsensitive": true
33-
}
34-
}
35-
]
24+
"@stylistic/semi": "error",
25+
"@stylistic/space-before-function-paren": "error",
26+
"@stylistic/space-infix-ops": "error",
27+
"@stylistic/space-unary-ops": "error",
28+
"@typescript-eslint/no-unused-vars": "error",
29+
"@typescript-eslint/ban-types": "off",
30+
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
31+
"@typescript-eslint/no-non-null-assertion": "off",
32+
"@typescript-eslint/no-explicit-any": "off",
33+
"react-hooks/rules-of-hooks": "error",
34+
"react-hooks/exhaustive-deps": "error",
35+
"react/prop-types": "off"
36+
},
37+
"env": {
38+
"browser": true,
39+
"es2021": true
3640
}
3741
}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cdk.context.json
2323
.vscode
2424
.venv
2525
.DS_Store
26+
*.iml
2627

2728
# Coverage Statistic Folders
2829
coverage

.pre-commit-config.yaml

-14
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,6 @@ repos:
4848
args: ['--skip=*.git*,*cdk.out*,*venv*,*mypy_cache*,*package-lock*,*node_modules*,*dist/*,*poetry.lock*,*coverage*', "-L=xdescribe"]
4949
pass_filenames: false
5050

51-
- repo: https://github.com/pre-commit/mirrors-prettier
52-
rev: 'v3.0.3'
53-
hooks:
54-
- id: prettier
55-
name: prettier-md
56-
files: .*\.(ya?ml|json|md)$
57-
exclude: ^node_modules/|config.yaml|^dist/
58-
59-
- id: prettier
60-
name: prettier-ts
61-
args: [--write, --config, .prettierrc]
62-
files: \.(ts|tsx)$
63-
exclude: ^node_modules/|^dist/
64-
6551
- repo: https://github.com/pycqa/isort
6652
rev: 5.12.0
6753
hooks:

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ LISA accelerates the use of generative AI applications by providing scalable, lo
2626
LISA was inspired by another AWS open source project [aws-genai-llm-chatbot](https://github.com/aws-samples/aws-genai-llm-chatbot) and deploys LLMs using the [text-generation-inference](https://github.com/huggingface/text-generation-inference/tree/main) container from HuggingFace. LISA is different from it's inspiration in a few ways:
2727

2828
1. LISA is designed to operate in Amazon Dedicated Cloud (ADC) partitions.
29-
2. LISA is designed to be composable so we've separated the the underlying LLM serving capability, this repository contains, LISA-Serve and the chat frontend, LISA-Chat, which are deployable as separate stacks.
29+
2. LISA is designed to be composable so we've separated the underlying LLM serving capability, this repository contains, LISA-Serve and the chat frontend, LISA-Chat, which are deployable as separate stacks.
3030
3. LISA is designed to support the OpenAI specification, so anywhere you can use the OpenAI API in your applications, you can insert LISA in its place.
3131

3232
## Deprecation Notes
@@ -488,7 +488,9 @@ Create `lib/user-interface/react/public/env.js` file with the following contents
488488
window.env = {
489489
AUTHORITY: '<Your IdP URL here>',
490490
CLIENT_ID: '<Your IdP Client Id Here>',
491-
"CUSTOM_SCOPES":[<add your optional list of custom scopes to pull groups from your IdP here>],
491+
JWT_GROUPS_PROP: '<The full path (period delimited) to the property for the groups that a user is a member of in the JWT token. For Cognito: cognito:groups>',
492+
ADMIN_GROUP: '<The admin group you would like LISA to check the JWT token for>',
493+
CUSTOM_SCOPES:[<add your optional list of custom scopes to pull groups from your IdP here>],
492494
// Alternatively you can set this to be your REST api elb endpoint
493495
RESTAPI_URI: 'http://localhost:8080/',
494496
RESTAPI_VERSION: 'v2',

bin/lisa.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -35,57 +35,57 @@ let configEnv = configFile.env || 'dev';
3535

3636
// Select configuration environment
3737
if (process.env.ENV) {
38-
configEnv = process.env.ENV;
38+
configEnv = process.env.ENV;
3939
}
4040
const configData = configFile[configEnv];
4141
if (!configData) {
42-
throw new Error(`Configuration for environment "${configEnv}" not found.`);
42+
throw new Error(`Configuration for environment "${configEnv}" not found.`);
4343
}
4444

4545
// Other command line argument overrides
4646
type EnvMapping = [string, keyof Config];
4747
const mappings: EnvMapping[] = [
48-
['PROFILE', 'profile'],
49-
['DEPLOYMENT_NAME', 'deploymentName'],
50-
['ACCOUNT_NUMBER', 'accountNumber'],
51-
['REGION', 'region'],
48+
['PROFILE', 'profile'],
49+
['DEPLOYMENT_NAME', 'deploymentName'],
50+
['ACCOUNT_NUMBER', 'accountNumber'],
51+
['REGION', 'region'],
5252
];
5353
mappings.forEach(([envVar, configVar]) => {
54-
const envValue = process.env[envVar];
55-
if (envValue) {
56-
(configData as any)[configVar] = envValue;
57-
}
54+
const envValue = process.env[envVar];
55+
if (envValue) {
56+
(configData as any)[configVar] = envValue;
57+
}
5858
});
5959

6060
// Validate and parse configuration
6161
let config: Config;
6262
try {
63-
config = ConfigSchema.parse(configData);
63+
config = ConfigSchema.parse(configData);
6464
} catch (error) {
65-
if (error instanceof Error) {
66-
console.error('Error parsing the configuration:', error.message);
67-
} else {
68-
console.error('An unexpected error occurred:', error);
69-
}
70-
process.exit(1);
65+
if (error instanceof Error) {
66+
console.error('Error parsing the configuration:', error.message);
67+
} else {
68+
console.error('An unexpected error occurred:', error);
69+
}
70+
process.exit(1);
7171
}
7272

7373
// Define environment
7474
const env: cdk.Environment = {
75-
account: config.accountNumber,
76-
region: config.region,
75+
account: config.accountNumber,
76+
region: config.region,
7777
};
7878

7979
// Application
8080
const app = new cdk.App();
8181
// Run CDK-nag on app if specified
8282
if (config.runCdkNag) {
83-
Aspects.of(app).add(new AwsSolutionsChecks({ reports: true, verbose: true }));
83+
Aspects.of(app).add(new AwsSolutionsChecks({ reports: true, verbose: true }));
8484
}
8585

8686
new LisaServeApplicationStage(app, config.deploymentStage, {
87-
env: env,
88-
config: config,
87+
env: env,
88+
config: config,
8989
});
9090

9191
app.synth();

jest.config.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
limitations under the License.
1515
*/
1616

17+
// eslint-disable-next-line no-undef
1718
module.exports = {
18-
testEnvironment: 'node',
19-
roots: ['<rootDir>/test'],
20-
testMatch: ['**/*.test.ts'],
21-
transform: {
22-
'^.+\\.tsx?$': 'ts-jest'
23-
},
24-
collectCoverage: true,
19+
testEnvironment: 'node',
20+
roots: ['<rootDir>/test'],
21+
testMatch: ['**/*.test.ts'],
22+
transform: {
23+
'^.+\\.tsx?$': 'ts-jest'
24+
},
25+
collectCoverage: true,
2526
};

lambda/authorizer/lambda_functions.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import logging
1717
import os
1818
import ssl
19-
from typing import Any, Dict, Union
19+
from typing import Any, Dict
2020

2121
import create_env_variables # noqa: F401
2222
import jwt
@@ -51,7 +51,7 @@ def lambda_handler(event: Dict[str, Any], context) -> Dict[str, Any]: # type: i
5151
if jwt_data := id_token_is_valid(id_token=id_token, client_id=client_id, authority=authority):
5252
is_admin_user = is_admin(jwt_data, admin_group, jwt_groups_property)
5353
allow_policy = generate_policy(effect="Allow", resource=event["methodArn"], username=jwt_data["sub"])
54-
allow_policy["context"] = {"username": jwt_data["sub"]} # type: ignore [index]
54+
allow_policy["context"] = {"username": jwt_data["sub"]}
5555

5656
if requested_resource.startswith("/models") and not is_admin_user:
5757
username = jwt_data.get("sub", "user")
@@ -78,11 +78,11 @@ def generate_policy(*, effect: str, resource: str, username: str = "username") -
7878
return policy
7979

8080

81-
def id_token_is_valid(*, id_token: str, client_id: str, authority: str) -> Union[Dict[str, Any], bool]:
81+
def id_token_is_valid(*, id_token: str, client_id: str, authority: str) -> Dict[str, Any] | None:
8282
"""Check whether an ID token is valid and return decoded data."""
8383
if not jwt.algorithms.has_crypto:
8484
logger.error("No crypto support for JWT, please install the cryptography dependency")
85-
return False
85+
return None
8686
logger.info(f"{authority}/.well-known/openid-configuration")
8787

8888
# Here we will point to the sponsor bundle if available, defined in the create_env_variables import above
@@ -94,7 +94,7 @@ def id_token_is_valid(*, id_token: str, client_id: str, authority: str) -> Union
9494
)
9595
if resp.status_code != 200:
9696
logger.error("Could not get OIDC metadata: %s", resp.content)
97-
return False
97+
return None
9898

9999
oidc_metadata = resp.json()
100100
try:
@@ -121,10 +121,10 @@ def id_token_is_valid(*, id_token: str, client_id: str, authority: str) -> Union
121121
return data
122122
except jwt.exceptions.PyJWTError as e:
123123
logger.exception(e)
124-
return False
124+
return None
125125

126126

127-
def is_admin(jwt_data: dict[str, Any], admin_group: str, jwt_groups_property: str):
127+
def is_admin(jwt_data: dict[str, Any], admin_group: str, jwt_groups_property: str) -> bool:
128128
"""Check if the user is an admin."""
129129
props = jwt_groups_property.split(".")
130130
current_node = jwt_data

lambda/models/lambda_functions.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@
4646
)
4747

4848

49-
@app.post(path="", include_in_schema=False)
50-
@app.post(path="/")
49+
@app.post(path="", include_in_schema=False) # type: ignore
50+
@app.post(path="/") # type: ignore
5151
async def create_model(model: CreateModelRequest) -> CreateModelResponse:
5252
"""Endpoint to create a model."""
5353
# TODO add service to create model
5454
return CreateModelResponse(ModelId=model.ModelId, ModelName=model.ModelName, Status=ModelStatus.CREATING)
5555

5656

57-
@app.get(path="", include_in_schema=False)
58-
@app.get(path="/")
57+
@app.get(path="", include_in_schema=False) # type: ignore
58+
@app.get(path="/") # type: ignore
5959
async def list_models() -> list[ListModelResponse]:
6060
"""Endpoint to list models."""
6161
# TODO add service to list models
@@ -81,7 +81,7 @@ async def list_models() -> list[ListModelResponse]:
8181
]
8282

8383

84-
@app.get(path="/{model_id}")
84+
@app.get(path="/{model_id}") # type: ignore
8585
async def get_model(
8686
model_id: Annotated[str, Path(title="The name of the model to get")],
8787
) -> DescribeModelResponse:
@@ -90,7 +90,7 @@ async def get_model(
9090
return DescribeModelResponse.DUMMY(model_id, model_id)
9191

9292

93-
@app.put(path="/{model_id}")
93+
@app.put(path="/{model_id}") # type: ignore
9494
async def put_model(
9595
model_id: Annotated[str, Path(title="The name of the model to update")], model: UpdateModelRequest
9696
) -> DescribeModelResponse:
@@ -100,7 +100,7 @@ async def put_model(
100100
return DescribeModelResponse(**model.model_dump())
101101

102102

103-
@app.put(path="/{model_id}/start")
103+
@app.put(path="/{model_id}/start") # type: ignore
104104
async def start_model(model_id: Annotated[str, Path(title="The name of the model to start")]) -> ListModelResponse:
105105
"""Endpoint to start a model."""
106106
# TODO add service to update model
@@ -115,7 +115,7 @@ async def start_model(model_id: Annotated[str, Path(title="The name of the model
115115
)
116116

117117

118-
@app.put(path="/{model_id}/stop")
118+
@app.put(path="/{model_id}/stop") # type: ignore
119119
async def stop_model(model_id: Annotated[str, Path(title="The name of the model to stop")]) -> ListModelResponse:
120120
"""Endpoint to stop a model."""
121121
# TODO add service to update model
@@ -130,7 +130,7 @@ async def stop_model(model_id: Annotated[str, Path(title="The name of the model
130130
)
131131

132132

133-
@app.delete(path="/{model_id}")
133+
@app.delete(path="/{model_id}") # type: ignore
134134
async def delete_model(
135135
model_id: Annotated[str, Path(title="The name of the model to delete")],
136136
) -> DeleteModelResponse:

0 commit comments

Comments
 (0)