@solidxai/core-ui 0.1.7-beta.9 → 0.1.7

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.
@@ -13,7 +13,7 @@ import { useDropzone } from 'react-dropzone';
13
13
  import { SettingDropzoneActivePlaceholder } from './SolidSettings/SettingDropzoneActivePlaceholder';
14
14
  import { SolidUploadedImage } from './SolidSettings/SolidUploadedImage';
15
15
  import { SettingsImageRemoveButton } from './SolidSettings/SettingsImageRemoveButton';
16
- import { AiModelConfigTab, ModelBehavior, ModelEntry, ProviderConfig, SolidAiConfig } from './SolidSettings/LlmSettings/AiModelConfigTab';
16
+ import { ModelConfigTab, ProvidersTab, ModelBehavior, ModelEntry, SolidAiConfig, ensureBuiltInProviders } from './SolidSettings/LlmSettings/AiModelConfigTab';
17
17
  import { useDispatch, useSelector } from 'react-redux';
18
18
  import { ERROR_MESSAGES } from '../../constants/error-messages';
19
19
  import { useBulkUpdateSolidSettingsMutation, useLazyGetSolidSettingsQuery } from '../../redux/api/solidSettingsApi';
@@ -72,8 +72,8 @@ export const GeneralSettings = () => {
72
72
  solidXGenAiCodeBuilderConfig: (() => {
73
73
  const defaultAiConfig: SolidAiConfig = {
74
74
  models: {
75
- default: { providerKey: "", behavior: { streaming: false, custom: "" } },
76
- fast: { providerKey: "", behavior: { streaming: false, custom: "" } },
75
+ default: { providerId: "", model: "", behavior: { streaming: false, custom: "" } },
76
+ fast: { providerId: "", model: "", behavior: { streaming: false, custom: "" } },
77
77
  },
78
78
  providers: {},
79
79
  };
@@ -1026,59 +1026,62 @@ export const GeneralSettings = () => {
1026
1026
  )
1027
1027
  }
1028
1028
 
1029
- const AI_TABS = [
1030
- { key: "fast" as const, label: "Fast Model", title: "Fast Model" },
1031
- { key: "default" as const, label: "Intelligent Model", title: "Intelligent Model (Reasoning & tool use)" },
1032
- ];
1033
-
1034
1029
  interface AiSettingsSectionProps {
1035
1030
  aiConfig: SolidAiConfig;
1036
1031
  onAiConfigChange: (config: SolidAiConfig) => void;
1037
1032
  }
1038
1033
 
1039
1034
  const DEFAULT_BEHAVIOR: ModelBehavior = { streaming: false, custom: "" };
1035
+ const DEFAULT_MODEL_ENTRY: ModelEntry = { providerId: "", model: "", behavior: DEFAULT_BEHAVIOR };
1040
1036
 
1041
1037
  const AiSettingsSection = ({ aiConfig, onAiConfigChange }: AiSettingsSectionProps) => {
1042
- const [activeTab, setActiveTab] = useState<"fast" | "default">("fast");
1038
+ const [activeTab, setActiveTab] = useState<"providers" | "default" | "fast">("providers");
1039
+ const [showAddProvider, setShowAddProvider] = useState(false);
1043
1040
 
1044
- const tabItems = AI_TABS.map((tab) => {
1045
- const modelEntry: ModelEntry = aiConfig.models?.[tab.key] ?? { providerKey: "", behavior: DEFAULT_BEHAVIOR };
1046
- const providerKey = modelEntry.providerKey ?? "";
1047
- const providerConfig: ProviderConfig = aiConfig.providers?.[providerKey] ?? { provider: providerKey, apiKey: "", model: "" };
1048
- const behavior: ModelBehavior = modelEntry.behavior ?? DEFAULT_BEHAVIOR;
1041
+ const providers = ensureBuiltInProviders(aiConfig.providers ?? {});
1049
1042
 
1050
- return {
1051
- value: tab.key,
1052
- label: tab.label,
1043
+ const tabItems = [
1044
+ {
1045
+ value: "providers" as const,
1046
+ label: "Providers",
1053
1047
  content: (
1054
- <AiModelConfigTab
1055
- providerKey={providerKey}
1056
- providerConfig={providerConfig}
1057
- behavior={behavior}
1058
- allProviders={aiConfig.providers ?? {}}
1059
- onProviderKeyChange={(newKey, newConfig) => {
1060
- onAiConfigChange({
1061
- ...aiConfig,
1062
- models: { ...aiConfig.models, [tab.key]: { ...modelEntry, providerKey: newKey } },
1063
- providers: { ...aiConfig.providers, [newKey]: newConfig },
1064
- });
1048
+ <ProvidersTab
1049
+ providers={providers}
1050
+ onProvidersChange={(newProviders) => {
1051
+ onAiConfigChange({ ...aiConfig, providers: newProviders });
1065
1052
  }}
1066
- onProviderConfigChange={(pk, config) => {
1067
- onAiConfigChange({
1068
- ...aiConfig,
1069
- providers: { ...aiConfig.providers, [pk]: config },
1070
- });
1053
+ showAddModal={showAddProvider}
1054
+ onAddModalClose={() => setShowAddProvider(false)}
1055
+ />
1056
+ ),
1057
+ },
1058
+ {
1059
+ value: "default" as const,
1060
+ label: "Intelligent Model",
1061
+ content: (
1062
+ <ModelConfigTab
1063
+ modelEntry={aiConfig.models?.default ?? DEFAULT_MODEL_ENTRY}
1064
+ providers={providers}
1065
+ onModelEntryChange={(entry) => {
1066
+ onAiConfigChange({ ...aiConfig, models: { ...aiConfig.models, default: entry } });
1071
1067
  }}
1072
- onBehaviorChange={(newBehavior) => {
1073
- onAiConfigChange({
1074
- ...aiConfig,
1075
- models: { ...aiConfig.models, [tab.key]: { ...modelEntry, behavior: newBehavior } },
1076
- });
1068
+ />
1069
+ ),
1070
+ },
1071
+ {
1072
+ value: "fast" as const,
1073
+ label: "Fast Model",
1074
+ content: (
1075
+ <ModelConfigTab
1076
+ modelEntry={aiConfig.models?.fast ?? DEFAULT_MODEL_ENTRY}
1077
+ providers={providers}
1078
+ onModelEntryChange={(entry) => {
1079
+ onAiConfigChange({ ...aiConfig, models: { ...aiConfig.models, fast: entry } });
1077
1080
  }}
1078
1081
  />
1079
1082
  ),
1080
- };
1081
- });
1083
+ },
1084
+ ];
1082
1085
 
1083
1086
  return (
1084
1087
  <div>
@@ -1088,8 +1091,15 @@ const AiSettingsSection = ({ aiConfig, onAiConfigChange }: AiSettingsSectionProp
1088
1091
  <SolidTabGroup
1089
1092
  tabs={tabItems}
1090
1093
  value={activeTab}
1091
- onValueChange={(value) => setActiveTab(value as "fast" | "default")}
1094
+ onValueChange={(value) => setActiveTab(value as "providers" | "default" | "fast")}
1092
1095
  tabPosition="left"
1096
+ extra={
1097
+ activeTab === "providers" ? (
1098
+ <SolidButton size="sm" onClick={() => setShowAddProvider(true)}>
1099
+ Add
1100
+ </SolidButton>
1101
+ ) : undefined
1102
+ }
1093
1103
  />
1094
1104
  </div>
1095
1105
  );
@@ -2,14 +2,14 @@ export interface ModelBehavior {
2
2
  streaming: boolean;
3
3
  custom: string;
4
4
  }
5
- export interface ProviderConfig {
6
- provider: string;
5
+ export interface ProviderEntry {
6
+ type: string;
7
7
  apiKey: string;
8
- model: string;
9
8
  baseUrl?: string;
10
9
  }
11
10
  export interface ModelEntry {
12
- providerKey: string;
11
+ providerId: string;
12
+ model: string;
13
13
  behavior: ModelBehavior;
14
14
  }
15
15
  export interface SolidAiConfig {
@@ -17,17 +17,21 @@ export interface SolidAiConfig {
17
17
  default: ModelEntry;
18
18
  fast: ModelEntry;
19
19
  };
20
- providers: Record<string, ProviderConfig>;
20
+ providers: Record<string, ProviderEntry>;
21
21
  }
22
- interface Props {
23
- providerKey: string;
24
- providerConfig: ProviderConfig;
25
- behavior: ModelBehavior;
26
- allProviders: Record<string, ProviderConfig>;
27
- onProviderKeyChange: (newProviderKey: string, config: ProviderConfig) => void;
28
- onProviderConfigChange: (providerKey: string, config: ProviderConfig) => void;
29
- onBehaviorChange: (behavior: ModelBehavior) => void;
22
+ export declare const ensureBuiltInProviders: (providers: Record<string, ProviderEntry>) => Record<string, ProviderEntry>;
23
+ interface ProvidersTabProps {
24
+ providers: Record<string, ProviderEntry>;
25
+ onProvidersChange: (providers: Record<string, ProviderEntry>) => void;
26
+ showAddModal?: boolean;
27
+ onAddModalClose?: () => void;
28
+ }
29
+ export declare const ProvidersTab: ({ providers, onProvidersChange, showAddModal, onAddModalClose }: ProvidersTabProps) => import("react/jsx-runtime").JSX.Element;
30
+ interface ModelConfigTabProps {
31
+ modelEntry: ModelEntry;
32
+ providers: Record<string, ProviderEntry>;
33
+ onModelEntryChange: (entry: ModelEntry) => void;
30
34
  }
31
- export declare const AiModelConfigTab: ({ providerKey, providerConfig, behavior, allProviders, onProviderKeyChange, onProviderConfigChange, onBehaviorChange, }: Props) => import("react/jsx-runtime").JSX.Element;
35
+ export declare const ModelConfigTab: ({ modelEntry, providers, onModelEntryChange }: ModelConfigTabProps) => import("react/jsx-runtime").JSX.Element;
32
36
  export {};
33
37
  //# sourceMappingURL=AiModelConfigTab.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiModelConfigTab.d.ts","sourceRoot":"","sources":["../../../../../src/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE;QACN,OAAO,EAAE,UAAU,CAAC;QACpB,IAAI,EAAE,UAAU,CAAC;KAClB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC3C;AAED,UAAU,KAAK;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,mBAAmB,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9E,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9E,gBAAgB,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;CACrD;AAkBD,eAAO,MAAM,gBAAgB,4HAQ1B,KAAK,4CA4FP,CAAC"}
1
+ {"version":3,"file":"AiModelConfigTab.d.ts","sourceRoot":"","sources":["../../../../../src/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE;QACN,OAAO,EAAE,UAAU,CAAC;QACpB,IAAI,EAAE,UAAU,CAAC;KAClB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C;AAmBD,eAAO,MAAM,sBAAsB,cAAe,OAAO,MAAM,EAAE,aAAa,CAAC,KAAG,OAAO,MAAM,EAAE,aAAa,CAG5G,CAAC;AA+KH,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACzC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,IAAI,CAAC;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,YAAY,oEAAqE,iBAAiB,4CA+E9G,CAAC;AAIF,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACzC,kBAAkB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,cAAc,kDAAmD,mBAAmB,4CA8DhG,CAAC"}
@@ -9,40 +9,144 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { SolidInput, SolidSelect, SolidSwitch, SolidTextarea } from "../../../shad-cn-ui";
14
- var PROVIDER_OPTIONS = [
12
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
+ import React, { useState } from "react";
14
+ import { SolidButton, SolidDialog, SolidDialogBody, SolidDialogFooter, SolidDialogHeader, SolidDialogTitle, SolidInput, SolidSelect, SolidSwitch, SolidTextarea, } from "../../../shad-cn-ui";
15
+ var PROVIDER_TYPE_OPTIONS = [
15
16
  { label: "OpenAI", value: "openai" },
16
17
  { label: "Anthropic", value: "anthropic" },
17
18
  { label: "OpenAI Compatible", value: "openai-compatible" },
18
19
  { label: "Anthropic Compatible", value: "anthropic-compatible" },
19
20
  ];
20
- var COMPATIBLE_PROVIDERS = ["openai-compatible", "anthropic-compatible"];
21
- var DEFAULT_PROVIDER_CONFIGS = {
22
- openai: { provider: "openai", apiKey: "", model: "" },
23
- anthropic: { provider: "anthropic", apiKey: "", model: "" },
24
- "openai-compatible": { provider: "openai-compatible", apiKey: "", model: "", baseUrl: "" },
25
- "anthropic-compatible": { provider: "anthropic-compatible", apiKey: "", model: "", baseUrl: "" },
21
+ var COMPATIBLE_TYPES = ["openai-compatible", "anthropic-compatible"];
22
+ var BUILT_IN_TYPES = ["openai", "anthropic"];
23
+ var DEFAULT_BUILT_IN_PROVIDERS = {
24
+ openai: { type: "openai", apiKey: "" },
25
+ anthropic: { type: "anthropic", apiKey: "" },
26
26
  };
27
- export var AiModelConfigTab = function (_a) {
28
- var providerKey = _a.providerKey, providerConfig = _a.providerConfig, behavior = _a.behavior, allProviders = _a.allProviders, onProviderKeyChange = _a.onProviderKeyChange, onProviderConfigChange = _a.onProviderConfigChange, onBehaviorChange = _a.onBehaviorChange;
29
- var isCompatible = COMPATIBLE_PROVIDERS.includes(providerKey);
30
- var handleProviderSelect = function (value) {
31
- var _a;
32
- var existingConfig = allProviders[value];
33
- var newConfig = (_a = existingConfig !== null && existingConfig !== void 0 ? existingConfig : DEFAULT_PROVIDER_CONFIGS[value]) !== null && _a !== void 0 ? _a : { provider: value, apiKey: "", model: "" };
34
- onProviderKeyChange(value, newConfig);
27
+ export var ensureBuiltInProviders = function (providers) { return (__assign(__assign({}, DEFAULT_BUILT_IN_PROVIDERS), providers)); };
28
+ var cardStyle = {
29
+ border: "1px solid var(--solid-border-color, #e2e8f0)",
30
+ borderRadius: "0.5rem",
31
+ padding: "1.25rem",
32
+ background: "var(--solid-card-bg, var(--solid-surface-bg, transparent))",
33
+ };
34
+ var ProviderModal = function (_a) {
35
+ var _b, _c, _d, _e;
36
+ var visible = _a.visible, onHide = _a.onHide, providers = _a.providers, onProvidersChange = _a.onProvidersChange, editKey = _a.editKey;
37
+ var isEdit = !!editKey;
38
+ var existingEntry = isEdit ? providers[editKey] : null;
39
+ var _f = useState((_b = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.type) !== null && _b !== void 0 ? _b : ""), providerType = _f[0], setProviderType = _f[1];
40
+ var _g = useState(editKey && !BUILT_IN_TYPES.includes(editKey) ? editKey : ""), providerName = _g[0], setProviderName = _g[1];
41
+ var _h = useState((_c = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.baseUrl) !== null && _c !== void 0 ? _c : ""), providerBaseUrl = _h[0], setProviderBaseUrl = _h[1];
42
+ var _j = useState((_d = existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.apiKey) !== null && _d !== void 0 ? _d : ""), providerApiKey = _j[0], setProviderApiKey = _j[1];
43
+ var isCompatible = COMPATIBLE_TYPES.includes(providerType);
44
+ // Reset state when modal opens with new data
45
+ React.useEffect(function () {
46
+ var _a, _b, _c;
47
+ if (visible) {
48
+ var entry = editKey ? providers[editKey] : null;
49
+ setProviderType((_a = entry === null || entry === void 0 ? void 0 : entry.type) !== null && _a !== void 0 ? _a : "");
50
+ setProviderName(editKey && !BUILT_IN_TYPES.includes(editKey) ? editKey : "");
51
+ setProviderBaseUrl((_b = entry === null || entry === void 0 ? void 0 : entry.baseUrl) !== null && _b !== void 0 ? _b : "");
52
+ setProviderApiKey((_c = entry === null || entry === void 0 ? void 0 : entry.apiKey) !== null && _c !== void 0 ? _c : "");
53
+ }
54
+ }, [visible, editKey]);
55
+ // In add mode, only show compatible types (built-ins are always pre-added)
56
+ // In edit mode, show all types but lock the value
57
+ var typeOptions = isEdit
58
+ ? PROVIDER_TYPE_OPTIONS
59
+ : PROVIDER_TYPE_OPTIONS.filter(function (opt) { return COMPATIBLE_TYPES.includes(opt.value); });
60
+ var handleSave = function () {
61
+ if (!providerType)
62
+ return;
63
+ var key;
64
+ var entry = { type: providerType, apiKey: providerApiKey };
65
+ if (isCompatible) {
66
+ if (!providerName.trim())
67
+ return;
68
+ key = providerName.trim();
69
+ entry.baseUrl = providerBaseUrl;
70
+ }
71
+ else {
72
+ key = providerType;
73
+ }
74
+ // In add mode, don't overwrite existing
75
+ if (!isEdit && providers[key])
76
+ return;
77
+ var next = __assign({}, providers);
78
+ // If editing and key changed (shouldn't happen for built-in), remove old
79
+ if (isEdit && editKey !== key) {
80
+ delete next[editKey];
81
+ }
82
+ next[key] = entry;
83
+ onProvidersChange(next);
84
+ onHide();
35
85
  };
36
- var handleConfigUpdate = function (key, value) {
37
- var _a;
38
- onProviderConfigChange(providerKey, __assign(__assign({}, providerConfig), (_a = {}, _a[key] = value, _a)));
86
+ var handleRemove = function () {
87
+ if (!editKey)
88
+ return;
89
+ var next = __assign({}, providers);
90
+ delete next[editKey];
91
+ onProvidersChange(next);
92
+ onHide();
39
93
  };
40
- var cardStyle = {
41
- border: "1px solid var(--solid-border-color, #e2e8f0)",
42
- borderRadius: "0.5rem",
43
- padding: "1.25rem",
44
- background: "var(--solid-card-bg, var(--solid-surface-bg, transparent))",
94
+ var canSave = providerType && (isCompatible ? providerName.trim() : true);
95
+ return (_jsxs(SolidDialog, { open: visible, onOpenChange: function (open) { return !open && onHide(); }, style: { width: "500px" }, children: [_jsx(SolidDialogHeader, { children: _jsx(SolidDialogTitle, { children: isEdit
96
+ ? "Edit Provider \u2014 ".concat(BUILT_IN_TYPES.includes(editKey) ? (_e = PROVIDER_TYPE_OPTIONS.find(function (o) { return o.value === editKey; })) === null || _e === void 0 ? void 0 : _e.label : editKey)
97
+ : "Add Provider" }) }), _jsx(SolidDialogBody, { children: _jsxs("div", { className: "flex flex-column gap-3", children: [!(isEdit && BUILT_IN_TYPES.includes(editKey)) && (_jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Provider Type" }), _jsx(SolidSelect, { className: "w-full", value: providerType, options: typeOptions, onChange: function (e) {
98
+ setProviderType(e.value);
99
+ if (!isEdit) {
100
+ setProviderName("");
101
+ setProviderBaseUrl("");
102
+ }
103
+ }, placeholder: "Select Provider Type", disabled: isEdit })] })), isCompatible && (_jsxs(_Fragment, { children: [!isEdit && (_jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Name (unique identifier)" }), _jsx(SolidInput, { placeholder: "e.g. openrouter, together-ai", value: providerName, onChange: function (e) { return setProviderName(e.target.value); }, className: "w-full" })] })), _jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Base URL" }), _jsx(SolidInput, { placeholder: "https://openrouter.ai/api/v1", value: providerBaseUrl, onChange: function (e) { return setProviderBaseUrl(e.target.value); }, className: "w-full" })] })] })), _jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "API Key" }), _jsx(SolidInput, { type: "password", value: providerApiKey, onChange: function (e) { return setProviderApiKey(e.target.value); }, className: "w-full" })] })] }) }), _jsx(SolidDialogFooter, { children: _jsxs("div", { className: "flex justify-content-between w-full", children: [_jsx("div", { children: isEdit && !BUILT_IN_TYPES.includes(editKey) && (_jsx(SolidButton, { variant: "ghost", onClick: handleRemove, style: { color: "var(--solid-danger-color, #ef4444)" }, children: "Remove" })) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(SolidButton, { variant: "outline", onClick: onHide, children: "Cancel" }), _jsx(SolidButton, { onClick: handleSave, disabled: !canSave, children: isEdit ? "Save" : "Add" })] })] }) })] }));
104
+ };
105
+ export var ProvidersTab = function (_a) {
106
+ var providers = _a.providers, onProvidersChange = _a.onProvidersChange, showAddModal = _a.showAddModal, onAddModalClose = _a.onAddModalClose;
107
+ var _b = useState(null), editKey = _b[0], setEditKey = _b[1];
108
+ var _c = useState(false), modalVisible = _c[0], setModalVisible = _c[1];
109
+ // Ensure built-in providers are always present
110
+ var allProviders = ensureBuiltInProviders(providers);
111
+ // Open modal when parent triggers add
112
+ React.useEffect(function () {
113
+ if (showAddModal) {
114
+ setEditKey(null);
115
+ setModalVisible(true);
116
+ }
117
+ }, [showAddModal]);
118
+ var providerEntries = Object.entries(allProviders);
119
+ var handleRowClick = function (key) {
120
+ setEditKey(key);
121
+ setModalVisible(true);
45
122
  };
46
- return (_jsxs("div", { className: "flex flex-column gap-4", children: [_jsxs("div", { style: cardStyle, children: [_jsx("p", { className: "solid-settings-subheading", children: "Provider Config" }), _jsxs("div", { className: "flex flex-column gap-3 mt-3", children: [_jsxs("div", { className: "flex flex-column gap-2", children: [_jsx("label", { className: "form-field-label", children: "Provider" }), _jsx(SolidSelect, { className: "w-full", value: providerKey, options: PROVIDER_OPTIONS, onChange: function (e) { return handleProviderSelect(e.value); }, placeholder: "Select Provider" })] }), isCompatible && (_jsxs("div", { className: "flex flex-column gap-2", children: [_jsx("label", { className: "form-field-label", children: "Base URL" }), _jsx(SolidInput, { placeholder: "http://localhost:8000", value: (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.baseUrl) || "", onChange: function (e) { return handleConfigUpdate("baseUrl", e.target.value); }, className: "w-full" })] })), _jsxs("div", { className: "flex flex-column gap-2", children: [_jsx("label", { className: "form-field-label", children: "API Key" }), _jsx(SolidInput, { type: "password", className: "w-full", value: (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.apiKey) || "", onChange: function (e) { return handleConfigUpdate("apiKey", e.target.value); } })] }), _jsxs("div", { className: "flex flex-column gap-2", children: [_jsx("label", { className: "form-field-label", children: "Model" }), _jsx(SolidInput, { placeholder: "e.g. gpt-4o-mini", value: (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.model) || "", onChange: function (e) { return handleConfigUpdate("model", e.target.value); }, className: "w-full" })] })] })] }), _jsxs("div", { style: cardStyle, children: [_jsx("p", { className: "solid-settings-subheading", children: "Behavior" }), _jsxs("div", { className: "flex flex-column gap-3 mt-3", children: [_jsxs("div", { className: "flex align-items-center gap-2", children: [_jsx(SolidSwitch, { checked: behavior.streaming, onChange: function (val) { return onBehaviorChange(__assign(__assign({}, behavior), { streaming: val })); } }), _jsx("label", { className: "form-field-label", style: { marginBottom: 0 }, children: "Streaming" })] }), _jsxs("div", { className: "flex flex-column gap-2", children: [_jsx("label", { className: "form-field-label", children: "Custom Params (JSON)" }), _jsx(SolidTextarea, { value: behavior.custom, onChange: function (e) { return onBehaviorChange(__assign(__assign({}, behavior), { custom: e.target.value })); }, placeholder: '{ "temperature": 0.7, "maxTokens": 1000 }', rows: 4, className: "w-full" })] })] })] })] }));
123
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "solid-simple-table", children: _jsxs("table", { style: { width: "100%" }, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: { textAlign: "left", padding: "0.75rem 1rem" }, children: "Name" }), _jsx("th", { style: { textAlign: "left", padding: "0.75rem 1rem" }, children: "Type" }), _jsx("th", { style: { textAlign: "left", padding: "0.75rem 1rem" }, children: "API Key" }), _jsx("th", { style: { textAlign: "left", padding: "0.75rem 1rem" }, children: "Base URL" })] }) }), _jsxs("tbody", { children: [providerEntries.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 4, style: { textAlign: "center", padding: "2rem 1rem", opacity: 0.5 }, children: "No providers configured." }) })), providerEntries.map(function (_a) {
124
+ var _b, _c;
125
+ var key = _a[0], entry = _a[1];
126
+ var typeLabel = (_c = (_b = PROVIDER_TYPE_OPTIONS.find(function (o) { return o.value === entry.type; })) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : entry.type;
127
+ var isBuiltIn = BUILT_IN_TYPES.includes(key);
128
+ var displayName = isBuiltIn ? typeLabel : key;
129
+ var maskedKey = entry.apiKey ? "\u2022".repeat(Math.min(entry.apiKey.length, 8)) : "-";
130
+ return (_jsxs("tr", { onClick: function () { return handleRowClick(key); }, style: { cursor: "pointer" }, className: "solid-table-row-hover", children: [_jsx("td", { style: { padding: "0.75rem 1rem", fontWeight: 500 }, children: displayName }), _jsx("td", { style: { padding: "0.75rem 1rem" }, children: typeLabel }), _jsx("td", { style: { padding: "0.75rem 1rem" }, children: maskedKey }), _jsx("td", { style: { padding: "0.75rem 1rem" }, children: entry.baseUrl || "-" })] }, key));
131
+ })] })] }) }), _jsx(ProviderModal, { visible: modalVisible, onHide: function () {
132
+ setModalVisible(false);
133
+ setEditKey(null);
134
+ onAddModalClose === null || onAddModalClose === void 0 ? void 0 : onAddModalClose();
135
+ }, providers: allProviders, onProvidersChange: onProvidersChange, editKey: editKey })] }));
136
+ };
137
+ export var ModelConfigTab = function (_a) {
138
+ var modelEntry = _a.modelEntry, providers = _a.providers, onModelEntryChange = _a.onModelEntryChange;
139
+ var providerOptions = Object.entries(providers).map(function (_a) {
140
+ var _b, _c;
141
+ var key = _a[0], entry = _a[1];
142
+ var typeLabel = (_c = (_b = PROVIDER_TYPE_OPTIONS.find(function (o) { return o.value === entry.type; })) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : entry.type;
143
+ var label = BUILT_IN_TYPES.includes(key) ? typeLabel : "".concat(key, " (").concat(typeLabel, ")");
144
+ return { label: label, value: key };
145
+ });
146
+ return (_jsxs("div", { className: "flex flex-column gap-4", children: [_jsxs("div", { style: __assign(__assign({}, cardStyle), { width: "50%" }), children: [_jsx("p", { className: "solid-settings-subheading", children: "Model Config" }), _jsxs("div", { className: "flex flex-column gap-3 mt-3", children: [_jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Provider" }), _jsx(SolidSelect, { className: "w-full", value: modelEntry.providerId, options: providerOptions, onChange: function (e) { return onModelEntryChange(__assign(__assign({}, modelEntry), { providerId: e.value })); }, placeholder: "Select Provider" })] }), _jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Model" }), _jsx(SolidInput, { placeholder: "e.g. gpt-4o-mini", value: modelEntry.model || "", onChange: function (e) { return onModelEntryChange(__assign(__assign({}, modelEntry), { model: e.target.value })); }, className: "w-full" })] })] })] }), _jsxs("div", { style: __assign(__assign({}, cardStyle), { width: "50%" }), children: [_jsx("p", { className: "solid-settings-subheading", children: "Behavior" }), _jsxs("div", { className: "flex flex-column gap-3 mt-3", children: [_jsxs("div", { className: "flex align-items-center gap-2", children: [_jsx(SolidSwitch, { checked: modelEntry.behavior.streaming, onChange: function (val) {
147
+ return onModelEntryChange(__assign(__assign({}, modelEntry), { behavior: __assign(__assign({}, modelEntry.behavior), { streaming: val }) }));
148
+ } }), _jsx("label", { className: "form-field-label", style: { marginBottom: 0 }, children: "Streaming" })] }), _jsxs("div", { children: [_jsx("label", { className: "form-field-label", children: "Custom Params (JSON)" }), _jsx(SolidTextarea, { value: modelEntry.behavior.custom, onChange: function (e) {
149
+ return onModelEntryChange(__assign(__assign({}, modelEntry), { behavior: __assign(__assign({}, modelEntry.behavior), { custom: e.target.value }) }));
150
+ }, placeholder: '{ "temperature": 0.7, "maxTokens": 1000 }', rows: 4, className: "w-full" })] })] })] })] }));
47
151
  };
48
152
  //# sourceMappingURL=AiModelConfigTab.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiModelConfigTab.js","sourceRoot":"","sources":["../../../../../src/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from \"react\";\nimport { SolidInput, SolidSelect, SolidSwitch, SolidTextarea } from \"../../../shad-cn-ui\";\n\nexport interface ModelBehavior {\n streaming: boolean;\n custom: string;\n}\n\nexport interface ProviderConfig {\n provider: string;\n apiKey: string;\n model: string;\n baseUrl?: string;\n}\n\nexport interface ModelEntry {\n providerKey: string;\n behavior: ModelBehavior;\n}\n\nexport interface SolidAiConfig {\n models: {\n default: ModelEntry;\n fast: ModelEntry;\n };\n providers: Record<string, ProviderConfig>;\n}\n\ninterface Props {\n providerKey: string;\n providerConfig: ProviderConfig;\n behavior: ModelBehavior;\n allProviders: Record<string, ProviderConfig>;\n onProviderKeyChange: (newProviderKey: string, config: ProviderConfig) => void;\n onProviderConfigChange: (providerKey: string, config: ProviderConfig) => void;\n onBehaviorChange: (behavior: ModelBehavior) => void;\n}\n\nconst PROVIDER_OPTIONS = [\n { label: \"OpenAI\", value: \"openai\" },\n { label: \"Anthropic\", value: \"anthropic\" },\n { label: \"OpenAI Compatible\", value: \"openai-compatible\" },\n { label: \"Anthropic Compatible\", value: \"anthropic-compatible\" },\n];\n\nconst COMPATIBLE_PROVIDERS = [\"openai-compatible\", \"anthropic-compatible\"];\n\nconst DEFAULT_PROVIDER_CONFIGS: Record<string, ProviderConfig> = {\n openai: { provider: \"openai\", apiKey: \"\", model: \"\" },\n anthropic: { provider: \"anthropic\", apiKey: \"\", model: \"\" },\n \"openai-compatible\": { provider: \"openai-compatible\", apiKey: \"\", model: \"\", baseUrl: \"\" },\n \"anthropic-compatible\": { provider: \"anthropic-compatible\", apiKey: \"\", model: \"\", baseUrl: \"\" },\n};\n\nexport const AiModelConfigTab = ({\n providerKey,\n providerConfig,\n behavior,\n allProviders,\n onProviderKeyChange,\n onProviderConfigChange,\n onBehaviorChange,\n}: Props) => {\n const isCompatible = COMPATIBLE_PROVIDERS.includes(providerKey);\n\n const handleProviderSelect = (value: string) => {\n const existingConfig = allProviders[value];\n const newConfig = existingConfig ?? DEFAULT_PROVIDER_CONFIGS[value] ?? { provider: value, apiKey: \"\", model: \"\" };\n onProviderKeyChange(value, newConfig);\n };\n\n const handleConfigUpdate = (key: keyof ProviderConfig, value: string) => {\n onProviderConfigChange(providerKey, { ...providerConfig, [key]: value });\n };\n\n const cardStyle: React.CSSProperties = {\n border: \"1px solid var(--solid-border-color, #e2e8f0)\",\n borderRadius: \"0.5rem\",\n padding: \"1.25rem\",\n background: \"var(--solid-card-bg, var(--solid-surface-bg, transparent))\",\n };\n\n return (\n <div className=\"flex flex-column gap-4\">\n <div style={cardStyle}>\n <p className=\"solid-settings-subheading\">Provider Config</p>\n <div className=\"flex flex-column gap-3 mt-3\">\n <div className=\"flex flex-column gap-2\">\n <label className=\"form-field-label\">Provider</label>\n <SolidSelect\n className=\"w-full\"\n value={providerKey}\n options={PROVIDER_OPTIONS}\n onChange={(e) => handleProviderSelect(e.value)}\n placeholder=\"Select Provider\"\n />\n </div>\n {isCompatible && (\n <div className=\"flex flex-column gap-2\">\n <label className=\"form-field-label\">Base URL</label>\n <SolidInput\n placeholder=\"http://localhost:8000\"\n value={providerConfig?.baseUrl || \"\"}\n onChange={(e) => handleConfigUpdate(\"baseUrl\", e.target.value)}\n className=\"w-full\"\n />\n </div>\n )}\n <div className=\"flex flex-column gap-2\">\n <label className=\"form-field-label\">API Key</label>\n <SolidInput\n type=\"password\"\n className=\"w-full\"\n value={providerConfig?.apiKey || \"\"}\n onChange={(e) => handleConfigUpdate(\"apiKey\", e.target.value)}\n />\n </div>\n <div className=\"flex flex-column gap-2\">\n <label className=\"form-field-label\">Model</label>\n <SolidInput\n placeholder=\"e.g. gpt-4o-mini\"\n value={providerConfig?.model || \"\"}\n onChange={(e) => handleConfigUpdate(\"model\", e.target.value)}\n className=\"w-full\"\n />\n </div>\n </div>\n </div>\n\n <div style={cardStyle}>\n <p className=\"solid-settings-subheading\">Behavior</p>\n <div className=\"flex flex-column gap-3 mt-3\">\n <div className=\"flex align-items-center gap-2\">\n <SolidSwitch\n checked={behavior.streaming}\n onChange={(val) => onBehaviorChange({ ...behavior, streaming: val })}\n />\n <label className=\"form-field-label\" style={{ marginBottom: 0 }}>Streaming</label>\n </div>\n <div className=\"flex flex-column gap-2\">\n <label className=\"form-field-label\">Custom Params (JSON)</label>\n <SolidTextarea\n value={behavior.custom}\n onChange={(e) => onBehaviorChange({ ...behavior, custom: e.target.value })}\n placeholder='{ \"temperature\": 0.7, \"maxTokens\": 1000 }'\n rows={4}\n className=\"w-full\"\n />\n </div>\n </div>\n </div>\n\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"AiModelConfigTab.js","sourceRoot":"","sources":["../../../../../src/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from \"react\";\nimport {\n SolidButton,\n SolidDialog,\n SolidDialogBody,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogTitle,\n SolidInput,\n SolidSelect,\n SolidSwitch,\n SolidTextarea,\n} from \"../../../shad-cn-ui\";\n\n\nexport interface ModelBehavior {\n streaming: boolean;\n custom: string;\n}\n\nexport interface ProviderEntry {\n type: string;\n apiKey: string;\n baseUrl?: string;\n}\n\nexport interface ModelEntry {\n providerId: string;\n model: string;\n behavior: ModelBehavior;\n}\n\nexport interface SolidAiConfig {\n models: {\n default: ModelEntry;\n fast: ModelEntry;\n };\n providers: Record<string, ProviderEntry>;\n}\n\n\nconst PROVIDER_TYPE_OPTIONS = [\n { label: \"OpenAI\", value: \"openai\" },\n { label: \"Anthropic\", value: \"anthropic\" },\n { label: \"OpenAI Compatible\", value: \"openai-compatible\" },\n { label: \"Anthropic Compatible\", value: \"anthropic-compatible\" },\n];\n\nconst COMPATIBLE_TYPES = [\"openai-compatible\", \"anthropic-compatible\"];\n\nconst BUILT_IN_TYPES = [\"openai\", \"anthropic\"];\n\nconst DEFAULT_BUILT_IN_PROVIDERS: Record<string, ProviderEntry> = {\n openai: { type: \"openai\", apiKey: \"\" },\n anthropic: { type: \"anthropic\", apiKey: \"\" },\n};\n\nexport const ensureBuiltInProviders = (providers: Record<string, ProviderEntry>): Record<string, ProviderEntry> => ({\n ...DEFAULT_BUILT_IN_PROVIDERS,\n ...providers,\n});\n\nconst cardStyle: React.CSSProperties = {\n border: \"1px solid var(--solid-border-color, #e2e8f0)\",\n borderRadius: \"0.5rem\",\n padding: \"1.25rem\",\n background: \"var(--solid-card-bg, var(--solid-surface-bg, transparent))\",\n};\n\n\ninterface ProviderModalProps {\n visible: boolean;\n onHide: () => void;\n providers: Record<string, ProviderEntry>;\n onProvidersChange: (providers: Record<string, ProviderEntry>) => void;\n editKey?: string | null; // null = add mode, string = edit mode\n}\n\nconst ProviderModal = ({ visible, onHide, providers, onProvidersChange, editKey }: ProviderModalProps) => {\n const isEdit = !!editKey;\n const existingEntry = isEdit ? providers[editKey] : null;\n\n const [providerType, setProviderType] = useState(existingEntry?.type ?? \"\");\n const [providerName, setProviderName] = useState(editKey && !BUILT_IN_TYPES.includes(editKey) ? editKey : \"\");\n const [providerBaseUrl, setProviderBaseUrl] = useState(existingEntry?.baseUrl ?? \"\");\n const [providerApiKey, setProviderApiKey] = useState(existingEntry?.apiKey ?? \"\");\n\n const isCompatible = COMPATIBLE_TYPES.includes(providerType);\n\n // Reset state when modal opens with new data\n React.useEffect(() => {\n if (visible) {\n const entry = editKey ? providers[editKey] : null;\n setProviderType(entry?.type ?? \"\");\n setProviderName(editKey && !BUILT_IN_TYPES.includes(editKey) ? editKey : \"\");\n setProviderBaseUrl(entry?.baseUrl ?? \"\");\n setProviderApiKey(entry?.apiKey ?? \"\");\n }\n }, [visible, editKey]);\n\n // In add mode, only show compatible types (built-ins are always pre-added)\n // In edit mode, show all types but lock the value\n const typeOptions = isEdit\n ? PROVIDER_TYPE_OPTIONS\n : PROVIDER_TYPE_OPTIONS.filter((opt) => COMPATIBLE_TYPES.includes(opt.value));\n\n const handleSave = () => {\n if (!providerType) return;\n\n let key: string;\n const entry: ProviderEntry = { type: providerType, apiKey: providerApiKey };\n\n if (isCompatible) {\n if (!providerName.trim()) return;\n key = providerName.trim();\n entry.baseUrl = providerBaseUrl;\n } else {\n key = providerType;\n }\n\n // In add mode, don't overwrite existing\n if (!isEdit && providers[key]) return;\n\n const next = { ...providers };\n\n // If editing and key changed (shouldn't happen for built-in), remove old\n if (isEdit && editKey !== key) {\n delete next[editKey!];\n }\n\n next[key] = entry;\n onProvidersChange(next);\n onHide();\n };\n\n const handleRemove = () => {\n if (!editKey) return;\n const next = { ...providers };\n delete next[editKey];\n onProvidersChange(next);\n onHide();\n };\n\n const canSave = providerType && (isCompatible ? providerName.trim() : true);\n\n return (\n <SolidDialog open={visible} onOpenChange={(open) => !open && onHide()} style={{ width: \"500px\" }}>\n <SolidDialogHeader>\n <SolidDialogTitle>\n {isEdit\n ? `Edit Provider — ${BUILT_IN_TYPES.includes(editKey!) ? PROVIDER_TYPE_OPTIONS.find((o) => o.value === editKey)?.label : editKey}`\n : \"Add Provider\"}\n </SolidDialogTitle>\n </SolidDialogHeader>\n <SolidDialogBody>\n <div className=\"flex flex-column gap-3\">\n {!(isEdit && BUILT_IN_TYPES.includes(editKey!)) && (\n <div>\n <label className=\"form-field-label\">Provider Type</label>\n <SolidSelect\n className=\"w-full\"\n value={providerType}\n options={typeOptions}\n onChange={(e) => {\n setProviderType(e.value);\n if (!isEdit) {\n setProviderName(\"\");\n setProviderBaseUrl(\"\");\n }\n }}\n placeholder=\"Select Provider Type\"\n disabled={isEdit}\n />\n </div>\n )}\n {isCompatible && (\n <>\n {!isEdit && (\n <div>\n <label className=\"form-field-label\">Name (unique identifier)</label>\n <SolidInput\n placeholder=\"e.g. openrouter, together-ai\"\n value={providerName}\n onChange={(e) => setProviderName(e.target.value)}\n className=\"w-full\"\n />\n </div>\n )}\n <div>\n <label className=\"form-field-label\">Base URL</label>\n <SolidInput\n placeholder=\"https://openrouter.ai/api/v1\"\n value={providerBaseUrl}\n onChange={(e) => setProviderBaseUrl(e.target.value)}\n className=\"w-full\"\n />\n </div>\n </>\n )}\n <div>\n <label className=\"form-field-label\">API Key</label>\n <SolidInput\n type=\"password\"\n value={providerApiKey}\n onChange={(e) => setProviderApiKey(e.target.value)}\n className=\"w-full\"\n />\n </div>\n </div>\n </SolidDialogBody>\n <SolidDialogFooter>\n <div className=\"flex justify-content-between w-full\">\n <div>\n {isEdit && !BUILT_IN_TYPES.includes(editKey!) && (\n <SolidButton variant=\"ghost\" onClick={handleRemove} style={{ color: \"var(--solid-danger-color, #ef4444)\" }}>\n Remove\n </SolidButton>\n )}\n </div>\n <div className=\"flex gap-2\">\n <SolidButton variant=\"outline\" onClick={onHide}>\n Cancel\n </SolidButton>\n <SolidButton onClick={handleSave} disabled={!canSave}>\n {isEdit ? \"Save\" : \"Add\"}\n </SolidButton>\n </div>\n </div>\n </SolidDialogFooter>\n </SolidDialog>\n );\n};\n\n// --- Providers Tab (List View) ---\n\ninterface ProvidersTabProps {\n providers: Record<string, ProviderEntry>;\n onProvidersChange: (providers: Record<string, ProviderEntry>) => void;\n showAddModal?: boolean;\n onAddModalClose?: () => void;\n}\n\nexport const ProvidersTab = ({ providers, onProvidersChange, showAddModal, onAddModalClose }: ProvidersTabProps) => {\n const [editKey, setEditKey] = useState<string | null>(null);\n const [modalVisible, setModalVisible] = useState(false);\n\n // Ensure built-in providers are always present\n const allProviders = ensureBuiltInProviders(providers);\n\n // Open modal when parent triggers add\n React.useEffect(() => {\n if (showAddModal) {\n setEditKey(null);\n setModalVisible(true);\n }\n }, [showAddModal]);\n\n const providerEntries = Object.entries(allProviders);\n\n const handleRowClick = (key: string) => {\n setEditKey(key);\n setModalVisible(true);\n };\n\n return (\n <>\n <div className=\"solid-simple-table\">\n <table style={{ width: \"100%\" }}>\n <thead>\n <tr>\n <th style={{ textAlign: \"left\", padding: \"0.75rem 1rem\" }}>Name</th>\n <th style={{ textAlign: \"left\", padding: \"0.75rem 1rem\" }}>Type</th>\n <th style={{ textAlign: \"left\", padding: \"0.75rem 1rem\" }}>API Key</th>\n <th style={{ textAlign: \"left\", padding: \"0.75rem 1rem\" }}>Base URL</th>\n </tr>\n </thead>\n <tbody>\n {providerEntries.length === 0 && (\n <tr>\n <td colSpan={4} style={{ textAlign: \"center\", padding: \"2rem 1rem\", opacity: 0.5 }}>\n No providers configured.\n </td>\n </tr>\n )}\n {providerEntries.map(([key, entry]) => {\n const typeLabel = PROVIDER_TYPE_OPTIONS.find((o) => o.value === entry.type)?.label ?? entry.type;\n const isBuiltIn = BUILT_IN_TYPES.includes(key);\n const displayName = isBuiltIn ? typeLabel : key;\n const maskedKey = entry.apiKey ? \"\\u2022\".repeat(Math.min(entry.apiKey.length, 8)) : \"-\";\n\n return (\n <tr\n key={key}\n onClick={() => handleRowClick(key)}\n style={{ cursor: \"pointer\" }}\n className=\"solid-table-row-hover\"\n >\n <td style={{ padding: \"0.75rem 1rem\", fontWeight: 500 }}>{displayName}</td>\n <td style={{ padding: \"0.75rem 1rem\" }}>{typeLabel}</td>\n <td style={{ padding: \"0.75rem 1rem\" }}>{maskedKey}</td>\n <td style={{ padding: \"0.75rem 1rem\" }}>{entry.baseUrl || \"-\"}</td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n <ProviderModal\n visible={modalVisible}\n onHide={() => {\n setModalVisible(false);\n setEditKey(null);\n onAddModalClose?.();\n }}\n providers={allProviders}\n onProvidersChange={onProvidersChange}\n editKey={editKey}\n />\n </>\n );\n};\n\n// --- Model Config Tab (used for Intelligent Model & Fast Model) ---\n\ninterface ModelConfigTabProps {\n modelEntry: ModelEntry;\n providers: Record<string, ProviderEntry>;\n onModelEntryChange: (entry: ModelEntry) => void;\n}\n\nexport const ModelConfigTab = ({ modelEntry, providers, onModelEntryChange }: ModelConfigTabProps) => {\n const providerOptions = Object.entries(providers).map(([key, entry]) => {\n const typeLabel = PROVIDER_TYPE_OPTIONS.find((o) => o.value === entry.type)?.label ?? entry.type;\n const label = BUILT_IN_TYPES.includes(key) ? typeLabel : `${key} (${typeLabel})`;\n return { label, value: key };\n });\n\n return (\n <div className=\"flex flex-column gap-4\">\n <div style={{ ...cardStyle, width: \"50%\" }}>\n <p className=\"solid-settings-subheading\">Model Config</p>\n <div className=\"flex flex-column gap-3 mt-3\">\n <div>\n <label className=\"form-field-label\">Provider</label>\n <SolidSelect\n className=\"w-full\"\n value={modelEntry.providerId}\n options={providerOptions}\n onChange={(e) => onModelEntryChange({ ...modelEntry, providerId: e.value })}\n placeholder=\"Select Provider\"\n />\n </div>\n <div>\n <label className=\"form-field-label\">Model</label>\n <SolidInput\n placeholder=\"e.g. gpt-4o-mini\"\n value={modelEntry.model || \"\"}\n onChange={(e) => onModelEntryChange({ ...modelEntry, model: e.target.value })}\n className=\"w-full\"\n />\n </div>\n </div>\n </div>\n\n <div style={{ ...cardStyle, width: \"50%\" }}>\n <p className=\"solid-settings-subheading\">Behavior</p>\n <div className=\"flex flex-column gap-3 mt-3\">\n <div className=\"flex align-items-center gap-2\">\n <SolidSwitch\n checked={modelEntry.behavior.streaming}\n onChange={(val) =>\n onModelEntryChange({ ...modelEntry, behavior: { ...modelEntry.behavior, streaming: val } })\n }\n />\n <label className=\"form-field-label\" style={{ marginBottom: 0 }}>Streaming</label>\n </div>\n <div>\n <label className=\"form-field-label\">Custom Params (JSON)</label>\n <SolidTextarea\n value={modelEntry.behavior.custom}\n onChange={(e) =>\n onModelEntryChange({ ...modelEntry, behavior: { ...modelEntry.behavior, custom: e.target.value } })\n }\n placeholder='{ \"temperature\": 0.7, \"maxTokens\": 1000 }'\n rows={4}\n className=\"w-full\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n};\n"]}