branch:
ViewCodeModal.tsx
6667 bytesRaw
import type { UIMessage } from "ai";
import { Link } from "@cloudflare/kumo";
import { XIcon } from "@phosphor-icons/react";
import type { PlaygroundState } from "../server";

const escapeString = (str: string) => {
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
};

// ── Provider config lookup tables ──

type ProviderKey = "openai" | "anthropic" | "google" | "xai";

const PROVIDER_CONFIG: Record<
  ProviderKey,
  {
    displayName: string;
    constructorName: string;
    sdkImport: string;
    gatewayImport: string;
    apiKeyEnv: string;
    baseUrl?: string;
  }
> = {
  openai: {
    displayName: "OpenAI",
    constructorName: "createOpenAI",
    sdkImport: 'createOpenAI from "@ai-sdk/openai"',
    gatewayImport: 'createOpenAI from "ai-gateway-provider/providers/openai"',
    apiKeyEnv: "OPENAI_API_KEY"
  },
  anthropic: {
    displayName: "Anthropic",
    constructorName: "createAnthropic",
    sdkImport: 'createAnthropic from "@ai-sdk/anthropic"',
    gatewayImport:
      'createAnthropic from "ai-gateway-provider/providers/anthropic"',
    apiKeyEnv: "ANTHROPIC_API_KEY"
  },
  google: {
    displayName: "Google",
    constructorName: "createGoogleGenerativeAI",
    sdkImport: 'createGoogleGenerativeAI from "@ai-sdk/google"',
    gatewayImport:
      'createGoogleGenerativeAI from "ai-gateway-provider/providers/google"',
    apiKeyEnv: "GOOGLE_GENERATIVE_AI_API_KEY"
  },
  xai: {
    displayName: "xAI",
    constructorName: "createOpenAI",
    sdkImport: 'createOpenAI from "@ai-sdk/openai"',
    gatewayImport: 'createOpenAI from "ai-gateway-provider/providers/openai"',
    apiKeyEnv: "XAI_API_KEY",
    baseUrl: "https://api.x.ai/v1"
  }
};

const getConfig = (provider: string | undefined) =>
  PROVIDER_CONFIG[(provider as ProviderKey) || "openai"] ||
  PROVIDER_CONFIG.openai;

// ── Code generation ──

const createMessageString = (
  messages: UIMessage[],
  params: PlaygroundState
) => {
  const messageArray = messages
    .map(
      (message) =>
        `    { role: "${message.role}", content: "${escapeString(
          message.parts
            .filter((part) => part.type === "text")
            .map((part) => part.text)
            .join("")
        )}"}`
    )
    .join(",\n");

  const streamTextTail = `  system: "${escapeString(params.system)}",
  temperature: ${params.temperature},
  messages: [
${messageArray}
  ],
});

return result.toDataStreamResponse();`;

  if (!params.useExternalProvider) {
    return `import { streamText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

const workersAi = createWorkersAI(env);

const result = streamText({
  model: workersAi("${params.model}"),
  ${streamTextTail}`;
  }

  const cfg = getConfig(params.externalProvider);
  const modelName = params.externalModel || params.model;
  const modelId = modelName.includes("/") ? modelName.split("/")[1] : modelName;
  const varName =
    params.externalProvider === "xai" ? "xai" : params.externalProvider;

  if (params.authMethod === "gateway") {
    const accountId = params.gatewayAccountId || "YOUR_ACCOUNT_ID";
    const gatewayId = params.gatewayId || "YOUR_GATEWAY_ID";

    return `import { streamText } from "ai";
import { createAiGateway } from "ai-gateway-provider";
import { ${cfg.gatewayImport} };

const gateway = createAiGateway({
  accountId: "${accountId}",
  gateway: "${gatewayId}",
  apiKey: env.CLOUDFLARE_API_KEY,
});

const ${varName} = ${cfg.constructorName}();
const model = ${varName}.chat("${modelId}");

const result = streamText({
  model: gateway(model),
  ${streamTextTail}`;
  }

  const baseUrlConfig = cfg.baseUrl ? `,\n  baseURL: "${cfg.baseUrl}"` : "";

  return `import { streamText } from "ai";
import { ${cfg.sdkImport} };

const ${varName} = ${cfg.constructorName}({
  apiKey: env.${cfg.apiKeyEnv}${baseUrlConfig},
});

const result = streamText({
  model: ${varName}("${modelId}"),
  ${streamTextTail}`;
};

// ── Component ──

const ViewCodeModal = ({
  visible,
  handleHide,
  params,
  messages
}: {
  visible: boolean;
  handleHide: (e: React.MouseEvent<HTMLElement>) => void;
  params: PlaygroundState;
  messages: UIMessage[];
}) => {
  if (!visible) return null;

  const cfg = getConfig(params.externalProvider);

  return (
    // oxlint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- modal backdrop dismiss
    <div
      onClick={handleHide}
      className="fixed inset-0 bg-kumo-base/50 backdrop-blur-sm z-20 flex md:items-center md:justify-center items-end md:p-16"
    >
      {/* oxlint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- stop propagation */}
      <div
        onClick={(e) => e.stopPropagation()}
        className="bg-kumo-base shadow-xl rounded-lg md:max-w-2xl w-full p-6 ring ring-kumo-line"
      >
        <h2 className="font-semibold text-xl flex items-center text-kumo-default">
          View code
          {/* oxlint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- close button */}
          <button
            type="button"
            onClick={handleHide}
            className="ml-auto text-kumo-secondary cursor-pointer hover:text-kumo-default"
          >
            <XIcon size={24} />
          </button>
        </h2>
        <p className="mt-2 text-kumo-secondary">
          {params.useExternalProvider ? (
            <>
              You can use the following code to deploy a Cloudflare Worker using{" "}
              {cfg.displayName} with the current playground messages and
              settings.
              {params.authMethod === "gateway" && (
                <>
                  {" "}
                  This uses{" "}
                  <Link href="https://developers.cloudflare.com/ai-gateway/">
                    Cloudflare AI Gateway
                  </Link>{" "}
                  for unified billing.
                </>
              )}
            </>
          ) : (
            <>
              You can use the following code to{" "}
              <Link href="https://developers.cloudflare.com/workers-ai/get-started/workers-wrangler/">
                deploy a Workers AI Worker
              </Link>{" "}
              using the current playground messages and settings.
            </>
          )}
        </p>

        <pre className="text-sm py-4 px-3 bg-kumo-control text-kumo-default rounded-md my-4 overflow-auto max-h-[300px]">
          {createMessageString(messages, params)}
        </pre>
      </div>
    </div>
  );
};

export default ViewCodeModal;