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

[Attach] Clean hotfix from #11385 #11403

Merged
merged 3 commits into from
Mar 17, 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
8 changes: 4 additions & 4 deletions front/lib/actions/conversation/include_file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type { Authenticator } from "@app/lib/auth";
import { AgentConversationIncludeFileAction } from "@app/lib/models/assistant/actions/conversation/include_file";
import {
CONTENT_OUTDATED_MSG,
renderFromFragmentId,
renderFromResourceId,
} from "@app/lib/resources/content_fragment_resource";
import { generateRandomModelSId } from "@app/lib/resources/string_ids";
import logger from "@app/logger/logger";
Expand Down Expand Up @@ -116,7 +116,7 @@ export class ConversationIncludeFileActionType extends BaseAction {
// message).
const files = listFiles(conversation);
for (const f of files) {
if (f.contentFragmentId === fileId && f.isIncludable) {
if (f.resourceId === fileId && f.isIncludable) {
if (f.contentFragmentVersion === "superseded") {
return new Ok({
fileId,
Expand All @@ -125,10 +125,10 @@ export class ConversationIncludeFileActionType extends BaseAction {
});
}

const r = await renderFromFragmentId(conversation.owner, {
const r = await renderFromResourceId(conversation.owner, {
contentType: f.contentType,
excludeImages: true,
contentFragmentId: f.contentFragmentId,
resourceId: f.resourceId,
model,
title: f.title,
contentFragmentVersion: f.contentFragmentVersion,
Expand Down
4 changes: 2 additions & 2 deletions front/lib/actions/conversation/list_files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
} from "@app/types";

export type ConversationFileType = {
contentFragmentId: string;
resourceId: string;
title: string;
contentType: SupportedContentFragmentType;
contentFragmentVersion: ContentFragmentVersion;
Expand Down Expand Up @@ -69,7 +69,7 @@ export class ConversationListFilesActionType extends BaseAction {
`// searchable: content can be searched alongside other searchable files' content using \`${DEFAULT_CONVERSATION_SEARCH_ACTION_NAME}\`\n` +
`\n`;
for (const f of this.files) {
content += `<file id="${f.contentFragmentId}" name="${_.escape(f.title)}" type="${f.contentType}" includable="${f.isIncludable}" queryable="${f.isQueryable}" searchable="${f.isSearchable}"`;
content += `<file id="${f.resourceId}" name="${_.escape(f.title)}" type="${f.contentType}" includable="${f.isIncludable}" queryable="${f.isQueryable}" searchable="${f.isSearchable}"`;

if (f.snippet) {
content += ` snippet="${_.escape(f.snippet)}"`;
Expand Down
6 changes: 2 additions & 4 deletions front/lib/api/assistant/jit_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,8 @@ async function getJITActions(
conversationId: conversation.sId,
fileIds: _.uniq(
filesUsableAsTableQuery
.map((f) => f.contentFragmentId)
.concat(
filesUsableAsRetrievalQuery.map((f) => f.contentFragmentId)
)
.map((f) => f.resourceId)
.concat(filesUsableAsRetrievalQuery.map((f) => f.resourceId))
),
workspaceId: conversation.owner.sId,
},
Expand Down
4 changes: 2 additions & 2 deletions front/lib/api/assistant/jit_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function listFiles(
const isSearchable = canDoJIT && isSearchableContentType(m.contentType);

files.push({
contentFragmentId: m.contentFragmentId,
resourceId: m.contentFragmentId,
title: m.title,
contentType: m.contentType,
snippet: m.snippet,
Expand Down Expand Up @@ -104,7 +104,7 @@ export function listFiles(
const isSearchable = canDoJIT && isSearchableContentType(f.contentType);

files.push({
contentFragmentId: f.fileId,
resourceId: f.fileId,
contentType: f.contentType,
title: f.title,
snippet: f.snippet,
Expand Down
90 changes: 47 additions & 43 deletions front/lib/resources/content_fragment_resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import { FileResource } from "@app/lib/resources/file_resource";
import { frontSequelize } from "@app/lib/resources/storage";
import { ContentFragmentModel } from "@app/lib/resources/storage/models/content_fragment";
import type { ReadonlyAttributesType } from "@app/lib/resources/storage/types";
import { generateRandomModelSId } from "@app/lib/resources/string_ids";
import {
generateRandomModelSId,
getResourceNameAndIdFromSId,
} from "@app/lib/resources/string_ids";
import logger from "@app/logger/logger";
import type {
ContentFragmentMessageTypeModel,
Expand Down Expand Up @@ -373,63 +376,36 @@ async function getSignedUrlForProcessedContent(
return getPrivateUploadBucket().getSignedUrl(fileCloudStoragePath);
}

export async function renderFromFragmentId(
export async function renderFromResourceId(
workspace: WorkspaceType,
{
contentType,
excludeImages,
contentFragmentId,
resourceId,
model,
title,
contentFragmentVersion,
}: {
contentType: SupportedContentFragmentType;
excludeImages: boolean;
contentFragmentId: string;
resourceId: string;
model: ModelConfigurationType;
title: string;
contentFragmentVersion: ContentFragmentVersion;
}
): Promise<Result<ContentFragmentMessageTypeModel, Error>> {
// TODO(pr,attach) this is a hack to fix incident here: https://dust4ai.slack.com/archives/C05B529FHV1/p1741976168265989
// to be properly fixed.
// if starting with fil_, it's not a content fragment id but a file id directly
const isOldFileId = contentFragmentId.startsWith("fil_");

let contentFragment: ContentFragmentResource | null = null;
if (!isOldFileId) {
contentFragment = await ContentFragmentResource.fromStringIdAndVersion(
contentFragmentId,
contentFragmentVersion
);
if (!contentFragment) {
throw new Error(
`Content fragment not found for sId ${contentFragmentId}`
);
}
}

const {
fileId: fileModelId,
nodeId,
nodeDataSourceViewId,
} = contentFragment ?? {
fileId: -1,
nodeId: null,
nodeDataSourceViewId: null,
// At time of writing, passed resourceId can be either a file or a content fragment.
const { resourceName } = getResourceNameAndIdFromSId(resourceId) ?? {
resourceName: "content_fragment",
};

let fileStringId;
if (isOldFileId) {
fileStringId = contentFragmentId;
} else {
fileStringId = fileModelId
? FileResource.modelIdToSId({
id: fileModelId,
workspaceId: workspace.id,
})
: null;
}
const { fileStringId, nodeId, nodeDataSourceViewId } =
resourceName === "file"
? { fileStringId: resourceId, nodeId: null, nodeDataSourceViewId: null }
: await getIncludeFileIdsFromContentFragmentResourceId(
workspace,
resourceId
);

if (isSupportedImageContentType(contentType)) {
if (excludeImages || !model.supportsVision) {
Expand Down Expand Up @@ -517,7 +493,7 @@ export async function renderFromFragmentId(
{
type: "text",
text: renderContentFragmentXml({
contentFragmentId,
contentFragmentId: resourceId,
contentType,
title,
version: contentFragmentVersion,
Expand Down Expand Up @@ -548,7 +524,7 @@ export async function renderFromFragmentId(
{
type: "text",
text: renderContentFragmentXml({
contentFragmentId,
contentFragmentId: resourceId,
contentType,
title,
version: contentFragmentVersion,
Expand Down Expand Up @@ -673,3 +649,31 @@ function renderContentFragmentXml({
}
return tag;
}

async function getIncludeFileIdsFromContentFragmentResourceId(
workspace: WorkspaceType,
resourceId: string
) {
const contentFragment = await ContentFragmentResource.fromStringIdAndVersion(
resourceId,
"latest"
);
if (!contentFragment) {
throw new Error(`Content fragment not found for sId ${resourceId}`);
}

if (!contentFragment.fileId) {
return {
fileStringId: null,
nodeId: contentFragment.nodeId,
nodeDataSourceViewId: contentFragment.nodeDataSourceViewId,
};
}

const fileStringId = FileResource.modelIdToSId({
id: contentFragment.fileId,
workspaceId: workspace.id,
});

return { fileStringId, nodeId: null, nodeDataSourceViewId: null };
}