diff --git a/.dockerignore b/.dockerignore index b649fd47a2..502634ea5d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,7 +15,3 @@ docs **/.husky **/node_modules -**/.next - -!examples/**/node_modules -!examples/**/.next \ No newline at end of file diff --git a/.github/workflows/preview-envs-deploy.yml b/.github/workflows/preview-envs-deploy.yml index 21f9592c97..1d9c97873b 100644 --- a/.github/workflows/preview-envs-deploy.yml +++ b/.github/workflows/preview-envs-deploy.yml @@ -1,4 +1,4 @@ -name: Preview Environments (Deploy) +name: Preview Environments on: pull_request: @@ -12,60 +12,26 @@ concurrency: jobs: build_images: runs-on: ubuntu-latest - name: Dockerize + name: Build Images strategy: matrix: app: - - { name: 'coagents-research-canvas-ui', path: 'examples/coagents-research-canvas/ui', dockerfile: 'Dockerfile.ui', type: 'node' } - - { name: 'coagents-research-canvas-agent', path: 'examples/coagents-research-canvas/agent', dockerfile: 'Dockerfile.agent', type: 'poetry' } - - { name: 'coagents-ai-researcher-ui', path: 'examples/coagents-ai-researcher/ui', dockerfile: 'Dockerfile.ui', type: 'node' } - - { name: 'coagents-ai-researcher-agent', path: 'examples/coagents-ai-researcher/agent', dockerfile: 'Dockerfile.agent', type: 'poetry' } - - { name: 'coagents-qa-native-ui', path: 'examples/coagents-qa-native/ui', dockerfile: 'Dockerfile.ui', type: 'node' } - - { name: 'coagents-qa-native-agent', path: 'examples/coagents-qa-native/agent', dockerfile: 'Dockerfile.agent', type: 'poetry' } - - { name: 'coagents-qa-text-ui', path: 'examples/coagents-qa-text/ui', dockerfile: 'Dockerfile.ui', type: 'node' } - - { name: 'coagents-qa-text-agent', path: 'examples/coagents-qa-text/agent', dockerfile: 'Dockerfile.agent', type: 'poetry' } + - { project: "coagents-research-canvas", type: "agent", deps: "remote-deps" } + - { project: "coagents-research-canvas", type: "agent", deps: "local-deps" } + - { project: "coagents-research-canvas", type: "ui" } + - { project: "coagents-ai-researcher", type: "agent", deps: "remote-deps" } + - { project: "coagents-ai-researcher", type: "agent", deps: "local-deps" } + - { project: "coagents-ai-researcher", type: "ui" } + - { project: "coagents-qa-text", type: "agent", deps: "remote-deps" } + - { project: "coagents-qa-text", type: "agent", deps: "local-deps" } + - { project: "coagents-qa-text", type: "ui" } + - { project: "coagents-qa-native", type: "agent", deps: "remote-deps" } + - { project: "coagents-qa-native", type: "agent", deps: "local-deps" } + - { project: "coagents-qa-native", type: "ui" } steps: - name: Checkout uses: actions/checkout@v4 - - uses: dopplerhq/secrets-fetch-action@v1.2.0 - id: doppler - with: - doppler-token: ${{ secrets.DOPPLER_TOKEN }} - doppler-project: copilotkit-previews - doppler-config: prd - - - name: (Node) Setup pnpm - if: matrix.app.type == 'node' - uses: pnpm/action-setup@v4 - with: - version: "9.5" - - - name: (Node) Use Node.js 21 - if: matrix.app.type == 'node' - uses: actions/setup-node@v2 - with: - node-version: 21.x - cache: 'pnpm' - cache-dependency-path: '**/pnpm-lock.yaml' - - - name: (Node) Install dependencies - if: matrix.app.type == 'node' - working-directory: ./CopilotKit - run: pnpm install --frozen-lockfile - - - name: (Node) Install app - if: matrix.app.type == 'node' - working-directory: ${{ matrix.app.path }} - run: pnpm install --frozen-lockfile - - - name: (Node) Build app - if: matrix.app.type == 'node' - working-directory: ${{ matrix.app.path }} - run: pnpm run build - env: - OPENAI_API_KEY: ${{ steps.doppler.outputs.OPENAI_API_KEY }} - - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -86,12 +52,83 @@ jobs: run: | aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 058264219587.dkr.ecr.us-east-1.amazonaws.com - - name: Build Image + - name: (UI) Setup pnpm + if: matrix.app.type == 'ui' + uses: pnpm/action-setup@v4 + with: + version: "9.5" + + - name: (UI) Use Node.js 21 + if: matrix.app.type == 'ui' + uses: actions/setup-node@v2 + with: + node-version: 21.x + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' + + - name: (UI) Install dependencies + if: matrix.app.type == 'ui' + working-directory: ./CopilotKit + run: pnpm install --frozen-lockfile + + - name: (UI) Install app dependencies + if: matrix.app.type == 'ui' + working-directory: examples/${{ matrix.app.project }}/${{ matrix.app.type }} + run: pnpm install --frozen-lockfile + + - name: (UI, Local Deps) Install monorepo + if: matrix.app.type == 'ui' + working-directory: ./CopilotKit + run: pnpm install --frozen-lockfile + + - name: (UI, Local Deps) Build @copilotkit packages + if: matrix.app.type == 'ui' + working-directory: ./CopilotKit + run: pnpx turbo run build + + - name: (UI, Local Deps) Link @copilotkit packages + if: matrix.app.type == 'ui' + working-directory: ./examples/${{ matrix.app.project }}/${{ matrix.app.type }} run: | - docker buildx build -t 058264219587.dkr.ecr.us-east-1.amazonaws.com/coagents:${{ matrix.app.name }}-${{ github.run_id }} --build-arg APP_DIR=${{ matrix.app.path }} --secret id=OPENAI_API_KEY . -f ./examples/${{ matrix.app.dockerfile }} --platform=linux/amd64 --provenance=false --push + for package in $(ls -d ../../../CopilotKit/packages/*); do + pnpm link $package + done + + - name: (UI) Build app + if: matrix.app.type == 'ui' + working-directory: examples/${{ matrix.app.project }}/${{ matrix.app.type }} + run: pnpm run build + env: + OPENAI_API_KEY: ${{ steps.doppler.outputs.OPENAI_API_KEY }} + + - name: (UI) Build Image + if: matrix.app.type == 'ui' + run: | + docker buildx build \ + -t 058264219587.dkr.ecr.us-east-1.amazonaws.com/coagents:${{ matrix.app.project }}-ui-${{ github.run_id }} \ + --build-arg APP_DIR=examples/${{ matrix.app.project }}/${{ matrix.app.type }} \ + -f ./examples/Dockerfile.${{ matrix.app.type }} \ + --platform=linux/amd64 \ + --provenance=false \ + --push \ + . + env: + DOCKER_BUILDKIT: 1 + + - name: (Agent) Build Image + if: matrix.app.type == 'agent' + run: | + docker buildx build \ + -t 058264219587.dkr.ecr.us-east-1.amazonaws.com/coagents:${{ matrix.app.project }}-agent-${{ matrix.app.deps }}-${{ github.run_id }} \ + --build-arg APP_DIR=examples/${{ matrix.app.project }}/${{ matrix.app.type }} \ + -f ./examples/Dockerfile.${{ matrix.app.type }} \ + --build-arg WITH_LOCAL_DEPS=${{ matrix.app.deps == 'local-deps' }} \ + --platform=linux/amd64 \ + --provenance=false \ + --push \ + . env: DOCKER_BUILDKIT: 1 - OPENAI_API_KEY: ${{ steps.doppler.outputs.OPENAI_API_KEY }} cdk_deploy: runs-on: ubuntu-latest @@ -138,8 +175,8 @@ jobs: run: cdk deploy --profile default --all --require-approval never --outputs-file=cdk_outputs.json --concurrency 10 env: UNIQUE_ENV_ID: 'PR${{ github.event.number }}' - OPENAI_API_KEY: ${{ steps.doppler.outputs.OPENAI_API_KEY }} - GITHUB_RUN_ID: "${{ github.run_id }}" + GITHUB_ACTIONS_RUN_ID: "${{ github.run_id }}" + GITHUB_PR_NUMBER: "${{ github.event.number }}" - name: Generate table from outputs id: generate-table diff --git a/.github/workflows/preview-envs-destroy.yml b/.github/workflows/preview-envs-destroy.yml index 0dfdcf9a4e..84b057e4db 100644 --- a/.github/workflows/preview-envs-destroy.yml +++ b/.github/workflows/preview-envs-destroy.yml @@ -1,4 +1,4 @@ -name: Preview Environments (Destroy) +name: Destroy Preview Environments on: pull_request: diff --git a/examples/Dockerfile.agent b/examples/Dockerfile.agent index 04267aa387..424a656bc1 100644 --- a/examples/Dockerfile.agent +++ b/examples/Dockerfile.agent @@ -1,6 +1,7 @@ FROM public.ecr.aws/docker/library/python:3.12.0-slim-bullseye COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter ARG APP_DIR +ARG WITH_LOCAL_DEPS ENV POETRY_VIRTUALENVS_CREATE=false @@ -14,8 +15,8 @@ COPY ${APP_DIR}/pyproject.toml ${APP_DIR}/poetry.lock ./ # Copy SDK and install dependencies in a single layer COPY sdk-python/ /opt/sdk-python WORKDIR /asset -RUN poetry install --no-interaction --no-ansi && \ - poetry add /opt/sdk-python +RUN poetry install --no-interaction --no-ansi +RUN if [ "$WITH_LOCAL_DEPS" = "true" ]; then poetry add /opt/sdk-python; fi # Copy application code last COPY ${APP_DIR}/ ./ diff --git a/examples/Dockerfile.ui b/examples/Dockerfile.ui index b8f8c16eaf..f2321c8f08 100644 --- a/examples/Dockerfile.ui +++ b/examples/Dockerfile.ui @@ -12,13 +12,14 @@ ARG APP_DIR COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter WORKDIR /app -# Copy the built artifacts from the builder stage -# COPY --from=builder-monorepo /app/${APP_DIR}/.next/standalone ./ -# COPY --from=builder-monorepo /app/${APP_DIR}/.next/static ./.next/static +COPY ${APP_DIR}/package.json ${APP_DIR}/pnpm-lock.yaml ./ +RUN pnpm i --frozen-lockfile +# Copy the built artifacts from the builder stage COPY ${APP_DIR}/.next/standalone ./ COPY ${APP_DIR}/.next/static ./.next/static + # Set the environment variables (if needed) ENV NODE_ENV=production EXPOSE 3000 diff --git a/infra/bin/infra.ts b/infra/bin/infra.ts index 6dc95c8bbf..26a696e2bf 100644 --- a/infra/bin/infra.ts +++ b/infra/bin/infra.ts @@ -1,171 +1,151 @@ #!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; -import { requireEnv } from "../lib/utils"; -import { PreviewProjectStack } from "../lib/demo-project-stack"; +import { createAgentProjectStack, createUIProjectStack } from "../lib/utils"; // app const app = new cdk.App(); -const uniqueEnvironmentId = requireEnv("UNIQUE_ENV_ID"); -const githubRunId = requireEnv("GITHUB_RUN_ID") -/** - * CoAgents Research Canvas Demo +/* + * Research Canvas */ -const researchCanvasAgent = new PreviewProjectStack(app, `CoAgentsResearchCanvasDemoAgent`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents Research Canvas - Agent", - demoDir: "examples/coagents-research-canvas/agent", - ecrImageTag: `coagents-research-canvas-agent-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.agent", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY", "TAVILY_API_KEY"], - port: "8000", - includeInPRComment: false, - outputEnvVariable: "COAGENTS_RESEARCH_CANVAS_AGENT_ENDPOINT_URL", +// Remote Dependencies +const agentWithRemoteDeps = createAgentProjectStack({ + app, + project: "coagents-research-canvas", + description: "CoAgents Research Canvas (Agent) - Remote Depenencies", + dependencies: "Remote" }); -const researchCanvasUI = new PreviewProjectStack(app, `CoAgentsResearchCanvasDemoUI`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents Research Canvas - UI", - demoDir: "examples/coagents-research-canvas/ui", - ecrImageTag: `coagents-research-canvas-ui-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.ui", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY"], - buildSecrets: ["OPENAI_API_KEY"], - environmentVariables: { - REMOTE_ACTION_URL: `${researchCanvasAgent.fnUrl}/copilotkit`, - }, - port: "3000", - includeInPRComment: true, - outputEnvVariable: "COAGENTS_RESEARCH_CANVAS_UI_ENDPOINT_URL" +const uiWithRemoteDeps = createUIProjectStack({ + app, + project: "coagents-research-canvas", + description: "CoAgents Research Canvas (UI) - Remote Depenencies", + dependencies: "Remote", + agentProject: agentWithRemoteDeps }); -/** +// Local Dependencies +const agentWithLocalDeps = createAgentProjectStack({ + app, + project: "coagents-research-canvas", + description: "CoAgents Research Canvas (Agent) - Local Depenencies", + dependencies: "Local" +}); + +const uiWithLocalDeps = createUIProjectStack({ + app, + project: "coagents-research-canvas", + description: "CoAgents Research Canvas (UI) - Local Depenencies", + dependencies: "Local", + agentProject: agentWithLocalDeps +}); + +/* * CoAgents Perplexity Demo */ -const perplexityAgent = new PreviewProjectStack(app, `CoAgentsPerplexityDemoAgent`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents Perplexity Clone - Agent", - demoDir: "examples/coagents-ai-researcher/agent", - ecrImageTag: `coagents-research-canvas-agent-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.agent", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY", "TAVILY_API_KEY"], - port: "8000", - includeInPRComment: false, - outputEnvVariable: "COAGENTS_PERPLEXITY_AGENT_ENDPOINT_URL", +// Remote Dependencies +const perplexityAgentWithRemoteDeps = createAgentProjectStack({ + app, + project: "coagents-ai-researcher", + description: "CoAgents Perplexity Clone (Agent) - Remote Dependencies", + dependencies: "Remote" }); -const perplexityUI = new PreviewProjectStack(app, `CoAgentsPerplexityDemoUI`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents Perplexity Clone - UI", - demoDir: "examples/coagents-ai-researcher/ui", - ecrImageTag: `coagents-ai-researcher-ui-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.ui", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY"], - buildSecrets: ["OPENAI_API_KEY"], - environmentVariables: { - // REMOTE_ACTION_URL: `${perplexityAgent.fnUrl}/copilotkit`, - }, - port: "3000", - includeInPRComment: true, - outputEnvVariable: "COAGENTS_PERPLEXITY_UI_ENDPOINT_URL", +const perplexityUIWithRemoteDeps = createUIProjectStack({ + app, + project: "coagents-ai-researcher", + description: "CoAgents Perplexity Clone (UI) - Remote Dependencies", + dependencies: "Remote", + agentProject: perplexityAgentWithRemoteDeps }); -/** +// Local Dependencies +const perplexityAgentWithLocalDeps = createAgentProjectStack({ + app, + project: "coagents-ai-researcher", + description: "CoAgents Perplexity Clone (Agent) - Local Dependencies", + dependencies: "Local" +}); + +const perplexityUIWithLocalDeps = createUIProjectStack({ + app, + project: "coagents-ai-researcher", + description: "CoAgents Perplexity Clone (UI) - Local Dependencies", + dependencies: "Local", + agentProject: perplexityAgentWithLocalDeps +}); + +/* * CoAgents QA Text Demo */ -const qaTextAgent = new PreviewProjectStack(app, `CoAgentsQATextDemoAgent`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents QA Text - Agent", - demoDir: "examples/coagents-ai-researcher/agent", - ecrImageTag: `coagents-ai-researcher-agent-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.agent", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY", "TAVILY_API_KEY"], - port: "8000", - includeInPRComment: false, - outputEnvVariable: "COAGENTS_QA_TEXT_AGENT_ENDPOINT_URL", +// Remote Dependencies +const qaTextAgentWithRemoteDeps = createAgentProjectStack({ + app, + project: "coagents-qa-text", + description: "CoAgents QA Text (Agent) - Remote Dependencies", + dependencies: "Remote" }); -const qaTextUI = new PreviewProjectStack(app, `CoAgentsQATextDemoUI`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents QA Text - UI", - demoDir: "examples/coagents-qa-text/ui", - ecrImageTag: `coagents-qa-text-ui-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.ui", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY"], - buildSecrets: ["OPENAI_API_KEY"], - environmentVariables: { - // REMOTE_ACTION_URL: `${qaTextAgent.fnUrl}/copilotkit`, - }, - port: "3000", - includeInPRComment: true, - outputEnvVariable: "COAGENTS_QA_TEXT_UI_ENDPOINT_URL", +const qaTextUIWithRemoteDeps = createUIProjectStack({ + app, + project: "coagents-qa-text", + description: "CoAgents QA Text (UI) - Remote Dependencies", + dependencies: "Remote", + agentProject: qaTextAgentWithRemoteDeps }); -/** +// Local Dependencies +const qaTextAgentWithLocalDeps = createAgentProjectStack({ + app, + project: "coagents-qa-text", + description: "CoAgents QA Text (Agent) - Local Dependencies", + dependencies: "Local" +}); + +const qaTextUIWithLocalDeps = createUIProjectStack({ + app, + project: "coagents-qa-text", + description: "CoAgents QA Text (UI) - Local Dependencies", + dependencies: "Local", + agentProject: qaTextAgentWithLocalDeps +}); + +/* * CoAgents QA Native Demo */ -const qaNativeAgent = new PreviewProjectStack(app, `CoAgentsQANativetDemoAgent`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents QA Native - Agent", - demoDir: "examples/coagents-ai-researcher/agent", - ecrImageTag: `coagents-qa-native-agent-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.agent", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY", "TAVILY_API_KEY"], - buildSecrets: ["OPENAI_API_KEY"], - port: "8000", - includeInPRComment: false, - outputEnvVariable: "COAGENTS_QA_NATIVE_AGENT_ENDPOINT_URL", +// Remote Dependencies +const qaNativeAgentWithRemoteDeps = createAgentProjectStack({ + app, + project: "coagents-qa-native", + description: "CoAgents QA Native (Agent) - Remote Dependencies", + dependencies: "Remote" }); -const qaNativeAUI = new PreviewProjectStack(app, `CoAgentsQANativeDemoUI`, { - env: { - account: process.env.CDK_DEFAULT_ACCOUNT, - }, - projectName: "CoAgents QA Native - UI", - demoDir: "examples/coagents-qa-native/ui", - ecrImageTag: `coagents-qa-native-ui-${githubRunId}`, - overrideDockerWorkdir: "./", - overrideDockerfile: "examples/Dockerfile.ui", - uniqueEnvironmentId, - environmentVariablesFromSecrets: ["OPENAI_API_KEY"], - buildSecrets: ["OPENAI_API_KEY"], - environmentVariables: { - // REMOTE_ACTION_URL: `${qaNativeAgent.fnUrl}/copilotkit`, - }, - port: "3000", - includeInPRComment: true, - outputEnvVariable: "COAGENTS_QA_NATIVE_UI_ENDPOINT_URL", -}); \ No newline at end of file +const qaNativeUIWithRemoteDeps = createUIProjectStack({ + app, + project: "coagents-qa-native", + description: "CoAgents QA Native (UI) - Remote Dependencies", + dependencies: "Remote", + agentProject: qaNativeAgentWithRemoteDeps +}); + +// Local Dependencies +const qaNativeAgentWithLocalDeps = createAgentProjectStack({ + app, + project: "coagents-qa-native", + description: "CoAgents QA Native (Agent) - Local Dependencies", + dependencies: "Local" +}); + +const qaNativeUIWithLocalDeps = createUIProjectStack({ + app, + project: "coagents-qa-native", + description: "CoAgents QA Native (UI) - Local Dependencies", + dependencies: "Local", + agentProject: qaNativeAgentWithLocalDeps +}); diff --git a/infra/generate-table.js b/infra/generate-table.js index 698b5793d7..9b0b6a7782 100644 --- a/infra/generate-table.js +++ b/infra/generate-table.js @@ -15,15 +15,45 @@ function generateTable() { console.log(json); + + + // const rows = [{ + // "Name": "test", + // "Preview (Local Dependencies)": "url" + // "Preview (Remote Dependencies)": "url", + // }] + + // Group entries by ProjectName + const projectGroups = Object.values(json).reduce((acc, entry) => { + if (!acc[entry.ProjectName]) { + acc[entry.ProjectName] = { + name: entry.ProjectName, + remote: '', + local: '' + }; + } + + // Add URLs based on dependency type + if (entry.Dependencies === 'Remote') { + acc[entry.ProjectName].remote = `[Preview](${entry.FunctionUrl})`; + } else if (entry.Dependencies === 'Local') { + acc[entry.ProjectName].local = `[Preview](${entry.FunctionUrl})`; + } + + return acc; + }, {}); + + // Convert grouped data to rows array + const rows = Object.values(projectGroups).map(project => ({ + "Name": project.name, + "Preview (Local Dependencies)": project.local, + "Preview (Remote Dependencies)": project.remote + })); + structure.push({ table: { - headers: ["Name", "URL"], - rows: Object.entries(json) - .filter(([key, value]) => value.IncludeInComment === "true") - .map(([key, value]) => ({ - Name: value.ProjectName, - URL: `[Link](${value.FunctionUrl})`, - })), + headers: ["Name", "Preview (Local Dependencies)", "Preview (Remote Dependencies)"], + rows, }, }); @@ -32,19 +62,19 @@ function generateTable() { fs.writeFileSync(path.resolve(__dirname, "./preview-comment.md"), md); } -function generateProcessedOutputForTests() { - let output = ""; - const json = JSON.parse(file); +// function generateProcessedOutputForTests() { +// let output = ""; +// const json = JSON.parse(file); - Object.entries(json) - .filter(([key, value]) => !!value.OutputEnvVariable) - .forEach(([key, value]) => { - const envVariableName = value.OutputEnvVariable; - output += `${envVariableName}="${value.FunctionUrl.replace(/\/$/, '')}"\n`; - }); +// Object.entries(json) +// .filter(([key, value]) => !!value.OutputEnvVariable) +// .forEach(([key, value]) => { +// const envVariableName = value.OutputEnvVariable; +// output += `${envVariableName}="${value.FunctionUrl.replace(/\/$/, '')}"\n`; +// }); - fs.writeFileSync(path.resolve(__dirname, "./.env.test"), output); -} +// fs.writeFileSync(path.resolve(__dirname, "./.env.test"), output); +// } generateTable(); -generateProcessedOutputForTests(); +// generateProcessedOutputForTests(); diff --git a/infra/lib/demo-project-stack.ts b/infra/lib/demo-project-stack.ts index f9ee69687a..1b73336776 100644 --- a/infra/lib/demo-project-stack.ts +++ b/infra/lib/demo-project-stack.ts @@ -8,18 +8,15 @@ import * as logs from "aws-cdk-lib/aws-logs"; // Add this import import * as events from "aws-cdk-lib/aws-events"; import * as targets from "aws-cdk-lib/aws-events-targets"; import * as ecr from "aws-cdk-lib/aws-ecr"; +import { requireEnv } from "./utils"; -interface ProjectStackProps extends cdk.StackProps { +export interface ProjectStackProps extends cdk.StackProps { /** * Path to the directory of the demo to deploy, relative to the root of the repository. */ projectName: string; + projectDescription: string; demoDir: string; - uniqueEnvironmentId: string; - /** - * Path to the workdir, relative to the root of the repository. By default, this will be the `demoDir`. - */ - overrideDockerWorkdir?: string; /** * Path to the Dockerfile to use, relative to the root of the repository. By default, this will be `${demoDir}/Dockerfile`. */ @@ -30,19 +27,23 @@ interface ProjectStackProps extends cdk.StackProps { environmentVariablesFromSecrets?: string[]; buildSecrets?: string[]; buildArgs?: Record; - port: number | string; + port: string; timeout?: number; memorySize?: number; includeInPRComment?: boolean; outputEnvVariable?: string; - ecrImageTag: string; + overrideBuildProps?: Partial; + imageTag: string; + outputs?: Record; } export class PreviewProjectStack extends cdk.Stack { fnUrl: string; constructor(scope: Construct, id: string, props: ProjectStackProps) { - const processedId = `${id}${props.uniqueEnvironmentId}`; + const uniqueEnvironmentId = requireEnv("UNIQUE_ENV_ID"); + const processedId = `${id}${uniqueEnvironmentId}`; + super(scope, processedId, props); const secrets = secretsmanager.Secret.fromSecretNameV2( @@ -87,35 +88,7 @@ export class PreviewProjectStack extends cdk.Stack { Object.assign(buildArgs, props.buildArgs); } - const dockerWorkdir = props.overrideDockerWorkdir ? - path.resolve(__dirname, "../../", props.overrideDockerWorkdir) : - path.resolve(__dirname, "../../", props.demoDir) - - // const fn = new lambda.Function(this, `Function`, { - // logGroup: logGroup, - // runtime: lambda.Runtime.FROM_IMAGE, - // architecture: lambda.Architecture.X86_64, - // handler: lambda.Handler.FROM_IMAGE, - // environment: { - // ...environmentVariables, - // PORT: props.port.toString(), - // AWS_LWA_INVOKE_MODE: "RESPONSE_STREAM", - // }, - // code: lambda.Code.fromAssetImage(dockerWorkdir, { - // platform: ecr_assets.Platform.LINUX_AMD64, - // buildSecrets, - // buildArgs, - // file: props.overrideDockerfile ? props.overrideDockerfile : "Dockerfile", - // }), - // timeout: cdk.Duration.seconds(props.timeout ?? 300), - // memorySize: props.memorySize ?? 1024, - // }); - - const ecrRepo = ecr.Repository.fromRepositoryName( - this, - "ExamplesRepo", - "coagents" - ); + const ecrRepository = ecr.Repository.fromRepositoryName(this, "ECRRepo", "coagents"); const fn = new lambda.Function(this, `Function`, { logGroup: logGroup, @@ -127,8 +100,8 @@ export class PreviewProjectStack extends cdk.Stack { PORT: props.port.toString(), AWS_LWA_INVOKE_MODE: "RESPONSE_STREAM", }, - code: lambda.Code.fromEcrImage(ecrRepo, { - tagOrDigest: props.ecrImageTag + code: lambda.Code.fromEcrImage(ecrRepository, { + tagOrDigest: props.imageTag }), timeout: cdk.Duration.seconds(props.timeout ?? 300), memorySize: props.memorySize ?? 1024, @@ -136,7 +109,7 @@ export class PreviewProjectStack extends cdk.Stack { const eventRule = new events.Rule(this, 'LambdaWarmUpSchedule', { - schedule: events.Schedule.rate(cdk.Duration.minutes(5)), + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); eventRule.addTarget(new targets.LambdaFunction(fn)); @@ -176,18 +149,24 @@ export class PreviewProjectStack extends cdk.Stack { value: props.projectName, }); - new cdk.CfnOutput(this, "UniqueEnvironmentId", { - value: `${props.uniqueEnvironmentId}`, + new cdk.CfnOutput(this, "ProjectDescription", { + value: props.projectDescription, }); - if (props.outputEnvVariable) { - new cdk.CfnOutput(this, "OutputEnvVariable", { - value: props.outputEnvVariable, - }); + new cdk.CfnOutput(this, "UniqueEnvironmentId", { + value: `${uniqueEnvironmentId}`, + }); + + if (props.outputs) { + for (const [key, value] of Object.entries(props.outputs)) { + new cdk.CfnOutput(this, key, { + value: value, + }); + } } // Add tag for PR number to all resources - cdk.Tags.of(this).add("env-id", props.uniqueEnvironmentId); + cdk.Tags.of(this).add("env-id", uniqueEnvironmentId); cdk.Tags.of(this).add("preview-env", "true"); } } diff --git a/infra/lib/utils.ts b/infra/lib/utils.ts index 0283ab088a..e6f7db98d4 100644 --- a/infra/lib/utils.ts +++ b/infra/lib/utils.ts @@ -1,7 +1,110 @@ +import { App } from "aws-cdk-lib"; +import { PreviewProjectStack } from "./demo-project-stack"; + export function requireEnv(name: string): string { const value = process.env[name]; if (!value) { throw new Error(`Required environment variable ${name} is missing`); } return value; -} \ No newline at end of file +} + +export function toCdkStackName(input: string) { + return input + .split("-") // Split the string by hyphens + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize each word + .join(""); // Join the words back together +} + +export function createAgentProjectStack({ + app, + project, + description, + dependencies, +}: { + app: App; + project: string; + description: string; + dependencies: "Remote" | "Local"; +}) { + const cdkStackName = + toCdkStackName(project) + "Agent" + dependencies + "Deps"; + const dockerfile = + dependencies === "Remote" + ? `examples/Dockerfile.agent-remote-deps` + : `examples/Dockerfile.agent-local-deps`; + const GITHUB_ACTIONS_RUN_ID = requireEnv("GITHUB_ACTIONS_RUN_ID"); + + const outputs: Record = { + Dependencies: dependencies, + }; + + if (process.env.GITHUB_PR_NUMBER) { + outputs["PRNumber"] = process.env.GITHUB_PR_NUMBER; + } + + return new PreviewProjectStack(app, cdkStackName, { + projectName: project, + projectDescription: description, + demoDir: `examples/${project}/agent`, + overrideDockerfile: dockerfile, + environmentVariablesFromSecrets: ["OPENAI_API_KEY", "TAVILY_API_KEY"], + port: "8000", + includeInPRComment: false, + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + }, + imageTag: `${project}-agent-${ + dependencies === "Remote" ? "remote-deps" : "local-deps" + }-${GITHUB_ACTIONS_RUN_ID}`, + outputs + }); +} + +export function createUIProjectStack({ + app, + project, + description, + dependencies, + agentProject, +}: { + app: App; + project: string; + description: string; + dependencies: "Remote" | "Local"; + agentProject: PreviewProjectStack; +}) { + const cdkStackName = toCdkStackName(project) + "UI" + dependencies + "Deps"; + const dockerfile = + dependencies === "Remote" + ? `examples/Dockerfile.ui-remote-deps` + : `examples/Dockerfile.ui-local-deps`; + const GITHUB_ACTIONS_RUN_ID = requireEnv("GITHUB_ACTIONS_RUN_ID"); + + const outputs: Record = { + Dependencies: dependencies, + }; + + if (process.env.GITHUB_PR_NUMBER) { + outputs["PRNumber"] = process.env.GITHUB_PR_NUMBER; + } + + return new PreviewProjectStack(app, cdkStackName, { + projectName: project, + projectDescription: `${description} (Dependencies: ${dependencies})`, + demoDir: `examples/${project}/ui`, + overrideDockerfile: dockerfile, + environmentVariablesFromSecrets: ["OPENAI_API_KEY"], + environmentVariables: { + REMOTE_ACTION_URL: `${agentProject.fnUrl}/copilotkit`, + }, + buildSecrets: ["OPENAI_API_KEY"], + port: "3000", + includeInPRComment: true, + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + }, + imageTag: `${project}-ui-${GITHUB_ACTIONS_RUN_ID}`, + outputs, + }); +}