diff --git a/docs/DEPLOY_OPTION.md b/docs/DEPLOY_OPTION.md
index a0160a12..0dd45cea 100644
--- a/docs/DEPLOY_OPTION.md
+++ b/docs/DEPLOY_OPTION.md
@@ -332,3 +332,18 @@ context の `dashboard` に `true` を設定します。(デフォルトは `fal
続いて、Amazon Bedrock のログの出力を設定します。[Amazon Bedrock の Settings](https://console.aws.amazon.com/bedrock/home#settings) を開き、Model invocation logging を有効化します。Select the logging destinations には CloudWatch Logs only を選択してください。(S3 にも出力したい場合、Both S3 and CloudWatch Logs を選択しても構いません。) また、Log group name には `npm run cdk:deploy` 時に出力された `GenerativeAiUseCasesDashboardStack.BedrockLogGroup` を指定してください。(例: `GenerativeAiUseCasesDashboardStack-LogGroupAAAAAAAA-BBBBBBBBBBBB`) Service role は任意の名前で新規に作成してください。なお、Model invocation logging の設定は、context で `modelRegion` として指定しているリージョンで行うことに留意してください。
設定完了後、`npm run cdk:deploy` 時に出力された `GenerativeAiUseCasesDashboardStack.DashboardUrl` を開いてください。
+
+## ファイルアップロード機能の有効化
+
+PDF や Excel などのファイルをアップロードしてテキストを抽出する、ファイルアップロード機能を利用することができます。対応しているファイルは、csv, doc, docx, md, pdf, ppt, pptx, tsv, xlsx です。
+
+**[packages/cdk/cdk.json](/packages/cdk/cdk.json) を編集**
+```
+{
+ "context": {
+ "recognizeFileEnabled": true
+ }
+}
+```
+
+ファイルアップロード機能は ECS (Fargate) 上で実行されます。そのため、有効化すると VPC が新たに作成されます。
\ No newline at end of file
diff --git a/packages/cdk/cdk.json b/packages/cdk/cdk.json
index 82bd6a67..a4d15ed1 100644
--- a/packages/cdk/cdk.json
+++ b/packages/cdk/cdk.json
@@ -37,13 +37,13 @@
"agentRegion": "us-east-1",
"searchAgentEnabled": false,
"searchApiKey": "",
- "agents": [
- ],
+ "agents": [],
"allowedIpV4AddressRanges": null,
"allowedIpV6AddressRanges": null,
"allowedCountryCodes": null,
"dashboard": false,
"anonymousUsageTracking": true,
+ "recognizeFileEnabled": false,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
@@ -81,4 +81,4 @@
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true
}
-}
+}
\ No newline at end of file
diff --git a/packages/cdk/lib/construct/index.ts b/packages/cdk/lib/construct/index.ts
index e09ee4ca..2afa2d94 100644
--- a/packages/cdk/lib/construct/index.ts
+++ b/packages/cdk/lib/construct/index.ts
@@ -6,4 +6,4 @@ export * from './rag';
export * from './transcribe';
export * from './common-web-acl';
export * from './agent';
-export * from './file';
+export * from './recognize-file';
diff --git a/packages/cdk/lib/construct/file.ts b/packages/cdk/lib/construct/recognize-file.ts
similarity index 79%
rename from packages/cdk/lib/construct/file.ts
rename to packages/cdk/lib/construct/recognize-file.ts
index 554bb2b6..8aab963f 100644
--- a/packages/cdk/lib/construct/file.ts
+++ b/packages/cdk/lib/construct/recognize-file.ts
@@ -1,6 +1,8 @@
import { Duration, RemovalPolicy } from 'aws-cdk-lib';
import {
AuthorizationType,
+ CfnMethod,
+ CfnStage,
CognitoUserPoolsAuthorizer,
ConnectionType,
Integration,
@@ -30,13 +32,13 @@ import { LogGroup } from 'aws-cdk-lib/aws-logs';
import { NetworkLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';
import { ServiceLinkedRole } from 'upsert-slr';
-export interface FileProps {
+export interface RecognizeFileProps {
userPool: UserPool;
api: RestApi;
}
-export class File extends Construct {
- constructor(scope: Construct, id: string, props: FileProps) {
+export class RecognizeFile extends Construct {
+ constructor(scope: Construct, id: string, props: RecognizeFileProps) {
super(scope, id);
// S3 (File Bucket)
@@ -124,6 +126,7 @@ export class File extends Construct {
memoryLimitMiB: 1024,
cpu: 512,
taskDefinition: taskDefinition,
+ publicLoadBalancer: false,
}
);
@@ -138,6 +141,11 @@ export class File extends Construct {
});
// API Gateway
+ const stage = props.api.deploymentStage.node.defaultChild as CfnStage;
+ stage.variables = {
+ vpcLinkId: link.vpcLinkId,
+ };
+
const authorizer = new CognitoUserPoolsAuthorizer(this, 'Authorizer', {
cognitoUserPools: [props.userPool],
});
@@ -150,7 +158,7 @@ export class File extends Construct {
const fileResource = props.api.root.addResource('file');
// POST: /file/recognize
- fileResource.addResource('recognize').addMethod(
+ const recognizeMethod = fileResource.addResource('recognize').addMethod(
'POST',
new Integration({
type: IntegrationType.HTTP_PROXY,
@@ -163,6 +171,16 @@ export class File extends Construct {
commonAuthorizerProps
);
+ // REST API の Method で VPC Link を参照すると、正しく削除できない問題がある。
+ // ステージ変数を利用するとその問題を回避できるため、ConnectionId (ここでは VPC Link ID) をステージ変数経由で使うように変更している。
+ // (L2 Construct では ConnectionId を設定できないため、L1 Construct のプロパティを直接変更している)
+ // 参考: https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-known-issues.html
+ const cfnRecognizeMethod = recognizeMethod.node.defaultChild as CfnMethod;
+ cfnRecognizeMethod.addPropertyOverride(
+ 'Integration.ConnectionId',
+ '${stageVariables.vpcLinkId}'
+ );
+
// POST: /file/url
fileResource
.addResource('url')
diff --git a/packages/cdk/lib/construct/web.ts b/packages/cdk/lib/construct/web.ts
index 54d9631a..0e03882a 100644
--- a/packages/cdk/lib/construct/web.ts
+++ b/packages/cdk/lib/construct/web.ts
@@ -23,6 +23,7 @@ export interface WebProps {
samlCognitoDomainName: string;
samlCognitoFederatedIdentityProviderName: string;
agentNames: string[];
+ recognizeFileEnabled: boolean;
}
export class Web extends Construct {
@@ -117,9 +118,12 @@ export class Web extends Construct {
VITE_APP_IMAGE_MODEL_IDS: JSON.stringify(props.imageGenerationModelIds),
VITE_APP_ENDPOINT_NAMES: JSON.stringify(props.endpointNames),
VITE_APP_SAMLAUTH_ENABLED: props.samlAuthEnabled.toString(),
- VITE_APP_SAML_COGNITO_DOMAIN_NAME: props.samlCognitoDomainName.toString(),
- VITE_APP_SAML_COGNITO_FEDERATED_IDENTITY_PROVIDER_NAME: props.samlCognitoFederatedIdentityProviderName.toString(),
+ VITE_APP_SAML_COGNITO_DOMAIN_NAME:
+ props.samlCognitoDomainName.toString(),
+ VITE_APP_SAML_COGNITO_FEDERATED_IDENTITY_PROVIDER_NAME:
+ props.samlCognitoFederatedIdentityProviderName.toString(),
VITE_APP_AGENT_NAMES: JSON.stringify(props.agentNames),
+ VITE_APP_RECOGNIZE_FILE_ENABLED: props.recognizeFileEnabled.toString(),
},
});
diff --git a/packages/cdk/lib/generative-ai-use-cases-stack.ts b/packages/cdk/lib/generative-ai-use-cases-stack.ts
index 6ba027be..04c0d1ce 100644
--- a/packages/cdk/lib/generative-ai-use-cases-stack.ts
+++ b/packages/cdk/lib/generative-ai-use-cases-stack.ts
@@ -8,7 +8,7 @@ import {
Rag,
Transcribe,
CommonWebAcl,
- File,
+ RecognizeFile,
} from './construct';
import { CfnWebACLAssociation } from 'aws-cdk-lib/aws-wafv2';
import * as cognito from 'aws-cdk-lib/aws-cognito';
@@ -53,6 +53,9 @@ export class GenerativeAiUseCasesStack extends Stack {
const samlCognitoFederatedIdentityProviderName: string =
this.node.tryGetContext('samlCognitoFederatedIdentityProviderName')!;
const agentEnabled = this.node.tryGetContext('agentEnabled') || false;
+ const recognizeFileEnabled: boolean = this.node.tryGetContext(
+ 'recognizeFileEnabled'
+ )!;
if (typeof ragEnabled !== 'boolean') {
throw new Error(errorMessageForBooleanContext('ragEnabled'));
@@ -66,6 +69,10 @@ export class GenerativeAiUseCasesStack extends Stack {
throw new Error(errorMessageForBooleanContext('samlAuthEnabled'));
}
+ if (typeof recognizeFileEnabled !== 'boolean') {
+ throw new Error(errorMessageForBooleanContext('recognizeFileEnabled'));
+ }
+
const auth = new Auth(this, 'Auth', {
selfSignUpEnabled,
allowedSignUpEmailDomains,
@@ -117,6 +124,7 @@ export class GenerativeAiUseCasesStack extends Stack {
samlCognitoDomainName,
samlCognitoFederatedIdentityProviderName,
agentNames: api.agentNames,
+ recognizeFileEnabled,
});
if (ragEnabled) {
@@ -132,10 +140,12 @@ export class GenerativeAiUseCasesStack extends Stack {
api: api.api,
});
- new File(this, 'File', {
- userPool: auth.userPool,
- api: api.api,
- });
+ if (recognizeFileEnabled) {
+ new RecognizeFile(this, 'RecognizeFile', {
+ userPool: auth.userPool,
+ api: api.api,
+ });
+ }
new CfnOutput(this, 'Region', {
value: this.region,
@@ -205,6 +215,10 @@ export class GenerativeAiUseCasesStack extends Stack {
value: JSON.stringify(api.agentNames),
});
+ new CfnOutput(this, 'RecognizeFileEnabled', {
+ value: recognizeFileEnabled.toString(),
+ });
+
this.userPool = auth.userPool;
this.userPoolClient = auth.client;
}
diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx
index 28a5539c..3b888565 100644
--- a/packages/web/src/App.tsx
+++ b/packages/web/src/App.tsx
@@ -29,6 +29,8 @@ import useInterUseCases from './hooks/useInterUseCases';
const ragEnabled: boolean = import.meta.env.VITE_APP_RAG_ENABLED === 'true';
const agentEnabled: boolean = import.meta.env.VITE_APP_AGENT_ENABLED === 'true';
+const recognizeFileEnabled: boolean =
+ import.meta.env.VITE_APP_RECOGNIZE_FILE_ENABLED === 'true';
const items: ItemProps[] = [
{
@@ -107,12 +109,14 @@ const items: ItemProps[] = [
icon: