Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show datasource configuration status #163

Merged
merged 4 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased

- Show datasource configuration status in the tree
- Add Data Sources to the Packages tree
- Hover (tree item) action in root element to Setup all Data Sources
- Hover (tree item) action in root element to Drop all Data Sources
Expand Down
24 changes: 23 additions & 1 deletion sema4ai/vscode-client/src/robo/dataSourceHandling.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Uri, window, commands, ProgressLocation, Progress, CancellationToken } from "vscode";
import { logError, OUTPUT_CHANNEL } from "../channel";
import { RobotEntry } from "../viewsCommon";
import { RobotEntry, treeViewIdToTreeDataProvider } from "../viewsCommon";
import { DatasourceInfo, DataSourceState } from "../protocols";
import { langServer } from "../extension";
import {
Expand All @@ -13,6 +13,8 @@ import { sleep } from "../time";
import { findActionPackagePath } from "../actionServer";
import { SEMA4AI_LIST_ACTIONS_INTERNAL } from "../robocorpCommands";
import { QuickPickItemWithAction, showSelectOneQuickPick } from "../ask";
import { TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE } from "../robocorpViews";
import { RobotsTreeDataProvider } from "../viewsRobots";

function isExternalDatasource(datasource: DatasourceInfo): boolean {
const externalEngines = ["custom", "files", "models"];
Expand Down Expand Up @@ -253,6 +255,11 @@ export const setupDataSource = async (entry?: RobotEntry) => {
}

await setupSingleDataSource(datasource, dataServerInfo, Uri.file(entry.robot.directory).toString());

const dataProvider = treeViewIdToTreeDataProvider.get(
TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE
) as RobotsTreeDataProvider;
dataProvider.updateDatasourceStatuses();
};

export async function dropDataSource(entry?: RobotEntry) {
Expand Down Expand Up @@ -333,6 +340,11 @@ export async function dropDataSource(entry?: RobotEntry) {
return;
}

const dataProvider = treeViewIdToTreeDataProvider.get(
TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE
) as RobotsTreeDataProvider;
dataProvider.updateDatasourceStatuses();

window.showInformationMessage(result["message"]);
}

Expand Down Expand Up @@ -420,6 +432,11 @@ export async function dropAllDataSources(entry?: RobotEntry) {
window.showInformationMessage("Data Sources dropped succesfully.");
}
);

const dataProvider = treeViewIdToTreeDataProvider.get(
TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE
) as RobotsTreeDataProvider;
dataProvider.updateDatasourceStatuses();
}

export async function setupAllDataSources(entry?: RobotEntry) {
Expand Down Expand Up @@ -512,4 +529,9 @@ export async function setupAllDataSources(entry?: RobotEntry) {
}
}
);

const dataProvider = treeViewIdToTreeDataProvider.get(
TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE
) as RobotsTreeDataProvider;
dataProvider.updateDatasourceStatuses();
}
95 changes: 92 additions & 3 deletions sema4ai/vscode-client/src/viewsRobots.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,52 @@
import * as vscode from "vscode";
import { OUTPUT_CHANNEL, logError } from "./channel";
import { uriExists } from "./files";
import { LocalPackageMetadataInfo, ActionResult, IActionInfo } from "./protocols";
import { LocalPackageMetadataInfo, ActionResult, IActionInfo, DatasourceInfo, DataSourceState } from "./protocols";
import * as roboCommands from "./robocorpCommands";
import { basename, RobotEntry, RobotEntryType } from "./viewsCommon";
import { getSelectedRobot } from "./viewsSelection";
import { isActionPackage, isAgentPackage } from "./common";
import path = require("path");
import { getDataSourceCaption, getDataSourceTooltip } from "./robo/actionPackage";
import { getDataSourceCaption, getDataSourceTooltip, listAllActionPackages } from "./robo/actionPackage";
import { langServer } from "./extension";

let _globalSentMetric: boolean = false;

function empty<T>(array: T[]) {
return array === undefined || array.length === 0;
}

function dataSourcesAreDifferent(
globalDataSourceState: Map<string, Map<string, DatasourceInfo>>,
currentDatasources: Map<string, Map<string, DatasourceInfo>>
): boolean {
if (globalDataSourceState.size !== currentDatasources.size) {
return true;
}

for (const [actionPath, currentInnerMap] of currentDatasources) {
const globalInnerMap = globalDataSourceState.get(actionPath);

if (!globalInnerMap) {
return true;
}

if (globalInnerMap.size !== currentInnerMap.size) {
return true;
}

for (const [sourceName, currentInfo] of currentInnerMap) {
const globalInfo = globalInnerMap.get(sourceName);

if (!globalInfo || JSON.stringify(currentInfo) !== JSON.stringify(globalInfo)) {
return true;
}
}
}

return false;
}

function getRobotLabel(robotInfo: LocalPackageMetadataInfo): string {
let label: string = undefined;
if (robotInfo.yamlContents) {
Expand Down Expand Up @@ -43,6 +75,7 @@ export class RobotsTreeDataProvider implements vscode.TreeDataProvider<RobotEntr
readonly onForceSelectionFromTreeData: vscode.Event<RobotEntry[]> = this._onForceSelectionFromTreeData.event;

private lastRoot: RobotEntry[] | undefined = undefined;
globalDataSourceState: Map<string, Map<string, DatasourceInfo>> = new Map();

fireRootChange() {
this._onDidChangeTreeData.fire(null);
Expand All @@ -52,6 +85,46 @@ export class RobotsTreeDataProvider implements vscode.TreeDataProvider<RobotEntr
this.fireRootChange();
}

async updateDatasourceStatuses(): Promise<void> {
let actionResult: ActionResult<LocalPackageMetadataInfo[]> = await listAllActionPackages();
if (!actionResult.success) {
return;
}
let localActionPackages: LocalPackageMetadataInfo[] = actionResult.result;
if (localActionPackages.length == 0) {
return;
}

const dataServerStatus = await vscode.commands.executeCommand("sema4ai-data.dataserver.status");
if (!dataServerStatus["success"]) {
return;
}

const currentDatasources: Map<string, Map<string, DatasourceInfo>> = new Map();

for (const actionPackage of localActionPackages) {
const dataSourceStateResult = (await langServer.sendRequest("computeDataSourceState", {
"action_package_yaml_directory_uri": actionPackage.directory,
"data_server_info": dataServerStatus["data"],
})) as ActionResult<DataSourceState>;

if (dataSourceStateResult.success) {
const dataSourcesState: DatasourceInfo[] = dataSourceStateResult.result.required_data_sources;
for (const item of dataSourcesState) {
const innerMap = currentDatasources.get(actionPackage.filePath) ?? new Map();
innerMap.set(getDataSourceCaption(item), item);

currentDatasources.set(actionPackage.filePath, innerMap);
}
}
}

if (currentDatasources.size > 0 && dataSourcesAreDifferent(this.globalDataSourceState, currentDatasources)) {
this.globalDataSourceState = currentDatasources;
this.fireRootChange();
}
}

/**
* Note that we make sure to only return valid entries here (i.e.: no entries
* where RobotEntry.type === RobotEntryType.Error).
Expand Down Expand Up @@ -93,6 +166,7 @@ export class RobotsTreeDataProvider implements vscode.TreeDataProvider<RobotEntr
if (element === undefined) {
// i.e.: this is the root entry, so, we've
// collected the actual robots here.
this.updateDatasourceStatuses();

let notifySelection = false;
if (empty(this.lastRoot) && empty(ret)) {
Expand Down Expand Up @@ -521,7 +595,22 @@ export class RobotsTreeDataProvider implements vscode.TreeDataProvider<RobotEntr
const children: RobotEntry[] = [];

for (const datasource of element.extraData.datasources) {
let name = getDataSourceCaption(datasource);
let dbName = getDataSourceCaption(datasource);
let dataSourcesFromActionPackage = this.globalDataSourceState.get(element.robot.filePath);
let isConfigured = dataSourcesFromActionPackage?.get(dbName)?.configured;

let name = `${dbName} ?`;
if (isConfigured !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also show the training state (when it's a model).

if (
datasource.model_state &&
["generating", "training", "creating"].includes(datasource.model_state)
) {
name = `${dbName} ◌`;
} else {
name = `${dbName} ${isConfigured ? "✓" : "✗"}`;
}
}

children.push({
"label": name,
"actionName": datasource.name,
Expand Down
Loading