ArunKr commited on
Commit
2ab4e26
·
verified ·
1 Parent(s): 84ff62b

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. main.py +4 -1
  2. static/dashboard.html +146 -18
  3. supabase_provider_configs.sql +59 -0
main.py CHANGED
@@ -33,7 +33,10 @@ app.mount("/static", StaticFiles(directory="static"), name="static")
33
  async def get_config():
34
  return {
35
  "supabase_url": os.environ.get("SUPABASE_URL", "https://znhglkwefxdhgajvrqmb.supabase.co"),
36
- "supabase_key": os.environ.get("SUPABASE_KEY")
 
 
 
37
  }
38
 
39
  @app.get("/")
 
33
  async def get_config():
34
  return {
35
  "supabase_url": os.environ.get("SUPABASE_URL", "https://znhglkwefxdhgajvrqmb.supabase.co"),
36
+ "supabase_key": os.environ.get("SUPABASE_KEY"),
37
+ "default_base_url": os.environ.get("DEFAULT_BASE_URL", "https://router.huggingface.co/v1"),
38
+ "default_api_key": os.environ.get("DEFAULT_API_KEY", ""),
39
+ "default_model": os.environ.get("DEFAULT_MODEL", "gpt-3.5-turbo"),
40
  }
41
 
42
  @app.get("/")
static/dashboard.html CHANGED
@@ -246,6 +246,17 @@
246
  class="bg-red-600 hover:bg-red-700 text-white px-2 rounded text-xs">Del</button>
247
  </div>
248
  </div>
 
 
 
 
 
 
 
 
 
 
 
249
  <div>
250
  <label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">API Key</label>
251
  <input type="password" id="chat-api-key" placeholder="sk-..."
@@ -549,6 +560,18 @@
549
  window.location.href = '/';
550
  });
551
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  loadProviders();
553
  await loadChatHistoryList();
554
  createTerminalTab(); // Init first tab
@@ -1036,11 +1059,51 @@
1036
  } catch (e) { alert(e.message); }
1037
  }
1038
 
1039
- function loadProviders() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1040
  const select = document.getElementById('saved-providers');
1041
  const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
 
1042
  select.innerHTML = '<option value="">Select...</option>';
1043
- Object.keys(saved).forEach(k => {
1044
  const opt = document.createElement('option');
1045
  opt.value = k;
1046
  opt.innerText = k;
@@ -1048,39 +1111,104 @@
1048
  });
1049
  }
1050
 
1051
- function saveProvider() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1052
  const name = prompt("Name:");
1053
  if (!name) return;
 
1054
  const config = {
1055
  apiKey: document.getElementById('chat-api-key').value,
1056
  baseUrl: document.getElementById('chat-base-url').value,
1057
  model: document.getElementById('chat-model').value
1058
  };
1059
- const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1060
- saved[name] = config;
1061
- localStorage.setItem('chat_providers', JSON.stringify(saved));
1062
- loadProviders();
1063
- document.getElementById('saved-providers').value = name;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  }
1065
 
1066
- function deleteProvider() {
1067
  const name = document.getElementById('saved-providers').value;
1068
  if (!name) return;
1069
  if (!confirm("Delete?")) return;
1070
- const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1071
- delete saved[name];
1072
- localStorage.setItem('chat_providers', JSON.stringify(saved));
1073
- loadProviders();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1074
  }
1075
 
1076
  function loadSelectedProvider(name) {
1077
  if (!name) return;
1078
- const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1079
- const config = saved[name];
1080
  if (config) {
1081
- document.getElementById('chat-api-key').value = config.apiKey;
1082
- document.getElementById('chat-base-url').value = config.baseUrl;
1083
- document.getElementById('chat-model').value = config.model;
1084
  }
1085
  }
1086
 
 
246
  class="bg-red-600 hover:bg-red-700 text-white px-2 rounded text-xs">Del</button>
247
  </div>
248
  </div>
249
+ <div>
250
+ <label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Provider Preset</label>
251
+ <div class="flex gap-2">
252
+ <select id="provider-presets" onchange="applyProviderPreset(this.value)"
253
+ class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
254
+ <option value="">Select preset...</option>
255
+ </select>
256
+ <button onclick="applyProviderPreset(document.getElementById('provider-presets').value)"
257
+ class="bg-gray-600 hover:bg-gray-700 text-white px-2 rounded text-xs">Apply</button>
258
+ </div>
259
+ </div>
260
  <div>
261
  <label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">API Key</label>
262
  <input type="password" id="chat-api-key" placeholder="sk-..."
 
560
  window.location.href = '/';
561
  });
562
 
563
+ initProviderPresets();
564
+ // Apply defaults if fields are empty
565
+ if (config.default_base_url && !document.getElementById('chat-base-url').value) {
566
+ document.getElementById('chat-base-url').value = config.default_base_url;
567
+ }
568
+ if (config.default_api_key && !document.getElementById('chat-api-key').value) {
569
+ document.getElementById('chat-api-key').value = config.default_api_key;
570
+ }
571
+ if (config.default_model && !document.getElementById('chat-model').value) {
572
+ document.getElementById('chat-model').value = config.default_model;
573
+ }
574
+
575
  loadProviders();
576
  await loadChatHistoryList();
577
  createTerminalTab(); // Init first tab
 
1059
  } catch (e) { alert(e.message); }
1060
  }
1061
 
1062
+ const STANDARD_PROVIDERS = [
1063
+ { key: 'huggingface-router', name: 'HuggingFace Router', baseUrl: 'https://router.huggingface.co/v1' },
1064
+ { key: 'openai', name: 'OpenAI', baseUrl: 'https://api.openai.com/v1' },
1065
+ { key: 'openrouter', name: 'OpenRouter', baseUrl: 'https://openrouter.ai/api/v1' },
1066
+ { key: 'groq', name: 'Groq (OpenAI-compatible)', baseUrl: 'https://api.groq.com/openai/v1' },
1067
+ { key: 'together', name: 'Together', baseUrl: 'https://api.together.xyz/v1' },
1068
+ { key: 'fireworks', name: 'Fireworks', baseUrl: 'https://api.fireworks.ai/inference/v1' },
1069
+ { key: 'mistral', name: 'Mistral', baseUrl: 'https://api.mistral.ai/v1' },
1070
+ { key: 'deepseek', name: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1' },
1071
+ ];
1072
+
1073
+ let providerCache = {}; // name -> { apiKey, baseUrl, model }
1074
+ let warnedProviderFallback = false;
1075
+
1076
+ function initProviderPresets() {
1077
+ const presets = document.getElementById('provider-presets');
1078
+ if (!presets) return;
1079
+ presets.innerHTML = '<option value="">Select preset...</option>';
1080
+ STANDARD_PROVIDERS.forEach((p) => {
1081
+ const opt = document.createElement('option');
1082
+ opt.value = p.key;
1083
+ opt.innerText = `${p.name} — ${p.baseUrl}`;
1084
+ presets.appendChild(opt);
1085
+ });
1086
+ }
1087
+
1088
+ function applyProviderPreset(key) {
1089
+ if (!key) return;
1090
+ const preset = STANDARD_PROVIDERS.find((p) => p.key === key);
1091
+ if (!preset) return;
1092
+ document.getElementById('chat-base-url').value = preset.baseUrl;
1093
+ }
1094
+
1095
+ async function getActiveUserId() {
1096
+ const { data, error } = await supabase.auth.getUser();
1097
+ if (error) throw error;
1098
+ return data?.user?.id || null;
1099
+ }
1100
+
1101
+ function loadProvidersFromLocalStorage() {
1102
  const select = document.getElementById('saved-providers');
1103
  const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1104
+ providerCache = saved;
1105
  select.innerHTML = '<option value="">Select...</option>';
1106
+ Object.keys(saved).forEach((k) => {
1107
  const opt = document.createElement('option');
1108
  opt.value = k;
1109
  opt.innerText = k;
 
1111
  });
1112
  }
1113
 
1114
+ async function loadProviders() {
1115
+ try {
1116
+ const uid = await getActiveUserId();
1117
+ if (!uid) return loadProvidersFromLocalStorage();
1118
+
1119
+ const { data, error } = await supabase
1120
+ .from('provider_configs')
1121
+ .select('name, api_key, base_url, model')
1122
+ .eq('user_id', uid)
1123
+ .order('name', { ascending: true });
1124
+ if (error) throw error;
1125
+
1126
+ providerCache = {};
1127
+ (data || []).forEach((row) => {
1128
+ providerCache[row.name] = { apiKey: row.api_key || '', baseUrl: row.base_url || '', model: row.model || '' };
1129
+ });
1130
+
1131
+ const select = document.getElementById('saved-providers');
1132
+ select.innerHTML = '<option value="">Select...</option>';
1133
+ Object.keys(providerCache).forEach((k) => {
1134
+ const opt = document.createElement('option');
1135
+ opt.value = k;
1136
+ opt.innerText = k;
1137
+ select.appendChild(opt);
1138
+ });
1139
+ } catch (e) {
1140
+ if (!warnedProviderFallback) {
1141
+ warnedProviderFallback = true;
1142
+ console.warn('Provider configs table missing or RLS blocked; falling back to localStorage.', e);
1143
+ }
1144
+ loadProvidersFromLocalStorage();
1145
+ }
1146
+ }
1147
+
1148
+ async function saveProvider() {
1149
  const name = prompt("Name:");
1150
  if (!name) return;
1151
+
1152
  const config = {
1153
  apiKey: document.getElementById('chat-api-key').value,
1154
  baseUrl: document.getElementById('chat-base-url').value,
1155
  model: document.getElementById('chat-model').value
1156
  };
1157
+
1158
+ try {
1159
+ const uid = await getActiveUserId();
1160
+ if (!uid) throw new Error('No active user session');
1161
+
1162
+ const { error } = await supabase
1163
+ .from('provider_configs')
1164
+ .upsert(
1165
+ { user_id: uid, name, api_key: config.apiKey, base_url: config.baseUrl, model: config.model },
1166
+ { onConflict: 'user_id,name' }
1167
+ );
1168
+ if (error) throw error;
1169
+ await loadProviders();
1170
+ document.getElementById('saved-providers').value = name;
1171
+ } catch (e) {
1172
+ // Fallback to local storage if table isn't available.
1173
+ const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1174
+ saved[name] = config;
1175
+ localStorage.setItem('chat_providers', JSON.stringify(saved));
1176
+ loadProvidersFromLocalStorage();
1177
+ document.getElementById('saved-providers').value = name;
1178
+ }
1179
  }
1180
 
1181
+ async function deleteProvider() {
1182
  const name = document.getElementById('saved-providers').value;
1183
  if (!name) return;
1184
  if (!confirm("Delete?")) return;
1185
+
1186
+ try {
1187
+ const uid = await getActiveUserId();
1188
+ if (!uid) throw new Error('No active user session');
1189
+
1190
+ const { error } = await supabase
1191
+ .from('provider_configs')
1192
+ .delete()
1193
+ .eq('user_id', uid)
1194
+ .eq('name', name);
1195
+ if (error) throw error;
1196
+ await loadProviders();
1197
+ } catch (e) {
1198
+ const saved = JSON.parse(localStorage.getItem('chat_providers') || '{}');
1199
+ delete saved[name];
1200
+ localStorage.setItem('chat_providers', JSON.stringify(saved));
1201
+ loadProvidersFromLocalStorage();
1202
+ }
1203
  }
1204
 
1205
  function loadSelectedProvider(name) {
1206
  if (!name) return;
1207
+ const config = providerCache[name];
 
1208
  if (config) {
1209
+ document.getElementById('chat-api-key').value = config.apiKey || '';
1210
+ document.getElementById('chat-base-url').value = config.baseUrl || '';
1211
+ document.getElementById('chat-model').value = config.model || '';
1212
  }
1213
  }
1214
 
supabase_provider_configs.sql ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Provider config storage for per-user API keys and base URLs.
2
+ -- Run this in Supabase SQL Editor.
3
+
4
+ create table if not exists public.provider_configs (
5
+ id uuid primary key default gen_random_uuid(),
6
+ user_id uuid not null references auth.users (id) on delete cascade,
7
+ name text not null,
8
+ api_key text not null default '',
9
+ base_url text not null default '',
10
+ model text not null default '',
11
+ created_at timestamptz not null default now(),
12
+ updated_at timestamptz not null default now(),
13
+ unique (user_id, name)
14
+ );
15
+
16
+ -- Keep updated_at current
17
+ create or replace function public.set_updated_at()
18
+ returns trigger
19
+ language plpgsql
20
+ as $$
21
+ begin
22
+ new.updated_at = now();
23
+ return new;
24
+ end;
25
+ $$;
26
+
27
+ drop trigger if exists provider_configs_set_updated_at on public.provider_configs;
28
+ create trigger provider_configs_set_updated_at
29
+ before update on public.provider_configs
30
+ for each row
31
+ execute procedure public.set_updated_at();
32
+
33
+ alter table public.provider_configs enable row level security;
34
+
35
+ drop policy if exists "provider_configs_select_own" on public.provider_configs;
36
+ create policy "provider_configs_select_own"
37
+ on public.provider_configs
38
+ for select
39
+ using (auth.uid() = user_id);
40
+
41
+ drop policy if exists "provider_configs_insert_own" on public.provider_configs;
42
+ create policy "provider_configs_insert_own"
43
+ on public.provider_configs
44
+ for insert
45
+ with check (auth.uid() = user_id);
46
+
47
+ drop policy if exists "provider_configs_update_own" on public.provider_configs;
48
+ create policy "provider_configs_update_own"
49
+ on public.provider_configs
50
+ for update
51
+ using (auth.uid() = user_id)
52
+ with check (auth.uid() = user_id);
53
+
54
+ drop policy if exists "provider_configs_delete_own" on public.provider_configs;
55
+ create policy "provider_configs_delete_own"
56
+ on public.provider_configs
57
+ for delete
58
+ using (auth.uid() = user_id);
59
+