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

Front: Add front endpoint & swr to fetch tags from core #10565

Merged
merged 2 commits into from
Feb 6, 2025
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
29 changes: 28 additions & 1 deletion front/lib/swr/data_sources.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { DataSourceType, LightWorkspaceType } from "@dust-tt/types";
import type {
DataSourceType,
LightWorkspaceType,
TagResult,
TagSearchParams,
TagSearchResponse,
} from "@dust-tt/types";
import { useMemo } from "react";
import type { Fetcher } from "swr";

Expand All @@ -25,3 +31,24 @@ export function useDataSourceUsage({
mutate,
};
}

export function useTagSearch({ owner }: { owner: LightWorkspaceType }) {
const searchTags = async (params: TagSearchParams): Promise<TagResult[]> => {
const res = await fetch(`/api/w/${owner.sId}/data_sources/tags`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});

if (!res.ok) {
throw new Error("Failed to search tags");
}

const data = (await res.json()) as TagSearchResponse;
return data.tags;
};

return { searchTags };
}
97 changes: 97 additions & 0 deletions front/pages/api/w/[wId]/data_sources/tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { CoreAPI } from "@dust-tt/types";
import { isLeft } from "fp-ts/Either";
import * as t from "io-ts";
import * as reporter from "io-ts-reporters";
import type { NextApiRequest, NextApiResponse } from "next";

import { withSessionAuthenticationForWorkspace } from "@app/lib/api/auth_wrappers";
import apiConfig from "@app/lib/api/config";
import type { Authenticator } from "@app/lib/auth";
import { getFeatureFlags } from "@app/lib/auth";
import logger from "@app/logger/logger";
import { apiError } from "@app/logger/withlogging";

export const PostTagSearchBodySchema = t.type({
query: t.string,
queryType: t.string,
dataSources: t.array(t.string),
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally here you should take an array of dataSourceViews instead (but can be fixed once core api is available)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I think I'll update when the endpoint is live as I'm likely gonna need to tweak things anyway!

});

export type PostTagSearchBody = t.TypeOf<typeof PostTagSearchBodySchema>;

async function handler(
req: NextApiRequest,
res: NextApiResponse,
auth: Authenticator
) {
const user = auth.getNonNullableUser();
if (!user || !auth.isUser()) {
return apiError(req, res, {
status_code: 401,
api_error: {
type: "data_source_auth_error",
message: "You are not authorized to fetch tags.",
},
});
}

const owner = auth.getNonNullableWorkspace();
const flags = await getFeatureFlags(owner);

if (!flags.includes("tags_filters")) {
return apiError(req, res, {
status_code: 403,
api_error: {
type: "feature_flag_not_found",
message: "The feature is not enabled for this workspace.",
},
});
}

const { method } = req;

if (method !== "POST") {
return apiError(req, res, {
status_code: 405,
api_error: {
type: "method_not_supported_error",
message: "The method passed is not supported, POST is expected.",
},
});
}

const bodyValidation = PostTagSearchBodySchema.decode(req.body);
if (isLeft(bodyValidation)) {
const pathError = reporter.formatValidationErrors(bodyValidation.left);

return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: `Invalid request body: ${pathError}`,
},
});
}

const { query, queryType, dataSources } = bodyValidation.right;

const coreAPI = new CoreAPI(apiConfig.getCoreAPIConfig(), logger);
const result = await coreAPI.searchTags({
query,
queryType,
dataSources,
});

if (result.isErr()) {
return apiError(req, res, {
status_code: 500,
api_error: {
type: "internal_server_error",
message: "Failed to search tags",
},
});
}
return res.status(200).json(result.value.response);
}

export default withSessionAuthenticationForWorkspace(handler);
1 change: 1 addition & 0 deletions sdks/js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ const WhitelistableFeaturesSchema = FlexibleEnumSchema<
| "labs_github_actions"
| "deepseek_r1_global_agent_feature"
| "bigquery_feature"
| "tags_filters"
>();

export type WhitelistableFeature = z.infer<typeof WhitelistableFeaturesSchema>;
Expand Down
16 changes: 16 additions & 0 deletions types/src/front/data_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,19 @@ export function isDataSourceNameValid(name: string): Result<void, string> {

return new Ok(undefined);
}

export type TagResult = {
tag: string;
match_count: number;
data_sources: string[];
};

export type TagSearchResponse = {
tags: TagResult[];
};

export type TagSearchParams = {
query: string;
queryType: string;
dataSources: string[];
};
38 changes: 36 additions & 2 deletions types/src/front/lib/core_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,22 @@ export interface CoreAPISearchCursorRequest {
cursor?: string;
}

export interface CoreAPISearchResponse {
export interface CoreAPISearchNodesResponse {
nodes: CoreAPIContentNode[];
next_page_cursor: string | null;
}

export interface CoreAPISearchTagsResponse {
error: string | null;
response: {
tags: {
tag: string;
match_count: number;
data_sources: string[];
}[];
};
}

export type CoreAPIDatasourceViewFilter = {
data_source_id: string;
view_filter: string[];
Expand Down Expand Up @@ -1612,7 +1623,7 @@ export class CoreAPI {
query?: string;
filter: CoreAPINodesSearchFilter;
options?: CoreAPISearchOptions;
}): Promise<CoreAPIResponse<CoreAPISearchResponse>> {
}): Promise<CoreAPIResponse<CoreAPISearchNodesResponse>> {
const response = await this._fetchWithError(`${this._url}/nodes/search`, {
method: "POST",
headers: {
Expand All @@ -1627,6 +1638,29 @@ export class CoreAPI {
return this._resultFromResponse(response);
}

async searchTags({
query,
queryType,
dataSources,
}: {
query: string;
queryType: string;
dataSources: string[];
}): Promise<CoreAPIResponse<CoreAPISearchTagsResponse>> {
const response = await this._fetchWithError(`${this._url}/tags/search`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query,
query_type: queryType,
data_sources: dataSources,
}),
});
return this._resultFromResponse(response);
}

async getDataSourceFolder({
projectId,
dataSourceId,
Expand Down
1 change: 1 addition & 0 deletions types/src/shared/feature_flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const WHITELISTABLE_FEATURES = [
"labs_github_actions",
"deepseek_r1_global_agent_feature",
"bigquery_feature",
"tags_filters",
] as const;
export type WhitelistableFeature = (typeof WHITELISTABLE_FEATURES)[number];
export function isWhitelistableFeature(
Expand Down
Loading