@elytracms/studio 0.0.55 → 0.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/auth.ts +152 -2
- package/convex/users.ts +41 -14
- package/dist/components/ai/ai-chat-panel-lazy.js +3 -3
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.d.ts +0 -9
- package/dist/components/ai/ai-chat-panel.js +161 -60
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/chat-ui.d.ts +62 -10
- package/dist/components/ai/chat-ui.js +130 -23
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/lib/ai/anthropic-provider.d.ts +0 -12
- package/dist/lib/ai/anthropic-provider.js +3 -11
- package/dist/lib/ai/anthropic-provider.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +58 -0
- package/dist/lib/ai/chat-context.js +71 -0
- package/dist/lib/ai/chat-context.js.map +1 -0
- package/dist/lib/ai/chat-loop.d.ts +70 -0
- package/dist/lib/ai/chat-loop.js +46 -0
- package/dist/lib/ai/chat-loop.js.map +1 -0
- package/dist/lib/ai/chat-state.d.ts +70 -57
- package/dist/lib/ai/chat-state.js +49 -90
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +84 -6
- package/dist/lib/ai/fake-provider.js +193 -8
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/gateway-provider.d.ts +30 -0
- package/dist/lib/ai/gateway-provider.js +73 -0
- package/dist/lib/ai/gateway-provider.js.map +1 -0
- package/dist/lib/ai/index.d.ts +3 -1
- package/dist/lib/ai/index.js +3 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/node-handler.d.ts +13 -4
- package/dist/lib/ai/node-handler.js +33 -3
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/route-provider.d.ts +35 -10
- package/dist/lib/ai/route-provider.js +13 -6
- package/dist/lib/ai/route-provider.js.map +1 -1
- package/dist/lib/ai/route.d.ts +50 -21
- package/dist/lib/ai/route.js +69 -40
- package/dist/lib/ai/route.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +169 -39
- package/dist/lib/ai/tools.js +308 -62
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +11 -16
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/auth/convex-auth-adapter.d.ts +37 -14
- package/dist/lib/auth/convex-auth-adapter.js +72 -16
- package/dist/lib/auth/convex-auth-adapter.js.map +1 -1
- package/dist/lib/i18n/messages.en.js +9 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/package.json +4 -3
- package/dist/lib/ai/create-flow.d.ts +0 -132
- package/dist/lib/ai/create-flow.js +0 -280
- package/dist/lib/ai/create-flow.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-chat-panel.js","sourceRoot":"","sources":["../../../src/components/ai/ai-chat-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useReducer, useState } from 'react'\nimport { X } from 'lucide-react'\nimport {\n createCollectionRegistry,\n resolveDocumentTitle,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocument, FieldValues } from '@elytracms/core/cms-core'\nimport { Button, Heading, Row, Spacer, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { resolveDeploymentAi } from '@elytracms/studio/lib/workspace'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport {\n AiRouteError,\n chatReducer,\n initialChatState,\n resolveAiConfig,\n resolveKnowledge,\n runCreateFlow,\n} from '@elytracms/studio/lib/ai'\nimport type {\n CreateFlowStep,\n CreateFlowTools,\n KnowledgeCollectionReader,\n ProposalReview,\n} from '@elytracms/studio/lib/ai'\nimport { useCms } from '../cms/cms-store'\nimport { validateComposition, validateRichTextEmbeds } from '../cms/cms-composition-validators'\nimport {\n AssistantTurn,\n Conversation,\n ConversationEmpty,\n PromptInput,\n ToolStatus,\n UserTurn,\n} from './chat-ui'\nimport { ProposalCard } from './proposal-card'\nimport { setAiPanelOpen } from '@elytracms/studio/lib/ai-panel'\n\n/**\n * The AI assistant chat panel (EC-199) — the right-hand collapsible surface in the CMS\n * area. It owns the chat state (the pure `chatReducer`), resolves the EC-331 provider\n * from `defineStudio({ ai })`, binds the narrow tools (search/list) to the operations\n * layer with the user's session, runs the creation orchestrator, and applies a\n * reviewed proposal as a DRAFT (operations upsert + a provenance-tagged version\n * snapshot). NEVER a modal (house rule). The whole module sits behind a lazy boundary\n * (`ai-chat-panel-lazy`), so an unconfigured studio pulls none of it — and no AI SDK.\n */\nexport function AiChatPanel() {\n const cms = useCms()\n const navigate = useNavigate()\n const [state, dispatch] = useReducer(chatReducer, initialChatState)\n const [input, setInput] = useState('')\n\n const projectId = cms.projectId\n const activeLocale = cms.activeLocale\n\n // Resolve the AI platform config once (presence + capability gate). The panel only\n // mounts when configured, but a misconfigured provider still fails loud here.\n const resolved = useMemo(() => resolveAiConfig(resolveDeploymentAi()), [])\n const provider = resolved.resolved?.provider ?? null\n const knowledgeSources = resolved.resolved?.knowledge ?? []\n const maxRepairAttempts = resolved.resolved?.limits?.maxRepairAttempts\n const ready = provider !== null && resolved.ok && projectId !== undefined\n\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n /** Merge a document's non-localized + active-locale values for title/prose reads. */\n const localeValues = useCallback(\n (doc: CmsDocument): FieldValues => ({\n ...doc.values,\n ...(doc.localized?.[activeLocale] ?? {}),\n }),\n [activeLocale],\n )\n\n /** Operations-backed tool readers — client-side, with the user's own session. */\n const tools = useMemo<CreateFlowTools>(() => {\n const matches = (query: string | undefined, ...fields: (string | undefined)[]) =>\n !query || fields.some((f) => f?.toLowerCase().includes(query.toLowerCase()))\n return {\n async searchDocuments({ collection, query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection })\n if (!result.ok) return []\n const target = cms.getCollection(collection)\n return result.value\n .map((rec) => ({\n id: rec.document.id,\n title: target\n ? resolveDocumentTitle(target, localeValues(rec.document), rec.document.id)\n : rec.document.id,\n }))\n .filter((r) => matches(query, r.title, r.id))\n },\n async listAssets({ query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) return []\n return result.value\n .map((asset) => ({ id: asset.id, title: asset.title ?? asset.filename }))\n .filter((r) => matches(query, r.title, r.id))\n },\n // EC-332: the translate flow loads a full document (draft values + localized).\n async getDocument({ collection, id }) {\n if (projectId === undefined) return null\n const { operations } = await getStudioOperations()\n const result = await operations.documents.get({ projectId, ref: { collection, id } })\n return result.ok ? result.value?.document ?? null : null\n },\n }\n }, [projectId, cms, localeValues])\n\n /** A fresh, collision-free draft id for the target collection. */\n const mintDocumentId = useCallback(\n (collectionId: string): string => {\n const existing = new Set(cms.documentsOf(collectionId).map((d) => d.id))\n let n = 1\n while (existing.has(`new-${collectionId}-${n}`)) n += 1\n return `new-${collectionId}-${n}`\n },\n [cms],\n )\n\n /** Read `fromCollection(...)` knowledge prose through the operations layer. */\n const readCollection = useCallback<KnowledgeCollectionReader>(\n async (source) => {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection: source.collectionId })\n if (!result.ok) return []\n const target = cms.getCollection(source.collectionId)\n const field =\n source.field ?? target?.fields.find((f) => f.type === 'text' || f.type === 'richText')?.name\n return result.value\n .map((rec) => {\n const values = localeValues(rec.document)\n const content = field ? String(values[field] ?? '') : ''\n return { content }\n })\n .filter((item) => item.content.trim().length > 0)\n },\n [projectId, cms, localeValues],\n )\n\n const runTurn = useCallback(\n async (text: string) => {\n if (!provider) return\n try {\n const knowledge = await resolveKnowledge(knowledgeSources, { readCollection })\n const result = await runCreateFlow({\n provider,\n tools,\n registry,\n collections: cms.collections,\n instruction: text,\n knowledge,\n locale: activeLocale,\n localeConfig: cms.localeConfig,\n mintDocumentId,\n validateRichTextEmbeds,\n validateComposition,\n ...(maxRepairAttempts !== undefined ? { maxRepairAttempts } : {}),\n onStep: (step: CreateFlowStep) => dispatch({ type: 'step', step }),\n })\n dispatch({ type: 'result', result })\n } catch (error) {\n const message =\n error instanceof AiRouteError\n ? translate('ai.error.route')\n : translate('ai.error.generic')\n dispatch({ type: 'fail', message })\n }\n },\n [\n provider,\n knowledgeSources,\n readCollection,\n tools,\n registry,\n cms.collections,\n activeLocale,\n mintDocumentId,\n maxRepairAttempts,\n ],\n )\n\n const submit = useCallback(() => {\n const text = input.trim()\n if (text.length === 0 || state.status === 'busy' || !ready) return\n setInput('')\n dispatch({ type: 'submit', text })\n void runTurn(text)\n }, [input, state.status, ready, runTurn])\n\n const applyProposal = useCallback(\n async (proposal: ProposalReview) => {\n if (projectId === undefined) return\n const doc: CmsDocument = {\n collection: proposal.collection,\n id: proposal.documentId,\n values: proposal.patch.values,\n ...(proposal.patch.localized ? { localized: proposal.patch.localized } : {}),\n }\n const { operations } = await getStudioOperations()\n const upserted = await operations.documents.upsert({ projectId, document: doc })\n if (!upserted.ok) {\n dispatch({ type: 'fail', message: translate('ai.error.applyFailed') })\n return\n }\n // Provenance: record an AI-tagged version snapshot (a restore point + the\n // 'assistant' origin surfaced in the versions pane). Best-effort — the draft\n // already landed above; a snapshot failure must not lose the applied content.\n try {\n await operations.documents.snapshot({\n projectId,\n ref: { collection: proposal.collection, id: proposal.documentId },\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n // Reflect in the session so the new draft shows in the list + is navigable.\n cms.saveDocument(doc)\n dispatch({ type: 'apply', proposalId: proposal.id })\n },\n [projectId, cms],\n )\n\n const openProposal = useCallback(\n (proposal: ProposalReview) => {\n if (projectId === undefined) return\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(proposal.collection, proposal.documentId) },\n })\n },\n [navigate, projectId],\n )\n\n return (\n <aside className=\"flex h-full min-h-0 w-[360px] shrink-0 flex-col border-l border-border bg-card\">\n <Row gap=\"2\" align=\"center\" className=\"h-12 shrink-0 border-b border-border px-3\">\n <Heading level={2} size=\"h4\">\n {translate('ai.panel.title')}\n </Heading>\n <Spacer />\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setAiPanelOpen(false)}\n title={translate('ai.panel.close')}\n aria-label={translate('ai.panel.close')}\n >\n <X />\n </Button>\n </Row>\n\n <Conversation atBottomKey={state.messages}>\n {state.messages.length === 0 ? <ConversationEmpty /> : null}\n {state.messages.map((message) => {\n if (message.role === 'user') return <UserTurn key={message.id} text={message.text} />\n if (message.kind === 'text')\n return (\n <AssistantTurn key={message.id}>\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {message.text}\n </Text>\n </AssistantTurn>\n )\n if (message.kind === 'activity')\n return (\n <AssistantTurn key={message.id}>\n <ToolStatus\n steps={message.steps}\n active={state.status === 'busy' && state.activityId === message.id}\n />\n </AssistantTurn>\n )\n return (\n <ProposalCard\n key={message.id}\n proposal={message.proposal}\n onApply={() => void applyProposal(message.proposal)}\n onDiscard={() => dispatch({ type: 'reject', proposalId: message.proposal.id })}\n onOpen={() => openProposal(message.proposal)}\n />\n )\n })}\n </Conversation>\n\n {ready ? (\n <PromptInput value={input} onChange={setInput} onSubmit={submit} disabled={state.status === 'busy'} />\n ) : (\n <div className=\"border-t border-border p-3\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.unavailable')}\n </Text>\n </div>\n )}\n </aside>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ai-chat-panel.js","sourceRoot":"","sources":["../../../src/components/ai/ai-chat-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useReducer, useRef, useState } from 'react'\nimport { MessageSquarePlus, X } from 'lucide-react'\nimport { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai'\nimport type { UIMessage } from 'ai'\nimport { useChat } from '@ai-sdk/react'\nimport {\n createCollectionRegistry,\n resolveDocumentTitle,\n} from '@elytracms/core/cms-core'\nimport type { CmsDocument, FieldValues } from '@elytracms/core/cms-core'\nimport { Button, Heading, Row, Spacer, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { resolveDeploymentAi } from '@elytracms/studio/lib/workspace'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport {\n DEFAULT_MAX_STEPS,\n buildChatContext,\n chatReviewReducer,\n clearConversation,\n createChatCandidateStore,\n createChatToolExecutor,\n initialChatReviewState,\n resolveAiConfig,\n resolveChatTarget,\n resolveKnowledge,\n withStepCap,\n} from '@elytracms/studio/lib/ai'\nimport type {\n ChatCandidateStore,\n ChatToolReaders,\n KnowledgeCollectionReader,\n ProposalReview,\n ResolvedKnowledge,\n RouteHeaders,\n} from '@elytracms/studio/lib/ai'\nimport { useCms } from '../cms/cms-store'\nimport { validateComposition, validateRichTextEmbeds } from '../cms/cms-composition-validators'\nimport {\n AssistantTurn,\n ChatErrorNote,\n Conversation,\n ConversationEmpty,\n PromptInput,\n ToolStatus,\n UserTurn,\n WorkingIndicator,\n describeToolPart,\n messageText,\n segmentMessageParts,\n} from './chat-ui'\nimport type { MessagePartView } from './chat-ui'\nimport { ProposalCard } from './proposal-card'\nimport { setAiPanelOpen } from '@elytracms/studio/lib/ai-panel'\n\n/**\n * The AI assistant chat panel (EC-199, moved onto the SDK's native protocol by\n * EC-334) — the right-hand collapsible surface in the CMS area. `useChat` +\n * `DefaultChatTransport` own the conversation against the route's native chat\n * branch (visible token streaming); `onToolCall` dispatches into the client\n * executor map (operations reads + terminal engines, all with the user's own\n * session); `sendAutomaticallyWhen` is the SDK predicate wrapped with the\n * per-turn step cap. What the SDK does not own lives in the pure review reducer:\n * full proposals keyed by `toolCallId` (reviewed as a diff, applied as a DRAFT\n * via operations upsert + a provenance-tagged snapshot — the EC-199 trust\n * boundary, unmoved) and blocking errors. EC-380: the \"new conversation\"\n * affordance clears busy-safely (stop → new chat id + review reset; pending\n * proposals are discarded with reject semantics).\n *\n * NEVER a modal (house rule). The whole module (including `ai` + `@ai-sdk/react`)\n * sits behind the lazy boundary (`ai-chat-panel-lazy`) — pinned in bundle tests.\n */\n\n/** Adapt a `RouteHeaders` (static or thunk) to the transport's headers resolvable. */\nfunction toTransportHeaders(headers: RouteHeaders | undefined): () => Promise<Record<string, string>> {\n return async () => {\n const resolved = typeof headers === 'function' ? await headers() : headers\n const record: Record<string, string> = {}\n new Headers(resolved ?? {}).forEach((value, key) => {\n record[key] = value\n })\n return record\n }\n}\n\nexport function AiChatPanel() {\n const cms = useCms()\n const navigate = useNavigate()\n const [review, dispatch] = useReducer(chatReviewReducer, initialChatReviewState)\n const [input, setInput] = useState('')\n // EC-380: the conversation counter — bumping it mints a fresh `useChat` id\n // (a brand-new conversation) and a fresh relation-candidate store.\n const [conversation, setConversation] = useState(1)\n\n const projectId = cms.projectId\n const activeLocale = cms.activeLocale\n\n // Resolve the AI platform config once (presence + capability gate). The panel only\n // mounts when configured, but a misconfigured provider still fails loud here.\n const resolved = useMemo(() => resolveAiConfig(resolveDeploymentAi()), [])\n const provider = resolved.resolved?.provider ?? null\n const knowledgeSources = useMemo(() => resolved.resolved?.knowledge ?? [], [resolved])\n const maxRepairAttempts = resolved.resolved?.limits?.maxRepairAttempts\n const ready = provider !== null && resolved.ok && projectId !== undefined\n\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n /** Merge a document's non-localized + active-locale values for title/prose reads. */\n const localeValues = useCallback(\n (doc: CmsDocument): FieldValues => ({\n ...doc.values,\n ...(doc.localized?.[activeLocale] ?? {}),\n }),\n [activeLocale],\n )\n\n /** Operations-backed tool readers — client-side, with the user's own session. */\n const readers = useMemo<ChatToolReaders>(() => {\n const matches = (query: string | undefined, ...fields: (string | undefined)[]) =>\n !query || fields.some((f) => f?.toLowerCase().includes(query.toLowerCase()))\n return {\n async searchDocuments({ collection, query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection })\n if (!result.ok) return []\n const target = cms.getCollection(collection)\n return result.value\n .map((rec) => ({\n id: rec.document.id,\n title: target\n ? resolveDocumentTitle(target, localeValues(rec.document), rec.document.id)\n : rec.document.id,\n }))\n .filter((r) => matches(query, r.title, r.id))\n },\n async listAssets({ query }) {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) return []\n return result.value\n .map((asset) => ({ id: asset.id, title: asset.title ?? asset.filename }))\n .filter((r) => matches(query, r.title, r.id))\n },\n // Full document (draft values + localized) — the getDocument/translate tools.\n async getDocument({ collection, id }) {\n if (projectId === undefined) return null\n const { operations } = await getStudioOperations()\n const result = await operations.documents.get({ projectId, ref: { collection, id } })\n return result.ok ? result.value?.document ?? null : null\n },\n }\n }, [projectId, cms, localeValues])\n\n /** A fresh, collision-free draft id for the target collection. */\n const mintDocumentId = useCallback(\n (collectionId: string): string => {\n const existing = new Set(cms.documentsOf(collectionId).map((d) => d.id))\n let n = 1\n while (existing.has(`new-${collectionId}-${n}`)) n += 1\n return `new-${collectionId}-${n}`\n },\n [cms],\n )\n\n /** Read `fromCollection(...)` knowledge prose through the operations layer. */\n const readCollection = useCallback<KnowledgeCollectionReader>(\n async (source) => {\n if (projectId === undefined) return []\n const { operations } = await getStudioOperations()\n const result = await operations.documents.list({ projectId, collection: source.collectionId })\n if (!result.ok) return []\n const target = cms.getCollection(source.collectionId)\n const field =\n source.field ?? target?.fields.find((f) => f.type === 'text' || f.type === 'richText')?.name\n return result.value\n .map((rec) => {\n const values = localeValues(rec.document)\n const content = field ? String(values[field] ?? '') : ''\n return { content }\n })\n .filter((item) => item.content.trim().length > 0)\n },\n [projectId, cms, localeValues],\n )\n\n // The knowledge snapshot the executors read — refreshed by the body thunk on\n // every request, so the terminal engines see what the model saw this turn.\n const knowledgeRef = useRef<ResolvedKnowledge>({ items: [] })\n\n // The chat transport target: the configured route provider's endpoint +\n // session-header thunk (EC-371), re-evaluated per request.\n const chatTarget = useMemo(() => resolveChatTarget(provider), [provider])\n\n const transport = useMemo(\n () =>\n new DefaultChatTransport({\n api: chatTarget.endpoint,\n headers: toTransportHeaders(chatTarget.headers),\n // The `body` THUNK — re-evaluated per request (fresh context every turn,\n // continuations included): collections-catalog summary + client-resolved\n // knowledge. The route folds these sections into the system prompt; it\n // resolves no data itself (zero-data-access proxy). EC-378's\n // current-document block joins via `extraSections`.\n body: async () => {\n const knowledge = await resolveKnowledge(knowledgeSources, { readCollection })\n knowledgeRef.current = knowledge\n return { context: buildChatContext({ collections: cms.collections, knowledge }) }\n },\n }),\n [chatTarget, knowledgeSources, readCollection, cms.collections],\n )\n\n // Per-conversation relation-candidate store (EC-380: replaced on clear, so a\n // fresh conversation cannot reference the previous one's searched ids).\n const candidatesRef = useRef<{ conversation: number; store: ChatCandidateStore }>({\n conversation,\n store: createChatCandidateStore(),\n })\n if (candidatesRef.current.conversation !== conversation) {\n candidatesRef.current = { conversation, store: createChatCandidateStore() }\n }\n const candidateStore = candidatesRef.current.store\n\n const executor = useMemo(() => {\n if (!provider) return null\n return createChatToolExecutor({\n provider,\n registry,\n collections: cms.collections,\n readers,\n mintDocumentId,\n candidates: candidateStore,\n onProposal: (toolCallId, proposal) => dispatch({ type: 'propose', toolCallId, proposal }),\n knowledge: () => knowledgeRef.current,\n locale: activeLocale,\n ...(cms.localeConfig ? { localeConfig: cms.localeConfig } : {}),\n validateRichTextEmbeds,\n validateComposition,\n ...(maxRepairAttempts !== undefined ? { maxRepairAttempts } : {}),\n })\n }, [\n provider,\n registry,\n cms.collections,\n cms.localeConfig,\n readers,\n mintDocumentId,\n activeLocale,\n maxRepairAttempts,\n candidateStore,\n ])\n\n const chat = useChat({\n // A new conversation is a new chat id — `useChat` recreates its state.\n id: `elytra-assistant-${conversation}`,\n transport,\n onToolCall: async ({ toolCall }) => {\n if (!executor) return\n const outcome = await executor.execute({\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n input: toolCall.input,\n })\n // Deliberately NOT awaited: `onToolCall` runs inside the chat's serial\n // stream job, and `addToolOutput` enqueues on the same executor —\n // awaiting it here deadlocks the stream (verified against ai@7.0.22).\n if (outcome.ok) {\n void chat.addToolOutput({\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n output: outcome.output,\n })\n } else {\n void chat.addToolOutput({\n state: 'output-error',\n tool: toolCall.toolName,\n toolCallId: toolCall.toolCallId,\n errorText: outcome.errorText,\n })\n }\n },\n // The SDK predicate wrapped with the per-user-turn step cap: a capped turn\n // stops cleanly with a visible note — never a silent stall (EC-334).\n sendAutomaticallyWhen: withStepCap(lastAssistantMessageIsCompleteWithToolCalls, {\n maxSteps: DEFAULT_MAX_STEPS,\n onCap: () =>\n dispatch({ type: 'fail', message: translate('ai.error.stepCap', { max: DEFAULT_MAX_STEPS }) }),\n }),\n onError: () => dispatch({ type: 'fail', message: translate('ai.error.route') }),\n })\n\n const busy = chat.status === 'submitted' || chat.status === 'streaming'\n\n const submit = useCallback(() => {\n const text = input.trim()\n if (text.length === 0 || busy || !ready) return\n setInput('')\n dispatch({ type: 'clear-error' })\n void chat.sendMessage({ text })\n }, [input, busy, ready, chat])\n\n // EC-380: clear / new topic — busy-safe (stop first), pending proposals\n // discarded with reject semantics (the review state resets; nothing was applied).\n const startNewConversation = useCallback(() => {\n void clearConversation({ status: chat.status, stop: chat.stop }, () => {\n dispatch({ type: 'clear' })\n setInput('')\n setConversation((current) => current + 1)\n })\n }, [chat.status, chat.stop])\n\n const applyProposal = useCallback(\n async (proposal: ProposalReview) => {\n if (projectId === undefined) return\n const doc: CmsDocument = {\n collection: proposal.collection,\n id: proposal.documentId,\n values: proposal.patch.values,\n ...(proposal.patch.localized ? { localized: proposal.patch.localized } : {}),\n }\n const { operations } = await getStudioOperations()\n const upserted = await operations.documents.upsert({ projectId, document: doc })\n if (!upserted.ok) {\n dispatch({ type: 'fail', message: translate('ai.error.applyFailed') })\n return\n }\n // Provenance: record an AI-tagged version snapshot (a restore point + the\n // 'assistant' origin surfaced in the versions pane). Best-effort — the draft\n // already landed above; a snapshot failure must not lose the applied content.\n try {\n await operations.documents.snapshot({\n projectId,\n ref: { collection: proposal.collection, id: proposal.documentId },\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n // Reflect in the session so the new draft shows in the list + is navigable.\n cms.saveDocument(doc)\n dispatch({ type: 'apply', toolCallId: proposal.id })\n },\n [projectId, cms],\n )\n\n const openProposal = useCallback(\n (proposal: ProposalReview) => {\n if (projectId === undefined) return\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(proposal.collection, proposal.documentId) },\n })\n },\n [navigate, projectId],\n )\n\n const hasProposal = useCallback(\n (toolCallId: string) => review.proposals[toolCallId] !== undefined,\n [review.proposals],\n )\n\n const renderAssistantMessage = (message: UIMessage) => {\n const segments = segmentMessageParts(message.parts as MessagePartView[], hasProposal)\n return segments.map((segment, index) => {\n if (segment.kind === 'text') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n {segment.text}\n </Text>\n </AssistantTurn>\n )\n }\n if (segment.kind === 'tools') {\n return (\n <AssistantTurn key={`${message.id}-${index}`}>\n <ToolStatus steps={segment.parts.map(describeToolPart)} />\n </AssistantTurn>\n )\n }\n const proposal = review.proposals[segment.toolCallId]\n if (!proposal) return null\n return (\n <ProposalCard\n key={segment.toolCallId}\n proposal={proposal}\n onApply={() => void applyProposal(proposal)}\n onDiscard={() => dispatch({ type: 'reject', toolCallId: proposal.id })}\n onOpen={() => openProposal(proposal)}\n />\n )\n })\n }\n\n return (\n <aside className=\"flex h-full min-h-0 w-[360px] shrink-0 flex-col border-l border-border bg-card\">\n <Row gap=\"2\" align=\"center\" className=\"h-12 shrink-0 border-b border-border px-3\">\n <Heading level={2} size=\"h4\">\n {translate('ai.panel.title')}\n </Heading>\n <Spacer />\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={startNewConversation}\n disabled={chat.messages.length === 0 && review.error === null && !busy}\n title={translate('ai.panel.newConversation')}\n aria-label={translate('ai.panel.newConversation')}\n >\n <MessageSquarePlus />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n onClick={() => setAiPanelOpen(false)}\n title={translate('ai.panel.close')}\n aria-label={translate('ai.panel.close')}\n >\n <X />\n </Button>\n </Row>\n\n <Conversation atBottomKey={chat.messages}>\n {chat.messages.length === 0 ? <ConversationEmpty /> : null}\n {chat.messages.map((message) =>\n message.role === 'user' ? (\n <UserTurn key={message.id} text={messageText(message)} />\n ) : (\n <div key={message.id} className=\"contents\">\n {renderAssistantMessage(message)}\n </div>\n ),\n )}\n {chat.status === 'submitted' ? (\n <AssistantTurn>\n <WorkingIndicator />\n </AssistantTurn>\n ) : null}\n {review.error !== null ? (\n <AssistantTurn>\n <ChatErrorNote message={review.error} />\n </AssistantTurn>\n ) : null}\n </Conversation>\n\n {ready ? (\n <PromptInput value={input} onChange={setInput} onSubmit={submit} disabled={busy} />\n ) : (\n <div className=\"border-t border-border p-3\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.unavailable')}\n </Text>\n </div>\n )}\n </aside>\n )\n}\n"]}
|
|
@@ -1,12 +1,59 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { UIMessage } from 'ai';
|
|
3
3
|
/**
|
|
4
|
-
* AI-Elements-style chat primitives (EC-199), vendored as
|
|
5
|
-
* components (named, Tailwind-token) rather than a package
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* AI-Elements-style chat primitives (EC-199, reworked for EC-334), vendored as
|
|
5
|
+
* local shadcn-style components (named, Tailwind-token) rather than a package
|
|
6
|
+
* dependency. The conversation now renders the AI SDK's UIMessages directly:
|
|
7
|
+
* streaming text parts grow token-by-token, tool parts render as the ToolStatus
|
|
8
|
+
* affordance (`input-streaming → input-available → output-available`), and the
|
|
9
|
+
* terminal tools' parts render as `ProposalCard` (the panel wires that — this
|
|
10
|
+
* module stays purely presentational; pure part-mapping cores are exported for
|
|
11
|
+
* direct node tests, house pattern).
|
|
9
12
|
*/
|
|
13
|
+
/** One UIMessage part, viewed structurally (the fields the chat surface reads). */
|
|
14
|
+
export interface MessagePartView {
|
|
15
|
+
type: string;
|
|
16
|
+
text?: string;
|
|
17
|
+
toolCallId?: string;
|
|
18
|
+
state?: string;
|
|
19
|
+
input?: unknown;
|
|
20
|
+
output?: unknown;
|
|
21
|
+
errorText?: string;
|
|
22
|
+
}
|
|
23
|
+
/** The tool name behind a tool part's `type` (`tool-<name>` / `dynamic-tool`). */
|
|
24
|
+
export declare function toolPartName(part: MessagePartView): string | null;
|
|
25
|
+
/** A rendered segment of one assistant message. */
|
|
26
|
+
export type MessageSegment = {
|
|
27
|
+
kind: 'text';
|
|
28
|
+
text: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'tools';
|
|
31
|
+
parts: MessagePartView[];
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'proposal';
|
|
34
|
+
toolCallId: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Fold an assistant UIMessage's parts into render segments: consecutive tool
|
|
38
|
+
* parts group into ONE ToolStatus block (the EC-199 affordance), a terminal
|
|
39
|
+
* tool part with a registered proposal becomes a `proposal` segment, and text
|
|
40
|
+
* parts pass through (empty ones dropped). Pure — tested directly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function segmentMessageParts(parts: readonly MessagePartView[], hasProposal: (toolCallId: string) => boolean): MessageSegment[];
|
|
43
|
+
/** One presentational tool-status line. */
|
|
44
|
+
export interface ChatToolStep {
|
|
45
|
+
icon: 'search' | 'assets' | 'document' | 'draft' | 'translate';
|
|
46
|
+
label: string;
|
|
47
|
+
/** Still running (no output yet) — renders the pulse affordance. */
|
|
48
|
+
running: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Map one tool part onto its status line (`input-streaming → input-available →
|
|
52
|
+
* output-available | output-error`). Pure — tested directly.
|
|
53
|
+
*/
|
|
54
|
+
export declare function describeToolPart(part: MessagePartView): ChatToolStep;
|
|
55
|
+
/** Join a UIMessage's text parts (the user-turn renderer). */
|
|
56
|
+
export declare function messageText(message: Pick<UIMessage, 'parts'>): string;
|
|
10
57
|
/** The scrolling conversation column; auto-scrolls to the newest turn. */
|
|
11
58
|
export declare function Conversation({ children, atBottomKey }: {
|
|
12
59
|
children: ReactNode;
|
|
@@ -22,11 +69,16 @@ export declare function AssistantTurn({ children }: {
|
|
|
22
69
|
}): import("react").JSX.Element;
|
|
23
70
|
/** The empty-state prompt shown before the first message. */
|
|
24
71
|
export declare function ConversationEmpty(): import("react").JSX.Element;
|
|
25
|
-
/** The tool-status affordance — the tool steps
|
|
26
|
-
export declare function ToolStatus({ steps
|
|
27
|
-
steps:
|
|
28
|
-
active: boolean;
|
|
72
|
+
/** The tool-status affordance — the tool steps of one segment, as calm status lines. */
|
|
73
|
+
export declare function ToolStatus({ steps }: {
|
|
74
|
+
steps: ChatToolStep[];
|
|
29
75
|
}): import("react").JSX.Element | null;
|
|
76
|
+
/** The busy indicator shown while the route request is in flight (pre-stream). */
|
|
77
|
+
export declare function WorkingIndicator(): import("react").JSX.Element;
|
|
78
|
+
/** A blocking-error note (route/provider failure, step cap) — visible, never silent. */
|
|
79
|
+
export declare function ChatErrorNote({ message }: {
|
|
80
|
+
message: string;
|
|
81
|
+
}): import("react").JSX.Element;
|
|
30
82
|
/** The prompt input — a growing textarea + send button; Enter sends, Shift+Enter newline. */
|
|
31
83
|
export declare function PromptInput({ value, onChange, onSubmit, disabled, }: {
|
|
32
84
|
value: string;
|
|
@@ -1,16 +1,122 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
-
import { FileText, Images, Languages, Search, Sparkles } from 'lucide-react';
|
|
3
|
+
import { FileText, Images, Languages, Search, Sparkles, TriangleAlert } from 'lucide-react';
|
|
4
4
|
import { Button, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
5
5
|
import { translate } from '@elytracms/studio/lib/i18n';
|
|
6
6
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
7
|
+
/** The terminal tools whose `output-available` part is a reviewable proposal. */
|
|
8
|
+
const TERMINAL_TOOLS = new Set(['createDocument', 'translateDocument']);
|
|
9
|
+
/** The tool name behind a tool part's `type` (`tool-<name>` / `dynamic-tool`). */
|
|
10
|
+
export function toolPartName(part) {
|
|
11
|
+
if (part.type === 'dynamic-tool') {
|
|
12
|
+
return typeof part.toolName === 'string'
|
|
13
|
+
? (part.toolName ?? null)
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
return part.type.startsWith('tool-') ? part.type.slice('tool-'.length) : null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fold an assistant UIMessage's parts into render segments: consecutive tool
|
|
20
|
+
* parts group into ONE ToolStatus block (the EC-199 affordance), a terminal
|
|
21
|
+
* tool part with a registered proposal becomes a `proposal` segment, and text
|
|
22
|
+
* parts pass through (empty ones dropped). Pure — tested directly.
|
|
23
|
+
*/
|
|
24
|
+
export function segmentMessageParts(parts, hasProposal) {
|
|
25
|
+
const segments = [];
|
|
26
|
+
for (const part of parts) {
|
|
27
|
+
if (part.type === 'text') {
|
|
28
|
+
const text = part.text ?? '';
|
|
29
|
+
if (text.length > 0)
|
|
30
|
+
segments.push({ kind: 'text', text });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const tool = toolPartName(part);
|
|
34
|
+
if (tool === null)
|
|
35
|
+
continue; // step-start / reasoning / data-* — not rendered
|
|
36
|
+
if (TERMINAL_TOOLS.has(tool) &&
|
|
37
|
+
part.toolCallId !== undefined &&
|
|
38
|
+
part.state === 'output-available' &&
|
|
39
|
+
hasProposal(part.toolCallId)) {
|
|
40
|
+
segments.push({ kind: 'proposal', toolCallId: part.toolCallId });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const last = segments.at(-1);
|
|
44
|
+
if (last?.kind === 'tools')
|
|
45
|
+
last.parts.push(part);
|
|
46
|
+
else
|
|
47
|
+
segments.push({ kind: 'tools', parts: [part] });
|
|
48
|
+
}
|
|
49
|
+
return segments;
|
|
50
|
+
}
|
|
51
|
+
function inputField(part, field) {
|
|
52
|
+
const input = part.input;
|
|
53
|
+
if (typeof input !== 'object' || input === null)
|
|
54
|
+
return undefined;
|
|
55
|
+
const value = input[field];
|
|
56
|
+
return typeof value === 'string' ? value : undefined;
|
|
57
|
+
}
|
|
7
58
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* pieces this one flow needs: a scrolling conversation, user/assistant turns, the
|
|
11
|
-
* tool-status affordance, an empty-state, and the prompt input. They are purely
|
|
12
|
-
* presentational; the panel owns the state (the `chatReducer`).
|
|
59
|
+
* Map one tool part onto its status line (`input-streaming → input-available →
|
|
60
|
+
* output-available | output-error`). Pure — tested directly.
|
|
13
61
|
*/
|
|
62
|
+
export function describeToolPart(part) {
|
|
63
|
+
const tool = toolPartName(part) ?? 'tool';
|
|
64
|
+
const running = part.state === 'input-streaming' || part.state === 'input-available';
|
|
65
|
+
const failed = part.state === 'output-error';
|
|
66
|
+
if (failed) {
|
|
67
|
+
return {
|
|
68
|
+
icon: 'draft',
|
|
69
|
+
label: part.errorText ?? translate('ai.error.generic'),
|
|
70
|
+
running: false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
switch (tool) {
|
|
74
|
+
case 'searchDocuments': {
|
|
75
|
+
const collection = inputField(part, 'collection') ?? '…';
|
|
76
|
+
if (running)
|
|
77
|
+
return { icon: 'search', label: translate('ai.step.searchRunning', { collection }), running };
|
|
78
|
+
const count = Array.isArray(part.output) ? part.output.length : 0;
|
|
79
|
+
return { icon: 'search', label: translate('ai.step.search', { collection, count }), running };
|
|
80
|
+
}
|
|
81
|
+
case 'listAssets': {
|
|
82
|
+
if (running)
|
|
83
|
+
return { icon: 'assets', label: translate('ai.step.listAssetsRunning'), running };
|
|
84
|
+
const count = Array.isArray(part.output) ? part.output.length : 0;
|
|
85
|
+
return { icon: 'assets', label: translate('ai.step.listAssets', { count }), running };
|
|
86
|
+
}
|
|
87
|
+
case 'getDocument':
|
|
88
|
+
return {
|
|
89
|
+
icon: 'document',
|
|
90
|
+
label: running
|
|
91
|
+
? translate('ai.step.getDocumentRunning')
|
|
92
|
+
: translate('ai.step.getDocument'),
|
|
93
|
+
running,
|
|
94
|
+
};
|
|
95
|
+
case 'createDocument':
|
|
96
|
+
return {
|
|
97
|
+
icon: 'draft',
|
|
98
|
+
label: translate('ai.step.drafting', { collection: inputField(part, 'collection') ?? '…' }),
|
|
99
|
+
running,
|
|
100
|
+
};
|
|
101
|
+
case 'translateDocument': {
|
|
102
|
+
const output = part.output;
|
|
103
|
+
if (!running && output?.status === 'nothing-to-translate') {
|
|
104
|
+
return { icon: 'translate', label: translate('ai.step.translateNothing'), running: false };
|
|
105
|
+
}
|
|
106
|
+
return { icon: 'translate', label: translate('ai.step.translatingDoc'), running };
|
|
107
|
+
}
|
|
108
|
+
default:
|
|
109
|
+
return { icon: 'draft', label: tool, running };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Join a UIMessage's text parts (the user-turn renderer). */
|
|
113
|
+
export function messageText(message) {
|
|
114
|
+
return message.parts
|
|
115
|
+
.filter((part) => part.type === 'text')
|
|
116
|
+
.map((part) => part.text ?? '')
|
|
117
|
+
.join('\n');
|
|
118
|
+
}
|
|
119
|
+
// --- Presentational components -------------------------------------------------------
|
|
14
120
|
/** The scrolling conversation column; auto-scrolls to the newest turn. */
|
|
15
121
|
export function Conversation({ children, atBottomKey }) {
|
|
16
122
|
const ref = useRef(null);
|
|
@@ -33,31 +139,32 @@ export function AssistantTurn({ children }) {
|
|
|
33
139
|
export function ConversationEmpty() {
|
|
34
140
|
return (_jsxs(Stack, { gap: "2", align: "center", className: "px-4 py-10 text-center", children: [_jsx("div", { className: "grid size-10 place-items-center rounded-full bg-muted text-muted-foreground", children: _jsx(Sparkles, { className: "size-5" }) }), _jsx(Text, { size: "sm", weight: "medium", children: translate('ai.panel.emptyTitle') }), _jsx(Text, { size: "xs", tone: "muted", className: "max-w-[24rem]", children: translate('ai.panel.emptyHint') })] }));
|
|
35
141
|
}
|
|
36
|
-
/** The tool-status affordance — the tool steps
|
|
37
|
-
export function ToolStatus({ steps
|
|
38
|
-
if (steps.length === 0
|
|
142
|
+
/** The tool-status affordance — the tool steps of one segment, as calm status lines. */
|
|
143
|
+
export function ToolStatus({ steps }) {
|
|
144
|
+
if (steps.length === 0)
|
|
39
145
|
return null;
|
|
40
|
-
return (
|
|
146
|
+
return (_jsx(Stack, { gap: "1", className: "rounded-lg border border-border/60 bg-muted/30 px-2.5 py-2", children: steps.map((step, i) => (_jsxs(Row, { gap: "2", align: "center", children: [_jsx(StepIcon, { icon: step.icon }), _jsx(Text, { size: "xs", tone: "muted", children: step.label }), step.running ? (_jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-muted-foreground", "aria-hidden": true })) : null] }, i))) }));
|
|
41
147
|
}
|
|
42
|
-
|
|
148
|
+
/** The busy indicator shown while the route request is in flight (pre-stream). */
|
|
149
|
+
export function WorkingIndicator() {
|
|
150
|
+
return (_jsxs(Row, { gap: "2", align: "center", children: [_jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-muted-foreground", "aria-hidden": true }), _jsx(Text, { size: "xs", tone: "muted", children: translate('ai.panel.working') })] }));
|
|
151
|
+
}
|
|
152
|
+
/** A blocking-error note (route/provider failure, step cap) — visible, never silent. */
|
|
153
|
+
export function ChatErrorNote({ message }) {
|
|
154
|
+
return (_jsxs(Row, { gap: "2", align: "start", className: "rounded-lg border border-destructive/30 bg-destructive/5 px-2.5 py-2", children: [_jsx(TriangleAlert, { className: "mt-0.5 size-3.5 shrink-0 text-destructive" }), _jsx(Text, { size: "xs", className: "text-destructive", children: message })] }));
|
|
155
|
+
}
|
|
156
|
+
function StepIcon({ icon }) {
|
|
43
157
|
const className = 'size-3.5 shrink-0 text-muted-foreground';
|
|
44
|
-
if (
|
|
158
|
+
if (icon === 'search')
|
|
45
159
|
return _jsx(Search, { className: className });
|
|
46
|
-
if (
|
|
160
|
+
if (icon === 'assets')
|
|
47
161
|
return _jsx(Images, { className: className });
|
|
48
|
-
if (
|
|
162
|
+
if (icon === 'translate')
|
|
49
163
|
return _jsx(Languages, { className: className });
|
|
164
|
+
if (icon === 'document')
|
|
165
|
+
return _jsx(FileText, { className: className });
|
|
50
166
|
return _jsx(FileText, { className: className });
|
|
51
167
|
}
|
|
52
|
-
function stepLabel(step) {
|
|
53
|
-
if (step.kind === 'search')
|
|
54
|
-
return translate('ai.step.search', { collection: step.collection, count: step.resultCount });
|
|
55
|
-
if (step.kind === 'listAssets')
|
|
56
|
-
return translate('ai.step.listAssets', { count: step.resultCount });
|
|
57
|
-
if (step.kind === 'translating')
|
|
58
|
-
return translate('ai.step.translating', { completed: step.completed, total: step.total });
|
|
59
|
-
return translate('ai.step.drafting', { collection: step.collection });
|
|
60
|
-
}
|
|
61
168
|
/** The prompt input — a growing textarea + send button; Enter sends, Shift+Enter newline. */
|
|
62
169
|
export function PromptInput({ value, onChange, onSubmit, disabled, }) {
|
|
63
170
|
const handleKeyDown = useCallback((event) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-ui.js","sourceRoot":"","sources":["../../../src/components/ai/chat-ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport type { KeyboardEvent, ReactNode } from 'react'\nimport { FileText, Images, Languages, Search, Sparkles } from 'lucide-react'\nimport { Button, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport type { CreateFlowStep } from '@elytracms/studio/lib/ai'\n\n/**\n * AI-Elements-style chat primitives (EC-199), vendored as local shadcn-style\n * components (named, Tailwind-token) rather than a package dependency — only the\n * pieces this one flow needs: a scrolling conversation, user/assistant turns, the\n * tool-status affordance, an empty-state, and the prompt input. They are purely\n * presentational; the panel owns the state (the `chatReducer`).\n */\n\n/** The scrolling conversation column; auto-scrolls to the newest turn. */\nexport function Conversation({ children, atBottomKey }: { children: ReactNode; atBottomKey: unknown }) {\n const ref = useRef<HTMLDivElement>(null)\n useEffect(() => {\n const el = ref.current\n if (el) el.scrollTop = el.scrollHeight\n }, [atBottomKey])\n return (\n <div ref={ref} className=\"min-h-0 flex-1 overflow-auto\">\n <Stack gap=\"3\" className=\"p-3\">\n {children}\n </Stack>\n </div>\n )\n}\n\n/** A user turn — a right-aligned bubble. */\nexport function UserTurn({ text }: { text: string }) {\n return (\n <Row justify=\"end\">\n <div className=\"max-w-[85%] rounded-2xl rounded-br-sm bg-primary px-3 py-2 text-primary-foreground\">\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words text-primary-foreground\">\n {text}\n </Text>\n </div>\n </Row>\n )\n}\n\n/** An assistant text turn — a left-aligned calm block. */\nexport function AssistantTurn({ children }: { children: ReactNode }) {\n return (\n <Row justify=\"start\" align=\"start\" gap=\"2\">\n <div className=\"mt-0.5 grid size-6 shrink-0 place-items-center rounded-full bg-muted text-muted-foreground\">\n <Sparkles className=\"size-3.5\" />\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </Row>\n )\n}\n\n/** The empty-state prompt shown before the first message. */\nexport function ConversationEmpty() {\n return (\n <Stack gap=\"2\" align=\"center\" className=\"px-4 py-10 text-center\">\n <div className=\"grid size-10 place-items-center rounded-full bg-muted text-muted-foreground\">\n <Sparkles className=\"size-5\" />\n </div>\n <Text size=\"sm\" weight=\"medium\">\n {translate('ai.panel.emptyTitle')}\n </Text>\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-[24rem]\">\n {translate('ai.panel.emptyHint')}\n </Text>\n </Stack>\n )\n}\n\n/** The tool-status affordance — the tool steps the loop ran, as calm status lines. */\nexport function ToolStatus({ steps, active }: { steps: CreateFlowStep[]; active: boolean }) {\n if (steps.length === 0 && !active) return null\n return (\n <Stack gap=\"1\" className=\"rounded-lg border border-border/60 bg-muted/30 px-2.5 py-2\">\n {steps.map((step, i) => (\n <Row key={i} gap=\"2\" align=\"center\">\n <StepIcon step={step} />\n <Text size=\"xs\" tone=\"muted\">\n {stepLabel(step)}\n </Text>\n </Row>\n ))}\n {active ? (\n <Row gap=\"2\" align=\"center\">\n <span className=\"inline-block size-1.5 animate-pulse rounded-full bg-muted-foreground\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.working')}\n </Text>\n </Row>\n ) : null}\n </Stack>\n )\n}\n\nfunction StepIcon({ step }: { step: CreateFlowStep }) {\n const className = 'size-3.5 shrink-0 text-muted-foreground'\n if (step.kind === 'search') return <Search className={className} />\n if (step.kind === 'listAssets') return <Images className={className} />\n if (step.kind === 'translating') return <Languages className={className} />\n return <FileText className={className} />\n}\n\nfunction stepLabel(step: CreateFlowStep): string {\n if (step.kind === 'search')\n return translate('ai.step.search', { collection: step.collection, count: step.resultCount })\n if (step.kind === 'listAssets') return translate('ai.step.listAssets', { count: step.resultCount })\n if (step.kind === 'translating')\n return translate('ai.step.translating', { completed: step.completed, total: step.total })\n return translate('ai.step.drafting', { collection: step.collection })\n}\n\n/** The prompt input — a growing textarea + send button; Enter sends, Shift+Enter newline. */\nexport function PromptInput({\n value,\n onChange,\n onSubmit,\n disabled,\n}: {\n value: string\n onChange: (value: string) => void\n onSubmit: () => void\n disabled: boolean\n}) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n if (!disabled && value.trim().length > 0) onSubmit()\n }\n },\n [disabled, value, onSubmit],\n )\n return (\n <div className=\"border-t border-border p-2.5\">\n <div\n className={cn(\n 'flex items-end gap-2 rounded-xl border border-border bg-background px-2.5 py-2',\n 'focus-within:border-primary/60',\n )}\n >\n <textarea\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={1}\n placeholder={translate('ai.panel.placeholder')}\n aria-label={translate('ai.panel.placeholder')}\n className=\"max-h-40 min-h-[1.5rem] flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <Button\n size=\"sm\"\n onClick={onSubmit}\n disabled={disabled || value.trim().length === 0}\n title={translate('ai.panel.send')}\n aria-label={translate('ai.panel.send')}\n >\n {translate('ai.panel.send')}\n </Button>\n </div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat-ui.js","sourceRoot":"","sources":["../../../src/components/ai/chat-ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport type { KeyboardEvent, ReactNode } from 'react'\nimport { FileText, Images, Languages, Search, Sparkles, TriangleAlert } from 'lucide-react'\n// Type-only (erased at compile; `verbatimModuleSyntax`): the UIMessage part shapes.\nimport type { UIMessage } from 'ai'\nimport { Button, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\n\n/**\n * AI-Elements-style chat primitives (EC-199, reworked for EC-334), vendored as\n * local shadcn-style components (named, Tailwind-token) rather than a package\n * dependency. The conversation now renders the AI SDK's UIMessages directly:\n * streaming text parts grow token-by-token, tool parts render as the ToolStatus\n * affordance (`input-streaming → input-available → output-available`), and the\n * terminal tools' parts render as `ProposalCard` (the panel wires that — this\n * module stays purely presentational; pure part-mapping cores are exported for\n * direct node tests, house pattern).\n */\n\n// --- UIMessage part mapping (pure cores) --------------------------------------------\n\n/** One UIMessage part, viewed structurally (the fields the chat surface reads). */\nexport interface MessagePartView {\n type: string\n text?: string\n toolCallId?: string\n state?: string\n input?: unknown\n output?: unknown\n errorText?: string\n}\n\n/** The terminal tools whose `output-available` part is a reviewable proposal. */\nconst TERMINAL_TOOLS = new Set(['createDocument', 'translateDocument'])\n\n/** The tool name behind a tool part's `type` (`tool-<name>` / `dynamic-tool`). */\nexport function toolPartName(part: MessagePartView): string | null {\n if (part.type === 'dynamic-tool') {\n return typeof (part as { toolName?: unknown }).toolName === 'string'\n ? ((part as { toolName?: string }).toolName ?? null)\n : null\n }\n return part.type.startsWith('tool-') ? part.type.slice('tool-'.length) : null\n}\n\n/** A rendered segment of one assistant message. */\nexport type MessageSegment =\n | { kind: 'text'; text: string }\n | { kind: 'tools'; parts: MessagePartView[] }\n | { kind: 'proposal'; toolCallId: string }\n\n/**\n * Fold an assistant UIMessage's parts into render segments: consecutive tool\n * parts group into ONE ToolStatus block (the EC-199 affordance), a terminal\n * tool part with a registered proposal becomes a `proposal` segment, and text\n * parts pass through (empty ones dropped). Pure — tested directly.\n */\nexport function segmentMessageParts(\n parts: readonly MessagePartView[],\n hasProposal: (toolCallId: string) => boolean,\n): MessageSegment[] {\n const segments: MessageSegment[] = []\n for (const part of parts) {\n if (part.type === 'text') {\n const text = part.text ?? ''\n if (text.length > 0) segments.push({ kind: 'text', text })\n continue\n }\n const tool = toolPartName(part)\n if (tool === null) continue // step-start / reasoning / data-* — not rendered\n if (\n TERMINAL_TOOLS.has(tool) &&\n part.toolCallId !== undefined &&\n part.state === 'output-available' &&\n hasProposal(part.toolCallId)\n ) {\n segments.push({ kind: 'proposal', toolCallId: part.toolCallId })\n continue\n }\n const last = segments.at(-1)\n if (last?.kind === 'tools') last.parts.push(part)\n else segments.push({ kind: 'tools', parts: [part] })\n }\n return segments\n}\n\n/** One presentational tool-status line. */\nexport interface ChatToolStep {\n icon: 'search' | 'assets' | 'document' | 'draft' | 'translate'\n label: string\n /** Still running (no output yet) — renders the pulse affordance. */\n running: boolean\n}\n\nfunction inputField(part: MessagePartView, field: string): string | undefined {\n const input = part.input\n if (typeof input !== 'object' || input === null) return undefined\n const value = (input as Record<string, unknown>)[field]\n return typeof value === 'string' ? value : undefined\n}\n\n/**\n * Map one tool part onto its status line (`input-streaming → input-available →\n * output-available | output-error`). Pure — tested directly.\n */\nexport function describeToolPart(part: MessagePartView): ChatToolStep {\n const tool = toolPartName(part) ?? 'tool'\n const running = part.state === 'input-streaming' || part.state === 'input-available'\n const failed = part.state === 'output-error'\n if (failed) {\n return {\n icon: 'draft',\n label: part.errorText ?? translate('ai.error.generic'),\n running: false,\n }\n }\n switch (tool) {\n case 'searchDocuments': {\n const collection = inputField(part, 'collection') ?? '…'\n if (running) return { icon: 'search', label: translate('ai.step.searchRunning', { collection }), running }\n const count = Array.isArray(part.output) ? part.output.length : 0\n return { icon: 'search', label: translate('ai.step.search', { collection, count }), running }\n }\n case 'listAssets': {\n if (running) return { icon: 'assets', label: translate('ai.step.listAssetsRunning'), running }\n const count = Array.isArray(part.output) ? part.output.length : 0\n return { icon: 'assets', label: translate('ai.step.listAssets', { count }), running }\n }\n case 'getDocument':\n return {\n icon: 'document',\n label: running\n ? translate('ai.step.getDocumentRunning')\n : translate('ai.step.getDocument'),\n running,\n }\n case 'createDocument':\n return {\n icon: 'draft',\n label: translate('ai.step.drafting', { collection: inputField(part, 'collection') ?? '…' }),\n running,\n }\n case 'translateDocument': {\n const output = part.output as { status?: unknown } | undefined\n if (!running && output?.status === 'nothing-to-translate') {\n return { icon: 'translate', label: translate('ai.step.translateNothing'), running: false }\n }\n return { icon: 'translate', label: translate('ai.step.translatingDoc'), running }\n }\n default:\n return { icon: 'draft', label: tool, running }\n }\n}\n\n/** Join a UIMessage's text parts (the user-turn renderer). */\nexport function messageText(message: Pick<UIMessage, 'parts'>): string {\n return (message.parts as MessagePartView[])\n .filter((part) => part.type === 'text')\n .map((part) => part.text ?? '')\n .join('\\n')\n}\n\n// --- Presentational components -------------------------------------------------------\n\n/** The scrolling conversation column; auto-scrolls to the newest turn. */\nexport function Conversation({ children, atBottomKey }: { children: ReactNode; atBottomKey: unknown }) {\n const ref = useRef<HTMLDivElement>(null)\n useEffect(() => {\n const el = ref.current\n if (el) el.scrollTop = el.scrollHeight\n }, [atBottomKey])\n return (\n <div ref={ref} className=\"min-h-0 flex-1 overflow-auto\">\n <Stack gap=\"3\" className=\"p-3\">\n {children}\n </Stack>\n </div>\n )\n}\n\n/** A user turn — a right-aligned bubble. */\nexport function UserTurn({ text }: { text: string }) {\n return (\n <Row justify=\"end\">\n <div className=\"max-w-[85%] rounded-2xl rounded-br-sm bg-primary px-3 py-2 text-primary-foreground\">\n <Text size=\"sm\" className=\"whitespace-pre-wrap break-words text-primary-foreground\">\n {text}\n </Text>\n </div>\n </Row>\n )\n}\n\n/** An assistant text turn — a left-aligned calm block. */\nexport function AssistantTurn({ children }: { children: ReactNode }) {\n return (\n <Row justify=\"start\" align=\"start\" gap=\"2\">\n <div className=\"mt-0.5 grid size-6 shrink-0 place-items-center rounded-full bg-muted text-muted-foreground\">\n <Sparkles className=\"size-3.5\" />\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </Row>\n )\n}\n\n/** The empty-state prompt shown before the first message. */\nexport function ConversationEmpty() {\n return (\n <Stack gap=\"2\" align=\"center\" className=\"px-4 py-10 text-center\">\n <div className=\"grid size-10 place-items-center rounded-full bg-muted text-muted-foreground\">\n <Sparkles className=\"size-5\" />\n </div>\n <Text size=\"sm\" weight=\"medium\">\n {translate('ai.panel.emptyTitle')}\n </Text>\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-[24rem]\">\n {translate('ai.panel.emptyHint')}\n </Text>\n </Stack>\n )\n}\n\n/** The tool-status affordance — the tool steps of one segment, as calm status lines. */\nexport function ToolStatus({ steps }: { steps: ChatToolStep[] }) {\n if (steps.length === 0) return null\n return (\n <Stack gap=\"1\" className=\"rounded-lg border border-border/60 bg-muted/30 px-2.5 py-2\">\n {steps.map((step, i) => (\n <Row key={i} gap=\"2\" align=\"center\">\n <StepIcon icon={step.icon} />\n <Text size=\"xs\" tone=\"muted\">\n {step.label}\n </Text>\n {step.running ? (\n <span\n className=\"inline-block size-1.5 animate-pulse rounded-full bg-muted-foreground\"\n aria-hidden\n />\n ) : null}\n </Row>\n ))}\n </Stack>\n )\n}\n\n/** The busy indicator shown while the route request is in flight (pre-stream). */\nexport function WorkingIndicator() {\n return (\n <Row gap=\"2\" align=\"center\">\n <span className=\"inline-block size-1.5 animate-pulse rounded-full bg-muted-foreground\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.panel.working')}\n </Text>\n </Row>\n )\n}\n\n/** A blocking-error note (route/provider failure, step cap) — visible, never silent. */\nexport function ChatErrorNote({ message }: { message: string }) {\n return (\n <Row gap=\"2\" align=\"start\" className=\"rounded-lg border border-destructive/30 bg-destructive/5 px-2.5 py-2\">\n <TriangleAlert className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" />\n <Text size=\"xs\" className=\"text-destructive\">\n {message}\n </Text>\n </Row>\n )\n}\n\nfunction StepIcon({ icon }: { icon: ChatToolStep['icon'] }) {\n const className = 'size-3.5 shrink-0 text-muted-foreground'\n if (icon === 'search') return <Search className={className} />\n if (icon === 'assets') return <Images className={className} />\n if (icon === 'translate') return <Languages className={className} />\n if (icon === 'document') return <FileText className={className} />\n return <FileText className={className} />\n}\n\n/** The prompt input — a growing textarea + send button; Enter sends, Shift+Enter newline. */\nexport function PromptInput({\n value,\n onChange,\n onSubmit,\n disabled,\n}: {\n value: string\n onChange: (value: string) => void\n onSubmit: () => void\n disabled: boolean\n}) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n if (!disabled && value.trim().length > 0) onSubmit()\n }\n },\n [disabled, value, onSubmit],\n )\n return (\n <div className=\"border-t border-border p-2.5\">\n <div\n className={cn(\n 'flex items-end gap-2 rounded-xl border border-border bg-background px-2.5 py-2',\n 'focus-within:border-primary/60',\n )}\n >\n <textarea\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={1}\n placeholder={translate('ai.panel.placeholder')}\n aria-label={translate('ai.panel.placeholder')}\n className=\"max-h-40 min-h-[1.5rem] flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <Button\n size=\"sm\"\n onClick={onSubmit}\n disabled={disabled || value.trim().length === 0}\n title={translate('ai.panel.send')}\n aria-label={translate('ai.panel.send')}\n >\n {translate('ai.panel.send')}\n </Button>\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anthropic provider adapter over the Vercel AI SDK (EC-331).
|
|
3
|
-
*
|
|
4
|
-
* The ONLY module that touches `ai` / `@ai-sdk/anthropic`, and it does so through
|
|
5
|
-
* dynamic `import()` — so importing this file (or the `lib/ai` barrel) never eagerly
|
|
6
|
-
* loads the AI SDK. Combined with the type-only `StudioAiConfig` import in the studio
|
|
7
|
-
* config, an unconfigured studio pulls no AI SDK module onto the critical path.
|
|
8
|
-
*
|
|
9
|
-
* Same dependency discipline as TipTap (EC-314): the SDK is a `@elytracms/studio`-only
|
|
10
|
-
* dependency, never in `packages/core`. Key custody stays with the host: the key is
|
|
11
|
-
* read from host env by the mount and passed in (or falls back to `ANTHROPIC_API_KEY`).
|
|
12
|
-
*/
|
|
13
1
|
import type { AiProvider } from './types.js';
|
|
14
2
|
export interface AnthropicProviderOptions {
|
|
15
3
|
/** Provider key (host-env secret). Falls back to the SDK's `ANTHROPIC_API_KEY` lookup. */
|
|
@@ -8,6 +8,8 @@ export const DEFAULT_ANTHROPIC_MODEL = 'claude-opus-4-8';
|
|
|
8
8
|
export function createAnthropicProvider(options = {}) {
|
|
9
9
|
const model = options.model ?? DEFAULT_ANTHROPIC_MODEL;
|
|
10
10
|
// Lazily construct the SDK language model — no eager import of the AI SDK.
|
|
11
|
+
// Exposed as the provider's `languageModel()` seam (EC-334): the route's native
|
|
12
|
+
// chat branch consumes it for `streamText`.
|
|
11
13
|
async function languageModel() {
|
|
12
14
|
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
13
15
|
const anthropic = createAnthropic({
|
|
@@ -20,6 +22,7 @@ export function createAnthropicProvider(options = {}) {
|
|
|
20
22
|
id: 'anthropic',
|
|
21
23
|
model,
|
|
22
24
|
capabilities: () => ({ toolCalling: true, structuredOutput: true }),
|
|
25
|
+
languageModel,
|
|
23
26
|
async generateStructured(request) {
|
|
24
27
|
const { generateObject, jsonSchema } = await import('ai');
|
|
25
28
|
const languageModelInstance = await languageModel();
|
|
@@ -43,17 +46,6 @@ export function createAnthropicProvider(options = {}) {
|
|
|
43
46
|
},
|
|
44
47
|
};
|
|
45
48
|
},
|
|
46
|
-
async streamChat(request) {
|
|
47
|
-
const { streamText } = await import('ai');
|
|
48
|
-
const languageModelInstance = await languageModel();
|
|
49
|
-
const result = streamText({
|
|
50
|
-
model: languageModelInstance,
|
|
51
|
-
...(request.system !== undefined ? { system: request.system } : {}),
|
|
52
|
-
// `AiChatMessage[]` is structurally a `ModelMessage[]` (role + string content).
|
|
53
|
-
messages: request.messages,
|
|
54
|
-
});
|
|
55
|
-
return result.toTextStreamResponse();
|
|
56
|
-
},
|
|
57
49
|
};
|
|
58
50
|
}
|
|
59
51
|
//# sourceMappingURL=anthropic-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../../../src/lib/ai/anthropic-provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../../../src/lib/ai/anthropic-provider.ts"],"names":[],"mappings":"AAyBA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAA;AAQxD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAoC,EAAE;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,uBAAuB,CAAA;IAEtD,2EAA2E;IAC3E,gFAAgF;IAChF,4CAA4C;IAC5C,KAAK,UAAU,aAAa;QAC1B,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAA;QAC7D,MAAM,SAAS,GAAG,eAAe,CAAC;YAChC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE,CAAC,CAAA;QACF,OAAO,SAAS,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,OAAO;QACL,EAAE,EAAE,WAAW;QACf,KAAK;QACL,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QACnE,aAAa;QAEb,KAAK,CAAC,kBAAkB,CACtB,OAAkC;YAElC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;YACzD,MAAM,qBAAqB,GAAG,MAAM,aAAa,EAAE,CAAA;YACnD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;gBAClC,KAAK,EAAE,qBAAqB;gBAC5B,+EAA+E;gBAC/E,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,MAA0C,CAAC;gBACtE,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/E,GAAG,CAAC,OAAO,CAAC,iBAAiB,KAAK,SAAS;oBACzC,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,EAAE;oBAClD,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAA;YACF,MAAM,KAAK,GAAG,MAAM,CAAC,KAA6B,CAAA;YAClD,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,KAAK,EAAE;oBACL,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/E,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnF;aACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Anthropic provider adapter over the Vercel AI SDK (EC-331).\n *\n * The ONLY module that touches `ai` / `@ai-sdk/anthropic`, and it does so through\n * dynamic `import()` — so importing this file (or the `lib/ai` barrel) never eagerly\n * loads the AI SDK. Combined with the type-only `StudioAiConfig` import in the studio\n * config, an unconfigured studio pulls no AI SDK module onto the critical path.\n *\n * Same dependency discipline as TipTap (EC-314): the SDK is a `@elytracms/studio`-only\n * dependency, never in `packages/core`. Key custody stays with the host: the key is\n * read from host env by the mount and passed in (or falls back to `ANTHROPIC_API_KEY`).\n */\n// Type-only (erased at compile) — the runtime SDK loads via dynamic `import()` below.\nimport type { LanguageModel } from 'ai'\nimport type { AiProvider, GenerateStructuredRequest, GenerateStructuredResult } from './types'\n\nexport interface AnthropicProviderOptions {\n /** Provider key (host-env secret). Falls back to the SDK's `ANTHROPIC_API_KEY` lookup. */\n apiKey?: string\n /** Model id; defaults to {@link DEFAULT_ANTHROPIC_MODEL}. */\n model?: string\n /** Optional base URL override (proxies / gateways). */\n baseURL?: string\n}\n\n/** Default model — a current tool-calling, structured-output Claude model. */\nexport const DEFAULT_ANTHROPIC_MODEL = 'claude-opus-4-8'\n\n/** A minimal view of the AI SDK usage object (field names read best-effort). */\ninterface SdkUsage {\n inputTokens?: number\n outputTokens?: number\n}\n\n/**\n * Build an Anthropic-backed {@link AiProvider}. Tool calling + structured output are\n * supported, so it satisfies the capability gate. The AI SDK is loaded lazily on\n * first call.\n */\nexport function createAnthropicProvider(options: AnthropicProviderOptions = {}): AiProvider {\n const model = options.model ?? DEFAULT_ANTHROPIC_MODEL\n\n // Lazily construct the SDK language model — no eager import of the AI SDK.\n // Exposed as the provider's `languageModel()` seam (EC-334): the route's native\n // chat branch consumes it for `streamText`.\n async function languageModel(): Promise<LanguageModel> {\n const { createAnthropic } = await import('@ai-sdk/anthropic')\n const anthropic = createAnthropic({\n ...(options.apiKey !== undefined ? { apiKey: options.apiKey } : {}),\n ...(options.baseURL !== undefined ? { baseURL: options.baseURL } : {}),\n })\n return anthropic(model)\n }\n\n return {\n id: 'anthropic',\n model,\n capabilities: () => ({ toolCalling: true, structuredOutput: true }),\n languageModel,\n\n async generateStructured(\n request: GenerateStructuredRequest,\n ): Promise<GenerateStructuredResult> {\n const { generateObject, jsonSchema } = await import('ai')\n const languageModelInstance = await languageModel()\n const result = await generateObject({\n model: languageModelInstance,\n // `request.schema` is a JSON-Schema object; `jsonSchema` accepts exactly that.\n schema: jsonSchema(request.schema as Parameters<typeof jsonSchema>[0]),\n ...(request.schemaName !== undefined ? { schemaName: request.schemaName } : {}),\n ...(request.schemaDescription !== undefined\n ? { schemaDescription: request.schemaDescription }\n : {}),\n ...(request.system !== undefined ? { system: request.system } : {}),\n prompt: request.prompt,\n })\n const usage = result.usage as SdkUsage | undefined\n return {\n object: result.object,\n usage: {\n ...(usage?.inputTokens !== undefined ? { inputTokens: usage.inputTokens } : {}),\n ...(usage?.outputTokens !== undefined ? { outputTokens: usage.outputTokens } : {}),\n },\n }\n },\n }\n}\n"]}
|