Skip to content

Commit

Permalink
wip -- working with json-schema
Browse files Browse the repository at this point in the history
  • Loading branch information
gtarpenning committed Oct 29, 2024
1 parent 0842c40 commit adfad73
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 114 deletions.
3 changes: 3 additions & 0 deletions weave-js/src/components/Form/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type TextFieldProps = {
type?: string;
autoComplete?: string;
dataTest?: string;
step?: number;
};

export const TextField = ({
Expand All @@ -55,6 +56,7 @@ export const TextField = ({
type,
autoComplete,
dataTest,
step,
}: TextFieldProps) => {
const textFieldSize = size ?? 'medium';
const leftPaddingForIcon = textFieldSize === 'medium' ? 'pl-34' : 'pl-36';
Expand Down Expand Up @@ -129,6 +131,7 @@ export const TextField = ({
type={type}
autoComplete={autoComplete}
data-test={dataTest}
step={step}
/>
{extraActions}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,22 @@ import React, {
} from 'react';

import { CellValueString } from '../../../Browse2/CellValueString';
import { parseRefMaybe } from '../../../Browse2/SmallRef';
import {useWFHooks} from '../../pages/wfReactInterface/context';
import {useGetTraceServerClientContext} from '../../pages/wfReactInterface/traceServerClientContext';
import {
FeedbackCreateError,
FeedbackCreateSuccess,
} from '../../pages/wfReactInterface/traceServerClientTypes';
import { CategoricalFeedback, HumanAnnotationPayload, HumanFeedback,NumericalFeedback, tsFeedbackType} from './humanFeedbackTypes';
import { HumanAnnotationPayload, HumanFeedback, tsHumanFeedbackColumn} from './humanFeedbackTypes';
import { parseRef } from '@wandb/weave/react';

// Constants
const STRUCTURED_FEEDBACK_TYPE = 'wandb.human_annotation.1';
const HUMAN_FEEDBACK_TYPE = 'wandb.human_annotation.1';
const MAGIC_FEEDBACK_TYPES = {
NUMERICAL: 'NumericalFeedback',
TEXT: 'TextFeedback',
BOOLEAN: 'BinaryFeedback',
CATEGORICAL: 'CategoricalFeedback',
NUMERICAL: 'number',
TEXT: 'text',
BOOLEAN: 'boolean',
CATEGORICAL: 'categorical',
}
const DEBOUNCE_VAL = 200;

Expand All @@ -40,7 +39,7 @@ interface HumanFeedbackProps {
entity: string;
project: string;
viewer: string | null;
sfData: tsFeedbackType;
hfColumn: tsHumanFeedbackColumn;
callRef: string;
readOnly?: boolean;
focused?: boolean;
Expand All @@ -51,9 +50,10 @@ const createFeedbackRequest = (
props: HumanFeedbackProps,
value: any,
) => {
const parsedRef = parseRef(props.sfData.ref);
const ref = props.hfColumn.ref;
const parsedRef = parseRef(ref);
const humanAnnotationPayload: HumanAnnotationPayload = {
annotation_column_ref: props.sfData.ref,
annotation_column_ref: ref,
value: {
[parsedRef.artifactName]: {
[parsedRef?.artifactVersion]: value
Expand All @@ -65,7 +65,7 @@ const createFeedbackRequest = (
project_id: `${props.entity}/${props.project}`,
weave_ref: props.callRef,
creator: props.viewer,
feedback_type: STRUCTURED_FEEDBACK_TYPE,
feedback_type: HUMAN_FEEDBACK_TYPE,
payload: humanAnnotationPayload,
sort_by: [{created_at: 'desc'}],
};
Expand All @@ -78,9 +78,10 @@ const renderFeedbackComponent = (
onAddFeedback: (value: any) => Promise<boolean>,
foundValue: string | number | null,
) => {
switch (props.sfData._type) {
// TODO validation on json_schema
switch (props.hfColumn.json_schema.type) {
case MAGIC_FEEDBACK_TYPES.NUMERICAL:
const numericalFeedback = props.sfData as NumericalFeedback;
const numericalFeedback = props.hfColumn.json_schema;
return (
<NumericalFeedbackColumn
min={numericalFeedback.min}
Expand All @@ -99,7 +100,7 @@ const renderFeedbackComponent = (
/>
);
case MAGIC_FEEDBACK_TYPES.CATEGORICAL:
const categoricalFeedback = props.sfData as CategoricalFeedback;
const categoricalFeedback = props.hfColumn.json_schema;
return (
<CategoricalFeedbackColumn
options={categoricalFeedback.options}
Expand Down Expand Up @@ -190,7 +191,7 @@ export const HumanFeedbackCell: React.FC<
}

const feedbackRefMatches = (feedback: HumanFeedback) =>
feedback.payload.annotation_column_ref === props.sfData.ref;
feedback.payload.annotation_column_ref === props.hfColumn.ref;

const currFeedback = query.result?.filter(
(feedback: HumanFeedback) =>
Expand All @@ -201,7 +202,7 @@ export const HumanFeedbackCell: React.FC<
}

setFoundFeedback(currFeedback);
}, [query?.result, query?.loading, props.sfData]);
}, [query?.result, query?.loading, props.hfColumn]);

// userId -> objectId -> objectHash : value
const combinedFeedback = foundFeedback.reduce((acc, feedback) => {
Expand All @@ -211,9 +212,7 @@ export const HumanFeedbackCell: React.FC<
};
}, {}) as Record<string, Record<string, Record<string, string>>>;

// rawValues is an array of values from the feedback
const parsedRef = parseRef(props.sfData.ref);

const parsedRef = parseRef(props.hfColumn.ref);
const rawValues = useMemo(() => {
let values = [];
for (const payload of Object.values(combinedFeedback)) {
Expand All @@ -224,7 +223,6 @@ export const HumanFeedbackCell: React.FC<
}, [combinedFeedback, parsedRef]);

const viewerFeedbackVal = props.viewer ? combinedFeedback[props.viewer]?.[parsedRef.artifactName]?.[parsedRef.artifactVersion] : null;

if (query?.loading) {
return <LoadingDots />;
}
Expand All @@ -249,12 +247,14 @@ export const NumericalFeedbackColumn = ({
onAddFeedback,
defaultValue,
focused,
isInteger,
}: {
min: number;
max: number;
onAddFeedback?: (value: number) => Promise<boolean>;
defaultValue: number | null;
focused?: boolean;
isInteger?: boolean;
}) => {
const [value, setValue] = useState<number | undefined>(
defaultValue ?? undefined
Expand Down Expand Up @@ -295,6 +295,7 @@ export const NumericalFeedbackColumn = ({
value={value?.toString() ?? ''}
onChange={onValueChange}
placeholder="..."
step={isInteger ? 1 : 0.001}
errorState={error}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import React, {useState} from 'react';

import {Icon} from '../../../../../Icon';
import {HumanFeedbackCell} from './HumanFeedback';
import {tsHumanFeedbackSpec} from './humanFeedbackTypes';
import {tsHumanFeedbackColumn} from './humanFeedbackTypes';
import { useViewerInfo } from '@wandb/weave/common/hooks/useViewerInfo';

type HumanFeedbackSidebarProps = {
feedbackOptions: tsHumanFeedbackSpec | null;
feedbackColumns: tsHumanFeedbackColumn[];
callID: string;
entity: string;
project: string;
};

export const HumanFeedbackSidebar = ({
feedbackOptions,
feedbackColumns,
callID,
entity,
project,
Expand All @@ -24,12 +24,9 @@ export const HumanFeedbackSidebar = ({
const {loading: loadingUserInfo, userInfo} = useViewerInfo();

const [isExpanded, setIsExpanded] = useState(true);
const feedbackCellCount = feedbackColumns.length ?? 0;

const feedbackFields = feedbackOptions?.feedback_fields;
const feedbackSpecRef = feedbackOptions?.ref;
const feedbackCellCount = feedbackFields?.length ?? 0;

if (loadingUserInfo || !feedbackSpecRef) {
if (loadingUserInfo) {
return null;
}

Expand All @@ -56,15 +53,20 @@ export const HumanFeedbackSidebar = ({
</button>
{isExpanded && (
<div>
{feedbackFields?.map((field, index) => (
{feedbackColumns?.map((field, index) => (
<div key={field.ref}>
<h3 className="text-gray-700 bg-gray-50 px-6 py-4 text-sm font-semibold">
{field.display_name}
{field.name}
</h3>
{field.description && (
<p className="text-gray-700 bg-gray-50 px-6 py-4 text-sm font-italic ">
{field.description}
</p>
)}
<div className="pb-8 pl-6 pr-8 pt-2">
<HumanFeedbackCell
focused={index === 0}
sfData={field}
hfColumn={field}
callRef={callRef}
entity={entity}
project={project}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,16 @@
import {Feedback} from '../../pages/wfReactInterface/traceServerClientTypes';

export type FeedbackType =
| BinaryFeedback
| TextFeedback
| CategoricalFeedback
| NumericalFeedback;

export type tsFeedbackType = FeedbackType & {
export type tsHumanFeedbackColumn = HumanFeedbackColumn & {
ref: string;
object_id: string;
};

export type HumanFeedbackSpec = {
feedback_fields: FeedbackType[];
};

export type tsHumanFeedbackSpec = {
feedback_fields: tsFeedbackType[];
ref: string;
};

export type BinaryFeedback = FeedbackField & {};

export type TextFeedback = FeedbackField & {
max_length: number;
};

export type CategoricalFeedback = FeedbackField & {
options: string[];
};

export type NumericalFeedback = FeedbackField & {
min: number;
max: number;
};

type FeedbackField = {
display_name: string;
feedback_type: string;
export type HumanFeedbackColumn = {
name: string;
description: string;

_type: string;
json_schema: Record<string, any>;
unique_among_creators?: boolean;
op_scope?: string[];
};

export type HumanAnnotationPayload = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,59 @@ import {useEffect, useMemo, useState} from 'react';
import {useWFHooks} from '../../pages/wfReactInterface/context';
import {objectVersionKeyToRefUri} from '../../pages/wfReactInterface/utilities';
import {ObjectVersionSchema} from '../../pages/wfReactInterface/wfDataModelHooksInterface';
import {tsHumanFeedbackSpec} from './humanFeedbackTypes';
import {tsHumanFeedbackColumn} from './humanFeedbackTypes';


const useResolveTypeObjects = (typeRefs: string[]) => {
const {useRefsData} = useWFHooks();
const refsData = useRefsData(typeRefs);
return useMemo(() => {
if (refsData.loading || refsData.result == null) {
return null;
}
const refDataWithRefs = refsData.result.map((x, i) => ({
...x,
ref: typeRefs[i],
}));
return refDataWithRefs;
}, [refsData.loading, refsData.result]);
};
// const useResolveTypeObjects = (typeRefs: string[]) => {
// const {useRefsData} = useWFHooks();
// const refsData = useRefsData(typeRefs);
// return useMemo(() => {
// if (refsData.loading || refsData.result == null) {
// return null;
// }
// const refDataWithRefs = refsData.result.map((x, i) => ({
// ...x,
// ref: typeRefs[i],
// }));
// return refDataWithRefs;
// }, [refsData.loading, refsData.result]);
// };


export const useHumanFeedbackOptions = (
entity: string,
project: string
): tsHumanFeedbackSpec | null => {
): tsHumanFeedbackColumn[] => {
const {useRootObjectVersions} = useWFHooks();

const [latestSpec, setLatestSpec] = useState<ObjectVersionSchema | null>(
const [cols, setCols] = useState<ObjectVersionSchema[] | null>(
null
);
const humanFeedbackObjects = useRootObjectVersions(
const humanAnnotationColumns = useRootObjectVersions(
entity,
project,
{
baseObjectClasses: ['HumanFeedback'],
baseObjectClasses: ['HumanAnnotationColumn'],
latestOnly: true,
},
undefined, // limit
false // metadataOnly
);
// TODO: this is not actually tsHumanFeedbackSpec, it's HumanFeedbackSpec
// we need to add the refs for each of the feedback fields
const feedbackFieldRefs = latestSpec?.val?.feedback_fields ?? []
const feedbackFields = useResolveTypeObjects(feedbackFieldRefs);

console.log('feedbackFields', feedbackFields);

useEffect(() => {
if (humanFeedbackObjects.loading || humanFeedbackObjects.result == null) {
return;
}
// sort by createdAtMs, most recent first
const spec = humanFeedbackObjects.result
?.sort((a, b) => b.createdAtMs - a.createdAtMs)
.pop();
if (!spec) {
if (humanAnnotationColumns.loading || humanAnnotationColumns.result == null) {
return;
}
setLatestSpec(spec);
}, [humanFeedbackObjects.loading, humanFeedbackObjects.result]);
setCols(humanAnnotationColumns.result);
}, [humanAnnotationColumns.loading, humanAnnotationColumns.result]);

return useMemo(() => {
if (latestSpec == null || feedbackFields == null) {
return null;
if (cols == null) {
return [];
}
return {
feedback_fields: feedbackFields,
ref: objectVersionKeyToRefUri(latestSpec),
};
}, [latestSpec, feedbackFields]);
return cols.map((col) => ({
...col.val,
ref: objectVersionKeyToRefUri(col),
}));
}, [cols]);
};
Loading

0 comments on commit adfad73

Please sign in to comment.