diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c45dda5fa0..a5649a71720 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: hooks: - id: mypy additional_dependencies: - [types-pkg-resources==0.1.3, types-all, wandb>=0.15.5] + [types-pkg-resources==0.1.3, types-all, wandb>=0.15.5, wandb<0.19.0] # Note: You have to update pyproject.toml[tool.mypy] too! args: ["--config-file=pyproject.toml"] exclude: (.*pyi$)|(weave_query)|(tests)|(examples) diff --git a/pyproject.toml b/pyproject.toml index 2f3e4c93bab..d392d527b60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -226,7 +226,7 @@ module = "weave_query.*" ignore_errors = true [tool.bumpversion] -current_version = "0.51.23-dev0" +current_version = "0.51.24-dev0" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/sdks/node/package-lock.json b/sdks/node/package-lock.json index 83fe5755ee6..bbd003cde05 100644 --- a/sdks/node/package-lock.json +++ b/sdks/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "weave", - "version": "0.7.0", + "version": "0.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "weave", - "version": "0.7.0", + "version": "0.7.3", "license": "Apache-2.0", "dependencies": { "cli-progress": "^3.12.0", diff --git a/sdks/node/package.json b/sdks/node/package.json index f2f884f3dcd..00861420103 100644 --- a/sdks/node/package.json +++ b/sdks/node/package.json @@ -1,11 +1,19 @@ { "name": "weave", - "version": "0.7.0", + "version": "0.7.3", "description": "AI development toolkit", - "types": "dist/src/index.d.ts", - "main": "dist/src/index.js", + "types": "dist/index.d.ts", + "main": "dist/index.js", "type": "commonjs", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, "scripts": { + "build": "tsc --outDir dist", + "prepare": "npm run build", "test": "jest --silent", "test:coverage": "jest --coverage", "test:watch": "jest --watch", @@ -14,6 +22,9 @@ "generate-api": "swagger-typescript-api -p ./weave.openapi.json -o ./src/generated -n traceServerApi.ts", "dev": "nodemon" }, + "files": [ + "dist" + ], "repository": { "type": "git", "url": "https://github.com/wandb/weave/js" diff --git a/sdks/node/tsconfig.json b/sdks/node/tsconfig.json index f44dc0d2933..141908653c1 100644 --- a/sdks/node/tsconfig.json +++ b/sdks/node/tsconfig.json @@ -7,19 +7,28 @@ "sourceMap": true, "strict": true, "esModuleInterop": true, - "outDir": "./dist", + "outDir": "dist", "paths": { "weave": ["./src/index.ts"] - } + }, + "declaration": true, + "declarationMap": true, + "rootDir": "src", + "tsBuildInfoFile": "dist/.tsbuildinfo" }, "include": ["src/**/*"], - "exclude": ["src", "examples", "dist", "node_modules"], - "references": [ - { - "path": "./src/tsconfig.src.json" - }, - { - "path": "./examples/tsconfig.examples.json" - } + "exclude": [ + "examples", + "dist", + "node_modules", + "src/integrations/checkOpenai.ts" ] + // "references": [ + // { + // "path": "./src/tsconfig.src.json" + // }, + // { + // "path": "./examples/tsconfig.examples.json" + // } + // ] } diff --git a/tests/integrations/instructor/instructor_test.py b/tests/integrations/instructor/instructor_test.py index 761d961b9af..b9667ebb086 100644 --- a/tests/integrations/instructor/instructor_test.py +++ b/tests/integrations/instructor/instructor_test.py @@ -64,7 +64,7 @@ def test_instructor_openai( assert op_name_from_ref(call.op_name) == "openai.chat.completions.create" output = call.output output_arguments = json.loads( - output.choices[0].message.tool_calls[0].function.arguments + output["choices"][0]["message"]["tool_calls"][0]["function"]["arguments"] ) assert "person_name" in output_arguments assert "age" in output_arguments @@ -112,7 +112,7 @@ async def extract_person(text: str) -> Person: assert op_name_from_ref(call.op_name) == "openai.chat.completions.create" output = call.output output_arguments = json.loads( - output.choices[0].message.tool_calls[0].function.arguments + output["choices"][0]["message"]["tool_calls"][0]["function"]["arguments"] ) assert "person_name" in output_arguments assert "age" in output_arguments @@ -166,7 +166,7 @@ def test_instructor_iterable( assert call.started_at < call.ended_at assert op_name_from_ref(call.op_name) == "openai.chat.completions.create" output = call.output - output_arguments = json.loads(output.choices[0].message.content) + output_arguments = json.loads(output["choices"][0]["message"]["content"]) assert "tasks" in output_arguments assert "person_name" in output_arguments["tasks"][0] assert "age" in output_arguments["tasks"][0] diff --git a/tests/integrations/openai/openai_test.py b/tests/integrations/openai/openai_test.py index 331a2580030..3022defc5a1 100644 --- a/tests/integrations/openai/openai_test.py +++ b/tests/integrations/openai/openai_test.py @@ -38,10 +38,10 @@ def test_openai_quickstart(client: weave.trace.weave_client.WeaveClient) -> None assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["requests"] == 1 assert usage["completion_tokens"] == 28 assert usage["prompt_tokens"] == 11 @@ -86,10 +86,10 @@ async def test_openai_async_quickstart( assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["requests"] == 1 assert usage["completion_tokens"] == 28 assert usage["prompt_tokens"] == 11 @@ -315,10 +315,10 @@ def test_openai_function_call(client: weave.trace.weave_client.WeaveClient) -> N assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["total_tokens"] == 117 assert usage["completion_tokens"] == 18 assert usage["prompt_tokens"] == 99 @@ -401,10 +401,10 @@ async def test_openai_function_call_async( assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["total_tokens"] == 117 assert usage["completion_tokens"] == 18 assert usage["prompt_tokens"] == 99 @@ -577,10 +577,10 @@ def test_openai_tool_call(client: weave.trace.weave_client.WeaveClient) -> None: assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["total_tokens"] == 117 assert usage["completion_tokens"] == 27 assert usage["prompt_tokens"] == 90 @@ -664,10 +664,10 @@ async def test_openai_tool_call_async( assert call.started_at < call.ended_at # type: ignore output = call.output - assert output.model == "gpt-4o-2024-05-13" - assert output.object == "chat.completion" + assert output["model"] == "gpt-4o-2024-05-13" + assert output["object"] == "chat.completion" - usage = call.summary["usage"][output.model] # type: ignore + usage = call.summary["usage"][output["model"]] # type: ignore assert usage["total_tokens"] == 117 assert usage["completion_tokens"] == 27 assert usage["prompt_tokens"] == 90 diff --git a/tests/trace/test_evaluations.py b/tests/trace/test_evaluations.py index f4993e9227d..e5c38ef0140 100644 --- a/tests/trace/test_evaluations.py +++ b/tests/trace/test_evaluations.py @@ -7,7 +7,7 @@ from PIL import Image import weave -from tests.trace.util import AnyIntMatcher +from tests.trace.util import AnyIntMatcher, AnyStrMatcher from weave import Evaluation, Model from weave.scorers import Scorer from weave.trace.refs import CallRef @@ -504,8 +504,8 @@ async def test_evaluation_data_topology(client): } }, "weave": { + "display_name": AnyStrMatcher(), "latency_ms": AnyIntMatcher(), - "trace_name": "Evaluation.evaluate", "status": "success", }, } @@ -1029,3 +1029,21 @@ def my_second_scorer(text, output, model_output): with pytest.raises(ValueError, match="Both 'output' and 'model_output'"): evaluation = weave.Evaluation(dataset=ds, scorers=[my_second_scorer]) + + +@pytest.mark.asyncio +async def test_evaluation_with_custom_name(client): + dataset = weave.Dataset(rows=[{"input": "hi", "output": "hello"}]) + evaluation = weave.Evaluation(dataset=dataset, evaluation_name="wow-custom!") + + @weave.op() + def model(input: str) -> str: + return "hmmm" + + await evaluation.evaluate(model) + + calls = list(client.get_calls(filter=tsi.CallsFilter(trace_roots_only=True))) + assert len(calls) == 1 + + call = calls[0] + assert call.display_name == "wow-custom!" diff --git a/tests/trace/test_exec.py b/tests/trace/test_exec.py index 117bfec18bc..bff1cf18f4d 100644 --- a/tests/trace/test_exec.py +++ b/tests/trace/test_exec.py @@ -101,7 +101,7 @@ def test_publish_works_for_code_with_no_source_file( ref = captured["ref"] op = ref.get() - actual_captured_code = op.art.path_contents["obj.py"].decode() + actual_captured_code = op.get_captured_code() expected_captured_code = expected_captured_code[1:] # ignore first newline assert actual_captured_code == expected_captured_code diff --git a/tests/trace/test_trace_server_common.py b/tests/trace/test_trace_server_common.py index 9bc7495481f..d9170f83eee 100644 --- a/tests/trace/test_trace_server_common.py +++ b/tests/trace/test_trace_server_common.py @@ -1,4 +1,5 @@ from weave.trace_server.trace_server_common import ( + DynamicBatchProcessor, LRUCache, get_nested_key, set_nested_key, @@ -54,3 +55,26 @@ def test_lru_cache(): cache["c"] = 10 assert cache["c"] == 10 assert cache["d"] == 4 + + +def test_dynamic_batch_processor(): + # Initialize processor with: + # - initial batch size of 2 + # - max size of 8 + # - growth factor of 2 + processor = DynamicBatchProcessor(initial_size=2, max_size=8, growth_factor=2) + + test_data = range(15) + + batches = list(processor.make_batches(iter(test_data))) + + # Expected batch sizes: 2, 4, 8, 1 + assert batches[0] == [0, 1] + assert batches[1] == [2, 3, 4, 5] + assert batches[2] == [6, 7, 8, 9, 10, 11, 12, 13] + assert batches[3] == [14] + assert len(batches) == 4 + + # Verify all items were processed + flattened = [item for batch in batches for item in batch] + assert flattened == list(range(15)) diff --git a/tests/trace/test_trace_settings.py b/tests/trace/test_trace_settings.py index bcbd27aeea8..7a4c0de4397 100644 --- a/tests/trace/test_trace_settings.py +++ b/tests/trace/test_trace_settings.py @@ -104,7 +104,7 @@ def test_func(): ref = weave.publish(test_func) test_func2 = ref.get() - code2 = test_func2.art.path_contents["obj.py"].decode() + code2 = test_func2.get_captured_code() assert "Code-capture was disabled" in code2 parse_and_apply_settings(UserSettings(capture_code=True)) @@ -117,7 +117,7 @@ def test_func(): ref2 = weave.publish(test_func) test_func3 = ref2.get() - code3 = test_func3.art.path_contents["obj.py"].decode() + code3 = test_func3.get_captured_code() assert "Code-capture was disabled" not in code3 @@ -130,7 +130,7 @@ def test_func(): ref = weave.publish(test_func) test_func2 = ref.get() - code2 = test_func2.art.path_contents["obj.py"].decode() + code2 = test_func2.get_captured_code() assert "Code-capture was disabled" in code2 os.environ["WEAVE_CAPTURE_CODE"] = "true" @@ -141,7 +141,7 @@ def test_func(): ref2 = weave.publish(test_func) test_func3 = ref2.get() - code3 = test_func3.art.path_contents["obj.py"].decode() + code3 = test_func3.get_captured_code() assert "Code-capture was disabled" not in code3 diff --git a/tests/trace/type_serializers/Audio/audio_test.py b/tests/trace/type_handlers/Audio/audio_test.py similarity index 100% rename from tests/trace/type_serializers/Audio/audio_test.py rename to tests/trace/type_handlers/Audio/audio_test.py diff --git a/tests/trace/type_serializers/Image/image_test.py b/tests/trace/type_handlers/Image/image_test.py similarity index 78% rename from tests/trace/type_serializers/Image/image_test.py rename to tests/trace/type_handlers/Image/image_test.py index 65a2c7b0ba7..dc99d6bf704 100644 --- a/tests/trace/type_serializers/Image/image_test.py +++ b/tests/trace/type_handlers/Image/image_test.py @@ -1,3 +1,4 @@ +import random from pathlib import Path import pytest @@ -130,3 +131,41 @@ def accept_image_jpg_pillow(val): assert res == "Image size: 100x100" finally: file_path.unlink() + + +@pytest.fixture +def dataset_ref(client): + # This fixture represents a saved dataset containing images + IMAGE_SIZE = (1024, 1024) + N_ROWS = 50 + + def make_random_image(): + random_colour = ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + ) + return Image.new("RGB", IMAGE_SIZE, random_colour) + + rows = [{"img": make_random_image()} for _ in range(N_ROWS)] + dataset = weave.Dataset(rows=rows) + ref = weave.publish(dataset) + + return ref + + +@pytest.mark.asyncio +async def test_images_in_dataset_for_evaluation(client, dataset_ref): + dataset = dataset_ref.get() + evaluation = weave.Evaluation(dataset=dataset) + + @weave.op + def model(img: Image) -> dict[str, str]: + return {"result": "hello"} + + # Expect that evaluation works for a ref-get'd dataset containing images + res = await evaluation.evaluate(model) + + assert isinstance(res, dict) + assert "model_latency" in res and "mean" in res["model_latency"] + assert isinstance(res["model_latency"]["mean"], (int, float)) diff --git a/tests/trace/util.py b/tests/trace/util.py index eb4c6002beb..beb651722e3 100644 --- a/tests/trace/util.py +++ b/tests/trace/util.py @@ -8,6 +8,13 @@ def client_is_sqlite(client): return isinstance(client.server._internal_trace_server, SqliteTraceServer) +class AnyStrMatcher: + """Matches any string.""" + + def __eq__(self, other): + return isinstance(other, str) + + class AnyIntMatcher: """Matches any integer.""" diff --git a/weave-js/src/assets/icons/icon-filled-circle.svg b/weave-js/src/assets/icons/icon-filled-circle.svg new file mode 100644 index 00000000000..2b57c31558b --- /dev/null +++ b/weave-js/src/assets/icons/icon-filled-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/weave-js/src/assets/icons/icon-moon.svg b/weave-js/src/assets/icons/icon-moon.svg new file mode 100644 index 00000000000..e448eab96c3 --- /dev/null +++ b/weave-js/src/assets/icons/icon-moon.svg @@ -0,0 +1,3 @@ + + + diff --git a/weave-js/src/assets/icons/icon-not-visible.svg b/weave-js/src/assets/icons/icon-not-visible.svg new file mode 100644 index 00000000000..b2782d987b9 --- /dev/null +++ b/weave-js/src/assets/icons/icon-not-visible.svg @@ -0,0 +1,5 @@ + + + diff --git a/weave-js/src/assets/icons/icon-pin-to-right.svg b/weave-js/src/assets/icons/icon-pin-to-right.svg new file mode 100644 index 00000000000..46a9c0bf114 --- /dev/null +++ b/weave-js/src/assets/icons/icon-pin-to-right.svg @@ -0,0 +1,5 @@ + + + diff --git a/weave-js/src/assets/icons/icon-sun.svg b/weave-js/src/assets/icons/icon-sun.svg new file mode 100644 index 00000000000..bb0c57891b0 --- /dev/null +++ b/weave-js/src/assets/icons/icon-sun.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/weave-js/src/common/util/SdkPointCloudToBabylon.test.ts b/weave-js/src/common/util/SdkPointCloudToBabylon.test.ts index 95c7639dc4a..df5eca57b46 100644 --- a/weave-js/src/common/util/SdkPointCloudToBabylon.test.ts +++ b/weave-js/src/common/util/SdkPointCloudToBabylon.test.ts @@ -4,6 +4,7 @@ import { DEFAULT_POINT_COLOR, getFilteringOptionsForPointCloud, getVertexCompatiblePositionsAndColors, + loadPointCloud, MAX_BOUNDING_BOX_LABELS_FOR_DISPLAY, MaxAlphaValue, } from './SdkPointCloudToBabylon'; @@ -174,3 +175,16 @@ describe('getFilteringOptionsForPointCloud', () => { expect(newClassIdToLabel.get(49)).toEqual('label49'); }); }); +describe('loadPointCloud', () => { + it('appropriate defaults set when loading point cloud from file', () => { + const fileContents = JSON.stringify({ + boxes: [], + points: [[]], + type: 'lidar/beta', + vectors: [], + }); + const babylonPointCloud = loadPointCloud(fileContents); + expect(babylonPointCloud.points).toHaveLength(1); + expect(babylonPointCloud.points[0].position).toEqual([0, 0, 0]); + }); +}); diff --git a/weave-js/src/common/util/SdkPointCloudToBabylon.ts b/weave-js/src/common/util/SdkPointCloudToBabylon.ts index 274e1676be4..d52682743ee 100644 --- a/weave-js/src/common/util/SdkPointCloudToBabylon.ts +++ b/weave-js/src/common/util/SdkPointCloudToBabylon.ts @@ -160,7 +160,7 @@ export const handlePoints = (object3D: Object3DScene): ScenePoint[] => { // Draw Points return truncatedPoints.map(point => { const [x, y, z, r, g, b] = point; - const position: Position = [x, y, z]; + const position: Position = [x ?? 0, y ?? 0, z ?? 0]; const category = r; if (r !== undefined && g !== undefined && b !== undefined) { diff --git a/weave-js/src/common/util/render_babylon.ts b/weave-js/src/common/util/render_babylon.ts index 10aee3f6c51..ebd213c2677 100644 --- a/weave-js/src/common/util/render_babylon.ts +++ b/weave-js/src/common/util/render_babylon.ts @@ -394,6 +394,15 @@ const pointCloudScene = ( // Apply vertexData to custom mesh vertexData.applyToMesh(pcMesh); + // A file without any points defined still includes a single, empty "point". + // In order to play nice with Babylon, we position this empty point at 0,0,0. + // Hence, a pointCloud with a single point at 0,0,0 is likely empty. + const isEmpty = + pointCloud.points.length === 1 && + pointCloud.points[0].position[0] === 0 && + pointCloud.points[0].position[1] === 0 && + pointCloud.points[0].position[2] === 0; + camera.parent = pcMesh; const pcMaterial = new Babylon.StandardMaterial('mat', scene); @@ -472,8 +481,8 @@ const pointCloudScene = ( new Vector3(edges.length * 2, edges.length * 2, edges.length * 2) ); - // If we are iterating over camera, target a box - if (index === meta?.cameraIndex) { + // If we are iterating over camera or the cloud is empty, target a box + if (index === meta?.cameraIndex || (index === 0 && isEmpty)) { camera.position = center.add(new Vector3(0, 0, 1000)); camera.target = center; camera.zoomOn([lines]); diff --git a/weave-js/src/components/FancyPage/FancyPageMenu.tsx b/weave-js/src/components/FancyPage/FancyPageMenu.tsx index c5971756016..6829b70fc06 100644 --- a/weave-js/src/components/FancyPage/FancyPageMenu.tsx +++ b/weave-js/src/components/FancyPage/FancyPageMenu.tsx @@ -60,7 +60,6 @@ export const FancyPageMenu = ({ return null; } const linkProps = { - key: menuItem.slug, to: menuItem.isDisabled ? {} : { @@ -76,7 +75,7 @@ export const FancyPageMenu = ({ }, }; return ( - + {menuItem.nameTooltip || menuItem.name} diff --git a/weave-js/src/components/FancyPage/useProjectSidebar.ts b/weave-js/src/components/FancyPage/useProjectSidebar.ts index 5cd1190dbbe..19dce4215df 100644 --- a/weave-js/src/components/FancyPage/useProjectSidebar.ts +++ b/weave-js/src/components/FancyPage/useProjectSidebar.ts @@ -10,7 +10,8 @@ export const useProjectSidebar = ( hasModelsData: boolean, hasWeaveData: boolean, hasTraceBackend: boolean = true, - hasModelsAccess: boolean = true + hasModelsAccess: boolean = true, + isLaunchActive: boolean = false ): FancyPageSidebarItem[] => { // Should show models sidebar items if we have models data or if we don't have a trace backend let showModelsSidebarItems = hasModelsData || !hasTraceBackend; @@ -41,6 +42,13 @@ export const useProjectSidebar = ( label: 'Models', isShown: isShowAll, }, + { + type: 'button' as const, + name: 'Overview', + slug: 'overview', + isShown: isModelsOnly, + iconName: IconNames.Info, + }, { type: 'button' as const, name: 'Workspace', @@ -61,7 +69,7 @@ export const useProjectSidebar = ( type: 'button' as const, name: 'Jobs', slug: 'jobs', - isShown: isModelsOnly, + isShown: isModelsOnly && isLaunchActive, isDisabled: viewingRestricted, iconName: IconNames.FlashBolt, }, @@ -98,13 +106,6 @@ export const useProjectSidebar = ( iconName: IconNames.VersionsLayers, isDisabled: viewingRestricted, }, - { - type: 'button' as const, - name: 'Overview', - slug: 'overview', - isShown: isModelsOnly, - iconName: IconNames.Info, - }, { type: 'menuPlaceholder' as const, isShown: isShowAll, @@ -250,5 +251,6 @@ export const useProjectSidebar = ( viewingRestricted, isModelsOnly, showWeaveSidebarItems, + isLaunchActive, ]); }; diff --git a/weave-js/src/components/Icon/Icon.tsx b/weave-js/src/components/Icon/Icon.tsx index a87f41dfe56..9c020b1bab9 100644 --- a/weave-js/src/components/Icon/Icon.tsx +++ b/weave-js/src/components/Icon/Icon.tsx @@ -74,6 +74,7 @@ import {ReactComponent as ImportExpandUncollapse} from '../../assets/icons/icon- import {ReactComponent as ImportExportShareUpload} from '../../assets/icons/icon-export-share-upload.svg'; import {ReactComponent as ImportFacebookSocial} from '../../assets/icons/icon-facebook-social.svg'; import {ReactComponent as ImportFailed} from '../../assets/icons/icon-failed.svg'; +import {ReactComponent as ImportFilledCircle} from '../../assets/icons/icon-filled-circle.svg'; import {ReactComponent as ImportFilterAlt} from '../../assets/icons/icon-filter-alt.svg'; import {ReactComponent as ImportFlashBolt} from '../../assets/icons/icon-flash-bolt.svg'; import {ReactComponent as ImportFolderAlt} from '../../assets/icons/icon-folder-alt.svg'; @@ -138,9 +139,11 @@ import {ReactComponent as ImportMinimizeMode} from '../../assets/icons/icon-mini import {ReactComponent as ImportModel} from '../../assets/icons/icon-model.svg'; import {ReactComponent as ImportModelOnDark} from '../../assets/icons/icon-model-on-dark.svg'; import {ReactComponent as ImportMolecule} from '../../assets/icons/icon-molecule.svg'; +import {ReactComponent as ImportMoon} from '../../assets/icons/icon-moon.svg'; import {ReactComponent as ImportMusicAudio} from '../../assets/icons/icon-music-audio.svg'; import {ReactComponent as ImportNewSectionAbove} from '../../assets/icons/icon-new-section-above.svg'; import {ReactComponent as ImportNewSectionBelow} from '../../assets/icons/icon-new-section-below.svg'; +import {ReactComponent as ImportNotVisible} from '../../assets/icons/icon-not-visible.svg'; import {ReactComponent as ImportNumber} from '../../assets/icons/icon-number.svg'; import {ReactComponent as ImportOpenNewTab} from '../../assets/icons/icon-open-new-tab.svg'; import {ReactComponent as ImportOpenaiLogo} from '../../assets/icons/icon-openai-logo.svg'; @@ -160,6 +163,7 @@ import {ReactComponent as ImportPaused} from '../../assets/icons/icon-paused.svg import {ReactComponent as ImportPencilEdit} from '../../assets/icons/icon-pencil-edit.svg'; import {ReactComponent as ImportPhoto} from '../../assets/icons/icon-photo.svg'; import {ReactComponent as ImportPin} from '../../assets/icons/icon-pin.svg'; +import {ReactComponent as ImportPinToRight} from '../../assets/icons/icon-pin-to-right.svg'; import {ReactComponent as ImportPlay} from '../../assets/icons/icon-play.svg'; import {ReactComponent as ImportPlotly} from '../../assets/icons/icon-plotly.svg'; import {ReactComponent as ImportPriorityCritical} from '../../assets/icons/icon-priority-critical.svg'; @@ -213,6 +217,7 @@ import {ReactComponent as ImportStar} from '../../assets/icons/icon-star.svg'; import {ReactComponent as ImportStarFilled} from '../../assets/icons/icon-star-filled.svg'; import {ReactComponent as ImportStop} from '../../assets/icons/icon-stop.svg'; import {ReactComponent as ImportStopped} from '../../assets/icons/icon-stopped.svg'; +import {ReactComponent as ImportSun} from '../../assets/icons/icon-sun.svg'; import {ReactComponent as ImportSwap} from '../../assets/icons/icon-swap.svg'; import {ReactComponent as ImportSweepBayes} from '../../assets/icons/icon-sweep-bayes.svg'; import {ReactComponent as ImportSweepGrid} from '../../assets/icons/icon-sweep-grid.svg'; @@ -497,6 +502,9 @@ export const IconFacebookSocial = (props: SVGIconProps) => ( export const IconFailed = (props: SVGIconProps) => ( ); +export const IconFilledCircle = (props: SVGIconProps) => ( + +); export const IconFilterAlt = (props: SVGIconProps) => ( ); @@ -689,6 +697,9 @@ export const IconModelOnDark = (props: SVGIconProps) => ( export const IconMolecule = (props: SVGIconProps) => ( ); +export const IconMoon = (props: SVGIconProps) => ( + +); export const IconMusicAudio = (props: SVGIconProps) => ( ); @@ -698,6 +709,9 @@ export const IconNewSectionAbove = (props: SVGIconProps) => ( export const IconNewSectionBelow = (props: SVGIconProps) => ( ); +export const IconNotVisible = (props: SVGIconProps) => ( + +); export const IconNumber = (props: SVGIconProps) => ( ); @@ -755,6 +769,9 @@ export const IconPhoto = (props: SVGIconProps) => ( export const IconPin = (props: SVGIconProps) => ( ); +export const IconPinToRight = (props: SVGIconProps) => ( + +); export const IconPlay = (props: SVGIconProps) => ( ); @@ -914,6 +931,9 @@ export const IconStop = (props: SVGIconProps) => ( export const IconStopped = (props: SVGIconProps) => ( ); +export const IconSun = (props: SVGIconProps) => ( + +); export const IconSwap = (props: SVGIconProps) => ( ); @@ -1134,6 +1154,7 @@ const ICON_NAME_TO_ICON: Record = { 'export-share-upload': IconExportShareUpload, 'facebook-social': IconFacebookSocial, failed: IconFailed, + 'filled-circle': IconFilledCircle, 'filter-alt': IconFilterAlt, 'flash-bolt': IconFlashBolt, 'folder-alt': IconFolderAlt, @@ -1198,9 +1219,11 @@ const ICON_NAME_TO_ICON: Record = { model: IconModel, 'model-on-dark': IconModelOnDark, molecule: IconMolecule, + moon: IconMoon, 'music-audio': IconMusicAudio, 'new-section-above': IconNewSectionAbove, 'new-section-below': IconNewSectionBelow, + 'not-visible': IconNotVisible, number: IconNumber, 'open-new-tab': IconOpenNewTab, 'openai-logo': IconOpenaiLogo, @@ -1220,6 +1243,7 @@ const ICON_NAME_TO_ICON: Record = { 'pencil-edit': IconPencilEdit, photo: IconPhoto, pin: IconPin, + 'pin-to-right': IconPinToRight, play: IconPlay, plotly: IconPlotly, 'priority-critical': IconPriorityCritical, @@ -1273,6 +1297,7 @@ const ICON_NAME_TO_ICON: Record = { 'star-filled': IconStarFilled, stop: IconStop, stopped: IconStopped, + sun: IconSun, swap: IconSwap, 'sweep-bayes': IconSweepBayes, 'sweep-grid': IconSweepGrid, diff --git a/weave-js/src/components/Icon/index.ts b/weave-js/src/components/Icon/index.ts index aa92f74b31a..85ea5332649 100644 --- a/weave-js/src/components/Icon/index.ts +++ b/weave-js/src/components/Icon/index.ts @@ -74,6 +74,7 @@ export { IconExportShareUpload, IconFacebookSocial, IconFailed, + IconFilledCircle, IconFilterAlt, IconFlashBolt, IconFolderAlt, @@ -138,9 +139,11 @@ export { IconModel, IconModelOnDark, IconMolecule, + IconMoon, IconMusicAudio, IconNewSectionAbove, IconNewSectionBelow, + IconNotVisible, IconNumber, IconOpenaiLogo, IconOpenNewTab, @@ -160,6 +163,7 @@ export { IconPencilEdit, IconPhoto, IconPin, + IconPinToRight, IconPlay, IconPlotly, IconPriorityCritical, @@ -213,6 +217,7 @@ export { IconStarFilled, IconStop, IconStopped, + IconSun, IconSwap, IconSweepBayes, IconSweepGrid, diff --git a/weave-js/src/components/Icon/types.ts b/weave-js/src/components/Icon/types.ts index 8002b47eb9c..87a1207bc85 100644 --- a/weave-js/src/components/Icon/types.ts +++ b/weave-js/src/components/Icon/types.ts @@ -73,6 +73,7 @@ export const IconNames = { ExportShareUpload: 'export-share-upload', FacebookSocial: 'facebook-social', Failed: 'failed', + FilledCircle: 'filled-circle', FilterAlt: 'filter-alt', FlashBolt: 'flash-bolt', FolderAlt: 'folder-alt', @@ -137,9 +138,11 @@ export const IconNames = { Model: 'model', ModelOnDark: 'model-on-dark', Molecule: 'molecule', + Moon: 'moon', MusicAudio: 'music-audio', NewSectionAbove: 'new-section-above', NewSectionBelow: 'new-section-below', + NotVisible: 'not-visible', Number: 'number', OpenNewTab: 'open-new-tab', OpenaiLogo: 'openai-logo', @@ -159,6 +162,7 @@ export const IconNames = { PencilEdit: 'pencil-edit', Photo: 'photo', Pin: 'pin', + PinToRight: 'pin-to-right', Play: 'play', Plotly: 'plotly', PriorityCritical: 'priority-critical', @@ -212,6 +216,7 @@ export const IconNames = { StarFilled: 'star-filled', Stop: 'stop', Stopped: 'stopped', + Sun: 'sun', Swap: 'swap', SweepBayes: 'sweep-bayes', SweepGrid: 'sweep-grid', diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3.tsx index 976e232716a..3517f4d3b9c 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3.tsx @@ -104,6 +104,7 @@ import { WFDataModelAutoProvider, } from './Browse3/pages/wfReactInterface/context'; import {useHasTraceServerClientContext} from './Browse3/pages/wfReactInterface/traceServerClientContext'; +import {TableRowSelectionProvider} from './TableRowSelectionContext'; import {useDrawerResize} from './useDrawerResize'; LicenseInfo.setLicenseKey( @@ -1149,63 +1150,3 @@ const Browse3Breadcrumbs: FC = props => { ); }; - -export const TableRowSelectionContext = React.createContext<{ - rowIdsConfigured: boolean; - rowIdInTable: (id: string) => boolean; - setRowIds?: (rowIds: string[]) => void; - getNextRowId?: (currentId: string) => string | null; - getPreviousRowId?: (currentId: string) => string | null; -}>({ - rowIdsConfigured: false, - rowIdInTable: (id: string) => false, - setRowIds: () => {}, - getNextRowId: () => null, - getPreviousRowId: () => null, -}); - -const TableRowSelectionProvider: FC<{children: React.ReactNode}> = ({ - children, -}) => { - const [rowIds, setRowIds] = useState([]); - const rowIdsConfigured = useMemo(() => rowIds.length > 0, [rowIds]); - const rowIdInTable = useCallback( - (currentId: string) => rowIds.includes(currentId), - [rowIds] - ); - - const getNextRowId = useCallback( - (currentId: string) => { - const currentIndex = rowIds.indexOf(currentId); - if (currentIndex !== -1) { - return rowIds[currentIndex + 1]; - } - return null; - }, - [rowIds] - ); - - const getPreviousRowId = useCallback( - (currentId: string) => { - const currentIndex = rowIds.indexOf(currentId); - if (currentIndex !== -1) { - return rowIds[currentIndex - 1]; - } - return null; - }, - [rowIds] - ); - - return ( - - {children} - - ); -}; diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/SideNav.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/SideNav.tsx deleted file mode 100644 index 75945264d97..00000000000 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/SideNav.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import {IconNames} from '@wandb/weave/components/Icon'; -import _ from 'lodash'; -import React, {useMemo} from 'react'; -import {useParams} from 'react-router-dom'; - -import {useWeaveflowRouteContext} from './context'; -import {useEvaluationsFilter} from './pages/CallsPage/evaluationsFilter'; -import {useURLSearchParamsDict} from './pages/util'; -import {Sidebar, SidebarItem} from './Sidebar'; - -export const SideNav = () => { - const params = useParams<{ - entity: string; - project: string; - tab: - | 'types' - | 'type-versions' - | 'objects' - | 'object-versions' - | 'ops' - | 'op-versions' - | 'calls' - | 'boards' - | 'tables'; - }>(); - const {entity, project} = params; - const currentProject = project; - const currentEntity = entity; - const query = useURLSearchParamsDict(); - const filters = useMemo(() => { - if (query.filter === undefined) { - return {}; - } - try { - return JSON.parse(query.filter); - } catch (e) { - console.log(e); - return {}; - } - }, [query.filter]); - - const evaluationsFilter = useEvaluationsFilter(currentEntity, currentProject); - - const selectedItemId = useMemo(() => { - const category = Object.keys(filters).find(key => - key.includes('baseObjectClass') - ); - let filterCategory; - if (category !== undefined) { - filterCategory = filters[category]; - } - - if (params.tab === 'types' || params.tab === 'type-versions') { - return 'types'; - } else if (params.tab === 'objects' || params.tab === 'object-versions') { - if (filterCategory === 'Model') { - return 'models'; - } else if (filterCategory === 'Dataset') { - return 'datasets'; - } - return 'objects'; - } else if (params.tab === 'ops' || params.tab === 'op-versions') { - return 'ops'; - } else if (params.tab === 'calls') { - if (_.isEqual(filters, evaluationsFilter)) { - return 'evaluation'; - } - return 'calls'; - } - return null; - }, [evaluationsFilter, filters, params.tab]); - - const {baseRouter} = useWeaveflowRouteContext(); - if (!currentProject || !currentEntity) { - return null; - } - - const items: SidebarItem[][] = [ - [ - { - id: 'evaluation', - name: 'Evaluations', - iconName: IconNames.TypeBoolean, - path: baseRouter.callsUIUrl(entity, project, evaluationsFilter), - }, - { - id: 'models', - name: 'Models', - iconName: IconNames.Model, - path: baseRouter.objectVersionsUIUrl(entity, project, { - baseObjectClass: 'Model', - }), - }, - { - id: 'datasets', - name: 'Datasets', - iconName: IconNames.Table, - path: baseRouter.objectVersionsUIUrl(entity, project, { - baseObjectClass: 'Dataset', - }), - }, - ], - [ - { - id: 'calls', - name: 'Traces', - iconName: IconNames.LayoutTabs, - path: baseRouter.callsUIUrl(entity, project, { - traceRootsOnly: true, - }), - }, - { - id: 'ops', - name: 'Operations', - iconName: IconNames.JobProgramCode, - path: baseRouter.opVersionsUIUrl(entity, project, {}), - }, - { - id: 'objects', - name: 'Objects', - iconName: IconNames.CubeContainer, - path: baseRouter.objectVersionsUIUrl(entity, project, {}), - }, - ], - ]; - - return ; -}; diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/FeedbackSidebar.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/FeedbackSidebar.tsx index 2e09a3516f7..0b3c9603fef 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/FeedbackSidebar.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/FeedbackSidebar.tsx @@ -3,7 +3,6 @@ import {useOrgName} from '@wandb/weave/common/hooks/useOrganization'; import {useViewerInfo} from '@wandb/weave/common/hooks/useViewerInfo'; import {useViewerUserInfo2} from '@wandb/weave/common/hooks/useViewerUserInfo'; import {Button} from '@wandb/weave/components/Button'; -import {Icon} from '@wandb/weave/components/Icon'; import {Loading} from '@wandb/weave/components/Loading'; import {annotationsViewed} from '@wandb/weave/integrations/analytics/viewEvents'; import {makeRefCall} from '@wandb/weave/util/refs'; @@ -93,22 +92,25 @@ export const FeedbackSidebar = ({ return (
-
+
Feedback
+
{humanAnnotationSpecs.length > 0 ? ( <> -
- + + (a.name ?? '').localeCompare(b.name ?? '') + )} setUnsavedFeedbackChanges={setUnsavedFeedbackChanges} />
-
+
- ); -}; - type HumanAnnotationInputsProps = { entity: string; project: string; @@ -254,7 +175,7 @@ const HumanAnnotationInputs = ({
{field.name}
{field.description && ( -
+
{field.description}
)} @@ -275,11 +196,3 @@ const HumanAnnotationInputs = ({
); }; - -const DisplayNumericCounter = ({count}: {count: number}) => { - return ( -
- {count} -
- ); -}; diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/HumanAnnotation.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/HumanAnnotation.tsx index eadcd2abe1d..7facffe9556 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/HumanAnnotation.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse3/feedback/StructuredFeedback/HumanAnnotation.tsx @@ -1,7 +1,6 @@ -import {Autocomplete, TextField as MuiTextField} from '@mui/material'; import {toast} from '@wandb/weave/common/components/elements/Toast'; -import {MOON_300} from '@wandb/weave/common/css/color.styles'; import {Button} from '@wandb/weave/components/Button'; +import {Select} from '@wandb/weave/components/Form/Select'; import {TextField} from '@wandb/weave/components/Form/TextField'; import {LoadingDots} from '@wandb/weave/components/LoadingDots'; import {Tailwind} from '@wandb/weave/components/Tailwind'; @@ -413,11 +412,11 @@ export const TextFeedbackColumn = ({ value={value} onChange={onValueChange} maxLength={maxLength} - placeholder="..." + placeholder="" /> {maxLength && (
- {`character max: ${maxLength}`} + {`Maximum characters: ${maxLength}`}
)}
@@ -445,78 +444,30 @@ export const EnumFeedbackColumn = ({ label: option, value: option, })); - opts.splice(0, 0, {label: '', value: ''}); return opts; }, [options]); - - const [value, setValue] = useState