188 lines · 5.7 KB · TypeScript
RawDownload
import {  type ModelSlug,  type ProviderKind,  type ProviderModelOptions,  type ThreadId,} from "@t3tools/contracts";import {  getModelCapabilities,  isClaudeUltrathinkPrompt,  normalizeClaudeModelOptions,  normalizeCodexModelOptions,  trimOrNull,  getDefaultEffort,  hasEffortLevel,} from "@t3tools/shared/model";import type { ReactNode } from "react";import { TraitsMenuContent, TraitsPicker } from "./TraitsPicker"; export type ComposerProviderStateInput = {  provider: ProviderKind;  model: ModelSlug;  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: ModelSlug;    modelOptions: ProviderModelOptions[ProviderKind] | undefined;    prompt: string;    onPromptChange: (prompt: string) => void;  }) => ReactNode;  renderTraitsPicker: (input: {    threadId: ThreadId;    model: ModelSlug;    modelOptions: ProviderModelOptions[ProviderKind] | undefined;    prompt: string;    onPromptChange: (prompt: string) => void;  }) => ReactNode;}; function getProviderStateFromCapabilities(  input: ComposerProviderStateInput,): ComposerProviderState {  const { provider, model, prompt, modelOptions } = input;  const caps = getModelCapabilities(provider, model);  const providerOptions = modelOptions?.[provider];   // Resolve effort  const rawEffort = providerOptions    ? "effort" in providerOptions      ? providerOptions.effort      : "reasoningEffort" in providerOptions        ? providerOptions.reasoningEffort        : null    : null;   const draftEffort = trimOrNull(rawEffort);  const defaultEffort = getDefaultEffort(caps);  const isPromptInjected = draftEffort    ? caps.promptInjectedEffortLevels.includes(draftEffort)    : false;  const promptEffort =    draftEffort && !isPromptInjected && hasEffortLevel(caps, draftEffort)      ? draftEffort      : defaultEffort && hasEffortLevel(caps, defaultEffort)        ? defaultEffort        : null;   // Normalize options for dispatch  const normalizedOptions =    provider === "codex"      ? normalizeCodexModelOptions(model, providerOptions)      : normalizeClaudeModelOptions(model, 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, modelOptions, prompt, onPromptChange }) => (      <TraitsMenuContent        provider="codex"        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),    renderTraitsPicker: ({ threadId, model, modelOptions, prompt, onPromptChange }) => (      <TraitsPicker        provider="codex"        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),  },  claudeAgent: {    getState: (input) => getProviderStateFromCapabilities(input),    renderTraitsMenuContent: ({ threadId, model, modelOptions, prompt, onPromptChange }) => (      <TraitsMenuContent        provider="claudeAgent"        threadId={threadId}        model={model}        modelOptions={modelOptions}        prompt={prompt}        onPromptChange={onPromptChange}      />    ),    renderTraitsPicker: ({ threadId, model, modelOptions, prompt, onPromptChange }) => (      <TraitsPicker        provider="claudeAgent"        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: ModelSlug;  modelOptions: ProviderModelOptions[ProviderKind] | undefined;  prompt: string;  onPromptChange: (prompt: string) => void;}): ReactNode {  return composerProviderRegistry[input.provider].renderTraitsMenuContent({    threadId: input.threadId,    model: input.model,    modelOptions: input.modelOptions,    prompt: input.prompt,    onPromptChange: input.onPromptChange,  });} export function renderProviderTraitsPicker(input: {  provider: ProviderKind;  threadId: ThreadId;  model: ModelSlug;  modelOptions: ProviderModelOptions[ProviderKind] | undefined;  prompt: string;  onPromptChange: (prompt: string) => void;}): ReactNode {  return composerProviderRegistry[input.provider].renderTraitsPicker({    threadId: input.threadId,    model: input.model,    modelOptions: input.modelOptions,    prompt: input.prompt,    onPromptChange: input.onPromptChange,  });}