Skip to content

Commit 65d49f6

Browse files
authored
Merge pull request #6390 from voxel51/feat/human-annotation
Human Annotation
2 parents 7efaaaf + 31fb893 commit 65d49f6

File tree

279 files changed

+27928
-1903
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

279 files changed

+27928
-1903
lines changed

.github/workflows/build-docker-internal.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ on:
33
workflow_dispatch: {}
44
schedule:
55
# 4:05AM On Thursdays for vuln assessments.
6-
- cron: '5 4 * * 4'
6+
- cron: "5 4 * * 4"
77
jobs:
88
build-app:
99
uses: ./.github/workflows/build.yml
@@ -12,8 +12,8 @@ jobs:
1212
runs-on: ubuntu-latest
1313
needs: [build-app]
1414
permissions:
15-
contents: 'read'
16-
id-token: 'write'
15+
contents: "read"
16+
id-token: "write"
1717
strategy:
1818
matrix:
1919
python:

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"packageManager": "[email protected]+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
5555
"dependencies": {
5656
"colormap": "^2.3.2",
57+
"pixi-viewport": "^6.0.3",
5758
"react-player": "^2.16.0",
5859
"react-plotly.js": "^2.6.0"
5960
},

app/packages/analytics/src/useTrackEvent.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ export default function useTrackEvent() {
1313
const info = useRecoilValue<AnalyticsInfo>(analyticsInfo);
1414
return useCallback(
1515
(eventName: string, properties?: Record<string, any>) => {
16-
const analytics = usingAnalytics(info);
17-
analytics.trackEvent(eventName, properties);
16+
try {
17+
const analytics = usingAnalytics(info);
18+
analytics.trackEvent(eventName, properties);
19+
} catch (err) {
20+
console.warn("Failed to track event", err);
21+
}
1822
},
1923
[info]
2024
);

app/packages/app/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<div id="modal"></div>
1616
<div id="colorModal"></div>
1717
<div id="queryPerformance"></div>
18+
<div id="annotation" style="display: none;"></div>
1819
<script type="module" src="/src/index.tsx"></script>
1920
</body>
2021
</html>

app/packages/components/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"jotai": "*",
3434
"re-resizable": "*",
3535
"react": "*",
36+
"react-markdown": "*",
3637
"react-relay": "*",
3738
"recoil": "*",
3839
"styled-components": "*"
@@ -48,8 +49,10 @@
4849
"classnames": "^2.3.1",
4950
"framer-motion": "^6.2.7",
5051
"material-icons": "^1.13.12",
52+
"re-resizable": "^6.9.17",
5153
"react-input-autosize": "^3.0.0",
5254
"react-laag": "^2.0.3",
55+
"react-markdown": "^8.0.7",
5356
"react-syntax-highlighter": "^15.5.0",
5457
"react-use": "^17.5.1"
5558
}

app/packages/components/src/components/CodeBlock/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export default function CodeBlock({ text, ...props }: CodeBlockProps) {
1414
return (
1515
<Box
1616
sx={{
17+
width: "100%",
18+
height: "100%",
1719
position: "relative",
1820
fontFamily: "Roboto Mono, monospace",
1921
fontSize: props?.fontSize || 14,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default } from "./Loading";
22
export { default as LoadingDots } from "./LoadingDots";
3+
export { default as LoadingSpinner } from "./LoadingSpinner";

app/packages/components/src/components/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { default as CodeTabs } from "./CodeTabs";
88
export { default as ColoredDot } from "./ColoredDot";
99
export { default as CopyButton } from "./CopyButton";
1010
export { default as Dialog } from "./Dialog";
11+
export { default as EditableLabel } from "./EditableLabel";
1112
export { default as ErrorBoundary, ErrorDisplayMarkup } from "./ErrorBoundary";
1213
export { default as ExternalLink, useExternalLink } from "./ExternalLink";
1314
export { default as FilterAndSelectionIndicator } from "./FilterAndSelectionIndicator";
@@ -19,10 +20,11 @@ export * from "./Icons";
1920
export { default as JSONPanel } from "./JSONPanel";
2021
export { default as JSONViewer } from "./JSONViewer";
2122
export { default as Link } from "./Link";
22-
export { default as Loading, LoadingDots } from "./Loading";
23+
export { default as Loading, LoadingDots, LoadingSpinner } from "./Loading";
2324
export { default as Markdown } from "./Markdown";
2425
export { default as MuiButton } from "./MuiButton";
2526
export { default as MuiIconFont } from "./MuiIconFont";
27+
export { default as PanelCTA } from "./PanelCTA";
2628
export { default as Pending } from "./Pending";
2729
export { default as PillButton } from "./PillButton";
2830
export { default as Popout, PopoutDiv } from "./Popout";
@@ -32,15 +34,13 @@ export { default as Resizable } from "./Resizable";
3234
export * from "./Selection";
3335
export { default as Selection } from "./Selection";
3436
export { default as Selector, SelectorValidationError } from "./Selector";
35-
export { default as StatusButton } from "./StatusButton";
3637
export type { UseSearch } from "./Selector";
38+
export { default as StatusButton } from "./StatusButton";
3739
export { default as TabOption } from "./TabOption";
3840
export { default as TextField } from "./TextField";
3941
export { default as ThemeProvider, useFont, useTheme } from "./ThemeProvider";
40-
export { default as Tooltip } from "./Tooltip";
4142
export { default as Toast } from "./Toast";
42-
export { default as PanelCTA } from "./PanelCTA";
43-
export { default as EditableLabel } from "./EditableLabel";
43+
export { default as Tooltip } from "./Tooltip";
4444
export { default as TooltipProvider } from "./TooltipProvider";
4545

4646
export * from "./types";

app/packages/core/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
},
1111
"dependencies": {
1212
"@fiftyone/components": "*",
13+
"@fiftyone/feature-flags": "*",
1314
"@fiftyone/flashlight": "*",
1415
"@fiftyone/looker": "*",
1516
"@fiftyone/map": "*",
@@ -26,8 +27,10 @@
2627
"classnames": "^2.2.6",
2728
"color": "^4.2.3",
2829
"color-string": "^1.9.1",
30+
"fast-json-patch": "^3.1.1",
2931
"framer-motion": "^6.2.8",
3032
"history": "^5.3.0",
33+
"json-edit-react": "^1.28.2",
3134
"lodash": "^4.17.21",
3235
"lru-cache": "^11.0.1",
3336
"luxon": "^3.6.1",
@@ -61,7 +64,7 @@
6164
"@types/lodash": "^4.14.182",
6265
"@types/luxon": "^3.6.2",
6366
"@types/mime": "^2.0.3",
64-
"@types/react": "^18.0.9",
67+
"@types/react": "^18.2.0",
6568
"@types/react-color": "^3.0.6",
6669
"@types/react-dom": "^18.0.3",
6770
"@types/react-grid-layout": "^1.3.5",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* Copyright 2017-2025, Voxel51, Inc.
3+
*/
4+
5+
import { Sample } from "@fiftyone/looker";
6+
import {
7+
FetchFunctionConfig,
8+
FetchFunctionResult,
9+
getFetchFunctionExtended,
10+
MalformedRequestError,
11+
NotFoundError,
12+
} from "@fiftyone/utilities";
13+
import * as jsonpatch from "fast-json-patch";
14+
import { encodeURIPath, parseETag } from "./util";
15+
16+
/**
17+
* List of JSON-patch operation deltas between two versions of a json object.
18+
*/
19+
export type JSONDeltas = jsonpatch.Operation[];
20+
21+
export type PatchSampleRequest = {
22+
datasetId: string;
23+
sampleId: string;
24+
deltas: JSONDeltas;
25+
versionToken: string;
26+
path?: string;
27+
labelId?: string;
28+
};
29+
30+
export type ErrorResponse = {
31+
errors: string[];
32+
};
33+
34+
export type PatchSampleResponse = {
35+
sample: Sample;
36+
versionToken: string;
37+
};
38+
39+
/**
40+
* Error resulting from a failed update operation.
41+
*/
42+
export class PatchApplicationError extends Error {
43+
constructor(message?: string) {
44+
super(message);
45+
this.name = "Patch Application Error";
46+
}
47+
}
48+
49+
const handleErrorResponse = async (response: Response) => {
50+
if (response.status === 400) {
51+
// either a malformed request, or a list of errors from applying the patch
52+
let errorResponse: ErrorResponse | undefined;
53+
try {
54+
// expected error response: '["error 1","error 2"]'
55+
const body = await response.text();
56+
const errorList = JSON.parse(body);
57+
if (Array.isArray(errorList)) {
58+
errorResponse = { errors: errorList };
59+
}
60+
} catch (err) {
61+
// doesn't look like a list of errors
62+
}
63+
if (errorResponse?.errors) {
64+
throw new PatchApplicationError(errorResponse.errors.join(", "));
65+
}
66+
67+
throw new MalformedRequestError();
68+
} else if (response.status === 404) {
69+
throw new NotFoundError({ path: "sample" });
70+
}
71+
};
72+
73+
/**
74+
* `fetch` with headers, error handling, etc.
75+
*
76+
* @param config fetch configuration
77+
*/
78+
const doFetch = <A, R>(
79+
config: FetchFunctionConfig<A>
80+
): Promise<FetchFunctionResult<R>> => {
81+
return getFetchFunctionExtended()({
82+
errorHandler: handleErrorResponse,
83+
...config,
84+
});
85+
};
86+
87+
/**
88+
* Patch a sample, applying the specified updates to its fields.
89+
*
90+
* @param request Patch sample request
91+
*/
92+
export const patchSample = async (
93+
request: PatchSampleRequest
94+
): Promise<PatchSampleResponse> => {
95+
const pathParts = ["dataset", request.datasetId, "sample", request.sampleId];
96+
if (request.path && request.labelId) {
97+
pathParts.push(request.path, request.labelId);
98+
}
99+
100+
const response = await doFetch<JSONDeltas, Sample>({
101+
path: encodeURIPath(pathParts),
102+
method: "PATCH",
103+
body: request.deltas,
104+
headers: {
105+
"Content-Type": "application/json-patch+json",
106+
"If-Match": `"${request.versionToken}"`,
107+
},
108+
});
109+
110+
return {
111+
sample: response.response,
112+
versionToken: parseETag(response.headers.get("ETag")),
113+
};
114+
};

0 commit comments

Comments
 (0)