200 lines · 6.0 KB · TypeScript
RawDownload
import {  type ProviderKind,  type ProviderModelOptions,  type ServerProviderModel,  type ThreadId,} from "@t3tools/contracts";import { isClaudeUltrathinkPrompt, resolveEffort } from "@t3tools/shared/model";import type { ReactNode } from "react";import { getProviderModelCapabilities } from "../../providerModels";import { TraitsMenuContent, TraitsPicker } from "./TraitsPicker";import {  normalizeClaudeModelOptionsWithCapabilities,  normalizeCodexModelOptionsWithCapabilities,} from "@t3tools/shared/model"; export type ComposerProviderStateInput = {  provider: ProviderKind;  model: string;  models: ReadonlyArray<ServerProviderModel>;  prompt: string;  modelOptions: ProviderModelOptions | null | undefined;}; export type ComposerProviderState = {  provider: ProviderKind;  promptEffort: string | null;  modelOptionsForDispatch: ProviderModelOptions[ProviderKind] | undefined;  composerFrameClassName?: string;  composerSurfaceClassName?: string;  modelPickerIconClassName?: string;}; type ProviderRegistryEntry = {  getState: (input: ComposerProviderStateInput) => ComposerProviderState;  renderTraitsMenuContent: (input: {    threadId: ThreadId;    model: string;    models: ReadonlyArray<ServerProviderModel>;    modelOptions: ProviderModelOptions[ProviderKind] | undefined;    prompt: string;    onPromptChange: (prompt: string) => void;  }) => ReactNode;  renderTraitsPicker: (input: {    threadId: ThreadId;    model: string;    models: ReadonlyArray<ServerProviderModel>;    modelOptions: ProviderModelOptions[ProviderKind] | undefined;    prompt: string;    onPromptChange: (prompt: string) => void;  }) => ReactNode;}; function getProviderStateFromCapabilities(  input: ComposerProviderStateInput,): ComposerProviderState {  const { provider, model, models, prompt, modelOptions } = input;  const caps = getProviderModelCapabilities(models, model, provider);  const providerOptions = modelOptions?.[provider];   // Resolve effort  const rawEffort = providerOptions    ? "effort" in providerOptions      ? providerOptions.effort      : "reasoningEffort" in providerOptions        ? providerOptions.reasoningEffort        : null    : null;   const promptEffort = resolveEffort(caps, rawEffort) ?? null;   // Normalize options for dispatch  const normalizedOptions =    provider === "codex"      ? normalizeCodexModelOptionsWithCapabilities(caps, providerOptions)      : normalizeClaudeModelOptionsWithCapabilities(caps, providerOptions);   // Ultrathink styling (driven by capabilities data, not provider identity)  const ultrathinkActive =    caps.promptInjectedEffortLevels.length > 0 && isClaudeUltrathinkPrompt(prompt);   return {    provider,    promptEffort,    modelOptionsForDispatch: normalizedOptions,    ...(ultrathinkActive ? { composerFrameClassName: "ultrathink-frame" } : {}),    ...(ultrathinkActive      ? { composerSurfaceClassName: "shadow-[0_0_0_1px_rgba(255,255,255,0.04)_inset]" }      : {}),    ...(ultrathinkActive ? { modelPickerIconClassName: "ultrathink-chroma" } : {}),  };} const composerProviderRegistry: Record<ProviderKind, ProviderRegistryEntry> = {  codex: {    getState: (input) => getProviderStateFromCapabilities(input),    renderTraitsMenuContent: ({      threadId,      model,      models,      modelOptions,      prompt,      onPromptChange,    }) => (      <TraitsMenuContent        provider="codex"        models={models}        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),    renderTraitsPicker: ({ threadId, model, models, modelOptions, prompt, onPromptChange }) => (      <TraitsPicker        provider="codex"        models={models}        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),  },  claudeAgent: {    getState: (input) => getProviderStateFromCapabilities(input),    renderTraitsMenuContent: ({      threadId,      model,      models,      modelOptions,      prompt,      onPromptChange,    }) => (      <TraitsMenuContent        provider="claudeAgent"        models={models}        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),    renderTraitsPicker: ({ threadId, model, models, modelOptions, prompt, onPromptChange }) => (      <TraitsPicker        provider="claudeAgent"        models={models}        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),  },}; export function getComposerProviderState(input: ComposerProviderStateInput): ComposerProviderState {  return composerProviderRegistry[input.provider].getState(input);} export function renderProviderTraitsMenuContent(input: {  provider: ProviderKind;  threadId: ThreadId;  model: string;  models: ReadonlyArray<ServerProviderModel>;  modelOptions: ProviderModelOptions[ProviderKind] | undefined;  prompt: string;  onPromptChange: (prompt: string) => void;}): ReactNode {  return composerProviderRegistry[input.provider].renderTraitsMenuContent({    threadId: input.threadId,    model: input.model,    models: input.models,    modelOptions: input.modelOptions,    prompt: input.prompt,    onPromptChange: input.onPromptChange,  });} export function renderProviderTraitsPicker(input: {  provider: ProviderKind;  threadId: ThreadId;  model: string;  models: ReadonlyArray<ServerProviderModel>;  modelOptions: ProviderModelOptions[ProviderKind] | undefined;  prompt: string;  onPromptChange: (prompt: string) => void;}): ReactNode {  return composerProviderRegistry[input.provider].renderTraitsPicker({    threadId: input.threadId,    model: input.model,    models: input.models,    modelOptions: input.modelOptions,    prompt: input.prompt,    onPromptChange: input.onPromptChange,  });}