diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/FeedbackGridInner.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/FeedbackGridInner.tsx index fb5b0574564..28a9078a3a6 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/FeedbackGridInner.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/FeedbackGridInner.tsx @@ -19,6 +19,7 @@ export const FeedbackGridInner = ({ feedback, currentViewerId, }: FeedbackGridInnerProps) => { + console.log('feedback', feedback); const columns: GridColDef[] = [ { field: 'feedback_type', @@ -42,6 +43,11 @@ export const FeedbackGridInner = ({ if (params.row.feedback_type === 'wandb.reaction.1') { return params.row.payload.emoji; } + if (params.row.feedback_type === 'wandb.human_annotation.1') { + // TODO: make this a helper function + const val = Object.values(Object.values(params.row.payload.value)[0])[0]; + return ; + } return ; }, }, diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/HumanFeedback.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/HumanFeedback.tsx index e1941f5eae4..6d828eff063 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/HumanFeedback.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/HumanFeedback.tsx @@ -23,6 +23,8 @@ import { FeedbackCreateSuccess, } from '../../pages/wfReactInterface/traceServerClientTypes'; import { CategoricalFeedback, HumanAnnotationPayload, HumanFeedback,NumericalFeedback, tsFeedbackType} from './humanFeedbackTypes'; +import { parseRef } from '@wandb/weave/react'; + // Constants const STRUCTURED_FEEDBACK_TYPE = 'wandb.human_annotation.1'; const MAGIC_FEEDBACK_TYPES = { @@ -31,42 +33,38 @@ const MAGIC_FEEDBACK_TYPES = { BOOLEAN: 'BinaryFeedback', CATEGORICAL: 'CategoricalFeedback', } -const DEBOUNCE_VAL = 100; +const DEBOUNCE_VAL = 200; // Interfaces -interface StructuredFeedbackProps { - sfData: tsFeedbackType; - callRef: string; +interface HumanFeedbackProps { entity: string; project: string; + viewer: string | null; + sfData: tsFeedbackType; + callRef: string; readOnly?: boolean; focused?: boolean; } // Utility function for creating feedback request const createFeedbackRequest = ( - props: StructuredFeedbackProps, + props: HumanFeedbackProps, value: any, ) => { - const parsedRef = parseRefMaybe(props.sfData.ref); - if (!parsedRef || !parsedRef?.artifactVersion) { - throw new Error('Invalid feedback ref'); - } - - const humanAnnotationPayload = { + const parsedRef = parseRef(props.sfData.ref); + const humanAnnotationPayload: HumanAnnotationPayload = { annotation_column_ref: props.sfData.ref, value: { - [props.sfData.object_id]: { + [parsedRef.artifactName]: { [parsedRef?.artifactVersion]: value } } - } as HumanAnnotationPayload; - console.log('humanAnnotationPayload', humanAnnotationPayload); + }; const baseRequest = { project_id: `${props.entity}/${props.project}`, weave_ref: props.callRef, - creator: null, + creator: props.viewer, feedback_type: STRUCTURED_FEEDBACK_TYPE, payload: humanAnnotationPayload, sort_by: [{created_at: 'desc'}], @@ -76,7 +74,7 @@ const createFeedbackRequest = ( }; const renderFeedbackComponent = ( - props: StructuredFeedbackProps, + props: HumanFeedbackProps, onAddFeedback: (value: any) => Promise, foundValue: string | number | null, ) => { @@ -123,8 +121,8 @@ const renderFeedbackComponent = ( } }; -export const StructuredFeedbackCell: React.FC< - StructuredFeedbackProps +export const HumanFeedbackCell: React.FC< + HumanFeedbackProps > = props => { const {useFeedback} = useWFHooks(); const query = useFeedback({ @@ -132,13 +130,13 @@ export const StructuredFeedbackCell: React.FC< project: props.project, weaveRef: props.callRef, }); - const [foundFeedback, setFoundFeedback] = useState([]); const getTsClient = useGetTraceServerClientContext(); useEffect(() => { if (!props.readOnly) { // We don't need to listen for feedback changes if the cell is editable + // it is being controlled by local state return; } return getTsClient().registerOnFeedbackListener( @@ -205,25 +203,60 @@ export const StructuredFeedbackCell: React.FC< setFoundFeedback(currFeedback); }, [query?.result, query?.loading, props.sfData]); + // userId -> objectId -> objectHash : value + const combinedFeedback = foundFeedback.reduce((acc, feedback) => { + return { + [feedback.creator ?? '']: feedback.payload.value, + ...acc, + }; + }, {}); + + console.log('combinedFeedback', combinedFeedback); + + // rawValues is an array of values from the feedback + const parsedRef = parseRef(props.sfData.ref); + + const rawValues = useMemo(() => { + let values = []; + for (const payload of Object.values(combinedFeedback)) { + const pRecord = payload as Record>; + values.push(pRecord[parsedRef.artifactName]?.[parsedRef.artifactVersion]); + } + return values; + }, [combinedFeedback, parsedRef]) + + console.log('rawValues', rawValues); + + if (query?.loading) { return ; } - console.log('foundFeedback', foundFeedback); - - const values = foundFeedback?.map(feedback => feedback.payload.value[props.sfData.object_id][props.sfData.ref]); - console.log('values', values); - if (props.readOnly) { // TODO: make this prettier, for now just join with commas return
- + +
; + } + + // TODO: fix, we want only one callsite for renderFeedbackComponent + if (Object.keys(combinedFeedback).length === 0) { + return
+ {renderFeedbackComponent(props, onAddFeedback, null)}
; } return ( -
- {values?.map(val => renderFeedbackComponent(props, onAddFeedback, val))} +
+ {rawValues?.map(val => renderFeedbackComponent(props, onAddFeedback, val))} + {/* {Object.entries(combinedFeedback) + .map(([userId, value]) => { + return renderFeedbackComponent( + props, + onAddFeedback, + value[0]?.[parsedRef.artifactName]?.[parsedRef.artifactVersion] + ) + })} */}
); }; @@ -316,7 +349,7 @@ export const TextFeedbackColumn = ({ }; return ( -
+
{ - const feedbackFields = feedbackOptions?.feedback_fields; - const feedbackSpecRef = feedbackOptions?.ref; const callRef = makeRefCall(entity, project, callID); + const {loading: loadingUserInfo, userInfo} = useViewerInfo(); + const [isExpanded, setIsExpanded] = useState(true); - const feedbackCount = feedbackFields?.length ?? 0; + const feedbackFields = feedbackOptions?.feedback_fields; + const feedbackSpecRef = feedbackOptions?.ref; + const feedbackCellCount = feedbackFields?.length ?? 0; - if (!feedbackSpecRef) { + if (loadingUserInfo || !feedbackSpecRef) { return null; } + const viewer = userInfo ? userInfo.id : null; + return (
@@ -44,7 +49,7 @@ export const HumanFeedbackSidebar = ({
Human scores - {feedbackCount} + {feedbackCellCount}
@@ -57,12 +62,13 @@ export const HumanFeedbackSidebar = ({ {field.display_name}
-
diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/tsHumanFeedback.ts b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/tsHumanFeedback.ts index 522096f8c8c..81c96c176c1 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/tsHumanFeedback.ts +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/HumanFeedback/tsHumanFeedback.ts @@ -5,6 +5,23 @@ import {objectVersionKeyToRefUri} from '../../pages/wfReactInterface/utilities'; import {ObjectVersionSchema} from '../../pages/wfReactInterface/wfDataModelHooksInterface'; import {tsHumanFeedbackSpec} 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]); +}; + + export const useHumanFeedbackOptions = ( entity: string, project: string @@ -24,13 +41,12 @@ export const useHumanFeedbackOptions = ( 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 val: tsHumanFeedbackSpec | null = latestSpec?.val; - const feedbackFields = val?.feedback_fields; + const feedbackFieldRefs = latestSpec?.val?.feedback_fields ?? [] + const feedbackFields = useResolveTypeObjects(feedbackFieldRefs); - // TODO: how do we get the refs for the sub-objects here? + console.log('feedbackFields', feedbackFields); useEffect(() => { if (humanFeedbackObjects.loading || humanFeedbackObjects.result == null) { diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/pages/CallPage/CallPage.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/pages/CallPage/CallPage.tsx index e7842af2b10..59e11018c9b 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/pages/CallPage/CallPage.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/pages/CallPage/CallPage.tsx @@ -223,23 +223,23 @@ const CallPageInnerVertical: FC<{ -