Skip to content

Commit

Permalink
Front: Add front endpoint & swr to fetch tags from core (#10565)
Browse files Browse the repository at this point in the history
* Front: Add front endpoint & swr to fetch tags from core

* types unhappy
  • Loading branch information
PopDaph authored Feb 6, 2025
1 parent f6e4af5 commit b3c2a5e
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 3 deletions.
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),
});

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

0 comments on commit b3c2a5e

Please sign in to comment.