Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions .github/actions/buildAllAndDeploy/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ inputs:
tf-state-bucket:
description: "Terraform state bucket"
required: true
openai-api-key:
description: "OpenAI API key"
required: false
default: ""
ahr-api-key:
description: "AHR API key"
required: true

anthropic-api-key:
description: "Claude API key"
required: true

outputs:
data-server-url:
description: "URL of the deployed data server"
Expand Down Expand Up @@ -97,13 +96,11 @@ runs:
- name: Build and Push Frontend Image
id: frontend
uses: SatcherInstitute/health-equity-tracker/.github/actions/buildAndPush@main
env:
VITE_OPENAI_API_KEY: ${{ inputs.openai-api-key }}
with:
dockerfile: "frontend_server/Dockerfile"
image-path: "gcr.io/${{ inputs.project-id }}/frontend"
deploy-context: ${{ inputs.environment }}
openai-api-key: ${{ inputs.openai-api-key }}
anthropic-api-key: ${{ inputs.anthropic-api-key }}

# Terraform and deployment
- name: Setup Terraform
Expand Down Expand Up @@ -140,6 +137,7 @@ runs:
-var 'data_server_image_digest=${{ steps.serving.outputs.image-digest }}' \
-var 'exporter_image_digest=${{ steps.exporter.outputs.image-digest }}' \
-var 'frontend_image_digest=${{ steps.frontend.outputs.image-digest }}' \
-var 'anthropic_api_key=${{ inputs.anthropic-api-key }}' \

data_server_url=$(terraform output data_server_url)
echo "data_server_url=$data_server_url" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -168,3 +166,4 @@ runs:
FRONTEND_URL: ${{ steps.terraform.outputs.frontend_url }}
PATH_TO_SA_CREDS: config/creds.json
AHR_API_KEY: ${{ inputs.ahr-api-key }}
ANTHROPIC_API_KEY: ${{ inputs.anthropic-api-key }}
9 changes: 5 additions & 4 deletions .github/actions/buildAndPush/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ inputs:
ahr-api-key:
description: "AHR API key used to fetch AHR data from the GraphQL endpoint"
required: false
anthropic-api-key:
description: "Claude API key used to fetch insights from Claude endpoint"
required: false
dockerfile:
description: "Relative path to dockerfile"
required: true
Expand All @@ -17,9 +20,7 @@ inputs:
deploy-context:
description: 'String value for deploy context. Should be "prod" or "dev". Only used for the frontend'
required: false
openai-api-key:
description: "OpenAI API key for generating insights"
required: false

outputs:
image-digest:
description: "Digest of image pushed to GCR"
Expand All @@ -36,7 +37,7 @@ runs:
docker build -t ${{ inputs.image-path }} -f ${{ inputs.dockerfile }} ${{ inputs.build-directory }} \
--build-arg="DEPLOY_CONTEXT=${{ inputs.deploy-context }}" \
--build-arg="AHR_API_KEY=${{ inputs.ahr-api-key }}" \
--build-arg="OPENAI_API_KEY=${{ inputs.openai-api-key }}"
--build-arg="ANTHROPIC_API_KEY"=${{ inputs.anthropic-api-key }}
shell: bash
- run: docker push ${{ inputs.image-path }}
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deployInfraTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
project-id: ${{ secrets.TEST_PROJECT_ID }}
tf-state-bucket: ${{ secrets.TEST_TF_STATE_BUCKET }}
ahr-api-key: ${{ secrets.AHR_API_KEY }}
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
1 change: 0 additions & 1 deletion .github/workflows/runDestroyInfraTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ jobs:
dockerfile: 'frontend_server/Dockerfile'
image-path: 'gcr.io/${{ secrets.TEST_PROJECT_ID }}/frontend'
deploy-context: 'dev'
openai-api-key: ${{ secrets.OPENAI_API_KEY }}

deploy:
if: github.repository == 'SatcherInstitute/health-equity-tracker'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testBackendChangesInfraTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
project-id: ${{ secrets.TEST_PROJECT_ID }}
tf-state-bucket: ${{ secrets.TEST_TF_STATE_BUCKET }}
ahr-api-key: ${{ secrets.AHR_API_KEY }}
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
5 changes: 5 additions & 0 deletions config/run.tf
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ resource "google_cloud_run_service" "frontend_service" {
value = google_cloud_run_service.data_server_service.status.0.url
}

env {
name = "ANTHROPIC_API_KEY"
value = var.anthropic_api_key
}

resources {
limits = {
memory = "8Gi"
Expand Down
6 changes: 6 additions & 0 deletions config/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,9 @@ variable "frontend_runner_identity_id" {
description = "Account id of the service account used when running the frontend service"
type = string
}

variable "anthropic_api_key" {
description = "Anthropic API key for AI insights"
type = string
sensitive = true
}
82 changes: 75 additions & 7 deletions frontend/src/cards/CardWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { AutoAwesome, DeleteForever } from '@mui/icons-material'
import { CircularProgress } from '@mui/material'
import IconButton from '@mui/material/IconButton'
import { useState } from 'react'
import type {
MetricQuery,
MetricQueryResponse,
MetricQueryResponse
} from '../data/query/MetricQuery'
import { WithMetadataAndMetrics } from '../data/react/WithLoadingOrErrorUI'
import type { MapOfDatasetMetadata } from '../data/utils/DatasetTypes'
import { splitIntoKnownsAndUnknowns } from '../data/utils/datasetutils'
import type { Fips } from '../data/utils/Fips'
import { SHOW_INSIGHT_GENERATION } from '../featureFlags'
import { generateCardInsight } from '../utils/generateCardInsight'
import type { ScrollableHashId } from '../utils/hooks/useStepObserver'
import CardOptionsMenu from './ui/CardOptionsMenu'
import InsightDisplay from './ui/InsightDisplay'
import InsightCard from './ui/InsightCard'
import { Sources } from './ui/Sources'

function CardWrapper(props: {
Expand Down Expand Up @@ -36,7 +42,12 @@ function CardWrapper(props: {
shareConfig?: any
demographicType?: any
metricIds?: any
fips?: Fips
}) {
const [insight, setInsight] = useState<string>('')
const [isGeneratingInsight, setIsGeneratingInsight] = useState<boolean>(false)
const [rateLimitReached, setRateLimitReached] = useState<boolean>(false)

const loadingComponent = (
<div
className={`relative m-2 flex justify-center rounded bg-white p-3 shadow-raised ${props.className}`}
Expand All @@ -48,6 +59,7 @@ function CardWrapper(props: {
)

const shouldShowInsightDisplay = SHOW_INSIGHT_GENERATION && props.shareConfig
const showInsightButton = shouldShowInsightDisplay && !rateLimitReached

return (
<WithMetadataAndMetrics
Expand All @@ -56,22 +68,78 @@ function CardWrapper(props: {
queries={props.queries ?? []}
>
{(metadata, queryResponses, geoData) => {
const queryResponse = queryResponses[0]

const handleGenerateInsight = async () => {
if (!props.shareConfig || !props.metricIds?.length) return

const validData = queryResponse.getValidRowsForField(
props.shareConfig.metricId,
)
const [knownData] = splitIntoKnownsAndUnknowns(
validData,
props.demographicType,
)
if (!knownData.length) return

setIsGeneratingInsight(true)
try {
const result = await generateCardInsight(
{ knownData, metricIds: props.metricIds },
props.scrollToHash,
props.fips,
)
if (result.rateLimited) {
setRateLimitReached(true)
} else {
setInsight(result.content)
}
} finally {
setIsGeneratingInsight(false)
}
}

const handleClearInsight = () => setInsight('')

return (
<article
className={`relative m-2 rounded-sm bg-white p-3 shadow-raised ${props.className}`}
>
{shouldShowInsightDisplay && (
<InsightDisplay
<InsightCard
demographicType={props.demographicType}
metricIds={props.metricIds}
queryResponses={queryResponses}
shareConfig={props.shareConfig}
hashId={props.scrollToHash}
fips={props.fips}
insight={insight}
isGeneratingInsight={isGeneratingInsight}
/>
)}
<CardOptionsMenu
reportTitle={props.reportTitle}
scrollToHash={props.scrollToHash}
/>
<div className='absolute top-2 right-2 z-10 flex items-center gap-1'>
{showInsightButton && (
<IconButton
aria-label={insight ? 'Clear insight' : 'Generate insight'}
onClick={insight ? handleClearInsight : handleGenerateInsight}
disabled={isGeneratingInsight}
size='small'
>
{isGeneratingInsight ? (
<CircularProgress size={20} />
) : insight ? (
<DeleteForever fontSize='small' />
) : (
<AutoAwesome fontSize='small' />
)}
</IconButton>
)}
<CardOptionsMenu
reportTitle={props.reportTitle}
scrollToHash={props.scrollToHash}
/>
</div>

{props.children(queryResponses, metadata, geoData)}
{!props.hideFooter && props.queries && (
<Sources
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/cards/MapCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ function MapCardWithKey(props: MapCardProps) {
expanded={isExtremesMode}
isCompareCard={props.isCompareCard}
className={props.className}
shareConfig={metricConfig}
metricIds={[metricConfig.metricId]}
fips={props.fips}
>
{(queryResponses, metadata, geoData) => {
// contains rows for sub-geos (if viewing US, this data will be STATE level)
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/cards/RateBarChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ export default function RateBarChartCard(props: RateBarChartCardProps) {
reportTitle={props.reportTitle}
className={props.className}
hasIntersectionalAllCompareBar={rateComparisonConfig !== undefined}
shareConfig={rateConfig}
metricIds={[rateConfig.metricId]}
fips={props.fips}
>
{([rateQueryResponseRate, rateQueryResponseRateAlls], metadata) => {
// for consistency, filter out any 'Unknown' rows that might have rates (like PHRMA)
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/cards/RateTrendsChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ export default function RateTrendsChartCard(props: RateTrendsChartCardProps) {
reportTitle={props.reportTitle}
expanded={a11yTableExpanded}
className={props.className}
shareConfig={metricConfigRates}
metricIds={[metricConfigRates.metricId]}
fips={props.fips}
>
{([queryResponseRates, queryResponsePctShares]) => {
let ratesData = queryResponseRates.getValidRowsForField(
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/cards/ShareTrendsChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ export default function ShareTrendsChartCard(props: ShareTrendsChartCardProps) {
reportTitle={props.reportTitle}
expanded={a11yTableExpanded}
className={props.className}
shareConfig={metricConfigInequitable}
metricIds={[metricConfigInequitable.metricId]}
fips={props.fips}
>
{([queryResponseInequity, queryResponsePctShares]) => {
const inequityData = queryResponseInequity.getValidRowsForField(
Expand Down
1 change: 1 addition & 0 deletions frontend/src/cards/StackedSharesBarChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export default function StackedSharesBarChartCard(
className={props.className}
shareConfig={shareConfig}
metricIds={metricIds}
fips={props.fips}
>
{([queryResponse]) => {
const validData = queryResponse.getValidRowsForField(
Expand Down
Loading
Loading