diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx index 57a2a628f12..8301eab7994 100644 --- a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx +++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/overview/route.tsx @@ -198,7 +198,7 @@ function RouteComponent() { {shouldDisplayArgoCdServicesBelowQovery && } {isServicesListTab && isAgenticWorkflowEnabled && ( - + )} diff --git a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx index 6e156acdc89..531b3e9f1a0 100644 --- a/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx +++ b/apps/console/src/routes/_authenticated/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview.tsx @@ -6,6 +6,7 @@ import { useEnvironment } from '@qovery/domains/environments/feature' import { EnableObservabilityModal } from '@qovery/domains/observability/feature' import { JobStatusesCallout } from '@qovery/domains/service-job/feature' import { TerraformResourcesSection } from '@qovery/domains/service-terraform/feature' +import { isAgenticWorkflow } from '@qovery/domains/services/data-access' import { ObservabilityCallout, ServiceOverview, useService } from '@qovery/domains/services/feature' import { MetricsWebSocketListener } from '@qovery/shared/util-web-sockets' @@ -53,7 +54,7 @@ function RouteComponent() { environment && } /> - {environment && service?.serviceType && ( + {environment && service?.serviceType && !isAgenticWorkflow(service) && ( ARGOCD_SERVICE_TAB_IDS.includes(tab.id)) } + if (isAgenticWorkflow(service)) { + return SERVICE_TABS.filter((tab) => AGENTIC_WORKFLOW_SERVICE_TAB_IDS.includes(tab.id)) + } + const isDatabase = service?.serviceType === 'DATABASE' const isManagedDatabaseService = isManagedDatabase(service) const hasMonitoring = hasServiceMonitoringTab(service, cluster) @@ -564,7 +570,7 @@ function OrganizationRoute() { name: service.name, description: 'description' in service ? service.description || '' : '', icon_uri: service.icon_uri, - service_type: service.service_type, + service_type: isAgenticWorkflow(service) ? ServiceTypeEnum.ARGOCD_APP : service.service_type, project_id: project.id, project_name: project.name, environment_id: environment.id, diff --git a/libs/domains/services/data-access/src/lib/domains-services-data-access.ts b/libs/domains/services/data-access/src/lib/domains-services-data-access.ts index 1be8f1db97c..4bc968deff7 100644 --- a/libs/domains/services/data-access/src/lib/domains-services-data-access.ts +++ b/libs/domains/services/data-access/src/lib/domains-services-data-access.ts @@ -379,19 +379,6 @@ export const services = createQueryKeys('services', { ) }, }), - listAgenticWorkflows: (environmentId: string) => ({ - queryKey: [environmentId], - async queryFn(): Promise { - const response = await agenticWorkflowsApi.listAgenticWorkflows(environmentId) - return ( - response.data.results?.map((service) => ({ - ...service, - serviceType: 'AGENTIC_WORKFLOW', - icon_uri: 'app://qovery-console/agentic-workflow', - })) ?? [] - ) - }, - }), argocdManifest: (serviceId: string) => ({ queryKey: [serviceId], async queryFn() { diff --git a/libs/domains/services/feature/src/index.ts b/libs/domains/services/feature/src/index.ts index 980dfa1af3f..76e88f44842 100644 --- a/libs/domains/services/feature/src/index.ts +++ b/libs/domains/services/feature/src/index.ts @@ -68,7 +68,6 @@ export * from './lib/hooks/use-favorite-services/use-favorite-services' export * from './lib/service-actions/service-actions' export * from './lib/argocd-service-list/argocd-service-list' export * from './lib/agentic-workflow-service-list/agentic-workflow-service-list' -export * from './lib/hooks/use-agentic-workflow-services/use-agentic-workflow-services' export * from './lib/argocd-manifest/argocd-manifest' export * from './lib/service-deployment-status-label/service-deployment-status-label' export * from './lib/service-overview/service-header/service-header' diff --git a/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.spec.tsx b/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.spec.tsx index 3d2bf230e10..96afeef8b61 100644 --- a/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.spec.tsx +++ b/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.spec.tsx @@ -1,33 +1,87 @@ +import { type Environment } from 'qovery-typescript-axios' +import { type ReactNode } from 'react' import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { AgenticWorkflowServiceList } from './agentic-workflow-service-list' -const mockUseAgenticWorkflowServices = jest.fn() +const mockUseServices = jest.fn() +const mockNavigate = jest.fn() +const environment = { + id: 'environment-1', + organization: { id: 'organization-1' }, + project: { id: 'project-1' }, +} as Environment -jest.mock('../hooks/use-agentic-workflow-services/use-agentic-workflow-services', () => ({ - useAgenticWorkflowServices: () => mockUseAgenticWorkflowServices(), +jest.mock('@tanstack/react-router', () => ({ + useNavigate: () => mockNavigate, + useParams: () => ({ organizationId: 'organization-1', projectId: 'project-1' }), + Link: ({ children, params, to, ...props }: { children: ReactNode; params: Record; to: string }) => ( + path.replace(`$${key}`, value), to)}> + {children} + + ), +})) + +jest.mock('../hooks/use-services/use-services', () => ({ + useServices: () => mockUseServices(), +})) + +jest.mock('../service-actions/service-actions', () => ({ + ServiceActions: ({ serviceId }: { serviceId: string }) => , })) describe('AgenticWorkflowServiceList', () => { it('should not render an empty section', () => { - mockUseAgenticWorkflowServices.mockReturnValue({ data: [] }) - const { container } = renderWithProviders() + mockUseServices.mockReturnValue({ data: [] }) + const { container } = renderWithProviders() expect(container).toBeEmptyDOMElement() }) it('should render agentic workflows in their own section', () => { - mockUseAgenticWorkflowServices.mockReturnValue({ + mockUseServices.mockReturnValue({ data: [ - { id: 'workflow-1', name: 'Review pull requests', enabled: true, model: { type: 'CLAUDE' } }, - { id: 'workflow-2', name: 'Triage incidents', enabled: false, model: { type: 'OPENAI' } }, + { + id: 'workflow-1', + name: 'Review pull requests', + enabled: true, + model: { type: 'CLAUDE' }, + service_type: 'AGENTIC_WORKFLOW', + serviceType: 'AGENTIC_WORKFLOW', + project_repositories: [ + { url: 'https://github.com/qovery/console', branch: 'staging', git_token_id: 'token-1' }, + { url: 'https://github.com/qovery/docs', branch: 'main', git_token_id: 'token-1' }, + ], + }, + { + id: 'workflow-2', + name: 'Triage incidents', + enabled: false, + model: { type: 'BEDROCK' }, + service_type: 'AGENTIC_WORKFLOW', + serviceType: 'AGENTIC_WORKFLOW', + project_repositories: [], + }, + { id: 'application-1', name: 'API', service_type: 'APPLICATION', serviceType: 'APPLICATION' }, ], }) - renderWithProviders() + renderWithProviders() expect(screen.getByRole('heading', { name: 'Agentic workflows' })).toBeInTheDocument() expect(screen.getByText('Review pull requests')).toBeInTheDocument() expect(screen.getByText('Triage incidents')).toBeInTheDocument() + expect(screen.getByText('Enabled')).toBeInTheDocument() + expect(screen.getByText('Disabled')).toBeInTheDocument() expect(screen.getByText('1 enabled')).toBeInTheDocument() - expect(screen.getByText('CLAUDE')).toBeInTheDocument() + expect(screen.getByText('Last operation')).toBeInTheDocument() + expect(screen.getByText('Model')).toBeInTheDocument() + expect(screen.getByText('Claude')).toBeInTheDocument() + expect(screen.getByText('Bedrock')).toBeInTheDocument() + expect(screen.queryByText('Git repositories')).not.toBeInTheDocument() + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'Actions for workflow-1' })).toBeInTheDocument() + expect(screen.getByRole('link', { name: 'Review pull requests' })).toHaveAttribute( + 'href', + expect.stringContaining('/service/workflow-1/overview') + ) }) }) diff --git a/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.tsx b/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.tsx index c8b739fb459..e9038e316bc 100644 --- a/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.tsx +++ b/libs/domains/services/feature/src/lib/agentic-workflow-service-list/agentic-workflow-service-list.tsx @@ -1,22 +1,64 @@ -import { type AgenticWorkflowResponse } from 'qovery-typescript-axios' -import { Badge, Heading, Icon, Section, TablePrimitives, Tooltip } from '@qovery/shared/ui' -import { useAgenticWorkflowServices } from '../hooks/use-agentic-workflow-services/use-agentic-workflow-services' +import { useNavigate } from '@tanstack/react-router' +import { type Environment } from 'qovery-typescript-axios' +import { type KeyboardEvent, type MouseEvent } from 'react' +import { match } from 'ts-pattern' +import { type AgenticWorkflow, isAgenticWorkflow } from '@qovery/domains/services/data-access' +import { IconEnum } from '@qovery/shared/enums' +import { Badge, Heading, Icon, Section, TablePrimitives } from '@qovery/shared/ui' +import { useServices } from '../hooks/use-services/use-services' +import { ServiceActions } from '../service-actions/service-actions' +import { ServiceLastDeploymentCell, ServiceNameCell } from '../service-list/service-list-cells' +import { ServiceStateChip } from '../service-state-chip/service-state-chip' const { Table } = TablePrimitives -const tableGridLayoutClassName = 'grid w-full grid-cols-[minmax(350px,1fr)_minmax(180px,260px)_140px]' +const tableGridLayoutClassName = + 'grid w-full grid-cols-[minmax(280px,1.1fr)_minmax(300px,1fr)_minmax(200px,0.7fr)_130px]' export interface AgenticWorkflowServiceListProps { - environmentId: string + environment: Environment } -export function AgenticWorkflowServiceList({ environmentId }: AgenticWorkflowServiceListProps) { - const { data: services = [] } = useAgenticWorkflowServices({ environmentId, suspense: true }) +function ModelCell({ service }: { service: AgenticWorkflow }) { + return match(service.model?.type) + .with('CLAUDE', () => ( + + + Claude + + )) + .with('BEDROCK', () => ( + + + Bedrock + + )) + .otherwise((model) => {model ?? 'Not configured'}) +} + +export function AgenticWorkflowServiceList({ environment }: AgenticWorkflowServiceListProps) { + const environmentId = environment.id + const organizationId = environment.organization.id + const projectId = environment.project.id + const navigate = useNavigate() + const { data: allServices = [] } = useServices({ environmentId, suspense: true }) + const services = allServices.filter(isAgenticWorkflow) as AgenticWorkflow[] if (services.length === 0) return null const enabledServicesCount = services.filter(({ enabled }) => enabled).length + const handleNavigateToService = (serviceId: string) => { + navigate({ + to: '/organization/$organizationId/project/$projectId/environment/$environmentId/service/$serviceId/overview', + params: { organizationId, projectId, environmentId, serviceId }, + }) + } + + const stopRowNavigation = (event: MouseEvent | KeyboardEvent) => { + event.stopPropagation() + } + return (
@@ -34,39 +76,51 @@ export function AgenticWorkflowServiceList({ environmentId }: AgenticWorkflowSer
Service + + Last operation + Model - Status + Actions - {services.map((service: AgenticWorkflowResponse) => ( - - -
- - - {service.name} - + {services.map((service) => ( + handleNavigateToService(service.id)} + onKeyDown={(event) => { + if (event.key === 'Enter') handleNavigateToService(service.id) + }} + > + +
+
+
- - {service.model?.type ?? '-'} + + + + + - - {service.enabled ? 'Enabled' : 'Disabled'} - +
+ +
))} diff --git a/libs/domains/services/feature/src/lib/hooks/use-agentic-workflow-services/use-agentic-workflow-services.ts b/libs/domains/services/feature/src/lib/hooks/use-agentic-workflow-services/use-agentic-workflow-services.ts deleted file mode 100644 index f712ad971e8..00000000000 --- a/libs/domains/services/feature/src/lib/hooks/use-agentic-workflow-services/use-agentic-workflow-services.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { queries } from '@qovery/state/util-queries' - -export interface UseAgenticWorkflowServicesProps { - environmentId: string - suspense?: boolean -} - -export function useAgenticWorkflowServices({ environmentId, suspense = false }: UseAgenticWorkflowServicesProps) { - return useQuery({ - ...queries.services.listAgenticWorkflows(environmentId), - suspense, - }) -} - -export default useAgenticWorkflowServices diff --git a/libs/domains/services/feature/src/lib/hooks/use-running-status/use-running-status.ts b/libs/domains/services/feature/src/lib/hooks/use-running-status/use-running-status.ts index d31c461f263..3bb5d34be9a 100644 --- a/libs/domains/services/feature/src/lib/hooks/use-running-status/use-running-status.ts +++ b/libs/domains/services/feature/src/lib/hooks/use-running-status/use-running-status.ts @@ -4,14 +4,14 @@ import { queries } from '@qovery/state/util-queries' export interface UseRunningStatusProps { environmentId?: string serviceId?: string + enabled?: boolean suspense?: boolean } -export function useRunningStatus({ environmentId, serviceId, suspense }: UseRunningStatusProps) { +export function useRunningStatus({ environmentId, serviceId, enabled = true, suspense }: UseRunningStatusProps) { return useQuery({ - // eslint-disable-next-line @typescript-eslint/no-extra-non-null-assertion - ...queries.services.runningStatus(environmentId!!, serviceId!!), - enabled: Boolean(environmentId) && Boolean(serviceId), + ...queries.services.runningStatus(environmentId ?? '', serviceId ?? ''), + enabled: enabled && Boolean(environmentId) && Boolean(serviceId), refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, diff --git a/libs/domains/services/feature/src/lib/hooks/use-service/use-service.ts b/libs/domains/services/feature/src/lib/hooks/use-service/use-service.ts index 4c215c8bd1a..135821bafa2 100644 --- a/libs/domains/services/feature/src/lib/hooks/use-service/use-service.ts +++ b/libs/domains/services/feature/src/lib/hooks/use-service/use-service.ts @@ -1,5 +1,7 @@ import { type UseQueryResult, useQuery } from '@tanstack/react-query' import { + type AgenticWorkflow, + type AgenticWorkflowType, type AnyService, type Application, type ApplicationType, @@ -55,23 +57,25 @@ export function useService< T extends ServiceType, R = T extends ApplicationType ? Application - : T extends ArgoCdType - ? ArgoCd - : T extends ContainerType - ? Container - : T extends DatabaseType - ? Database - : T extends JobType - ? Job - : T extends 'CRON_JOB' + : T extends AgenticWorkflowType + ? AgenticWorkflow + : T extends ArgoCdType + ? ArgoCd + : T extends ContainerType + ? Container + : T extends DatabaseType + ? Database + : T extends JobType ? Job - : T extends 'LIFECYCLE_JOB' + : T extends 'CRON_JOB' ? Job - : T extends HelmType - ? Helm - : T extends TerraformType - ? Terraform - : never, + : T extends 'LIFECYCLE_JOB' + ? Job + : T extends HelmType + ? Helm + : T extends TerraformType + ? Terraform + : never, >(props: { serviceId: string; serviceType: T; suspense?: boolean }): UseQueryResult export function useService({ serviceId, suspense = false, ...props }: UseServiceProps) { const { data: serviceType } = useServiceType({ diff --git a/libs/domains/services/feature/src/lib/service-avatar/service-avatar.tsx b/libs/domains/services/feature/src/lib/service-avatar/service-avatar.tsx index 284ddabc4f5..c9917b5a2e5 100644 --- a/libs/domains/services/feature/src/lib/service-avatar/service-avatar.tsx +++ b/libs/domains/services/feature/src/lib/service-avatar/service-avatar.tsx @@ -34,10 +34,10 @@ export const ServiceAvatar = forwardRef, ServiceAvatar .with({ serviceType: 'JOB', job_type: 'LIFECYCLE' }, () => 'LIFECYCLE_JOB') .with({ serviceType: 'JOB', job_type: 'CRON' }, () => 'CRON_JOB') .with({ serviceType: 'ARGOCD_APP' }, () => 'ARGOCD') + .with({ serviceType: 'AGENTIC_WORKFLOW' }, () => 'AGENTIC_WORKFLOW') .with({ serviceType: 'APPLICATION' }, { serviceType: 'CONTAINER' }, () => 'APPLICATION') .with({ serviceType: 'DATABASE' }, () => 'DATABASE') .with({ serviceType: 'TERRAFORM' }, () => 'TERRAFORM') - .with({ serviceType: 'AGENTIC_WORKFLOW' }, () => 'AGENTIC_WORKFLOW') .exhaustive() const serviceAvatar = ServiceIcons[service.icon_uri as IconURI] diff --git a/libs/domains/services/feature/src/lib/service-blueprint-update-flow/blueprint-update-flow.tsx b/libs/domains/services/feature/src/lib/service-blueprint-update-flow/blueprint-update-flow.tsx index e7904cbe520..f24b9387255 100644 --- a/libs/domains/services/feature/src/lib/service-blueprint-update-flow/blueprint-update-flow.tsx +++ b/libs/domains/services/feature/src/lib/service-blueprint-update-flow/blueprint-update-flow.tsx @@ -59,12 +59,12 @@ export function BlueprintUpdateFlow({ const { mutateAsync: deployBlueprint, isLoading: isDeployLoading } = useDeployBlueprint({ environmentId, serviceId: service.id, - serviceType: service.service_type, + serviceType: service.serviceType, }) const { mutateAsync: updateBlueprint, isLoading: isUpdateLoading } = useUpdateBlueprint({ environmentId, serviceId: service.id, - serviceType: service.service_type, + serviceType: service.serviceType, }) const [activeSection, setActiveSection] = useState(() => blueprintUpdate ? getFirstAvailableUpdateSection(blueprintUpdate) : 'required' diff --git a/libs/domains/services/feature/src/lib/service-creation-flow/agentic-workflow/agentic-workflow-configuration/agentic-workflow-configuration.tsx b/libs/domains/services/feature/src/lib/service-creation-flow/agentic-workflow/agentic-workflow-configuration/agentic-workflow-configuration.tsx index 2494f20cf56..1addec9bb63 100644 --- a/libs/domains/services/feature/src/lib/service-creation-flow/agentic-workflow/agentic-workflow-configuration/agentic-workflow-configuration.tsx +++ b/libs/domains/services/feature/src/lib/service-creation-flow/agentic-workflow/agentic-workflow-configuration/agentic-workflow-configuration.tsx @@ -80,13 +80,15 @@ export function isOutputComplete(output: AgenticWorkflowOutput) { function AgenticWorkflowSection({ children, headerAction, + icon, iconName, invalid, section, }: { children: ReactNode headerAction?: ReactNode - iconName: IconName + icon?: ReactNode + iconName?: IconName invalid?: boolean section: AgenticWorkflowConfigurationSection }) { @@ -98,7 +100,7 @@ function AgenticWorkflowSection({ const headerContent = ( <>
- + {icon ?? (iconName ? : null)} {title}
@@ -349,7 +351,11 @@ export function AgenticWorkflowConfiguration() { - + } + invalid={sectionInvalid['ai-model']} + > ) }) + .with({ serviceType: 'AGENTIC_WORKFLOW' }, (workflow) => ( + + + + {workflow.name} + + + + {workflow.enabled ? 'Enabled' : 'Disabled'} + + + )) .otherwise(() => ( ({ @@ -359,7 +370,13 @@ describe('ServiceHeader', () => { }) const renderServiceHeader = ( - serviceId: 'application-mock' | 'database-mock' | 'job-mock' | 'argocd-mock' | 'terraform-mock' + serviceId: + | 'application-mock' + | 'database-mock' + | 'job-mock' + | 'argocd-mock' + | 'terraform-mock' + | 'agentic-workflow-mock' ) => renderWithProviders() it('renders application details and git metadata', () => { @@ -409,6 +426,16 @@ describe('ServiceHeader', () => { expect(screen.getByRole('button', { name: /313a525/i })).toBeInTheDocument() }) + it('renders agentic workflow model and enabled state', () => { + renderServiceHeader('agentic-workflow-mock') + + expect(screen.getByRole('heading', { name: 'Review pull requests' })).toBeInTheDocument() + const modelBadge = screen.getByText('Claude').parentElement + expect(modelBadge).toBeInTheDocument() + expect(screen.getByText('Enabled')).toBeInTheDocument() + expect(modelBadge?.querySelector('img')).toHaveAttribute('src', '/assets/ai-tools/claude.svg') + }) + it('renders an up to date badge for a current blueprint service', () => { mockUseBlueprintUpdate.mockReturnValue({ data: { diff --git a/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx index 35da9b144e4..d84cac9b58e 100644 --- a/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx +++ b/libs/domains/services/feature/src/lib/service-overview/service-header/service-header.tsx @@ -8,7 +8,7 @@ import { useCluster, useClusterRunningStatusSocket, } from '@qovery/domains/clusters/feature' -import { type AnyService, isArgoCd } from '@qovery/domains/services/data-access' +import { type AnyService, isAgenticWorkflow, isArgoCd } from '@qovery/domains/services/data-access' import { IconEnum, ServiceTypeEnum, @@ -95,6 +95,7 @@ function ServiceHeaderIdentity({ environment, service }: ServiceHeaderIdentityPr const { data: cluster } = useCluster({ organizationId, clusterId: environment.cluster_id, suspense: true }) const isArgoCdService = isArgoCd(service) + const isAgenticWorkflowService = isAgenticWorkflow(service) useClusterRunningStatusSocket({ organizationId, clusterId: environment.cluster_id }) @@ -150,7 +151,7 @@ function ServiceHeaderIdentity({ environment, service }: ServiceHeaderIdentityPr
{isArgoCdService ? ( - ) : ( + ) : isAgenticWorkflowService ? null : ( )}
@@ -237,17 +238,19 @@ function ServiceHeaderMetadata({ service }: ServiceHeaderMetadataProps) { const { organizationId = '', projectId = '', environmentId = '', serviceId = '' } = useParams({ strict: false }) const { data: masterCredentials } = useMasterCredentials({ serviceId, - serviceType: service?.service_type, + serviceType: isAgenticWorkflow(service) ? undefined : service?.service_type, }) const [, copyToClipboard] = useCopyToClipboard() const blueprintId = 'blueprint_id' in service ? service.blueprint_id : undefined - const gitRepository = match(service) - .with({ serviceType: 'APPLICATION' }, ({ git_repository }) => git_repository) - .with({ serviceType: 'JOB', source: P.when(isJobGitSource) }, ({ source }) => source.docker?.git_repository) - .with({ serviceType: 'HELM', source: P.when(isHelmGitSource) }, ({ source }) => source.git?.git_repository) - .with({ serviceType: 'TERRAFORM' }, ({ terraform_files_source }) => terraform_files_source?.git?.git_repository) - .with({ serviceType: 'ARGOCD_APP' }, ({ git_repository }) => git_repository) - .otherwise(() => undefined) + const gitRepository = isAgenticWorkflow(service) + ? undefined + : match(service) + .with({ serviceType: 'APPLICATION' }, ({ git_repository }) => git_repository) + .with({ serviceType: 'JOB', source: P.when(isJobGitSource) }, ({ source }) => source.docker?.git_repository) + .with({ serviceType: 'HELM', source: P.when(isHelmGitSource) }, ({ source }) => source.git?.git_repository) + .with({ serviceType: 'TERRAFORM' }, ({ terraform_files_source }) => terraform_files_source?.git?.git_repository) + .with({ serviceType: 'ARGOCD_APP' }, ({ git_repository }) => git_repository) + .otherwise(() => undefined) const containerImage = match(service) .with({ serviceType: ServiceTypeEnum.JOB, source: P.when(isJobContainerSource) }, ({ source }) => source.image) @@ -273,6 +276,7 @@ function ServiceHeaderMetadata({ service }: ServiceHeaderMetadataProps) { .otherwise(() => undefined) const isArgoCdService = isArgoCd(service) + const isAgenticWorkflowService = isAgenticWorkflow(service) const handleCopyCredentials = (credentials: Credentials) => { if (!databaseSource) { @@ -382,6 +386,29 @@ function ServiceHeaderMetadata({ service }: ServiceHeaderMetadataProps) { )} )} + {isAgenticWorkflowService && ( + <> + + {match(service.model.type) + .with('CLAUDE', () => ( + <> + + Claude + + )) + .with('BEDROCK', () => ( + <> + + Bedrock + + )) + .otherwise((model) => model)} + + + {service.enabled ? 'Enabled' : 'Disabled'} + + + )} {'auto_deploy' in service && service.auto_deploy && match(service) @@ -415,7 +442,7 @@ function ServiceHeaderMetadata({ service }: ServiceHeaderMetadataProps) { ) : null} */} - {!isArgoCdService && ( + {!isArgoCdService && !isAgenticWorkflowService && ( service?.serviceType === 'JOB' && service.job_type === 'LIFECYCLE', [service]) const isTerraformService = useMemo(() => service?.serviceType === 'TERRAFORM', [service]) const isKedaAutoscaling = useMemo( () => @@ -208,7 +208,7 @@ function ServiceOverviewContent({ {hasNoMetrics && observabilityCallout} {isEditableService(service) && } - {!isTerraformService && isEditableService(service) && ( + {!isTerraformService && (isEditableService(service) || isAgenticWorkflow(service)) && ( )} {isArgoCd(service) && }