diff --git a/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx b/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx index b0e2447b2..e718b90a8 100644 --- a/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx @@ -2,13 +2,27 @@ import { IncidentChat } from "./incident-chat"; import { IncidentDto } from "@/entities/incidents/model"; +import { EmptyStateCard } from "@/shared/ui"; +import { useConfig } from "@/utils/hooks/useConfig"; import { CopilotKit } from "@copilotkit/react-core"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; export function IncidentChatClientPage({ incident, }: { incident: IncidentDto; }) { + const { data: config } = useConfig(); + if (config && !config.OPEN_AI_API_KEY_SET) { + return ( + + ); + } + return ( diff --git a/keep-ui/app/api/copilotkit/route.ts b/keep-ui/app/api/copilotkit/route.ts index d8ff2a8dc..e8cde87f1 100644 --- a/keep-ui/app/api/copilotkit/route.ts +++ b/keep-ui/app/api/copilotkit/route.ts @@ -3,20 +3,37 @@ import { OpenAIAdapter, copilotRuntimeNextJSAppRouterEndpoint, } from "@copilotkit/runtime"; -import OpenAI from "openai"; +import OpenAI, { OpenAIError } from "openai"; import { NextRequest } from "next/server"; -const openai = new OpenAI({ - organization: process.env.OPEN_AI_ORGANIZATION_ID, - apiKey: process.env.OPEN_AI_API_KEY, -}); -const serviceAdapter = new OpenAIAdapter({ openai }); -const runtime = new CopilotRuntime(); +function initializeCopilotRuntime() { + try { + const openai = new OpenAI({ + organization: process.env.OPEN_AI_ORGANIZATION_ID, + apiKey: process.env.OPEN_AI_API_KEY, + }); + const serviceAdapter = new OpenAIAdapter({ openai }); + const runtime = new CopilotRuntime(); + return { runtime, serviceAdapter }; + } catch (error) { + if (error instanceof OpenAIError) { + console.log("Error connecting to OpenAI", error); + } else { + console.error("Error initializing Copilot Runtime", error); + } + return null; + } +} + +const runtimeOptions = initializeCopilotRuntime(); export const POST = async (req: NextRequest) => { + if (!runtimeOptions) { + return new Response("Error initializing Copilot Runtime", { status: 500 }); + } const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ - runtime, - serviceAdapter, + runtime: runtimeOptions.runtime, + serviceAdapter: runtimeOptions.serviceAdapter, endpoint: "/api/copilotkit", }); diff --git a/keep-ui/next-env.d.ts b/keep-ui/next-env.d.ts index 725dd6f24..40c3d6809 100644 --- a/keep-ui/next-env.d.ts +++ b/keep-ui/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/keep-ui/shared/lib/server/getConfig.ts b/keep-ui/shared/lib/server/getConfig.ts index 7a823ed15..b10cbbe70 100644 --- a/keep-ui/shared/lib/server/getConfig.ts +++ b/keep-ui/shared/lib/server/getConfig.ts @@ -54,5 +54,6 @@ export function getConfig() { POSTHOG_HOST: process.env.POSTHOG_HOST, SENTRY_DISABLED: process.env.SENTRY_DISABLED, READ_ONLY: process.env.KEEP_READ_ONLY === "true", + OPEN_AI_API_KEY_SET: !!process.env.OPEN_AI_API_KEY, }; } diff --git a/keep-ui/shared/ui/EmptyState/EmptyStateCard.tsx b/keep-ui/shared/ui/EmptyState/EmptyStateCard.tsx new file mode 100644 index 000000000..c683e3e0e --- /dev/null +++ b/keep-ui/shared/ui/EmptyState/EmptyStateCard.tsx @@ -0,0 +1,36 @@ +import { Card } from "@tremor/react"; +import { CircleStackIcon } from "@heroicons/react/24/outline"; +import clsx from "clsx"; + +export function EmptyStateCard({ + title, + icon, + description, + className, + children, +}: { + icon?: React.ElementType; + title: string; + description: string; + className?: string; + children?: React.ReactNode; +}) { + const Icon = icon || CircleStackIcon; + return ( + +
+ +

+ {title} +

+

+ {description} +

+ {children} +
+
+ ); +} diff --git a/keep-ui/shared/ui/EmptyState/index.ts b/keep-ui/shared/ui/EmptyState/index.ts new file mode 100644 index 000000000..a31d06c86 --- /dev/null +++ b/keep-ui/shared/ui/EmptyState/index.ts @@ -0,0 +1 @@ +export { EmptyStateCard } from "./EmptyStateCard"; diff --git a/keep-ui/shared/ui/index.ts b/keep-ui/shared/ui/index.ts index 4cfab5810..307db1bbb 100644 --- a/keep-ui/shared/ui/index.ts +++ b/keep-ui/shared/ui/index.ts @@ -2,3 +2,4 @@ export { TablePagination } from "./TablePagination"; export { TabLinkNavigation, TabNavigationLink } from "./TabLinkNavigation"; export { DateTimeField } from "./DateTimeField"; export { FieldHeader } from "./FieldHeader"; +export { EmptyStateCard } from "./EmptyState"; diff --git a/keep-ui/types/internal-config.ts b/keep-ui/types/internal-config.ts index 550828b27..c0d13710b 100644 --- a/keep-ui/types/internal-config.ts +++ b/keep-ui/types/internal-config.ts @@ -18,4 +18,5 @@ export interface InternalConfig { SENTRY_DISABLED: string; // READ ONLY READ_ONLY: boolean; + OPEN_AI_API_KEY_SET: boolean; }