Skip to content

Commit

Permalink
Merge branch 'master' into griffin/feedback-replace-ts
Browse files Browse the repository at this point in the history
  • Loading branch information
gtarpenning committed Nov 8, 2024
2 parents 91ea1fb + 6f65b1c commit 026b28e
Show file tree
Hide file tree
Showing 17 changed files with 601 additions and 24 deletions.
98 changes: 92 additions & 6 deletions tests/trace/test_feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
from weave.trace.weave_client import WeaveClient, get_ref
from weave.trace_server import trace_server_interface as tsi
from weave.trace_server.errors import InvalidRequest
from weave.trace_server.trace_server_interface import (
FeedbackCreateReq,
FeedbackQueryReq,
FeedbackReplaceReq,
)


def test_client_feedback(client) -> None:
Expand Down Expand Up @@ -173,9 +178,11 @@ def test_annotation_feedback(client: WeaveClient) -> None:
"wb_user_id": "shawn",
"creator": None,
# Sad - seems like sqlite and clickhouse remote different types here
"created_at": create_res.created_at.isoformat().replace("T", " ")
if client_is_sqlite(client)
else MatchAnyDatetime(),
"created_at": (
create_res.created_at.isoformat().replace("T", " ")
if client_is_sqlite(client)
else MatchAnyDatetime()
),
"feedback_type": feedback_type,
"payload": payload,
"annotation_ref": annotation_ref,
Expand Down Expand Up @@ -321,9 +328,11 @@ def test_runnable_feedback(client: WeaveClient) -> None:
"wb_user_id": "shawn",
"creator": None,
# Sad - seems like sqlite and clickhouse remote different types here
"created_at": create_res.created_at.isoformat().replace("T", " ")
if client_is_sqlite(client)
else MatchAnyDatetime(),
"created_at": (
create_res.created_at.isoformat().replace("T", " ")
if client_is_sqlite(client)
else MatchAnyDatetime()
),
"feedback_type": feedback_type,
"payload": payload,
"annotation_ref": None,
Expand Down Expand Up @@ -535,3 +544,80 @@ def test_filter_and_sort_by_feedback(client: WeaveClient) -> None:
calls = list(calls)
assert len(calls) == 2
assert [c.id for c in calls] == [ids[2], ids[0]]


def test_feedback_replace(client) -> None:
# Create initial feedback
create_req = FeedbackCreateReq(
project_id="test/project",
weave_ref="weave:///test/project/obj/123:abc",
feedback_type="reaction",
payload={"emoji": "👍"},
wb_user_id="test_user",
)
initial_feedback = client.server.feedback_create(create_req)

# Create another feedback with different type
note_feedback = client.server.feedback_create(
FeedbackCreateReq(
project_id="test/project",
weave_ref="weave:///test/project/obj/456:def",
feedback_type="note",
payload={"note": "This is a test note"},
wb_user_id="test_user",
)
)

# Replace the first feedback with new content
replace_req = FeedbackReplaceReq(
project_id="test/project",
weave_ref="weave:///test/project/obj/123:abc",
feedback_type="note",
payload={"note": "Updated feedback"},
feedback_id=initial_feedback.id,
wb_user_id="test_user",
)
replaced_feedback = client.server.feedback_replace(replace_req)

# Verify the replacement
assert note_feedback.id != replaced_feedback.id

# Verify the other feedback remains unchanged
query_res = client.server.feedback_query(
FeedbackQueryReq(
project_id="test/project", fields=["id", "feedback_type", "payload"]
)
)

feedbacks = query_res.result
assert len(feedbacks) == 2

# Find the non-replaced feedback and verify it's unchanged
other_feedback = next(f for f in feedbacks if f["id"] == note_feedback.id)
assert other_feedback["feedback_type"] == "note"
assert other_feedback["payload"] == {"note": "This is a test note"}

# now replace the replaced feedback with the original content
replace_req = FeedbackReplaceReq(
project_id="test/project",
weave_ref="weave:///test/project/obj/123:abc",
feedback_type="reaction",
payload={"emoji": "👍"},
feedback_id=replaced_feedback.id,
wb_user_id="test_user",
)
replaced_feedback = client.server.feedback_replace(replace_req)

assert replaced_feedback.id != initial_feedback.id

# Verify the latest feedback payload
query_res = client.server.feedback_query(
FeedbackQueryReq(
project_id="test/project", fields=["id", "feedback_type", "payload"]
)
)
feedbacks = query_res.result
assert len(feedbacks) == 2
new_feedback = next(f for f in feedbacks if f["id"] == replaced_feedback.id)
assert new_feedback["feedback_type"] == "reaction"
assert new_feedback["payload"] == {"emoji": "👍"}
6 changes: 6 additions & 0 deletions weave-js/src/assets/icons/icon-buzz-bot10.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions weave-js/src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {ReactComponent as ImportBookDictionary} from '../../assets/icons/icon-bo
import {ReactComponent as ImportBoolean} from '../../assets/icons/icon-boolean.svg';
import {ReactComponent as ImportBoxPlot} from '../../assets/icons/icon-box-plot.svg';
import {ReactComponent as ImportBug} from '../../assets/icons/icon-bug.svg';
import {ReactComponent as ImportBuzzBot10} from '../../assets/icons/icon-buzz-bot10.svg';
import {ReactComponent as ImportCategoryMultimodal} from '../../assets/icons/icon-category-multimodal.svg';
import {ReactComponent as ImportChartHorizontalBars} from '../../assets/icons/icon-chart-horizontal-bars.svg';
import {ReactComponent as ImportChartPie} from '../../assets/icons/icon-chart-pie.svg';
Expand Down Expand Up @@ -324,6 +325,9 @@ export const IconBoxPlot = (props: SVGIconProps) => (
export const IconBug = (props: SVGIconProps) => (
<ImportBug {...updateIconProps(props)} />
);
export const IconBuzzBot10 = (props: SVGIconProps) => (
<ImportBuzzBot10 {...updateIconProps(props)} />
);
export const IconCategoryMultimodal = (props: SVGIconProps) => (
<ImportCategoryMultimodal {...updateIconProps(props)} />
);
Expand Down Expand Up @@ -1058,6 +1062,7 @@ const ICON_NAME_TO_ICON: Record<IconName, ElementType> = {
boolean: IconBoolean,
'box-plot': IconBoxPlot,
bug: IconBug,
'buzz-bot10': IconBuzzBot10,
'category-multimodal': IconCategoryMultimodal,
'chart-horizontal-bars': IconChartHorizontalBars,
'chart-pie': IconChartPie,
Expand Down
1 change: 1 addition & 0 deletions weave-js/src/components/Icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export {
IconBoolean,
IconBoxPlot,
IconBug,
IconBuzzBot10,
IconCategoryMultimodal,
IconChartHorizontalBars,
IconChartPie,
Expand Down
1 change: 1 addition & 0 deletions weave-js/src/components/Icon/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const IconNames = {
Boolean: 'boolean',
BoxPlot: 'box-plot',
Bug: 'bug',
BuzzBot10: 'buzz-bot10',
CategoryMultimodal: 'category-multimodal',
ChartHorizontalBars: 'chart-horizontal-bars',
ChartPie: 'chart-pie',
Expand Down
20 changes: 20 additions & 0 deletions weave-js/src/components/PagePanelComponents/Home/Browse3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import {OpPage} from './Browse3/pages/OpPage';
import {OpsPage} from './Browse3/pages/OpsPage';
import {OpVersionPage} from './Browse3/pages/OpVersionPage';
import {OpVersionsPage} from './Browse3/pages/OpVersionsPage';
import {PlaygroundPage} from './Browse3/pages/PlaygroundPage/PlaygroundPage';
import {TablePage} from './Browse3/pages/TablePage';
import {TablesPage} from './Browse3/pages/TablesPage';
import {useURLSearchParamsDict} from './Browse3/pages/util';
Expand Down Expand Up @@ -523,6 +524,14 @@ const Browse3ProjectRoot: FC<{
<Route path={`${projectRoot}/tables`}>
<TablesPageBinding />
</Route>
{/* PLAYGROUND */}
<Route
path={[
`${projectRoot}/playground/:itemName`,
`${projectRoot}/playground`,
]}>
<PlaygroundPageBinding />
</Route>
</Switch>
</Box>
);
Expand Down Expand Up @@ -1035,6 +1044,17 @@ const AppBarLink = (props: ComponentProps<typeof RouterLink>) => (
/>
);

const PlaygroundPageBinding = () => {
const params = useParamsDecoded<Browse3TabItemParams>();
return (
<PlaygroundPage
entity={params.entity}
project={params.project}
callId={params.itemName}
/>
);
};

const Browse3Breadcrumbs: FC = props => {
const params = useParamsDecoded<Browse3Params>();
const query = useURLSearchParamsDict();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {Box} from '@mui/material';
import {WeaveLoader} from '@wandb/weave/common/components/WeaveLoader';
import React, {useEffect, useMemo, useState} from 'react';

import {SimplePageLayout} from '../common/SimplePageLayout';
import {useWFHooks} from '../wfReactInterface/context';
import {DEFAULT_SYSTEM_MESSAGE, usePlaygroundState} from './usePlaygroundState';

export type PlaygroundPageProps = {
entity: string;
project: string;
callId: string;
};

export const PlaygroundPage = (props: PlaygroundPageProps) => {
return (
<SimplePageLayout
title={'Playground (dev)'}
hideTabsIfSingle
tabs={[
{
label: 'main',
content: <PlaygroundPageInner {...props} />,
},
]}
/>
);
};

export const PlaygroundPageInner = (props: PlaygroundPageProps) => {
const {
setPlaygroundStateFromTraceCall,
playgroundStates,
setPlaygroundStateField,
} = usePlaygroundState();

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [settingsTab, setSettingsTab] = useState<number | null>(null);

const {useCall} = useWFHooks();
const call = useCall(
useMemo(() => {
return props.callId
? {
entity: props.entity,
project: props.project,
callId: props.callId,
}
: null;
}, [props.entity, props.project, props.callId])
);

useEffect(() => {
if (!call.loading && call.result) {
if (call.result.traceCall?.inputs) {
setPlaygroundStateFromTraceCall(call.result.traceCall);
}
} else if (
playgroundStates.length === 1 &&
!playgroundStates[0].traceCall.project_id
) {
setPlaygroundStateField(0, 'traceCall', {
inputs: {
messages: [DEFAULT_SYSTEM_MESSAGE],
},
project_id: `${props.entity}/${props.project}`,
});
}
// Only set the call the first time the page loads, and we get the call
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.callId]);

return (
<Box
sx={{
display: 'flex',
height: '100%',
width: '100%',
}}>
{call.loading ? (
<Box
sx={{
display: 'flex',
height: '100%',
width: '100%',
}}>
<WeaveLoader />
</Box>
) : (
<div>Playground</div>
)}
{settingsTab !== null && <div>Settings</div>}
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './PlaygroundPage';
Loading

0 comments on commit 026b28e

Please sign in to comment.