Skip to content

Commit 3b70c48

Browse files
authored
Merge pull request #140 from aws-samples/contract_compliance_nova
feat(sample): update contract compliance deps and enable Amazon Nova model support
2 parents 33fb6b1 + f76bbde commit 3b70c48

File tree

11 files changed

+98
-33
lines changed

11 files changed

+98
-33
lines changed

samples/contract-compliance-analysis/back-end/README.md

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Contract Compliance Analysis - Back-end
22

3+
## Table of contents
4+
5+
- [Basic setup](#basic-setup)
6+
- [Local environment or Cloud9](#local-environment-or-cloud9)
7+
- [Cloud9 environment (optional)](#cloud9-setup-optional)
8+
- [Setup steps](#setup-steps)
9+
- [How to customize contract analysis according to your use case](#how-to-customize-contract-analysis-according-to-your-use-case)
10+
- [How to use a different Amazon Bedrock FM](#how-to-use-a-different-amazon-bedrock-fm)
11+
312
## Basic setup
413

514
### Local environment or Cloud9
@@ -9,8 +18,6 @@ You have the option of running the setup from a local workspace or from a Cloud9
918
In case you opt for Cloud9, you have to setup a Cloud9 environment in the same AWS Account where this Backend will
1019
be installed.
1120

12-
If your local workspace has a non-x86 processor architecture (for instance ARM, like the M processor from Macbooks), it's strongly recommended to perform the setup steps from a Cloud9 environment, to avoid bundling issues of Lambda function dependencies (see [ticket](https://github.com/awslabs/generative-ai-cdk-constructs/issues/541)). Otherwise, set the `DOCKER_DEFAULT_PLATFORM` environmental variable to `linux/amd64` to build `x86_64` packages.
13-
1421
#### Cloud9 setup (optional)
1522

1623
1. Follow the steps on [https://docs.aws.amazon.com/cloud9/latest/user-guide/setting-up.html](https://docs.aws.amazon.com/cloud9/latest/user-guide/setting-up.html)
@@ -88,16 +95,12 @@ cdk bootstrap
8895
cdk deploy --require-approval=never
8996
```
9097

91-
> Use `DOCKER_DEFAULT_PLATFORM=linux/amd64 cdk deploy --require-approval=never` on macOS
92-
9398
2. Any modifications made to the code can be applied to the deployed stack by running the same command again.
9499

95100
```shell
96101
cdk deploy --require-approval=never
97102
```
98103

99-
> Use `DOCKER_DEFAULT_PLATFORM=linux/amd64 cdk deploy --require-approval=never` on macOS
100-
101104
#### Populate Guidelines table
102105

103106
Once the Stack is setup, you need to populate the DynamoDB Guidelines table with the data from the Guidelines Excel sheet that is included in the `guidelines` folder.
@@ -137,7 +140,7 @@ Click the **Enable specific models** button and enable the checkbox for Anthropi
137140

138141
Click **Next** and **Submit** buttons
139142

140-
## How to customize contract analysis accordding to your use case
143+
## How to customize contract analysis according to your use case
141144

142145
This solution was designed to support analysis of contracts of different types and of different languages, based on the assumption that the contracts establish an agreement between two parties: a given company and another party. The solution already comes pre-configured to analyze service contract contracts in English for the company *AnyCompany*, together with an example of guidelines.
143146

@@ -164,3 +167,49 @@ The recommended sequence of steps:
164167
```shell
165168
python load_guidelines.py --guidelines_file_path <custom_guidelines_file_path>
166169
```
170+
171+
## How to use a different Amazon Bedrock FM
172+
173+
By default, the application uses Anthropic Claude 3 Haiku v1. Here are steps explaining how to update the model to use. For this example, we will use [Amazon Nova Pro v1](https://aws.amazon.com/blogs/aws/introducing-amazon-nova-frontier-intelligence-and-industry-leading-price-performance/):
174+
175+
- Open the [app_properties.yaml](./app_properties.yaml) file and update the field ```claude_model_id``` to use the model you selected. In this case, we update the field to ```us.amazon.nova-pro-v1:0```. Replace it with the model id you want to use. The list of model ids available through Amazon Bedrock is available in the [documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html). Ensure the model you are selecting is enabled in the console (Amazon Bedrock -> Model access) and available in your region.
176+
- Depending on the model selected, you might need to update some hardcoded values regarding the max number of new tokens generated. For instance, Amazon Nova Pro v1 supports 5000 output tokens, which doesn't require any modifications. However, some models might have a max output tokens of 3000, which requires some changes in the sample. Update the following lines if required:
177+
- In file [fn-preprocess-contract/index.py](./stack/sfn/preprocessing/fn-preprocess-contract/index.py), update line 96 to change the chunks size to a value smaller than the max tokens output for your model, as well as line 107 to match your model's max output tokens.
178+
- In file [scripts/utils/llm.py](./scripts/utils/llm.py), update the max tokens output line 28.
179+
- In file [common-layer/llm.py](./stack/sfn/common-layer/llm.py) update the max tokens output line 30.
180+
- In file [fn-classify-clauses/index.py](.stack/sfn/classification/fn-classify-clauses/index.py), update line 182 the max tokens output for your model
181+
- Re-deploy the solution as described in previous sections
182+
183+
### Troubleshooting
184+
185+
#### KeyError in step X
186+
187+
If you change the model, it is possible that you face an error in the step function run. This can be due to the parsing of the LLM response.
188+
In that case, identify the failing lambda function from the step functions logs, and update the dedicated lambda function code to enable verbose messaging. For instance, if the failing lambda function is ```PreprocessingStepPreproc```, open the file [fn-preprocess-contract/index.py](./stack/sfn/preprocessing/fn-preprocess-contract/index.py) and update the invoke_llm code:
189+
190+
```python
191+
llm_response, model_usage, stop_reason = invoke_llm(
192+
prompt=PROMPT_TEMPLATE.format(CONTRACT_EXCERPT=contract_excerpt),
193+
model_id=prompt_vars_dict.get("claude_model_id", ''),
194+
temperature=0.0,
195+
top_p=0.999,
196+
max_new_tokens=4096,
197+
verbose=True # <- turn on verbose mode
198+
)
199+
```
200+
201+
Then, modify the file [common-layer/llm.py](./stack/sfn/common-layer/llm.py) and print the response from the runnable invocation:
202+
203+
```python
204+
response = chain.invoke({})
205+
logger.info(f"Model response: {response}") # <- log the response
206+
content = response.content
207+
```
208+
209+
Re-deploy the solution, and verify in the logs the structure of the response. Depending on the model used, it is possible that the schema of the reponse is different, thus the ```usage``` and ```stop reason``` values might require to be parsed differently. In that case, add the correct code in the file [common-layer/llm.py](./stack/sfn/common-layer/llm.py):
210+
211+
```python
212+
if ('mymodel' in model_id):
213+
usage_data = response.XXX # <- specify how to parse usage data
214+
stop_reason = response.XXX # <- specify how to parse stop reason
215+
```
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
aws-cdk-lib==2.144.0
2-
aws_cdk.aws_lambda_python_alpha==2.144.0a0
1+
aws-cdk-lib==2.166.0
2+
aws_cdk.aws_lambda_python_alpha==2.166.0a0
33
constructs>=10.0.0,<11.0.0
4-
cdk_nag==2.28.145
5-
openpyxl==3.1.3
6-
boto3==1.34.106
7-
pandas==2.2.2
8-
awswrangler==3.8.0
4+
cdk_nag==2.34.16
5+
openpyxl==3.1.5
6+
boto3==1.35.76
7+
pandas==2.2.3
8+
awswrangler==3.10.1
99
argparse==1.4.0
1010
retrying==1.3.4
11-
PyYAML==6.0.1
12-
cdklabs.generative_ai_cdk_constructs==0.1.198
11+
PyYAML==6.0.2

samples/contract-compliance-analysis/back-end/stack/sfn/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232
from .evaluation import EvaluationStep
3333
from .risk import RiskStep
3434

35-
from cdklabs.generative_ai_cdk_constructs import LangchainCommonDepsLayer
36-
37-
3835
class StepFunctionsStack(NestedStack):
3936

4037
def __init__(
@@ -50,12 +47,13 @@ def __init__(
5047
):
5148
super().__init__(scope, id, **kwargs)
5249

53-
self.langchain_deps_layer = LangchainCommonDepsLayer(
50+
self.langchain_deps_layer = lambda_python.PythonLayerVersion(
5451
self,
55-
"LangChainDependenciesLayer",
56-
runtime=lambda_.Runtime.PYTHON_3_12,
57-
architecture=lambda_.Architecture.X86_64
58-
).layer
52+
'LangChainDependenciesLayer',
53+
entry=os.path.join(os.path.dirname(__file__), "langchain-deps-layer"),
54+
compatible_architectures=[lambda_.Architecture.X86_64],
55+
compatible_runtimes=[lambda_.Runtime.PYTHON_3_12],
56+
)
5957

6058
self.common_layer = lambda_python.PythonLayerVersion(
6159
self,

samples/contract-compliance-analysis/back-end/stack/sfn/classification/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from constructs import Construct
1616
from aws_cdk import (
1717
Duration,
18+
Aws,
1819
aws_lambda as lambda_,
1920
aws_stepfunctions as sfn,
2021
aws_stepfunctions_tasks as tasks,
@@ -75,7 +76,8 @@ def __init__(
7576
"bedrock:InvokeModel",
7677
],
7778
resources=[
78-
"arn:aws:bedrock:*::foundation-model/anthropic*"
79+
"arn:aws:bedrock:*::foundation-model/*",
80+
"arn:aws:bedrock:*:"+Aws.ACCOUNT_ID+":inference-profile/*",
7981
]
8082
))
8183

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
more-itertools==10.3.0
1+
more-itertools==10.5.0

samples/contract-compliance-analysis/back-end/stack/sfn/common-layer/llm.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,19 @@ def invoke_llm(prompt, model_id, temperature=0.5, top_k=None, top_p=0.8, max_new
6060
response = chain.invoke({})
6161
content = response.content
6262

63+
usage_data = None
64+
stop_reason = None
65+
66+
if ('anthropic' in model_id):
67+
usage_data = response.response_metadata['usage']
68+
stop_reason = response.response_metadata['stop_reason']
69+
elif('amazon.nova' in model_id):
70+
usage_data = response.usage_metadata
71+
stop_reason = response.response_metadata['stopReason']
72+
6373
if verbose:
6474
logger.info(f"Model response: {content}")
65-
logger.info(f"Model usage: {response.response_metadata['usage']}")
66-
logger.info(f"Model stop_reason: {response.response_metadata['stop_reason']}")
75+
logger.info(f"Model usage: {usage_data}")
76+
logger.info(f"Model stop_reason: {stop_reason}")
6777

68-
return content, response.response_metadata['usage'], response.response_metadata["stop_reason"]
78+
return content, usage_data, stop_reason

samples/contract-compliance-analysis/back-end/stack/sfn/evaluation/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from constructs import Construct
1616
from aws_cdk import (
1717
Duration,
18+
Aws,
1819
aws_lambda as lambda_,
1920
aws_stepfunctions as sfn,
2021
aws_stepfunctions_tasks as tasks,
@@ -74,7 +75,8 @@ def __init__(
7475
"bedrock:InvokeModel",
7576
],
7677
resources=[
77-
"arn:aws:bedrock:*::foundation-model/anthropic*"
78+
"arn:aws:bedrock:*::foundation-model/*",
79+
"arn:aws:bedrock:*:"+Aws.ACCOUNT_ID+":inference-profile/*",
7880
]
7981
))
8082

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
langchain==0.3.9
2+
langchain-community==0.3.9
3+
langchain-aws==0.2.9

samples/contract-compliance-analysis/back-end/stack/sfn/preprocessing/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from constructs import Construct
1616
from aws_cdk import (
1717
Duration,
18+
Aws,
1819
aws_lambda as lambda_,
1920
aws_stepfunctions as sfn,
2021
aws_stepfunctions_tasks as tasks,
@@ -85,7 +86,8 @@ def __init__(
8586
"bedrock:InvokeModel",
8687
],
8788
resources=[
88-
"arn:aws:bedrock:*::foundation-model/anthropic*"
89+
"arn:aws:bedrock:*::foundation-model/*",
90+
"arn:aws:bedrock:*:"+Aws.ACCOUNT_ID+":inference-profile/*",
8991
]
9092
))
9193

samples/contract-compliance-analysis/back-end/stack/sfn/preprocessing/fn-preprocess-contract/index.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def handler(event, context):
156156
))
157157
# Merge the contracts, accepting only the inclusion of separators
158158
merged_contract = "".join([
159-
diff[2:] if diff.startswith(" ") or diff.startswith("- ") or diff.startswith(f"+ {CLAUSE_SEPARATOR}\n") else ""
159+
diff[2:] if diff.startswith(" ") or diff.startswith("- ") or diff.startswith(f"+ {CLAUSE_SEPARATOR}\n") or diff.startswith(f"+ {CLAUSE_SEPARATOR} \n") else ""
160160
for diff in diffs
161161
])
162162
# Get each individual clause and insert into table

0 commit comments

Comments
 (0)