@sqlrooms/ai-settings 0.26.0-rc.0

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.
Files changed (35) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +3 -0
  3. package/dist/AiSettingsSlice.d.ts +29 -0
  4. package/dist/AiSettingsSlice.d.ts.map +1 -0
  5. package/dist/AiSettingsSlice.js +115 -0
  6. package/dist/AiSettingsSlice.js.map +1 -0
  7. package/dist/components/AiModelParameters.d.ts +3 -0
  8. package/dist/components/AiModelParameters.d.ts.map +1 -0
  9. package/dist/components/AiModelParameters.js +79 -0
  10. package/dist/components/AiModelParameters.js.map +1 -0
  11. package/dist/components/AiModelUsage.d.ts +18 -0
  12. package/dist/components/AiModelUsage.d.ts.map +1 -0
  13. package/dist/components/AiModelUsage.js +60 -0
  14. package/dist/components/AiModelUsage.js.map +1 -0
  15. package/dist/components/AiModelsSettings.d.ts +10 -0
  16. package/dist/components/AiModelsSettings.d.ts.map +1 -0
  17. package/dist/components/AiModelsSettings.js +229 -0
  18. package/dist/components/AiModelsSettings.js.map +1 -0
  19. package/dist/components/AiProvidersSettings.d.ts +3 -0
  20. package/dist/components/AiProvidersSettings.d.ts.map +1 -0
  21. package/dist/components/AiProvidersSettings.js +88 -0
  22. package/dist/components/AiProvidersSettings.js.map +1 -0
  23. package/dist/components/AiSettingsPanel.d.ts +17 -0
  24. package/dist/components/AiSettingsPanel.d.ts.map +1 -0
  25. package/dist/components/AiSettingsPanel.js +20 -0
  26. package/dist/components/AiSettingsPanel.js.map +1 -0
  27. package/dist/defaultSettings.d.ts +3 -0
  28. package/dist/defaultSettings.d.ts.map +1 -0
  29. package/dist/defaultSettings.js +40 -0
  30. package/dist/defaultSettings.js.map +1 -0
  31. package/dist/index.d.ts +15 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +13 -0
  34. package/dist/index.js.map +1 -0
  35. package/package.json +49 -0
package/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright 2025 Ilya Boyandin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @sqlrooms/ai
2
+
3
+ AI settings UI and state management
@@ -0,0 +1,29 @@
1
+ import { AiSettingsSliceConfig } from '@sqlrooms/ai-config';
2
+ import { AiSliceState } from '@sqlrooms/ai-core';
3
+ import { BaseRoomConfig, RoomState, type StateCreator } from '@sqlrooms/room-store';
4
+ export type AiSettingsSliceState = {
5
+ aiSettings: {
6
+ config: AiSettingsSliceConfig;
7
+ setMaxSteps: (maxSteps: number) => void;
8
+ setAdditionalInstruction: (additionalInstruction: string) => void;
9
+ updateProvider: (provider: string, updates: {
10
+ baseUrl?: string;
11
+ apiKey?: string;
12
+ }) => void;
13
+ addProvider: (provider: string, baseUrl: string, apiKey: string) => void;
14
+ addModelToProvider: (provider: string, modelName: string) => void;
15
+ removeModelFromProvider: (provider: string, modelName: string) => void;
16
+ removeProvider: (provider: string) => void;
17
+ addCustomModel: (baseUrl: string, apiKey: string, modelName: string) => void;
18
+ updateCustomModel: (oldModelName: string, baseUrl: string, apiKey: string, newModelName: string) => void;
19
+ removeCustomModel: (modelName: string) => void;
20
+ };
21
+ };
22
+ type CreateAiSettingsSliceParams = {
23
+ config?: Partial<AiSettingsSliceConfig>;
24
+ };
25
+ export declare function createAiSettingsSlice<PC extends BaseRoomConfig>(props?: CreateAiSettingsSliceParams): StateCreator<AiSettingsSliceState>;
26
+ type RoomStateWithAiSettings = RoomState<BaseRoomConfig> & AiSliceState & AiSettingsSliceState;
27
+ export declare function useStoreWithAiSettings<T>(selector: (state: RoomStateWithAiSettings) => T): T;
28
+ export {};
29
+ //# sourceMappingURL=AiSettingsSlice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiSettingsSlice.d.ts","sourceRoot":"","sources":["../src/AiSettingsSlice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,cAAc,EAEd,SAAS,EAET,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE;QACV,MAAM,EAAE,qBAAqB,CAAC;QAC9B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;QACxC,wBAAwB,EAAE,CAAC,qBAAqB,EAAE,MAAM,KAAK,IAAI,CAAC;QAClE,cAAc,EAAE,CACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;YACP,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,MAAM,CAAC,EAAE,MAAM,CAAC;SACjB,KACE,IAAI,CAAC;QACV,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACzE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QAClE,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;QACvE,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;QAC3C,cAAc,EAAE,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,KACd,IAAI,CAAC;QACV,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;QACV,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;KAChD,CAAC;CACH,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACzC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EAAE,SAAS,cAAc,EAC7D,KAAK,CAAC,EAAE,2BAA2B,GAClC,YAAY,CAAC,oBAAoB,CAAC,CA0KpC;AAED,KAAK,uBAAuB,GAAG,SAAS,CAAC,cAAc,CAAC,GACtD,YAAY,GACZ,oBAAoB,CAAC;AAGvB,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,CAAC,GAC9C,CAAC,CAIH"}
@@ -0,0 +1,115 @@
1
+ import { createBaseSlice, useBaseRoomStore, } from '@sqlrooms/room-store';
2
+ import { produce } from 'immer';
3
+ import { createDefaultAiSettingsConfig } from './defaultSettings';
4
+ export function createAiSettingsSlice(props) {
5
+ const config = createDefaultAiSettingsConfig(props?.config);
6
+ return createBaseSlice((set, get) => ({
7
+ aiSettings: {
8
+ config,
9
+ setMaxSteps: (maxSteps) => {
10
+ set((state) => produce(state, (draft) => {
11
+ draft.aiSettings.config.modelParameters.maxSteps = maxSteps;
12
+ }));
13
+ },
14
+ setAdditionalInstruction: (additionalInstruction) => {
15
+ set((state) => produce(state, (draft) => {
16
+ draft.aiSettings.config.modelParameters.additionalInstruction =
17
+ additionalInstruction;
18
+ }));
19
+ },
20
+ updateProvider: (provider, updates) => {
21
+ set((state) => produce(state, (draft) => {
22
+ if (draft.aiSettings.config.providers[provider]) {
23
+ Object.assign(draft.aiSettings.config.providers[provider], updates);
24
+ }
25
+ }));
26
+ },
27
+ addProvider: (provider, baseUrl, apiKey) => {
28
+ set((state) => produce(state, (draft) => {
29
+ draft.aiSettings.config.providers[provider] = {
30
+ baseUrl,
31
+ apiKey,
32
+ models: [],
33
+ };
34
+ }));
35
+ },
36
+ addModelToProvider: (provider, modelName) => {
37
+ set((state) => produce(state, (draft) => {
38
+ if (draft.aiSettings.config.providers[provider]) {
39
+ // Check if model already exists
40
+ const modelExists = draft.aiSettings.config.providers[provider].models.some((model) => model.modelName === modelName);
41
+ if (!modelExists) {
42
+ draft.aiSettings.config.providers[provider].models.push({
43
+ modelName: modelName,
44
+ });
45
+ }
46
+ }
47
+ }));
48
+ },
49
+ removeModelFromProvider: (provider, modelName) => {
50
+ set((state) => produce(state, (draft) => {
51
+ if (draft.aiSettings.config.providers[provider]) {
52
+ draft.aiSettings.config.providers[provider].models =
53
+ draft.aiSettings.config.providers[provider].models.filter((model) => model.modelName !== modelName);
54
+ }
55
+ }));
56
+ },
57
+ removeProvider: (provider) => {
58
+ set((state) => produce(state, (draft) => {
59
+ delete draft.aiSettings.config.providers[provider];
60
+ }));
61
+ },
62
+ addCustomModel: (baseUrl, apiKey, modelName) => {
63
+ set((state) => produce(state, (draft) => {
64
+ const newCustomModel = {
65
+ baseUrl,
66
+ apiKey,
67
+ modelName,
68
+ };
69
+ // Check if a custom model with the same name already exists
70
+ const existingModelIndex = draft.aiSettings.config.customModels.findIndex((model) => model.modelName.toLowerCase() === modelName.toLowerCase());
71
+ if (existingModelIndex !== -1) {
72
+ // Update existing model
73
+ draft.aiSettings.config.customModels[existingModelIndex] =
74
+ newCustomModel;
75
+ }
76
+ else {
77
+ // Add new model
78
+ draft.aiSettings.config.customModels.push(newCustomModel);
79
+ }
80
+ }));
81
+ },
82
+ updateCustomModel: (oldModelName, baseUrl, apiKey, newModelName) => {
83
+ set((state) => produce(state, (draft) => {
84
+ // Find the model to update
85
+ const modelIndex = draft.aiSettings.config.customModels.findIndex((model) => model.modelName === oldModelName);
86
+ if (modelIndex !== -1) {
87
+ // Check if the new name conflicts with another model (excluding the current one)
88
+ const conflictingModelIndex = draft.aiSettings.config.customModels.findIndex((model, index) => index !== modelIndex &&
89
+ model.modelName.toLowerCase() ===
90
+ newModelName.toLowerCase());
91
+ if (conflictingModelIndex === -1) {
92
+ // Update the model
93
+ draft.aiSettings.config.customModels[modelIndex] = {
94
+ baseUrl,
95
+ apiKey,
96
+ modelName: newModelName,
97
+ };
98
+ }
99
+ }
100
+ }));
101
+ },
102
+ removeCustomModel: (modelName) => {
103
+ set((state) => produce(state, (draft) => {
104
+ draft.aiSettings.config.customModels =
105
+ draft.aiSettings.config.customModels.filter((model) => model.modelName !== modelName);
106
+ }));
107
+ },
108
+ },
109
+ }));
110
+ }
111
+ // Hook to access aiSettings from the room store
112
+ export function useStoreWithAiSettings(selector) {
113
+ return useBaseRoomStore((state) => selector(state));
114
+ }
115
+ //# sourceMappingURL=AiSettingsSlice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiSettingsSlice.js","sourceRoot":"","sources":["../src/AiSettingsSlice.ts"],"names":[],"mappings":"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","sourcesContent":["import {AiSettingsSliceConfig} from '@sqlrooms/ai-config';\nimport {AiSliceState} from '@sqlrooms/ai-core';\nimport {\n BaseRoomConfig,\n createBaseSlice,\n RoomState,\n useBaseRoomStore,\n type StateCreator,\n} from '@sqlrooms/room-store';\nimport {produce} from 'immer';\nimport {createDefaultAiSettingsConfig} from './defaultSettings';\n\nexport type AiSettingsSliceState = {\n aiSettings: {\n config: AiSettingsSliceConfig;\n setMaxSteps: (maxSteps: number) => void;\n setAdditionalInstruction: (additionalInstruction: string) => void;\n updateProvider: (\n provider: string,\n updates: {\n baseUrl?: string;\n apiKey?: string;\n },\n ) => void;\n addProvider: (provider: string, baseUrl: string, apiKey: string) => void;\n addModelToProvider: (provider: string, modelName: string) => void;\n removeModelFromProvider: (provider: string, modelName: string) => void;\n removeProvider: (provider: string) => void;\n addCustomModel: (\n baseUrl: string,\n apiKey: string,\n modelName: string,\n ) => void;\n updateCustomModel: (\n oldModelName: string,\n baseUrl: string,\n apiKey: string,\n newModelName: string,\n ) => void;\n removeCustomModel: (modelName: string) => void;\n };\n};\n\ntype CreateAiSettingsSliceParams = {\n config?: Partial<AiSettingsSliceConfig>;\n};\n\nexport function createAiSettingsSlice<PC extends BaseRoomConfig>(\n props?: CreateAiSettingsSliceParams,\n): StateCreator<AiSettingsSliceState> {\n const config = createDefaultAiSettingsConfig(props?.config);\n return createBaseSlice<PC, AiSettingsSliceState>((set, get) => ({\n aiSettings: {\n config,\n\n setMaxSteps: (maxSteps: number) => {\n set((state) =>\n produce(state, (draft) => {\n draft.aiSettings.config.modelParameters.maxSteps = maxSteps;\n }),\n );\n },\n\n setAdditionalInstruction: (additionalInstruction: string) => {\n set((state) =>\n produce(state, (draft) => {\n draft.aiSettings.config.modelParameters.additionalInstruction =\n additionalInstruction;\n }),\n );\n },\n\n updateProvider: (\n provider: string,\n updates: {\n baseUrl?: string;\n apiKey?: string;\n },\n ) => {\n set((state) =>\n produce(state, (draft) => {\n if (draft.aiSettings.config.providers[provider]) {\n Object.assign(\n draft.aiSettings.config.providers[provider],\n updates,\n );\n }\n }),\n );\n },\n\n addProvider: (provider: string, baseUrl: string, apiKey: string) => {\n set((state) =>\n produce(state, (draft) => {\n draft.aiSettings.config.providers[provider] = {\n baseUrl,\n apiKey,\n models: [],\n };\n }),\n );\n },\n\n addModelToProvider: (provider: string, modelName: string) => {\n set((state) =>\n produce(state, (draft) => {\n if (draft.aiSettings.config.providers[provider]) {\n // Check if model already exists\n const modelExists = draft.aiSettings.config.providers[\n provider\n ].models.some((model) => model.modelName === modelName);\n\n if (!modelExists) {\n draft.aiSettings.config.providers[provider].models.push({\n modelName: modelName,\n });\n }\n }\n }),\n );\n },\n\n removeModelFromProvider: (provider: string, modelName: string) => {\n set((state) =>\n produce(state, (draft) => {\n if (draft.aiSettings.config.providers[provider]) {\n draft.aiSettings.config.providers[provider].models =\n draft.aiSettings.config.providers[provider].models.filter(\n (model) => model.modelName !== modelName,\n );\n }\n }),\n );\n },\n\n removeProvider: (provider: string) => {\n set((state) =>\n produce(state, (draft) => {\n delete draft.aiSettings.config.providers[provider];\n }),\n );\n },\n\n addCustomModel: (baseUrl: string, apiKey: string, modelName: string) => {\n set((state) =>\n produce(state, (draft) => {\n const newCustomModel = {\n baseUrl,\n apiKey,\n modelName,\n };\n\n // Check if a custom model with the same name already exists\n const existingModelIndex =\n draft.aiSettings.config.customModels.findIndex(\n (model) =>\n model.modelName.toLowerCase() === modelName.toLowerCase(),\n );\n\n if (existingModelIndex !== -1) {\n // Update existing model\n draft.aiSettings.config.customModels[existingModelIndex] =\n newCustomModel;\n } else {\n // Add new model\n draft.aiSettings.config.customModels.push(newCustomModel);\n }\n }),\n );\n },\n\n updateCustomModel: (\n oldModelName: string,\n baseUrl: string,\n apiKey: string,\n newModelName: string,\n ) => {\n set((state) =>\n produce(state, (draft) => {\n // Find the model to update\n const modelIndex = draft.aiSettings.config.customModels.findIndex(\n (model) => model.modelName === oldModelName,\n );\n\n if (modelIndex !== -1) {\n // Check if the new name conflicts with another model (excluding the current one)\n const conflictingModelIndex =\n draft.aiSettings.config.customModels.findIndex(\n (model, index) =>\n index !== modelIndex &&\n model.modelName.toLowerCase() ===\n newModelName.toLowerCase(),\n );\n\n if (conflictingModelIndex === -1) {\n // Update the model\n draft.aiSettings.config.customModels[modelIndex] = {\n baseUrl,\n apiKey,\n modelName: newModelName,\n };\n }\n }\n }),\n );\n },\n\n removeCustomModel: (modelName: string) => {\n set((state) =>\n produce(state, (draft) => {\n draft.aiSettings.config.customModels =\n draft.aiSettings.config.customModels.filter(\n (model) => model.modelName !== modelName,\n );\n }),\n );\n },\n },\n }));\n}\n\ntype RoomStateWithAiSettings = RoomState<BaseRoomConfig> &\n AiSliceState &\n AiSettingsSliceState;\n\n// Hook to access aiSettings from the room store\nexport function useStoreWithAiSettings<T>(\n selector: (state: RoomStateWithAiSettings) => T,\n): T {\n return useBaseRoomStore<BaseRoomConfig, RoomState<BaseRoomConfig>, T>(\n (state) => selector(state as unknown as RoomStateWithAiSettings),\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+ export declare const AiModelParameters: FC;
3
+ //# sourceMappingURL=AiModelParameters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelParameters.d.ts","sourceRoot":"","sources":["../../src/components/AiModelParameters.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,EAAE,EAAS,MAAM,OAAO,CAAC;AAgBjC,eAAO,MAAM,iBAAiB,EAAE,EAgM/B,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { Sliders, Wrench, FileText, Upload, Eye } from 'lucide-react';
4
+ import { Textarea, Input, Button, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, useDisclosure, useToast, } from '@sqlrooms/ui';
5
+ import { useStoreWithAiSettings } from '../AiSettingsSlice';
6
+ export const AiModelParameters = () => {
7
+ const maxSteps = useStoreWithAiSettings((s) => s.aiSettings.config.modelParameters.maxSteps);
8
+ const setMaxStepsAiChatUi = useStoreWithAiSettings((s) => s.aiSettings.setMaxSteps);
9
+ const additionalInstruction = useStoreWithAiSettings((s) => s.aiSettings.config.modelParameters.additionalInstruction);
10
+ const setAdditionalInstruction = useStoreWithAiSettings((s) => s.aiSettings.setAdditionalInstruction);
11
+ const fileInputRef = useRef(null);
12
+ const { toast } = useToast();
13
+ const { isOpen, onOpen, onClose } = useDisclosure();
14
+ const handleMaxStepsChange = (value) => {
15
+ setMaxStepsAiChatUi(value);
16
+ };
17
+ const handleAdditionalInstructionChange = (value) => {
18
+ setAdditionalInstruction(value);
19
+ };
20
+ const getFullInstructions = useStoreWithAiSettings((s) => s.ai.getFullInstructions);
21
+ const handleFileUpload = async (event) => {
22
+ const file = event.target.files?.[0];
23
+ if (!file)
24
+ return;
25
+ // Validate file type
26
+ const allowedTypes = ['text/plain', 'text/markdown', 'application/json'];
27
+ const allowedExtensions = ['.txt', '.md', '.json', '.text'];
28
+ const fileExtension = file.name
29
+ .toLowerCase()
30
+ .slice(file.name.lastIndexOf('.'));
31
+ if (!allowedTypes.includes(file.type) &&
32
+ !allowedExtensions.includes(fileExtension)) {
33
+ toast({
34
+ title: 'Invalid file type',
35
+ description: 'Please select a text file (.txt, .md, .json)',
36
+ variant: 'destructive',
37
+ });
38
+ return;
39
+ }
40
+ // Validate file size (max 1MB)
41
+ const maxSize = 1024 * 1024; // 1MB
42
+ if (file.size > maxSize) {
43
+ toast({
44
+ title: 'File too large',
45
+ description: 'File size must be less than 1MB',
46
+ variant: 'destructive',
47
+ });
48
+ return;
49
+ }
50
+ try {
51
+ const text = await file.text();
52
+ setAdditionalInstruction(text);
53
+ toast({
54
+ title: 'File uploaded successfully',
55
+ description: 'System instructions have been updated from the uploaded file.',
56
+ });
57
+ }
58
+ catch (error) {
59
+ console.error('Error reading file:', error);
60
+ toast({
61
+ title: 'Error reading file',
62
+ description: 'Please try again.',
63
+ variant: 'destructive',
64
+ });
65
+ }
66
+ // Reset the input so the same file can be selected again
67
+ if (fileInputRef.current) {
68
+ fileInputRef.current.value = '';
69
+ }
70
+ };
71
+ const handleUploadButtonClick = () => {
72
+ fileInputRef.current?.click();
73
+ };
74
+ const handleViewFullInstructions = () => {
75
+ onOpen();
76
+ };
77
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-md flex items-center gap-2 pb-6 font-medium", children: [_jsx(Sliders, { className: "h-4 w-4" }), "Model Parameters"] }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(Wrench, { className: "h-4 w-4" }), "Max Tool Steps"] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx(Input, { type: "number", min: "1", max: "20", step: "1", value: maxSteps, onChange: (e) => handleMaxStepsChange(parseInt(e.target.value) || 1), className: "flex-1" }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(FileText, { className: "h-4 w-4" }), "Additional Instructions"] }), _jsx(Textarea, { value: additionalInstruction, onChange: (e) => handleAdditionalInstructionChange(e.target.value), placeholder: "Enter custom system instructions for the AI model...", className: "min-h-[80px] resize-y", autoResize: false }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleUploadButtonClick, className: "flex items-center gap-2", children: [_jsx(Upload, { className: "h-4 w-4" }), "Upload File"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleViewFullInstructions, className: "flex items-center gap-2", children: [_jsx(Eye, { className: "h-4 w-4" }), "View Instructions"] })] }), _jsx(Input, { ref: fileInputRef, type: "file", accept: ".txt,.md,.json,.text,text/plain,text/markdown,application/json", onChange: handleFileUpload, style: { display: 'none' } })] })] }), _jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: _jsxs(DialogContent, { className: "flex h-[80vh] max-w-4xl flex-col", children: [_jsxs(DialogHeader, { className: "flex-shrink-0", children: [_jsx(DialogTitle, { children: "Full System Instructions" }), _jsx(DialogDescription, { children: "Complete system instructions that will be sent to the AI model, including default instructions and your additional custom instructions." })] }), _jsx("div", { className: "mt-4 min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "bg-muted/50 h-full overflow-auto rounded-lg p-4", children: _jsx("pre", { className: "overflow-wrap-anywhere w-full max-w-full break-words font-mono text-sm leading-relaxed", children: getFullInstructions() }) }) })] }) })] }));
78
+ };
79
+ //# sourceMappingURL=AiModelParameters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelParameters.js","sourceRoot":"","sources":["../../src/components/AiModelParameters.tsx"],"names":[],"mappings":"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","sourcesContent":["import {FC, useRef} from 'react';\nimport {Sliders, Wrench, FileText, Upload, Eye} from 'lucide-react';\nimport {\n Textarea,\n Input,\n Button,\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n useDisclosure,\n useToast,\n} from '@sqlrooms/ui';\nimport {useStoreWithAiSettings} from '../AiSettingsSlice';\n\nexport const AiModelParameters: FC = () => {\n const maxSteps = useStoreWithAiSettings(\n (s) => s.aiSettings.config.modelParameters.maxSteps,\n );\n const setMaxStepsAiChatUi = useStoreWithAiSettings(\n (s) => s.aiSettings.setMaxSteps,\n );\n\n const additionalInstruction = useStoreWithAiSettings(\n (s) => s.aiSettings.config.modelParameters.additionalInstruction,\n );\n const setAdditionalInstruction = useStoreWithAiSettings(\n (s) => s.aiSettings.setAdditionalInstruction,\n );\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const {toast} = useToast();\n\n const {isOpen, onOpen, onClose} = useDisclosure();\n\n const handleMaxStepsChange = (value: number) => {\n setMaxStepsAiChatUi(value);\n };\n\n const handleAdditionalInstructionChange = (value: string) => {\n setAdditionalInstruction(value);\n };\n\n const getFullInstructions = useStoreWithAiSettings(\n (s) => s.ai.getFullInstructions,\n );\n\n const handleFileUpload = async (\n event: React.ChangeEvent<HTMLInputElement>,\n ) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n // Validate file type\n const allowedTypes = ['text/plain', 'text/markdown', 'application/json'];\n const allowedExtensions = ['.txt', '.md', '.json', '.text'];\n const fileExtension = file.name\n .toLowerCase()\n .slice(file.name.lastIndexOf('.'));\n\n if (\n !allowedTypes.includes(file.type) &&\n !allowedExtensions.includes(fileExtension)\n ) {\n toast({\n title: 'Invalid file type',\n description: 'Please select a text file (.txt, .md, .json)',\n variant: 'destructive',\n });\n return;\n }\n\n // Validate file size (max 1MB)\n const maxSize = 1024 * 1024; // 1MB\n if (file.size > maxSize) {\n toast({\n title: 'File too large',\n description: 'File size must be less than 1MB',\n variant: 'destructive',\n });\n return;\n }\n\n try {\n const text = await file.text();\n setAdditionalInstruction(text);\n toast({\n title: 'File uploaded successfully',\n description:\n 'System instructions have been updated from the uploaded file.',\n });\n } catch (error) {\n console.error('Error reading file:', error);\n toast({\n title: 'Error reading file',\n description: 'Please try again.',\n variant: 'destructive',\n });\n }\n\n // Reset the input so the same file can be selected again\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n };\n\n const handleUploadButtonClick = () => {\n fileInputRef.current?.click();\n };\n\n const handleViewFullInstructions = () => {\n onOpen();\n };\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-md flex items-center gap-2 pb-6 font-medium\">\n <Sliders className=\"h-4 w-4\" />\n Model Parameters\n </label>\n <div className=\"grid grid-cols-1 gap-4\">\n {/* Max Steps */}\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <Wrench className=\"h-4 w-4\" />\n Max Tool Steps\n </label>\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"number\"\n min=\"1\"\n max=\"20\"\n step=\"1\"\n value={maxSteps}\n onChange={(e) =>\n handleMaxStepsChange(parseInt(e.target.value) || 1)\n }\n className=\"flex-1\"\n />\n </div>\n </div>\n\n {/* Additional Instruction */}\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <FileText className=\"h-4 w-4\" />\n Additional Instructions\n </label>\n <Textarea\n value={additionalInstruction}\n onChange={(e) => handleAdditionalInstructionChange(e.target.value)}\n placeholder=\"Enter custom system instructions for the AI model...\"\n className=\"min-h-[80px] resize-y\"\n autoResize={false}\n />\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleUploadButtonClick}\n className=\"flex items-center gap-2\"\n >\n <Upload className=\"h-4 w-4\" />\n Upload File\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleViewFullInstructions}\n className=\"flex items-center gap-2\"\n >\n <Eye className=\"h-4 w-4\" />\n View Instructions\n </Button>\n </div>\n <Input\n ref={fileInputRef}\n type=\"file\"\n accept=\".txt,.md,.json,.text,text/plain,text/markdown,application/json\"\n onChange={handleFileUpload}\n style={{display: 'none'}}\n />\n </div>\n </div>\n\n {/* Full Instructions Modal */}\n <Dialog open={isOpen} onOpenChange={onClose}>\n <DialogContent className=\"flex h-[80vh] max-w-4xl flex-col\">\n <DialogHeader className=\"flex-shrink-0\">\n <DialogTitle>Full System Instructions</DialogTitle>\n <DialogDescription>\n Complete system instructions that will be sent to the AI model,\n including default instructions and your additional custom\n instructions.\n </DialogDescription>\n </DialogHeader>\n <div className=\"mt-4 min-h-0 flex-1 overflow-hidden\">\n <div className=\"bg-muted/50 h-full overflow-auto rounded-lg p-4\">\n <pre className=\"overflow-wrap-anywhere w-full max-w-full break-words font-mono text-sm leading-relaxed\">\n {getFullInstructions()}\n </pre>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n );\n};\n"]}
@@ -0,0 +1,18 @@
1
+ import { FC } from 'react';
2
+ export interface ModelUsageData {
3
+ totalSpend: number;
4
+ maxBudget: number;
5
+ isLoadingSpend: boolean;
6
+ weeklySpend?: Array<{
7
+ date: string;
8
+ spend: number;
9
+ }>;
10
+ isLoadingWeeklySpend?: boolean;
11
+ }
12
+ type AiModelUsageProps = {
13
+ className?: string;
14
+ modelUsage?: ModelUsageData;
15
+ };
16
+ export declare const AiModelUsage: FC<AiModelUsageProps>;
17
+ export {};
18
+ //# sourceMappingURL=AiModelUsage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelUsage.d.ts","sourceRoot":"","sources":["../../src/components/AiModelUsage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAazB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACnD,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,KAAK,iBAAiB,GAAG;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC;AA6BF,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAgJ9C,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CreditCard, BarChart3, DollarSign } from 'lucide-react';
3
+ import { SkeletonPane } from '@sqlrooms/ui';
4
+ import { ChartContainer, ChartTooltip } from '@sqlrooms/recharts';
5
+ import { Bar, XAxis, YAxis, CartesianGrid, ResponsiveContainer, BarChart, } from 'recharts';
6
+ const fillMissingDays = (weeklySpend) => {
7
+ const today = new Date();
8
+ const pastWeek = [];
9
+ // Generate the past 7 days
10
+ for (let i = 6; i >= 0; i--) {
11
+ const date = new Date(today);
12
+ date.setDate(date.getDate() - i);
13
+ const dateString = date.toISOString().split('T')[0]; // YYYY-MM-DD format
14
+ // Find existing data for this date
15
+ const existingData = weeklySpend.find((day) => day.date === dateString);
16
+ if (existingData) {
17
+ pastWeek.push(existingData);
18
+ }
19
+ else {
20
+ // Fill missing day with 0 spend
21
+ pastWeek.push({
22
+ date: dateString || '',
23
+ spend: 0,
24
+ });
25
+ }
26
+ }
27
+ return pastWeek;
28
+ };
29
+ export const AiModelUsage = ({ className = '', modelUsage, }) => {
30
+ if (!modelUsage)
31
+ return null;
32
+ const { totalSpend, maxBudget, isLoadingSpend, weeklySpend, isLoadingWeeklySpend, } = modelUsage;
33
+ const getCurrentMonthRange = () => {
34
+ const now = new Date();
35
+ const month = now.toLocaleDateString('en-US', { month: 'long' });
36
+ const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
37
+ return `${month} 1-${lastDay}`;
38
+ };
39
+ return (_jsxs("div", { className: `space-y-4 ${className}`, children: [_jsxs("h3", { className: "text-md flex items-center gap-2 pb-6 font-medium", children: [_jsx(CreditCard, { className: "h-4 w-4" }), "Billing & Usage"] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(DollarSign, { className: "h-4 w-4" }), "Spend ", getCurrentMonthRange()] }), _jsx("div", { className: "bg-muted rounded-lg p-4", children: isLoadingSpend ? (_jsx(SkeletonPane, { n: 2, rowHeight: "32px", className: "space-y-4" })) : (_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-muted-foreground text-sm", children: "Total Spend" }), _jsxs("p", { className: "text-2xl font-bold", children: ["$", totalSpend.toFixed(3)] })] }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-muted-foreground text-sm", children: "Max Budget" }), _jsxs("p", { className: "text-2xl font-bold", children: ["$", maxBudget.toFixed(2)] })] })] })) }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(BarChart3, { className: "h-4 w-4" }), "Daily Spending Trend"] }), _jsx("div", { className: "bg-muted h-32 rounded-lg p-4", style: { position: 'relative' }, children: isLoadingWeeklySpend ? (_jsx(SkeletonPane, { n: 7, rowHeight: "100%", className: "w-full" })) : (_jsx(ChartContainer, { config: {
40
+ spend: {
41
+ label: 'Daily Spend',
42
+ color: 'hsl(var(--primary))',
43
+ },
44
+ }, className: "h-full w-full", style: {
45
+ // Remove any default hover backgrounds
46
+ backgroundColor: 'transparent',
47
+ }, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: fillMissingDays(weeklySpend || []).map((day) => ({
48
+ ...day,
49
+ dayLabel: new Date(day.date).toLocaleDateString('en-US', {
50
+ weekday: 'short',
51
+ }),
52
+ })), children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--muted-foreground))", opacity: 0.3 }), _jsx(XAxis, { dataKey: "dayLabel", tick: { fontSize: 10 }, tickLine: false, axisLine: false }), _jsx(YAxis, { tick: { fontSize: 10 }, tickLine: false, axisLine: false, tickFormatter: (value) => `$${value.toFixed(3)}` }), _jsx(ChartTooltip, { content: ({ active, payload }) => {
53
+ if (active && payload && payload.length && payload[0]) {
54
+ const data = payload[0].payload;
55
+ return (_jsxs("div", { className: "bg-background border-border rounded-lg border p-2 shadow-lg", children: [_jsx("p", { className: "text-sm font-medium", children: data.date }), _jsx("p", { className: "text-sm font-medium", children: data.dayLabel }), _jsxs("p", { className: "text-muted-foreground text-sm", children: ["$", data.spend.toFixed(3)] })] }));
56
+ }
57
+ return null;
58
+ }, cursor: false }), _jsx(Bar, { dataKey: "spend", fill: "hsl(var(--primary))", radius: [2, 2, 0, 0] })] }) }) })) }), _jsx("p", { className: "text-muted-foreground text-center text-xs", children: "Last 7 days spending" })] })] })] }));
59
+ };
60
+ //# sourceMappingURL=AiModelUsage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelUsage.js","sourceRoot":"","sources":["../../src/components/AiModelUsage.tsx"],"names":[],"mappings":"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","sourcesContent":["import {FC} from 'react';\nimport {CreditCard, BarChart3, DollarSign} from 'lucide-react';\nimport {SkeletonPane} from '@sqlrooms/ui';\nimport {ChartContainer, ChartTooltip} from '@sqlrooms/recharts';\nimport {\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n ResponsiveContainer,\n BarChart,\n} from 'recharts';\n\nexport interface ModelUsageData {\n totalSpend: number;\n maxBudget: number;\n isLoadingSpend: boolean;\n weeklySpend?: Array<{date: string; spend: number}>;\n isLoadingWeeklySpend?: boolean;\n}\n\ntype AiModelUsageProps = {\n className?: string;\n modelUsage?: ModelUsageData;\n};\n\nconst fillMissingDays = (weeklySpend: Array<{date: string; spend: number}>) => {\n const today = new Date();\n const pastWeek: Array<{date: string; spend: number}> = [];\n\n // Generate the past 7 days\n for (let i = 6; i >= 0; i--) {\n const date = new Date(today);\n date.setDate(date.getDate() - i);\n const dateString = date.toISOString().split('T')[0]; // YYYY-MM-DD format\n\n // Find existing data for this date\n const existingData = weeklySpend.find((day) => day.date === dateString);\n\n if (existingData) {\n pastWeek.push(existingData);\n } else {\n // Fill missing day with 0 spend\n pastWeek.push({\n date: dateString || '',\n spend: 0,\n });\n }\n }\n\n return pastWeek;\n};\n\nexport const AiModelUsage: FC<AiModelUsageProps> = ({\n className = '',\n modelUsage,\n}) => {\n if (!modelUsage) return null;\n\n const {\n totalSpend,\n maxBudget,\n isLoadingSpend,\n weeklySpend,\n isLoadingWeeklySpend,\n } = modelUsage;\n\n const getCurrentMonthRange = () => {\n const now = new Date();\n const month = now.toLocaleDateString('en-US', {month: 'long'});\n const lastDay = new Date(\n now.getFullYear(),\n now.getMonth() + 1,\n 0,\n ).getDate();\n return `${month} 1-${lastDay}`;\n };\n\n return (\n <div className={`space-y-4 ${className}`}>\n <h3 className=\"text-md flex items-center gap-2 pb-6 font-medium\">\n <CreditCard className=\"h-4 w-4\" />\n Billing & Usage\n </h3>\n <div className=\"flex flex-col gap-4\">\n <p className=\"text-muted-foreground flex items-center gap-2 text-xs\">\n <DollarSign className=\"h-4 w-4\" />\n Spend {getCurrentMonthRange()}\n </p>\n\n {/* Spending Overview */}\n <div className=\"bg-muted rounded-lg p-4\">\n {isLoadingSpend ? (\n <SkeletonPane n={2} rowHeight=\"32px\" className=\"space-y-4\" />\n ) : (\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"text-center\">\n <p className=\"text-muted-foreground text-sm\">Total Spend</p>\n <p className=\"text-2xl font-bold\">${totalSpend.toFixed(3)}</p>\n </div>\n <div className=\"text-center\">\n <p className=\"text-muted-foreground text-sm\">Max Budget</p>\n <p className=\"text-2xl font-bold\">${maxBudget.toFixed(2)}</p>\n </div>\n </div>\n )}\n </div>\n\n {/* Daily Spending Chart */}\n <div className=\"space-y-2\">\n <label className=\"text-muted-foreground flex items-center gap-2 text-xs\">\n <BarChart3 className=\"h-4 w-4\" />\n Daily Spending Trend\n </label>\n <div\n className=\"bg-muted h-32 rounded-lg p-4\"\n style={{position: 'relative'}}\n >\n {isLoadingWeeklySpend ? (\n <SkeletonPane n={7} rowHeight=\"100%\" className=\"w-full\" />\n ) : (\n <ChartContainer\n config={{\n spend: {\n label: 'Daily Spend',\n color: 'hsl(var(--primary))',\n },\n }}\n className=\"h-full w-full\"\n style={{\n // Remove any default hover backgrounds\n backgroundColor: 'transparent',\n }}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart\n data={fillMissingDays(weeklySpend || []).map((day) => ({\n ...day,\n dayLabel: new Date(day.date).toLocaleDateString('en-US', {\n weekday: 'short',\n }),\n }))}\n >\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"hsl(var(--muted-foreground))\"\n opacity={0.3}\n />\n <XAxis\n dataKey=\"dayLabel\"\n tick={{fontSize: 10}}\n tickLine={false}\n axisLine={false}\n />\n <YAxis\n tick={{fontSize: 10}}\n tickLine={false}\n axisLine={false}\n tickFormatter={(value) => `$${value.toFixed(3)}`}\n />\n <ChartTooltip\n content={({active, payload}: any) => {\n if (active && payload && payload.length && payload[0]) {\n const data = payload[0].payload;\n return (\n <div className=\"bg-background border-border rounded-lg border p-2 shadow-lg\">\n <p className=\"text-sm font-medium\">{data.date}</p>\n <p className=\"text-sm font-medium\">\n {data.dayLabel}\n </p>\n <p className=\"text-muted-foreground text-sm\">\n ${data.spend.toFixed(3)}\n </p>\n </div>\n );\n }\n return null;\n }}\n cursor={false}\n />\n <Bar\n dataKey=\"spend\"\n fill=\"hsl(var(--primary))\"\n radius={[2, 2, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n </ChartContainer>\n )}\n </div>\n <p className=\"text-muted-foreground text-center text-xs\">\n Last 7 days spending\n </p>\n </div>\n </div>\n </div>\n );\n};\n"]}
@@ -0,0 +1,10 @@
1
+ import { FC } from 'react';
2
+ export interface AiModelsSettingsProps {
3
+ showProviderModels?: boolean;
4
+ showCustomModels?: boolean;
5
+ allowEditProviderModels?: boolean;
6
+ allowCustomModels?: boolean;
7
+ className?: string;
8
+ }
9
+ export declare const AiModelsSettings: FC<AiModelsSettingsProps>;
10
+ //# sourceMappingURL=AiModelsSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelsSettings.d.ts","sourceRoot":"","sources":["../../src/components/AiModelsSettings.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAC,EAAE,EAAoB,MAAM,OAAO,CAAC;AAG5C,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CAoqBtD,CAAC"}
@@ -0,0 +1,229 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useStoreWithAi } from '@sqlrooms/ai-core';
3
+ import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, Input, Label, useDisclosure, useToast, } from '@sqlrooms/ui';
4
+ import { Blocks, CirclePlus, Cpu, Key, Plus, Server, Settings, Trash2, } from 'lucide-react';
5
+ import { useMemo, useState } from 'react';
6
+ import { useStoreWithAiSettings } from '../AiSettingsSlice';
7
+ export const AiModelsSettings = ({ className = '', allowEditProviderModels = true, allowCustomModels = true, }) => {
8
+ const { toast } = useToast();
9
+ const aiConfig = useStoreWithAiSettings((s) => s.aiSettings.config);
10
+ const setAiModel = useStoreWithAi((s) => s.ai.setAiModel);
11
+ const addModelToProvider = useStoreWithAiSettings((s) => s.aiSettings.addModelToProvider);
12
+ const removeModelFromProvider = useStoreWithAiSettings((s) => s.aiSettings.removeModelFromProvider);
13
+ const addCustomModel = useStoreWithAiSettings((s) => s.aiSettings.addCustomModel);
14
+ const updateCustomModel = useStoreWithAiSettings((s) => s.aiSettings.updateCustomModel);
15
+ const removeCustomModel = useStoreWithAiSettings((s) => s.aiSettings.removeCustomModel);
16
+ const providers = useMemo(() => Object.entries(aiConfig.providers), [aiConfig.providers]);
17
+ const customModels = aiConfig.customModels || [];
18
+ // Dialog state for adding a custom model
19
+ const { isOpen: isCustomModelDialogOpen, onOpen: openCustomModelDialog, onClose: closeCustomModelDialog, } = useDisclosure();
20
+ const [customName, setCustomName] = useState('');
21
+ const [customBaseUrl, setCustomBaseUrl] = useState('');
22
+ const [customApiKey, setCustomApiKey] = useState('');
23
+ const [baseUrlError, setBaseUrlError] = useState(false);
24
+ // Dialog state for editing a custom model
25
+ const { isOpen: isEditCustomModelDialogOpen, onOpen: openEditCustomModelDialog, onClose: closeEditCustomModelDialog, } = useDisclosure();
26
+ const [editingModel, setEditingModel] = useState(null);
27
+ const [editBaseUrlError, setEditBaseUrlError] = useState(false);
28
+ // Dialog state for adding a model to a provider
29
+ const { isOpen: isAddProviderModelDialogOpen, onOpen: openAddProviderModelDialog, onClose: closeAddProviderModelDialog, } = useDisclosure();
30
+ const [selectedProviderKey, setSelectedProviderKey] = useState('');
31
+ const [newModelName, setNewModelName] = useState('');
32
+ // Dialog state for deleting a model
33
+ const { isOpen: isDeleteModelDialogOpen, onOpen: openDeleteModelDialog, onClose: closeDeleteModelDialog, } = useDisclosure();
34
+ const [modelToDelete, setModelToDelete] = useState(null);
35
+ const handleAddCustomToSession = () => {
36
+ // Reset error states
37
+ setBaseUrlError(false);
38
+ // Validate required fields
39
+ if (!customName.trim()) {
40
+ toast({
41
+ title: 'Model Name Required',
42
+ description: 'Please enter a model name.',
43
+ variant: 'destructive',
44
+ });
45
+ return;
46
+ }
47
+ if (!customBaseUrl.trim()) {
48
+ setBaseUrlError(true);
49
+ toast({
50
+ title: 'Base URL Required',
51
+ description: 'Please enter a base URL for the model.',
52
+ variant: 'destructive',
53
+ });
54
+ return;
55
+ }
56
+ const trimmedName = customName.trim();
57
+ // Add the custom model to the config
58
+ addCustomModel(customBaseUrl.trim(), customApiKey.trim(), trimmedName);
59
+ // Update the current session to use this custom model
60
+ setAiModel('custom', trimmedName);
61
+ // Show success message
62
+ toast({
63
+ title: 'Custom Model Added',
64
+ description: `Successfully added "${trimmedName}" to your custom models.`,
65
+ variant: 'default',
66
+ });
67
+ // Reset form
68
+ setCustomName('');
69
+ setCustomBaseUrl('');
70
+ setCustomApiKey('');
71
+ setBaseUrlError(false);
72
+ closeCustomModelDialog();
73
+ };
74
+ const handleEditCustomModel = (modelName) => {
75
+ const customModel = customModels.find((cm) => cm.modelName === modelName);
76
+ if (customModel) {
77
+ setEditingModel({
78
+ oldModelName: modelName,
79
+ modelName: modelName,
80
+ baseUrl: customModel.baseUrl,
81
+ apiKey: customModel.apiKey,
82
+ });
83
+ setEditBaseUrlError(false);
84
+ openEditCustomModelDialog();
85
+ }
86
+ };
87
+ const handleUpdateCustomModel = () => {
88
+ if (!editingModel)
89
+ return;
90
+ // Reset error states
91
+ setEditBaseUrlError(false);
92
+ // Validate required fields
93
+ if (!editingModel.modelName.trim()) {
94
+ toast({
95
+ title: 'Model Name Required',
96
+ description: 'Please enter a model name.',
97
+ variant: 'destructive',
98
+ });
99
+ return;
100
+ }
101
+ if (!editingModel.baseUrl.trim()) {
102
+ setEditBaseUrlError(true);
103
+ toast({
104
+ title: 'Base URL Required',
105
+ description: 'Please enter a base URL for the model.',
106
+ variant: 'destructive',
107
+ });
108
+ return;
109
+ }
110
+ const trimmedName = editingModel.modelName.trim();
111
+ const trimmedBaseUrl = editingModel.baseUrl.trim();
112
+ const trimmedApiKey = editingModel.apiKey.trim();
113
+ // Check for duplicate model names (excluding the current model being edited)
114
+ const duplicateModel = customModels.find((cm) => cm.modelName.toLowerCase() === trimmedName.toLowerCase() &&
115
+ cm.modelName !== editingModel.oldModelName);
116
+ if (duplicateModel) {
117
+ toast({
118
+ title: 'Model Name Already Exists',
119
+ description: `A custom model with the name "${trimmedName}" already exists.`,
120
+ variant: 'destructive',
121
+ });
122
+ return;
123
+ }
124
+ // Update the custom model
125
+ updateCustomModel(editingModel.oldModelName, trimmedBaseUrl, trimmedApiKey, trimmedName);
126
+ // Show success message
127
+ toast({
128
+ title: 'Custom Model Updated',
129
+ description: `Successfully updated "${trimmedName}".`,
130
+ variant: 'default',
131
+ });
132
+ // Reset form and close dialog
133
+ setEditingModel(null);
134
+ setEditBaseUrlError(false);
135
+ closeEditCustomModelDialog();
136
+ };
137
+ const handleOpenAddProviderModelDialog = (providerKey) => {
138
+ setSelectedProviderKey(providerKey);
139
+ setNewModelName('');
140
+ openAddProviderModelDialog();
141
+ };
142
+ const handleAddModelToProvider = () => {
143
+ if (!selectedProviderKey || !newModelName.trim())
144
+ return;
145
+ // Check for duplicates
146
+ const provider = aiConfig.providers[selectedProviderKey];
147
+ const modelExists = provider?.models.some((model) => model.modelName.toLowerCase() === newModelName.trim().toLowerCase());
148
+ if (modelExists) {
149
+ toast({
150
+ title: 'Model Already Exists',
151
+ description: `The model "${newModelName.trim()}" already exists in this provider.`,
152
+ variant: 'destructive',
153
+ });
154
+ return;
155
+ }
156
+ addModelToProvider(selectedProviderKey, newModelName.trim());
157
+ setNewModelName('');
158
+ closeAddProviderModelDialog();
159
+ };
160
+ const handleDeleteModel = (type, providerKey, modelName) => {
161
+ setModelToDelete({ type, providerKey, modelName });
162
+ openDeleteModelDialog();
163
+ };
164
+ const confirmDeleteModel = () => {
165
+ if (!modelToDelete)
166
+ return;
167
+ if (modelToDelete.type === 'provider' && modelToDelete.providerKey) {
168
+ removeModelFromProvider(modelToDelete.providerKey, modelToDelete.modelName);
169
+ toast({
170
+ title: 'Model Deleted',
171
+ description: `Successfully removed "${modelToDelete.modelName}" from the provider.`,
172
+ variant: 'default',
173
+ });
174
+ }
175
+ else if (modelToDelete.type === 'custom') {
176
+ removeCustomModel(modelToDelete.modelName);
177
+ toast({
178
+ title: 'Custom Model Deleted',
179
+ description: `Successfully removed "${modelToDelete.modelName}" from custom models.`,
180
+ variant: 'default',
181
+ });
182
+ }
183
+ setModelToDelete(null);
184
+ closeDeleteModelDialog();
185
+ };
186
+ return (_jsxs("div", { className: `space-y-2 ${className}`, children: [_jsxs("label", { className: "text-md flex items-center gap-2 pb-4 font-medium", children: [_jsx(Blocks, { className: "h-4 w-4" }), "Models"] }), _jsxs("div", { className: "w-full space-y-4", children: [providers.map(([providerKey, provider]) => (_jsxs("div", { className: "flex w-full items-start gap-4", children: [_jsx("div", { className: "flex w-20 items-start justify-start text-sm font-medium", children: providerKey.charAt(0).toUpperCase() + providerKey.slice(1) }), _jsxs("div", { className: "flex flex-1 flex-col items-start gap-2", children: [provider.models.map((m) => (_jsxs("div", { className: "flex w-full items-center justify-between gap-2 border-b p-1 text-xs", children: [_jsx("span", { className: "text-foreground/90", children: m.modelName }), allowEditProviderModels && (_jsx(Button, { size: "xs", variant: "ghost", onClick: () => handleDeleteModel('provider', providerKey, m.modelName), className: "h-6 w-6 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700", children: _jsx(Trash2, { className: "h-3 w-3" }) }))] }, m.modelName))), allowEditProviderModels && (_jsxs(Button, { size: "xs", variant: "secondary", onClick: () => handleOpenAddProviderModelDialog(providerKey), children: [_jsx(CirclePlus, { className: "h-3 w-3" }), "Add"] }))] }), _jsx("div", { className: "flex w-12 items-start justify-end" })] }, providerKey))), allowCustomModels && (_jsxs("div", { className: "flex w-full items-start gap-4", children: [_jsx("div", { className: "flex w-20 items-start justify-start text-sm font-medium", children: "Custom" }), _jsxs("div", { className: "flex flex-1 flex-col items-start gap-2", children: [customModels.map((cm) => (_jsxs("div", { className: "flex w-full items-center justify-between gap-2 border-b p-1 text-xs", children: [_jsx("span", { className: "text-foreground/90", children: cm.modelName }), _jsxs("div", { className: "flex w-full items-center justify-end", children: [_jsx(Button, { size: "xs", variant: "ghost", onClick: () => handleDeleteModel('custom', undefined, cm.modelName), className: "h-6 w-6 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700", children: _jsx(Trash2, { className: "h-3 w-3" }) }), _jsx(Button, { size: "xs", variant: "ghost", onClick: () => handleEditCustomModel(cm.modelName), children: _jsx(Settings, { className: "h-3 w-3" }) })] })] }, cm.modelName))), _jsxs(Button, { size: "xs", variant: "secondary", onClick: () => {
187
+ setCustomName('');
188
+ setCustomBaseUrl('');
189
+ setCustomApiKey('');
190
+ setBaseUrlError(false);
191
+ openCustomModelDialog();
192
+ }, children: [_jsx(CirclePlus, { className: "h-3 w-3" }), "Add"] })] }), _jsx("div", { className: "flex w-12 items-start justify-end" })] }))] }), allowCustomModels && (_jsxs(Dialog, { open: isCustomModelDialogOpen, onOpenChange: (open) => {
193
+ if (open) {
194
+ openCustomModelDialog();
195
+ }
196
+ else {
197
+ setBaseUrlError(false);
198
+ closeCustomModelDialog();
199
+ }
200
+ }, children: [_jsx(DialogTrigger, { asChild: true }), _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Blocks, { className: "h-4 w-4" }), " Add Custom Model"] }), _jsx(DialogDescription, { className: "text-xs", children: "Provide connection details for your model provider." })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "custom-name", className: "w-20 text-sm", children: "Name" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Cpu, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "custom-name", value: customName, onChange: (e) => setCustomName(e.target.value), placeholder: "e.g., My cool model", className: "pl-8" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "custom-baseUrl", className: "w-20 text-sm", children: "baseUrl" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Server, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "custom-baseUrl", value: customBaseUrl, onChange: (e) => {
201
+ setCustomBaseUrl(e.target.value);
202
+ if (baseUrlError && e.target.value.trim()) {
203
+ setBaseUrlError(false);
204
+ }
205
+ }, placeholder: "https://api.example.com", className: `pl-8 ${baseUrlError ? 'border-red-500 focus:border-red-500' : ''}` })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "custom-apiKey", className: "w-20 text-sm", children: "API key" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Key, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "custom-apiKey", type: "password", value: customApiKey, onChange: (e) => setCustomApiKey(e.target.value), placeholder: "Optional", className: "pl-8" })] })] }), _jsx("div", { className: "flex justify-end pt-1", children: _jsxs(Button, { size: "sm", onClick: handleAddCustomToSession, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Add"] }) })] })] })] })), _jsxs(Dialog, { open: isAddProviderModelDialogOpen, onOpenChange: (open) => open ? openAddProviderModelDialog() : closeAddProviderModelDialog(), children: [_jsx(DialogTrigger, { asChild: true }), _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Blocks, { className: "h-4 w-4" }), " Add Model to Provider"] }), _jsxs(DialogDescription, { className: "text-xs", children: ["Add a new model to", ' ', selectedProviderKey &&
206
+ selectedProviderKey.charAt(0).toUpperCase() +
207
+ selectedProviderKey.slice(1)] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "new-model-name", className: "w-24 text-sm", children: "Model Name" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Cpu, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "new-model-name", value: newModelName, onChange: (e) => setNewModelName(e.target.value), placeholder: "e.g., gpt-4-turbo", className: "pl-8" })] })] }), _jsx("div", { className: "flex justify-end pt-1", children: _jsxs(Button, { size: "sm", onClick: handleAddModelToProvider, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Add Model"] }) })] })] })] }), allowCustomModels && (_jsxs(Dialog, { open: isEditCustomModelDialogOpen, onOpenChange: (open) => {
208
+ if (open) {
209
+ openEditCustomModelDialog();
210
+ }
211
+ else {
212
+ setEditBaseUrlError(false);
213
+ closeEditCustomModelDialog();
214
+ }
215
+ }, children: [_jsx(DialogTrigger, { asChild: true }), _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Settings, { className: "h-4 w-4" }), " Edit Custom Model"] }), _jsx(DialogDescription, { className: "text-xs", children: "Update the connection details for your custom model." })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "edit-model-name", className: "w-20 text-sm", children: "Name" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Cpu, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "edit-model-name", value: editingModel?.modelName || '', onChange: (e) => setEditingModel((prev) => prev ? { ...prev, modelName: e.target.value } : null), placeholder: "e.g., My cool model", className: "pl-8" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "edit-model-baseUrl", className: "w-20 text-sm", children: "baseUrl" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Server, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "edit-model-baseUrl", value: editingModel?.baseUrl || '', onChange: (e) => {
216
+ setEditingModel((prev) => prev ? { ...prev, baseUrl: e.target.value } : null);
217
+ if (editBaseUrlError && e.target.value.trim()) {
218
+ setEditBaseUrlError(false);
219
+ }
220
+ }, placeholder: "https://api.example.com", className: `pl-8 ${editBaseUrlError ? 'border-red-500 focus:border-red-500' : ''}` })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "edit-model-apiKey", className: "w-20 text-sm", children: "API key" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Key, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "edit-model-apiKey", type: "password", value: editingModel?.apiKey || '', onChange: (e) => setEditingModel((prev) => prev ? { ...prev, apiKey: e.target.value } : null), placeholder: "Optional", className: "pl-8" })] })] }), _jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => {
221
+ setEditingModel(null);
222
+ setEditBaseUrlError(false);
223
+ closeEditCustomModelDialog();
224
+ }, children: "Cancel" }), _jsxs(Button, { size: "sm", onClick: handleUpdateCustomModel, children: [_jsx(Settings, { className: "mr-2 h-4 w-4" }), " Update"] })] })] })] })] })), (allowEditProviderModels || allowCustomModels) && (_jsxs(Dialog, { open: isDeleteModelDialogOpen, onOpenChange: (open) => open ? openDeleteModelDialog() : closeDeleteModelDialog(), children: [_jsx(DialogTrigger, { asChild: true }), _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Trash2, { className: "h-4 w-4 text-red-500" }), " Delete Model"] }), _jsxs(DialogDescription, { className: "text-xs", children: ["Are you sure you want to delete \"", modelToDelete?.modelName, "\"? This action cannot be undone."] })] }), _jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => {
225
+ setModelToDelete(null);
226
+ closeDeleteModelDialog();
227
+ }, children: "Cancel" }), _jsxs(Button, { size: "sm", variant: "destructive", onClick: confirmDeleteModel, children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), " Delete"] })] })] })] }))] }));
228
+ };
229
+ //# sourceMappingURL=AiModelsSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiModelsSettings.js","sourceRoot":"","sources":["../../src/components/AiModelsSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {useStoreWithAi} from '@sqlrooms/ai-core';\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n Input,\n Label,\n useDisclosure,\n useToast,\n} from '@sqlrooms/ui';\nimport {\n Blocks,\n CirclePlus,\n Cpu,\n Key,\n Plus,\n Server,\n Settings,\n Trash2,\n} from 'lucide-react';\nimport {FC, useMemo, useState} from 'react';\nimport {useStoreWithAiSettings} from '../AiSettingsSlice';\n\nexport interface AiModelsSettingsProps {\n showProviderModels?: boolean;\n showCustomModels?: boolean;\n allowEditProviderModels?: boolean;\n allowCustomModels?: boolean;\n className?: string;\n}\n\nexport const AiModelsSettings: FC<AiModelsSettingsProps> = ({\n className = '',\n allowEditProviderModels = true,\n allowCustomModels = true,\n}) => {\n const {toast} = useToast();\n const aiConfig = useStoreWithAiSettings((s) => s.aiSettings.config);\n const setAiModel = useStoreWithAi((s) => s.ai.setAiModel);\n const addModelToProvider = useStoreWithAiSettings(\n (s) => s.aiSettings.addModelToProvider,\n );\n const removeModelFromProvider = useStoreWithAiSettings(\n (s) => s.aiSettings.removeModelFromProvider,\n );\n const addCustomModel = useStoreWithAiSettings(\n (s) => s.aiSettings.addCustomModel,\n );\n const updateCustomModel = useStoreWithAiSettings(\n (s) => s.aiSettings.updateCustomModel,\n );\n const removeCustomModel = useStoreWithAiSettings(\n (s) => s.aiSettings.removeCustomModel,\n );\n\n const providers = useMemo(\n () => Object.entries(aiConfig.providers),\n [aiConfig.providers],\n );\n const customModels = aiConfig.customModels || [];\n\n // Dialog state for adding a custom model\n const {\n isOpen: isCustomModelDialogOpen,\n onOpen: openCustomModelDialog,\n onClose: closeCustomModelDialog,\n } = useDisclosure();\n const [customName, setCustomName] = useState('');\n const [customBaseUrl, setCustomBaseUrl] = useState('');\n const [customApiKey, setCustomApiKey] = useState('');\n const [baseUrlError, setBaseUrlError] = useState(false);\n\n // Dialog state for editing a custom model\n const {\n isOpen: isEditCustomModelDialogOpen,\n onOpen: openEditCustomModelDialog,\n onClose: closeEditCustomModelDialog,\n } = useDisclosure();\n const [editingModel, setEditingModel] = useState<{\n oldModelName: string;\n modelName: string;\n baseUrl: string;\n apiKey: string;\n } | null>(null);\n const [editBaseUrlError, setEditBaseUrlError] = useState(false);\n\n // Dialog state for adding a model to a provider\n const {\n isOpen: isAddProviderModelDialogOpen,\n onOpen: openAddProviderModelDialog,\n onClose: closeAddProviderModelDialog,\n } = useDisclosure();\n const [selectedProviderKey, setSelectedProviderKey] = useState('');\n const [newModelName, setNewModelName] = useState('');\n\n // Dialog state for deleting a model\n const {\n isOpen: isDeleteModelDialogOpen,\n onOpen: openDeleteModelDialog,\n onClose: closeDeleteModelDialog,\n } = useDisclosure();\n const [modelToDelete, setModelToDelete] = useState<{\n type: 'provider' | 'custom';\n providerKey?: string;\n modelName: string;\n } | null>(null);\n\n const handleAddCustomToSession = () => {\n // Reset error states\n setBaseUrlError(false);\n\n // Validate required fields\n if (!customName.trim()) {\n toast({\n title: 'Model Name Required',\n description: 'Please enter a model name.',\n variant: 'destructive',\n });\n return;\n }\n\n if (!customBaseUrl.trim()) {\n setBaseUrlError(true);\n toast({\n title: 'Base URL Required',\n description: 'Please enter a base URL for the model.',\n variant: 'destructive',\n });\n return;\n }\n\n const trimmedName = customName.trim();\n\n // Add the custom model to the config\n addCustomModel(customBaseUrl.trim(), customApiKey.trim(), trimmedName);\n\n // Update the current session to use this custom model\n setAiModel('custom', trimmedName);\n\n // Show success message\n toast({\n title: 'Custom Model Added',\n description: `Successfully added \"${trimmedName}\" to your custom models.`,\n variant: 'default',\n });\n\n // Reset form\n setCustomName('');\n setCustomBaseUrl('');\n setCustomApiKey('');\n setBaseUrlError(false);\n closeCustomModelDialog();\n };\n\n const handleEditCustomModel = (modelName: string) => {\n const customModel = customModels.find((cm) => cm.modelName === modelName);\n if (customModel) {\n setEditingModel({\n oldModelName: modelName,\n modelName: modelName,\n baseUrl: customModel.baseUrl,\n apiKey: customModel.apiKey,\n });\n setEditBaseUrlError(false);\n openEditCustomModelDialog();\n }\n };\n\n const handleUpdateCustomModel = () => {\n if (!editingModel) return;\n\n // Reset error states\n setEditBaseUrlError(false);\n\n // Validate required fields\n if (!editingModel.modelName.trim()) {\n toast({\n title: 'Model Name Required',\n description: 'Please enter a model name.',\n variant: 'destructive',\n });\n return;\n }\n\n if (!editingModel.baseUrl.trim()) {\n setEditBaseUrlError(true);\n toast({\n title: 'Base URL Required',\n description: 'Please enter a base URL for the model.',\n variant: 'destructive',\n });\n return;\n }\n\n const trimmedName = editingModel.modelName.trim();\n const trimmedBaseUrl = editingModel.baseUrl.trim();\n const trimmedApiKey = editingModel.apiKey.trim();\n\n // Check for duplicate model names (excluding the current model being edited)\n const duplicateModel = customModels.find(\n (cm) =>\n cm.modelName.toLowerCase() === trimmedName.toLowerCase() &&\n cm.modelName !== editingModel.oldModelName,\n );\n\n if (duplicateModel) {\n toast({\n title: 'Model Name Already Exists',\n description: `A custom model with the name \"${trimmedName}\" already exists.`,\n variant: 'destructive',\n });\n return;\n }\n\n // Update the custom model\n updateCustomModel(\n editingModel.oldModelName,\n trimmedBaseUrl,\n trimmedApiKey,\n trimmedName,\n );\n\n // Show success message\n toast({\n title: 'Custom Model Updated',\n description: `Successfully updated \"${trimmedName}\".`,\n variant: 'default',\n });\n\n // Reset form and close dialog\n setEditingModel(null);\n setEditBaseUrlError(false);\n closeEditCustomModelDialog();\n };\n\n const handleOpenAddProviderModelDialog = (providerKey: string) => {\n setSelectedProviderKey(providerKey);\n setNewModelName('');\n openAddProviderModelDialog();\n };\n\n const handleAddModelToProvider = () => {\n if (!selectedProviderKey || !newModelName.trim()) return;\n\n // Check for duplicates\n const provider = aiConfig.providers[selectedProviderKey];\n const modelExists = provider?.models.some(\n (model) =>\n model.modelName.toLowerCase() === newModelName.trim().toLowerCase(),\n );\n\n if (modelExists) {\n toast({\n title: 'Model Already Exists',\n description: `The model \"${newModelName.trim()}\" already exists in this provider.`,\n variant: 'destructive',\n });\n return;\n }\n\n addModelToProvider(selectedProviderKey, newModelName.trim());\n setNewModelName('');\n closeAddProviderModelDialog();\n };\n\n const handleDeleteModel = (\n type: 'provider' | 'custom',\n providerKey: string | undefined,\n modelName: string,\n ) => {\n setModelToDelete({type, providerKey, modelName});\n openDeleteModelDialog();\n };\n\n const confirmDeleteModel = () => {\n if (!modelToDelete) return;\n\n if (modelToDelete.type === 'provider' && modelToDelete.providerKey) {\n removeModelFromProvider(\n modelToDelete.providerKey,\n modelToDelete.modelName,\n );\n toast({\n title: 'Model Deleted',\n description: `Successfully removed \"${modelToDelete.modelName}\" from the provider.`,\n variant: 'default',\n });\n } else if (modelToDelete.type === 'custom') {\n removeCustomModel(modelToDelete.modelName);\n toast({\n title: 'Custom Model Deleted',\n description: `Successfully removed \"${modelToDelete.modelName}\" from custom models.`,\n variant: 'default',\n });\n }\n\n setModelToDelete(null);\n closeDeleteModelDialog();\n };\n\n return (\n <div className={`space-y-2 ${className}`}>\n <label className=\"text-md flex items-center gap-2 pb-4 font-medium\">\n <Blocks className=\"h-4 w-4\" />\n Models\n </label>\n\n {/* Providers and their models */}\n <div className=\"w-full space-y-4\">\n {providers.map(([providerKey, provider]) => (\n <div key={providerKey} className=\"flex w-full items-start gap-4\">\n {/* Provider name column - 30% */}\n <div className=\"flex w-20 items-start justify-start text-sm font-medium\">\n {providerKey.charAt(0).toUpperCase() + providerKey.slice(1)}\n </div>\n\n {/* Models column - 65% (fills the rest) */}\n <div className=\"flex flex-1 flex-col items-start gap-2\">\n {provider.models.map((m) => (\n <div\n key={m.modelName}\n className=\"flex w-full items-center justify-between gap-2 border-b p-1 text-xs\"\n >\n <span className=\"text-foreground/90\">{m.modelName}</span>\n {allowEditProviderModels && (\n <Button\n size=\"xs\"\n variant=\"ghost\"\n onClick={() =>\n handleDeleteModel('provider', providerKey, m.modelName)\n }\n className=\"h-6 w-6 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n )}\n </div>\n ))}\n {allowEditProviderModels && (\n <Button\n size=\"xs\"\n variant=\"secondary\"\n onClick={() => handleOpenAddProviderModelDialog(providerKey)}\n >\n <CirclePlus className=\"h-3 w-3\" />\n Add\n </Button>\n )}\n </div>\n\n {/* add model button column - 5% */}\n <div className=\"flex w-12 items-start justify-end\"></div>\n </div>\n ))}\n\n {/* Custom models */}\n {allowCustomModels && (\n <div className=\"flex w-full items-start gap-4\">\n <div className=\"flex w-20 items-start justify-start text-sm font-medium\">\n Custom\n </div>\n <div className=\"flex flex-1 flex-col items-start gap-2\">\n {customModels.map((cm) => (\n <div\n key={cm.modelName}\n className=\"flex w-full items-center justify-between gap-2 border-b p-1 text-xs\"\n >\n <span className=\"text-foreground/90\">{cm.modelName}</span>\n <div className=\"flex w-full items-center justify-end\">\n <Button\n size=\"xs\"\n variant=\"ghost\"\n onClick={() =>\n handleDeleteModel('custom', undefined, cm.modelName)\n }\n className=\"h-6 w-6 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n onClick={() => handleEditCustomModel(cm.modelName)}\n >\n <Settings className=\"h-3 w-3\" />\n </Button>\n </div>\n </div>\n ))}\n <Button\n size=\"xs\"\n variant=\"secondary\"\n onClick={() => {\n setCustomName('');\n setCustomBaseUrl('');\n setCustomApiKey('');\n setBaseUrlError(false);\n openCustomModelDialog();\n }}\n >\n <CirclePlus className=\"h-3 w-3\" />\n Add\n </Button>\n </div>\n <div className=\"flex w-12 items-start justify-end\"></div>\n </div>\n )}\n </div>\n\n {/* Add Custom Model Dialog */}\n {allowCustomModels && (\n <Dialog\n open={isCustomModelDialogOpen}\n onOpenChange={(open) => {\n if (open) {\n openCustomModelDialog();\n } else {\n setBaseUrlError(false);\n closeCustomModelDialog();\n }\n }}\n >\n <DialogTrigger asChild>\n {/* handled via onOpen button above */}\n </DialogTrigger>\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"flex items-center gap-2 text-base\">\n <Blocks className=\"h-4 w-4\" /> Add Custom Model\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n Provide connection details for your model provider.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"custom-name\" className=\"w-20 text-sm\">\n Name\n </Label>\n <div className=\"relative flex-1\">\n <Cpu className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"custom-name\"\n value={customName}\n onChange={(e) => setCustomName(e.target.value)}\n placeholder=\"e.g., My cool model\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"custom-baseUrl\" className=\"w-20 text-sm\">\n baseUrl\n </Label>\n <div className=\"relative flex-1\">\n <Server className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"custom-baseUrl\"\n value={customBaseUrl}\n onChange={(e) => {\n setCustomBaseUrl(e.target.value);\n if (baseUrlError && e.target.value.trim()) {\n setBaseUrlError(false);\n }\n }}\n placeholder=\"https://api.example.com\"\n className={`pl-8 ${baseUrlError ? 'border-red-500 focus:border-red-500' : ''}`}\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"custom-apiKey\" className=\"w-20 text-sm\">\n API key\n </Label>\n <div className=\"relative flex-1\">\n <Key className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"custom-apiKey\"\n type=\"password\"\n value={customApiKey}\n onChange={(e) => setCustomApiKey(e.target.value)}\n placeholder=\"Optional\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button size=\"sm\" onClick={handleAddCustomToSession}>\n <Plus className=\"mr-2 h-4 w-4\" /> Add\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n\n {/* Add Model to Provider Dialog */}\n <Dialog\n open={isAddProviderModelDialogOpen}\n onOpenChange={(open) =>\n open ? openAddProviderModelDialog() : closeAddProviderModelDialog()\n }\n >\n <DialogTrigger asChild>\n {/* handled via onOpen button above */}\n </DialogTrigger>\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"flex items-center gap-2 text-base\">\n <Blocks className=\"h-4 w-4\" /> Add Model to Provider\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n Add a new model to{' '}\n {selectedProviderKey &&\n selectedProviderKey.charAt(0).toUpperCase() +\n selectedProviderKey.slice(1)}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"new-model-name\" className=\"w-24 text-sm\">\n Model Name\n </Label>\n <div className=\"relative flex-1\">\n <Cpu className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"new-model-name\"\n value={newModelName}\n onChange={(e) => setNewModelName(e.target.value)}\n placeholder=\"e.g., gpt-4-turbo\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button size=\"sm\" onClick={handleAddModelToProvider}>\n <Plus className=\"mr-2 h-4 w-4\" /> Add Model\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Edit Custom Model Dialog */}\n {allowCustomModels && (\n <Dialog\n open={isEditCustomModelDialogOpen}\n onOpenChange={(open) => {\n if (open) {\n openEditCustomModelDialog();\n } else {\n setEditBaseUrlError(false);\n closeEditCustomModelDialog();\n }\n }}\n >\n <DialogTrigger asChild>\n {/* handled via onOpen button above */}\n </DialogTrigger>\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"flex items-center gap-2 text-base\">\n <Settings className=\"h-4 w-4\" /> Edit Custom Model\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n Update the connection details for your custom model.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"edit-model-name\" className=\"w-20 text-sm\">\n Name\n </Label>\n <div className=\"relative flex-1\">\n <Cpu className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"edit-model-name\"\n value={editingModel?.modelName || ''}\n onChange={(e) =>\n setEditingModel((prev) =>\n prev ? {...prev, modelName: e.target.value} : null,\n )\n }\n placeholder=\"e.g., My cool model\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"edit-model-baseUrl\" className=\"w-20 text-sm\">\n baseUrl\n </Label>\n <div className=\"relative flex-1\">\n <Server className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"edit-model-baseUrl\"\n value={editingModel?.baseUrl || ''}\n onChange={(e) => {\n setEditingModel((prev) =>\n prev ? {...prev, baseUrl: e.target.value} : null,\n );\n if (editBaseUrlError && e.target.value.trim()) {\n setEditBaseUrlError(false);\n }\n }}\n placeholder=\"https://api.example.com\"\n className={`pl-8 ${editBaseUrlError ? 'border-red-500 focus:border-red-500' : ''}`}\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"edit-model-apiKey\" className=\"w-20 text-sm\">\n API key\n </Label>\n <div className=\"relative flex-1\">\n <Key className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"edit-model-apiKey\"\n type=\"password\"\n value={editingModel?.apiKey || ''}\n onChange={(e) =>\n setEditingModel((prev) =>\n prev ? {...prev, apiKey: e.target.value} : null,\n )\n }\n placeholder=\"Optional\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setEditingModel(null);\n setEditBaseUrlError(false);\n closeEditCustomModelDialog();\n }}\n >\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleUpdateCustomModel}>\n <Settings className=\"mr-2 h-4 w-4\" /> Update\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )}\n\n {/* Delete Model Confirmation Dialog */}\n {(allowEditProviderModels || allowCustomModels) && (\n <Dialog\n open={isDeleteModelDialogOpen}\n onOpenChange={(open) =>\n open ? openDeleteModelDialog() : closeDeleteModelDialog()\n }\n >\n <DialogTrigger asChild>\n {/* handled via onOpen button above */}\n </DialogTrigger>\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"flex items-center gap-2 text-base\">\n <Trash2 className=\"h-4 w-4 text-red-500\" /> Delete Model\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n Are you sure you want to delete &quot;{modelToDelete?.modelName}\n &quot;? This action cannot be undone.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"flex justify-end gap-2 pt-1\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setModelToDelete(null);\n closeDeleteModelDialog();\n }}\n >\n Cancel\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n onClick={confirmDeleteModel}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" /> Delete\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )}\n </div>\n );\n};\n"]}
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+ export declare const AiProvidersSettings: FC;
3
+ //# sourceMappingURL=AiProvidersSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiProvidersSettings.d.ts","sourceRoot":"","sources":["../../src/components/AiProvidersSettings.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAC,EAAE,EAAW,MAAM,OAAO,CAAC;AA0B1C,eAAO,MAAM,mBAAmB,EAAE,EAgUjC,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState } from 'react';
3
+ import { Button, Input, Label, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger, DialogFooter, useDisclosure, useToast, } from '@sqlrooms/ui';
4
+ import { Cone, Settings, Plus, Server, Key, Trash2, CirclePlus, } from 'lucide-react';
5
+ import { useStoreWithAiSettings } from '../AiSettingsSlice';
6
+ export const AiProvidersSettings = () => {
7
+ const { toast } = useToast();
8
+ const updateProvider = useStoreWithAiSettings((state) => state.aiSettings.updateProvider);
9
+ const addProvider = useStoreWithAiSettings((state) => state.aiSettings.addProvider);
10
+ const removeProvider = useStoreWithAiSettings((state) => state.aiSettings.removeProvider);
11
+ const providers = useStoreWithAiSettings((state) => state.aiSettings.config.providers);
12
+ const modelProviders = React.useMemo(() => {
13
+ const result = {};
14
+ Object.entries(providers).forEach(([key, provider]) => {
15
+ result[key] = {
16
+ apiKey: provider.apiKey,
17
+ baseUrl: provider.baseUrl,
18
+ };
19
+ });
20
+ return result;
21
+ }, [providers]);
22
+ const [expandedProviders, setExpandedProviders] = useState(new Set());
23
+ // Dialog state for adding a new provider
24
+ const { isOpen, onOpen, onClose } = useDisclosure();
25
+ const [newProviderKey, setNewProviderKey] = useState('');
26
+ const [newProviderApiKey, setNewProviderApiKey] = useState('');
27
+ const [newProviderBaseUrl, setNewProviderBaseUrl] = useState('');
28
+ // Dialog state for delete confirmation
29
+ const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose, } = useDisclosure();
30
+ const [providerToDelete, setProviderToDelete] = useState(null);
31
+ const handleAddProvider = () => {
32
+ if (newProviderKey) {
33
+ // Check if provider already exists
34
+ if (providers[newProviderKey]) {
35
+ toast({
36
+ title: 'Provider already exists',
37
+ description: `A provider with the key "${newProviderKey}" already exists. Please choose a different key.`,
38
+ variant: 'destructive',
39
+ });
40
+ return;
41
+ }
42
+ addProvider(newProviderKey, newProviderBaseUrl, newProviderApiKey);
43
+ setNewProviderKey('');
44
+ setNewProviderApiKey('');
45
+ setNewProviderBaseUrl('');
46
+ onClose();
47
+ toast({
48
+ title: 'Provider added successfully',
49
+ description: `Provider "${newProviderKey}" has been added.`,
50
+ });
51
+ }
52
+ };
53
+ const handleUpdateProvider = (providerKey, field, value) => {
54
+ updateProvider(providerKey, { [field]: value });
55
+ };
56
+ const handleDeleteProvider = (providerKey) => {
57
+ setProviderToDelete({ key: providerKey });
58
+ onDeleteOpen();
59
+ };
60
+ const confirmDeleteProvider = () => {
61
+ if (providerToDelete) {
62
+ removeProvider(providerToDelete.key);
63
+ onDeleteClose();
64
+ setProviderToDelete(null);
65
+ toast({
66
+ title: 'Provider deleted successfully',
67
+ description: `Provider "${providerToDelete.key}" and its associated models have been removed.`,
68
+ });
69
+ }
70
+ };
71
+ const toggleProviderExpanded = (providerKey) => {
72
+ setExpandedProviders((prev) => {
73
+ const newSet = new Set(prev);
74
+ if (newSet.has(providerKey)) {
75
+ newSet.delete(providerKey);
76
+ }
77
+ else {
78
+ newSet.add(providerKey);
79
+ }
80
+ return newSet;
81
+ });
82
+ };
83
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-md flex items-center gap-2 pb-6 font-medium", children: [_jsx(Cone, { className: "h-4 w-4" }), "Providers"] }), _jsx("div", { className: "space-y-1", children: Object.entries(modelProviders).map(([providerKey, provider]) => {
84
+ const isExpanded = expandedProviders.has(providerKey);
85
+ return (_jsxs("div", { className: "space-y-0 rounded-lg p-0", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { className: "w-20 flex-shrink-0 text-sm", children: providerKey.charAt(0).toUpperCase() + providerKey.slice(1) }), _jsx(Input, { id: `${providerKey}-apiKey`, type: "password", value: provider.apiKey, onChange: (e) => handleUpdateProvider(providerKey, 'apiKey', e.target.value), placeholder: "Enter API key", className: "flex-1" }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteProvider(providerKey), className: "h-6 h-8 w-6 w-8 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700", children: _jsx(Trash2, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => toggleProviderExpanded(providerKey), className: "h-8 w-8 flex-shrink-0", children: _jsx(Settings, { className: "h-4 w-4" }) })] }), isExpanded && (_jsxs("div", { className: "flex items-center gap-3 pt-1", children: [_jsx("div", { className: "w-20 flex-shrink-0" }), ' ', _jsxs("div", { className: "flex flex-1 items-center gap-3", children: [_jsx(Label, { htmlFor: `${providerKey}-baseUrl`, className: "text-muted-foreground flex-shrink-0 text-xs", children: "baseUrl:" }), _jsx(Input, { id: `${providerKey}-baseUrl`, type: "url", value: provider.baseUrl, onChange: (e) => handleUpdateProvider(providerKey, 'baseUrl', e.target.value), placeholder: "Enter base URL", className: "flex-1" })] }), _jsx("div", { className: "w-16 flex-shrink-0" }), ' '] }))] }, providerKey));
86
+ }) }), _jsx("div", { className: "flex w-full p-2", children: _jsxs(Button, { onClick: onOpen, variant: "secondary", size: "sm", children: [_jsx(CirclePlus, { className: "h-3 w-3" }), "Add"] }) }), _jsxs(Dialog, { open: isOpen, onOpenChange: (open) => (open ? onOpen() : onClose()), children: [_jsx(DialogTrigger, { asChild: true }), _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Cone, { className: "h-4 w-4" }), " Add New Provider"] }), _jsx(DialogDescription, { className: "text-xs", children: "Configure a new model provider with API credentials." })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "new-provider-key", className: "w-20 text-sm", children: "Key" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Cone, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "new-provider-key", value: newProviderKey, onChange: (e) => setNewProviderKey(e.target.value), placeholder: "e.g., anthropic", className: "pl-8" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "new-provider-api-key", className: "w-20 text-sm", children: "API Key" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Key, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "new-provider-api-key", type: "password", value: newProviderApiKey, onChange: (e) => setNewProviderApiKey(e.target.value), placeholder: "Enter API key", className: "pl-8" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Label, { htmlFor: "new-provider-url", className: "w-20 text-sm", children: "baseUrl" }), _jsxs("div", { className: "relative flex-1", children: [_jsx(Server, { className: "absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { id: "new-provider-url", type: "url", value: newProviderBaseUrl, onChange: (e) => setNewProviderBaseUrl(e.target.value), placeholder: "Enter base URL", className: "pl-8" })] })] }), _jsx("div", { className: "flex justify-end pt-1", children: _jsxs(Button, { size: "sm", onClick: handleAddProvider, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Add Provider"] }) })] })] })] }), _jsx(Dialog, { open: isDeleteOpen, onOpenChange: (open) => (open ? onDeleteOpen() : onDeleteClose()), children: _jsxs(DialogContent, { className: "border-0 p-5", children: [_jsxs(DialogHeader, { className: "mb-1", children: [_jsxs(DialogTitle, { className: "text-destructive flex items-center gap-2 text-base", children: [_jsx(Trash2, { className: "h-4 w-4" }), " Delete Provider"] }), _jsx(DialogDescription, { className: "text-xs", children: "This action cannot be undone. This will permanently delete the provider and all its associated models." })] }), _jsxs("div", { className: "py-2", children: [_jsxs("p", { className: "text-sm", children: ["Are you sure you want to delete the provider", ' ', _jsxs("strong", { children: ["\u201C", providerToDelete?.key, "\u201D"] }), "?"] }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: "All models associated with this provider will also be removed." })] }), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: onDeleteClose, children: "Cancel" }), _jsxs(Button, { variant: "destructive", size: "sm", onClick: confirmDeleteProvider, children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), "Delete Provider"] })] })] }) })] }));
87
+ };
88
+ //# sourceMappingURL=AiProvidersSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiProvidersSettings.js","sourceRoot":"","sources":["../../src/components/AiProvidersSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {FC, useState} from 'react';\nimport {\n Button,\n Input,\n Label,\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogTrigger,\n DialogFooter,\n useDisclosure,\n useToast,\n} from '@sqlrooms/ui';\nimport {\n Cone,\n Settings,\n Plus,\n Server,\n Key,\n Trash2,\n CirclePlus,\n} from 'lucide-react';\nimport {useStoreWithAiSettings} from '../AiSettingsSlice';\n\nexport const AiProvidersSettings: FC = () => {\n const {toast} = useToast();\n const updateProvider = useStoreWithAiSettings(\n (state) => state.aiSettings.updateProvider,\n );\n const addProvider = useStoreWithAiSettings(\n (state) => state.aiSettings.addProvider,\n );\n const removeProvider = useStoreWithAiSettings(\n (state) => state.aiSettings.removeProvider,\n );\n const providers = useStoreWithAiSettings(\n (state) => state.aiSettings.config.providers,\n );\n const modelProviders = React.useMemo(() => {\n const result: Record<string, {apiKey: string; baseUrl: string}> = {};\n Object.entries(providers).forEach(([key, provider]) => {\n result[key] = {\n apiKey: provider.apiKey,\n baseUrl: provider.baseUrl,\n };\n });\n return result;\n }, [providers]);\n\n const [expandedProviders, setExpandedProviders] = useState<Set<string>>(\n new Set(),\n );\n\n // Dialog state for adding a new provider\n const {isOpen, onOpen, onClose} = useDisclosure();\n const [newProviderKey, setNewProviderKey] = useState('');\n const [newProviderApiKey, setNewProviderApiKey] = useState('');\n const [newProviderBaseUrl, setNewProviderBaseUrl] = useState('');\n\n // Dialog state for delete confirmation\n const {\n isOpen: isDeleteOpen,\n onOpen: onDeleteOpen,\n onClose: onDeleteClose,\n } = useDisclosure();\n const [providerToDelete, setProviderToDelete] = useState<{\n key: string;\n } | null>(null);\n\n const handleAddProvider = () => {\n if (newProviderKey) {\n // Check if provider already exists\n if (providers[newProviderKey]) {\n toast({\n title: 'Provider already exists',\n description: `A provider with the key \"${newProviderKey}\" already exists. Please choose a different key.`,\n variant: 'destructive',\n });\n return;\n }\n\n addProvider(newProviderKey, newProviderBaseUrl, newProviderApiKey);\n setNewProviderKey('');\n setNewProviderApiKey('');\n setNewProviderBaseUrl('');\n onClose();\n\n toast({\n title: 'Provider added successfully',\n description: `Provider \"${newProviderKey}\" has been added.`,\n });\n }\n };\n\n const handleUpdateProvider = (\n providerKey: string,\n field: 'apiKey' | 'baseUrl',\n value: string,\n ) => {\n updateProvider(providerKey, {[field]: value});\n };\n\n const handleDeleteProvider = (providerKey: string) => {\n setProviderToDelete({key: providerKey});\n onDeleteOpen();\n };\n\n const confirmDeleteProvider = () => {\n if (providerToDelete) {\n removeProvider(providerToDelete.key);\n onDeleteClose();\n setProviderToDelete(null);\n\n toast({\n title: 'Provider deleted successfully',\n description: `Provider \"${providerToDelete.key}\" and its associated models have been removed.`,\n });\n }\n };\n\n const toggleProviderExpanded = (providerKey: string) => {\n setExpandedProviders((prev) => {\n const newSet = new Set(prev);\n if (newSet.has(providerKey)) {\n newSet.delete(providerKey);\n } else {\n newSet.add(providerKey);\n }\n return newSet;\n });\n };\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-md flex items-center gap-2 pb-6 font-medium\">\n <Cone className=\"h-4 w-4\" />\n Providers\n </label>\n\n {/* Existing Providers */}\n <div className=\"space-y-1\">\n {Object.entries(modelProviders).map(([providerKey, provider]) => {\n const isExpanded = expandedProviders.has(providerKey);\n\n return (\n <div key={providerKey} className=\"space-y-0 rounded-lg p-0\">\n {/* First row: Provider name, API key input, delete button, and cogwheel button */}\n <div className=\"flex items-center gap-3\">\n <Label className=\"w-20 flex-shrink-0 text-sm\">\n {providerKey.charAt(0).toUpperCase() + providerKey.slice(1)}\n </Label>\n <Input\n id={`${providerKey}-apiKey`}\n type=\"password\"\n value={provider.apiKey}\n onChange={(e) =>\n handleUpdateProvider(providerKey, 'apiKey', e.target.value)\n }\n placeholder=\"Enter API key\"\n className=\"flex-1\"\n />\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => handleDeleteProvider(providerKey)}\n className=\"h-6 h-8 w-6 w-8 p-0 text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => toggleProviderExpanded(providerKey)}\n className=\"h-8 w-8 flex-shrink-0\"\n >\n <Settings className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Second row: baseUrl input (toggleable) */}\n {isExpanded && (\n <div className=\"flex items-center gap-3 pt-1\">\n <div className=\"w-20 flex-shrink-0\" />{' '}\n {/* Spacer to align with provider name above */}\n <div className=\"flex flex-1 items-center gap-3\">\n <Label\n htmlFor={`${providerKey}-baseUrl`}\n className=\"text-muted-foreground flex-shrink-0 text-xs\"\n >\n baseUrl:\n </Label>\n <Input\n id={`${providerKey}-baseUrl`}\n type=\"url\"\n value={provider.baseUrl}\n onChange={(e) =>\n handleUpdateProvider(\n providerKey,\n 'baseUrl',\n e.target.value,\n )\n }\n placeholder=\"Enter base URL\"\n className=\"flex-1\"\n />\n </div>\n <div className=\"w-16 flex-shrink-0\" />{' '}\n {/* Spacer to align with delete and cogwheel buttons above */}\n </div>\n )}\n </div>\n );\n })}\n </div>\n\n {/* Add New Provider */}\n <div className=\"flex w-full p-2\">\n <Button onClick={onOpen} variant=\"secondary\" size=\"sm\">\n <CirclePlus className=\"h-3 w-3\" />\n Add\n </Button>\n </div>\n\n {/* Add New Provider Dialog */}\n <Dialog\n open={isOpen}\n onOpenChange={(open) => (open ? onOpen() : onClose())}\n >\n <DialogTrigger asChild>\n {/* handled via onOpen button above */}\n </DialogTrigger>\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"flex items-center gap-2 text-base\">\n <Cone className=\"h-4 w-4\" /> Add New Provider\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n Configure a new model provider with API credentials.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"new-provider-key\" className=\"w-20 text-sm\">\n Key\n </Label>\n <div className=\"relative flex-1\">\n <Cone className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"new-provider-key\"\n value={newProviderKey}\n onChange={(e) => setNewProviderKey(e.target.value)}\n placeholder=\"e.g., anthropic\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"new-provider-api-key\" className=\"w-20 text-sm\">\n API Key\n </Label>\n <div className=\"relative flex-1\">\n <Key className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"new-provider-api-key\"\n type=\"password\"\n value={newProviderApiKey}\n onChange={(e) => setNewProviderApiKey(e.target.value)}\n placeholder=\"Enter API key\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Label htmlFor=\"new-provider-url\" className=\"w-20 text-sm\">\n baseUrl\n </Label>\n <div className=\"relative flex-1\">\n <Server className=\"absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2\" />\n <Input\n id=\"new-provider-url\"\n type=\"url\"\n value={newProviderBaseUrl}\n onChange={(e) => setNewProviderBaseUrl(e.target.value)}\n placeholder=\"Enter base URL\"\n className=\"pl-8\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button size=\"sm\" onClick={handleAddProvider}>\n <Plus className=\"mr-2 h-4 w-4\" /> Add Provider\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Delete Confirmation Dialog */}\n <Dialog\n open={isDeleteOpen}\n onOpenChange={(open) => (open ? onDeleteOpen() : onDeleteClose())}\n >\n <DialogContent className=\"border-0 p-5\">\n <DialogHeader className=\"mb-1\">\n <DialogTitle className=\"text-destructive flex items-center gap-2 text-base\">\n <Trash2 className=\"h-4 w-4\" /> Delete Provider\n </DialogTitle>\n <DialogDescription className=\"text-xs\">\n This action cannot be undone. This will permanently delete the\n provider and all its associated models.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"py-2\">\n <p className=\"text-sm\">\n Are you sure you want to delete the provider{' '}\n <strong>&ldquo;{providerToDelete?.key}&rdquo;</strong>?\n </p>\n <p className=\"text-muted-foreground mt-1 text-xs\">\n All models associated with this provider will also be removed.\n </p>\n </div>\n\n <DialogFooter className=\"gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={onDeleteClose}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={confirmDeleteProvider}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete Provider\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n );\n};\n"]}
@@ -0,0 +1,17 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { UseDisclosureReturnValue } from '@sqlrooms/ui';
3
+ interface AiSettingsPanelProps {
4
+ disclosure: UseDisclosureReturnValue;
5
+ }
6
+ export declare const AiSettingsPanelBase: FC<PropsWithChildren<AiSettingsPanelProps>>;
7
+ export declare const AiSettingsPanel: FC<PropsWithChildren<AiSettingsPanelProps>> & {
8
+ ProvidersSettings: FC;
9
+ ModelsSettings: FC<import("./AiModelsSettings").AiModelsSettingsProps>;
10
+ ModelUsage: FC<{
11
+ className?: string;
12
+ modelUsage?: import("./AiModelUsage").ModelUsageData;
13
+ }>;
14
+ ModelParametersSettings: FC;
15
+ };
16
+ export {};
17
+ //# sourceMappingURL=AiSettingsPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiSettingsPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiSettingsPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,EAAE,EAAE,iBAAiB,EAAC,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAS,wBAAwB,EAAC,MAAM,cAAc,CAAC;AAQ9D,UAAU,oBAAoB;IAC5B,UAAU,EAAE,wBAAwB,CAAC;CACtC;AAED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAClC,iBAAiB,CAAC,oBAAoB,CAAC,CAoBxC,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;;;;CAK1B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@sqlrooms/ui';
3
+ import { X } from 'lucide-react';
4
+ import { AiModelParameters } from './AiModelParameters';
5
+ import { AiModelUsage } from './AiModelUsage';
6
+ import { AiProvidersSettings } from './AiProvidersSettings';
7
+ import { AiModelsSettings } from './AiModelsSettings';
8
+ export const AiSettingsPanelBase = ({ disclosure, children }) => {
9
+ const { isOpen, onClose } = disclosure;
10
+ if (!isOpen)
11
+ return null;
12
+ return (_jsx("div", { className: "bg-background border-border w-full rounded-lg border shadow-sm", children: _jsxs("div", { className: "relative flex flex-col gap-12 overflow-y-auto p-6", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: onClose, className: "absolute right-2 top-2 z-10", children: _jsx(X, { className: "h-4 w-4" }) }), children] }) }));
13
+ };
14
+ export const AiSettingsPanel = Object.assign(AiSettingsPanelBase, {
15
+ ProvidersSettings: AiProvidersSettings,
16
+ ModelsSettings: AiModelsSettings,
17
+ ModelUsage: AiModelUsage,
18
+ ModelParametersSettings: AiModelParameters,
19
+ });
20
+ //# sourceMappingURL=AiSettingsPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiSettingsPanel.js","sourceRoot":"","sources":["../../src/components/AiSettingsPanel.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,MAAM,EAA2B,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAC,CAAC,EAAC,MAAM,cAAc,CAAC;AAE/B,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAMpD,MAAM,CAAC,MAAM,mBAAmB,GAE5B,CAAC,EAAC,UAAU,EAAE,QAAQ,EAAC,EAAE,EAAE;IAC7B,MAAM,EAAC,MAAM,EAAE,OAAO,EAAC,GAAG,UAAU,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,CACL,cAAK,SAAS,EAAC,gEAAgE,YAC7E,eAAK,SAAS,EAAC,mDAAmD,aAChE,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,MAAM,EACX,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,6BAA6B,YAEvC,KAAC,CAAC,IAAC,SAAS,EAAC,SAAS,GAAG,GAClB,EACR,QAAQ,IACL,GACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE;IAChE,iBAAiB,EAAE,mBAAmB;IACtC,cAAc,EAAE,gBAAgB;IAChC,UAAU,EAAE,YAAY;IACxB,uBAAuB,EAAE,iBAAiB;CAC3C,CAAC,CAAC","sourcesContent":["import {FC, PropsWithChildren} from 'react';\nimport {Button, UseDisclosureReturnValue} from '@sqlrooms/ui';\nimport {X} from 'lucide-react';\n\nimport {AiModelParameters} from './AiModelParameters';\nimport {AiModelUsage} from './AiModelUsage';\nimport {AiProvidersSettings} from './AiProvidersSettings';\nimport {AiModelsSettings} from './AiModelsSettings';\n\ninterface AiSettingsPanelProps {\n disclosure: UseDisclosureReturnValue;\n}\n\nexport const AiSettingsPanelBase: FC<\n PropsWithChildren<AiSettingsPanelProps>\n> = ({disclosure, children}) => {\n const {isOpen, onClose} = disclosure;\n if (!isOpen) return null;\n\n return (\n <div className=\"bg-background border-border w-full rounded-lg border shadow-sm\">\n <div className=\"relative flex flex-col gap-12 overflow-y-auto p-6\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClose}\n className=\"absolute right-2 top-2 z-10\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n {children}\n </div>\n </div>\n );\n};\n\nexport const AiSettingsPanel = Object.assign(AiSettingsPanelBase, {\n ProvidersSettings: AiProvidersSettings,\n ModelsSettings: AiModelsSettings,\n ModelUsage: AiModelUsage,\n ModelParametersSettings: AiModelParameters,\n});\n"]}
@@ -0,0 +1,3 @@
1
+ import { AiSettingsSliceConfig } from '@sqlrooms/ai-config';
2
+ export declare function createDefaultAiSettingsConfig(props?: Partial<AiSettingsSliceConfig>): AiSettingsSliceConfig;
3
+ //# sourceMappingURL=defaultSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultSettings.d.ts","sourceRoot":"","sources":["../src/defaultSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,qBAAqB,EAAC,MAAM,qBAAqB,CAAC;AAE1D,wBAAgB,6BAA6B,CAC3C,KAAK,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GACrC,qBAAqB,CAsCvB"}
@@ -0,0 +1,40 @@
1
+ export function createDefaultAiSettingsConfig(props) {
2
+ return {
3
+ providers: {
4
+ openai: {
5
+ baseUrl: 'https://api.openai.com/v1',
6
+ apiKey: '',
7
+ models: [
8
+ {
9
+ modelName: 'gpt-4.1',
10
+ },
11
+ {
12
+ modelName: 'gpt-5',
13
+ },
14
+ ],
15
+ },
16
+ anthropic: {
17
+ baseUrl: 'https://api.anthropic.com',
18
+ apiKey: '',
19
+ models: [
20
+ {
21
+ modelName: 'claude-4-sonnet',
22
+ },
23
+ ],
24
+ },
25
+ },
26
+ customModels: [
27
+ // {
28
+ // baseUrl: 'http://localhost:11434/v1',
29
+ // apiKey: '',
30
+ // modelName: 'qwen3',
31
+ // },
32
+ ],
33
+ modelParameters: {
34
+ maxSteps: 5,
35
+ additionalInstruction: '',
36
+ },
37
+ ...props,
38
+ };
39
+ }
40
+ //# sourceMappingURL=defaultSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultSettings.js","sourceRoot":"","sources":["../src/defaultSettings.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,6BAA6B,CAC3C,KAAsC;IAEtC,OAAO;QACL,SAAS,EAAE;YACT,MAAM,EAAE;gBACN,OAAO,EAAE,2BAA2B;gBACpC,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE;oBACN;wBACE,SAAS,EAAE,SAAS;qBACrB;oBACD;wBACE,SAAS,EAAE,OAAO;qBACnB;iBACF;aACF;YACD,SAAS,EAAE;gBACT,OAAO,EAAE,2BAA2B;gBACpC,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE;oBACN;wBACE,SAAS,EAAE,iBAAiB;qBAC7B;iBACF;aACF;SACF;QACD,YAAY,EAAE;QACZ,IAAI;QACJ,0CAA0C;QAC1C,gBAAgB;QAChB,wBAAwB;QACxB,KAAK;SACN;QACD,eAAe,EAAE;YACf,QAAQ,EAAE,CAAC;YACX,qBAAqB,EAAE,EAAE;SAC1B;QACD,GAAG,KAAK;KACT,CAAC;AACJ,CAAC","sourcesContent":["import {AiSettingsSliceConfig} from '@sqlrooms/ai-config';\n\nexport function createDefaultAiSettingsConfig(\n props?: Partial<AiSettingsSliceConfig>,\n): AiSettingsSliceConfig {\n return {\n providers: {\n openai: {\n baseUrl: 'https://api.openai.com/v1',\n apiKey: '',\n models: [\n {\n modelName: 'gpt-4.1',\n },\n {\n modelName: 'gpt-5',\n },\n ],\n },\n anthropic: {\n baseUrl: 'https://api.anthropic.com',\n apiKey: '',\n models: [\n {\n modelName: 'claude-4-sonnet',\n },\n ],\n },\n },\n customModels: [\n // {\n // baseUrl: 'http://localhost:11434/v1',\n // apiKey: '',\n // modelName: 'qwen3',\n // },\n ],\n modelParameters: {\n maxSteps: 5,\n additionalInstruction: '',\n },\n ...props,\n };\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * {@include ../README.md}
3
+ * @packageDocumentation
4
+ */
5
+ export { createAiSettingsSlice, useStoreWithAiSettings } from './AiSettingsSlice';
6
+ export type { AiSettingsSliceState } from './AiSettingsSlice';
7
+ export { createDefaultAiSettingsConfig } from './defaultSettings';
8
+ export { AiSettingsPanel } from './components/AiSettingsPanel';
9
+ export { AiProvidersSettings } from './components/AiProvidersSettings';
10
+ export { AiModelsSettings } from './components/AiModelsSettings';
11
+ export { AiModelParameters } from './components/AiModelParameters';
12
+ export { AiModelUsage } from './components/AiModelUsage';
13
+ export type { ModelUsageData } from './components/AiModelUsage';
14
+ export { AiSettingsSliceConfig } from '@sqlrooms/ai-config';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,qBAAqB,EAAE,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AAEhF,YAAY,EAAC,oBAAoB,EAAC,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAC,6BAA6B,EAAC,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,mBAAmB,EAAC,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAC,qBAAqB,EAAC,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * {@include ../README.md}
3
+ * @packageDocumentation
4
+ */
5
+ export { createAiSettingsSlice, useStoreWithAiSettings } from './AiSettingsSlice';
6
+ export { createDefaultAiSettingsConfig } from './defaultSettings';
7
+ export { AiSettingsPanel } from './components/AiSettingsPanel';
8
+ export { AiProvidersSettings } from './components/AiProvidersSettings';
9
+ export { AiModelsSettings } from './components/AiModelsSettings';
10
+ export { AiModelParameters } from './components/AiModelParameters';
11
+ export { AiModelUsage } from './components/AiModelUsage';
12
+ export { AiSettingsSliceConfig } from '@sqlrooms/ai-config';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,qBAAqB,EAAE,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAC,6BAA6B,EAAC,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,mBAAmB,EAAC,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAGvD,OAAO,EAAC,qBAAqB,EAAC,MAAM,qBAAqB,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nexport {createAiSettingsSlice, useStoreWithAiSettings} from './AiSettingsSlice';\n\nexport type {AiSettingsSliceState} from './AiSettingsSlice';\nexport {createDefaultAiSettingsConfig} from './defaultSettings';\n\nexport {AiSettingsPanel} from './components/AiSettingsPanel';\nexport {AiProvidersSettings} from './components/AiProvidersSettings';\nexport {AiModelsSettings} from './components/AiModelsSettings';\nexport {AiModelParameters} from './components/AiModelParameters';\nexport {AiModelUsage} from './components/AiModelUsage';\nexport type {ModelUsageData} from './components/AiModelUsage';\n\nexport {AiSettingsSliceConfig} from '@sqlrooms/ai-config';\n"]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@sqlrooms/ai-settings",
3
+ "version": "0.26.0-rc.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "module": "dist/index.js",
7
+ "type": "module",
8
+ "author": "Ilya Boyandin <ilya@boyandin.me>",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/sqlrooms/sqlrooms.git"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "dependencies": {
21
+ "@ai-sdk/provider": "^1.1.3",
22
+ "@sqlrooms/ai-config": "0.26.0-rc.0",
23
+ "@sqlrooms/ai-core": "0.26.0-rc.0",
24
+ "@sqlrooms/recharts": "0.26.0-rc.0",
25
+ "@sqlrooms/room-config": "0.26.0-rc.0",
26
+ "@sqlrooms/ui": "0.26.0-rc.0",
27
+ "@sqlrooms/utils": "0.26.0-rc.0",
28
+ "ai": "^4.3.19",
29
+ "immer": "^10.1.1",
30
+ "lucide-react": "^0.475.0",
31
+ "react-markdown": "^10.1.0",
32
+ "recharts": "^2.12.7",
33
+ "rehype-raw": "^7.0.0",
34
+ "remark-gfm": "^4.0.0",
35
+ "zod": "^3.25.73"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=18",
39
+ "react-dom": ">=18"
40
+ },
41
+ "scripts": {
42
+ "dev": "tsc -w",
43
+ "build": "tsc",
44
+ "lint": "eslint .",
45
+ "typecheck": "tsc --noEmit",
46
+ "typedoc": "typedoc"
47
+ },
48
+ "gitHead": "05d355acee6ea65b33b09c6e49d7746ffacb866e"
49
+ }