Skip to content

Commit fa16a10

Browse files
authored
Merge pull request #204 from co-cddo/feat/ai-contact-centre
ai-contact-centre: 8-step walkthrough, StackSet hub, dynamic queue lookup
2 parents 5dacd0f + 68d54ea commit fa16a10

531 files changed

Lines changed: 36380 additions & 6 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-blueprints.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313
- 'cloudformation/scenarios/minute/cdk/**'
1414
- 'cloudformation/scenarios/fixmystreet/cdk/**'
1515
- 'cloudformation/scenarios/paperless-ngx/cdk/**'
16+
- 'cloudformation/scenarios/ai-contact-centre/**'
1617
- 'cloudformation/isb-hub/**'
1718
workflow_dispatch:
1819

@@ -469,6 +470,26 @@ jobs:
469470
aws s3 sync cloudformation/scenarios/simply-readable/website-build/ \
470471
"s3://${BUCKET}/scenarios/simply-readable/website-build/" --region us-east-1 --delete
471472
473+
# AI Contact Centre: SAM-style scenario. `sam package` zips each Lambda
474+
# CodeUri, uploads zips to the blueprints bucket, and writes a packaged
475+
# template (with absolute s3:// CodeUri values) to dist/template.yaml.
476+
# The hub CDK then uploads dist/template.yaml as the StackSet template.
477+
# No `sam build` needed: lambdas are pure Python with no native deps.
478+
- uses: aws-actions/setup-sam@v2
479+
with:
480+
use-installer: true
481+
482+
- name: Package AI Contact Centre (sam package)
483+
working-directory: cloudformation/scenarios/ai-contact-centre
484+
run: |
485+
mkdir -p dist
486+
sam package \
487+
--template-file template.yaml \
488+
--output-template-file dist/template.yaml \
489+
--s3-bucket ndx-try-isb-blueprints-568672915267 \
490+
--s3-prefix scenarios/ai-contact-centre/assets \
491+
--region us-east-1
492+
472493
- run: npm ci
473494
working-directory: cloudformation/isb-hub
474495

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,20 @@ package-lock.json
5353

5454
# Generated CloudFormation templates
5555
cloudformation/scenarios/*/template.yaml
56+
# Whitelist scenarios where template.yaml is hand-authored (not CDK-generated)
57+
!cloudformation/scenarios/ai-contact-centre/template.yaml
58+
59+
# AI Contact Centre — Lambda build artefacts (re-bundled by sam package on deploy)
60+
cloudformation/scenarios/ai-contact-centre/.aws-sam/
61+
cloudformation/scenarios/ai-contact-centre/packaged.yaml
62+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/fpdf/
63+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/fpdf2-*.dist-info/
64+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/defusedxml/
65+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/defusedxml-*.dist-info/
66+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/PIL/
67+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/pillow-*.dist-info/
68+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/fontTools/
69+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/fonttools-*.dist-info/
70+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/bin/
71+
cloudformation/scenarios/ai-contact-centre/lambdas/share-pdf-builder/share/
72+
cloudformation/scenarios/ai-contact-centre/lambdas/seed-kb/documents/

_bmad-output/implementation-artifacts/tech-spec-ai-contact-centre.md

Lines changed: 908 additions & 0 deletions
Large diffs are not rendered by default.

_bmad-output/implementation-artifacts/tech-spec-bops-planning-system.md

Lines changed: 893 additions & 0 deletions
Large diffs are not rendered by default.

cloudformation/isb-hub/lib/isb-hub-stack.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ interface ScenarioConfig {
1919
description: string;
2020
/** Optional parameters passed to the StackSet (and through to stack instances) */
2121
parameterKeys?: string[];
22+
/**
23+
* SAM-style scenario: source template lives at `scenarios/<name>/dist/template.yaml`
24+
* (produced by `sam package`) rather than the scenario root. The CI deploy job
25+
* runs `sam package` which uploads Lambda zips to the blueprints bucket and
26+
* writes the packaged template into dist/.
27+
*/
28+
samStyle?: boolean;
2229
}
2330

2431
const SCENARIOS: ScenarioConfig[] = [
@@ -31,9 +38,14 @@ const SCENARIOS: ScenarioConfig[] = [
3138
{ name: 'localgov-drupal', description: 'NDX:Try LocalGov Drupal - AI-enhanced CMS for UK councils' },
3239
{ name: 'simply-readable', description: 'NDX:Try Simply Readable - Document Translation & Easy Read, built by Swindon Borough Council' },
3340
{ name: 'localgov-ims', description: 'NDX:Try LocalGov IMS - Income Management System with GOV.UK Pay', parameterKeys: ['GovUkPayApiKey'] },
34-
{ name: 'minute', description: 'Minute AI - Meeting transcription and AI-powered minute generation' },
35-
{ name: 'fixmystreet', description: 'NDX:Try FixMyStreet - Citizen problem reporting platform for UK councils' },
36-
{ name: 'paperless-ngx', description: 'NDX:Try Paperless-ngx - Document archive with OCR, full-text search and Bedrock AI' },
41+
// TODO(orphan-import): minute, fixmystreet, paperless-ngx have ACTIVE
42+
// StackSets in the hub account NOT owned by IsbHubStack. CDK create fails
43+
// with AlreadyExists. Re-enable once imported via
44+
// `cloudformation create-change-set --change-set-type IMPORT`.
45+
// { name: 'minute', description: 'Minute AI - Meeting transcription and AI-powered minute generation' },
46+
// { name: 'fixmystreet', description: 'NDX:Try FixMyStreet - Citizen problem reporting platform for UK councils' },
47+
// { name: 'paperless-ngx', description: 'NDX:Try Paperless-ngx - Document archive with OCR, full-text search and Bedrock AI' },
48+
{ name: 'ai-contact-centre', description: 'NDX:Try AI Contact Centre - Amazon Connect with Lex, Bedrock RAG, multimodal photo describe, multi-intent triage, and multilingual support', samStyle: true },
3749
{ name: 'all-demo', description: 'NDX:Try All Demo - Deploys all 7 scenarios as nested stacks' },
3850
];
3951

@@ -68,23 +80,50 @@ export class IsbHubStack extends cdk.Stack {
6880

6981
// ========================================================================
7082
// TEMPLATE UPLOADS — one BucketDeployment per scenario
83+
//
84+
// Scenarios with no local template.yaml are skipped with a warning. CI is
85+
// expected to synthesize/package every scenario before this stack runs, so
86+
// a missing template in CI is a build error; locally it lets developers
87+
// iterate on hub config without first building every scenario.
7188
// ========================================================================
7289
const deployments: Record<string, s3deploy.BucketDeployment> = {};
90+
const skipped: string[] = [];
7391

7492
for (const scenario of SCENARIOS) {
7593
const pascalName = scenario.name
7694
.split('-')
7795
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
7896
.join('');
7997

98+
// SAM-style scenarios source from dist/ (produced by `sam package`); others
99+
// from the scenario root.
100+
const sourceDir = scenario.samStyle
101+
? path.join(__dirname, '..', '..', 'scenarios', scenario.name, 'dist')
102+
: path.join(__dirname, '..', '..', 'scenarios', scenario.name);
103+
104+
const templatePath = path.join(sourceDir, 'template.yaml');
105+
if (!fs.existsSync(templatePath)) {
106+
if (process.env.CI === 'true') {
107+
throw new Error(`Missing template for scenario '${scenario.name}': ${templatePath}. CI must synthesize this scenario before the hub stack runs.`);
108+
}
109+
console.warn(`[isb-hub] WARN: skipping scenario '${scenario.name}' — no template at ${templatePath} (run the synth step locally first if you want it deployed)`);
110+
skipped.push(scenario.name);
111+
continue;
112+
}
113+
80114
const deployment = new s3deploy.BucketDeployment(this, `${pascalName}Templates`, {
81115
sources: [
82-
s3deploy.Source.asset(path.join(__dirname, '..', '..', 'scenarios', scenario.name), {
116+
s3deploy.Source.asset(sourceDir, {
83117
exclude: ['*', '!template.yaml'],
84118
}),
85119
],
86120
destinationBucket: bucket,
87121
destinationKeyPrefix: `scenarios/${scenario.name}`,
122+
// Don't delete sibling objects under the same prefix. SAM-style
123+
// scenarios upload Lambda zips to scenarios/<name>/assets/ via
124+
// `sam package` BEFORE this BucketDeployment runs; default prune:true
125+
// would `aws s3 sync --delete` and remove them.
126+
prune: scenario.samStyle ? false : undefined,
88127
});
89128

90129
// Grant permissions on imported bucket (CDK can't auto-grant on imported resources)
@@ -174,12 +213,16 @@ export class IsbHubStack extends cdk.Stack {
174213
// STACKSETS — one per scenario, no stack instances (ISB manages those)
175214
// ========================================================================
176215
for (const scenario of SCENARIOS) {
216+
if (skipped.includes(scenario.name)) continue;
217+
177218
const pascalName = scenario.name
178219
.split('-')
179220
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
180221
.join('');
181222

182-
const templatePath = path.join(__dirname, '..', '..', 'scenarios', scenario.name, 'template.yaml');
223+
const templatePath = scenario.samStyle
224+
? path.join(__dirname, '..', '..', 'scenarios', scenario.name, 'dist', 'template.yaml')
225+
: path.join(__dirname, '..', '..', 'scenarios', scenario.name, 'template.yaml');
183226
const templateContent = fs.readFileSync(templatePath, 'utf8');
184227
const contentHash = crypto.createHash('sha256').update(templateContent).digest('hex').substring(0, 16);
185228

0 commit comments

Comments
 (0)