GenUアプリの開発環境を作成する時に最初に困りそうなこと(メモ)

# 最初に(GenUとは)

「Generative AI Use Cases JP」の略称が「GenU」で、AWSの生成AIサービス活用事例のサンプル実装集Webアプリです。  
AWS Japanのプロトタイピングエンジニアさん達が中心になって開発を行っているものと思っています。  
ソースコードがgithubで公開されており、フロントエンド(React)やバックエンド実装、cdkでのインフラ構築実装のサンプルとしても参考になります。

[Generative AI Use Cases JP (略称:GenU)](https://github.com/aws-samples/generative-ai-use-cases-jp)
[参考: 生成 AI アプリをノーコードで作成・社内配布できる GenU ユースケースビルダー](https://aws.amazon.com/jp/blogs/news/genu-use-cases-builder/)

ライセンスは[MIT No Attribution(MIT-0)](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/LICENSE)で公開されており、自由にカスタマイズや商用利用が可能です。  

# 当記事について

公式の手順に従ってCDKでGenUを構築した後に「自分でカスタマイズしてみたい」とWindowsのローカル環境に開発環境を作成した際に、最初に戸惑うかも（？）とおもう下記３点に焦点を当てた、私なりの解決法のメモとなります。

- "npm run web:devww"コマンドがエラーになるんだが？
- フロントアプリのデバッグってどうやるの？
- Lambda関数のデバッグってどうやるの？

GenU自体はcdkを用いて簡単に構築できるようになっています。  
手順は[GitHub](https://github.com/aws-samples/generative-ai-use-cases-jp)に説明がありますし、既存記事もいろいろありそうなのでここでは省略します。  
ローカル開発環境の構築についても下記GitHubページに手順があります。当記事は、それを私なりに補足する形のものになります。  
開発環境としてVSCodeを利用する前提です。

[ローカル環境構築手順（generative-ai-use-cases-jp）](https://github.com/aws-samples/generative-ai-use-cases-jp/blob/main/docs/DEVELOPMENT.md)

# ■"npm run web:devww"コマンドがエラーになるんだが？

GenUのフロントエンドはReact＋Vite＋TypeScriptの構成で実装されており、ソースコードは"packages/web"フォルダの下にあります。  
先述の「ローカル環境構築手順」の通りに実施すれば、"npm run web:devww"コマンド実行でReactアプリが"localhost:5173"で起動するはずですが、下記のエラーになる場合があります。  

> ./web_devw_win.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル web_devw_win.ps1 を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/fad9f997-12a6-4462-acf8-08c5670d7453.png)

powershellのコンソールを開き下記コマンドを実行することで解消できました(ちなみに、この解決法はChatGPTさんに教えてもらいました)。

（powershellコマンド）
```powershell
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/f4da3290-bfce-4829-b603-89b2a664669e.png)

### (補足)"npm run web:dev"コマンドがエラーになるんだが？の場合

"npm run web:devww"コマンドではなく、".env"ファイルを手作業で作成して"npm run web:dev"コマンドで起動したい方もいるかもしれません(何らかの事情でWeb開発環境のローカルでAWS CLIを利用できない場合など)。
この場合、下記のようなまた別のエラーになるかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/1bd7a40d-7951-4a2a-a2e2-9d396dc3e20c.png)

こちらのエラーになった場合、下記の参考記事を参考に"npm-scripts"が利用するシェルをbashに変更することでエラーは解消できると思います。

[参考: Windows環境でnpm-scripts実行に使用するシェルを変更する](https://zenn.dev/unsoluble_sugar/articles/96c3e32ac4e723)


# ■フロントアプリのデバッグってどうやるの？

こちらはGenUに限った話ではなく、どちらかというとViteを使ったReactアプリ開発全般に寄った話題です。
前述のコマンドでwebアプリが無事実行できた場合、localhostのポート"5173"(デフォルトでは)でGenUのフロントアプリが起動します。
このまま任意のブラウザで http://localhost:5173 にアクセスすればアプリの画面にアクセスすることは可能です。しかしまだデバッガを用いた作業はできるようにはなっていません。

"npm run web:devww"コマンドにてアプリを起動している状態で、さらにVSCode上でデバッグメニューからデバッグ用の画面を起動(Launch)することにより、ブレークポイントを設定したステップ実行などが可能になります。

そのためにはまずVSCodeのデバッグメニューの「create a launch.json file」から作成するか手作業で".vscode/launch.json"ファイルを作成して、下記のような内容を設定します(Chromeを利用する場合)。


（launch.jsonファイルの設定例）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/1bd97621-1de3-46d3-a82f-ff6b33cf89e2.png)

下記２点がポイントです。
- urlのポートが正しい(viteのデフォルトでは"5173")になっていること
- webRootが"${workspaceFolder}/packages/web"になっていること

(設定内容例)
```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Chrome",
            "request": "launch",
            "type": "chrome",
            "url": "http://localhost:5173",
            "webRoot": "${workspaceFolder}/packages/web"
        }
    ]
}
```

(デバッグメニューからの起動)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/aa7d64e7-4217-45c5-b448-2c115cad2b5e.png)


# ■Lambda関数のデバッグってどうやるの？

Lambda関数のソースコードは"packages/cdk/lambda"フォルダの下に存在し、TypeScriptで実装されておりcdkでデプロイされます。
GenUのバックエンド側の開発にあたり、実際にデプロイしないと関数の動作確認ができないというのはさすがに辛いです。
私の場合、下記の方法でローカル環境でのデバッグを可能にしています。

### ●"Jest"を使ったテストコードから実行

シンプルにテストフレームワークの「Jest」を利用したテストコードを作成し、ローカル環境にインストールされたnodejsを使って対象のLambda関数をデバッグ実行する方法です。

以下、Streamingではない形でAIの返答を返すLambdaのソース"predict.ts"（オリジナルのGenUに存在するコードそのまま）をテスト実行するコードを追加してみた例です。


```typescript:test/lambda/predict.test.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { mockAPIGatewayProxyEvent } from './lambda_testutil';
import { handler } from '../../lambda/predict';
import { expect, describe, it } from '@jest/globals';

describe('Unit test for app handler', function () {
  it('verifies successful response', async () => {
    const event = mockAPIGatewayProxyEvent({
      body: JSON.stringify(body),
      httpMethod: 'POST',
      path: '/chat',
    });
    const result: APIGatewayProxyResult = await handler(event);
    console.log(result.body);
    expect(result.body).toEqual(JSON.stringify("てすとです。"));
  });
});

const body = {
  "id": "/chat",
  "model": {
    "modelId": "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
    "type": "bedrock",
  },
  "messages": [
    {
      "role": "system",
      "content": `あなたはテストを支援するAIアシスタントです。
ここではテストのため、ユーザー入力内容をそのまま返してください。
説明文は不要です。一字一句変更せずにそのまま返してください。
`,
      "extraData": []
    },
    {
      "role": "user",
      "content": "てすとです。",
      "extraData": []
    }
  ],
}
```

※ ソースは[GitHub（predict.test.ts）](https://github.com/chs-k-kinoshita/generative-ai-use-cases-jp/blob/develop/packages/cdk/test/lambda/predict.test.ts)にpushしています。

「Jest Runner」というVSCode拡張をインストールするとテストコード上に"Run | Debug"表示がされて画面上のクリックだけで実行できるようになり便利です。
「Debug」でJest実行するとソース内に設定したブレークポイントで止めてステップ実行することが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/415dfc7b-35c5-4d77-a379-e193d30d6e1e.png)

"jest.config.ts"にで共通の環境変数の設定処理を指定しておくと便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531623/7fe89f21-2cb5-4976-9706-2013d9037ced.png)

```typescript:test/setupEnv.ts
export default (): void => {
  console.log("\nSetup test environment");
  process.env.MODEL_REGION = 'us-east-1';
  process.env.MODEL_IDS = JSON.stringify([
    { "modelId": "us.anthropic.claude-3-7-sonnet-20250219-v1:0", "region": "us-east-1" }
  ]);
  return;
};
```

前述の「predict.test.ts」では「lambda_testutil.ts」のmockAPIGatewayProxyEventという関数から、Lambdaのハンドラに渡すパラメータとしてApiGatewayイベント構造のデータを生成しています。
テンプレート化したものから部分的にプロパティを上書き(overrides)できるようなutility関数として作成しています（ご参考に）。

```typescript: test/lambda/lambda_testutil.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';

export const mockAPIGatewayProxyEvent = (overrides?: Partial<APIGatewayProxyEvent>): APIGatewayProxyEvent => {
  return {
    body: "{}",
    headers: { 'Content-Type': 'application/json' },
    multiValueHeaders: {},
    httpMethod: 'GET',
    isBase64Encoded: false,
    path: '/',
    pathParameters: null,
    queryStringParameters: null,
    multiValueQueryStringParameters: null,
    stageVariables: null,
    requestContext: {
      accountId: '123456789012',
      apiId: 'api-id',
      authorizer: undefined,
      protocol: 'HTTP/1.1',
      httpMethod: 'POST',
      identity: {
        accessKey: null,
        accountId: null,
        apiKey: null,
        apiKeyId: null,
        caller: null,
        clientCert: null,
        cognitoAuthenticationProvider: null,
        cognitoAuthenticationType: null,
        cognitoIdentityId: null,
        cognitoIdentityPoolId: null,
        principalOrgId: null,
        sourceIp: '127.0.0.1',
        user: null,
        userAgent: 'Custom User Agent String',
        userArn: null,
      },
      path: '/',
      stage: 'dev',
      requestId: 'request-id',
      requestTimeEpoch: Date.now(),
      resourceId: 'resource-id',
      resourcePath: '/',
    },
    resource: '/',
    ...overrides,
  };
};
```
---

# 最後に

Lambda関数のローカルデバッグ方法は、SAM LocalやAWS Toolkitを使うなどもっと良いやり方があるのかもしれません。新しい発見があった場合はアップデートしようと思います。
「GenUに自分用の機能追加してみた」的なことは勉強を兼ねてこれからもやってみたいと思っています。

以上、何かのご参考になれば幸いです。