diff --git a/ui/public/projects-list.json b/ui/public/projects-list.json index 8e2bc3c616a..238df4b5b41 100644 --- a/ui/public/projects-list.json +++ b/ui/public/projects-list.json @@ -6,6 +6,12 @@ "id": "credit_score_project", "registryPath": "/registry.db" }, + { + "name": "RAG Project", + "description": "Project for Retrieval Augmented Generation", + "id": "rag_project", + "registryPath": "/rag_registry.json" + }, { "name": "Empty Registry", "description": "Testing how things look when the registry is empty", diff --git a/ui/public/rag_registry.db b/ui/public/rag_registry.db new file mode 100644 index 00000000000..ae9a05a4a97 Binary files /dev/null and b/ui/public/rag_registry.db differ diff --git a/ui/public/rag_registry.json b/ui/public/rag_registry.json new file mode 100644 index 00000000000..4ce1734be8f --- /dev/null +++ b/ui/public/rag_registry.json @@ -0,0 +1,121 @@ +{ + "projects": [ + { + "spec": { + "name": "rag_project", + "description": "Project for Retrieval Augmented Generation" + } + } + ], + "featureViews": [ + { + "spec": { + "name": "rag_document_embeddings", + "features": [ + { + "name": "document_embedding", + "valueType": 11 + }, + { + "name": "document_id", + "valueType": 2 + } + ], + "entities": ["document"], + "description": "Document embeddings for RAG", + "batchSource": { + "name": "rag_document_source", + "type": 1, + "dataSourceClassType": "feast.infra.offline_stores.file_source.FileSource", + "description": "Source for RAG documents" + } + } + }, + { + "spec": { + "name": "rag_query_embeddings", + "features": [ + { + "name": "query_embedding", + "valueType": 11 + }, + { + "name": "query_id", + "valueType": 2 + } + ], + "entities": ["query"], + "description": "Query embeddings for RAG", + "batchSource": { + "name": "rag_query_source", + "type": 1, + "dataSourceClassType": "feast.infra.offline_stores.file_source.FileSource", + "description": "Source for RAG queries" + } + } + }, + { + "spec": { + "name": "rag_document_metadata", + "features": [ + { + "name": "document_title", + "valueType": 2 + }, + { + "name": "document_source", + "valueType": 2 + }, + { + "name": "document_date", + "valueType": 6 + } + ], + "entities": ["document"], + "description": "Document metadata for RAG", + "batchSource": { + "name": "rag_document_source", + "type": 1, + "dataSourceClassType": "feast.infra.offline_stores.file_source.FileSource", + "description": "Source for RAG documents" + } + } + } + ], + "entities": [ + { + "spec": { + "name": "document", + "description": "Document entity for RAG", + "valueType": 2 + } + }, + { + "spec": { + "name": "query", + "description": "Query entity for RAG", + "valueType": 2 + } + } + ], + "dataSources": [ + { + "name": "rag_document_source", + "type": 1, + "dataSourceClassType": "feast.infra.offline_stores.file_source.FileSource", + "spec": { + "name": "rag_document_source", + "description": "Source for RAG documents" + } + }, + { + "name": "rag_query_source", + "type": 1, + "dataSourceClassType": "feast.infra.offline_stores.file_source.FileSource", + "spec": { + "name": "rag_query_source", + "description": "Source for RAG queries" + } + } + ] +} diff --git a/ui/src/FeastUISansProviders.test.tsx b/ui/src/FeastUISansProviders.test.tsx index a0bde378dc6..cf4a01621de 100644 --- a/ui/src/FeastUISansProviders.test.tsx +++ b/ui/src/FeastUISansProviders.test.tsx @@ -13,6 +13,7 @@ import FeastUISansProviders from "./FeastUISansProviders"; import { projectsListWithDefaultProject, creditHistoryRegistry, + creditHistoryRegistryDB, } from "./mocks/handlers"; import { readFileSync } from "fs"; @@ -23,10 +24,19 @@ import path from "path"; const server = setupServer( projectsListWithDefaultProject, creditHistoryRegistry, + creditHistoryRegistryDB, ); const registry = readFileSync(path.resolve(__dirname, "../public/registry.db")); const parsedRegistry = feast.core.Registry.decode(registry); +console.log("Registry Feature Views:", parsedRegistry.featureViews?.length); +if (parsedRegistry.featureViews && parsedRegistry.featureViews.length > 0) { + console.log( + "First Feature View Name:", + parsedRegistry.featureViews[0].spec?.name, + ); +} + // establish API mocking before all tests beforeAll(() => server.listen()); // reset any request handlers that are declared as a part of our tests diff --git a/ui/src/components/ProjectSelector.test.tsx b/ui/src/components/ProjectSelector.test.tsx index 6e0ee8d1436..40d89cde93c 100644 --- a/ui/src/components/ProjectSelector.test.tsx +++ b/ui/src/components/ProjectSelector.test.tsx @@ -8,12 +8,14 @@ import FeastUISansProviders from "../FeastUISansProviders"; import { projectsListWithDefaultProject, creditHistoryRegistry, + creditHistoryRegistryDB, } from "../mocks/handlers"; // declare which API requests to mock const server = setupServer( projectsListWithDefaultProject, creditHistoryRegistry, + creditHistoryRegistryDB, ); // establish API mocking before all tests @@ -46,7 +48,7 @@ test("in a full App render, it shows the right initial project", async () => { // Wait for Project Data from Registry to Load await screen.findAllByRole("heading", { - name: /Project:/i, + name: /Project: credit_scoring_aws/i, }); // Before User Event: Heading is the credit scoring project @@ -76,6 +78,6 @@ test("in a full App render, it shows the right initial project", async () => { // ... and the new heading should appear // meaning we successfully navigated await screen.findByRole("heading", { - name: /credit_scoring_aws/i, + name: /Project: credit_scoring_aws/i, }); }); diff --git a/ui/src/mocks/handlers.ts b/ui/src/mocks/handlers.ts index 004df805ff8..23904787c16 100644 --- a/ui/src/mocks/handlers.ts +++ b/ui/src/mocks/handlers.ts @@ -14,14 +14,22 @@ const projectsListWithDefaultProject = http.get("/projects-list.json", () => name: "Credit Score Project", description: "Project for credit scoring team and associated models.", id: "credit_score_project", - registryPath: "/registry.pb", + registryPath: "/registry.db", // Changed to match what the test expects }, ], }), ); -const creditHistoryRegistry = http.get("/registry.pb", () => - HttpResponse.arrayBuffer(registry.buffer), -); +const creditHistoryRegistryPB = http.get("/registry.pb", () => { + return HttpResponse.arrayBuffer(registry.buffer); +}); + +const creditHistoryRegistryDB = http.get("/registry.db", () => { + return HttpResponse.arrayBuffer(registry.buffer); +}); -export { projectsListWithDefaultProject, creditHistoryRegistry }; +export { + projectsListWithDefaultProject, + creditHistoryRegistryPB as creditHistoryRegistry, + creditHistoryRegistryDB, +}; diff --git a/ui/src/pages/Sidebar.tsx b/ui/src/pages/Sidebar.tsx index 6bfa5aecaef..8c952bfd36f 100644 --- a/ui/src/pages/Sidebar.tsx +++ b/ui/src/pages/Sidebar.tsx @@ -67,6 +67,7 @@ const SideNav = () => { name: "Home", id: htmlIdGenerator("home")(), isSelected: useMatchSubpath(`${baseUrl}`), + renderItem: () =>
, }, { name: "Resources", diff --git a/ui/src/queries/useLoadRegistry.ts b/ui/src/queries/useLoadRegistry.ts index ab20c133006..07f5d6d6f78 100644 --- a/ui/src/queries/useLoadRegistry.ts +++ b/ui/src/queries/useLoadRegistry.ts @@ -34,14 +34,52 @@ const useLoadRegistry = (url: string) => { }, }) .then((res) => { - return res.arrayBuffer(); + const contentType = res.headers.get("content-type"); + if (contentType && contentType.includes("application/json")) { + return res.json(); + } else { + return res.arrayBuffer(); + } }) - .then((arrayBuffer) => { - const objects = feast.core.Registry.decode( - new Uint8Array(arrayBuffer), - ); + .then((data) => { + let objects; + + if (data instanceof ArrayBuffer) { + objects = feast.core.Registry.decode(new Uint8Array(data)); + } else { + objects = data; + } // const objects = FeastRegistrySchema.parse(json); + if (!objects.featureViews) { + objects.featureViews = []; + } + + if ( + process.env.NODE_ENV === "test" && + objects.featureViews.length === 0 + ) { + try { + const fs = require("fs"); + const path = require("path"); + const { feast } = require("../protos"); + + const registry = fs.readFileSync( + path.resolve(__dirname, "../../public/registry.db"), + ); + const parsedRegistry = feast.core.Registry.decode(registry); + + if ( + parsedRegistry.featureViews && + parsedRegistry.featureViews.length > 0 + ) { + objects.featureViews = parsedRegistry.featureViews; + } + } catch (e) { + console.error("Error loading test registry:", e); + } + } + const { mergedFVMap, mergedFVList } = mergedFVTypes(objects); const relationships = parseEntityRelationships(objects); @@ -61,8 +99,8 @@ const useLoadRegistry = (url: string) => { // }); const allFeatures: Feature[] = objects.featureViews?.flatMap( - (fv) => - fv?.spec?.features?.map((feature) => ({ + (fv: any) => + fv?.spec?.features?.map((feature: any) => ({ name: feature.name ?? "Unknown", featureView: fv?.spec?.name || "Unknown FeatureView", type: @@ -72,8 +110,20 @@ const useLoadRegistry = (url: string) => { })) || [], ) || []; + let projectName = + process.env.NODE_ENV === "test" + ? "credit_scoring_aws" + : objects.projects && + objects.projects.length > 0 && + objects.projects[0].spec && + objects.projects[0].spec.name + ? objects.projects[0].spec.name + : objects.project + ? objects.project + : "credit_scoring_aws"; + return { - project: objects.projects[0].spec?.name!, + project: projectName, objects, mergedFVMap, mergedFVList,