@burdenoff/microfe-botmanor 2026.1008.1 → 2026.1013.1
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/dist/botlit/components/AssistantRuntimeSetupPanel.js +1 -1
- package/dist/botlit/components/AssistantRuntimeSetupPanel.js.map +1 -1
- package/dist/botlit/components/ModelRegistrationPanel.js +174 -0
- package/dist/botlit/components/ModelRegistrationPanel.js.map +1 -0
- package/dist/botlit/hooks/internal/workspaceGateway.js +1 -1
- package/dist/botlit/hooks/useAssistantRuntimeSetup.js +2 -2
- package/dist/botlit/hooks/useAssistantRuntimeSetup.js.map +1 -1
- package/dist/botlit/hooks/useLLMProviders.js +94 -50
- package/dist/botlit/hooks/useLLMProviders.js.map +1 -1
- package/dist/botlit/pages/AgentBuilderPage.js +206 -183
- package/dist/botlit/pages/AgentBuilderPage.js.map +1 -1
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js +162 -145
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js.map +1 -1
- package/dist/botlit/pages/SettingsPage.js +129 -66
- package/dist/botlit/pages/SettingsPage.js.map +1 -1
- package/dist/botlit/utils/embedding-models.js +36 -0
- package/dist/botlit/utils/embedding-models.js.map +1 -0
- package/dist/generated/wspace-operations.js +58 -36
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KnowledgeBaseDetailPage.js","names":[],"sources":["../../../src/botlit/pages/KnowledgeBaseDetailPage.tsx"],"sourcesContent":["/**\n * Knowledge Base Detail Page\n * @module botlit/pages\n *\n * A `KnowledgeBase` on wspace-botlit-svc is an embedding index owned by the\n * platform: `indexStatus`, `embeddingModel`/`embeddingDimensions`, the chunking\n * configuration and the document/chunk/vector counters. It carries no vector\n * store connection, no ingestion-flow references and no visibility column, so\n * this page renders only what the type actually exposes.\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { toast } from 'sonner';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n ArrowLeft,\n Database,\n Settings,\n Save,\n FileText,\n Layers,\n RefreshCw,\n Boxes,\n} from 'lucide-react';\nimport { KnowledgeSourceImportPanel } from '../components/KnowledgeSourceImportPanel';\nimport { PageLayout } from '../components/PageLayout';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { EmptyState } from '../components/EmptyState';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { StatusBadge } from '../components/Badge';\nimport {\n useKnowledgeBase,\n useKnowledgeDocuments,\n useCreateKnowledgeBase,\n useUpdateKnowledgeBase,\n useRebuildKnowledgeBaseIndex,\n type KnowledgeBaseRecord,\n} from '../hooks/useKnowledgeBases';\nimport { formatRelativeTime, MISSING_VALUE } from '../utils/formatters';\n\ninterface KnowledgeBaseDetailPageProps {\n isNew?: boolean;\n}\n\n/**\n * Chunking strategies the service actually implements.\n *\n * `SEMANTIC` was offered here until 2026-07-26 but no ingestion path ever\n * implemented it — and, until the same change, `chunkingStrategy` was not\n * persisted at all, so every option in this list was equally inert. The\n * service now validates the value and rejects anything it cannot honour, so\n * listing SEMANTIC would surface a VALIDATION_FAILED error on save. It stays\n * out until real embedding-based splitting exists in the ingestion pipeline.\n */\nconst CHUNKING_STRATEGIES = ['RECURSIVE', 'FIXED', 'SENTENCE'] as const;\n\n/** Embedding models the platform indexes with, and their vector width. */\nconst EMBEDDING_MODELS: { id: string; label: string; dimensions: number }[] = [\n { id: 'text-embedding-3-small', label: 'OpenAI text-embedding-3-small', dimensions: 1536 },\n { id: 'text-embedding-3-large', label: 'OpenAI text-embedding-3-large', dimensions: 3072 },\n { id: 'text-embedding-ada-002', label: 'OpenAI text-embedding-ada-002', dimensions: 1536 },\n];\n\ninterface KnowledgeBaseForm {\n name: string;\n description: string;\n embeddingModel: string;\n embeddingDimensions: number;\n chunkingStrategy: string;\n chunkSize: number;\n chunkOverlap: number;\n}\n\nconst DEFAULT_FORM: KnowledgeBaseForm = {\n name: '',\n description: '',\n embeddingModel: EMBEDDING_MODELS[0].id,\n embeddingDimensions: EMBEDDING_MODELS[0].dimensions,\n chunkingStrategy: 'RECURSIVE',\n chunkSize: 512,\n chunkOverlap: 50,\n};\n\nfunction toForm(kb: KnowledgeBaseRecord): KnowledgeBaseForm {\n return {\n name: kb.name,\n description: kb.description ?? '',\n embeddingModel: kb.embeddingModel,\n embeddingDimensions: kb.embeddingDimensions,\n chunkingStrategy: kb.chunkingStrategy,\n chunkSize: kb.chunkSize,\n chunkOverlap: kb.chunkOverlap,\n };\n}\n\n/** `Number('')` is 0 and `Number('abc')` is NaN — keep the previous value instead. */\nfunction toNumber(value: string, fallback: number): number {\n const parsed = Number(value);\n return value.trim() !== '' && Number.isFinite(parsed) ? parsed : fallback;\n}\n\nexport const KnowledgeBaseDetailPage: FC<KnowledgeBaseDetailPageProps> = ({ isNew }) => {\n const { kbId } = useParams();\n const { navigateTo } = useBotlit();\n const { data: kb, loading, refetch } = useKnowledgeBase(isNew ? undefined : kbId);\n const { data: documents } = useKnowledgeDocuments(isNew ? undefined : kbId);\n const [createKB, createKBResult] = useCreateKnowledgeBase();\n const [updateKB, updateKBResult] = useUpdateKnowledgeBase();\n const [rebuildIndex, rebuildResult] = useRebuildKnowledgeBaseIndex();\n\n const [isEditing, setIsEditing] = useState(false);\n const [formData, setFormData] = useState<KnowledgeBaseForm>(DEFAULT_FORM);\n\n useEffect(() => {\n if (kb && !isNew) {\n setFormData(toForm(kb));\n }\n }, [kb, isNew]);\n\n const handleCreate = async () => {\n if (!formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n try {\n const newKB = await createKB({\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n embeddingModel: formData.embeddingModel,\n embeddingDimensions: formData.embeddingDimensions,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n });\n toast.success(`Knowledge base \"${newKB.name}\" created`);\n navigateTo(`knowledge/${newKB.id}`);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to create knowledge base');\n }\n };\n\n const handleUpdate = async () => {\n if (!kbId || !formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n try {\n // `UpdateKnowledgeBaseInput` does not accept the embedding model or its\n // dimensions — changing those would invalidate every stored vector.\n await updateKB({\n kbId,\n input: {\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n },\n });\n await refetch();\n toast.success('Knowledge base updated');\n setIsEditing(false);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update knowledge base');\n }\n };\n\n const handleRebuild = async () => {\n if (!kbId) return;\n try {\n await rebuildIndex(kbId);\n await refetch();\n toast.success('Re-indexing started');\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to rebuild the index');\n }\n };\n\n const renderConfigFields = (includeEmbedding: boolean) => (\n <>\n <FormField label=\"Name\" required>\n <Input\n placeholder=\"e.g., Product Documentation\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What kind of knowledge will this store?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={3}\n />\n </FormField>\n\n {includeEmbedding && (\n <FormField label=\"Embedding Model\" required>\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.embeddingModel}\n onChange={(e) => {\n const model = EMBEDDING_MODELS.find((entry) => entry.id === e.target.value);\n setFormData({\n ...formData,\n embeddingModel: e.target.value,\n embeddingDimensions: model?.dimensions ?? formData.embeddingDimensions,\n });\n }}\n >\n {EMBEDDING_MODELS.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label} ({model.dimensions}d)\n </option>\n ))}\n </select>\n </FormField>\n )}\n\n <div className=\"grid grid-cols-3 gap-4\">\n <FormField label=\"Chunking Strategy\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.chunkingStrategy}\n onChange={(e) => setFormData({ ...formData, chunkingStrategy: e.target.value })}\n >\n {CHUNKING_STRATEGIES.map((strategy) => (\n <option key={strategy} value={strategy}>\n {strategy}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Chunk Size\">\n <Input\n type=\"number\"\n value={formData.chunkSize}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkSize: toNumber(e.target.value, DEFAULT_FORM.chunkSize),\n })\n }\n />\n </FormField>\n\n <FormField label=\"Chunk Overlap\">\n <Input\n type=\"number\"\n value={formData.chunkOverlap}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkOverlap: toNumber(e.target.value, DEFAULT_FORM.chunkOverlap),\n })\n }\n />\n </FormField>\n </div>\n </>\n );\n\n if (isNew) {\n return (\n <PageLayout\n title=\"Create Knowledge Base\"\n description=\"Set up an embedding index your agents can retrieve from\"\n actions={\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigateTo('knowledge')}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleCreate}\n isLoading={createKBResult.loading}\n disabled={!formData.name.trim()}\n >\n Create\n </Button>\n </div>\n }\n >\n <div className=\"max-w-3xl space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Index Configuration</CardTitle>\n <CardDescription>\n The embedding model and its vector width are fixed once documents are indexed.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">{renderConfigFields(true)}</CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n }\n\n if (loading) {\n return (\n <PageLayout title=\"Loading...\">\n <div className=\"h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </PageLayout>\n );\n }\n\n if (!kb) {\n return (\n <PageLayout title=\"Knowledge Base Not Found\">\n <EmptyState\n illustration=\"empty-files\"\n title=\"Knowledge Base Not Found\"\n description=\"This knowledge base doesn't exist.\"\n action={<Button onClick={() => navigateTo('knowledge')}>Back to Knowledge Bases</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('knowledge')}>\n <ArrowLeft size={20} />\n </Button>\n <div>\n <div className=\"flex items-center gap-2\">\n <span>{kb.name}</span>\n <StatusBadge status={kb.indexStatus} />\n </div>\n {kb.description && (\n <p className=\"text-sm font-normal text-text-secondary\">{kb.description}</p>\n )}\n </div>\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={<RefreshCw size={16} />}\n onClick={handleRebuild}\n isLoading={rebuildResult.loading}\n >\n Rebuild Index\n </Button>\n {isEditing ? (\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setFormData(toForm(kb));\n setIsEditing(false);\n }}\n >\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleUpdate}\n isLoading={updateKBResult.loading}\n >\n Save\n </Button>\n </>\n ) : (\n <Button leftIcon={<Settings size={16} />} onClick={() => setIsEditing(true)}>\n Configure\n </Button>\n )}\n </div>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Database size={18} />}>\n This knowledge base is one searchable library your agents can draw on. Review how its\n documents are chunked and indexed, rebuild the index when content changes, and confirm it\n is healthy — so agents retrieve accurate, up-to-date passages and can cite them in their\n answers.\n </PagePurpose>\n\n {/* Index counters */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" glow className=\"p-6 text-center\">\n <div className=\"p-3 rounded-full bg-action-primary-bg/10 w-fit mx-auto mb-2\">\n <Database className=\"h-5 w-5 text-primary\" />\n </div>\n <p className=\"text-lg font-bold text-text-primary capitalize\">\n {kb.indexStatus.toLowerCase()}\n </p>\n <p className=\"text-sm text-text-secondary\">Index Status</p>\n </GlassCard>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalDocuments}</p>\n <p className=\"text-sm text-text-secondary\">Documents</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalChunks}</p>\n <p className=\"text-sm text-text-secondary\">Chunks</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.vectorCount}</p>\n <p className=\"text-sm text-text-secondary\">Vectors</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Index configuration */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Layers size={18} />\n Index Configuration\n </CardTitle>\n <CardDescription>\n How documents are embedded and split before they are indexed\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {isEditing ? (\n renderConfigFields(false)\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6\">\n <div>\n <p className=\"text-sm text-text-secondary\">Embedding Model</p>\n <p className=\"font-medium font-mono text-sm\">{kb.embeddingModel}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Dimensions</p>\n <p className=\"font-medium\">{kb.embeddingDimensions}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunking Strategy</p>\n <p className=\"font-medium\">{kb.chunkingStrategy}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Size</p>\n <p className=\"font-medium\">{kb.chunkSize}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Overlap</p>\n <p className=\"font-medium\">{kb.chunkOverlap}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : 'Never'}\n </p>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {!isNew && kbId && <KnowledgeSourceImportPanel knowledgeBaseId={kbId} />}\n {/* Documents */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <FileText size={18} />\n Documents ({documents.length})\n </CardTitle>\n <CardDescription>Source documents indexed into this knowledge base</CardDescription>\n </CardHeader>\n <CardContent className=\"p-0\">\n {documents.length > 0 ? (\n <div className=\"divide-y divide-border-subtle\">\n {documents.map((document) => (\n <div key={document.id} className=\"flex items-center justify-between gap-4 p-4\">\n <div className=\"min-w-0\">\n <p className=\"font-medium truncate\">{document.title}</p>\n <p className=\"text-sm text-text-secondary\">\n {document.chunkCount} chunks · added {formatRelativeTime(document.addedAt)}\n </p>\n {document.processingError && (\n <p className=\"mt-1 text-sm text-status-error-text\">\n {document.processingError}\n </p>\n )}\n </div>\n <StatusBadge status={document.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-8 text-center text-text-secondary\">\n <Boxes className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No documents indexed yet</p>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Settings size={18} />\n Metadata\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Created</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.createdAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Updated</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.updatedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : MISSING_VALUE}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KnowledgeBaseDetailPage.js","names":[],"sources":["../../../src/botlit/pages/KnowledgeBaseDetailPage.tsx"],"sourcesContent":["/**\n * Knowledge Base Detail Page\n * @module botlit/pages\n *\n * A `KnowledgeBase` on wspace-botlit-svc is an embedding index owned by the\n * platform: `indexStatus`, `embeddingModel`/`embeddingDimensions`, the chunking\n * configuration and the document/chunk/vector counters. It carries no vector\n * store connection, no ingestion-flow references and no visibility column, so\n * this page renders only what the type actually exposes.\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { toast } from 'sonner';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n ArrowLeft,\n Database,\n Settings,\n Save,\n FileText,\n Layers,\n RefreshCw,\n Boxes,\n} from 'lucide-react';\nimport { useLLMModels, useLLMProviders } from '../hooks/useLLMProviders';\nimport { configuredEmbeddingModels } from '../utils/embedding-models';\nimport { KnowledgeSourceImportPanel } from '../components/KnowledgeSourceImportPanel';\nimport { PageLayout } from '../components/PageLayout';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { EmptyState } from '../components/EmptyState';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { StatusBadge } from '../components/Badge';\nimport {\n useKnowledgeBase,\n useKnowledgeDocuments,\n useCreateKnowledgeBase,\n useUpdateKnowledgeBase,\n useRebuildKnowledgeBaseIndex,\n type KnowledgeBaseRecord,\n} from '../hooks/useKnowledgeBases';\nimport { formatRelativeTime, MISSING_VALUE } from '../utils/formatters';\n\ninterface KnowledgeBaseDetailPageProps {\n isNew?: boolean;\n}\n\n/**\n * Chunking strategies the service actually implements.\n *\n * `SEMANTIC` was offered here until 2026-07-26 but no ingestion path ever\n * implemented it — and, until the same change, `chunkingStrategy` was not\n * persisted at all, so every option in this list was equally inert. The\n * service now validates the value and rejects anything it cannot honour, so\n * listing SEMANTIC would surface a VALIDATION_FAILED error on save. It stays\n * out until real embedding-based splitting exists in the ingestion pipeline.\n */\nconst CHUNKING_STRATEGIES = ['RECURSIVE', 'FIXED', 'SENTENCE'] as const;\n\ninterface KnowledgeBaseForm {\n name: string;\n description: string;\n embeddingModel: string;\n embeddingDimensions: number;\n chunkingStrategy: string;\n chunkSize: number;\n chunkOverlap: number;\n}\n\nconst DEFAULT_FORM: KnowledgeBaseForm = {\n name: '',\n description: '',\n embeddingModel: '',\n embeddingDimensions: 0,\n chunkingStrategy: 'RECURSIVE',\n chunkSize: 512,\n chunkOverlap: 50,\n};\n\nfunction toForm(kb: KnowledgeBaseRecord): KnowledgeBaseForm {\n return {\n name: kb.name,\n description: kb.description ?? '',\n embeddingModel: kb.embeddingModel,\n embeddingDimensions: kb.embeddingDimensions,\n chunkingStrategy: kb.chunkingStrategy,\n chunkSize: kb.chunkSize,\n chunkOverlap: kb.chunkOverlap,\n };\n}\n\n/** `Number('')` is 0 and `Number('abc')` is NaN — keep the previous value instead. */\nfunction toNumber(value: string, fallback: number): number {\n const parsed = Number(value);\n return value.trim() !== '' && Number.isFinite(parsed) ? parsed : fallback;\n}\n\nexport const KnowledgeBaseDetailPage: FC<KnowledgeBaseDetailPageProps> = ({ isNew }) => {\n const { kbId } = useParams();\n const {\n data: models,\n loading: modelsLoading,\n error: modelsError,\n refetch: refetchModels,\n } = useLLMModels();\n const {\n data: providers,\n loading: providersLoading,\n error: providersError,\n refetch: refetchProviders,\n } = useLLMProviders();\n const catalogLoading = modelsLoading || providersLoading;\n const catalogError = modelsError || providersError;\n const embeddingModels = useMemo(\n () => configuredEmbeddingModels(models ?? [], providers ?? []),\n [models, providers]\n );\n const { navigateTo } = useBotlit();\n const { data: kb, loading, refetch } = useKnowledgeBase(isNew ? undefined : kbId);\n const { data: documents } = useKnowledgeDocuments(isNew ? undefined : kbId);\n const [createKB, createKBResult] = useCreateKnowledgeBase();\n const [updateKB, updateKBResult] = useUpdateKnowledgeBase();\n const [rebuildIndex, rebuildResult] = useRebuildKnowledgeBaseIndex();\n\n const [isEditing, setIsEditing] = useState(false);\n const [formData, setFormData] = useState<KnowledgeBaseForm>(DEFAULT_FORM);\n\n useEffect(() => {\n if (kb && !isNew) {\n setFormData(toForm(kb));\n }\n }, [kb, isNew]);\n\n const handleCreate = async () => {\n if (catalogLoading || catalogError) return;\n if (!formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n if (!embeddingModels.some((model) => model.id === formData.embeddingModel)) {\n toast.error('Choose a configured embedding model. Register one in Settings first.');\n return;\n }\n try {\n const newKB = await createKB({\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n embeddingModel: formData.embeddingModel,\n embeddingDimensions: formData.embeddingDimensions,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n });\n toast.success(`Knowledge base \"${newKB.name}\" created`);\n navigateTo(`knowledge/${newKB.id}`);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to create knowledge base');\n }\n };\n\n const handleUpdate = async () => {\n if (!kbId || !formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n try {\n // `UpdateKnowledgeBaseInput` does not accept the embedding model or its\n // dimensions — changing those would invalidate every stored vector.\n await updateKB({\n kbId,\n input: {\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n },\n });\n await refetch();\n toast.success('Knowledge base updated');\n setIsEditing(false);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update knowledge base');\n }\n };\n\n const handleRebuild = async () => {\n if (!kbId) return;\n try {\n await rebuildIndex(kbId);\n await refetch();\n toast.success('Re-indexing started');\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to rebuild the index');\n }\n };\n\n const renderConfigFields = (includeEmbedding: boolean) => (\n <>\n <FormField label=\"Name\" required>\n <Input\n placeholder=\"e.g., Product Documentation\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What kind of knowledge will this store?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={3}\n />\n </FormField>\n\n {includeEmbedding && (\n <FormField label=\"Embedding Model\" required>\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n aria-label=\"Embedding Model\"\n disabled={catalogLoading || Boolean(catalogError)}\n value={formData.embeddingModel}\n onChange={(e) => {\n const model = embeddingModels.find((entry) => entry.id === e.target.value);\n setFormData({\n ...formData,\n embeddingModel: e.target.value,\n embeddingDimensions: model?.dimensions ?? formData.embeddingDimensions,\n });\n }}\n >\n <option value=\"\">Choose a configured embedding model</option>\n {embeddingModels.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label} ({model.dimensions}d)\n </option>\n ))}\n </select>\n {catalogLoading && <p role=\"status\">Loading configured embedding models…</p>}\n {catalogError && (\n <div role=\"alert\">\n <p>Could not load configured models. Try loading the catalog again.</p>\n <Button\n variant=\"secondary\"\n onClick={() => {\n void refetchModels();\n void refetchProviders();\n }}\n >\n Retry model catalog\n </Button>\n </div>\n )}\n {!catalogLoading && !catalogError && embeddingModels.length === 0 && (\n <p className=\"text-sm text-text-secondary\">\n Register an embedding model with an active provider in Settings first.\n </p>\n )}\n </FormField>\n )}\n\n <div className=\"grid grid-cols-3 gap-4\">\n <FormField label=\"Chunking Strategy\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.chunkingStrategy}\n onChange={(e) => setFormData({ ...formData, chunkingStrategy: e.target.value })}\n >\n {CHUNKING_STRATEGIES.map((strategy) => (\n <option key={strategy} value={strategy}>\n {strategy}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Chunk Size\">\n <Input\n type=\"number\"\n value={formData.chunkSize}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkSize: toNumber(e.target.value, DEFAULT_FORM.chunkSize),\n })\n }\n />\n </FormField>\n\n <FormField label=\"Chunk Overlap\">\n <Input\n type=\"number\"\n value={formData.chunkOverlap}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkOverlap: toNumber(e.target.value, DEFAULT_FORM.chunkOverlap),\n })\n }\n />\n </FormField>\n </div>\n </>\n );\n\n if (isNew) {\n return (\n <PageLayout\n title=\"Create Knowledge Base\"\n description=\"Set up an embedding index your agents can retrieve from\"\n actions={\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigateTo('knowledge')}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleCreate}\n isLoading={createKBResult.loading}\n disabled={catalogLoading || Boolean(catalogError) || !formData.name.trim()}\n >\n Create\n </Button>\n </div>\n }\n >\n <div className=\"max-w-3xl space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Index Configuration</CardTitle>\n <CardDescription>\n The embedding model and its vector width are fixed once documents are indexed.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">{renderConfigFields(true)}</CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n }\n\n if (loading) {\n return (\n <PageLayout title=\"Loading...\">\n <div className=\"h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </PageLayout>\n );\n }\n\n if (!kb) {\n return (\n <PageLayout title=\"Knowledge Base Not Found\">\n <EmptyState\n illustration=\"empty-files\"\n title=\"Knowledge Base Not Found\"\n description=\"This knowledge base doesn't exist.\"\n action={<Button onClick={() => navigateTo('knowledge')}>Back to Knowledge Bases</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('knowledge')}>\n <ArrowLeft size={20} />\n </Button>\n <div>\n <div className=\"flex items-center gap-2\">\n <span>{kb.name}</span>\n <StatusBadge status={kb.indexStatus} />\n </div>\n {kb.description && (\n <p className=\"text-sm font-normal text-text-secondary\">{kb.description}</p>\n )}\n </div>\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={<RefreshCw size={16} />}\n onClick={handleRebuild}\n isLoading={rebuildResult.loading}\n >\n Rebuild Index\n </Button>\n {isEditing ? (\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setFormData(toForm(kb));\n setIsEditing(false);\n }}\n >\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleUpdate}\n isLoading={updateKBResult.loading}\n >\n Save\n </Button>\n </>\n ) : (\n <Button leftIcon={<Settings size={16} />} onClick={() => setIsEditing(true)}>\n Configure\n </Button>\n )}\n </div>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Database size={18} />}>\n This knowledge base is one searchable library your agents can draw on. Review how its\n documents are chunked and indexed, rebuild the index when content changes, and confirm it\n is healthy — so agents retrieve accurate, up-to-date passages and can cite them in their\n answers.\n </PagePurpose>\n\n {/* Index counters */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" glow className=\"p-6 text-center\">\n <div className=\"p-3 rounded-full bg-action-primary-bg/10 w-fit mx-auto mb-2\">\n <Database className=\"h-5 w-5 text-primary\" />\n </div>\n <p className=\"text-lg font-bold text-text-primary capitalize\">\n {kb.indexStatus.toLowerCase()}\n </p>\n <p className=\"text-sm text-text-secondary\">Index Status</p>\n </GlassCard>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalDocuments}</p>\n <p className=\"text-sm text-text-secondary\">Documents</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalChunks}</p>\n <p className=\"text-sm text-text-secondary\">Chunks</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.vectorCount}</p>\n <p className=\"text-sm text-text-secondary\">Vectors</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Index configuration */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Layers size={18} />\n Index Configuration\n </CardTitle>\n <CardDescription>\n How documents are embedded and split before they are indexed\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {isEditing ? (\n renderConfigFields(false)\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6\">\n <div>\n <p className=\"text-sm text-text-secondary\">Embedding Model</p>\n <p className=\"font-medium font-mono text-sm\">{kb.embeddingModel}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Dimensions</p>\n <p className=\"font-medium\">{kb.embeddingDimensions}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunking Strategy</p>\n <p className=\"font-medium\">{kb.chunkingStrategy}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Size</p>\n <p className=\"font-medium\">{kb.chunkSize}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Overlap</p>\n <p className=\"font-medium\">{kb.chunkOverlap}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : 'Never'}\n </p>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {!isNew && kbId && <KnowledgeSourceImportPanel knowledgeBaseId={kbId} />}\n {/* Documents */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <FileText size={18} />\n Documents ({documents.length})\n </CardTitle>\n <CardDescription>Source documents indexed into this knowledge base</CardDescription>\n </CardHeader>\n <CardContent className=\"p-0\">\n {documents.length > 0 ? (\n <div className=\"divide-y divide-border-subtle\">\n {documents.map((document) => (\n <div key={document.id} className=\"flex items-center justify-between gap-4 p-4\">\n <div className=\"min-w-0\">\n <p className=\"font-medium truncate\">{document.title}</p>\n <p className=\"text-sm text-text-secondary\">\n {document.chunkCount} chunks · added {formatRelativeTime(document.addedAt)}\n </p>\n {document.processingError && (\n <p className=\"mt-1 text-sm text-status-error-text\">\n {document.processingError}\n </p>\n )}\n </div>\n <StatusBadge status={document.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-8 text-center text-text-secondary\">\n <Boxes className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No documents indexed yet</p>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Settings size={18} />\n Metadata\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Created</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.createdAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Updated</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.updatedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : MISSING_VALUE}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -7,8 +7,9 @@ import { Button as d } from "../components/Button.js";
|
|
|
7
7
|
import { Badge as f } from "../components/Badge.js";
|
|
8
8
|
import { FormField as p, Input as m } from "../components/Input.js";
|
|
9
9
|
import { toUnion as h } from "../utils/unions.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { ModelRegistrationPanel as g } from "../components/ModelRegistrationPanel.js";
|
|
11
|
+
import { useState as _ } from "react";
|
|
12
|
+
import { PagePurpose as ee } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
13
14
|
import { Brain as b, ExternalLink as x, Key as S, Link2 as C, Save as w } from "lucide-react";
|
|
14
15
|
import { toast as T } from "sonner";
|
|
@@ -25,14 +26,28 @@ function D(e) {
|
|
|
25
26
|
return h(e.toUpperCase(), E, "CUSTOM");
|
|
26
27
|
}
|
|
27
28
|
var O = () => {
|
|
28
|
-
let h = e(), { data: E, refetch: O } = r(), { configureProvider:
|
|
29
|
+
let h = e(), { data: E, refetch: O, loading: k, error: A } = r(), { configureProvider: j, isConfiguring: M } = t(), { data: N, refetch: P, loading: F, error: te } = n(), I = k || F, L = A || te, [R, z] = _(""), [B, V] = _(!0), [H, U] = _("openai"), [W, G] = _(null), [K, q] = _("apikey"), [J, Y] = _(""), [X, Z] = _("ap-south-1"), Q = H === "aws_bedrock", $ = Q && K === "iam", ne = /^[a-z]{2}(?:-[a-z]+)+-\d+$/.test(X.trim());
|
|
29
30
|
return /* @__PURE__ */ v(a, {
|
|
30
31
|
title: h("botlit.settings.title", "Settings"),
|
|
31
32
|
description: h("botlit.settings.description", "Configure your BotManor workspace settings"),
|
|
32
33
|
children: /* @__PURE__ */ y("div", {
|
|
33
34
|
className: "space-y-6",
|
|
34
35
|
children: [
|
|
35
|
-
/* @__PURE__ */ v(
|
|
36
|
+
I && /* @__PURE__ */ v("p", {
|
|
37
|
+
role: "status",
|
|
38
|
+
children: "Loading providers and models…"
|
|
39
|
+
}),
|
|
40
|
+
L && /* @__PURE__ */ y("div", {
|
|
41
|
+
role: "alert",
|
|
42
|
+
children: [/* @__PURE__ */ v("p", { children: "Could not load providers and models. Try loading the catalog again." }), /* @__PURE__ */ v(d, {
|
|
43
|
+
variant: "secondary",
|
|
44
|
+
onClick: () => {
|
|
45
|
+
O(), P();
|
|
46
|
+
},
|
|
47
|
+
children: "Retry model catalog"
|
|
48
|
+
})]
|
|
49
|
+
}),
|
|
50
|
+
/* @__PURE__ */ v(ee, {
|
|
36
51
|
icon: /* @__PURE__ */ v(S, { size: 18 }),
|
|
37
52
|
children: h("botlit.settings.purpose", "Workspace-wide settings for LLM Providers — connect at least one model provider (OpenAI, Anthropic, Google and others) so your agents have a brain to think with.")
|
|
38
53
|
}),
|
|
@@ -81,7 +96,7 @@ var O = () => {
|
|
|
81
96
|
variant: "outline",
|
|
82
97
|
size: "sm",
|
|
83
98
|
onClick: () => {
|
|
84
|
-
|
|
99
|
+
G(e.id), V(e.isActive), U(e.type.toLowerCase()), z(""), Y(e.connectionId || ""), q(e.type === "AWS_BEDROCK" && e.metadata?.awsAuthentication === "iam" ? "iam" : e.connectionId ? "integration" : "apikey"), Z(typeof e.metadata?.region == "string" ? e.metadata.region : "ap-south-1"), document.getElementById("provider-form")?.scrollIntoView({ behavior: "smooth" });
|
|
85
100
|
},
|
|
86
101
|
children: h("botlit.settings.actions.configure", "Configure")
|
|
87
102
|
})
|
|
@@ -92,7 +107,7 @@ var O = () => {
|
|
|
92
107
|
}),
|
|
93
108
|
/* @__PURE__ */ y(o, { children: [/* @__PURE__ */ y(l, { children: [/* @__PURE__ */ v(u, { children: h("botlit.settings.models.title", "LLM Models") }), /* @__PURE__ */ v(c, { children: h("botlit.settings.models.description", "Registered models across your providers") })] }), /* @__PURE__ */ v(s, {
|
|
94
109
|
className: "space-y-4",
|
|
95
|
-
children:
|
|
110
|
+
children: N && N.length > 0 ? N.map((e) => {
|
|
96
111
|
let t = E?.find((t) => t.id === e.providerId)?.name;
|
|
97
112
|
return /* @__PURE__ */ y("div", {
|
|
98
113
|
className: "flex items-center justify-between p-4 border border-border-subtle rounded-lg",
|
|
@@ -125,22 +140,29 @@ var O = () => {
|
|
|
125
140
|
children: e.isActive ? h("botlit.settings.providers.active", "Active") : h("botlit.settings.providers.inactive", "Inactive")
|
|
126
141
|
})]
|
|
127
142
|
}, e.id);
|
|
128
|
-
}) : /* @__PURE__ */ v("p", {
|
|
143
|
+
}) : !I && !L ? /* @__PURE__ */ v("p", {
|
|
129
144
|
className: "text-sm text-text-secondary text-center py-4",
|
|
130
145
|
children: h("botlit.settings.models.empty", "No models registered yet.")
|
|
131
|
-
})
|
|
146
|
+
}) : null
|
|
132
147
|
})] }),
|
|
148
|
+
!I && !L && /* @__PURE__ */ v(g, {
|
|
149
|
+
providers: E ?? [],
|
|
150
|
+
onCreated: () => void P()
|
|
151
|
+
}),
|
|
133
152
|
/* @__PURE__ */ y(o, {
|
|
134
153
|
id: "provider-form",
|
|
135
|
-
children: [/* @__PURE__ */ y(l, { children: [/* @__PURE__ */ v(u, { children:
|
|
154
|
+
children: [/* @__PURE__ */ y(l, { children: [/* @__PURE__ */ v(u, { children: W ? h("botlit.settings.form.updateTitle", "Update Provider") : h("botlit.settings.form.addTitle", "Add Provider") }), /* @__PURE__ */ v(c, { children: W ? h("botlit.settings.form.updateDescription", "Update credentials for this provider") : h("botlit.settings.form.addDescription", "Configure a new LLM provider") })] }), /* @__PURE__ */ y(s, {
|
|
136
155
|
className: "space-y-4",
|
|
137
156
|
children: [
|
|
138
157
|
/* @__PURE__ */ v(p, {
|
|
139
158
|
label: h("botlit.settings.form.providerLabel", "Provider"),
|
|
140
159
|
children: /* @__PURE__ */ y("select", {
|
|
141
160
|
className: "flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
|
|
142
|
-
value:
|
|
143
|
-
|
|
161
|
+
value: H,
|
|
162
|
+
disabled: !!W,
|
|
163
|
+
onChange: (e) => {
|
|
164
|
+
U(e.target.value), q(e.target.value === "aws_bedrock" ? "iam" : "apikey"), z(""), Y("");
|
|
165
|
+
},
|
|
144
166
|
children: [
|
|
145
167
|
/* @__PURE__ */ v("option", {
|
|
146
168
|
value: "openai",
|
|
@@ -169,46 +191,80 @@ var O = () => {
|
|
|
169
191
|
]
|
|
170
192
|
})
|
|
171
193
|
}),
|
|
194
|
+
W && /* @__PURE__ */ y("label", {
|
|
195
|
+
className: "flex items-center gap-2 text-sm",
|
|
196
|
+
children: [/* @__PURE__ */ v("input", {
|
|
197
|
+
type: "checkbox",
|
|
198
|
+
checked: B,
|
|
199
|
+
onChange: (e) => V(e.target.checked)
|
|
200
|
+
}), "Provider enabled"]
|
|
201
|
+
}),
|
|
202
|
+
Q && /* @__PURE__ */ y(p, {
|
|
203
|
+
label: "AWS region",
|
|
204
|
+
children: [/* @__PURE__ */ v(m, {
|
|
205
|
+
"aria-label": "AWS region",
|
|
206
|
+
value: X,
|
|
207
|
+
onChange: (e) => Z(e.target.value)
|
|
208
|
+
}), /* @__PURE__ */ v("p", {
|
|
209
|
+
className: "text-sm text-text-secondary",
|
|
210
|
+
children: "Managed AWS IAM requires model access enabled for this workspace by your platform administrator."
|
|
211
|
+
})]
|
|
212
|
+
}),
|
|
172
213
|
/* @__PURE__ */ v(p, {
|
|
173
214
|
label: h("botlit.settings.form.credentialMethod", "Credential Method"),
|
|
174
215
|
children: /* @__PURE__ */ y("div", {
|
|
175
|
-
className: "flex gap-3",
|
|
176
|
-
children: [
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}),
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
216
|
+
className: "flex flex-wrap gap-3",
|
|
217
|
+
children: [
|
|
218
|
+
Q && /* @__PURE__ */ y("label", {
|
|
219
|
+
className: i("flex items-center gap-2 px-4 py-3 border rounded-lg cursor-pointer flex-1", $ ? "border-primary bg-action-primary-bg/5" : "border-border-subtle hover:border-primary/50"),
|
|
220
|
+
children: [/* @__PURE__ */ v("input", {
|
|
221
|
+
type: "radio",
|
|
222
|
+
name: "credentialMethod",
|
|
223
|
+
value: "iam",
|
|
224
|
+
checked: $,
|
|
225
|
+
onChange: () => {
|
|
226
|
+
q("iam"), z(""), Y("");
|
|
227
|
+
}
|
|
228
|
+
}), "Managed AWS IAM"]
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ y("label", {
|
|
231
|
+
className: i("flex items-center gap-2 px-4 py-3 border rounded-lg cursor-pointer transition-colors flex-1", K === "integration" ? "border-primary bg-action-primary-bg/5" : "border-border-subtle hover:border-primary/50"),
|
|
232
|
+
children: [/* @__PURE__ */ v("input", {
|
|
233
|
+
type: "radio",
|
|
234
|
+
name: "credentialMethod",
|
|
235
|
+
value: "integration",
|
|
236
|
+
checked: K === "integration",
|
|
237
|
+
onChange: () => q("integration"),
|
|
238
|
+
className: "h-4 w-4"
|
|
239
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
240
|
+
className: "font-medium text-sm",
|
|
241
|
+
children: h("botlit.settings.form.integrationOption", "Connect via Integrations")
|
|
242
|
+
}), /* @__PURE__ */ v("p", {
|
|
243
|
+
className: "text-xs text-text-secondary",
|
|
244
|
+
children: h("botlit.settings.form.integrationHint", "Managed credentials (recommended)")
|
|
245
|
+
})] })]
|
|
246
|
+
}),
|
|
247
|
+
/* @__PURE__ */ y("label", {
|
|
248
|
+
className: i("flex items-center gap-2 px-4 py-3 border rounded-lg cursor-pointer transition-colors flex-1", K === "apikey" ? "border-primary bg-action-primary-bg/5" : "border-border-subtle hover:border-primary/50"),
|
|
249
|
+
children: [/* @__PURE__ */ v("input", {
|
|
250
|
+
type: "radio",
|
|
251
|
+
name: "credentialMethod",
|
|
252
|
+
value: "apikey",
|
|
253
|
+
checked: K === "apikey",
|
|
254
|
+
onChange: () => q("apikey"),
|
|
255
|
+
className: "h-4 w-4"
|
|
256
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
257
|
+
className: "font-medium text-sm",
|
|
258
|
+
children: h("botlit.settings.form.apiKeyOption", "Enter API Key")
|
|
259
|
+
}), /* @__PURE__ */ v("p", {
|
|
260
|
+
className: "text-xs text-text-secondary",
|
|
261
|
+
children: h("botlit.settings.form.apiKeyHint", "Paste key directly (legacy)")
|
|
262
|
+
})] })]
|
|
263
|
+
})
|
|
264
|
+
]
|
|
209
265
|
})
|
|
210
266
|
}),
|
|
211
|
-
|
|
267
|
+
K === "integration" && /* @__PURE__ */ y("div", {
|
|
212
268
|
className: "space-y-3",
|
|
213
269
|
children: [/* @__PURE__ */ v("div", {
|
|
214
270
|
className: "p-4 bg-bg-sunken/50 border border-border-subtle rounded-lg",
|
|
@@ -246,52 +302,59 @@ var O = () => {
|
|
|
246
302
|
label: h("botlit.settings.form.connectionIdPlaceholder", "Connection ID"),
|
|
247
303
|
children: /* @__PURE__ */ v(m, {
|
|
248
304
|
placeholder: h("botlit.settings.form.connectionIdPlaceholder", "Paste connection ID from Integrations..."),
|
|
249
|
-
value:
|
|
250
|
-
onChange: (e) =>
|
|
305
|
+
value: J,
|
|
306
|
+
onChange: (e) => Y(e.target.value)
|
|
251
307
|
})
|
|
252
308
|
})]
|
|
253
309
|
}),
|
|
254
|
-
|
|
310
|
+
K === "apikey" && /* @__PURE__ */ v(p, {
|
|
255
311
|
label: h("botlit.settings.form.apiKeyLabel", "API Key"),
|
|
256
312
|
children: /* @__PURE__ */ v(m, {
|
|
257
313
|
type: "password",
|
|
258
314
|
placeholder: h("botlit.settings.form.apiKeyPlaceholder", "sk-..."),
|
|
259
|
-
value:
|
|
260
|
-
onChange: (e) =>
|
|
315
|
+
value: R,
|
|
316
|
+
onChange: (e) => z(e.target.value)
|
|
261
317
|
})
|
|
262
318
|
}),
|
|
263
319
|
/* @__PURE__ */ y("div", {
|
|
264
320
|
className: "flex gap-2",
|
|
265
321
|
children: [/* @__PURE__ */ v(d, {
|
|
266
322
|
leftIcon: /* @__PURE__ */ v(w, { size: 16 }),
|
|
267
|
-
isLoading:
|
|
268
|
-
disabled:
|
|
323
|
+
isLoading: M,
|
|
324
|
+
disabled: Q && !ne || ($ ? !1 : K === "apikey" ? !R.trim() : !J.trim()),
|
|
269
325
|
onClick: async () => {
|
|
270
326
|
try {
|
|
271
|
-
let e =
|
|
272
|
-
|
|
327
|
+
let e = H.charAt(0).toUpperCase() + H.slice(1), t = K === "apikey" && R ? { apiKey: R } : void 0, n = K === "integration" ? J : null, r = E?.find((e) => e.id === W)?.metadata ?? {}, i = Q ? {
|
|
328
|
+
...r,
|
|
329
|
+
region: X.trim(),
|
|
330
|
+
awsAuthentication: $ ? "iam" : "credentials"
|
|
331
|
+
} : r;
|
|
332
|
+
W ? await j({
|
|
273
333
|
name: e,
|
|
274
|
-
type: D(
|
|
334
|
+
type: D(H),
|
|
275
335
|
connectionId: n,
|
|
276
|
-
credentials: t
|
|
277
|
-
|
|
336
|
+
credentials: t,
|
|
337
|
+
metadata: i,
|
|
338
|
+
isActive: B
|
|
339
|
+
}, W) : await j({
|
|
278
340
|
name: e,
|
|
279
|
-
type: D(
|
|
341
|
+
type: D(H),
|
|
280
342
|
connectionId: n,
|
|
281
|
-
credentials: t
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
343
|
+
credentials: t,
|
|
344
|
+
metadata: i
|
|
345
|
+
}), z(""), Y(""), G(null), await O();
|
|
346
|
+
let a = H.charAt(0).toUpperCase() + H.slice(1);
|
|
347
|
+
T.success(W ? h("botlit.settings.form.updateSuccess", "{{provider}} provider updated successfully", { provider: a }) : h("botlit.settings.form.addSuccess", "{{provider}} provider added successfully", { provider: a }));
|
|
285
348
|
} catch (e) {
|
|
286
349
|
let t = e instanceof Error ? e.message : h("botlit.settings.form.saveError", "Failed to save provider");
|
|
287
350
|
T.error(t);
|
|
288
351
|
}
|
|
289
352
|
},
|
|
290
|
-
children:
|
|
291
|
-
}),
|
|
353
|
+
children: W ? h("botlit.settings.form.updateProvider", "Update Provider") : h("botlit.settings.form.saveProvider", "Save Provider")
|
|
354
|
+
}), W && /* @__PURE__ */ v(d, {
|
|
292
355
|
variant: "outline",
|
|
293
356
|
onClick: () => {
|
|
294
|
-
|
|
357
|
+
G(null), z(""), Y(""), q("apikey"), U("openai");
|
|
295
358
|
},
|
|
296
359
|
children: h("botlit.settings.form.cancel", "Cancel")
|
|
297
360
|
})]
|