Skip to content

Commit f80a0e9

Browse files
committed
feat: Update application branding and add AI model catalog feature
- Changed the application title to "DBFuse AI" and updated the favicon to a new logo. - Implemented an authentication guard that handles transient authentication errors and checks for stored tokens. - Added a new service method to fetch AI model catalog from the backend. - Defined a new interface for AI model catalog response. - Updated the login component footer to include a link to Docker Hub. - Enhanced error handling in the home component for AI prompt generation. - Refactored the config component to load AI model catalog on initialization. - Added Docker Hub links to the navbar component. - Introduced a new API endpoint for retrieving AI model catalog. - Improved error handling in the base controller for various AI provider errors. - Updated AI models in constants to include new models and providers. - Removed rate limiting from authentication routes for simplicity. - Enhanced LLM service to handle model initialization and error logging more effectively.
1 parent 5a024c8 commit f80a0e9

25 files changed

Lines changed: 464 additions & 180 deletions

File tree

cli.js

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const argv = require("minimist")(process.argv.slice(2));
88
const chalk = require("chalk");
99

1010
const connectionStore = require("./src/config/connection-store");
11+
const { AI_MODELS } = require("./src/core/constants/ai.constants");
12+
const { inferProviderFromModel, normalizeProvider } = require("./src/core/env");
1113

1214
const MIN_NODE_VERSION = 16;
1315
const MIN_NPM_VERSION = 8;
@@ -24,54 +26,40 @@ require("dotenv").config();
2426
const defaultPort = 5000;
2527
const isVerbose = !!(argv.verbose || argv.v);
2628

29+
// Build CLI model display info from AI_MODELS constants
2730
const supportedModels = {
2831
gemini: {
29-
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
32+
models: AI_MODELS.GEMINI.models,
3033
note: "Free tier available",
3134
description: "Google's latest AI models",
3235
},
3336
openai: {
34-
models: ["gpt-5", "gpt-5-mini", "gpt-5-nano", "gpt-4.1", "gpt-4o"],
37+
models: AI_MODELS.OPENAI.models,
3538
note: "Paid",
3639
description: "OpenAI's ChatGPT models",
3740
},
3841
anthropic: {
39-
models: [
40-
"claude-opus-4-1",
41-
"claude-opus-4",
42-
"claude-sonnet-4",
43-
"claude-3-7-sonnet",
44-
"claude-3-5-haiku",
45-
],
42+
models: AI_MODELS.ANTHROPIC.models,
4643
note: "Paid (Haiku most affordable)",
4744
description: "Anthropic's Claude models",
4845
},
4946
mistral: {
50-
models: ["mistral-medium-2508", "mistral-large-2411", "mistral-small-2407", "codestral-2508"],
47+
models: AI_MODELS.MISTRAL.models,
5148
note: "Paid",
5249
description: "Mistral AI's models",
5350
},
5451
cohere: {
55-
models: [
56-
"command-a-03-2025",
57-
"command-a-reasoning-08-2025",
58-
"command-a-vision-07-2025",
59-
"command-r7b-12-2024",
60-
],
52+
models: AI_MODELS.COHERE.models,
6153
note: "Free tier available",
6254
description: "Cohere's language models",
6355
},
6456
huggingface: {
65-
models: [
66-
"microsoft/DialoGPT-medium",
67-
"facebook/blenderbot-400M-distill",
68-
"microsoft/DialoGPT-large",
69-
],
57+
models: AI_MODELS.HUGGINGFACE.models,
7058
note: "Free",
7159
description: "Open-source models via Hugging Face",
7260
},
7361
perplexity: {
74-
models: ["sonar", "sonar-pro", "sonar-reasoning", "sonar-reasoning-pro", "sonar-deep-research"],
62+
models: AI_MODELS.PERPLEXITY.models,
7563
note: "Paid",
7664
description: "Perplexity's search-enhanced models",
7765
},
@@ -629,7 +617,12 @@ async function main() {
629617
}
630618

631619
if (!provider) {
632-
console.error(chalk.red("Invalid AI model specified. Exiting..."));
620+
const inferredProvider = inferProviderFromModel(argv.model);
621+
provider = normalizeProvider(inferredProvider);
622+
}
623+
624+
if (!provider) {
625+
console.error(chalk.red("Unable to infer AI provider from the model. Exiting..."));
633626
process.exit(1);
634627
}
635628
config.aiProvider = provider;

client/dbfuse-ai-client/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
5-
<title>Angular Vite</title>
5+
<title>DBFuse AI</title>
66
<base href="/" />
77
<meta
88
name="viewport"
99
content="width=device-width, initial-scale=1"
1010
/>
1111
<link
1212
rel="icon"
13-
type="image/x-icon"
14-
href="./favicon.ico"
13+
type="image/png"
14+
href="/assets/dbfuse-ai-logo.png"
1515
/>
1616
<link
1717
rel="preconnect"

client/dbfuse-ai-client/src/app/core/guards/auth.guard.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Router } from '@angular/router';
33
import { AuthService } from '@core/services/auth/auth.service';
44
import { Observable, map, catchError, of } from 'rxjs';
55
import { HttpErrorResponse } from '@angular/common/http';
6+
import { getSafeSessionStorage } from '@core/utils/browser-adapter';
67

78
@Injectable({
89
providedIn: 'root',
@@ -16,14 +17,6 @@ export class AuthGuard {
1617
canActivate(): Observable<boolean> {
1718
return this.authService.isAuthenticated().pipe(
1819
map((response: any) => {
19-
if (response instanceof HttpErrorResponse) {
20-
if (response.status === 401) {
21-
this.router.navigate(['/login'], { replaceUrl: true }); // Absolute path with replaceUrl
22-
return false;
23-
}
24-
throw response;
25-
}
26-
2720
const isAuthenticated = response && response.authenticated === true;
2821
if (isAuthenticated) {
2922
return true;
@@ -33,6 +26,9 @@ export class AuthGuard {
3326
}
3427
}),
3528
catchError((error: HttpErrorResponse) => {
29+
if (this.isTransientAuthError(error) && this.hasStoredToken()) {
30+
return of(true);
31+
}
3632
const state =
3733
error?.status === 401
3834
? { authError: 'Invalid username or password. Please try again.' }
@@ -42,4 +38,13 @@ export class AuthGuard {
4238
}),
4339
);
4440
}
41+
42+
private hasStoredToken(): boolean {
43+
return Boolean(getSafeSessionStorage().getItem('token'));
44+
}
45+
46+
private isTransientAuthError(error: HttpErrorResponse): boolean {
47+
const status = error?.status ?? 0;
48+
return status === 0 || status === 502 || status === 503 || status === 504;
49+
}
4550
}

client/dbfuse-ai-client/src/app/core/services/backend/backend.service.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
ConnectionConfig,
1717
ConfigData,
1818
SaveResponse,
19+
AIModelCatalogResponse,
1920
} from '@core/utils/storage/storage.types';
2021
import { getSafeSessionStorage } from '@core/utils/browser-adapter';
2122

@@ -69,6 +70,12 @@ export class BackendService {
6970
return this._http.post<SaveResponse>(`${this.BASE_URL}/api/config`, config, { headers: this.getHeaders() });
7071
}
7172

73+
getAIModelCatalog(): Observable<AIModelCatalogResponse> {
74+
return this._http.get<AIModelCatalogResponse>(`${this.BASE_URL}/api/config/ai-models`, {
75+
headers: this.getHeaders(),
76+
});
77+
}
78+
7279
// Database Information Methods
7380
getDatabases(): Observable<{ databases: DatabaseStats[]; count: number; retrievedAt: string }> {
7481
return this._http.get<{ databases: DatabaseStats[]; count: number; retrievedAt: string }>(

client/dbfuse-ai-client/src/app/core/utils/storage/storage.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,12 @@ export interface ModelOption {
320320
models: string[];
321321
}
322322

323+
export interface AIModelCatalogResponse {
324+
providers: ModelOption[];
325+
fallbackModel?: string;
326+
generatedAt?: string;
327+
}
328+
323329
export interface OpenAIPromptResponse {
324330
query: string;
325331
explanation?: string;

client/dbfuse-ai-client/src/app/features/auth/login/login.component.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ <h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">Welcome back</
190190
<!-- Footer -->
191191
<div class="text-center">
192192
<p class="text-xs text-gray-500 dark:text-gray-400">Open source MySQL database management tool</p>
193-
<div class="flex justify-center space-x-4 mt-2">
193+
<div class="flex justify-center space-x-4 mt-2">
194194
<a
195195
href="https://github.com/kshashikumar/dbfuse-ai"
196196
target="_blank"
@@ -199,7 +199,16 @@ <h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">Welcome back</
199199
>
200200
GitHub
201201
</a>
202-
<span class="text-gray-300 dark:text-gray-600"></span>
202+
<span class="text-gray-300 dark:text-gray-600">|</span>
203+
<a
204+
href="https://hub.docker.com/r/shashikumarkasturi/dbfuse-ai"
205+
target="_blank"
206+
rel="noopener noreferrer"
207+
class="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-200"
208+
>
209+
Docker Hub
210+
</a>
211+
<span class="text-gray-300 dark:text-gray-600">|</span>
203212
<a
204213
href="https://www.npmjs.com/package/dbfuse-ai"
205214
target="_blank"
@@ -212,3 +221,4 @@ <h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">Welcome back</
212221
</div>
213222
</div>
214223
</div>
224+

client/dbfuse-ai-client/src/app/features/home/home/home.component.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,11 +591,23 @@ export class HomeComponent implements OnInit, OnChanges, OnDestroy {
591591
},
592592
error: (error) => {
593593
console.error('AI prompt error:', error);
594-
alert('Failed to generate SQL query. Please ensure you are connected to a database.');
594+
alert(this.getAIErrorMessage(error));
595595
},
596596
});
597597
}
598598

599+
private getAIErrorMessage(error: any): string {
600+
const fallback = 'Failed to generate SQL query. Please try again.';
601+
if (!error) return fallback;
602+
if (typeof error === 'string') return error;
603+
const body = error.error;
604+
if (typeof body === 'string') return body;
605+
if (body && typeof body.error === 'string') return body.error;
606+
if (body && typeof body.message === 'string') return body.message;
607+
if (typeof error.message === 'string') return error.message;
608+
return fallback;
609+
}
610+
599611
onDiscQueryClick() {
600612
if (this.selectedTab >= 0) {
601613
this.tabContent[this.selectedTab] = '';

client/dbfuse-ai-client/src/app/features/settings/config/config.component.ts

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,45 +34,7 @@ export class ConfigComponent implements OnInit {
3434
showConnectionsKey = false;
3535
pendingConnectionsReset = false;
3636

37-
supportedModels: ModelOption[] = [
38-
{
39-
provider: 'Gemini',
40-
models: ['gemini-2.5-flash', 'gemini-2.5-pro'],
41-
},
42-
{
43-
provider: 'OpenAI',
44-
models: ['gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1', 'gpt-4o'],
45-
},
46-
{
47-
provider: 'Anthropic',
48-
models: ['claude-opus-4-1', 'claude-opus-4', 'claude-sonnet-4', 'claude-3-7-sonnet', 'claude-3-5-haiku'],
49-
},
50-
{
51-
provider: 'Mistral',
52-
models: ['mistral-medium-2508', 'mistral-large-2411', 'mistral-small-2407', 'codestral-2508'],
53-
},
54-
{
55-
provider: 'Cohere',
56-
models: [
57-
'command-a-03-2025',
58-
'command-a-reasoning-08-2025',
59-
'command-a-vision-07-2025',
60-
'command-r7b-12-2024',
61-
],
62-
},
63-
{
64-
provider: 'HuggingFace',
65-
models: [
66-
'meta-llama/Llama-3.1-8B-Instruct',
67-
'meta-llama/Llama-3.1-70B-Instruct',
68-
'Qwen/Qwen2.5-7B-Instruct',
69-
],
70-
},
71-
{
72-
provider: 'Perplexity',
73-
models: ['sonar', 'sonar-pro', 'sonar-reasoning', 'sonar-reasoning-pro', 'sonar-deep-research'],
74-
},
75-
];
37+
supportedModels: ModelOption[] = [];
7638

7739
constructor(
7840
private backendService: BackendService,
@@ -82,6 +44,20 @@ export class ConfigComponent implements OnInit {
8244

8345
ngOnInit() {
8446
this.loadConfig();
47+
this.loadModelCatalog();
48+
}
49+
50+
loadModelCatalog() {
51+
this.backendService.getAIModelCatalog().subscribe({
52+
next: (data) => {
53+
this.supportedModels = data?.providers || [];
54+
},
55+
error: (error) => {
56+
console.warn('Failed to load AI model catalog.', error);
57+
this.supportedModels = [];
58+
this.showMessage('Failed to load AI model catalog', 'error');
59+
},
60+
});
8561
}
8662

8763
loadConfig() {

client/dbfuse-ai-client/src/app/layouts/components/navbar/navbar.component.html

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@
5353
</svg>
5454
<span class="hidden lg:ml-2 lg:block">GitHub</span>
5555
</a>
56+
<a
57+
href="https://hub.docker.com/r/shashikumarkasturi/dbfuse-ai"
58+
target="_blank"
59+
rel="noopener noreferrer"
60+
class="inline-flex items-center px-3 py-2 text-sm font-medium text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400 transition-colors duration-200"
61+
title="View on Docker Hub"
62+
>
63+
<svg
64+
class="w-5 h-5"
65+
fill="currentColor"
66+
viewBox="0 0 24 24"
67+
>
68+
<path
69+
d="M12 3l9 4-9 4-9-4 9-4zm-9 4v10l9 4 9-4V7l-9 4-9-4z"
70+
></path>
71+
</svg>
72+
<span class="hidden lg:ml-2 lg:block">Docker Hub</span>
73+
</a>
5674
<a
5775
href="https://www.npmjs.com/package/dbfuse-ai"
5876
target="_blank"
@@ -216,6 +234,24 @@
216234
</svg>
217235
GitHub
218236
</a>
237+
<a
238+
href="https://hub.docker.com/r/shashikumarkasturi/dbfuse-ai"
239+
target="_blank"
240+
rel="noopener noreferrer"
241+
class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700 transition-colors duration-150"
242+
role="menuitem"
243+
>
244+
<svg
245+
class="w-4 h-4 mr-3"
246+
fill="currentColor"
247+
viewBox="0 0 24 24"
248+
>
249+
<path
250+
d="M12 3l9 4-9 4-9-4 9-4zm-9 4v10l9 4 9-4V7l-9 4-9-4z"
251+
></path>
252+
</svg>
253+
Docker Hub
254+
</a>
219255
<a
220256
href="https://www.npmjs.com/package/dbfuse-ai"
221257
target="_blank"
9.65 KB
Loading

0 commit comments

Comments
 (0)