# {{STACK_NAME}} CDK Infrastructure

This directory contains the AWS CDK infrastructure code for deploying {{STACK_NAME}} AI chat microservice to AWS.

## Overview

The CDK app uses the declarative infrastructure API framework from `@worktif/runtime/infra` to provision all required AWS resources:

- **DynamoDB Table**: Stores conversation session history
- **OpenSearch Domain**: Vector similarity search for RAG (Retrieval-Augmented Generation)
- **Lambda Functions**: Handle chat API endpoints with Bedrock integration
- **API Gateway**: HTTP routing for the chat API
- **IAM Roles**: Permissions for Lambda to access Bedrock, DynamoDB, and OpenSearch
- **S3 Bucket**: Stores static assets and cached responses
- **CloudFront Distribution**: CDN for global content delivery

## Directory Structure

```
cdk/
├── bin/
│   └── app.ts              # CDK app entry point
├── lib/
│   ├── config.ts           # Shared configuration
│   ├── infra-stack.ts      # RuntimeInfraStack configuration
│   ├── ai-stack.ts         # AI-specific resources (DynamoDB, OpenSearch)
│   ├── web-stack.ts        # RuntimeWebStack (microservices) configuration
│   ├── runtime-stack.ts    # RuntimeStack (SSR) configuration
│   └── awake-stack.ts      # RuntimeAwakeStack (dev only) configuration
├── cdk.json
├── package.json
├── tsconfig.json
└── README.md
```

### Stack Configuration Files

| File | Purpose |
|------|---------|
| `lib/config.ts` | Centralized configuration with stage-specific defaults |
| `lib/infra-stack.ts` | Base infrastructure (S3, IAM) using declarative API |
| `lib/ai-stack.ts` | AI resources: DynamoDB session table, OpenSearch domain, Bedrock permissions |
| `lib/web-stack.ts` | Chat handler Lambda with Bedrock, Memory, Search services |
| `lib/runtime-stack.ts` | SSR Lambda configuration (optional) |
| `lib/awake-stack.ts` | Runtime Lambda Debug (dev stage only) |

## Prerequisites

1. **AWS CLI**: Install and configure AWS CLI with your credentials
   ```bash
   aws configure
   ```

2. **Node.js**: Version 18 or higher

3. **AWS CDK**: Install globally (optional, can use npx)
   ```bash
   npm install -g aws-cdk
   ```

4. **AWS Bedrock Access**: Ensure your AWS account has access to Bedrock models
   - Go to AWS Console → Bedrock → Model access
   - Request access to Claude models (or your preferred model)

## Getting Started

### 1. Install Dependencies

```bash
# Install project dependencies (from project root)
cd ..
npm install

# Install CDK dependencies
cd cdk
npm install
```

### 2. Bootstrap CDK (First Time Only)

Bootstrap provisions the CDK toolkit resources in your AWS account:

```bash
cdk bootstrap
```

This only needs to be done once per AWS account/region combination.

### 3. Deploy Infrastructure

Deploy to the default stage (dev):

```bash
cdk deploy --all
```

Deploy to a specific stage:

```bash
npm run deploy:dev      # Deploy to dev
npm run deploy:staging  # Deploy to staging
npm run deploy:prod     # Deploy to prod
```

### 4. View Deployment Outputs

After deployment, CDK will display important outputs:

- **API Gateway URL**: Your chat API endpoint
- **Session Table Name**: DynamoDB table for conversation history
- **Search Domain Endpoint**: OpenSearch domain for vector search
- **Lambda Function ARN**: The chat handler function

## AI Stack Resources

The AI stack (`lib/ai-stack.ts`) creates:

### DynamoDB Session Table

Stores conversation history per session:

| Attribute | Type | Description |
|-----------|------|-------------|
| sessionId | String (PK) | Unique session identifier |
| messages | List | Array of conversation messages |
| createdAt | String | ISO timestamp of session creation |
| updatedAt | String | ISO timestamp of last update |
| ttl | Number | TTL for automatic expiration |

### OpenSearch Domain

Vector similarity search for RAG pattern:

- **Engine**: OpenSearch 2.11 with KNN plugin
- **Instance**: t3.small.search (configurable)
- **Storage**: 10GB GP3 EBS volume
- **Index**: `knowledge-base` with vector embeddings

### IAM Permissions

Lambda execution role includes:

- `bedrock:InvokeModel` - Call Bedrock AI models
- `dynamodb:GetItem`, `dynamodb:UpdateItem` - Session memory
- `es:ESHttp*` - OpenSearch queries

## Configuration

### Environment Variables

The Lambda functions use these environment variables:

| Variable | Description | Default |
|----------|-------------|---------|
| `BEDROCK_MODEL_ID` | Bedrock model identifier | `anthropic.claude-3-sonnet-20240229-v1:0` |
| `MEMORY_TABLE_NAME` | DynamoDB table name | Set by CDK output |
| `OPENSEARCH_ENDPOINT` | OpenSearch domain endpoint | Set by CDK output |
| `OPENSEARCH_INDEX` | OpenSearch index name | `knowledge-base` |
| `AWS_REGION` | AWS region | `{{REGION}}` |

### Stage-Specific Defaults

| Stage | Removal Policy | OpenSearch | Session TTL |
|-------|---------------|------------|-------------|
| dev | DESTROY | t3.small.search | 24 hours |
| staging | SNAPSHOT | t3.medium.search | 7 days |
| prod | RETAIN | r6g.large.search | 30 days |

## Populating OpenSearch

To use RAG (Retrieval-Augmented Generation), you need to populate OpenSearch with vector embeddings:

### 1. Create the Index

```bash
# Using curl with AWS SigV4
curl -X PUT "https://<opensearch-endpoint>/knowledge-base" \
  -H "Content-Type: application/json" \
  --aws-sigv4 "aws:amz:{{REGION}}:es" \
  -d '{
    "mappings": {
      "properties": {
        "content": { "type": "text" },
        "embedding": {
          "type": "knn_vector",
          "dimension": 1536,
          "method": {
            "name": "hnsw",
            "space_type": "cosinesimil",
            "engine": "nmslib"
          }
        },
        "metadata": { "type": "object" }
      }
    },
    "settings": {
      "index": { "knn": true }
    }
  }'
```

### 2. Index Documents with Embeddings

```typescript
// Example: Index a document with embedding
const embedding = await getEmbedding(documentContent); // Use Bedrock Titan or OpenAI

await opensearchClient.index({
  index: 'knowledge-base',
  body: {
    content: documentContent,
    embedding: embedding,
    metadata: { source: 'docs', category: 'faq' }
  }
});
```

## Multiple Stages

The configuration automatically handles stage-specific defaults:

| Stage | Removal Policy | Encryption | PITR |
|-------|---------------|------------|------|
| dev | DESTROY | Optional | No |
| staging | SNAPSHOT | Yes | Yes |
| prod | RETAIN | Yes | Yes |

Deploy to different stages:

```bash
# Deploy to dev
cdk deploy --all --context stage=dev

# Deploy to staging
cdk deploy --all --context stage=staging

# Deploy to production
cdk deploy --all --context stage=prod
```

## Useful Commands

- `npm run build`: Compile TypeScript to JavaScript
- `npm run watch`: Watch for changes and compile
- `cdk synth`: Synthesize CloudFormation template
- `cdk diff --all`: Compare deployed stack with current state
- `cdk deploy --all`: Deploy stack to AWS
- `cdk destroy`: Remove all resources (use with caution!)

## Troubleshooting

### "Bedrock model not available"

**Problem**: The specified Bedrock model is not accessible.

**Solution**:
1. Go to AWS Console → Bedrock → Model access
2. Request access to the model (e.g., Claude 3 Sonnet)
3. Wait for approval (usually instant for most models)
4. Update `BEDROCK_MODEL_ID` if using a different model

### "OpenSearch domain creation failed"

**Problem**: OpenSearch domain fails to create.

**Solution**:
- Check service quotas for OpenSearch domains
- Ensure your VPC has available IP addresses (if using VPC)
- Try a smaller instance type for dev environments

### "Unable to resolve AWS account"

**Problem**: CDK can't determine your AWS account ID.

**Solution**: Set environment variables or configure AWS CLI:
```bash
# Option 1: Set environment variables
export CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export AWS_REGION={{REGION}}

# Option 2: Configure AWS CLI (recommended)
aws configure set region {{REGION}}
```

### "Lambda bundle not found"

**Problem**: The Lambda bundle hasn't been built.

**Solution**: Build your Runtime Web application first:
```bash
cd ..
npm run build
cd cdk
cdk deploy --all
```

### "Session table access denied"

**Problem**: Lambda can't access DynamoDB table.

**Solution**: The AI stack automatically grants permissions. If issues persist:
1. Check the Lambda execution role has DynamoDB permissions
2. Verify the table name matches `MEMORY_TABLE_NAME` environment variable

## Cost Considerations

### Development Stage
- OpenSearch: ~$25/month (t3.small.search)
- DynamoDB: Pay-per-request (minimal for dev)
- Bedrock: Pay-per-token (varies by usage)

### Production Stage
- OpenSearch: ~$200+/month (r6g.large.search with replicas)
- DynamoDB: Pay-per-request with PITR backup costs
- Bedrock: Pay-per-token (budget based on expected traffic)

**Tip**: Use `cdk destroy` to remove dev resources when not in use.

## Learn More

- [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/)
- [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/)
- [OpenSearch KNN Plugin](https://opensearch.org/docs/latest/search-plugins/knn/)
- [Runtime Web Documentation](https://runtimeweb.com/docs)
- [Declarative Infrastructure API Reference](https://runtimeweb.com/docs/cdk/declarative-api)
