Skip to content

Commit

Permalink
Fix file uploads (#6126)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbren authored Jan 7, 2025
1 parent cf0f6e5 commit 3733c64
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 14 deletions.
9 changes: 7 additions & 2 deletions frontend/src/hooks/mutation/use-create-conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ export const useCreateConversation = () => {
const { gitHubToken } = useAuth();
const queryClient = useQueryClient();

const { selectedRepository, files } = useSelector(
const { selectedRepository, files, importedProjectZip } = useSelector(
(state: RootState) => state.initialQuery,
);

return useMutation({
mutationFn: (variables: { q?: string }) => {
if (!variables.q?.trim() && !selectedRepository && files.length === 0) {
if (
!variables.q?.trim() &&
!selectedRepository &&
files.length === 0 &&
!importedProjectZip
) {
throw new Error("No query provided");
}

Expand Down
16 changes: 16 additions & 0 deletions frontend/src/hooks/mutation/use-upload-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useMutation } from "@tanstack/react-query";
import OpenHands from "#/api/open-hands";
import { useConversation } from "#/context/conversation-context";

type UploadFilesArgs = {
files: File[];
};

export const useUploadFiles = () => {
const { conversationId } = useConversation();

return useMutation({
mutationFn: ({ files }: UploadFilesArgs) =>
OpenHands.uploadFiles(conversationId, files),
});
};
37 changes: 35 additions & 2 deletions frontend/src/routes/_oh.app/hooks/use-handle-runtime-active.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,64 @@
import React from "react";
import { useSelector } from "react-redux";
import toast from "react-hot-toast";
import { useDispatch, useSelector } from "react-redux";
import { useAuth } from "#/context/auth-context";
import { useWsClient } from "#/context/ws-client-provider";
import { getGitHubTokenCommand } from "#/services/terminal-service";
import { setImportedProjectZip } from "#/state/initial-query-slice";
import { RootState } from "#/store";
import { base64ToBlob } from "#/utils/base64-to-blob";
import { useUploadFiles } from "../../../hooks/mutation/use-upload-files";
import { useGitHubUser } from "../../../hooks/query/use-github-user";
import { isGitHubErrorReponse } from "#/api/github-axios-instance";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";

export const useHandleRuntimeActive = () => {
const { gitHubToken } = useAuth();
const { send } = useWsClient();
const { curAgentState } = useSelector((state: RootState) => state.agent);

const dispatch = useDispatch();

const { data: user } = useGitHubUser();
const { mutate: uploadFiles } = useUploadFiles();
const { curAgentState } = useSelector((state: RootState) => state.agent);

const runtimeActive = !RUNTIME_INACTIVE_STATES.includes(curAgentState);

const { importedProjectZip } = useSelector(
(state: RootState) => state.initialQuery,
);

const userId = React.useMemo(() => {
if (user && !isGitHubErrorReponse(user)) return user.id;
return null;
}, [user]);

const handleUploadFiles = (zip: string) => {
const blob = base64ToBlob(zip);
const file = new File([blob], "imported-project.zip", {
type: blob.type,
});
uploadFiles(
{ files: [file] },
{
onError: () => {
toast.error("Failed to upload project files.");
},
},
);
dispatch(setImportedProjectZip(null));
};

React.useEffect(() => {
if (runtimeActive && userId && gitHubToken) {
// Export if the user valid, this could happen mid-session so it is handled here
send(getGitHubTokenCommand(gitHubToken));
}
}, [userId, gitHubToken, runtimeActive]);

React.useEffect(() => {
if (runtimeActive && importedProjectZip) {
handleUploadFiles(importedProjectZip);
}
}, [runtimeActive, importedProjectZip]);
};
26 changes: 16 additions & 10 deletions frontend/src/utils/base64-to-blob.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
export const base64ToBlob = (base64: string) => {
// Remove the prefix (e.g. data:image/png;base64,)
const base64WithoutPrefix = base64.split(",")[1];
const base64WithoutPrefix = base64.split(",")[1] || base64;

// Decode to bytes
const bytes = atob(base64WithoutPrefix);

// Create an array of byte values
const byteNumbers = new Array(bytes.length);
for (let i = 0; i < bytes.length; i += 1) {
byteNumbers[i] = bytes.charCodeAt(i);
}
// Process in chunks to avoid memory issues
const chunkSize = 8192; // Process 8KB at a time
const chunks = [];

for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.slice(i, i + chunkSize);
const array = new Uint8Array(chunk.length);

// Convert to Uint8Array
const array = new Uint8Array(byteNumbers);
for (let j = 0; j < chunk.length; j += 1) {
array[j] = chunk.charCodeAt(j);
}

chunks.push(array);
}

// Create a Blob
return new Blob([array], { type: "application/zip" });
// Create a Blob from all chunks
return new Blob(chunks, { type: "application/zip" });
};

0 comments on commit 3733c64

Please sign in to comment.