File size: 6,532 Bytes
baa4c21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import React, { useEffect, useState } from 'react';
import { Switch } from '~/components/ui/Switch';
import { useSettings } from '~/lib/hooks/useSettings';
import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS } from '~/lib/stores/settings';
import type { IProviderConfig } from '~/types/model';
import { logStore } from '~/lib/stores/logs';

// Import a default fallback icon
import { providerBaseUrlEnvKeys } from '~/utils/constants';

const DefaultIcon = '/icons/Default.svg'; // Adjust the path as necessary

export default function ProvidersTab() {
  const { providers, updateProviderSettings, isLocalModel } = useSettings();
  const [filteredProviders, setFilteredProviders] = useState<IProviderConfig[]>([]);

  // Load base URLs from cookies
  const [searchTerm, setSearchTerm] = useState('');

  useEffect(() => {
    let newFilteredProviders: IProviderConfig[] = Object.entries(providers).map(([key, value]) => ({
      ...value,
      name: key,
    }));

    if (searchTerm && searchTerm.length > 0) {
      newFilteredProviders = newFilteredProviders.filter((provider) =>
        provider.name.toLowerCase().includes(searchTerm.toLowerCase()),
      );
    }

    if (!isLocalModel) {
      newFilteredProviders = newFilteredProviders.filter((provider) => !LOCAL_PROVIDERS.includes(provider.name));
    }

    newFilteredProviders.sort((a, b) => a.name.localeCompare(b.name));

    // Split providers into regular and URL-configurable
    const regular = newFilteredProviders.filter((p) => !URL_CONFIGURABLE_PROVIDERS.includes(p.name));
    const urlConfigurable = newFilteredProviders.filter((p) => URL_CONFIGURABLE_PROVIDERS.includes(p.name));

    setFilteredProviders([...regular, ...urlConfigurable]);
  }, [providers, searchTerm, isLocalModel]);

  const renderProviderCard = (provider: IProviderConfig) => {
    const envBaseUrlKey = providerBaseUrlEnvKeys[provider.name].baseUrlKey;
    const envBaseUrl = envBaseUrlKey ? import.meta.env[envBaseUrlKey] : undefined;
    const isUrlConfigurable = URL_CONFIGURABLE_PROVIDERS.includes(provider.name);

    return (
      <div

        key={provider.name}

        className="flex flex-col provider-item hover:bg-bolt-elements-bg-depth-3 p-4 rounded-lg border border-bolt-elements-borderColor"

      >

        <div className="flex items-center justify-between mb-2">

          <div className="flex items-center gap-2">

            <img

              src={`/icons/${provider.name}.svg`}

              onError={(e) => {

                e.currentTarget.src = DefaultIcon;

              }}

              alt={`${provider.name} icon`}

              className="w-6 h-6 dark:invert"

            />

            <span className="text-bolt-elements-textPrimary">{provider.name}</span>

          </div>

          <Switch

            className="ml-auto"

            checked={provider.settings.enabled}

            onCheckedChange={(enabled) => {

              updateProviderSettings(provider.name, { ...provider.settings, enabled });



              if (enabled) {

                logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name });

              } else {

                logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name });

              }

            }}

          />

        </div>

        {isUrlConfigurable && provider.settings.enabled && (

          <div className="mt-2">

            {envBaseUrl && (

              <label className="block text-xs text-bolt-elements-textSecondary text-green-300 mb-2">

                Set On (.env) : {envBaseUrl}

              </label>

            )}

            <label className="block text-sm text-bolt-elements-textSecondary mb-2">

              {envBaseUrl ? 'Override Base Url' : 'Base URL '}:{' '}

            </label>

            <input

              type="text"

              value={provider.settings.baseUrl || ''}

              onChange={(e) => {

                let newBaseUrl: string | undefined = e.target.value;



                if (newBaseUrl && newBaseUrl.trim().length === 0) {

                  newBaseUrl = undefined;

                }



                updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl });

                logStore.logProvider(`Base URL updated for ${provider.name}`, {

                  provider: provider.name,

                  baseUrl: newBaseUrl,

                });

              }}

              placeholder={`Enter ${provider.name} base URL`}

              className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"

            />

          </div>

        )}

      </div>
    );
  };

  const regularProviders = filteredProviders.filter((p) => !URL_CONFIGURABLE_PROVIDERS.includes(p.name));
  const urlConfigurableProviders = filteredProviders.filter((p) => URL_CONFIGURABLE_PROVIDERS.includes(p.name));

  return (
    <div className="p-4">

      <div className="flex mb-4">

        <input

          type="text"

          placeholder="Search providers..."

          value={searchTerm}

          onChange={(e) => setSearchTerm(e.target.value)}

          className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"

        />

      </div>



      {/* Regular Providers Grid */}

      <div className="grid grid-cols-2 gap-4 mb-8">{regularProviders.map(renderProviderCard)}</div>



      {/* URL Configurable Providers Section */}

      {urlConfigurableProviders.length > 0 && (

        <div className="mt-8">

          <h3 className="text-lg font-semibold mb-2 text-bolt-elements-textPrimary">Experimental Providers</h3>

          <p className="text-sm text-bolt-elements-textSecondary mb-4">

            These providers are experimental and allow you to run AI models locally or connect to your own

            infrastructure. They require additional setup but offer more flexibility.

          </p>

          <div className="space-y-4">{urlConfigurableProviders.map(renderProviderCard)}</div>

        </div>

      )}

    </div>
  );
}