,
}
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index b3ebd7d0..5b7449b1 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,5 +1,5 @@
{
- "productName": "TFTSR",
+ "productName": "Troubleshooting and RCA Assistant",
"version": "0.2.10",
"identifier": "com.tftsr.devops",
"build": {
@@ -14,7 +14,7 @@
},
"windows": [
{
- "title": "TFTSR \u2014 IT Triage & RCA",
+ "title": "Troubleshooting and RCA Assistant",
"width": 1280,
"height": 800,
"resizable": true,
@@ -36,9 +36,9 @@
],
"resources": [],
"externalBin": [],
- "copyright": "TFTSR Contributors",
+ "copyright": "Troubleshooting and RCA Assistant Contributors",
"category": "Utility",
- "shortDescription": "IT Incident Triage & RCA Tool",
- "longDescription": "Structured AI-backed tool for IT incident triage, 5-whys root cause analysis, and post-mortem documentation with offline Ollama support."
+ "shortDescription": "Troubleshooting and RCA Assistant",
+ "longDescription": "Structured AI-backed assistant for IT troubleshooting, 5-whys root cause analysis, and post-mortem documentation with offline Ollama support."
}
}
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
index 1f2ba40a..82226fea 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -59,7 +59,7 @@ export default function App() {
{!collapsed && (
- TFTSR
+ Troubleshooting and RCA Assistant
)}
Dashboard
- IT Triage & Root Cause Analysis
+ Troubleshooting and Root Cause Analysis Assistant
diff --git a/src/pages/Settings/AIProviders.tsx b/src/pages/Settings/AIProviders.tsx
index dec32ea1..f4911c34 100644
--- a/src/pages/Settings/AIProviders.tsx
+++ b/src/pages/Settings/AIProviders.tsx
@@ -19,6 +19,35 @@ import {
import { useSettingsStore } from "@/stores/settingsStore";
import { testProviderConnectionCmd, type ProviderConfig } from "@/lib/tauriCommands";
+export const CUSTOM_REST_MODELS = [
+ "ChatGPT4o",
+ "ChatGPT4o-mini",
+ "ChatGPT-o3-mini",
+ "Gemini-2_0-Flash-001",
+ "Gemini-2_5-Flash",
+ "Claude-Sonnet-3_7",
+ "Openai-gpt-4_1-mini",
+ "Openai-o4-mini",
+ "Claude-Sonnet-4",
+ "ChatGPT-o3-pro",
+ "OpenAI-ChatGPT-4_1",
+ "OpenAI-GPT-4_1-Nano",
+ "ChatGPT-5",
+ "VertexGemini",
+ "ChatGPT-5_1",
+ "ChatGPT-5_1-chat",
+ "ChatGPT-5_2-Chat",
+ "Gemini-3_Pro-Preview",
+ "Gemini-3_1-flash-lite-preview",
+] as const;
+
+export const CUSTOM_MODEL_OPTION = "__custom_model__";
+export const LEGACY_API_FORMAT = "msi_genai";
+export const CUSTOM_REST_FORMAT = "custom_rest";
+
+export const normalizeApiFormat = (format?: string): string | undefined =>
+ format === LEGACY_API_FORMAT ? CUSTOM_REST_FORMAT : format;
+
const emptyProvider: ProviderConfig = {
name: "",
provider_type: "openai",
@@ -50,19 +79,39 @@ export default function AIProviders() {
const [form, setForm] = useState
({ ...emptyProvider });
const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);
const [isTesting, setIsTesting] = useState(false);
+ const [isCustomModel, setIsCustomModel] = useState(false);
+ const [customModelInput, setCustomModelInput] = useState("");
const startAdd = () => {
setForm({ ...emptyProvider });
setEditIndex(null);
setIsAdding(true);
setTestResult(null);
+ setIsCustomModel(false);
+ setCustomModelInput("");
};
const startEdit = (index: number) => {
- setForm({ ...ai_providers[index] });
+ const provider = ai_providers[index];
+ const apiFormat = normalizeApiFormat(provider.api_format);
+ const nextForm = { ...provider, api_format: apiFormat };
+
+ setForm(nextForm);
setEditIndex(index);
setIsAdding(true);
setTestResult(null);
+
+ const isCustomRestProvider =
+ nextForm.provider_type === "custom" && apiFormat === CUSTOM_REST_FORMAT;
+ const knownModel = CUSTOM_REST_MODELS.includes(nextForm.model as (typeof CUSTOM_REST_MODELS)[number]);
+
+ if (isCustomRestProvider && !knownModel) {
+ setIsCustomModel(true);
+ setCustomModelInput(nextForm.model);
+ } else {
+ setIsCustomModel(false);
+ setCustomModelInput("");
+ }
};
const handleSave = () => {
@@ -244,11 +293,21 @@ export default function AIProviders() {
Model
- setForm({ ...form, model: e.target.value })}
- placeholder="gpt-4o"
- />
+ {form.provider_type === "custom"
+ && normalizeApiFormat(form.api_format) === CUSTOM_REST_FORMAT ? (
+ setForm({ ...form, model: e.target.value })}
+ placeholder="Select API Format below to choose model"
+ disabled
+ />
+ ) : (
+ setForm({ ...form, model: e.target.value })}
+ placeholder="gpt-4o"
+ />
+ )}
@@ -285,7 +344,7 @@ export default function AIProviders() {
onValueChange={(v) => {
const format = v;
const defaults =
- format === "msi_genai"
+ format === CUSTOM_REST_FORMAT
? {
custom_endpoint_path: "",
custom_auth_header: "x-msi-genai-api-key",
@@ -297,6 +356,10 @@ export default function AIProviders() {
custom_auth_prefix: "Bearer ",
};
setForm({ ...form, api_format: format, ...defaults });
+ if (format !== CUSTOM_REST_FORMAT) {
+ setIsCustomModel(false);
+ setCustomModelInput("");
+ }
}}
>
@@ -304,11 +367,11 @@ export default function AIProviders() {
OpenAI Compatible
- MSI GenAI
+ Custom REST
- Select the API format. MSI GenAI uses a different request/response structure.
+ Select the API format. Custom REST uses a non-OpenAI request/response structure.
@@ -349,12 +412,12 @@ export default function AIProviders() {
placeholder="Bearer "
/>
- Prefix added before API key (e.g., "Bearer " for OpenAI, empty for MSI GenAI)
+ Prefix added before API key (e.g., "Bearer " for OpenAI, empty for Custom REST)
- {/* MSI GenAI specific: User ID field */}
- {form.api_format === "msi_genai" && (
+ {/* Custom REST specific: User ID field */}
+ {normalizeApiFormat(form.api_format) === CUSTOM_REST_FORMAT && (
User ID (CORE ID)
)}
+
+ {/* Custom REST specific: model dropdown with custom option */}
+ {normalizeApiFormat(form.api_format) === CUSTOM_REST_FORMAT && (
+
+ Model
+ {
+ if (value === CUSTOM_MODEL_OPTION) {
+ setIsCustomModel(true);
+ if (CUSTOM_REST_MODELS.includes(form.model as (typeof CUSTOM_REST_MODELS)[number])) {
+ setForm({ ...form, model: "" });
+ setCustomModelInput("");
+ }
+ } else {
+ setIsCustomModel(false);
+ setCustomModelInput("");
+ setForm({ ...form, model: value });
+ }
+ }}
+ >
+
+
+
+
+ {CUSTOM_REST_MODELS.map((model) => (
+
+ {model}
+
+ ))}
+ Custom model...
+
+
+ {isCustomModel && (
+ {
+ const value = e.target.value;
+ setCustomModelInput(value);
+ setForm({ ...form, model: value });
+ }}
+ placeholder="Enter custom model ID"
+ />
+ )}
+
+ )}
>
)}
diff --git a/src/pages/Settings/Integrations.tsx b/src/pages/Settings/Integrations.tsx
index 42635506..0fd41a6e 100644
--- a/src/pages/Settings/Integrations.tsx
+++ b/src/pages/Settings/Integrations.tsx
@@ -453,7 +453,7 @@ export default function Integrations() {
Integrations
- Connect TFTSR with your existing tools and platforms. Choose the authentication method that works best for your environment.
+ Connect Troubleshooting and RCA Assistant with your existing tools and platforms. Choose the authentication method that works best for your environment.
diff --git a/tests/unit/aiProvidersCustomRest.test.ts b/tests/unit/aiProvidersCustomRest.test.ts
new file mode 100644
index 00000000..bc4819f9
--- /dev/null
+++ b/tests/unit/aiProvidersCustomRest.test.ts
@@ -0,0 +1,25 @@
+import { describe, it, expect } from "vitest";
+import {
+ CUSTOM_MODEL_OPTION,
+ CUSTOM_REST_FORMAT,
+ CUSTOM_REST_MODELS,
+ LEGACY_API_FORMAT,
+ normalizeApiFormat,
+} from "@/pages/Settings/AIProviders";
+
+describe("AIProviders Custom REST helpers", () => {
+ it("maps legacy msi_genai api_format to custom_rest", () => {
+ expect(normalizeApiFormat(LEGACY_API_FORMAT)).toBe(CUSTOM_REST_FORMAT);
+ });
+
+ it("keeps openai api_format unchanged", () => {
+ expect(normalizeApiFormat("openai")).toBe("openai");
+ });
+
+ it("contains the guide model list and custom model option sentinel", () => {
+ expect(CUSTOM_REST_MODELS).toContain("ChatGPT4o");
+ expect(CUSTOM_REST_MODELS).toContain("VertexGemini");
+ expect(CUSTOM_REST_MODELS).toContain("Gemini-3_Pro-Preview");
+ expect(CUSTOM_MODEL_OPTION).toBe("__custom_model__");
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index 90c8a375..09dca661 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,7 +17,7 @@
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": { "@/*": ["src/*"] },
- "types": ["vitest/globals"]
+ "types": ["vitest/globals", "@testing-library/jest-dom"]
},
"include": ["src", "tests/unit"],
"references": [{ "path": "./tsconfig.node.json" }]