aws-genai-llm-chatbot
177 строк · 5.2 Кб
1import * as ec2 from "aws-cdk-lib/aws-ec2";
2import * as oss from "aws-cdk-lib/aws-opensearchserverless";
3import * as sfn from "aws-cdk-lib/aws-stepfunctions";
4import * as iam from "aws-cdk-lib/aws-iam";
5import { Construct } from "constructs";
6import { Shared } from "../../shared";
7import { SystemConfig } from "../../shared/types";
8import { Utils } from "../../shared/utils";
9import { RagDynamoDBTables } from "../rag-dynamodb-tables";
10import { CreateOpenSearchWorkspace } from "./create-opensearch-workspace";
11
12export interface OpenSearchVectorProps {
13readonly config: SystemConfig;
14readonly shared: Shared;
15readonly ragDynamoDBTables: RagDynamoDBTables;
16}
17
18export class OpenSearchVector extends Construct {
19public readonly openSearchCollectionName: string;
20public readonly openSearchCollectionEndpoint: string;
21public readonly openSearchCollection: oss.CfnCollection;
22public readonly createOpenSearchWorkspaceWorkflow: sfn.StateMachine;
23public addToAccessPolicy: (
24name: string,
25principal: (string | undefined)[],
26permission: string[]
27) => void;
28
29constructor(scope: Construct, id: string, props: OpenSearchVectorProps) {
30super(scope, id);
31
32const collectionName = Utils.getName(
33props.config,
34"genaichatbot-workspaces"
35);
36
37const sg = new ec2.SecurityGroup(this, "SecurityGroup", {
38vpc: props.shared.vpc,
39});
40
41sg.addIngressRule(
42ec2.Peer.ipv4(props.shared.vpc.vpcCidrBlock),
43ec2.Port.tcp(443)
44);
45
46const cfnVpcEndpoint = new oss.CfnVpcEndpoint(this, "VpcEndpoint", {
47name: Utils.getName(props.config, "genaichatbot-vpce"),
48// Make sure the subnets are not in the same availability zone.
49subnetIds: props.shared.vpc.selectSubnets({
50onePerAz: true,
51subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
52}).subnetIds,
53vpcId: props.shared.vpc.vpcId,
54securityGroupIds: [sg.securityGroupId],
55});
56
57const cfnNetworkSecurityPolicy = new oss.CfnSecurityPolicy(
58this,
59"NetworkSecurityPolicy",
60{
61name: Utils.getName(props.config, "genaichatbot-network-policy"),
62type: "network",
63policy: JSON.stringify([
64{
65Rules: [
66{
67ResourceType: "collection",
68Resource: [`collection/${collectionName}`],
69},
70],
71AllowFromPublic: false,
72SourceVPCEs: [cfnVpcEndpoint.attrId],
73},
74]).replace(/(\r\n|\n|\r)/gm, ""),
75}
76);
77
78cfnNetworkSecurityPolicy.node.addDependency(cfnVpcEndpoint);
79
80const cfnEncryptionSecurityPolicy = new oss.CfnSecurityPolicy(
81this,
82"EncryptionSecurityPolicy",
83{
84name: Utils.getName(props.config, "genaichatbot-encryption-policy", 32),
85type: "encryption",
86policy: JSON.stringify({
87Rules: [
88{
89ResourceType: "collection",
90Resource: [`collection/${collectionName}`],
91},
92],
93AWSOwnedKey: true,
94}).replace(/(\r\n|\n|\r)/gm, ""),
95}
96);
97
98cfnEncryptionSecurityPolicy.node.addDependency(cfnNetworkSecurityPolicy);
99
100const cfnCollection = new oss.CfnCollection(this, "OpenSearchCollection", {
101name: collectionName,
102type: "VECTORSEARCH",
103});
104
105const createWorkflow = new CreateOpenSearchWorkspace(
106this,
107"CreateOpenSearchWorkspace",
108{
109config: props.config,
110shared: props.shared,
111ragDynamoDBTables: props.ragDynamoDBTables,
112openSearchCollectionName: collectionName,
113openSearchCollection: cfnCollection,
114collectionEndpoint: cfnCollection.attrCollectionEndpoint,
115}
116);
117
118cfnCollection.node.addDependency(cfnNetworkSecurityPolicy);
119cfnCollection.node.addDependency(cfnEncryptionSecurityPolicy);
120
121this.addToAccessPolicyIntl(
122props.config,
123collectionName,
124"create-workflow",
125[createWorkflow.createWorkspaceRole?.roleArn],
126[
127"aoss:CreateIndex",
128"aoss:DeleteIndex",
129"aoss:UpdateIndex",
130"aoss:DescribeIndex",
131]
132);
133
134this.addToAccessPolicy = (
135name: string,
136principal: (string | undefined)[],
137permission: string[]
138) => {
139this.addToAccessPolicyIntl(
140props.config,
141collectionName,
142name,
143principal,
144permission
145);
146};
147
148this.createOpenSearchWorkspaceWorkflow = createWorkflow.stateMachine;
149this.openSearchCollectionEndpoint = cfnCollection.attrCollectionEndpoint;
150this.openSearchCollection = cfnCollection;
151}
152
153private addToAccessPolicyIntl(
154config: SystemConfig,
155collectionName: string,
156name: string,
157principal: (string | undefined)[],
158permission: string[]
159) {
160new oss.CfnAccessPolicy(this, `AccessPolicy-${name}`, {
161name: Utils.getName(config, `access-policy-${name}`, 32),
162type: "data",
163policy: JSON.stringify([
164{
165Rules: [
166{
167ResourceType: "index",
168Resource: [`index/${collectionName}/*`],
169Permission: permission,
170},
171],
172Principal: principal,
173},
174]).replace(/(\r\n|\n|\r)/gm, ""),
175});
176}
177}
178