From 910540381a4c8ec23167dc26f2ef9ce2f0e64c0c Mon Sep 17 00:00:00 2001 From: Andrew Truong Date: Sun, 25 Aug 2024 12:01:11 -0400 Subject: [PATCH] !!! move files from weave/legacy -> weave/legacy/weave to match new repo --- .github/workflows/test.yaml | 31 +- Eval Board Syn.ipynb | 630 ++++++------- Eval Board.ipynb | 310 +++--- Text Extraction Eval.ipynb | 884 +++++++++--------- examples/prompts/llm_monitoring/README.md | 2 +- .../openai_client_quickstart.ipynb | 4 +- ...nai_proxy_quickstart_enterprise_mode.ipynb | 4 +- .../dev/synthetic_trace_data.ipynb | 2 +- .../trace_quickstart_decorator.ipynb | 2 +- .../trace_quickstart_langchain.ipynb | 2 +- .../cypress/e2e/interactive/blank.py | 4 +- .../cypress/e2e/interactive/distribution.py | 9 +- .../cypress/e2e/interactive/scatter.py | 10 +- mypy.ini | 90 +- pyrightconfig.json | 2 +- weave/__init__.py | 26 +- weave/conftest.py | 14 +- weave/deploy/gcp/__init__.py | 4 +- weave/deploy/modal/__init__.py | 4 +- weave/deploy/modal/stub.py | 2 +- weave/init_message.py | 2 +- weave/legacy/Dockerfile | 51 + weave/legacy/core_types/__init__.py | 1 - weave/legacy/ecosystem/__init__.py | 1 - weave/legacy/ecosystem/bertviz/__init__.py | 1 - weave/legacy/ecosystem/craiyon/__init__.py | 1 - .../legacy/ecosystem/huggingface/__init__.py | 12 - weave/legacy/ecosystem/keras/__init__.py | 6 - weave/legacy/ecosystem/lens/__init__.py | 1 - weave/legacy/ecosystem/py/__init__.py | 1 - weave/legacy/ecosystem/pytorch/__init__.py | 1 - weave/legacy/ecosystem/sklearn/__init__.py | 1 - weave/legacy/ecosystem/slack/__init__.py | 2 - weave/legacy/ecosystem/slurm/__init__.py | 1 - .../legacy/ecosystem/torchvision/__init__.py | 1 - weave/legacy/ecosystem/wandb/__init__.py | 13 - .../examples/apps/explore_embeddings.ipynb | 756 +++++++-------- .../examples/experimental/AutoBoard.ipynb | 77 +- .../examples/experimental/Mutations.ipynb | 134 +-- ...oductionMonitoringConceptualOverview.ipynb | 710 +++++++------- .../ProductionMonitoring/StreamTable.md | 2 +- .../stream_table_api.ipynb | 314 +++---- .../experimental/Table Summary Panel.ipynb | 184 ++-- .../experimental/app/Embeddings.ipynb | 240 ++--- .../examples/experimental/app/RunChain.ipynb | 348 +++---- .../experimental/app/scenario_compare.ipynb | 346 +++---- .../examples/experimental/bert_viz.ipynb | 134 +-- .../experimental/composite_histograms.ipynb | 266 +++--- .../examples/experimental/dir_browsing.ipynb | 48 +- .../examples/experimental/ecosystem.ipynb | 46 +- .../experimental/huggingface_datasets.ipynb | 172 ++-- .../experimental/huggingface_models.ipynb | 256 ++--- .../experimental/image_gen_craiyon.ipynb | 98 +- .../examples/experimental/image_gen_ops.ipynb | 246 ++--- .../examples/experimental/layout_panels.ipynb | 406 ++++---- .../experimental/llm_monitor_helper.ipynb | 390 ++++---- .../examples/experimental/mnist_train.ipynb | 158 ++-- .../experimental/object_version_compare.ipynb | 226 ++--- .../experimental/old_openai_monitoring.ipynb | 446 ++++----- .../experimental/openai_monitoring.ipynb | 474 +++++----- .../generate_synth_mon_board.ipynb | 378 ++++---- .../prompts_dev/synthetic_openai_data.ipynb | 328 +++---- ...4 Tutorial - Publishing Data and Ops.ipynb | 244 ++--- .../skip_test/Composable Python panels.ipynb | 152 +-- .../skip_test/Confusion Matrix.ipynb | 570 +++++------ .../experimental/skip_test/Custom ops.ipynb | 292 +++--- .../skip_test/Diffusion explore 2.ipynb | 196 ++-- .../skip_test/Diffusion explore.ipynb | 332 +++---- .../skip_test/Diffusion story.ipynb | 238 ++--- .../experimental/skip_test/Docbot.ipynb | 652 ++++++------- .../experimental/skip_test/Hack demo.ipynb | 228 ++--- .../experimental/skip_test/KerasModel.ipynb | 604 ++++++------ .../skip_test/LLMs Via Weave.ipynb | 216 ++--- .../experimental/skip_test/Model Cards.ipynb | 152 +-- .../experimental/skip_test/Models.ipynb | 200 ++-- .../experimental/skip_test/Monitor2.ipynb | 614 ++++++------ .../experimental/skip_test/Monitor3.ipynb | 634 ++++++------- .../skip_test/Multiple Y Axes.ipynb | 362 +++---- .../skip_test/Mutation - Code Editor.ipynb | 104 +-- .../experimental/skip_test/Mutation.ipynb | 102 +- .../experimental/skip_test/OpenAI.ipynb | 206 ++-- .../skip_test/Oxford-IIIT Pet Dataset.ipynb | 224 ++--- .../skip_test/Performance profiling.ipynb | 300 +++--- .../skip_test/ProdMonStreamTableIntro.ipynb | 264 +++--- .../experimental/skip_test/Shap.ipynb | 114 +-- .../experimental/skip_test/StreamTables.ipynb | 178 ++-- .../experimental/skip_test/TimeSeries.ipynb | 290 +++--- .../experimental/skip_test/Untitled.ipynb | 96 +- .../experimental/skip_test/Vectorizing.ipynb | 176 ++-- .../experimental/skip_test/WB data.ipynb | 154 +-- .../skip_test/Weave geo data.ipynb | 206 ++-- .../experimental/skip_test/art_explore.ipynb | 276 +++--- .../skip_test/branching_runs.ipynb | 424 ++++----- .../skip_test/image_gen_replicate.ipynb | 148 +-- .../skip_test/langchain docbot.ipynb | 172 ++-- .../skip_test/plot_selection.ipynb | 120 +-- .../experimental/skip_test/slurm.ipynb | 126 +-- .../skip_test/weave_engine_tracing.ipynb | 246 ++--- .../experimental/skip_test/weave_scifi.ipynb | 154 +-- .../examples/experimental/spacy_demo.ipynb | 98 +- .../experimental/table_data_passing.ipynb | 158 ++-- .../examples/experimental/tag_search.ipynb | 214 ++--- weave/legacy/examples/get_started.ipynb | 596 ++++++------ weave/legacy/examples/reference/WB_API.ipynb | 172 ++-- .../examples/reference/confusion_matrix.ipynb | 462 ++++----- .../reference/control/Object Picker.ipynb | 236 ++--- .../examples/reference/control/Slider.ipynb | 144 +-- .../reference/create_plots_ui_guide.ipynb | 654 ++++++------- .../layout/Group & LabeledItem.ipynb | 90 +- .../legacy/examples/reference/markdown.ipynb | 94 +- .../reference/panels/plot/synced_axes.ipynb | 159 +++- .../examples/reference/vis/Distribution.ipynb | 270 +++--- .../examples/reference/vis/Scatter.ipynb | 196 ++-- .../vis/derived_plots_from_tables.ipynb | 216 ++--- weave/legacy/ops_arrow/__init__.py | 12 - weave/legacy/ops_arrow/ops.py | 11 - .../legacy/ops_primitives/test_list_range.py | 7 - weave/legacy/panels/__init__.py | 70 -- weave/legacy/scripts/clear_cache.py | 2 +- weave/legacy/tests/test_access.py | 4 +- weave/legacy/tests/test_api.py | 4 +- weave/legacy/tests/test_arrow.py | 22 +- weave/legacy/tests/test_arrow_awl.py | 8 +- weave/legacy/tests/test_arrow_concat.py | 6 +- weave/legacy/tests/test_arrow_perf.py | 2 +- weave/legacy/tests/test_arrow_topy.py | 2 +- weave/legacy/tests/test_arrow_vectorizer.py | 18 +- weave/legacy/tests/test_artifact.py | 4 +- weave/legacy/tests/test_artifact_metadata.py | 10 +- weave/legacy/tests/test_assignment.py | 2 +- weave/legacy/tests/test_async.py | 2 +- weave/legacy/tests/test_async_queue.py | 2 +- weave/legacy/tests/test_basic_ops.py | 10 +- weave/legacy/tests/test_box.py | 2 +- weave/legacy/tests/test_cache.py | 2 +- weave/legacy/tests/test_codify.py | 70 +- weave/legacy/tests/test_compile.py | 40 +- weave/legacy/tests/test_complex_calls.py | 2 +- weave/legacy/tests/test_cond.py | 32 +- weave/legacy/tests/test_const_type_mapper.py | 2 +- weave/legacy/tests/test_custom_types.py | 6 +- weave/legacy/tests/test_datetime_timestamp.py | 2 +- weave/legacy/tests/test_decorators.py | 8 +- weave/legacy/tests/test_derive_op.py | 4 +- weave/legacy/tests/test_dispatch.py | 2 +- weave/legacy/tests/test_examples.py | 4 +- weave/legacy/tests/test_execute.py | 12 +- weave/legacy/tests/test_execute_fast.py | 6 +- weave/legacy/tests/test_execution_graphs.py | 4 +- weave/legacy/tests/test_file.py | 2 +- weave/legacy/tests/test_filesystem.py | 2 +- weave/legacy/tests/test_gql_to_weave.py | 4 +- weave/legacy/tests/test_graph.py | 4 +- weave/legacy/tests/test_graph_debug.py | 2 +- weave/legacy/tests/test_hypothesis.py | 6 +- weave/legacy/tests/test_infer_types.py | 2 +- weave/legacy/tests/test_io_service.py | 2 +- weave/legacy/tests/test_join.py | 6 +- weave/legacy/tests/test_js_compat.py | 6 +- weave/legacy/tests/test_language.py | 4 +- weave/legacy/tests/test_levenshtein.py | 2 +- weave/legacy/tests/test_list_arrow_compat.py | 14 +- weave/legacy/tests/test_list_indexing.py | 2 +- weave/legacy/tests/test_logging.py | 2 +- weave/legacy/tests/test_mappability.py | 24 +- weave/legacy/tests/test_mappers_arrow.py | 4 +- weave/legacy/tests/test_mappers_python.py | 4 +- weave/legacy/tests/test_media.py | 4 +- weave/legacy/tests/test_media_user.py | 6 +- weave/legacy/tests/test_monitoring.py | 2 +- weave/legacy/tests/test_monitoring_openai.py | 20 +- weave/legacy/tests/test_mutation2.py | 76 +- weave/legacy/tests/test_mutations.py | 4 +- weave/legacy/tests/test_node_ref.py | 6 +- weave/legacy/tests/test_nullability.py | 6 +- weave/legacy/tests/test_number_bin.py | 4 +- weave/legacy/tests/test_numpy.py | 8 +- weave/legacy/tests/test_op.py | 8 +- weave/legacy/tests/test_op_behaviors.py | 10 +- weave/legacy/tests/test_op_coverage.py | 2 +- weave/legacy/tests/test_op_def.py | 4 +- weave/legacy/tests/test_op_def_type.py | 2 +- weave/legacy/tests/test_op_dispatching.py | 14 +- weave/legacy/tests/test_op_serialization.py | 4 +- weave/legacy/tests/test_panel_coverage.py | 2 +- weave/legacy/tests/test_panel_time_series.py | 4 +- weave/legacy/tests/test_panels.py | 28 +- weave/legacy/tests/test_partial_object.py | 14 +- weave/legacy/tests/test_plot.py | 42 +- weave/legacy/tests/test_projection_timeout.py | 2 +- weave/legacy/tests/test_publish_flow.py | 100 +- weave/legacy/tests/test_pydantic.py | 2 +- weave/legacy/tests/test_ref_tracking.py | 4 +- weave/legacy/tests/test_refs.py | 4 +- .../legacy/tests/test_relpath_no_syscalls.py | 2 +- weave/legacy/tests/test_run_segment.py | 8 +- weave/legacy/tests/test_serialize.py | 10 +- weave/legacy/tests/test_show.py | 4 +- weave/legacy/tests/test_stitch.py | 42 +- weave/legacy/tests/test_storage.py | 12 +- weave/legacy/tests/test_stream_table.py | 2 +- weave/legacy/tests/test_table_ops.py | 8 +- weave/legacy/tests/test_tagging.py | 18 +- weave/legacy/tests/test_templates.py | 24 +- weave/legacy/tests/test_timestamp_bin.py | 10 +- weave/legacy/tests/test_trace.py | 8 +- .../tests/test_typeddict_notrequired.py | 4 +- weave/legacy/tests/test_uris.py | 4 +- weave/legacy/tests/test_wb.py | 22 +- weave/legacy/tests/test_wb_data_types.py | 38 +- weave/legacy/tests/test_wb_domain_ops.py | 10 +- weave/legacy/tests/test_wb_domain_types.py | 8 +- weave/legacy/tests/test_wb_end_to_end.py | 14 +- .../test_wb_history_loading_compatability.py | 14 +- weave/legacy/tests/test_wb_stream_table.py | 14 +- weave/legacy/tests/test_wb_tables.py | 48 +- weave/legacy/tests/test_weave_api.py | 18 +- weave/legacy/tests/test_weave_types.py | 124 +-- weave/legacy/tests/test_weavejs_fixes.py | 4 +- weave/legacy/tests/test_weavify.py | 6 +- weave/legacy/tests/test_with_columns.py | 2 +- .../tests/util/concrete_tagged_value.py | 4 +- weave/legacy/tests/util/geom.py | 4 +- .../tests/util/list_arrow_test_helpers.py | 6 +- weave/legacy/tests/util/op_specs.py | 6 +- weave/legacy/tests/util/tag_test_util.py | 12 +- weave/legacy/tests/util/weavejs_ops.py | 10 +- weave/legacy/{arrow => weave}/__init__.py | 0 weave/legacy/{ => weave}/_dict_utils.py | 8 +- weave/legacy/{ => weave}/api.py | 36 +- .../twitter => weave/arrow}/__init__.py | 0 weave/legacy/{ => weave}/arrow/arrow.py | 16 +- weave/legacy/{ => weave}/arrow/arrow_tags.py | 10 +- weave/legacy/{ => weave}/arrow/concat.py | 10 +- .../legacy/{ => weave}/arrow/constructors.py | 14 +- weave/legacy/{ => weave}/arrow/convert.py | 16 +- weave/legacy/{ => weave}/arrow/list_.py | 24 +- weave/legacy/{ => weave}/arrow_util.py | 0 weave/legacy/{ => weave}/artifact_base.py | 4 +- weave/legacy/{ => weave}/artifact_fs.py | 16 +- weave/legacy/{ => weave}/artifact_local.py | 6 +- weave/legacy/{ => weave}/artifact_mem.py | 6 +- weave/legacy/{ => weave}/artifact_wandb.py | 12 +- weave/legacy/{ => weave}/async_demo.py | 2 +- weave/legacy/{ => weave}/async_map.py | 0 weave/legacy/{ => weave}/async_queue.py | 0 weave/legacy/{ => weave}/box.py | 2 +- weave/legacy/{ => weave}/cache.py | 4 +- weave/legacy/{ => weave}/client.py | 4 +- weave/legacy/{ => weave}/client_interface.py | 0 .../{ => weave}/codifiable_value_mixin.py | 0 weave/legacy/{ => weave}/codify.py | 22 +- weave/legacy/{ => weave}/compile.py | 16 +- weave/legacy/{ => weave}/compile_domain.py | 10 +- weave/legacy/{ => weave}/compile_table.py | 6 +- weave/legacy/{ => weave}/context.py | 18 +- weave/legacy/{ => weave}/context_state.py | 6 +- weave/legacy/weave/core_types/__init__.py | 1 + .../core_types/stream_table_type.py | 2 +- weave/legacy/{ => weave}/debug.py | 0 weave/legacy/{ => weave}/debug_compile.py | 2 +- weave/legacy/{ => weave}/debug_types.py | 4 +- .../legacy/{ => weave}/decorator_arrow_op.py | 8 +- weave/legacy/{ => weave}/decorator_class.py | 6 +- .../legacy/{ => weave}/decorator_mutation.py | 0 weave/legacy/{ => weave}/decorator_op.py | 8 +- weave/legacy/{ => weave}/decorator_type.py | 8 +- weave/legacy/{ => weave}/decorator_type.pyi | 0 weave/legacy/{ => weave}/decorators.py | 0 weave/legacy/{ => weave}/derive_op.py | 8 +- weave/legacy/{ => weave}/dispatch.py | 10 +- weave/legacy/{ => weave}/eager.py | 8 +- weave/legacy/weave/ecosystem/__init__.py | 1 + weave/legacy/{ => weave}/ecosystem/all.py | 6 +- .../weave/ecosystem/bertviz/__init__.py | 1 + .../{ => weave}/ecosystem/bertviz/panels.py | 18 +- .../weave/ecosystem/craiyon/__init__.py | 1 + .../{ => weave}/ecosystem/craiyon/ops.py | 0 .../ecosystem/example}/__init__.py | 4 +- .../{ => weave}/ecosystem/example/ops.py | 0 .../ecosystem/hdbscan}/__init__.py | 4 +- .../{ => weave}/ecosystem/hdbscan/ops.py | 2 +- .../weave/ecosystem/huggingface/__init__.py | 12 + .../ecosystem/huggingface/hfmodel.py | 8 +- .../huggingface/huggingface_datasets.py | 2 +- .../huggingface/huggingface_models.py | 38 +- .../huggingface/model_textclassification.py | 20 +- .../huggingface/model_textgeneration.py | 10 +- .../ecosystem/keras}/__init__.py | 5 +- .../{ => weave}/ecosystem/keras/model.py | 0 .../ecosystem/langchain/__init__.py | 4 +- .../{ => weave}/ecosystem/langchain/lc.py | 12 +- .../{ => weave}/ecosystem/langchain/util.py | 2 +- weave/legacy/weave/ecosystem/lens/__init__.py | 1 + .../legacy/{ => weave}/ecosystem/lens/lens.py | 0 weave/legacy/weave/ecosystem/py/__init__.py | 1 + .../legacy/{ => weave}/ecosystem/py/pydoc.py | 54 +- .../weave/ecosystem/pytorch/__init__.py | 1 + .../{ => weave}/ecosystem/pytorch/model.py | 0 .../ecosystem/replicate/__init__.py | 4 +- .../{ => weave}/ecosystem/replicate/rep.py | 0 weave/legacy/{ => weave}/ecosystem/root.py | 8 +- .../ecosystem/scenario/__init__.py | 18 +- .../{ => weave}/ecosystem/shap/__init__.py | 4 +- .../legacy/{ => weave}/ecosystem/shap/shap.py | 14 +- .../legacy/weave/ecosystem/shawn/__init__.py | 7 + .../{ => weave}/ecosystem/shawn/petdataset.py | 4 +- .../{ => weave}/ecosystem/shawn/scratch.py | 24 +- .../weave/ecosystem/sklearn/__init__.py | 1 + .../{ => weave}/ecosystem/sklearn/datasets.py | 2 +- .../legacy/weave/ecosystem/slack/__init__.py | 2 + .../{ => weave}/ecosystem/slack/panels.py | 22 +- .../{ => weave}/ecosystem/slack/slack.py | 2 +- .../{ => weave}/ecosystem/slack/slackapi.py | 0 .../ecosystem/slack/slackapi_readexport.py | 0 .../legacy/weave/ecosystem/slurm/__init__.py | 1 + .../legacy/{ => weave}/ecosystem/slurm/ops.py | 24 +- .../ecosystem/spacy}/__init__.py | 4 +- .../{ => weave}/ecosystem/spacy/spacy.py | 20 +- .../test_notebook_ecosystem_executions.py | 4 +- .../ecosystem/torch_mnist_model_example.py | 8 +- .../weave/ecosystem/torchvision/__init__.py | 1 + .../ecosystem/torchvision/datasets.py | 4 +- .../ecosystem/twitter}/__init__.py | 0 .../{ => weave}/ecosystem/twitter/tweet.py | 0 .../ecosystem/umap}/__init__.py | 4 +- .../legacy/{ => weave}/ecosystem/umap/ops.py | 2 +- .../legacy/weave/ecosystem/wandb/__init__.py | 13 + .../ecosystem/wandb/gql_artifact_dag.py | 2 +- .../ecosystem/wandb/panel_distribution.py | 50 +- .../{ => weave}/ecosystem/wandb/panel_geo.py | 56 +- .../ecosystem/wandb/panel_scatter.py | 52 +- .../ecosystem/wandb/panel_time_series.py | 80 +- .../{ => weave}/ecosystem/wandb/run_chain.py | 12 +- .../wandb/test_panel_distribution.py | 4 +- .../{ => weave}/ecosystem/wandb/wandb_objs.py | 56 +- .../ecosystem/wandb/weave_plotly.py | 6 +- .../{ => weave}/ecosystem/xgboost/__init__.py | 4 +- .../{ => weave}/ecosystem/xgboost/model.py | 0 weave/legacy/{ => weave}/engine_trace.py | 4 +- weave/legacy/{ => weave}/environment.py | 8 +- weave/legacy/{ => weave}/errors.py | 0 weave/legacy/{ => weave}/execute.py | 6 +- weave/legacy/{ => weave}/execute_fast.py | 6 +- weave/legacy/{ => weave}/file_base.py | 2 +- weave/legacy/{ => weave}/file_local.py | 2 +- weave/legacy/{ => weave}/file_util.py | 4 +- weave/legacy/{ => weave}/filesystem.py | 2 +- weave/legacy/{ => weave}/forward_graph.py | 4 +- weave/legacy/{ => weave}/gql_json_cache.py | 0 weave/legacy/{ => weave}/gql_op_plugin.py | 4 +- weave/legacy/{ => weave}/gql_schema.py | 4 +- weave/legacy/{ => weave}/gql_to_weave.py | 6 +- weave/legacy/{ => weave}/graph.py | 12 +- weave/legacy/{ => weave}/graph_debug.py | 6 +- weave/legacy/{ => weave}/graph_mapper.py | 8 +- weave/legacy/{ => weave}/infer_types.py | 4 +- weave/legacy/{ => weave}/input_provider.py | 0 weave/legacy/{ => weave}/io_service.py | 2 +- weave/legacy/{ => weave}/language_autocall.py | 4 +- .../language_features}/__init__.py | 0 .../language_features/tagging}/__init__.py | 0 .../tagging/is_tag_getter.py | 8 +- .../tagging/make_tag_getter_op.py | 12 +- .../language_features/tagging/opdef_util.py | 6 +- .../tagging/process_opdef_output_type.py | 12 +- .../tagging/process_opdef_resolve_fn.py | 18 +- .../language_features/tagging/tag_store.py | 6 +- .../tagging/tagged_value_type.py | 10 +- .../tagging/tagged_value_type_helpers.py | 4 +- .../tagging/tagging_op_logic.py | 4 +- .../language_features/tagging/tagging_ops.py | 6 +- .../{ => weave}/language_nullability.py | 8 +- weave/legacy/{ => weave}/logs.py | 2 +- weave/legacy/{ => weave}/make_type.py | 4 +- weave/legacy/{ => weave}/mappers.py | 4 +- weave/legacy/{ => weave}/mappers_arrow.py | 12 +- weave/legacy/{ => weave}/mappers_gql.py | 10 +- weave/legacy/{ => weave}/mappers_publisher.py | 20 +- weave/legacy/{ => weave}/mappers_python.py | 4 +- .../legacy/{ => weave}/mappers_python_def.py | 16 +- weave/legacy/{ => weave}/mappers_weave.py | 6 +- weave/legacy/{ => weave}/memo.py | 2 +- .../legacy/{ => weave}/monitoring/__init__.py | 4 +- .../{ => weave}/monitoring/langchain.py | 4 +- .../legacy/{ => weave}/monitoring/monitor.py | 6 +- .../{ => weave}/monitoring/openai/__init__.py | 0 .../{ => weave}/monitoring/openai/models.py | 4 +- .../{ => weave}/monitoring/openai/openai.py | 6 +- .../{ => weave}/monitoring/openai/util.py | 2 +- weave/legacy/{ => weave}/node_ref.py | 4 +- weave/legacy/{ => weave}/object_context.py | 10 +- .../{ => weave}/object_type_ref_util.py | 2 +- weave/legacy/{ => weave}/op_aliases.py | 0 weave/legacy/{ => weave}/op_args.py | 4 +- weave/legacy/{ => weave}/op_def.py | 22 +- weave/legacy/{ => weave}/op_def_type.py | 8 +- weave/legacy/{ => weave}/op_execute.py | 0 weave/legacy/{ => weave}/op_policy.py | 0 weave/legacy/{ => weave}/ops.py | 0 weave/legacy/weave/ops_arrow/__init__.py | 12 + .../{ => weave}/ops_arrow/arraylist_ops.py | 12 +- weave/legacy/{ => weave}/ops_arrow/boolean.py | 10 +- .../{ => weave}/ops_arrow/convert_ops.py | 8 +- weave/legacy/{ => weave}/ops_arrow/date.py | 12 +- weave/legacy/{ => weave}/ops_arrow/dict.py | 24 +- .../legacy/{ => weave}/ops_arrow/list_join.py | 20 +- .../legacy/{ => weave}/ops_arrow/list_ops.py | 28 +- .../{ => weave}/ops_arrow/list_range.py | 6 +- weave/legacy/{ => weave}/ops_arrow/number.py | 12 +- weave/legacy/{ => weave}/ops_arrow/obj.py | 8 +- weave/legacy/weave/ops_arrow/ops.py | 11 + weave/legacy/{ => weave}/ops_arrow/ref_ops.py | 12 +- weave/legacy/{ => weave}/ops_arrow/string.py | 12 +- weave/legacy/{ => weave}/ops_arrow/util.py | 8 +- .../legacy/{ => weave}/ops_arrow/vectorize.py | 30 +- .../legacy/{ => weave}/ops_domain/__init__.py | 2 +- .../ops_domain/artifact_alias_ops.py | 12 +- .../ops_domain/artifact_collection_ops.py | 12 +- .../ops_domain/artifact_membership_ops.py | 10 +- .../ops_domain/artifact_type_ops.py | 10 +- .../ops_domain/artifact_version_ops.py | 24 +- .../{ => weave}/ops_domain/entity_ops.py | 12 +- .../legacy/{ => weave}/ops_domain/org_ops.py | 6 +- .../{ => weave}/ops_domain/project_ops.py | 16 +- .../ops_domain/repo_insight_ops.py | 12 +- .../{ => weave}/ops_domain/report_ops.py | 12 +- .../weave/ops_domain/run_history/__init__.py | 0 .../ops_domain/run_history/context.py | 0 .../run_history/history_op_common.py | 32 +- .../run_history/run_history_v1_legacy_ops.py | 16 +- .../run_history_v2_parquet_media.py | 22 +- ...run_history_v3_parquet_stream_optimized.py | 28 +- .../legacy/{ => weave}/ops_domain/run_ops.py | 22 +- .../{ => weave}/ops_domain/run_queue_ops.py | 12 +- .../{ => weave}/ops_domain/run_segment.py | 10 +- .../ops_domain/stream_table_ops.py | 12 +- weave/legacy/{ => weave}/ops_domain/table.py | 12 +- .../{ => weave}/ops_domain/trace_tree.py | 10 +- .../legacy/{ => weave}/ops_domain/user_ops.py | 12 +- .../ops_domain/wandb_domain_gql.py | 16 +- .../{ => weave}/ops_domain/wb_domain_types.py | 6 +- .../legacy/{ => weave}/ops_domain/wb_util.py | 30 +- .../{ => weave}/ops_domain/wbgqlquery_op.py | 14 +- .../legacy/{ => weave}/ops_domain/wbmedia.py | 12 +- .../{ => weave}/ops_primitives/__init__.py | 2 +- .../{ => weave}/ops_primitives/_dict_utils.py | 8 +- .../legacy/{ => weave}/ops_primitives/any.py | 4 +- .../{ => weave}/ops_primitives/artifacts.py | 6 +- .../{ => weave}/ops_primitives/boolean.py | 8 +- .../legacy/{ => weave}/ops_primitives/csv_.py | 4 +- .../legacy/{ => weave}/ops_primitives/date.py | 4 +- .../legacy/{ => weave}/ops_primitives/dict.py | 14 +- .../legacy/{ => weave}/ops_primitives/file.py | 14 +- .../ops_primitives/file_artifact.py | 4 +- .../{ => weave}/ops_primitives/file_local.py | 8 +- .../legacy/{ => weave}/ops_primitives/geom.py | 2 +- .../legacy/{ => weave}/ops_primitives/html.py | 4 +- .../{ => weave}/ops_primitives/image.py | 4 +- .../{ => weave}/ops_primitives/json_.py | 4 +- .../{ => weave}/ops_primitives/list_.py | 24 +- .../ops_primitives/list_tag_getters.py | 4 +- .../{ => weave}/ops_primitives/markdown.py | 4 +- .../{ => weave}/ops_primitives/number.py | 6 +- .../{ => weave}/ops_primitives/number_bin.py | 14 +- .../legacy/{ => weave}/ops_primitives/obj.py | 6 +- .../{ => weave}/ops_primitives/op_def.py | 8 +- .../{ => weave}/ops_primitives/pandas_.py | 12 +- .../ops_primitives/projection_utils.py | 4 +- .../{ => weave}/ops_primitives/random_junk.py | 6 +- .../{ => weave}/ops_primitives/server.py | 2 +- .../legacy/{ => weave}/ops_primitives/set_.py | 4 +- .../legacy/{ => weave}/ops_primitives/sql.py | 10 +- .../{ => weave}/ops_primitives/string.py | 4 +- .../{ => weave}/ops_primitives/test_any.py | 6 +- .../{ => weave}/ops_primitives/test_dict.py | 6 +- .../{ => weave}/ops_primitives/test_file.py | 4 +- .../{ => weave}/ops_primitives/test_image.py | 4 +- .../{ => weave}/ops_primitives/test_list.py | 12 +- .../weave/ops_primitives/test_list_range.py | 7 + .../{ => weave}/ops_primitives/test_pandas.py | 6 +- .../{ => weave}/ops_primitives/test_set.py | 2 +- .../{ => weave}/ops_primitives/test_type.py | 4 +- .../ops_primitives/test_typeddict.py | 8 +- .../ops_primitives/timestamp_bin.py | 14 +- .../legacy/{ => weave}/ops_primitives/type.py | 6 +- .../{ => weave}/ops_primitives/weave_api.py | 4 +- weave/legacy/{ => weave}/panel.py | 10 +- weave/legacy/{ => weave}/panel_util.py | 6 +- weave/legacy/weave/panels/__init__.py | 70 ++ weave/legacy/{ => weave}/panels/bank.py | 2 +- weave/legacy/{ => weave}/panels/panel_auto.py | 2 +- .../legacy/{ => weave}/panels/panel_basic.py | 2 +- .../legacy/{ => weave}/panels/panel_board.py | 12 +- weave/legacy/{ => weave}/panels/panel_card.py | 2 +- .../legacy/{ => weave}/panels/panel_color.py | 2 +- .../{ => weave}/panels/panel_daterange.py | 2 +- .../legacy/{ => weave}/panels/panel_domain.py | 2 +- .../{ => weave}/panels/panel_dropdown.py | 4 +- weave/legacy/{ => weave}/panels/panel_each.py | 6 +- .../{ => weave}/panels/panel_each_column.py | 4 +- .../{ => weave}/panels/panel_expression.py | 2 +- .../legacy/{ => weave}/panels/panel_facet.py | 22 +- .../{ => weave}/panels/panel_facet_tabs.py | 6 +- .../{ => weave}/panels/panel_filter_editor.py | 2 +- .../panels/panel_function_editor.py | 2 +- .../legacy/{ => weave}/panels/panel_group.py | 10 +- .../panels/panel_group_panel_info.py | 0 .../panels/panel_grouping_editor.py | 2 +- .../{ => weave}/panels/panel_histogram.py | 2 +- weave/legacy/{ => weave}/panels/panel_html.py | 2 +- .../{ => weave}/panels/panel_labeled_item.py | 2 +- .../legacy/{ => weave}/panels/panel_legacy.py | 2 +- .../{ => weave}/panels/panel_markdown.py | 2 +- .../{ => weave}/panels/panel_object_picker.py | 4 +- weave/legacy/{ => weave}/panels/panel_plot.py | 14 +- .../legacy/{ => weave}/panels/panel_query.py | 10 +- .../{ => weave}/panels/panel_sections.py | 6 +- .../legacy/{ => weave}/panels/panel_select.py | 6 +- .../panels/panel_sidebarcontainer.py | 4 +- .../legacy/{ => weave}/panels/panel_slider.py | 6 +- .../legacy/{ => weave}/panels/panel_string.py | 2 +- .../{ => weave}/panels/panel_string_editor.py | 2 +- .../legacy/{ => weave}/panels/panel_table.py | 34 +- .../legacy/{ => weave}/panels/panel_trace.py | 4 +- .../{ => weave}/panels/panel_trace_span.py | 2 +- .../{ => weave}/panels/panel_weavelink.py | 6 +- .../legacy/{ => weave}/panels/table_state.py | 6 +- .../legacy/{ => weave}/panels_py/__init__.py | 6 +- .../panels_py/generator_templates.py | 4 +- .../panels_py/instructions/panel_autoboard.md | 0 .../instructions/panel_llm_monitor.md | 0 .../instructions/panel_trace_monitor.md | 6 +- .../{ => weave}/panels_py/panel_autoboard.py | 84 +- .../{ => weave}/panels_py/panel_eval.py | 92 +- .../panels_py/panel_example_custom_board.py | 20 +- .../panels_py/panel_llm_monitor.py | 66 +- .../panels_py/panel_observability.py | 174 ++-- .../{ => weave}/panels_py/panel_seedboard.py | 14 +- .../panels_py/panel_trace_monitor.py | 50 +- weave/legacy/{ => weave}/parallelism.py | 2 +- weave/legacy/{ => weave}/partial_object.py | 8 +- weave/legacy/{ => weave}/path_util.py | 2 +- .../legacy/{ => weave}/propagate_gql_keys.py | 6 +- weave/legacy/{ => weave}/pyfunc_type_util.py | 6 +- weave/legacy/{ => weave}/ref_base.py | 10 +- weave/legacy/{ => weave}/registry_mem.py | 10 +- weave/legacy/{ => weave}/run.py | 0 .../{ => weave}/run_streamtable_span.py | 8 +- weave/legacy/{ => weave}/runfiles_wandb.py | 8 +- weave/legacy/{ => weave}/runs.py | 2 +- weave/legacy/{ => weave}/serialize.py | 6 +- weave/legacy/{ => weave}/server.py | 6 +- .../{ => weave}/server_error_handling.py | 4 +- weave/legacy/{ => weave}/server_interface.py | 0 weave/legacy/{ => weave}/show.py | 8 +- weave/legacy/{ => weave}/signal_handlers.py | 2 +- weave/legacy/{ => weave}/stitch.py | 8 +- weave/legacy/{ => weave}/storage.py | 12 +- .../{ => weave}/stream_data_interfaces.py | 0 weave/legacy/{ => weave}/timestamp.py | 0 weave/legacy/weave/trace_legacy.py | 84 ++ weave/legacy/{ => weave}/trace_local.py | 10 +- weave/legacy/{ => weave}/types_numpy.py | 2 +- weave/legacy/{ => weave}/uris.py | 2 +- weave/legacy/{ => weave}/urls.py | 2 +- weave/legacy/{ => weave}/usage_analytics.py | 2 +- weave/legacy/{ => weave}/util.py | 2 +- weave/legacy/{ => weave}/val_const.py | 2 +- weave/legacy/{ => weave}/value_or_error.py | 0 weave/legacy/{ => weave}/wandb_api.py | 8 +- weave/legacy/{ => weave}/wandb_client_api.py | 2 +- .../legacy/{ => weave}/wandb_file_manager.py | 4 +- .../wandb_interface/project_creator.py | 0 .../wandb_interface/wandb_artifact_pusher.py | 6 +- .../wandb_interface/wandb_lite_run.py | 4 +- .../wandb_interface/wandb_stream_table.py | 14 +- weave/legacy/{ => weave}/wandb_util.py | 6 +- weave/legacy/{ => weave}/weave_http.py | 2 +- weave/legacy/{ => weave}/weave_inspector.py | 6 +- weave/legacy/{ => weave}/weave_internal.py | 16 +- weave/legacy/{ => weave}/weave_pydantic.py | 2 +- weave/legacy/{ => weave}/weave_types.py | 26 +- weave/legacy/{ => weave}/weavejs_fixes.py | 4 +- weave/legacy/{ => weave}/weavify.py | 10 +- weave/syndata_mon.py | 2 +- weave/test_scripts/wandb_artifact_perf.py | 2 +- weave/tests/fixture_fakewandb.py | 6 +- weave/tests/trace/op_versioning_solo.py | 2 +- weave/tests/trace/test_op_versioning.py | 2 +- weave/tests/trace/test_server.py | 14 +- weave/tests/trace/test_weave_client.py | 2 +- weave/tests/trace/test_weaveflow.py | 2 +- weave/tests/wandb_system_tests_conftest.py | 2 +- weave/trace/api.py | 4 +- weave/trace/box.py | 2 +- weave/trace/cli.py | 2 +- weave/trace/client_context/weave_client.py | 4 +- weave/trace/custom_objs.py | 4 +- weave/trace/feedback.py | 2 +- weave/trace/op.py | 4 +- weave/trace/op_type.py | 4 +- weave/trace/ref_util.py | 6 +- weave/trace/rich_pydantic_util.py | 2 +- weave/trace/serve_fastapi.py | 6 +- weave/trace/weave_client.py | 4 +- weave/trace/weave_init.py | 8 +- .../trace_server/remote_http_trace_server.py | 4 +- weave/weave_server.py | 16 +- 609 files changed, 13997 insertions(+), 13764 deletions(-) create mode 100644 weave/legacy/Dockerfile delete mode 100644 weave/legacy/core_types/__init__.py delete mode 100644 weave/legacy/ecosystem/__init__.py delete mode 100644 weave/legacy/ecosystem/bertviz/__init__.py delete mode 100644 weave/legacy/ecosystem/craiyon/__init__.py delete mode 100644 weave/legacy/ecosystem/huggingface/__init__.py delete mode 100644 weave/legacy/ecosystem/keras/__init__.py delete mode 100644 weave/legacy/ecosystem/lens/__init__.py delete mode 100644 weave/legacy/ecosystem/py/__init__.py delete mode 100644 weave/legacy/ecosystem/pytorch/__init__.py delete mode 100644 weave/legacy/ecosystem/sklearn/__init__.py delete mode 100644 weave/legacy/ecosystem/slack/__init__.py delete mode 100644 weave/legacy/ecosystem/slurm/__init__.py delete mode 100644 weave/legacy/ecosystem/torchvision/__init__.py delete mode 100644 weave/legacy/ecosystem/wandb/__init__.py delete mode 100644 weave/legacy/ops_arrow/__init__.py delete mode 100644 weave/legacy/ops_arrow/ops.py delete mode 100644 weave/legacy/ops_primitives/test_list_range.py delete mode 100644 weave/legacy/panels/__init__.py rename weave/legacy/{arrow => weave}/__init__.py (100%) rename weave/legacy/{ => weave}/_dict_utils.py (97%) rename weave/legacy/{ => weave}/api.py (78%) rename weave/legacy/{ecosystem/twitter => weave/arrow}/__init__.py (100%) rename weave/legacy/{ => weave}/arrow/arrow.py (97%) rename weave/legacy/{ => weave}/arrow/arrow_tags.py (93%) rename weave/legacy/{ => weave}/arrow/concat.py (98%) rename weave/legacy/{ => weave}/arrow/constructors.py (90%) rename weave/legacy/{ => weave}/arrow/convert.py (98%) rename weave/legacy/{ => weave}/arrow/list_.py (98%) rename weave/legacy/{ => weave}/arrow_util.py (100%) rename weave/legacy/{ => weave}/artifact_base.py (95%) rename weave/legacy/{ => weave}/artifact_fs.py (97%) rename weave/legacy/{ => weave}/artifact_local.py (99%) rename weave/legacy/{ => weave}/artifact_mem.py (90%) rename weave/legacy/{ => weave}/artifact_wandb.py (99%) rename weave/legacy/{ => weave}/async_demo.py (97%) rename weave/legacy/{ => weave}/async_map.py (100%) rename weave/legacy/{ => weave}/async_queue.py (100%) rename weave/legacy/{ => weave}/box.py (99%) rename weave/legacy/{ => weave}/cache.py (98%) rename weave/legacy/{ => weave}/client.py (91%) rename weave/legacy/{ => weave}/client_interface.py (100%) rename weave/legacy/{ => weave}/codifiable_value_mixin.py (100%) rename weave/legacy/{ => weave}/codify.py (91%) rename weave/legacy/{ => weave}/compile.py (99%) rename weave/legacy/{ => weave}/compile_domain.py (97%) rename weave/legacy/{ => weave}/compile_table.py (92%) rename weave/legacy/{ => weave}/context.py (88%) rename weave/legacy/{ => weave}/context_state.py (98%) create mode 100644 weave/legacy/weave/core_types/__init__.py rename weave/legacy/{ => weave}/core_types/stream_table_type.py (78%) rename weave/legacy/{ => weave}/debug.py (100%) rename weave/legacy/{ => weave}/debug_compile.py (97%) rename weave/legacy/{ => weave}/debug_types.py (97%) rename weave/legacy/{ => weave}/decorator_arrow_op.py (96%) rename weave/legacy/{ => weave}/decorator_class.py (93%) rename weave/legacy/{ => weave}/decorator_mutation.py (100%) rename weave/legacy/{ => weave}/decorator_op.py (93%) rename weave/legacy/{ => weave}/decorator_type.py (95%) rename weave/legacy/{ => weave}/decorator_type.pyi (100%) rename weave/legacy/{ => weave}/decorators.py (100%) rename weave/legacy/{ => weave}/derive_op.py (98%) rename weave/legacy/{ => weave}/dispatch.py (98%) rename weave/legacy/{ => weave}/eager.py (93%) create mode 100644 weave/legacy/weave/ecosystem/__init__.py rename weave/legacy/{ => weave}/ecosystem/all.py (90%) create mode 100644 weave/legacy/weave/ecosystem/bertviz/__init__.py rename weave/legacy/{ => weave}/ecosystem/bertviz/panels.py (83%) create mode 100644 weave/legacy/weave/ecosystem/craiyon/__init__.py rename weave/legacy/{ => weave}/ecosystem/craiyon/ops.py (100%) rename weave/legacy/{ecosystem/hdbscan => weave/ecosystem/example}/__init__.py (73%) rename weave/legacy/{ => weave}/ecosystem/example/ops.py (100%) rename weave/legacy/{ecosystem/umap => weave/ecosystem/hdbscan}/__init__.py (73%) rename weave/legacy/{ => weave}/ecosystem/hdbscan/ops.py (95%) create mode 100644 weave/legacy/weave/ecosystem/huggingface/__init__.py rename weave/legacy/{ => weave}/ecosystem/huggingface/hfmodel.py (96%) rename weave/legacy/{ => weave}/ecosystem/huggingface/huggingface_datasets.py (98%) rename weave/legacy/{ => weave}/ecosystem/huggingface/huggingface_models.py (78%) rename weave/legacy/{ => weave}/ecosystem/huggingface/model_textclassification.py (89%) rename weave/legacy/{ => weave}/ecosystem/huggingface/model_textgeneration.py (91%) rename weave/legacy/{ecosystem/spacy => weave/ecosystem/keras}/__init__.py (51%) rename weave/legacy/{ => weave}/ecosystem/keras/model.py (100%) rename weave/legacy/{ => weave}/ecosystem/langchain/__init__.py (67%) rename weave/legacy/{ => weave}/ecosystem/langchain/lc.py (98%) rename weave/legacy/{ => weave}/ecosystem/langchain/util.py (99%) create mode 100644 weave/legacy/weave/ecosystem/lens/__init__.py rename weave/legacy/{ => weave}/ecosystem/lens/lens.py (100%) create mode 100644 weave/legacy/weave/ecosystem/py/__init__.py rename weave/legacy/{ => weave}/ecosystem/py/pydoc.py (76%) create mode 100644 weave/legacy/weave/ecosystem/pytorch/__init__.py rename weave/legacy/{ => weave}/ecosystem/pytorch/model.py (100%) rename weave/legacy/{ => weave}/ecosystem/replicate/__init__.py (57%) rename weave/legacy/{ => weave}/ecosystem/replicate/rep.py (100%) rename weave/legacy/{ => weave}/ecosystem/root.py (96%) rename weave/legacy/{ => weave}/ecosystem/scenario/__init__.py (79%) rename weave/legacy/{ => weave}/ecosystem/shap/__init__.py (52%) rename weave/legacy/{ => weave}/ecosystem/shap/shap.py (85%) create mode 100644 weave/legacy/weave/ecosystem/shawn/__init__.py rename weave/legacy/{ => weave}/ecosystem/shawn/petdataset.py (96%) rename weave/legacy/{ => weave}/ecosystem/shawn/scratch.py (69%) create mode 100644 weave/legacy/weave/ecosystem/sklearn/__init__.py rename weave/legacy/{ => weave}/ecosystem/sklearn/datasets.py (81%) create mode 100644 weave/legacy/weave/ecosystem/slack/__init__.py rename weave/legacy/{ => weave}/ecosystem/slack/panels.py (73%) rename weave/legacy/{ => weave}/ecosystem/slack/slack.py (97%) rename weave/legacy/{ => weave}/ecosystem/slack/slackapi.py (100%) rename weave/legacy/{ => weave}/ecosystem/slack/slackapi_readexport.py (100%) create mode 100644 weave/legacy/weave/ecosystem/slurm/__init__.py rename weave/legacy/{ => weave}/ecosystem/slurm/ops.py (79%) rename weave/legacy/{ecosystem/shawn => weave/ecosystem/spacy}/__init__.py (51%) rename weave/legacy/{ => weave}/ecosystem/spacy/spacy.py (72%) rename weave/legacy/{ => weave}/ecosystem/test_notebook_ecosystem_executions.py (98%) rename weave/legacy/{ => weave}/ecosystem/torch_mnist_model_example.py (93%) create mode 100644 weave/legacy/weave/ecosystem/torchvision/__init__.py rename weave/legacy/{ => weave}/ecosystem/torchvision/datasets.py (98%) rename weave/legacy/{language_features => weave/ecosystem/twitter}/__init__.py (100%) rename weave/legacy/{ => weave}/ecosystem/twitter/tweet.py (100%) rename weave/legacy/{ecosystem/example => weave/ecosystem/umap}/__init__.py (74%) rename weave/legacy/{ => weave}/ecosystem/umap/ops.py (96%) create mode 100644 weave/legacy/weave/ecosystem/wandb/__init__.py rename weave/legacy/{ => weave}/ecosystem/wandb/gql_artifact_dag.py (98%) rename weave/legacy/{ => weave}/ecosystem/wandb/panel_distribution.py (75%) rename weave/legacy/{ => weave}/ecosystem/wandb/panel_geo.py (77%) rename weave/legacy/{ => weave}/ecosystem/wandb/panel_scatter.py (65%) rename weave/legacy/{ => weave}/ecosystem/wandb/panel_time_series.py (80%) rename weave/legacy/{ => weave}/ecosystem/wandb/run_chain.py (91%) rename weave/legacy/{ => weave}/ecosystem/wandb/test_panel_distribution.py (93%) rename weave/legacy/{ => weave}/ecosystem/wandb/wandb_objs.py (73%) rename weave/legacy/{ => weave}/ecosystem/wandb/weave_plotly.py (97%) rename weave/legacy/{ => weave}/ecosystem/xgboost/__init__.py (51%) rename weave/legacy/{ => weave}/ecosystem/xgboost/model.py (100%) rename weave/legacy/{ => weave}/engine_trace.py (98%) rename weave/legacy/{ => weave}/environment.py (98%) rename weave/legacy/{ => weave}/errors.py (100%) rename weave/legacy/{ => weave}/execute.py (99%) rename weave/legacy/{ => weave}/execute_fast.py (97%) rename weave/legacy/{ => weave}/file_base.py (98%) rename weave/legacy/{ => weave}/file_local.py (98%) rename weave/legacy/{ => weave}/file_util.py (88%) rename weave/legacy/{ => weave}/filesystem.py (98%) rename weave/legacy/{ => weave}/forward_graph.py (98%) rename weave/legacy/{ => weave}/gql_json_cache.py (100%) rename weave/legacy/{ => weave}/gql_op_plugin.py (95%) rename weave/legacy/{ => weave}/gql_schema.py (87%) rename weave/legacy/{ => weave}/gql_to_weave.py (97%) rename weave/legacy/{ => weave}/graph.py (98%) rename weave/legacy/{ => weave}/graph_debug.py (98%) rename weave/legacy/{ => weave}/graph_mapper.py (75%) rename weave/legacy/{ => weave}/infer_types.py (97%) rename weave/legacy/{ => weave}/input_provider.py (100%) rename weave/legacy/{ => weave}/io_service.py (99%) rename weave/legacy/{ => weave}/language_autocall.py (93%) rename weave/legacy/{language_features/tagging => weave/language_features}/__init__.py (100%) rename weave/legacy/{ops_domain/run_history => weave/language_features/tagging}/__init__.py (100%) rename weave/legacy/{ => weave}/language_features/tagging/is_tag_getter.py (84%) rename weave/legacy/{ => weave}/language_features/tagging/make_tag_getter_op.py (87%) rename weave/legacy/{ => weave}/language_features/tagging/opdef_util.py (91%) rename weave/legacy/{ => weave}/language_features/tagging/process_opdef_output_type.py (85%) rename weave/legacy/{ => weave}/language_features/tagging/process_opdef_resolve_fn.py (91%) rename weave/legacy/{ => weave}/language_features/tagging/tag_store.py (98%) rename weave/legacy/{ => weave}/language_features/tagging/tagged_value_type.py (98%) rename weave/legacy/{ => weave}/language_features/tagging/tagged_value_type_helpers.py (94%) rename weave/legacy/{ => weave}/language_features/tagging/tagging_op_logic.py (86%) rename weave/legacy/{ => weave}/language_features/tagging/tagging_ops.py (84%) rename weave/legacy/{ => weave}/language_nullability.py (83%) rename weave/legacy/{ => weave}/logs.py (99%) rename weave/legacy/{ => weave}/make_type.py (81%) rename weave/legacy/{ => weave}/mappers.py (81%) rename weave/legacy/{ => weave}/mappers_arrow.py (98%) rename weave/legacy/{ => weave}/mappers_gql.py (93%) rename weave/legacy/{ => weave}/mappers_publisher.py (92%) rename weave/legacy/{ => weave}/mappers_python.py (85%) rename weave/legacy/{ => weave}/mappers_python_def.py (97%) rename weave/legacy/{ => weave}/mappers_weave.py (95%) rename weave/legacy/{ => weave}/memo.py (97%) rename weave/legacy/{ => weave}/monitoring/__init__.py (58%) rename weave/legacy/{ => weave}/monitoring/langchain.py (94%) rename weave/legacy/{ => weave}/monitoring/monitor.py (98%) rename weave/legacy/{ => weave}/monitoring/openai/__init__.py (100%) rename weave/legacy/{ => weave}/monitoring/openai/models.py (90%) rename weave/legacy/{ => weave}/monitoring/openai/openai.py (98%) rename weave/legacy/{ => weave}/monitoring/openai/util.py (98%) rename weave/legacy/{ => weave}/node_ref.py (97%) rename weave/legacy/{ => weave}/object_context.py (95%) rename weave/legacy/{ => weave}/object_type_ref_util.py (97%) rename weave/legacy/{ => weave}/op_aliases.py (100%) rename weave/legacy/{ => weave}/op_args.py (98%) rename weave/legacy/{ => weave}/op_def.py (97%) rename weave/legacy/{ => weave}/op_def_type.py (99%) rename weave/legacy/{ => weave}/op_execute.py (100%) rename weave/legacy/{ => weave}/op_policy.py (100%) rename weave/legacy/{ => weave}/ops.py (100%) create mode 100644 weave/legacy/weave/ops_arrow/__init__.py rename weave/legacy/{ => weave}/ops_arrow/arraylist_ops.py (96%) rename weave/legacy/{ => weave}/ops_arrow/boolean.py (93%) rename weave/legacy/{ => weave}/ops_arrow/convert_ops.py (71%) rename weave/legacy/{ => weave}/ops_arrow/date.py (94%) rename weave/legacy/{ => weave}/ops_arrow/dict.py (96%) rename weave/legacy/{ => weave}/ops_arrow/list_join.py (93%) rename weave/legacy/{ => weave}/ops_arrow/list_ops.py (97%) rename weave/legacy/{ => weave}/ops_arrow/list_range.py (61%) rename weave/legacy/{ => weave}/ops_arrow/number.py (97%) rename weave/legacy/{ => weave}/ops_arrow/obj.py (72%) create mode 100644 weave/legacy/weave/ops_arrow/ops.py rename weave/legacy/{ => weave}/ops_arrow/ref_ops.py (83%) rename weave/legacy/{ => weave}/ops_arrow/string.py (97%) rename weave/legacy/{ => weave}/ops_arrow/util.py (91%) rename weave/legacy/{ => weave}/ops_arrow/vectorize.py (97%) rename weave/legacy/{ => weave}/ops_domain/__init__.py (93%) rename weave/legacy/{ => weave}/ops_domain/artifact_alias_ops.py (61%) rename weave/legacy/{ => weave}/ops_domain/artifact_collection_ops.py (94%) rename weave/legacy/{ => weave}/ops_domain/artifact_membership_ops.py (89%) rename weave/legacy/{ => weave}/ops_domain/artifact_type_ops.py (89%) rename weave/legacy/{ => weave}/ops_domain/artifact_version_ops.py (96%) rename weave/legacy/{ => weave}/ops_domain/entity_ops.py (82%) rename weave/legacy/{ => weave}/ops_domain/org_ops.py (85%) rename weave/legacy/{ => weave}/ops_domain/project_ops.py (93%) rename weave/legacy/{ => weave}/ops_domain/repo_insight_ops.py (94%) rename weave/legacy/{ => weave}/ops_domain/report_ops.py (91%) create mode 100644 weave/legacy/weave/ops_domain/run_history/__init__.py rename weave/legacy/{ => weave}/ops_domain/run_history/context.py (100%) rename weave/legacy/{ => weave}/ops_domain/run_history/history_op_common.py (94%) rename weave/legacy/{ => weave}/ops_domain/run_history/run_history_v1_legacy_ops.py (89%) rename weave/legacy/{ => weave}/ops_domain/run_history/run_history_v2_parquet_media.py (90%) rename weave/legacy/{ => weave}/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py (98%) rename weave/legacy/{ => weave}/ops_domain/run_ops.py (95%) rename weave/legacy/{ => weave}/ops_domain/run_queue_ops.py (51%) rename weave/legacy/{ => weave}/ops_domain/run_segment.py (91%) rename weave/legacy/{ => weave}/ops_domain/stream_table_ops.py (68%) rename weave/legacy/{ => weave}/ops_domain/table.py (99%) rename weave/legacy/{ => weave}/ops_domain/trace_tree.py (97%) rename weave/legacy/{ => weave}/ops_domain/user_ops.py (77%) rename weave/legacy/{ => weave}/ops_domain/wandb_domain_gql.py (96%) rename weave/legacy/{ => weave}/ops_domain/wb_domain_types.py (97%) rename weave/legacy/{ => weave}/ops_domain/wb_util.py (87%) rename weave/legacy/{ => weave}/ops_domain/wbgqlquery_op.py (87%) rename weave/legacy/{ => weave}/ops_domain/wbmedia.py (97%) rename weave/legacy/{ => weave}/ops_primitives/__init__.py (92%) rename weave/legacy/{ => weave}/ops_primitives/_dict_utils.py (97%) rename weave/legacy/{ => weave}/ops_primitives/any.py (61%) rename weave/legacy/{ => weave}/ops_primitives/artifacts.py (96%) rename weave/legacy/{ => weave}/ops_primitives/boolean.py (92%) rename weave/legacy/{ => weave}/ops_primitives/csv_.py (97%) rename weave/legacy/{ => weave}/ops_primitives/date.py (98%) rename weave/legacy/{ => weave}/ops_primitives/dict.py (92%) rename weave/legacy/{ => weave}/ops_primitives/file.py (90%) rename weave/legacy/{ => weave}/ops_primitives/file_artifact.py (87%) rename weave/legacy/{ => weave}/ops_primitives/file_local.py (83%) rename weave/legacy/{ => weave}/ops_primitives/geom.py (93%) rename weave/legacy/{ => weave}/ops_primitives/html.py (82%) rename weave/legacy/{ => weave}/ops_primitives/image.py (93%) rename weave/legacy/{ => weave}/ops_primitives/json_.py (90%) rename weave/legacy/{ => weave}/ops_primitives/list_.py (97%) rename weave/legacy/{ => weave}/ops_primitives/list_tag_getters.py (74%) rename weave/legacy/{ => weave}/ops_primitives/markdown.py (82%) rename weave/legacy/{ => weave}/ops_primitives/number.py (98%) rename weave/legacy/{ => weave}/ops_primitives/number_bin.py (81%) rename weave/legacy/{ => weave}/ops_primitives/obj.py (93%) rename weave/legacy/{ => weave}/ops_primitives/op_def.py (68%) rename weave/legacy/{ => weave}/ops_primitives/pandas_.py (97%) rename weave/legacy/{ => weave}/ops_primitives/projection_utils.py (98%) rename weave/legacy/{ => weave}/ops_primitives/random_junk.py (82%) rename weave/legacy/{ => weave}/ops_primitives/server.py (76%) rename weave/legacy/{ => weave}/ops_primitives/set_.py (94%) rename weave/legacy/{ => weave}/ops_primitives/sql.py (97%) rename weave/legacy/{ => weave}/ops_primitives/string.py (98%) rename weave/legacy/{ => weave}/ops_primitives/test_any.py (73%) rename weave/legacy/{ => weave}/ops_primitives/test_dict.py (80%) rename weave/legacy/{ => weave}/ops_primitives/test_file.py (54%) rename weave/legacy/{ => weave}/ops_primitives/test_image.py (89%) rename weave/legacy/{ => weave}/ops_primitives/test_list.py (96%) create mode 100644 weave/legacy/weave/ops_primitives/test_list_range.py rename weave/legacy/{ => weave}/ops_primitives/test_pandas.py (85%) rename weave/legacy/{ => weave}/ops_primitives/test_set.py (89%) rename weave/legacy/{ => weave}/ops_primitives/test_type.py (79%) rename weave/legacy/{ => weave}/ops_primitives/test_typeddict.py (96%) rename weave/legacy/{ => weave}/ops_primitives/timestamp_bin.py (86%) rename weave/legacy/{ => weave}/ops_primitives/type.py (85%) rename weave/legacy/{ => weave}/ops_primitives/weave_api.py (99%) rename weave/legacy/{ => weave}/panel.py (95%) rename weave/legacy/{ => weave}/panel_util.py (80%) create mode 100644 weave/legacy/weave/panels/__init__.py rename weave/legacy/{ => weave}/panels/bank.py (93%) rename weave/legacy/{ => weave}/panels/panel_auto.py (77%) rename weave/legacy/{ => weave}/panels/panel_basic.py (84%) rename weave/legacy/{ => weave}/panels/panel_board.py (92%) rename weave/legacy/{ => weave}/panels/panel_card.py (93%) rename weave/legacy/{ => weave}/panels/panel_color.py (81%) rename weave/legacy/{ => weave}/panels/panel_daterange.py (93%) rename weave/legacy/{ => weave}/panels/panel_domain.py (90%) rename weave/legacy/{ => weave}/panels/panel_dropdown.py (88%) rename weave/legacy/{ => weave}/panels/panel_each.py (82%) rename weave/legacy/{ => weave}/panels/panel_each_column.py (84%) rename weave/legacy/{ => weave}/panels/panel_expression.py (68%) rename weave/legacy/{ => weave}/panels/panel_facet.py (86%) rename weave/legacy/{ => weave}/panels/panel_facet_tabs.py (73%) rename weave/legacy/{ => weave}/panels/panel_filter_editor.py (94%) rename weave/legacy/{ => weave}/panels/panel_function_editor.py (91%) rename weave/legacy/{ => weave}/panels/panel_group.py (97%) rename weave/legacy/{ => weave}/panels/panel_group_panel_info.py (100%) rename weave/legacy/{ => weave}/panels/panel_grouping_editor.py (94%) rename weave/legacy/{ => weave}/panels/panel_histogram.py (86%) rename weave/legacy/{ => weave}/panels/panel_html.py (90%) rename weave/legacy/{ => weave}/panels/panel_labeled_item.py (93%) rename weave/legacy/{ => weave}/panels/panel_legacy.py (98%) rename weave/legacy/{ => weave}/panels/panel_markdown.py (90%) rename weave/legacy/{ => weave}/panels/panel_object_picker.py (95%) rename weave/legacy/{ => weave}/panels/panel_plot.py (98%) rename weave/legacy/{ => weave}/panels/panel_query.py (89%) rename weave/legacy/{ => weave}/panels/panel_sections.py (73%) rename weave/legacy/{ => weave}/panels/panel_select.py (85%) rename weave/legacy/{ => weave}/panels/panel_sidebarcontainer.py (89%) rename weave/legacy/{ => weave}/panels/panel_slider.py (85%) rename weave/legacy/{ => weave}/panels/panel_string.py (94%) rename weave/legacy/{ => weave}/panels/panel_string_editor.py (82%) rename weave/legacy/{ => weave}/panels/panel_table.py (93%) rename weave/legacy/{ => weave}/panels/panel_trace.py (94%) rename weave/legacy/{ => weave}/panels/panel_trace_span.py (85%) rename weave/legacy/{ => weave}/panels/panel_weavelink.py (89%) rename weave/legacy/{ => weave}/panels/table_state.py (97%) rename weave/legacy/{ => weave}/panels_py/__init__.py (64%) rename weave/legacy/{ => weave}/panels_py/generator_templates.py (97%) rename weave/legacy/{ => weave}/panels_py/instructions/panel_autoboard.md (100%) rename weave/legacy/{ => weave}/panels_py/instructions/panel_llm_monitor.md (100%) rename weave/legacy/{ => weave}/panels_py/instructions/panel_trace_monitor.md (93%) rename weave/legacy/{ => weave}/panels_py/panel_autoboard.py (84%) rename weave/legacy/{ => weave}/panels_py/panel_eval.py (67%) rename weave/legacy/{ => weave}/panels_py/panel_example_custom_board.py (76%) rename weave/legacy/{ => weave}/panels_py/panel_llm_monitor.py (85%) rename weave/legacy/{ => weave}/panels_py/panel_observability.py (81%) rename weave/legacy/{ => weave}/panels_py/panel_seedboard.py (60%) rename weave/legacy/{ => weave}/panels_py/panel_trace_monitor.py (81%) rename weave/legacy/{ => weave}/parallelism.py (99%) rename weave/legacy/{ => weave}/partial_object.py (96%) rename weave/legacy/{ => weave}/path_util.py (90%) rename weave/legacy/{ => weave}/propagate_gql_keys.py (93%) rename weave/legacy/{ => weave}/pyfunc_type_util.py (98%) rename weave/legacy/{ => weave}/ref_base.py (95%) rename weave/legacy/{ => weave}/registry_mem.py (95%) rename weave/legacy/{ => weave}/run.py (100%) rename weave/legacy/{ => weave}/run_streamtable_span.py (93%) rename weave/legacy/{ => weave}/runfiles_wandb.py (97%) rename weave/legacy/{ => weave}/runs.py (92%) rename weave/legacy/{ => weave}/serialize.py (97%) rename weave/legacy/{ => weave}/server.py (97%) rename weave/legacy/{ => weave}/server_error_handling.py (98%) rename weave/legacy/{ => weave}/server_interface.py (100%) rename weave/legacy/{ => weave}/show.py (95%) rename weave/legacy/{ => weave}/signal_handlers.py (99%) rename weave/legacy/{ => weave}/stitch.py (98%) rename weave/legacy/{ => weave}/storage.py (98%) rename weave/legacy/{ => weave}/stream_data_interfaces.py (100%) rename weave/legacy/{ => weave}/timestamp.py (100%) create mode 100644 weave/legacy/weave/trace_legacy.py rename weave/legacy/{ => weave}/trace_local.py (95%) rename weave/legacy/{ => weave}/types_numpy.py (96%) rename weave/legacy/{ => weave}/uris.py (98%) rename weave/legacy/{ => weave}/urls.py (96%) rename weave/legacy/{ => weave}/usage_analytics.py (95%) rename weave/legacy/{ => weave}/util.py (98%) rename weave/legacy/{ => weave}/val_const.py (90%) rename weave/legacy/{ => weave}/value_or_error.py (100%) rename weave/legacy/{ => weave}/wandb_api.py (98%) rename weave/legacy/{ => weave}/wandb_client_api.py (98%) rename weave/legacy/{ => weave}/wandb_file_manager.py (99%) rename weave/legacy/{ => weave}/wandb_interface/project_creator.py (100%) rename weave/legacy/{ => weave}/wandb_interface/wandb_artifact_pusher.py (95%) rename weave/legacy/{ => weave}/wandb_interface/wandb_lite_run.py (98%) rename weave/legacy/{ => weave}/wandb_interface/wandb_stream_table.py (97%) rename weave/legacy/{ => weave}/wandb_util.py (98%) rename weave/legacy/{ => weave}/weave_http.py (98%) rename weave/legacy/{ => weave}/weave_inspector.py (99%) rename weave/legacy/{ => weave}/weave_internal.py (93%) rename weave/legacy/{ => weave}/weave_pydantic.py (95%) rename weave/legacy/{ => weave}/weave_types.py (99%) rename weave/legacy/{ => weave}/weavejs_fixes.py (99%) rename weave/legacy/{ => weave}/weavify.py (93%) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4ebd119ef67..7023d1a87e5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -125,15 +125,42 @@ jobs: DD_SERVICE: weave-python DD_ENV: ci WEAVE_SENTRY_ENV: ci + CI: 1 + WB_SERVER_HOST: http://wandbservice + WF_CLICKHOUSE_HOST: weave_clickhouse + WEAVE_SERVER_DISABLE_ECOSYSTEM: 1 # This runner specifically runs the tests that use the `client` fixture (those that support clickhouse client tests) # However, we skip tests marked with `skip_clickhouse_client`. These should be considered TODOs and an exception - run: CI=1 WB_SERVER_HOST=http://wandbservice WF_CLICKHOUSE_HOST=weave_clickhouse WEAVE_SERVER_DISABLE_ECOSYSTEM=1 source /root/venv/bin/activate && cd weave && pytest -m "weave_client and not skip_clickhouse_client" --weave-server=clickhouse --job-num=${{ matrix.job_num }} --timeout=90 ./flow ./integrations ./legacy/tests ./tests ./legacy/ops_arrow ./legacy/ecosystem ./trace_server ./trace --ddtrace --durations=5 + run: | + source /root/venv/bin/activate && \ + cd weave && \ + pytest -m "weave_client and not skip_clickhouse_client" \ + --weave-server=clickhouse \ + --job-num=${{ matrix.job_num }} \ + --timeout=90 \ + --ddtrace \ + --durations=5 \ + ./integrations ./legacy ./trace_server ./trace ./tests - name: Run Python Unit Tests env: DD_SERVICE: weave-python DD_ENV: ci WEAVE_SENTRY_ENV: ci - run: CI=1 WB_SERVER_HOST=http://wandbservice WF_CLICKHOUSE_HOST=weave_clickhouse WEAVE_SERVER_DISABLE_ECOSYSTEM=1 source /root/venv/bin/activate && pip install 'dspy-ai>=0.1.5' && pip install pydantic -U && cd weave && pytest --job-num=${{ matrix.job_num }} --timeout=90 ./flow ./integrations ./legacy/tests ./tests ./legacy/ops_arrow ./legacy/ecosystem ./trace_server ./trace --ddtrace --durations=5 + CI: 1 + WB_SERVER_HOST: http://wandbservice + WF_CLICKHOUSE_HOST: weave_clickhouse + WEAVE_SERVER_DISABLE_ECOSYSTEM: 1 + run: | + source /root/venv/bin/activate && \ + pip install 'dspy-ai>=0.1.5' && \ + pip install pydantic -U && \ + cd weave && \ + pytest \ + --job-num=${{ matrix.job_num }} \ + --timeout=90 \ + --ddtrace \ + --durations=5 \ + ./integrations ./legacy ./trace_server ./trace ./tests # nbmake: # name: Run notebooks with nbmake diff --git a/Eval Board Syn.ipynb b/Eval Board Syn.ipynb index 50aa1e926c0..6a7ea54e9e6 100644 --- a/Eval Board Syn.ipynb +++ b/Eval Board Syn.ipynb @@ -1,318 +1,318 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "5214b543", - "metadata": {}, - "source": [ - "- Load two eval_results\n", - "\n", - "EvalResult\n", - "- example, label, result, item_summary" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "5214b543", + "metadata": {}, + "source": [ + "- Load two eval_results\n", + "\n", + "EvalResult\n", + "- example, label, result, item_summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "314bb6b4", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import string\n", + "import time\n", + "\n", + "import weave\n", + "\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.panels import panel_board" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "550daef6", + "metadata": {}, + "outputs": [], + "source": [ + "def rand_string_n(n: int) -> str:\n", + " return \"\".join(\n", + " random.choice(string.ascii_uppercase + string.digits) for _ in range(n)\n", + " )\n", + "\n", + "\n", + "dataset_raw = [\n", + " {\n", + " \"id\": str(i),\n", + " \"example\": rand_string_n(10),\n", + " \"label\": random.choice(string.ascii_uppercase),\n", + " }\n", + " for i in range(50)\n", + "]\n", + "dataset = weave.save(dataset_raw, \"dataset\")\n", + "# dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0d930d8", + "metadata": {}, + "outputs": [], + "source": [ + "def predict(dataset_row, config):\n", + " if random.random() < config[\"correct_chance\"]:\n", + " return dataset_row[\"label\"]\n", + " return random.choice(string.ascii_uppercase)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb86b95c", + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate(dataset, predict_config):\n", + " eval_result = []\n", + " correct_count = 0\n", + " count = 0\n", + " for dataset_row in dataset:\n", + " start_time = time.time()\n", + " result = predict(dataset_row, predict_config)\n", + " latency = time.time() - start_time\n", + " latency = random.gauss(\n", + " predict_config[\"latency_mu\"], predict_config[\"latency_sigma\"]\n", + " )\n", + " correct = dataset_row[\"label\"] == result\n", + " if correct:\n", + " correct_count += 1\n", + " count += 1\n", + " eval_result.append(\n", + " {\n", + " \"dataset_id\": dataset_row[\"id\"],\n", + " \"result\": result,\n", + " \"summary\": {\"latency\": latency, \"correct\": correct},\n", + " }\n", + " )\n", + " return {\n", + " \"config\": predict_config,\n", + " \"eval_table\": eval_result,\n", + " \"summary\": {\"accuracy\": correct_count / len(dataset)},\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05d16a5e", + "metadata": {}, + "outputs": [], + "source": [ + "eval_result_raw0 = evaluate(\n", + " dataset_raw, {\"correct_chance\": 0.5, \"latency_mu\": 0.3, \"latency_sigma\": 0.1}\n", + ")\n", + "eval_result_raw1 = evaluate(\n", + " dataset_raw, {\"correct_chance\": 0.5, \"latency_mu\": 0.4, \"latency_sigma\": 0.2}\n", + ")\n", + "eval_result0 = weave.save(eval_result_raw0, \"eval_result0\")\n", + "eval_result1 = weave.save(eval_result_raw1, \"eval_result1\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8065ad6", + "metadata": {}, + "outputs": [], + "source": [ + "varbar = panel_board.varbar()\n", + "\n", + "dataset_var = varbar.add(\"dataset\", dataset)\n", + "eval_result0_var = varbar.add(\"eval_result0\", eval_result0)\n", + "eval_result1_var = varbar.add(\"eval_result1\", eval_result1)\n", + "\n", + "summary = varbar.add(\n", + " \"summary\",\n", + " weave.legacy.weave.ops.make_list(\n", + " a=weave.legacy.weave.ops.TypedDict.merge(\n", + " weave.legacy.weave.ops.dict_(name=\"res0\"), eval_result0_var[\"summary\"]\n", + " ),\n", + " b=weave.legacy.weave.ops.TypedDict.merge(\n", + " weave.legacy.weave.ops.dict_(name=\"res1\"), eval_result1_var[\"summary\"]\n", + " ),\n", + " ),\n", + ")\n", + "\n", + "weave.legacy.weave.ops.make_list(\n", + " a=eval_result0_var[\"eval_table\"], b=eval_result0_var[\"eval_table\"]\n", + ")\n", + "\n", + "concatted_evals = varbar.add(\n", + " \"concatted_evals\",\n", + " weave.legacy.weave.ops.List.concat(\n", + " weave.legacy.weave.ops.make_list(\n", + " a=eval_result0_var[\"eval_table\"].map(\n", + " lambda row: weave.legacy.weave.ops.TypedDict.merge(\n", + " weave.legacy.weave.ops.dict_(name=\"res0\"), row\n", + " )\n", + " ),\n", + " b=eval_result1_var[\"eval_table\"].map(\n", + " lambda row: weave.legacy.weave.ops.TypedDict.merge(\n", + " weave.legacy.weave.ops.dict_(name=\"res1\"), row\n", + " )\n", + " ),\n", + " )\n", + " ),\n", + ")\n", + "\n", + "# join evals together first\n", + "joined_evals = varbar.add(\n", + " \"joined_evals\",\n", + " weave.legacy.weave.ops.join_all(\n", + " weave.legacy.weave.ops.make_list(\n", + " a=eval_result0_var[\"eval_table\"], b=eval_result1_var[\"eval_table\"]\n", + " ),\n", + " lambda row: row[\"dataset_id\"],\n", + " False,\n", + " ),\n", + ")\n", + "\n", + "# then join dataset to evals\n", + "dataset_evals = varbar.add(\n", + " \"dataset_evals\",\n", + " weave.legacy.weave.ops.join_2(\n", + " dataset_var,\n", + " joined_evals,\n", + " lambda row: row[\"id\"],\n", + " lambda row: row[\"dataset_id\"][0],\n", + " \"dataset\",\n", + " \"evals\",\n", + " False,\n", + " False,\n", + " ),\n", + ")\n", + "\n", + "\n", + "main = weave.legacy.weave.panels.Group(\n", + " layoutMode=\"grid\",\n", + " showExpressions=True,\n", + " enableAddPanel=True,\n", + ")\n", + "\n", + "#### Run/config info TODO\n", + "\n", + "#### Summary info\n", + "\n", + "main.add(\n", + " \"accuracy\",\n", + " weave.legacy.weave.panels.Plot(\n", + " summary,\n", + " x=lambda row: row[\"accuracy\"],\n", + " y=lambda row: row[\"name\"],\n", + " color=lambda row: row[\"name\"],\n", + " ),\n", + " layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=0, w=12, h=4),\n", + ")\n", + "\n", + "\n", + "main.add(\n", + " \"latency\",\n", + " weave.legacy.weave.panels.Plot(\n", + " concatted_evals,\n", + " x=lambda row: row[\"summary\"][\"latency\"],\n", + " y=lambda row: row[\"name\"],\n", + " color=lambda row: row[\"name\"],\n", + " mark=\"boxplot\",\n", + " ),\n", + " layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=0, w=12, h=4),\n", + ")\n", + "\n", + "# ct = main.add('concat_t', concatted_evals, layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=24, h=12))\n", + "# main.add('dataset_table', dataset)\n", + "# main.add('joined_evals', joined_evals)\n", + "# main.add('dataset_evals', dataset_evals, layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=24, h=6))\n", + "\n", + "##### Example details\n", + "\n", + "# more ideas: show examples that all got wrong, or that are confusing\n", + "\n", + "faceted_view = weave.legacy.weave.panels.Facet(\n", + " dataset_evals,\n", + " x=lambda row: row[\"evals.summary\"][0][\"correct\"],\n", + " y=lambda row: row[\"evals.summary\"][1][\"correct\"],\n", + " select=lambda row: row.count(),\n", + ")\n", + "\n", + "faceted = main.add(\n", + " \"faceted\",\n", + " faceted_view,\n", + " layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=12, h=6),\n", + ")\n", + "\n", + "main.add(\n", + " \"example_latencies\",\n", + " weave.legacy.weave.panels.Plot(\n", + " dataset_evals,\n", + " x=lambda row: row[\"evals.summary\"][\"latency\"][0],\n", + " y=lambda row: row[\"evals.summary\"][\"latency\"][1],\n", + " ),\n", + " layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=4, w=12, h=6),\n", + ")\n", + "\n", + "faceted_sel = weave.legacy.weave.panels.Table(faceted.selected())\n", + "faceted_sel.config.rowSize = 2\n", + "faceted_sel.add_column(lambda row: row[\"dataset.id\"], \"id\")\n", + "faceted_sel.add_column(lambda row: row[\"dataset.example\"], \"example\")\n", + "faceted_sel.add_column(lambda row: row[\"dataset.label\"], \"label\")\n", + "faceted_sel.add_column(\n", + " lambda row: weave.legacy.weave.ops.dict_(\n", + " res0=row[\"evals.result\"][0], res1=row[\"evals.result\"][1]\n", + " ),\n", + " \"result\",\n", + ")\n", + "faceted_sel.add_column(\n", + " lambda row: weave.legacy.weave.ops.dict_(\n", + " res0=row[\"evals.summary\"][0][\"correct\"], res1=row[\"evals.summary\"][1][\"correct\"]\n", + " ),\n", + " \"correct\",\n", + ")\n", + "faceted_sel.add_column(\n", + " lambda row: weave.legacy.weave.ops.dict_(\n", + " res0=row[\"evals.summary\"][0][\"latency\"], res1=row[\"evals.summary\"][1][\"latency\"]\n", + " ),\n", + " \"latency\",\n", + ")\n", + "\n", + "main.add(\n", + " \"faceted_sel\",\n", + " faceted_sel,\n", + " layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=10, w=24, h=12),\n", + ")\n", + "\n", + "weave.legacy.weave.panels.Board(vars=varbar, panels=main)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "314bb6b4", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import string\n", - "import time\n", - "\n", - "import weave\n", - "\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.panels import panel_board" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "550daef6", - "metadata": {}, - "outputs": [], - "source": [ - "def rand_string_n(n: int) -> str:\n", - " return \"\".join(\n", - " random.choice(string.ascii_uppercase + string.digits) for _ in range(n)\n", - " )\n", - "\n", - "\n", - "dataset_raw = [\n", - " {\n", - " \"id\": str(i),\n", - " \"example\": rand_string_n(10),\n", - " \"label\": random.choice(string.ascii_uppercase),\n", - " }\n", - " for i in range(50)\n", - "]\n", - "dataset = weave.save(dataset_raw, \"dataset\")\n", - "# dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0d930d8", - "metadata": {}, - "outputs": [], - "source": [ - "def predict(dataset_row, config):\n", - " if random.random() < config[\"correct_chance\"]:\n", - " return dataset_row[\"label\"]\n", - " return random.choice(string.ascii_uppercase)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb86b95c", - "metadata": {}, - "outputs": [], - "source": [ - "def evaluate(dataset, predict_config):\n", - " eval_result = []\n", - " correct_count = 0\n", - " count = 0\n", - " for dataset_row in dataset:\n", - " start_time = time.time()\n", - " result = predict(dataset_row, predict_config)\n", - " latency = time.time() - start_time\n", - " latency = random.gauss(\n", - " predict_config[\"latency_mu\"], predict_config[\"latency_sigma\"]\n", - " )\n", - " correct = dataset_row[\"label\"] == result\n", - " if correct:\n", - " correct_count += 1\n", - " count += 1\n", - " eval_result.append(\n", - " {\n", - " \"dataset_id\": dataset_row[\"id\"],\n", - " \"result\": result,\n", - " \"summary\": {\"latency\": latency, \"correct\": correct},\n", - " }\n", - " )\n", - " return {\n", - " \"config\": predict_config,\n", - " \"eval_table\": eval_result,\n", - " \"summary\": {\"accuracy\": correct_count / len(dataset)},\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05d16a5e", - "metadata": {}, - "outputs": [], - "source": [ - "eval_result_raw0 = evaluate(\n", - " dataset_raw, {\"correct_chance\": 0.5, \"latency_mu\": 0.3, \"latency_sigma\": 0.1}\n", - ")\n", - "eval_result_raw1 = evaluate(\n", - " dataset_raw, {\"correct_chance\": 0.5, \"latency_mu\": 0.4, \"latency_sigma\": 0.2}\n", - ")\n", - "eval_result0 = weave.save(eval_result_raw0, \"eval_result0\")\n", - "eval_result1 = weave.save(eval_result_raw1, \"eval_result1\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e8065ad6", - "metadata": {}, - "outputs": [], - "source": [ - "varbar = panel_board.varbar()\n", - "\n", - "dataset_var = varbar.add(\"dataset\", dataset)\n", - "eval_result0_var = varbar.add(\"eval_result0\", eval_result0)\n", - "eval_result1_var = varbar.add(\"eval_result1\", eval_result1)\n", - "\n", - "summary = varbar.add(\n", - " \"summary\",\n", - " weave.legacy.ops.make_list(\n", - " a=weave.legacy.ops.TypedDict.merge(\n", - " weave.legacy.ops.dict_(name=\"res0\"), eval_result0_var[\"summary\"]\n", - " ),\n", - " b=weave.legacy.ops.TypedDict.merge(\n", - " weave.legacy.ops.dict_(name=\"res1\"), eval_result1_var[\"summary\"]\n", - " ),\n", - " ),\n", - ")\n", - "\n", - "weave.legacy.ops.make_list(\n", - " a=eval_result0_var[\"eval_table\"], b=eval_result0_var[\"eval_table\"]\n", - ")\n", - "\n", - "concatted_evals = varbar.add(\n", - " \"concatted_evals\",\n", - " weave.legacy.ops.List.concat(\n", - " weave.legacy.ops.make_list(\n", - " a=eval_result0_var[\"eval_table\"].map(\n", - " lambda row: weave.legacy.ops.TypedDict.merge(\n", - " weave.legacy.ops.dict_(name=\"res0\"), row\n", - " )\n", - " ),\n", - " b=eval_result1_var[\"eval_table\"].map(\n", - " lambda row: weave.legacy.ops.TypedDict.merge(\n", - " weave.legacy.ops.dict_(name=\"res1\"), row\n", - " )\n", - " ),\n", - " )\n", - " ),\n", - ")\n", - "\n", - "# join evals together first\n", - "joined_evals = varbar.add(\n", - " \"joined_evals\",\n", - " weave.legacy.ops.join_all(\n", - " weave.legacy.ops.make_list(\n", - " a=eval_result0_var[\"eval_table\"], b=eval_result1_var[\"eval_table\"]\n", - " ),\n", - " lambda row: row[\"dataset_id\"],\n", - " False,\n", - " ),\n", - ")\n", - "\n", - "# then join dataset to evals\n", - "dataset_evals = varbar.add(\n", - " \"dataset_evals\",\n", - " weave.legacy.ops.join_2(\n", - " dataset_var,\n", - " joined_evals,\n", - " lambda row: row[\"id\"],\n", - " lambda row: row[\"dataset_id\"][0],\n", - " \"dataset\",\n", - " \"evals\",\n", - " False,\n", - " False,\n", - " ),\n", - ")\n", - "\n", - "\n", - "main = weave.legacy.panels.Group(\n", - " layoutMode=\"grid\",\n", - " showExpressions=True,\n", - " enableAddPanel=True,\n", - ")\n", - "\n", - "#### Run/config info TODO\n", - "\n", - "#### Summary info\n", - "\n", - "main.add(\n", - " \"accuracy\",\n", - " weave.legacy.panels.Plot(\n", - " summary,\n", - " x=lambda row: row[\"accuracy\"],\n", - " y=lambda row: row[\"name\"],\n", - " color=lambda row: row[\"name\"],\n", - " ),\n", - " layout=weave.legacy.panels.GroupPanelLayout(x=0, y=0, w=12, h=4),\n", - ")\n", - "\n", - "\n", - "main.add(\n", - " \"latency\",\n", - " weave.legacy.panels.Plot(\n", - " concatted_evals,\n", - " x=lambda row: row[\"summary\"][\"latency\"],\n", - " y=lambda row: row[\"name\"],\n", - " color=lambda row: row[\"name\"],\n", - " mark=\"boxplot\",\n", - " ),\n", - " layout=weave.legacy.panels.GroupPanelLayout(x=12, y=0, w=12, h=4),\n", - ")\n", - "\n", - "# ct = main.add('concat_t', concatted_evals, layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=24, h=12))\n", - "# main.add('dataset_table', dataset)\n", - "# main.add('joined_evals', joined_evals)\n", - "# main.add('dataset_evals', dataset_evals, layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=24, h=6))\n", - "\n", - "##### Example details\n", - "\n", - "# more ideas: show examples that all got wrong, or that are confusing\n", - "\n", - "faceted_view = weave.legacy.panels.Facet(\n", - " dataset_evals,\n", - " x=lambda row: row[\"evals.summary\"][0][\"correct\"],\n", - " y=lambda row: row[\"evals.summary\"][1][\"correct\"],\n", - " select=lambda row: row.count(),\n", - ")\n", - "\n", - "faceted = main.add(\n", - " \"faceted\",\n", - " faceted_view,\n", - " layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=12, h=6),\n", - ")\n", - "\n", - "main.add(\n", - " \"example_latencies\",\n", - " weave.legacy.panels.Plot(\n", - " dataset_evals,\n", - " x=lambda row: row[\"evals.summary\"][\"latency\"][0],\n", - " y=lambda row: row[\"evals.summary\"][\"latency\"][1],\n", - " ),\n", - " layout=weave.legacy.panels.GroupPanelLayout(x=12, y=4, w=12, h=6),\n", - ")\n", - "\n", - "faceted_sel = weave.legacy.panels.Table(faceted.selected())\n", - "faceted_sel.config.rowSize = 2\n", - "faceted_sel.add_column(lambda row: row[\"dataset.id\"], \"id\")\n", - "faceted_sel.add_column(lambda row: row[\"dataset.example\"], \"example\")\n", - "faceted_sel.add_column(lambda row: row[\"dataset.label\"], \"label\")\n", - "faceted_sel.add_column(\n", - " lambda row: weave.legacy.ops.dict_(\n", - " res0=row[\"evals.result\"][0], res1=row[\"evals.result\"][1]\n", - " ),\n", - " \"result\",\n", - ")\n", - "faceted_sel.add_column(\n", - " lambda row: weave.legacy.ops.dict_(\n", - " res0=row[\"evals.summary\"][0][\"correct\"], res1=row[\"evals.summary\"][1][\"correct\"]\n", - " ),\n", - " \"correct\",\n", - ")\n", - "faceted_sel.add_column(\n", - " lambda row: weave.legacy.ops.dict_(\n", - " res0=row[\"evals.summary\"][0][\"latency\"], res1=row[\"evals.summary\"][1][\"latency\"]\n", - " ),\n", - " \"latency\",\n", - ")\n", - "\n", - "main.add(\n", - " \"faceted_sel\",\n", - " faceted_sel,\n", - " layout=weave.legacy.panels.GroupPanelLayout(x=0, y=10, w=24, h=12),\n", - ")\n", - "\n", - "weave.legacy.panels.Board(vars=varbar, panels=main)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/Eval Board.ipynb b/Eval Board.ipynb index be2baf592eb..1cf4b810b8a 100644 --- a/Eval Board.ipynb +++ b/Eval Board.ipynb @@ -1,161 +1,163 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 61, - "id": "314bb6b4", - "metadata": {}, - "outputs": [], - "source": [ - "import typing\n", - "\n", - "import weave\n", - "from weave.legacy import weave_internal\n", - "\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy import ops_domain\n", - "from weave.legacy.panels import panel_board" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "id": "550daef6", - "metadata": {}, - "outputs": [], - "source": [ - "@weave.type()\n", - "class Dataset:\n", - " rows: list[typing.Any]" - ] - }, + "cells": [ + { + "cell_type": "code", + "execution_count": 61, + "id": "314bb6b4", + "metadata": {}, + "outputs": [], + "source": [ + "import typing\n", + "\n", + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave import ops_domain\n", + "from weave.legacy.weave.panels import panel_board" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "550daef6", + "metadata": {}, + "outputs": [], + "source": [ + "@weave.type()\n", + "class Dataset:\n", + " rows: list[typing.Any]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "e8065ad6", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 64, - "id": "e8065ad6", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "data": { + "text/html": [ + "\n", + " \n", + " " ], - "source": [ - "varbar = panel_board.varbar()\n", - "\n", - "entity_name_val = varbar.add(\"entity_name_val\", \"shawn\", hidden=True)\n", - "entity = ops_domain.entity(entity_name_val)\n", - "entity_name = varbar.add(\n", - " \"entity_name\",\n", - " weave.legacy.panels.Dropdown(\n", - " entity_name_val, choices=ops_domain.viewer().entities().name()\n", - " ),\n", - ")\n", - "\n", - "project_name_val = varbar.add(\"project_name_val\", \"weave-flow1\", hidden=True)\n", - "project = ops_domain.project(entity_name_val, project_name_val)\n", - "project_name = varbar.add(\n", - " \"project_name\",\n", - " weave.legacy.panels.Dropdown(project_name_val, choices=entity.projects().name()),\n", - ")\n", - "\n", - "dataset_name_val = varbar.add(\"dataset_name_val\", \"my_dataset1\", hidden=True)\n", - "dataset = varbar.add(\n", - " \"dataset\",\n", - " weave.legacy.ops.get(\n", - " weave_internal.const(\"wandb-artifact:///\")\n", - " + entity_name_val\n", - " + \"/\"\n", - " + project_name_val\n", - " + \"/\"\n", - " + dataset_name_val\n", - " + \":latest/obj\"\n", - " ),\n", - " hidden=True,\n", - ")\n", - "dataset_ref = varbar.add(\n", - " \"dataset_ref\",\n", - " weave.legacy.ops.ref(\n", - " weave_internal.const(\"wandb-artifact:///\")\n", - " + entity_name_val\n", - " + \"/\"\n", - " + project_name_val\n", - " + \"/\"\n", - " + dataset_name_val\n", - " + \":latest/obj\"\n", - " ),\n", - ")\n", - "dataset_name = varbar.add(\n", - " \"dataset_name\",\n", - " weave.legacy.panels.Dropdown(\n", - " dataset_name_val, choices=project.artifactType(\"Dataset\").artifacts().name()\n", - " ),\n", - ")\n", - "\n", - "# Now here I want to pick model A and model B\n", - "# I need to a way to get to artifact from an object, like get_ref.artifact ?\n", - "# like dataset_ref.artifact().usedBy().filter(lambda row: row.jobName() == 'eval')\n", - "# then from there find consuming runs of a given job_type (\"eval\")\n", - "# let the user pick two of those\n", - "\n", - "# want a really nice data grid of the table metric comparsion per row\n", - "\n", - "main = weave.legacy.panels.Group(\n", - " layoutMode=\"grid\",\n", - " showExpressions=True,\n", - " enableAddPanel=True,\n", - ")\n", - "\n", - "main.add(\"ename\", entity_name_val)\n", - "main.add(\"pname\", project_name_val)\n", - "main.add(\"dsrows\", weave.legacy.ops.obj_getattr(dataset, \"rows\"))\n", - "# main.add(\"artifact\", dataset_ref.artifact())\n", - "\n", - "# I Need a programmable Table. One that's columns are defined by Nodes\n", - "# Same for panels, I want to be able to generate a bunch of panels\n", - "# Can I do this with Weaveify?\n", - "# Like I can pass a function in in the Board that returns the panel I want maybe?\n", - "\n", - "weave.legacy.panels.Board(vars=varbar, panels=main)" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } + ], + "source": [ + "varbar = panel_board.varbar()\n", + "\n", + "entity_name_val = varbar.add(\"entity_name_val\", \"shawn\", hidden=True)\n", + "entity = ops_domain.entity(entity_name_val)\n", + "entity_name = varbar.add(\n", + " \"entity_name\",\n", + " weave.legacy.weave.panels.Dropdown(\n", + " entity_name_val, choices=ops_domain.viewer().entities().name()\n", + " ),\n", + ")\n", + "\n", + "project_name_val = varbar.add(\"project_name_val\", \"weave-flow1\", hidden=True)\n", + "project = ops_domain.project(entity_name_val, project_name_val)\n", + "project_name = varbar.add(\n", + " \"project_name\",\n", + " weave.legacy.weave.panels.Dropdown(\n", + " project_name_val, choices=entity.projects().name()\n", + " ),\n", + ")\n", + "\n", + "dataset_name_val = varbar.add(\"dataset_name_val\", \"my_dataset1\", hidden=True)\n", + "dataset = varbar.add(\n", + " \"dataset\",\n", + " weave.legacy.weave.ops.get(\n", + " weave_internal.const(\"wandb-artifact:///\")\n", + " + entity_name_val\n", + " + \"/\"\n", + " + project_name_val\n", + " + \"/\"\n", + " + dataset_name_val\n", + " + \":latest/obj\"\n", + " ),\n", + " hidden=True,\n", + ")\n", + "dataset_ref = varbar.add(\n", + " \"dataset_ref\",\n", + " weave.legacy.weave.ops.ref(\n", + " weave_internal.const(\"wandb-artifact:///\")\n", + " + entity_name_val\n", + " + \"/\"\n", + " + project_name_val\n", + " + \"/\"\n", + " + dataset_name_val\n", + " + \":latest/obj\"\n", + " ),\n", + ")\n", + "dataset_name = varbar.add(\n", + " \"dataset_name\",\n", + " weave.legacy.weave.panels.Dropdown(\n", + " dataset_name_val, choices=project.artifactType(\"Dataset\").artifacts().name()\n", + " ),\n", + ")\n", + "\n", + "# Now here I want to pick model A and model B\n", + "# I need to a way to get to artifact from an object, like get_ref.artifact ?\n", + "# like dataset_ref.artifact().usedBy().filter(lambda row: row.jobName() == 'eval')\n", + "# then from there find consuming runs of a given job_type (\"eval\")\n", + "# let the user pick two of those\n", + "\n", + "# want a really nice data grid of the table metric comparsion per row\n", + "\n", + "main = weave.legacy.weave.panels.Group(\n", + " layoutMode=\"grid\",\n", + " showExpressions=True,\n", + " enableAddPanel=True,\n", + ")\n", + "\n", + "main.add(\"ename\", entity_name_val)\n", + "main.add(\"pname\", project_name_val)\n", + "main.add(\"dsrows\", weave.legacy.weave.ops.obj_getattr(dataset, \"rows\"))\n", + "# main.add(\"artifact\", dataset_ref.artifact())\n", + "\n", + "# I Need a programmable Table. One that's columns are defined by Nodes\n", + "# Same for panels, I want to be able to generate a bunch of panels\n", + "# Can I do this with Weaveify?\n", + "# Like I can pass a function in in the Board that returns the panel I want maybe?\n", + "\n", + "weave.legacy.weave.panels.Board(vars=varbar, panels=main)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 5 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/Text Extraction Eval.ipynb b/Text Extraction Eval.ipynb index 64daefac32b..a2d2705eb97 100644 --- a/Text Extraction Eval.ipynb +++ b/Text Extraction Eval.ipynb @@ -1,445 +1,445 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "04bba52c", - "metadata": {}, - "outputs": [], - "source": [ - "import glob\n", - "import os\n", - "import random\n", - "import time\n", - "import typing\n", - "\n", - "import weave\n", - "\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "04bba52c", + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import os\n", + "import random\n", + "import time\n", + "import typing\n", + "\n", + "import weave\n", + "\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8172d67c", + "metadata": {}, + "outputs": [], + "source": [ + "raw_labels = {\n", + " \"Articles_of_Incorporation_Real_Example_3_txt\": {\n", + " \"name\": \"HealthFirst Solutions LLC\",\n", + " \"shares\": 500000,\n", + " },\n", + " \"Articles_of_Incorporation_Real_Example_2_txt\": {\n", + " \"name\": \"GreenLeaf LLC\",\n", + " \"shares\": None,\n", + " \"directors\": [\"Sarah Miller\", \"Daniel Lee\"],\n", + " },\n", + " \"Articles_of_Incorporation_Real_Example_1_txt\": {\n", + " \"name\": \"TechBoost Corp\",\n", + " \"shares\": 1000000,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_10_txt\": {\n", + " \"name\": \"Brown, Fernandez and Smith\",\n", + " \"shares\": 41141,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_9_txt\": {\n", + " \"name\": \"Ruiz-Goodman\",\n", + " \"shares\": 31783,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_8_txt\": {\n", + " \"name\": \"Gibson, Hunt and Davidson\",\n", + " \"shares\": 96403,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_7_txt\": {\n", + " \"name\": \"Boyd-Browning\",\n", + " \"shares\": 41300,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_6_txt\": {\n", + " \"name\": \"Newton, Moreno and Yang\",\n", + " \"shares\": 73981,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_5_txt\": {\n", + " \"name\": \"Matthews and Sons\",\n", + " \"shares\": 98608,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_4_txt\": {\n", + " \"name\": \"Moore LLC\",\n", + " \"shares\": 5732,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_3_txt\": {\n", + " \"name\": \"Mullen Inc\",\n", + " \"shares\": 76197,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_2_txt\": {\n", + " \"name\": \"Ellis and Sons\",\n", + " \"shares\": 54183,\n", + " },\n", + " \"Highly_Varied_Article_of_Incorporation_1_txt\": {\n", + " \"name\": \"French, Wyatt and Coleman\",\n", + " \"shares\": 78821,\n", + " },\n", + "}\n", + "\n", + "\n", + "def read_dataset(root):\n", + " dataset_rows = []\n", + " for p in glob.glob(os.path.join(root, \"*.txt\")):\n", + " # Have to do replace here because of weave '.' access issues\n", + " example_id = os.path.basename(p).replace(\".\", \"_\")\n", + " label = raw_labels.get(example_id)\n", + " if label:\n", + " dataset_rows.append(\n", + " {\"id\": example_id, \"example\": open(p).read(), \"label\": label}\n", + " )\n", + " return dataset_rows" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb93f2a4", + "metadata": {}, + "outputs": [], + "source": [ + "# Can't just make our own types, server won't deserialize.\n", + "# A fairly easy fix.\n", + "@weave.type()\n", + "class Dataset:\n", + " rows: list[typing.Any]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92c6535c", + "metadata": {}, + "outputs": [], + "source": [ + "raw_dataset = read_dataset(\"/Users/shawn/datasets/aoi\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e803efd", + "metadata": {}, + "outputs": [], + "source": [ + "# raw_dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc723706", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "dataset = weave.save(Dataset(raw_dataset), \"my_dataset5\")\n", + "# Now, here I really want to make my own labels in the UI immediately.\n", + "# where should the added column go? A new version of this dataset?\n", + "# yeah sure why not.\n", + "# What's missing for editing to be good?\n", + "# - batch editing, ie make a bunch of changes and choose where/how to save" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a99df38", + "metadata": {}, + "outputs": [], + "source": [ + "# published = weave.publish(Dataset(raw_dataset), 'weave-flow1/my_dataset1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77c71a23", + "metadata": {}, + "outputs": [], + "source": [ + "dataset.rows\n", + "# Here i went to render labels next to dataset.\n", + "# I need access to labels in the notebook memory... would be easy enough to pass in" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "331d88d6", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "\n", + "def split_paragraphs(doc):\n", + " lines = [l.strip() for l in doc.split(\"\\n\")]\n", + " stripped_doc = \"\\n\".join(lines)\n", + " return [p.strip() for p in stripped_doc.split(\"\\n\\n\")]\n", + "\n", + "\n", + "def find_first_numeric(s):\n", + " match = re.search(r\"\\d+\", s)\n", + " if match is None:\n", + " return None\n", + " return int(match.group().replace(\",\", \"\"))\n", + "\n", + "\n", + "def predict(dataset_row, config):\n", + " paragraphs = split_paragraphs(dataset_row[\"example\"])\n", + " capital_paragraph = None\n", + " name_paragraph = None\n", + " for p in paragraphs:\n", + " if \"name\" in p.lower():\n", + " name_paragraph = p\n", + " if \"share\" in p.lower():\n", + " capital_paragraph = p\n", + " result = {\n", + " \"name\": None,\n", + " \"shares\": None,\n", + " }\n", + " if capital_paragraph:\n", + " paragraph_start = config.get(\"shares_skip_chars\", 0)\n", + " result[\"shares\"] = find_first_numeric(capital_paragraph[paragraph_start:])\n", + " if name_paragraph:\n", + " match = re.search(r\"is \", name_paragraph)\n", + " if match is not None:\n", + " result[\"name\"] = name_paragraph[match.end() :]\n", + " if result[\"name\"] and config.get(\"name_up_to_period\"):\n", + " match = re.search(r\"\\.\", result[\"name\"])\n", + " if match is not None:\n", + " result[\"name\"] = result[\"name\"][: match.start()]\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2378174", + "metadata": {}, + "outputs": [], + "source": [ + "for dataset_row in weave.use(dataset.rows):\n", + " print(predict(dataset_row, {\"shares_skip_chars\": 4}))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efc63aa7", + "metadata": {}, + "outputs": [], + "source": [ + "fields = [\"name\", \"shares\", \"directors\"]\n", + "\n", + "\n", + "def p_r_f1(tp, fp, fn):\n", + " # if any denom is zero, then zero. could use NaN instead...\n", + " precision = 0\n", + " if tp or fp:\n", + " precision = tp / (tp + fp)\n", + " recall = 0\n", + " if tp or fn:\n", + " recall = tp / (tp + fn)\n", + " f1 = 0\n", + " if precision or recall:\n", + " f1 = 2 * (precision * recall) / (precision + recall)\n", + " return precision, recall, f1\n", + "\n", + "\n", + "def summarize_item(item_result, item_label):\n", + " item_summary = {}\n", + " for f in fields:\n", + " item_summary[f\"{f}_negative\"] = item_result.get(f) is None\n", + " item_summary[f\"{f}_correct\"] = item_result.get(f) == item_label.get(f)\n", + "\n", + " item_correct = sum([item_summary[f\"{f}_correct\"] for f in fields])\n", + " item_tp = sum(\n", + " [item_label.get(f) is not None and item_summary[f\"{f}_correct\"] for f in fields]\n", + " )\n", + " item_fp = sum(\n", + " [\n", + " item_label.get(f) is not None and not item_summary[f\"{f}_correct\"]\n", + " for f in fields\n", + " ]\n", + " )\n", + " item_tn = sum(\n", + " [item_label.get(f) is None and item_summary[f\"{f}_correct\"] for f in fields]\n", + " )\n", + " item_fn = sum(\n", + " [item_label.get(f) is None and not item_summary[f\"{f}_correct\"] for f in fields]\n", + " )\n", + "\n", + " item_precision, item_recall, item_f1 = p_r_f1(item_tp, item_fp, item_fn)\n", + "\n", + " return {\n", + " **item_summary,\n", + " \"correct\": item_correct,\n", + " \"tp\": item_tp,\n", + " \"fp\": item_fp,\n", + " \"tn\": item_tn,\n", + " \"fn\": item_fn,\n", + " \"precision\": item_precision,\n", + " \"recall\": item_recall,\n", + " \"f1\": item_f1,\n", + " }\n", + "\n", + "\n", + "def field_pr(eval_result, field_name):\n", + " tp = sum(\n", + " not item[\"summary\"][f\"{field_name}_negative\"]\n", + " and item[\"summary\"][f\"{field_name}_correct\"]\n", + " for item in eval_result\n", + " )\n", + " fp = sum(\n", + " not item[\"summary\"][f\"{field_name}_negative\"]\n", + " and not item[\"summary\"][f\"{field_name}_correct\"]\n", + " for item in eval_result\n", + " )\n", + " tn = sum(\n", + " item[\"summary\"][f\"{field_name}_negative\"]\n", + " and item[\"summary\"][f\"{field_name}_correct\"]\n", + " for item in eval_result\n", + " )\n", + " fn = sum(\n", + " item[\"summary\"][f\"{field_name}_negative\"]\n", + " and not item[\"summary\"][f\"{field_name}_correct\"]\n", + " for item in eval_result\n", + " )\n", + "\n", + " precision, recall, f1 = p_r_f1(tp, fp, fn)\n", + " return {\n", + " \"tp\": tp,\n", + " \"fp\": fp,\n", + " \"tn\": tn,\n", + " \"fn\": fn,\n", + " \"precision\": precision,\n", + " \"recall\": recall,\n", + " \"f1\": f1,\n", + " }\n", + "\n", + "\n", + "def summarize(eval_result):\n", + " summary = {}\n", + " for field in fields:\n", + " summary[f\"field_{field}\"] = field_pr(eval_result, field)\n", + " for metric in [\"precision\", \"recall\", \"f1\"]:\n", + " summary[f\"avg_{metric}\"] = sum(\n", + " summary[f\"field_{f}\"][metric] for f in fields\n", + " ) / len(fields)\n", + " return summary\n", + "\n", + "\n", + "def evaluate(dataset, predict_config):\n", + " eval_result = []\n", + " correct_count = 0\n", + " count = 0\n", + " for dataset_row in dataset:\n", + " start_time = time.time()\n", + " result = predict(dataset_row, predict_config)\n", + " latency = time.time() - start_time\n", + " latency = random.gauss(\n", + " predict_config[\"latency_mu\"], predict_config[\"latency_sigma\"]\n", + " )\n", + " item_summary = summarize_item(result, dataset_row[\"label\"])\n", + " eval_result.append(\n", + " {\n", + " \"dataset_id\": dataset_row[\"id\"],\n", + " \"result\": result,\n", + " \"summary\": {\"latency\": latency, **item_summary},\n", + " }\n", + " )\n", + " return {\n", + " \"config\": predict_config,\n", + " \"eval_table\": eval_result,\n", + " \"summary\": summarize(eval_result),\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2248364", + "metadata": {}, + "outputs": [], + "source": [ + "eval_results0 = weave.save(\n", + " evaluate(weave.use(dataset).rows, {\"latency_mu\": 0.3, \"latency_sigma\": 0.1}),\n", + " \"eval_results0\",\n", + ")\n", + "eval_results1 = weave.save(\n", + " evaluate(\n", + " weave.use(dataset).rows,\n", + " {\"latency_mu\": 0.3, \"latency_sigma\": 0.1, \"name_up_to_period\": True},\n", + " ),\n", + " \"eval_results1\",\n", + ")\n", + "eval_results2 = weave.save(\n", + " evaluate(\n", + " weave.use(dataset).rows,\n", + " {\n", + " \"latency_mu\": 0.3,\n", + " \"latency_sigma\": 0.1,\n", + " \"name_up_to_period\": True,\n", + " \"shares_skip_chars\": 4,\n", + " },\n", + " ),\n", + " \"eval_results2\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93727275", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.panels_py import panel_eval\n", + "\n", + "panel_eval.eval_board(dataset.rows, eval_results0, eval_results2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "166e8607", + "metadata": {}, + "outputs": [], + "source": [ + "# To add:\n", + "# - backed by W&B runs\n", + "# - show run code / config comparison\n", + "# - show traces of pipelines and compare them\n", + "# - add view of N runs instead of 2" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "8172d67c", - "metadata": {}, - "outputs": [], - "source": [ - "raw_labels = {\n", - " \"Articles_of_Incorporation_Real_Example_3_txt\": {\n", - " \"name\": \"HealthFirst Solutions LLC\",\n", - " \"shares\": 500000,\n", - " },\n", - " \"Articles_of_Incorporation_Real_Example_2_txt\": {\n", - " \"name\": \"GreenLeaf LLC\",\n", - " \"shares\": None,\n", - " \"directors\": [\"Sarah Miller\", \"Daniel Lee\"],\n", - " },\n", - " \"Articles_of_Incorporation_Real_Example_1_txt\": {\n", - " \"name\": \"TechBoost Corp\",\n", - " \"shares\": 1000000,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_10_txt\": {\n", - " \"name\": \"Brown, Fernandez and Smith\",\n", - " \"shares\": 41141,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_9_txt\": {\n", - " \"name\": \"Ruiz-Goodman\",\n", - " \"shares\": 31783,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_8_txt\": {\n", - " \"name\": \"Gibson, Hunt and Davidson\",\n", - " \"shares\": 96403,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_7_txt\": {\n", - " \"name\": \"Boyd-Browning\",\n", - " \"shares\": 41300,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_6_txt\": {\n", - " \"name\": \"Newton, Moreno and Yang\",\n", - " \"shares\": 73981,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_5_txt\": {\n", - " \"name\": \"Matthews and Sons\",\n", - " \"shares\": 98608,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_4_txt\": {\n", - " \"name\": \"Moore LLC\",\n", - " \"shares\": 5732,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_3_txt\": {\n", - " \"name\": \"Mullen Inc\",\n", - " \"shares\": 76197,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_2_txt\": {\n", - " \"name\": \"Ellis and Sons\",\n", - " \"shares\": 54183,\n", - " },\n", - " \"Highly_Varied_Article_of_Incorporation_1_txt\": {\n", - " \"name\": \"French, Wyatt and Coleman\",\n", - " \"shares\": 78821,\n", - " },\n", - "}\n", - "\n", - "\n", - "def read_dataset(root):\n", - " dataset_rows = []\n", - " for p in glob.glob(os.path.join(root, \"*.txt\")):\n", - " # Have to do replace here because of weave '.' access issues\n", - " example_id = os.path.basename(p).replace(\".\", \"_\")\n", - " label = raw_labels.get(example_id)\n", - " if label:\n", - " dataset_rows.append(\n", - " {\"id\": example_id, \"example\": open(p).read(), \"label\": label}\n", - " )\n", - " return dataset_rows" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb93f2a4", - "metadata": {}, - "outputs": [], - "source": [ - "# Can't just make our own types, server won't deserialize.\n", - "# A fairly easy fix.\n", - "@weave.type()\n", - "class Dataset:\n", - " rows: list[typing.Any]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92c6535c", - "metadata": {}, - "outputs": [], - "source": [ - "raw_dataset = read_dataset(\"/Users/shawn/datasets/aoi\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e803efd", - "metadata": {}, - "outputs": [], - "source": [ - "# raw_dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc723706", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "dataset = weave.save(Dataset(raw_dataset), \"my_dataset5\")\n", - "# Now, here I really want to make my own labels in the UI immediately.\n", - "# where should the added column go? A new version of this dataset?\n", - "# yeah sure why not.\n", - "# What's missing for editing to be good?\n", - "# - batch editing, ie make a bunch of changes and choose where/how to save" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a99df38", - "metadata": {}, - "outputs": [], - "source": [ - "# published = weave.publish(Dataset(raw_dataset), 'weave-flow1/my_dataset1')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77c71a23", - "metadata": {}, - "outputs": [], - "source": [ - "dataset.rows\n", - "# Here i went to render labels next to dataset.\n", - "# I need access to labels in the notebook memory... would be easy enough to pass in" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "331d88d6", - "metadata": {}, - "outputs": [], - "source": [ - "import re\n", - "\n", - "\n", - "def split_paragraphs(doc):\n", - " lines = [l.strip() for l in doc.split(\"\\n\")]\n", - " stripped_doc = \"\\n\".join(lines)\n", - " return [p.strip() for p in stripped_doc.split(\"\\n\\n\")]\n", - "\n", - "\n", - "def find_first_numeric(s):\n", - " match = re.search(r\"\\d+\", s)\n", - " if match is None:\n", - " return None\n", - " return int(match.group().replace(\",\", \"\"))\n", - "\n", - "\n", - "def predict(dataset_row, config):\n", - " paragraphs = split_paragraphs(dataset_row[\"example\"])\n", - " capital_paragraph = None\n", - " name_paragraph = None\n", - " for p in paragraphs:\n", - " if \"name\" in p.lower():\n", - " name_paragraph = p\n", - " if \"share\" in p.lower():\n", - " capital_paragraph = p\n", - " result = {\n", - " \"name\": None,\n", - " \"shares\": None,\n", - " }\n", - " if capital_paragraph:\n", - " paragraph_start = config.get(\"shares_skip_chars\", 0)\n", - " result[\"shares\"] = find_first_numeric(capital_paragraph[paragraph_start:])\n", - " if name_paragraph:\n", - " match = re.search(r\"is \", name_paragraph)\n", - " if match is not None:\n", - " result[\"name\"] = name_paragraph[match.end() :]\n", - " if result[\"name\"] and config.get(\"name_up_to_period\"):\n", - " match = re.search(r\"\\.\", result[\"name\"])\n", - " if match is not None:\n", - " result[\"name\"] = result[\"name\"][: match.start()]\n", - " return result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2378174", - "metadata": {}, - "outputs": [], - "source": [ - "for dataset_row in weave.use(dataset.rows):\n", - " print(predict(dataset_row, {\"shares_skip_chars\": 4}))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efc63aa7", - "metadata": {}, - "outputs": [], - "source": [ - "fields = [\"name\", \"shares\", \"directors\"]\n", - "\n", - "\n", - "def p_r_f1(tp, fp, fn):\n", - " # if any denom is zero, then zero. could use NaN instead...\n", - " precision = 0\n", - " if tp or fp:\n", - " precision = tp / (tp + fp)\n", - " recall = 0\n", - " if tp or fn:\n", - " recall = tp / (tp + fn)\n", - " f1 = 0\n", - " if precision or recall:\n", - " f1 = 2 * (precision * recall) / (precision + recall)\n", - " return precision, recall, f1\n", - "\n", - "\n", - "def summarize_item(item_result, item_label):\n", - " item_summary = {}\n", - " for f in fields:\n", - " item_summary[f\"{f}_negative\"] = item_result.get(f) is None\n", - " item_summary[f\"{f}_correct\"] = item_result.get(f) == item_label.get(f)\n", - "\n", - " item_correct = sum([item_summary[f\"{f}_correct\"] for f in fields])\n", - " item_tp = sum(\n", - " [item_label.get(f) is not None and item_summary[f\"{f}_correct\"] for f in fields]\n", - " )\n", - " item_fp = sum(\n", - " [\n", - " item_label.get(f) is not None and not item_summary[f\"{f}_correct\"]\n", - " for f in fields\n", - " ]\n", - " )\n", - " item_tn = sum(\n", - " [item_label.get(f) is None and item_summary[f\"{f}_correct\"] for f in fields]\n", - " )\n", - " item_fn = sum(\n", - " [item_label.get(f) is None and not item_summary[f\"{f}_correct\"] for f in fields]\n", - " )\n", - "\n", - " item_precision, item_recall, item_f1 = p_r_f1(item_tp, item_fp, item_fn)\n", - "\n", - " return {\n", - " **item_summary,\n", - " \"correct\": item_correct,\n", - " \"tp\": item_tp,\n", - " \"fp\": item_fp,\n", - " \"tn\": item_tn,\n", - " \"fn\": item_fn,\n", - " \"precision\": item_precision,\n", - " \"recall\": item_recall,\n", - " \"f1\": item_f1,\n", - " }\n", - "\n", - "\n", - "def field_pr(eval_result, field_name):\n", - " tp = sum(\n", - " not item[\"summary\"][f\"{field_name}_negative\"]\n", - " and item[\"summary\"][f\"{field_name}_correct\"]\n", - " for item in eval_result\n", - " )\n", - " fp = sum(\n", - " not item[\"summary\"][f\"{field_name}_negative\"]\n", - " and not item[\"summary\"][f\"{field_name}_correct\"]\n", - " for item in eval_result\n", - " )\n", - " tn = sum(\n", - " item[\"summary\"][f\"{field_name}_negative\"]\n", - " and item[\"summary\"][f\"{field_name}_correct\"]\n", - " for item in eval_result\n", - " )\n", - " fn = sum(\n", - " item[\"summary\"][f\"{field_name}_negative\"]\n", - " and not item[\"summary\"][f\"{field_name}_correct\"]\n", - " for item in eval_result\n", - " )\n", - "\n", - " precision, recall, f1 = p_r_f1(tp, fp, fn)\n", - " return {\n", - " \"tp\": tp,\n", - " \"fp\": fp,\n", - " \"tn\": tn,\n", - " \"fn\": fn,\n", - " \"precision\": precision,\n", - " \"recall\": recall,\n", - " \"f1\": f1,\n", - " }\n", - "\n", - "\n", - "def summarize(eval_result):\n", - " summary = {}\n", - " for field in fields:\n", - " summary[f\"field_{field}\"] = field_pr(eval_result, field)\n", - " for metric in [\"precision\", \"recall\", \"f1\"]:\n", - " summary[f\"avg_{metric}\"] = sum(\n", - " summary[f\"field_{f}\"][metric] for f in fields\n", - " ) / len(fields)\n", - " return summary\n", - "\n", - "\n", - "def evaluate(dataset, predict_config):\n", - " eval_result = []\n", - " correct_count = 0\n", - " count = 0\n", - " for dataset_row in dataset:\n", - " start_time = time.time()\n", - " result = predict(dataset_row, predict_config)\n", - " latency = time.time() - start_time\n", - " latency = random.gauss(\n", - " predict_config[\"latency_mu\"], predict_config[\"latency_sigma\"]\n", - " )\n", - " item_summary = summarize_item(result, dataset_row[\"label\"])\n", - " eval_result.append(\n", - " {\n", - " \"dataset_id\": dataset_row[\"id\"],\n", - " \"result\": result,\n", - " \"summary\": {\"latency\": latency, **item_summary},\n", - " }\n", - " )\n", - " return {\n", - " \"config\": predict_config,\n", - " \"eval_table\": eval_result,\n", - " \"summary\": summarize(eval_result),\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2248364", - "metadata": {}, - "outputs": [], - "source": [ - "eval_results0 = weave.save(\n", - " evaluate(weave.use(dataset).rows, {\"latency_mu\": 0.3, \"latency_sigma\": 0.1}),\n", - " \"eval_results0\",\n", - ")\n", - "eval_results1 = weave.save(\n", - " evaluate(\n", - " weave.use(dataset).rows,\n", - " {\"latency_mu\": 0.3, \"latency_sigma\": 0.1, \"name_up_to_period\": True},\n", - " ),\n", - " \"eval_results1\",\n", - ")\n", - "eval_results2 = weave.save(\n", - " evaluate(\n", - " weave.use(dataset).rows,\n", - " {\n", - " \"latency_mu\": 0.3,\n", - " \"latency_sigma\": 0.1,\n", - " \"name_up_to_period\": True,\n", - " \"shares_skip_chars\": 4,\n", - " },\n", - " ),\n", - " \"eval_results2\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93727275", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.panels_py import panel_eval\n", - "\n", - "panel_eval.eval_board(dataset.rows, eval_results0, eval_results2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "166e8607", - "metadata": {}, - "outputs": [], - "source": [ - "# To add:\n", - "# - backed by W&B runs\n", - "# - show run code / config comparison\n", - "# - show traces of pipelines and compare them\n", - "# - add view of N runs instead of 2" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/examples/prompts/llm_monitoring/README.md b/examples/prompts/llm_monitoring/README.md index fcb12dd331b..7636594f80f 100644 --- a/examples/prompts/llm_monitoring/README.md +++ b/examples/prompts/llm_monitoring/README.md @@ -20,7 +20,7 @@ There are two main ways to authenticate OpenAI API calls so you can view them in ### OpenAI API 1. Set your OPENAI_API_KEY in your environment/script/notebook (e.g. via `os.environ["OPENAI_API_KEY"]`). -2. Import openai as follows: `from weave.legacy.monitoring import openai`. +2. Import openai as follows: `from weave.legacy.weave.monitoring import openai`. 3. Make calls via the OpenAI SDK as usual. See details and create an interactive board in the [OpenAI monitoring notebook](./openai_client_quickstart.ipynb). diff --git a/examples/prompts/llm_monitoring/openai_client_quickstart.ipynb b/examples/prompts/llm_monitoring/openai_client_quickstart.ipynb index f9b39f1af13..f0fc64bdceb 100644 --- a/examples/prompts/llm_monitoring/openai_client_quickstart.ipynb +++ b/examples/prompts/llm_monitoring/openai_client_quickstart.ipynb @@ -135,7 +135,7 @@ "metadata": {}, "outputs": [], "source": [ - "from weave.legacy.monitoring import openai, init_monitor\n", + "from weave.legacy.weave.monitoring import openai, init_monitor\n", "m = init_monitor(f\"{WB_ENTITY}/{WB_PROJECT}/{STREAM_NAME}\")\n", "\n", "# specifying a single model for simplicity\n", @@ -245,7 +245,7 @@ "metadata": {}, "outputs": [], "source": [ - "from weave.legacy.monitoring.openai import message_from_stream\n", + "from weave.legacy.weave.monitoring.openai import message_from_stream\n", "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[\n", " {\"role\": \"system\", \"content\": \"You are a robot and only speak in robot, like beep bloop bop.\"},\n", " {\"role\": \"user\", \"content\": \"Tell me a 50-word story.\"},\n", diff --git a/examples/prompts/llm_monitoring/openai_proxy_quickstart_enterprise_mode.ipynb b/examples/prompts/llm_monitoring/openai_proxy_quickstart_enterprise_mode.ipynb index 57d8c1eaa96..b2c1cda36cb 100644 --- a/examples/prompts/llm_monitoring/openai_proxy_quickstart_enterprise_mode.ipynb +++ b/examples/prompts/llm_monitoring/openai_proxy_quickstart_enterprise_mode.ipynb @@ -251,8 +251,8 @@ "outputs": [], "source": [ "import openai\n", - "from weave.legacy.monitoring import openai as openaimon\n", - "from weave.legacy.monitoring import init_monitor\n", + "from weave.legacy.weave.monitoring import openai as openaimon\n", + "from weave.legacy.weave.monitoring import init_monitor\n", "\n", "m = init_monitor(f\"{WB_ENTITY}/{WB_PROJECT}/{STREAM_NAME}\")\n", "\n", diff --git a/examples/prompts/trace_debugging/dev/synthetic_trace_data.ipynb b/examples/prompts/trace_debugging/dev/synthetic_trace_data.ipynb index 3834e6ea2a5..be0eae16ac5 100644 --- a/examples/prompts/trace_debugging/dev/synthetic_trace_data.ipynb +++ b/examples/prompts/trace_debugging/dev/synthetic_trace_data.ipynb @@ -39,7 +39,7 @@ "outputs": [], "source": [ "import weave\n", - "from weave.legacy.monitoring import StreamTable\n", + "from weave.legacy.weave.monitoring import StreamTable\n", "from weave.stream_data_interfaces import TraceSpanDict" ] }, diff --git a/examples/prompts/trace_debugging/trace_quickstart_decorator.ipynb b/examples/prompts/trace_debugging/trace_quickstart_decorator.ipynb index d692e72734b..bc0dd40d255 100644 --- a/examples/prompts/trace_debugging/trace_quickstart_decorator.ipynb +++ b/examples/prompts/trace_debugging/trace_quickstart_decorator.ipynb @@ -46,7 +46,7 @@ "source": [ "import weave\n", "import time\n", - "from weave.legacy.monitoring import init_monitor" + "from weave.legacy.weave.monitoring import init_monitor" ] }, { diff --git a/examples/prompts/trace_debugging/trace_quickstart_langchain.ipynb b/examples/prompts/trace_debugging/trace_quickstart_langchain.ipynb index 4e5080d95a1..72c3257f562 100644 --- a/examples/prompts/trace_debugging/trace_quickstart_langchain.ipynb +++ b/examples/prompts/trace_debugging/trace_quickstart_langchain.ipynb @@ -75,7 +75,7 @@ "source": [ "import langchain\n", "import weave\n", - "from weave.legacy.monitoring.langchain import WeaveTracer" + "from weave.legacy.weave.monitoring.langchain import WeaveTracer" ] }, { diff --git a/integration_test/cypress/e2e/interactive/blank.py b/integration_test/cypress/e2e/interactive/blank.py index ef823b13559..0dc51529825 100644 --- a/integration_test/cypress/e2e/interactive/blank.py +++ b/integration_test/cypress/e2e/interactive/blank.py @@ -1,12 +1,12 @@ import weave -from weave.legacy.show import show_url +from weave.legacy.weave.show import show_url weave.use_fixed_server_port() obj = [ 1, 2, 3, -] # weave.legacy.panels.Board({}, [weave.legacy.panels.BoardPanel(weave.legacy.panels.Table([1, 2, 3]))]) +] # weave.legacy.weave.panels.Board({}, [weave.legacy.weave.panels.BoardPanel(weave.legacy.weave.panels.Table([1, 2, 3]))]) blank = weave.save(obj) print(show_url(obj)) diff --git a/integration_test/cypress/e2e/interactive/distribution.py b/integration_test/cypress/e2e/interactive/distribution.py index e81e06a7c78..f516a04364c 100644 --- a/integration_test/cypress/e2e/interactive/distribution.py +++ b/integration_test/cypress/e2e/interactive/distribution.py @@ -1,12 +1,12 @@ import random import weave -from weave.legacy import panels -from weave.legacy.show import show_url +from weave.legacy.weave import panels +from weave.legacy.weave.show import show_url # Weave package now defaults to eager mode, but lazy mode required for this example notebook for now. weave.use_lazy_execution() -from weave.legacy.ecosystem import wandb +from weave.legacy.weave.ecosystem import wandb weave.use_fixed_server_port() @@ -38,7 +38,8 @@ {}, [ panels.BoardPanel( - panel, layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12) + panel, + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12), ) ], ) diff --git a/integration_test/cypress/e2e/interactive/scatter.py b/integration_test/cypress/e2e/interactive/scatter.py index 0c0b117866d..b0e170fcc56 100644 --- a/integration_test/cypress/e2e/interactive/scatter.py +++ b/integration_test/cypress/e2e/interactive/scatter.py @@ -1,8 +1,8 @@ import random import weave -from weave.legacy.ecosystem import wandb -from weave.legacy.show import show_url +from weave.legacy.weave.ecosystem import wandb +from weave.legacy.weave.show import show_url weave.use_fixed_server_port() @@ -19,14 +19,14 @@ ] ) -panel: weave.legacy.panels.Group = weave.legacy.panels.Group( +panel: weave.legacy.weave.panels.Group = weave.legacy.weave.panels.Group( items={ "scatter": wandb.Scatter( # type: ignore data, x_fn=lambda item: item["a"], y_fn=lambda item: item["b"] ), - "table": lambda scatter: weave.legacy.panels.LabeledItem( + "table": lambda scatter: weave.legacy.weave.panels.LabeledItem( label="Selected items", - item=weave.legacy.panels.Group( + item=weave.legacy.weave.panels.Group( style="height: 400px;", preferHorizontal=True, items={"table": scatter.selected()}, diff --git a/mypy.ini b/mypy.ini index b8c601a669f..8793034417e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,19 +7,19 @@ exclude = (.*pyi$)|(weave/ecosystem)|(weave/tests)|(weave/panel)|(weave/ops)|(we ;; 1. put in exclude above ;; 2. put in follow_imports = skip below ;; 3. put in exclude in .pre-commit-config.yaml -[mypy-weave.legacy.ecosystem.*] +[mypy-weave.legacy.weave.ecosystem.*] follow_imports = skip -[mypy-weave.legacy.panels_py.*] +[mypy-weave.legacy.weave.panels_py.*] follow_imports = skip -[mypy-weave.legacy.panels.*] +[mypy-weave.legacy.weave.panels.*] follow_imports = skip -[mypy-weave.legacy.ops_primitives.*] +[mypy-weave.legacy.weave.ops_primitives.*] follow_imports = skip -[mypy-weave.legacy.ops_domain.*] +[mypy-weave.legacy.weave.ops_domain.*] follow_imports = skip [mypy-pyarrow.*] @@ -95,15 +95,15 @@ disallow_untyped_calls = True ; show_error_codes = True -[mypy-weave.legacy._dict_utils] +[mypy-weave.legacy.weave._dict_utils] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.arrow.*] +[mypy-weave.legacy.weave.arrow.*] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.ops_arrow.*] +[mypy-weave.legacy.weave.ops_arrow.*] disallow_untyped_defs = False disallow_untyped_calls = False @@ -131,19 +131,19 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.arrow_util] +[mypy-weave.legacy.weave.arrow_util] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.box] +[mypy-weave.legacy.weave.box] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers] +[mypy-weave.legacy.weave.mappers] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.client_interface] +[mypy-weave.legacy.weave.client_interface] disallow_untyped_defs = False disallow_untyped_calls = False @@ -151,11 +151,11 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.weave_types] +[mypy-weave.legacy.weave.weave_types] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.uris] +[mypy-weave.legacy.weave.uris] disallow_untyped_defs = False disallow_untyped_calls = False @@ -163,15 +163,15 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.op_args] +[mypy-weave.legacy.weave.op_args] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.context_state] +[mypy-weave.legacy.weave.context_state] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers_weave] +[mypy-weave.legacy.weave.mappers_weave] disallow_untyped_defs = False disallow_untyped_calls = False @@ -183,15 +183,15 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.artifact_base] +[mypy-weave.legacy.weave.artifact_base] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.artifact_local] +[mypy-weave.legacy.weave.artifact_local] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.artifact_wandb] +[mypy-weave.legacy.weave.artifact_wandb] disallow_untyped_defs = False disallow_untyped_calls = False @@ -199,19 +199,19 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.client] +[mypy-weave.legacy.weave.client] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers_python_def] +[mypy-weave.legacy.weave.mappers_python_def] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers_gql] +[mypy-weave.legacy.weave.mappers_gql] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers_publisher] +[mypy-weave.legacy.weave.mappers_publisher] disallow_untyped_defs = False disallow_untyped_calls = False @@ -227,7 +227,7 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.forward_graph] +[mypy-weave.legacy.weave.forward_graph] disallow_untyped_defs = False disallow_untyped_calls = False @@ -235,15 +235,15 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.make_type] +[mypy-weave.legacy.weave.make_type] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.serialize] +[mypy-weave.legacy.weave.serialize] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.op_def] +[mypy-weave.legacy.weave.op_def] disallow_untyped_defs = False disallow_untyped_calls = False @@ -255,19 +255,19 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.execute_fast] +[mypy-weave.legacy.weave.execute_fast] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.op_def_type] +[mypy-weave.legacy.weave.op_def_type] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.op_execute] +[mypy-weave.legacy.weave.op_execute] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.execute] +[mypy-weave.legacy.weave.execute] disallow_untyped_defs = False disallow_untyped_calls = False @@ -275,19 +275,19 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.graph_mapper] +[mypy-weave.legacy.weave.graph_mapper] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.context] +[mypy-weave.legacy.weave.context] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.decorator_class] +[mypy-weave.legacy.weave.decorator_class] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.mappers_arrow] +[mypy-weave.legacy.weave.mappers_arrow] disallow_untyped_defs = False disallow_untyped_calls = False @@ -295,11 +295,11 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.decorator_op] +[mypy-weave.legacy.weave.decorator_op] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.decorator_arrow_op] +[mypy-weave.legacy.weave.decorator_arrow_op] disallow_untyped_defs = False disallow_untyped_calls = False @@ -307,15 +307,15 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.decorator_type] +[mypy-weave.legacy.weave.decorator_type] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.panel_util] +[mypy-weave.legacy.weave.panel_util] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.compile] +[mypy-weave.legacy.weave.compile] disallow_untyped_defs = False disallow_untyped_calls = False @@ -327,7 +327,7 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.panel] +[mypy-weave.legacy.weave.panel] disallow_untyped_defs = False disallow_untyped_calls = False @@ -335,15 +335,15 @@ disallow_untyped_calls = False disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.derive_op] +[mypy-weave.legacy.weave.derive_op] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.async_demo] +[mypy-weave.legacy.weave.async_demo] disallow_untyped_defs = False disallow_untyped_calls = False -[mypy-weave.legacy.runs] +[mypy-weave.legacy.weave.runs] disallow_untyped_defs = False disallow_untyped_calls = False diff --git a/pyrightconfig.json b/pyrightconfig.json index 84be57f4887..8bb7fa5aee8 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -19,7 +19,7 @@ "weave/box.py", "weave/cli.py", "weave/client_interface.py", - "weave.legacy.codify.py", + "weave.legacy.weave.codify.py", "weave/compile_domain.py", "weave/conftest.py", "weave/context.py", diff --git a/weave/__init__.py b/weave/__init__.py index 9ea806d2e21..4a92a3147a7 100644 --- a/weave/__init__.py +++ b/weave/__init__.py @@ -3,22 +3,22 @@ import sys # We track what modules were loaded before importing weave, so we can ensure -# that someone doesn't introduce auto-importing loading weave.legacy.ops or weave.legacy.panels +# that someone doesn't introduce auto-importing loading weave.legacy.weave.ops or weave.legacy.weave.panels # (because they are slow to import and have more dependencies, and they are part of # the engine and UI layers which should be kept separate from the core layer). pre_init_modules = set(sys.modules.keys()) -from weave.legacy import context_state as _context_state +from weave.legacy.weave import context_state as _context_state _loading_builtins_token = _context_state.set_loading_built_ins() -from weave.legacy import weave_types as types -from weave.legacy import storage -from weave.legacy.api import * -from weave.legacy.errors import * -from weave.legacy import mappers_python_def -from weave.legacy import wandb_api as _wandb_api -from weave.legacy import context as _context +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import storage +from weave.legacy.weave.api import * +from weave.legacy.weave.errors import * +from weave.legacy.weave import mappers_python_def +from weave.legacy.weave import wandb_api as _wandb_api +from weave.legacy.weave import context as _context from weave import version @@ -40,20 +40,20 @@ from weave.trace.util import ThreadPoolExecutor, Thread # See the comment above pre_init_modules above. This is check to ensure we don't accidentally -# introduce loading weave.legacy.ops or weave.legacy.panels when importing weave. +# introduce loading weave.legacy.weave.ops or weave.legacy.weave.panels when importing weave. newly_added_modules = set(sys.modules.keys()) - pre_init_modules ops_modules = [] panels_modules = [] for module_name in newly_added_modules: - if module_name.startswith("weave.legacy.ops"): + if module_name.startswith("weave.legacy.weave.ops"): ops_modules.append(module_name) - if module_name.startswith("weave.legacy.panels"): + if module_name.startswith("weave.legacy.weave.panels"): panels_modules.append(module_name) if ops_modules or panels_modules: all_invalid_modules = ops_modules + panels_modules invalid_submodules = set([".".join(m.split(".")[:2]) for m in all_invalid_modules]) raise errors.WeaveInternalError( - "importing weave should not import weave.legacy.ops or weave.legacy.panels, but the following modules were imported: " + "importing weave should not import weave.legacy.weave.ops or weave.legacy.weave.panels, but the following modules were imported: " + ", ".join(invalid_submodules) ) diff --git a/weave/conftest.py b/weave/conftest.py index a18d90ad4e2..a33d2ac523c 100644 --- a/weave/conftest.py +++ b/weave/conftest.py @@ -13,9 +13,11 @@ from flask.testing import FlaskClient import weave -from weave.legacy import client as client_legacy -from weave.legacy import context_state, environment, io_service, serialize -from weave.legacy.language_features.tagging.tag_store import isolated_tagging_context +from weave.legacy.weave import client as client_legacy +from weave.legacy.weave import context_state, environment, io_service, serialize +from weave.legacy.weave.language_features.tagging.tag_store import ( + isolated_tagging_context, +) from weave.trace import weave_init from weave.trace_server import ( clickhouse_trace_server_batched, @@ -24,7 +26,7 @@ ) from weave.trace_server import trace_server_interface as tsi -from .legacy import logs +from .legacy.weave import logs from .tests import fixture_fakewandb from .tests.trace.trace_server_clickhouse_conftest import * from .tests.wandb_system_tests_conftest import * @@ -37,7 +39,7 @@ # tests. context_state._eager_mode.set(False) -# A lot of tests rely on weave.legacy.ops.* being in scope. Importing this here +# A lot of tests rely on weave.legacy.weave.ops.* being in scope. Importing this here # makes that work... ### Disable datadog engine tracing @@ -269,7 +271,7 @@ def factory(process=False): @pytest.fixture() def consistent_table_col_ids(): - from weave.legacy.panels import table_state + from weave.legacy.weave.panels import table_state with table_state.use_consistent_col_ids(): yield diff --git a/weave/deploy/gcp/__init__.py b/weave/deploy/gcp/__init__.py index 3100723a4ce..dfe8947be6c 100644 --- a/weave/deploy/gcp/__init__.py +++ b/weave/deploy/gcp/__init__.py @@ -8,8 +8,8 @@ from pathlib import Path from weave import __version__ -from weave.legacy import environment -from weave.legacy.artifact_wandb import WeaveWBArtifactURI as WeaveWBArtifactURI +from weave.legacy.weave import environment +from weave.legacy.weave.artifact_wandb import WeaveWBArtifactURI as WeaveWBArtifactURI from weave.trace.refs import ObjectRef, parse_uri from ..util import execute, safe_name diff --git a/weave/deploy/modal/__init__.py b/weave/deploy/modal/__init__.py index 947cbb2f057..6972f32ce7f 100644 --- a/weave/deploy/modal/__init__.py +++ b/weave/deploy/modal/__init__.py @@ -5,8 +5,8 @@ import typing from pathlib import Path -from weave.legacy import artifact_wandb as artifact_wandb -from weave.legacy import environment +from weave.legacy.weave import artifact_wandb as artifact_wandb +from weave.legacy.weave import environment from weave.trace.refs import ObjectRef, parse_uri try: diff --git a/weave/deploy/modal/stub.py b/weave/deploy/modal/stub.py index 89eeff34bcc..29242192fde 100644 --- a/weave/deploy/modal/stub.py +++ b/weave/deploy/modal/stub.py @@ -4,7 +4,7 @@ from modal import Image, Secret, Stub, asgi_app from weave.deploy.util import safe_name -from weave.legacy.uris import WeaveURI +from weave.legacy.weave.uris import WeaveURI from weave.trace.refs import ObjectRef, parse_uri image = ( diff --git a/weave/init_message.py b/weave/init_message.py index d76b8e963be..92f972e71c7 100644 --- a/weave/init_message.py +++ b/weave/init_message.py @@ -1,6 +1,6 @@ import typing -from weave.legacy import urls +from weave.legacy.weave import urls if typing.TYPE_CHECKING: import packaging.version # type: ignore[import-not-found] diff --git a/weave/legacy/Dockerfile b/weave/legacy/Dockerfile new file mode 100644 index 00000000000..e1d677577ff --- /dev/null +++ b/weave/legacy/Dockerfile @@ -0,0 +1,51 @@ +FROM frolvlad/alpine-glibc:alpine-3.17_glibc-2.34 +LABEL MAINTAINER="danny goldstein " + +ENV CONDA_VERSION=4.9.2 \ + CONDA_MD5=b4e46fcc8029e2cfa731b788f25b1d36 \ + PYTHON_VERSION=39 \ + PYTHONDONTWRITEBYTECODE=true \ + PATH=/opt/conda/bin/:/opt/conda/envs/base/:$PATH \ + WEAVE_LOCAL_ARTIFACT_DIR=/local-artifacts + +# We do the following all in one block: +# - Create user and group weave +# - Install miniconda install dependencies +# - Download miniconda and check the md5sum +# - Install miniconda +# - Install tini +# - Remove all conda managed static libraries +# - Remove all conda managed *.pyc files +# - Cleanup conda files +# - Uninstall miniconda install dependencies +RUN mkdir /weave && + apk add --no-cache wget bzip2 && + wget --quiet https://repo.continuum.io/miniconda/Miniconda3-py${PYTHON_VERSION}_$CONDA_VERSION-Linux-x86_64.sh && + echo "${CONDA_MD5} Miniconda3-py${PYTHON_VERSION}_$CONDA_VERSION-Linux-x86_64.sh" >miniconda.md5 && + if [ $(md5sum -c miniconda.md5 | awk '{print $2}') != "OK" ]; then exit 1; fi && + mv Miniconda3-py${PYTHON_VERSION}_$CONDA_VERSION-Linux-x86_64.sh miniconda.sh && + sh ./miniconda.sh -b -p /opt/conda && + rm miniconda.sh miniconda.md5 && + ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh && + echo ". /opt/conda/etc/profile.d/conda.sh" >>/weave/.profile && + echo "conda activate base" >>/weave/.profile && + /opt/conda/bin/conda install conda==$CONDA_VERSION && + echo "conda == $CONDA_VERSION" >>/opt/conda/conda-meta/pinned && + /opt/conda/bin/conda install --freeze-installed tini pip gunicorn python=3.9.7 -y && + find /opt/conda/ -follow -type f -name '*.a' -delete && + find /opt/conda/ -follow -type f -name '*.pyc' -delete && + /opt/conda/bin/conda clean -afy && + apk del wget bzip2 + +ENV PORT 9239 + +WORKDIR /weave +ADD . . + +RUN pip install -r requirements.engine.txt +RUN mkdir /local-artifacts + +EXPOSE 9239 + +ENTRYPOINT [ "tini", "-g", "--" ] +CMD ["gunicorn", "weave.legacy.weave.weave_server:app"] diff --git a/weave/legacy/core_types/__init__.py b/weave/legacy/core_types/__init__.py deleted file mode 100644 index 9b2fa51859e..00000000000 --- a/weave/legacy/core_types/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.core_types.stream_table_type import StreamTableType diff --git a/weave/legacy/ecosystem/__init__.py b/weave/legacy/ecosystem/__init__.py deleted file mode 100644 index 265d3d5e9bb..00000000000 --- a/weave/legacy/ecosystem/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.root import * diff --git a/weave/legacy/ecosystem/bertviz/__init__.py b/weave/legacy/ecosystem/bertviz/__init__.py deleted file mode 100644 index 87a014869a2..00000000000 --- a/weave/legacy/ecosystem/bertviz/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.bertviz.panels import * diff --git a/weave/legacy/ecosystem/craiyon/__init__.py b/weave/legacy/ecosystem/craiyon/__init__.py deleted file mode 100644 index b007a23646e..00000000000 --- a/weave/legacy/ecosystem/craiyon/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.craiyon.ops import * diff --git a/weave/legacy/ecosystem/huggingface/__init__.py b/weave/legacy/ecosystem/huggingface/__init__.py deleted file mode 100644 index 2f42a92960c..00000000000 --- a/weave/legacy/ecosystem/huggingface/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from weave.legacy import context_state as _context - -_loading_builtins_token = _context.set_loading_built_ins() - -from weave.legacy.ecosystem.huggingface.hfmodel import ModelOutputAttention -from weave.legacy.ecosystem.huggingface.huggingface_datasets import * -from weave.legacy.ecosystem.huggingface.huggingface_models import * -from weave.legacy.ecosystem.huggingface.model_textclassification import ( - FullTextClassificationPipelineOutput, -) - -_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/keras/__init__.py b/weave/legacy/ecosystem/keras/__init__.py deleted file mode 100644 index 7e87b9429a8..00000000000 --- a/weave/legacy/ecosystem/keras/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from weave.legacy import context_state as _context - -_loading_builtins_token = _context.set_loading_built_ins() -from weave.legacy.ecosystem.keras.model import * - -_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/lens/__init__.py b/weave/legacy/ecosystem/lens/__init__.py deleted file mode 100644 index 68f024d1abb..00000000000 --- a/weave/legacy/ecosystem/lens/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.lens.lens import * diff --git a/weave/legacy/ecosystem/py/__init__.py b/weave/legacy/ecosystem/py/__init__.py deleted file mode 100644 index a6167c9b4cf..00000000000 --- a/weave/legacy/ecosystem/py/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.py import pydoc diff --git a/weave/legacy/ecosystem/pytorch/__init__.py b/weave/legacy/ecosystem/pytorch/__init__.py deleted file mode 100644 index 6e0da8b69d5..00000000000 --- a/weave/legacy/ecosystem/pytorch/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.pytorch.model import * diff --git a/weave/legacy/ecosystem/sklearn/__init__.py b/weave/legacy/ecosystem/sklearn/__init__.py deleted file mode 100644 index 6c3b24135bb..00000000000 --- a/weave/legacy/ecosystem/sklearn/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.sklearn.datasets import * diff --git a/weave/legacy/ecosystem/slack/__init__.py b/weave/legacy/ecosystem/slack/__init__.py deleted file mode 100644 index 24421beadde..00000000000 --- a/weave/legacy/ecosystem/slack/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from weave.legacy.ecosystem.slack.panels import * -from weave.legacy.ecosystem.slack.slack import * diff --git a/weave/legacy/ecosystem/slurm/__init__.py b/weave/legacy/ecosystem/slurm/__init__.py deleted file mode 100644 index 208a2d8e6c9..00000000000 --- a/weave/legacy/ecosystem/slurm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.slurm.ops import * diff --git a/weave/legacy/ecosystem/torchvision/__init__.py b/weave/legacy/ecosystem/torchvision/__init__.py deleted file mode 100644 index f875c477039..00000000000 --- a/weave/legacy/ecosystem/torchvision/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from weave.legacy.ecosystem.torchvision.datasets import * diff --git a/weave/legacy/ecosystem/wandb/__init__.py b/weave/legacy/ecosystem/wandb/__init__.py deleted file mode 100644 index 709aa0d4414..00000000000 --- a/weave/legacy/ecosystem/wandb/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from weave.legacy import context_state as _context - -_loading_builtins_token = _context.set_loading_built_ins() - -from weave.legacy.ecosystem.wandb import wandb_objs -from weave.legacy.ecosystem.wandb.panel_distribution import * -from weave.legacy.ecosystem.wandb.panel_geo import * -from weave.legacy.ecosystem.wandb.panel_scatter import * -from weave.legacy.ecosystem.wandb.panel_time_series import * -from weave.legacy.ecosystem.wandb.run_chain import * -from weave.legacy.ecosystem.wandb.weave_plotly import * - -_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/examples/apps/explore_embeddings.ipynb b/weave/legacy/examples/apps/explore_embeddings.ipynb index cb8a21a4a36..a5e820dc9d7 100644 --- a/weave/legacy/examples/apps/explore_embeddings.ipynb +++ b/weave/legacy/examples/apps/explore_embeddings.ipynb @@ -1,383 +1,383 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "d3f10990", - "metadata": {}, - "source": [ - "# Embedding explorer\n", - "\n", - "In this tutorial, we'll create a notebook-based Embeddings explorer. We will use functions, or **Weave Ops**, defined in the Weave ecosystem so that they're available to compatible types in the Weave UI.\n", - "\n", - "You'll learn how to:\n", - "- Save and version embeddings\n", - "- Build an interactive embeddings explorer using Weave\n", - "- Use the Weave ecosystem to: \n", - " - Embed a string column using OpenAI Embeddings\n", - " - Cluster & project the embeddings using umap\n", - "\n", - "Note: we will follow the clustering / projection approach from here: https://umap-learn.readthedocs.io/en/latest/clustering.html" - ] - }, - { - "cell_type": "markdown", - "id": "dd81428f", - "metadata": {}, - "source": [ - "## Import Dependencies\n", - "\n", - "Make sure you have weave installed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb252cdb", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install weave openai tiktoken tenacity hdbscan" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13a3497f", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas\n", - "import weave\n", - "from weave.legacy.ecosystem import openai\n", - "from weave.legacy.ecosystem import umap\n", - "from weave.legacy.ecosystem import hdbscan" - ] - }, - { - "cell_type": "markdown", - "id": "43771ee8", - "metadata": {}, - "source": [ - "## Download the data" - ] - }, - { - "cell_type": "markdown", - "id": "2c74303f", - "metadata": {}, - "source": [ - "We'll use a pokemon dataset from https://calmcode.io/datasets/pokemon.json." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a297c756", - "metadata": {}, - "outputs": [], - "source": [ - "!curl -O https://calmcode.io/datasets/pokemon.json" - ] - }, - { - "cell_type": "markdown", - "id": "81a36bc4", - "metadata": {}, - "source": [ - "## Read the data and save it to Weave" - ] - }, - { - "cell_type": "markdown", - "id": "a7b73421", - "metadata": {}, - "source": [ - "Weave will store and version the data locally under the name you pass as the second argument to `weave.save`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e68f6c86", - "metadata": {}, - "outputs": [], - "source": [ - "raw_data = pandas.read_json('./pokemon.json')\n", - "data = weave.save(weave.legacy.ops.dataframe_to_arrow(raw_data), 'pokemon_data')" - ] - }, - { - "cell_type": "markdown", - "id": "8352ddb9", - "metadata": {}, - "source": [ - "We can show the data by executing it in a notebook, this will call `weave.show` on it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "614f551d", - "metadata": {}, - "outputs": [], - "source": [ - "data # or weave.show(data)" - ] - }, - { - "attachments": { - "image.png": { - "image/png": "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" + "cells": [ + { + "cell_type": "markdown", + "id": "d3f10990", + "metadata": {}, + "source": [ + "# Embedding explorer\n", + "\n", + "In this tutorial, we'll create a notebook-based Embeddings explorer. We will use functions, or **Weave Ops**, defined in the Weave ecosystem so that they're available to compatible types in the Weave UI.\n", + "\n", + "You'll learn how to:\n", + "- Save and version embeddings\n", + "- Build an interactive embeddings explorer using Weave\n", + "- Use the Weave ecosystem to: \n", + " - Embed a string column using OpenAI Embeddings\n", + " - Cluster & project the embeddings using umap\n", + "\n", + "Note: we will follow the clustering / projection approach from here: https://umap-learn.readthedocs.io/en/latest/clustering.html" + ] + }, + { + "cell_type": "markdown", + "id": "dd81428f", + "metadata": {}, + "source": [ + "## Import Dependencies\n", + "\n", + "Make sure you have weave installed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb252cdb", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install weave openai tiktoken tenacity hdbscan" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13a3497f", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas\n", + "import weave\n", + "from weave.legacy.weave.ecosystem import openai\n", + "from weave.legacy.weave.ecosystem import umap\n", + "from weave.legacy.weave.ecosystem import hdbscan" + ] + }, + { + "cell_type": "markdown", + "id": "43771ee8", + "metadata": {}, + "source": [ + "## Download the data" + ] + }, + { + "cell_type": "markdown", + "id": "2c74303f", + "metadata": {}, + "source": [ + "We'll use a pokemon dataset from https://calmcode.io/datasets/pokemon.json." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a297c756", + "metadata": {}, + "outputs": [], + "source": [ + "!curl -O https://calmcode.io/datasets/pokemon.json" + ] + }, + { + "cell_type": "markdown", + "id": "81a36bc4", + "metadata": {}, + "source": [ + "## Read the data and save it to Weave" + ] + }, + { + "cell_type": "markdown", + "id": "a7b73421", + "metadata": {}, + "source": [ + "Weave will store and version the data locally under the name you pass as the second argument to `weave.save`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e68f6c86", + "metadata": {}, + "outputs": [], + "source": [ + "raw_data = pandas.read_json('./pokemon.json')\n", + "data = weave.save(weave.legacy.weave.ops.dataframe_to_arrow(raw_data), 'pokemon_data')" + ] + }, + { + "cell_type": "markdown", + "id": "8352ddb9", + "metadata": {}, + "source": [ + "We can show the data by executing it in a notebook, this will call `weave.show` on it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "614f551d", + "metadata": {}, + "outputs": [], + "source": [ + "data # or weave.show(data)" + ] + }, + { + "attachments": { + "image.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAADKCAYAAACsc5cBAAAMP2lDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkEBoAQSkhN4EkRpASggtgPQiiEpIAoQSYyCo2JFFBdeCigjY0FURxQ6IBUXsLIq9LxZUlHWxYFfepICu+8r3Jt/M/PnnzH/OnDu3DABqxzkiUQ6qDkCuMF8cE+xPH5+UTCc9BSSgDH+OQJvDzRMxo6LCASxD/d/Lu+sAkfZX7KVa/xz/r0WDx8/jAoBEQZzGy+PmQnwAALyGKxLnA0CU8mbT8kVSDCvQEsMAIV4kxRlyXCPFaXK8R2YTF8OCuB0AJRUOR5wBgOolyNMLuBlQQ7UfYkchTyAEQI0OsU9u7hQexKkQW0MbEcRSfUbaDzoZf9NMG9bkcDKGsXwtsqIUIMgT5XBm/J/p+N8lN0cy5MMSVpVMcUiMdM0wbzezp4RJsQrEfcK0iEiINSH+IODJ7CFGKZmSkHi5PWrAzWPBnAEdiB15nIAwiA0gDhLmRIQr+LR0QRAbYrhD0OmCfHYcxLoQL+LnBcYqbDaKp8QofKGN6WIWU8Gf5YhlfqW+7kuy45kK/deZfLZCH1MtzIxLhJgCsXmBICECYlWIHfKyY8MUNmMLM1kRQzZiSYw0fnOIY/jCYH+5PlaQLg6KUdiX5uYNrRfbmClgRyjwvvzMuBB5frB2LkcWP1wLdokvZMYP6fDzxocPrYXHDwiUrx17xhfGxyp0Pojy/WPkc3GKKCdKYY+b8nOCpbwpxC55BbGKuXhCPtyQcn08XZQfFSePEy/M4oRGyePBl4NwwAIBgA4ksKaBKSALCDr7mvrgP/lIEOAAMcgAfGCvYIZmJMpGhLCNBYXgT4j4IG94nr9slA8KIP91mJW39iBdNlogm5ENnkCcC8JADvwvkc0SDntLAI8hI/iHdw6sXBhvDqzS8X/PD7HfGSZkwhWMZMgjXW3IkhhIDCCGEIOINrg+7oN74eGw9YPVCWfgHkPr+G5PeELoIjwkXCN0E25NFhSJf4pyHOiG+kGKXKT9mAvcEmq64v64N1SHyrgOrg/scRfoh4n7Qs+ukGUp4pZmhf6T9t9W8MPVUNiRHckoeQTZj2z980xVW1XXYRVprn/MjzzWtOF8s4ZHfvbP+iH7PNiH/WyJLcL2Y2ewE9g57AjWBOhYK9aMdWBHpXh4dz2W7a4hbzGyeLKhjuAf/oaurDSTeY71jr2OX+Rj+fzp0mc0YE0RzRALMjLz6Uz4RuDT2UKuwyi6k6OTMwDS94v88fUmWvbeQHQ6vnML/gDAu3VwcPDwdy60FYC97vD2P/Sds2bAV4cyAGcPcSXiAjmHSxsCfEqowTtNDxgBM2AN1+ME3IAX8AOBIBREgjiQBCbB6DPhPheDaWAWmA9KQBlYDlaDKrABbAbbwS6wDzSBI+AEOA0ugEvgGrgDd08PeAH6wTvwGUEQEkJFaIgeYoxYIHaIE8JAfJBAJByJQZKQVCQDESISZBayAClDypEqZBNSh+xFDiEnkHNIF3ILeYD0Iq+RTyiGqqBaqCFqiY5GGSgTDUPj0IloBjoVLUSL0aVoJVqL7kQb0RPoBfQa2o2+QAcwgCljOpgJZo8xMBYWiSVj6ZgYm4OVYhVYLdaAtcDrfAXrxvqwjzgRp+F03B7u4BA8HufiU/E5+BK8Ct+ON+Lt+BX8Ad6PfyNQCQYEO4IngU0YT8ggTCOUECoIWwkHCafgvdRDeEckEnWIVkR3eC8mEbOIM4lLiOuIu4nHiV3ER8QBEomkR7IjeZMiSRxSPqmEtJa0k9RKukzqIX1QUlYyVnJSClJKVhIqFSlVKO1QOqZ0Wemp0meyOtmC7EmOJPPIM8jLyFvILeSL5B7yZ4oGxYriTYmjZFHmUyopDZRTlLuUN8rKyqbKHsrRygLlecqVynuUzyo/UP6ooqliq8JSSVGRqCxV2aZyXOWWyhsqlWpJ9aMmU/OpS6l11JPU+9QPqjRVB1W2Kk91rmq1aqPqZdWXamQ1CzWm2iS1QrUKtf1qF9X61MnqluosdY76HPVq9UPqN9QHNGgaYzQiNXI1lmjs0Din8UyTpGmpGajJ0yzW3Kx5UvMRDaOZ0Vg0Lm0BbQvtFK1Hi6hlpcXWytIq09ql1anVr62p7aKdoD1du1r7qHa3DqZjqcPWydFZprNP57rOpxGGI5gj+CMWj2gYcXnEe92Run66fN1S3d2613Q/6dH1AvWy9VboNend08f1bfWj9afpr9c/pd83Umuk10juyNKR+0beNkANbA1iDGYabDboMBgwNDIMNhQZrjU8adhnpGPkZ5RltMromFGvMc3Yx1hgvMq41fg5XZvOpOfQK+nt9H4TA5MQE4nJJpNOk8+mVqbxpkWmu03vmVHMGGbpZqvM2sz6zY3Nx5nPMq83v21BtmBYZFqssThj8d7SyjLRcqFlk+UzK10rtlWhVb3VXWuqta/1VOta66s2RBuGTbbNOptLtqitq22mbbXtRTvUzs1OYLfOrmsUYZTHKOGo2lE37FXsmfYF9vX2Dxx0HMIdihyaHF6ONh+dPHrF6DOjvzm6OuY4bnG8M0ZzTOiYojEtY1472TpxnaqdrjpTnYOc5zo3O79ysXPhu6x3uelKcx3nutC1zfWrm7ub2K3Brdfd3D3Vvcb9BkOLEcVYwjjrQfDw95jrccTjo6ebZ77nPs+/vOy9sr12eD0bazWWP3bL2Efept4c703e3T50n1SfjT7dvia+HN9a34d+Zn48v61+T5k2zCzmTuZLf0d/sf9B//csT9Zs1vEALCA4oDSgM1AzMD6wKvB+kGlQRlB9UH+wa/DM4OMhhJCwkBUhN9iGbC67jt0f6h46O7Q9TCUsNqwq7GG4bbg4vGUcOi503MpxdyMsIoQRTZEgkh25MvJelFXU1KjD0cToqOjq6CcxY2JmxZyJpcVOjt0R+y7OP25Z3J1463hJfFuCWkJKQl3C+8SAxPLE7vGjx88efyFJP0mQ1JxMSk5I3po8MCFwwuoJPSmuKSUp1ydaTZw+8dwk/Uk5k45OVpvMmbw/lZCamLoj9QsnklPLGUhjp9Wk9XNZ3DXcFzw/3ipeL9+bX85/mu6dXp7+LMM7Y2VGb6ZvZkVmn4AlqBK8ygrJ2pD1Pjsye1v2YE5izu5cpdzU3ENCTWG2sH2K0ZTpU7pEdqISUfdUz6mrp/aLw8Rb85C8iXnN+VrwQ75DYi35RfKgwKeguuDDtIRp+6drTBdO75hhO2PxjKeFQYW/zcRncme2zTKZNX/Wg9nM2ZvmIHPS5rTNNZtbPLdnXvC87fMp87Pn/17kWFRe9HZB4oKWYsPiecWPfgn+pb5EtURccmOh18INi/BFgkWdi50Xr138rZRXer7Msayi7MsS7pLzv475tfLXwaXpSzuXuS1bv5y4XLj8+grfFdvLNcoLyx+tHLeycRV9Vemqt6snrz5X4VKxYQ1ljWRNd2V4ZfNa87XL136pyqy6Vu1fvbvGoGZxzft1vHWX1/utb9hguKFsw6eNgo03NwVvaqy1rK3YTNxcsPnJloQtZ35j/Fa3VX9r2dav24TburfHbG+vc6+r22GwY1k9Wi+p792ZsvPSroBdzQ32DZt26+wu2wP2SPY835u69/q+sH1t+xn7Gw5YHKg5SDtY2og0zmjsb8ps6m5Oau46FHqorcWr5eBhh8PbjpgcqT6qfXTZMcqx4mODrYWtA8dFx/tOZJx41Da57c7J8Sevtke3d54KO3X2dNDpk2eYZ1rPep89cs7z3KHzjPNNF9wuNHa4dhz83fX3g51unY0X3S82X/K41NI1tuvYZd/LJ64EXDl9lX31wrWIa13X46/fvJFyo/sm7+azWzm3Xt0uuP35zry7hLul99TvVdw3uF/7h80fu7vduo8+CHjQ8TD24Z1H3EcvHuc9/tJT/IT6pOKp8dO6Z07PjvQG9V56PuF5zwvRi899JX9q/Fnz0vrlgb/8/uroH9/f80r8avD1kjd6b7a9dXnbNhA1cP9d7rvP70s/6H3Y/pHx8cynxE9PP0/7QvpS+dXma8u3sG93B3MHB0UcMUf2KYDBiqanA/B6GwDUJABo8HxGmSA//8kKIj+zyhD4T1h+RpQVNwAa4Pd7dB/8urkBwJ4t8PgF9dVSAIiiAhDnAVBn5+E6dFaTnSulhQjPARsjv6blpoF/U+Rnzh/i/rkHUlUX8HP/L27XfGtsZL/GAAAAlmVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAhKACAAQAAAABAAABgKADAAQAAAABAAAAygAAAABBU0NJSQAAAFNjcmVlbnNob3QPGUdjAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC22lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+Mzg0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjIwMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjE0NC8xPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xNDQvMTwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CibMm/YAADucSURBVHgB7V0HfFVF1j8hhBASEkpICD303ntV1raIimAXy+qufXVtCKuu+iGia1t1VURXdK2rIgiIoIj0Jr2GloQWQnpCEtLfN/95mZf7bt5L3kteu++dk9/LvXf6/OfOOTNnzswNMgkiDe3evVs+9ezZU+PKt4wAI8AIMAL+hkADf6sQ14cRYAQYAUbAMQRYADiGE4diBBgBRsDvEGAB4HdNyhViBBgBRsAxBFgAOIYTh2IEGAFGwO8QYAHgd03KFWIEGAFGwDEEWAA4hhOHYgQYAUbA7xBgAeB3TcoVYgQYAUbAMQRYADiGE4diBBgBRsDvEGAB4HdNyhViBBgBRsAxBGwKgKCgIMdicyhGgBFgBBgBwyJQTQCEhoYSTodITU2liooKw1aMC84IMAKMACNQMwJB+rOAsrOz6cSJEzXHYl9GgBFgBBgBwyNQTQCgRoWFhZSRkUEQBrqz4gxfYa4AI8AIMAKMgBkBmwKAwWEEGAFGgBHwfwSqrQH4f5W5howAI8AIMAJAgAUAvweMACPACAQoAiwAArThudqMACPACLAA4HeAEWAEGIEARYAFQIA2PFebEWAEGAEWAPwOMAKMACMQoAiwAAjQhudqMwKMACPAAoDfAUaAEWAEAhSBhnWp98lTpykrK7suUTkOI8AIMAKMgI8gUCcBgEPiysvLfaQKXAxGgBFgBBiBuiBQJwGgMhoyeKC65SsjwAgwAoyAwRDgNQCDNRgXlxFgBBgBVyHAAsBVSHI6jAAjwAgYDAEWAAZrMC4uI8AIMAKuQoAFgKuQ5HQYAUaAETAYAiwADNZgXFxGgBFgBFyFAAsAVyHJ6TACjAAjYDAEWAAYrMG4uIwAI8AIuAoBFgCuQpLTYQQYAUbAYAiwADBYg3FxGQFGgBFwFQIsAFyFJKfDCDACjIDBEGABYLAG4+IyAowAI+AqBOp1FpCrCsHpGBOBkpISunDhApWWlpLJZDJmJSpLHRQURI0aNaKwsDAKCQkxdF248IyAowiwAHAUKQ5nhUBhYSEVFBRYuRn5AQKsuLhY/sLDw6lJkyZGrg6XnRFwCAEWAA7BxIG0CGDkr5g/Rs0YMWMEbWSCAMBMRtWtYcOGckZg5Dpx2RmB2hDgNYDaEGL/aghg9A8C88fP6MwfdVEqINQHpOooH/gfI+CnCLAA8NOGdWe1ysrKZPL+qCtXdVJ1dCeOnDYj4G0EWAB4uwUMmL9a8PWHkb8eflUnVUe9Pz8zAv6EAAsAf2pNrgsjwAgwAk4gwALACbA4KCPgbwjk5xfQvn37/a1aXB8HEWAB4CBQHMwzCDz/fy/RpKum0rvvfeCZDAM4lz1799HN0++k+R8tCGAUArvqbAYa2O3v8tonJibRkWPHrdKFgWhkZFOKbtmSYmJjKCoy0spf+4CNZfn5+VQkbPKZ3INARUUF/efjT+kj8YP17o03TLOb0ZdffUPHjh+n8WPH0EUXjaff1qyl9Rs2UdcuXeiWm2+wGe+nQ0m0/2xGNb9HLxpCDRsYf8yZV1RCH2zaU61+wzvE0YSu7aq5+7IDCwBfbh0Dlm3j5i30YQ0jyqCgBjRq1HC64bqpNHjQQI/W8MflK4SdfylNuWayX5iu1hW8p2Y9S2vWrqM2bdrQnNnPUd8+ve0mte337bRx02aKiYmRAuDwkWO0dNlyGjN6lF0BAOa/4lBytTQfHj9YCIBqzoZzKCots1m/qMahLAAM15q6Au/evZsuvvhi6Xr48GH54uuC1Ph400030cqVK+muu+6i119/vcaw/uwZKUb51065SlaxoryCcnLzKC0tjXbs3E2bNm2hzZu30uwX/kHjx43xCAyw6nntjbeovLycrrn6SsMKAFENKjhbSoXppRTVKZRCo4Kdwu+nlb9I5t+8eXOa//47FCtmZDVRg8oRe7DuqtxtxX1y4jDCDwTcL3nvO1vBDOsW07QJ/frg9Zbyf7L1AH22/aDl2Ug3PAPQtRbsv3NycqRrXUwBob5A/EDfSBQVFUV3/+kOHbpEuXl5NPfl12iTmCm8+NIr9MVnH1Or6Ohq4VztgPYA8zc6FZwtoZUPJFODRkF0zRddnapOTk4uvfHm24Rdzq/MnV0r80fioaHmjXGNdFfl7lQBOLDPIWDYCdnevXvpvffeo23btvkcqFwg+whA///cs7PkoWtFRUXCAuWA/cAu9IHg8QcqLxFTAEHhMSHUIMS54zdef/MtOTi5687badDAAQ7BER4eIcNF6K7K3aFEOJDPImDYGcCSJUvo2WefpWeeeYaGDx/uswB7smCYsWRkZjqcZRAFUXR0S4fDuyogTtzs3DmeDhw4SEeOHKWJF09wKunc3Fz65tvv6dDhI5ScfILi4lpTj+7dpMqpfTvrRbiTJ0/Ryp9X0ZmUs5Y8Xn39XxYVUPdu3eSagMWzDjcl+eVUcM6skmkQbM2UyworqGGYGGdZO1flIvh5WbEI09ixsRjCghpFOqf6OXgwgVYI9U9oaCjdcP3UqvxruVPvR6tW5vek6ur+WVuZWKwG1WXhGGLSHuQyUTv/isvKKbShc9jaScqtzsAmWKzgq42LdcmsQKyHGVYAZDrB6OoCjhHjYEQ9afK1DhcdzGDD2lUOh3dlwOBgcydzVi2zddt2mvvKa5SVlSWLgw6QkZEhbdmXLvuJHnrgHqHjn2wpapIQEJ998ZXlGTdYDFY0blxenQVA+v5C2vOfdMo9ISyWBK8KbhxEMf2b0JAHW9OxH7MpaWUuFeeW0+Xvx1NEnPUR02c2naejy3IoN6mYyi5UUFirhhQ3LIL63RZtFhiqgOJaXmqitbNOSpfsY2brqMyDF2j1Eyc0oYhGPNGGwltb56MCrFm3Xt5OnvRHgnrOUWrdOlYGjY3VX2teO3A0fX04MOCFe46KRdYkSskznzbbJjKcrugVT9MGdKuROReKxdmvdiTQvpR0OpaRQyHBDah365bUv00rmtq/m3zW5wdB8eOBRFp99CQlZeYSLHywmNuxRSTdNrQXDW5vrrc+njeeU88X0KdivSEhLZtO55ynBuLdjxXrEaPj29Btw3pTeCPbbf/h5n208/Q5unFQDxrYNoY+3rJf1veCwIsFQA0tWR/pWkOybvMKFrrdKydd4XD6DRvafmEcTqCOAcH0k5KSZezuYuTuKCUmJtHMvz8rdfmwILrnL3dRt65d6NTpM2JGsJCW/7SSXhc6bixAXyxMFkH9+/WlN157hfbtP0ALPvmvdHv91bli5GQecTdv3ky6OfsvM+ECbXzxDAU1CKL4S6MEg29E6fsK6ey2Alq2zWwG2358Uwpr0VDkVZU6FnG3v51KJ3/Lo4g2IdT58iipz0/bU0iJy3ModXs+TXy1Y7XF3ZIC82i4KiUivZupehBLcCy8gyZMGGdxc+SmT6+eQkBeJSyG4mRwXPEMd1dThQDnxZ+30KakFDmy7SOYN+hAaib9Z8s+OnQuk17442jJ+PR5w/Jo7qptlCqERmTjRoS4RUKY7EvJoC3JZ2mzSHP2lWOoaeVaBuJDYDyzbAPtEQKjWVgo9YptSRGhIXQsPYf2ifSeXLKObh3Si+4a2VefncefIaBeX72DSkTf6dwyii7p3oHSCy7QUVHWb3cfoV8On6D5N15KLcPDqpXtbF4+HRFCA9g8tm0Nnc7Np+6tmlGriCbGEwDz5s0TaoMjtH69eUSzbNkySk1NtVT6ueeeo3YaNcCePXsI6iJY9+CHUW///v3pkUceEeaIoyzxbN3gYDCk/f7779PatWsF00qiQYMG0cSJE+mhhx6imiwhbKWn3M6dO0dvvvkmbdmyhY4dO0a9e/emkSNH0owZMygiwqxzVWGduTYS5X3+H087E8UrYT/48GM6f/68WIwMqdEEUVs4qLfA3CE8wPxf++dLcjETYTrHd6KZMx6XJ5Mu/mEpvfPv92nE8GHiTP8wAoMfOmQQYXakaMjgQXVuO5XGgS8yCAx3/Ox21KJbY+ncfUpzgnvCt1nU++aW1OvG6uq1xBU5kvm3HR1Bwx6No+BKPX6fm4l2f5RGx3/Mof2fZdCQh6pGnghzhZhFgE5vPE9bXz1LbUZG0KiZbaSbI//OpKTIYB3aW6vIaovbrVtXenrWDEuwpuL91D5bPFxw8/baXZL5d4luRnMnjxPMzIxrVmERzVq6XvohzN8uGmyVG/yf/nEDFZSUyZHw7WI0jNExCGqODzbtlaP878XM4o7hfSxxm4SIWVdUBPVv24qmi9G+VtWEGcTfvv+Nvt6ZQBMFs+0kZgTepPgWUdQ5OooeEaa0XQXzVoQZzD+F4PtZCIB5G/fS05eNUF7VrphZRYrZzQc3XELxQoiADDcDePfdd2n//qqt64qxq9r+9a9/lQIgOTmZnnzySVq4cKHla1Vg2NgEc+jQIfr+++/p008/pZtvFj3PDn333Xc0c+ZMi7oBwU6ePEk//PADrVixgr7++ms52rQT3aYzTETvuOMOghAAtRSbo3755Rf5++abb2jx4sXUs6frR1c2C+NGR5ytf+x4oszBJDDPyhajWyFMl//0Mx1KSJDuDz90v9TfO1IMLBZjFA/68913Wpi/dKj896c7bqOfVvws10Gg91dmqNowrrivKDNR5uEiihnQxML8Vbrdr21BCQuz6HxKiXKyXEvzK6SAaCRMN4c81NrC/GUAwa/63d6KTm04T8mrc6mvUAXZMvFUi8ANhbrJUcI3DmCdBusfrJf4ImGUv/TAcanGeO6KURbmj7K2aNKY4HbfN6tkmEt7dpQjfFWPd9fvpvziUqnmuVPD4OEPtchjYgNap+bCLFmokPT05MSheif53FUIoacuGU7P/7SJFu89Vk3o2IzkgOPWE2fpm11H7Ia8um8Xm3sJwLDfmTaxWjy8BY+LOiRn5Um1zgNjB1BzgZctgqB849qLqH2zphZv8zzY8uj7Nx9++KFkll3ETkTQrbfeamGgYKSdO3eW7njZf/zxR+rTpw99/PHHdPDgQTkKhPXQ6NGj5cc/HnvsMauRoYyo+XfPPfcQ8lm0aJEUAhAcTz9tHmH/9NNPcgFaE7zW2wMHDtDkyZMpPT2dnnjiCcI6BvTXsI+//fbbCfsOsH/AHwjM/q4/3yd/d9/zAD351N/lCB7Mv0WLFvTwQw84pXs/lmgWJtHCZNTexiWM9gcO6C/hO14Z3h1YlhVVUIXQy4faWIjFYm6wMNEsyatucpp5RHw+UwiBuCHhFNKketcLDg0yCxQxszh/proAQV3KKxeBg0Orx7dXVwx6QJhx1XXWai9tV7kfFAIANL5LO2orRuV6aiPc4AdSYXFfKEb4a46dokZiTenOEVWje/hpaapg/o6LTHPMUZ3iqLHgI2eEysRVlFlQRLvPpNn9peUXOp0VZi7jKrGpqawXdW1vxfyRkeFmAFCVgJo2NUux+Ph4uuSSS6Sb9h/UQBs2bKCBAwdavfT9+vWTo//27dvLESnUQzfccIM2quX+8ccfp7lz51q+EYvNMy+++KJMb/bs2fTRRx9JS6RWrVpZ4tR088ADDxD2GWBWgXQVIT5mIxAQmzdvpuXLl9OkSZOUt8NXqEnUVN+RSLACatvWcTWCI2mqMI0bN5b6dzxjLSUqKpJat25NUEFMGD/OYl+uwtd2hbUPqLYRrPLH4q+7qFFEMDVt14igt4cw0FrwpO0poPIiE7XsXV0Xe/60manniIXfLa+YVTL6MuadNIfJFzOIaBtpVM0AHBcAaAt85hJfccvIyPSK5Ze+nvpnxdS7adQb+jDw++mQtQBIFAu3oA7Nm9pdBNWnY+sZ+WMWck7oybMvFFOwWNuJEOsFRaK/QofuKrqoW3sa1M7+AjrWL2oiqLQ2JqZQihBK58SicInYZNk4JFgcvWEWoGdF+fvGRdtMAgvbejKcANBXoKbnwYOtdYUqLCwaIBh+//13Onr0qHKudoUKSX0gROsJNdMrr7wiP4gOAXL33XdrvW3eb9y4kdatWyd00s3p+eeftxkGM457772XVq1aVScBAD33tdNuspm2LUd3WgFBqEFP7yo6f97cCcNr+Vav+pavCu+q/PXpdLgokg4IXT0WgvvcGm1ZBN417xxhJN92RPVRbL7YwQsqzCiV1kH6NNVzY7FwjMViW6QWep3dAxAnhC/O9Dl1+rRPCgDFyLXqCX39lZ8KC/9TwhoG1FpYCtWFsEj83obddCQ9W0bHIjGYcHmFiXKLiqUbVCeuIqw74OcswezzA6HjxzlLsN6BhVOzsMZi5tNAPqsyqqut9GPEoq+enC+JPgWDPGN0nCjUAlg/2Ldvn2Xh+NSpU07XAMyte/fuMi2sNThCWIwGTZgwQS5E24qj1FrHRUetC8EKaOq11zgcFWoyo1DHju1lUVNTzWsn9sqt/Dt17GAviEvce05rIQVAxv4LwkSz6h0KahhEY55uQ5EdQqvl07hZsHTrc3M0dZlUtZBXLWANDg0qm6zSiKmGkNZeI0YMkwJg567dDm8Cs07BvU9Q+8C0EaaftodtZDEL1aqIYMkCwsjXWdpzJp1mLVsvF3//NmGw0L23l8xfm86Uj36QljdaN0/fQxi9sGKzXASHyedNg3tK81atSmuRWKf49/pdNRZNa42mAhqHA6gSO3kFs4eq5vPPP7cs5kIPqphfcR1PnYQ6A8LEUQGgFq4xum/btq3NWmDhFFQXoYR4sAKa9dQTuPU76hzfSdYJKq48YUEUWakC1FYUQv6w2FgGio/vJP67j5J/FaoH0QMH3RsjNnKZqCC1hFr2CKNW/ZpQWEvb3QpqI1D28bqPKBsIAQNSqiD54MC/CePG0hdffk3fLVxMt0+/xebM1oFk3BYE9vpYID0urG/skfJDWEUwiQSdyj5P54tLrMw8VRh71w837yXsO5g7ZRwNEJZAesLCMtL09sYw2PDDNBY2/P8nzGBtmadDJVQXclyRWJfUvRzn73//uzT5fPvtt+WI/Z133pE69jxxLMBll11Wr9Kpb8aqj4jXlpjauAQb9a5du9r89erVi8aPH08jRtg35aotH3/1H9C/nzSRhZD87rtFNqu5dt0GOnNG2OaL4fHoUdYY4jhqRSfE7uD60glhx49F4PjLm1H3a5oLQRBLUAvZY/7Ir3lXYZ0helzKVnHcdU71RWJHyhQaZRYuBanmwYIjcRBm0KABwtR4hDQ6+GXVakejeSxc79gWMq8NiWcoR+jg9QQ3+IFUWNzDQggWO7CPxyYpe6TXqGEPwGFhGx8m1DEwA7VFC/ccseXscbddp9NknsM7trbJ/CGkYAZaF7I9VKlLSj4W57XXXpMLrdj1CNPKiy66yKUlxJ4AUI8ePRxKF8wdNGDAALnI61CkAAykGLXWbh8woB3v+fNd9Ma/3qbPv/wftRGL11dcdokFob1798vTPuGA0z5xNISWoANXtHnLVorv1FE91umKDWDY5bv+udNiB284hTYNlpY9YNCNWwQLQRAiNolZJx0eG0JdJzenY0uyacc7qTT88bhq1kBSsAgz0daDbeu01cLwWbFhrCirTORl7sI4ciLh+yzqI/YfBOmOo1ClwOzwxptvp3fenUfDhw31qbWAAWJ0O6JjnJwFYDPYy1eNs9jlQ//90i9bCfpthEFYLT128VB68LtfafH+4xQpNnRhB692lLxRCI7Pth+iV68Zb5khhAqrIVjP4GjnTLGhKlq3gQqWOjWZa2rzd/d9uNicBjqTU32Uj/K/uWaHMIM1Gw84Wxa/FQCw0QfNmTPH5cz/tFhIU2oaRwUAzFFBWHjG7EGpoKQj/7Mg0E/s3IUN/4aNm+URDrfdWrVPA4wdOmycZf/S3H/SV19/QzjLB20B1Q82ifXs2YPuFTuE9dSqVTQNGzqEft++g+Z98JH4sMk6GbesrLROarPeN7WktXsLKb3yp8+vSUxDadffbmzVzANhwKCxYzh1RwH9/GAydZjQVFoUlYqdvmDq6fsuUIuejSl2ULhgYvpUxemcQjhE9w0jrD38NvMUxQ0Pp6LsMhkPpqet+jah2IHVF/uQUhuxB+CB+/4shOg7NGPWM/TBe2+7RRX02e8HLTp57cj7DcGocH4NKE4s2uL4AkWwuvnH5SPp0cVrCCPeaR8vpTFC343gsHrBKLdHTAsZBmG11COmOd0/ZoBYJN1Dn247QD8nJFMfYQnTVOwBOCB2D2MXLEb6R8VVHe2ANCb1jqfF+47RjB/W0fUDu1OsKBN2y2IX8C8iDWwA+/XISW1W8h6L0N+J3beKsGkMtPVEqtXs5TqRplJRqbB1uU7s1oH+t/MwLT+YSI3EOUVDxfEU2DWNoytWinLCYKCXmEEdOmc+HsWZPAwrALCBCpRQualIW2nYPatFV+2uYBUG/tnZ2erR6eurr74qN5ShDI7OLLD4i1Es7P7feOMNuevX6YwNHAH6eUdouGDSXcReDtjxf/X1tzT9Fmurpheee1psJhtK8z9cII+TSEpKlsnC+gcfmbn9tlsI5wzZym/GE4/SP55/UW5EOywOksMP5pFPPPaIlUBWcdVVX+78lFLa9PIZajM6XKh/WlC+OKK5RNj3lxaWU7FQ7eSdKpYMeeu/UigqvpM48qHKtA/nBU18tT0dXpRNR5dm0+HFVZ0Wfp0nRVHf6dEiS5NdS6CRT8XRZmFGmnnoAh1bJkZ+YqbRLD6UBt8fIzanhdmsu6rDDddfJwTsr/LspGefm03/eGamPJlV+bviukXoqxPSquql0lwlmJWinoKZY/etlqBrf+nKsTRf7NxdLRjvSmHxAsJI/bIeHeme0f2lPt5Wu0zt35X6irWBd9btlAz/bKVOHHEHixnDoxcPkUJHG/e+Mf1F2kG0ZN9xem3175aiwPzzgbEDhcBpTr9CtSLeXW289POFlrJZIglJlZyVK3/KDXb3rhAAEJYvTR4r6raLFu09Kn8qjyFCGOBLa/8SwrVOJCrmNAkba9P2HbucjufKCML0EhzFJEw6TeJ0yGpJiw1h0v/qq6+28jt79qzpD3/4g/RD/DvvvNPKX4zQLX5jx441icVbK3+xjmASDEaGeeutt6z88HDbbbdJP1z1JI6vln7iNEyTWJjWe5tOnDhhEuqqau6+5iA2rpnwE2fs2/wJe3NTfX/iS1SmDRs3mcQOVrtpJSYmmVb/ttZ0KOFwjeG0ZUF6R44eM636dbVp27btpqys7Grpi13aJvzEQMHmb8OcU6ZvpySYivPLbPoj3vZ3z5q+vSbBdHxltt0wCHchu9SUtr/AlJN8wVR6obzGsPrylBaXm/JOF5nKxFXvV9Mz6vzo40+Zho4Ya5oy7SaT2GHtVPya0naVX1FpqSk5M1f+cO9Mugh/8GyG6VBqpqmkzH4bqTSF+sR0JC3LtDU5xZQg4pSWOYenSkd/dXW/FaN+k9iTYNp1+pxp56lzJqG6ciiLMmFGVFJZJ32EIDg4KzmST5wUu1izaMjggc5GdVl4bJjCjl4Qzs+ZOnWqtPLBeTrjxo2TG7ZwXDQI+nd85Qs7bTdt2iTDDx06lLCbVwgAWrBggQyHf9u3b6dhw4bJ3arYA4DduthPgB3G8MPZPSAsIuMcIv0+gfnz50tbfoz2URYsRCsSLwhNnz6dvvrqK+mETWkoB0av2KmMjWtID2agsDLyVcJOZpD23KI6vEa+Wj15bAIKZ2+D30/3JolF3DK6+vOu1sc5aGp08OtMOvS/TBr+WBy1H2etBtIE8+otjtR++9/vyXdu1cplcubk1QL5cebaNQlfqmbw84KcLVCOOI8dH++GTtFbhJ28UMHArBILhlD54JA4nKMzZswY+cOXucC0cdQCdO9Qv0ybNk0eDgdrHJz1gw1hU6ZMsVQjRZgZ4rgJqHaQNvTLuOIICVjyQGUApo4wtvT4YBqrV6+WZwbt3LlT7vpVieMlQP44/A3lQXlxlhHuYU6KskA9BAHkqy8M6qK+dqYXfqqe2qsRBQPOzgGpTWXa+uA+8/AFyhO7eQszS6llzzC58UsbBsdEH/g8Q5qJ9rtTHO/s4Fn/2jQ8cd+nt7A6E+ahsGQbMXyoJ7L0+zwc6beOhPEUUIadASiAcKokbP1h2onNWeosIOWPQ9dgg9+smTgnRjBYdQ698nfkii30+PIYzrDp27evQ2ngpE8IJuj+7TU4RtIQADjWAkIBQskIhHKDsWtnAKrcjjL82sLV5q/yc8cVB6eB7M0ALmSW0aaXUig3WZgrCv17pLDvxyavciE3CtJKpXVOSHgDGvrXWHnGvzvKyGm6FwF7fVblWpt/beEcja/ScdfV8ALAXcBwuvYRsCUAtAxb3aur/ZR80wcCAB0UB8/ZI1O5idKEJQ7O78cH2otzxReaxCFwsP6JbB9KnS6JqmbiaS8tdvcvBBRzV1fUTnuvamvLTfl56mpYKyBPAcT5VCGgGDpUX9iQpZ4RQt2rq9atKgXfv0P58UMdtXWpVnIx8o/pHyZ/1fwqHWqMby8Su/sNAmh/PZPXPqv3Q+vm6cqzAPA04gbNT72sKD5041hfga4c6wBaP3Wvrqq6+ufa3JW/p68QbFisx0F5zn6u0tNl5fw8j4A9Zq11x7uufUYp9W5af72fJ2vFAsCTaBs0Lz3zBtPHYjjWX8AstYvBKqz2qu5V9fXPyt2bV5QJG/Qg1CDgMANA3ZgYAXsIaJk4wuBZ76biwh3vGH44iwxXbVj9s4rn7isLAHcjbPD08WJqCc/44Yx5vMiwCMIiuWKWyh9xquKKl1/8gdQezio/6VztX23+2s5TLXIdHJAeBBkW5GEVU1v+dcjCKoq707fKjB+cRqC29wv+aEPVO/BtDVPlWd0qLq76D/DADX1FG0YVDukpd+Xm7isLAHcjbOD09UxKy+RRLTBMWC7JjlApGOAuO0Xls0pDXeGvJXvu2jCevmfVj6cRN05+9hi0cldXwckxApJqRLipH2qKe/Xe46oVEni2pOEBWFgAeABkf8hCz/zxouIHd1zV2F49m93MswAV1hYOKpzy0z8rd3ddne1sni6fu+rN6TqGgP790D+rVOCufnBT92Z1D/qBedSvmD388S7hij6j3FV6nrqyAPAU0gbLR8vo1L32qmf0FRXl8oXG3gfsuMZpqfguMPZhgFRcd8CATqQI9+hMuNr6qY6mDafiasuo7nFV99pwyk3rr9yADQjPCidtXHXPV/9BAO8TCF8bxC7++Ph4eVIwVKXqXVPvg3rGFW7aK9JQbrh3N7EAcDfCBk8fLyN+inCvZWrKH1dsasPOa2yCw+5rcUaTtBZScd11RQfCD6S/V3kqd3049Yxw+nqquFp3vZvyU1db6Sg/XNW9Soev/oUANpziGJiYmBjx/YWR8qgXbEBFn1HvIN4BPKuBivad0b6PnkCGBYAnUDZoHlqGpV5as5uZWSp/fFUNZyx9+eWX8rOb6qwgT1VblcNT+XE+jIA9BGAejZ842FEe74ITBG655RZ5bhlMi5UggMpU3StBgPcY5EkhwALAXksGsLsthqpeTry4RGa9P15guK9fv15+dhNnG6kvpQUwfFx1RkAigIEQjp2HuTQMCyZOnCiZu5nhg9mL3YSC0Ie0TF/1Na2bDOiGf+YSuCFhTtKYCKiXD6VX97jKn3QzM33lhkPycJoqTjNl5m/MNudSuw8B9An0DfQR9BXVb8xX0Zdkn6pSDcJdkfZeubn6ygLA1Yj6QXr6F08+Q8deKQjUyB86/qVLl9KhQ4f8oNZcBUbAfQigj6CvoM+gP6k+hD4lhv/STZu7vg9q/Vx5zwLAlWgaPC28dPoXDy+qpEo/FQbuGNmsWbOGR/4Gb3cuvvsRwEwAfQV9RjF/1ZekEBBFsPS1yuJY/N1YPBYAbgTXaElrmb/+5VPP6oqXdceOHeTpBV+jYcrlZQQUAugr6DN6AVBbv1Px3XFlAeAOVP0qzarpqZb5i68ZSp2mX1WVK8MIuBkBrAOg72iFALI0CwGzKbObi2CVPAsAKzgC90E7CgEKitlXmtdbvbDwgzu+lsbECDACjiOAPmNeTqs0rBB9Sal+tO7aFPV9U+tX3/t6mYEmJibWN3+O7yMIqJdQFUcKAPEgvsYthQH81a8cbuKHT2QyMQKMgOMIoM9gl3yQ2K0eLH4wCVU/mH3CHfMAvQkowriD3JOqO0rKaXocATUhVSMQXDF9xaIV7vnQNI83CWdocATQZ2R/quxL2r6Fqqk+56lq1msGoP/+rqcKzfm4HgHtDAAvpfZnGfmLlxf3eInx049SXF8qTpER8C8E0Gc6dOggvyuO75NjZK+uuJezAMwEKn+q9jwDUEjw1eUIqFGISlg+ixcQpPzMAqFqbUCF5SsjwAg4j4C5P2GgZd3HZEqi76l+p1LWPyv3+l5ZBVRfBP00vnYqql5W7fwULy4TI8AIOI+AVd8RHc3SvyqT0vY951N3LgYLAOfwCpjQNkccGtWQVhgEDChcUUbAFQhomb6VNDAnbrPvuSJfG2mwALABSiA72X/5zNNSi7+NFzeQceO6MwIOI1DZd9CXzP3J9pjf0tccTtj5gCwAnMcsAGKYX0jtCyheVU29q+soNZ58ywgwAjUgoGf62r5V1edsC4Uakq2TFwuAOsEWOJHwQla9lJX6SiEMtG6BgwbXlBGoPwKyT+n6kL6f1T8Xx1JgAeAYToEVSjf4wMsJJ8X01TWwQOHaMgKuQ0D1IX3fsuSg64MWdxffsABwMaD+kJxi9ng51YuqrZfy17rxPSPACDiGgGL6+tCqv9nz14d3xTMLAFegGABp2BIEAVBtriIj4HYEvNm3WAC4vXn9KwMxKWBiBBgBFyDgC32JBYALGtLISXhz9GFk3LjsjICnEXBHX2UBUNmKALekpISKiops6r1VY+MsnOLiYhlWufnL1f4LZr0iZT+cvyDB9WAE3ItA9T5k3cdU7tXDKR/XXFkACBwzMzNp9W+/0YynZtG0626Uz7bgBfPfsXMn3f/AQzR79pwaBYWt+EZxc/dLZxQcuJyMgLcQ8FQfrNdpoN4Cx9X5Hjx4iBYvXkIHxYebExOTqLS01GYWGRkZtHDh9/T779vJNHSIzTCB5MingQZSa3Nd/REBngGIVh0yZAg9+8zf6eqrJtttYwiFn39ZRefOpVHvPr3lxxzsBvZTDzkqsT1T9dMac7UYATciIPqSp0b69mrBAkAg06RJGMXExFBk06b2cKITJ07St98tpEsu+QN16tiBKnxhCd9uad3v4e0X1/015BwYAfcg4Et9hwWAg20856W51L5dO5p67RRq2JA1Zw7CxsEYAUbAhxFgTlZL42Dh97PPPqcjR47S5599QmFhYTXGeP/99ykvL89umNjYWBo1ahSFhITYDeNJDzUawVXdI3/co+74YbJTUVFOZWVlcuZTUflFMG14T5aZ82IEjIoA+szJkyfNXwHDF8HEx18woGzQIFh8BYysvg+s6qj9Opir191YACiU7VyxQPyvt96hK664nM6lpclfqlgHyM7Opi1bt1KPHj2oRfPmltgdO3akgoICy7P2BgwUjZ+YmEiDBg3SenntXjFxXLX3KJASAPgOMASA/BQkhAJ/EtJr7cUZGx8BDCLlZyCFAMCH4c2fhMTnIYMsAgC1VMzeFQIAfRfm640aNbLSYLAAqOV9OnrsmGTy6enp9PHHn8jQh4S1EPYLLFjwKT1w/31WAmDSpEl2U8zNzaV58+ZReHg4tW7d2m44T3pomb7+Xi8AIMDKKwUA7tUL6snyujovdAjMxtA5UCdHCW0IsifsHU2HwwUWAugzrVq1Mo/6KwWAmgFoBYCe6au+pq7OogZ+lZKSQpGRkfKn4rMAUEjYuY4fN5b69O5tGR0j2Ftvv0MwCX3s0b9Ru3Zt7cRkZyMgcOmll9LFF19MP/zwA61fv96hIqPDvvDCC3IQ8MwzzzgUhwMxAr6IQMALgAsXLtDhw0ckQ084fJhMYoS7fsNGai109VDvxMbGUMuWLa3arnnzZrLz9+jR3S9GwVaVc+KhrqMRJ7Jwe9A+ffrQtddeS/v373dYAGDKjjhY62EB4PYm4gzciEDAC4C0tHR6481/0a+rf7PA/OhjT1C0YPqzZ79Q494ASwS+8SgCjzzyCOXn59N//vMfj+bLmTEC/oZAwAuAjsKm/7+fLnCqXee8ONup8BzYtQjceuutYl/GCRYAroWVUwtABHgfQAA2uquq7A0VEPTvzZo18xkzWldhyekwAt5AIOBnAN4AnfOsGwKXXXaZ3LENHXyLFi3opptukgnhmI6FCxdaEo2KipLHe7Rt25bi4uKkhc/p06dpxYoVdq12YPEEwTJ27FiKj4+XAgZx1q1bR6mpqZa0Hb3p3LkzDR8+XBgJtJP5nzlzhlavXk05OTmOJsHhGAG3I8ACwO0QcwauQuC2224Ti/KxMrno6Gj605/+JO+xHqAEwIgRI+i6666j7t27S5tq2D83F/s0ICSw4PvPf/5TLuDry4Tws2bNot7C4gvmoDgaBKah2LT33nvv0dGjR/VR7D5PnDhRCicw//Pnz8twEErDhg2TaZ06dcpuXPZgBDyJAAsAT6LtZ3l5WgX0zTffyNE5FoGxL2P+/PkSUTB3RREREdSlSxf6+eefCYwWDBgC4J577qEbbriBNm3aRKtWrVLBLdcJEybQgQMH6IsvvpAWYU3FuVBXXnml/EGIzJgxQ26Ms0Swc9O1a1dC+bBP4JNPPpFrFZix9OvXj+6++265eD1nzhw7sdmZEfAsAiwAPIu3YXJzhLmrjWOeqtTSpUvlKB4MFjux1ahfm/+ePXvo9ddfp71791pG3w3EbktsgHn22WcJaiRbAuDgwYP07rvvSiEAdRDomNgEiFkD9gr07NmTEKY2+stf/iLNhzGbWL58uZxFIM7u3bupb9++dM0114gNhAvkppza0mJ/RsDdCPAisLsRNnD6jggBX6seNuht3LjRwvxRB6wXYMYAAiO3Rdu3b6d9+/ZZjfLxvFUc94FZxeDBg21Fs3KDkLn88sulumjlypUW5o9A0P2vXbtW7inp1q2bVTx+YAS8hQDPALyFvB/k66sCAkc7YLSN7zy0b99eqoDUZj4s9NoirRpJ649R/1VXXUWdOnXSOtu8RxiojqDymT59erUwivHjvCgmRsAXEGAB4AutwGVwGQJg9FDDQOcOVQ7WAbCAi9H8yJEjrQ7CciRTnKECaty4ca3B1QJ1kyZNpPDRR8C5Q1BRqYVhvT8/MwKeRoAFgKcR96P8fHEG8OCDD9K0adNo0aJFtGbNGkpOTqasrCx5jPfMmTOdRh/WRqA0cRJsbaRMPKGGwgKwPcImNiZGwBcQYAHgC63AZXAYAbVAa+t7CrD2mTp1qrTi+eijjwh2/IocGcGrsOqKxeOhQ4fKxyNHjihnu1cc8w2LIaw5YBGaTwq1CxV7+AgCvAjsIw1hxGJ4YwaAI7VBOE5bz9Sh34cOHuU6e/asBVLo5GHmWRNBLw8VjZZwSijWEvANh127dlm8IIRgAaWvPyyTfvvtN8IGNJicIl89hYaG6p34mRHwGgI8A/Aa9JxxXRDIzMyUI3swWdj2b9myRdrcw8IGDBg/CAdsGoPeH5Y5AwYMkBu6SkpK7GY5ZswYeuKJJ2jHjh1SRw97fqiSIBT++9//0rlz5yxxMcqHSggjfWw8g6UQCIIBX4TDZrI77riDsPlr586dskyYnWBdApvLMDthYgR8AQEWAL7QCgYtg34E7IlqgIHiFNCHH35YWtqMHj2aMKrGR3rAlMFc77rrLrnpCgvAODsIQuH777+Xu3vtjcDxPQBYDWGhGIICC7oYwf/73/+mJUuWWFUNjB4j/euvv54ef/xxuvPOO6mwsFCGgdB5+eWX5W5kHFUxbtw46YeNYRAm2KDGxAj4CgIsAHylJbgcDiMAhoyz+KG2wef1YOMPax0wZuwWxpePcBYPdPhYB0hKSpIbsSAI9N90hr0+VDw4J2jbtm1yE1ebNm3kwjHO79m8ebPN83s+++wzaV2k/7IbZgdIC/nieAkIElgFoYw4U8iRzWQOA8EBGYF6IsACoJ4ABnJ0b8wAgDesbSAEoO/HqBpmlcpcExY/y5Ytk6oflA9rBhAMIFujb5hl4gf6/fffpcoG6hos4OJjQfYoISFBChaoeVTeKiyEANLEQrAqIwRWTSooFZevjIAnEWAB4Em0OS+XIQCmrhaE9YnCT5lk6v1qewbzhhmnIwR1VE3moVgoBuNnYgR8FQG2AvLVljFAubw1AzAANFxERsAQCLAAMEQzcSEZAUaAEXA9AiwAXI9pwKTIM4CAaWquqJ8iwALATxuWq8UIMAKMQG0IsACoDSH2t4sAzwDsQsMejIAhEGABYIhm8s1CevqDML6JApeKETAuAiwAjNt2Xi85zwC83gRcAEagXgiwAKgXfByZEWAEGAHjIsACwLht5/WS8wzA603ABWAE6oUAC4B6wRfYkVkABHb7c+2NjwALAOO3IdeAEWAEGIE6IcACoE6wcSRGgBFgBIyPAAsA47eh12rAKiCvQc8ZMwIuQYAFgEtg5EQYAUaAETAeAnwctPHazGdKjBlA9JA7faY8XBBGwNcRwEeKfIlYAPhSaxisLBAAEfE1f2zdYFXi4jICAYWAb4mjgIKeK8sIMAKMgHcRYAHgXfw5d0aAEWAEvIYACwCvQW/8jNkKyPhtyDUIbARYAAR2+3PtGQFGIIARYAEQwI1f36rzDKC+CHJ8RsC7CLAA8C7+hs6dBYChm48LzwgQCwB+CWpEoCYmX5NfjYmyJyPACPgEAiwAfKIZfLsQzOh9u324dIxAXRFgAVBX5DgesWDgl4ARMDYCLACM3X5eLT0LAK/Cz5kzAvVGgAVAvSHkBBgBRoARMCYCLACM2W5eLTVG/urn1YJw5owAI1AvBFgA1As+jswIMAKMgHERYAFg3LbjkjMCjAAjUC8EWADUC77AjsyLwIHd/lx74yPAAsD4bcg1YAQYAUagTgiwAKgTbBwJCBhpBtAkNJjCxI+JEWAEqhBgAVCFBd85iYBRBEDDBkH08JSudO+kzk7WkIMzAv6NAAsA/25ft9bOKAKggRAAlwyOpYsGtHIrHpw4I2A0BFgAGK3FfKi8RhEAroasd4dIun9yF+oY28TVSXN6jIBHEWAB4FG4/SuzQBUA3ds1patHxVFss8b+1aBcm4BDgAVAwDW56yocqAKgWURDCmnYQCyCuw5LTokR8AYCDb2RKefpHwiYTCaPV6Rn+6bUpU0ErdmTTsHBQTSkazNqG91EMOQgSs0uoh1Hsiktp9jpcrVtGUYDujQTo/pQGfecSGNvYg6dzrhgSQthendsSr06Rkq34T2aU3SUOfzBE3mUlFpgCcs3jIAREGABYIRW8rEygvHj540ZQP/OzeiqkXEk1nWpl9DFd24dTmUVJoqObEQlZRVCIDSnL1afdIoZj+rVkv44vDV1jGlCxSINUKgY4Y/q1YJ++j2VNh3MlG5d20bQdePaUUxzs+pnVO9oGtilXPqVlFY4laeMxP8YAS8jwALAyw1g5Oy9IQAimzSkuBaNaeqYtnQq/QIt3XqWss+XUHjjhtLKZ1y/aCorN9Er3xyWQqo2fDvFhtPtl3ak1iLNRRvOUGLlKD5eCBbkgRH+2awiydwxwv9+YwpNHduGmoZF0Pr9GZR41jzqP3LmfG1ZsT8j4HMIsADwuSYxToG8IQAUOqeEagYj/aMp+VQhZgCgk2mF1E2M0kf3aUld4sLpmPCrjW6Y0E7G+XrNKVq06QydLyyTUfYI9U8jMQu4+eL2dOOE9vTy/xJk+shjbF+kH0E7j2bT1oSs2rJgf0bAZxHgRWCfbRouWE0I7E/Oo8Onz1uYP8Liec/xXMKu396Vevqa0ogQs4YxQliA6S/ZnGJh/ogDt6VbUiivsFQKlKZi5sHECPgbAiwA/K1F3VAfe4u9BQXeW/QsKzfr6vXVVaP+ttFheq9qzwgD1RFUPLYWjuGWklkkBUo7sdDMxAj4GwIsAPytRV1cH3vMH9kUFRW5OLf6J4eFYFBoSO2vdnRUIxk2J79EXm39U35YZGZiBPwNAZ7XVrZoaWkpZWRkUmFhIXXs2IEaNrSGJi0tjXJycgnhIppGUFzr1tSoETMFX+sQzSJCZJGy8uwzdVXm3IJSedtEzALskfJTYe2FY3dGwIgI2H/zjVibOpY5KSmJ1qxdR2vWrKVTp07T/77+glq1Mp8bU15eTlu3bqOvvv4f5eXlUVZWFjVu3JhuuulGmnrtFK+YQtaxmi6PVtPswOWZOZBgA7Ezq28ns41+8rna1VOwIoIJabtWYRQVHkJ6Jh8p3Dq0akLlIgwWfxWpRWdsBmNiBIyMAL/BovWysrMp9Wwq5efnU1JyMpWVmS1B0LDlQte8+IclFB/fie6/716a+dQMatu2Db311jt07tw5BGHyAgLYlKVnwCOE3X63tk2lTv/gyTxLqSCoYCek37l7XizwwoonqkkITRL7AHBonCLcTxrWWgqGbSIMFoMVnb9gfj/aObDOoOLwlRHwRQR4BiBapV/fvtSta1f6/Isvaeeu3VbtFBLSkO679x6KjY2l8PCqhcCNGzfT3n37qLVQBTF5HoHhPVtQqRDOsAYqLCqjDjHhdNmQWGm6uXhTCmXkVqmAMILPzCumZmJEP0BsJIOJJ0g401e/naLuwnR0sthcFiyYfsIpsz0/dhz/UQgAxEMYhFV0WIS5Ymhrwp4D7D5Ozy2mggvl5MisQ6XBV0bAFxBgASBaAbp8/MKEakdPsHXv3DneyrlRaCM5WoyIiLBy5wfPIZAkNmBhs9ZAwdCx8IsNWw3EfPbzX0/Qr7vSrAoC5r31UBZdIRj6ny7vRLM+3kcXis07eI8I09H5y5PkaP/qUW1oZE/zMRIx4kiIk+mFtHxbKiWIMFradjiLdh3LoX6do2j6xA6UJ2YE+5JyacHK2tVO2nT4nhHwNgIsAJxsAawJ7NixkxoEB9PAAQOqxX7//fflWkE1D41DTk4OJSYmaly8d6v0+FJNojvbp6KiQqjAysWOWjFariiXqrEK8VAh3OCOn7dor2C4R8Xu23ixk7eVYNbQ32M0vut4jpU9vyrfD8LO/4TQ47cSgkJbTejz1+1Np3MiLgQK/EEY1SeLnb+YESidv0rrXHYxLfg5mXAsdMumjaiorFzORJQ/XxmBmhA4efKkOMcqWPIQrFvB4KRBg2CpomwgRjH4aTdZ4l49q2tN6dfkl5GRIYxdMixBWABYoKj9Bgxx7959tHLlzzT91lspLKy6rXn79u3Jnn081hZOnz5NISEhNuPWXgLXh7AlALRuYPJggCZThRQA5QIDJQDq+zLWpzZQ6+xJzKUDQgXUVOjwC4vLqFicx2OPcGTDabHoGyE2dJWUWgsupIXD3BLEukFEmNmKKP9CqZXaR58uwh87ky/CNxRHT1SQWhfQh+NnRkCPAIxIlAAIFsweAiAoCIw/SDJ/CACQ6l+4au/16TnyDN5VXFwseY/WwpEFgCPoiTBgimfOnKEFn3wq9P6xwgLoGkujaJOYPHmy9tHqPjc3l+bNmyfWEsIpLi7Oys9bD1pmr71HedQMAAJAzQCUAIAwUy+qN8ou+oQkWPFk12DHry0bVEU1mYeKpKwWe7Vxbd3L9MQ5REyMgDMIxMTEmEf9YhagBABmAFoBoGf69RUA2LOTkpJCUVFRFBlptpRDmdkKyMGWg4XQ+/PmU3p6Ov357rvlorCDUTkYI8AIMAI+iQALAAeaBaPeF+fMpZ07d0lT0AED+nt19OtAkT0SRM0YPJJZZSalYhSPBVyc+MnECDAC9UMg4FVA0NfvEXr91NRU2r1nr1R1rFixUo7wwehbC1XNUzNn0aJFi+naKddIHf6XX30tVUKAfvqtt0h9Xv2agWM7igDO5sf5PDj4jYkRYATqh0DAC4Dc3DxasmSp2O27VSLZOT6e/vvZ59SsWTN6+OGHxEKgiQ4nHKYOYnEX1j/4aelmsSMYCzpMnkHgqFh4xY+JEWAE6o9AwAuANm3i6OW5c2pEcumSxTX6sycjwAgwAkZEgNcAjNhqXGZGgBFgBFyAAAsAF4DISTACjAAjYEQEWAAYsdW4zIwAI8AIuAABFgAuAJGTYAQYAUbAiAiwADBiq3GZGQFGgBFwAQIsAFwAIifBCDACjIAREWABYMRW4zIzAowAI+ACBFgAuADEQE3CG0dBBCrWXG9GwB0IsABwB6qcJiPACDACBkCABYABGomLyAgwAoyAOxAI+KMg3AFqIKWZseOTQKou15URqBcC+BgU0XP1SsOVkVkAuBLNAEzrfNLaAKw1V5kRqBsC+Pa4LxGrgHypNQxWFvWVIoMVm4vLCDAClQiwAOBXgRFgBBiBAEWABUCANjxXmxFgBBiB/wfmxUvcXrAu3QAAAABJRU5ErkJggg==" + } + }, + "cell_type": "markdown", + "id": "7619577f", + "metadata": {}, + "source": [ + "You can change the type of the displayed weave panel by changing `Table` to `Plot` and it will intelligently change to display a plot of the data.\n", + "![image.png](attachment:image.png)\n", + "\n", + "You've now created a Weave `Plot` Panel, or **Weave PanelPlot**. The default PanelPlot will show the HP (hit points or health points) of all the Pokemon versus their total stats. From the resulting PanelPlot, you can \n", + "* hover your mouse over a point to see the full data for it in the tooltip\n", + "* click on the magnifuing glass in the bottom right corner, then click and drag over a region of the plot to rescale and zoom to the selected region\n", + "* double-click on the plot area to reset to the original zoom level" + ] + }, + { + "cell_type": "markdown", + "id": "356dc2a9", + "metadata": {}, + "source": [ + "## Embed and cluster the data" + ] + }, + { + "cell_type": "markdown", + "id": "4fd47c57", + "metadata": {}, + "source": [ + "Now we can pass a column from this data to another Weave Op to embed it. This will turn each row in the column `name` into a vector." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43cec2d8", + "metadata": {}, + "outputs": [], + "source": [ + "embeddings = openai.openai_embed(data['name'], {\"model\": \"text-embedding-ada-002\"})" + ] + }, + { + "cell_type": "markdown", + "id": "6715de4a", + "metadata": {}, + "source": [ + "Now, we'll project this to 2 dimensions for clustering, and then we'll cluster this projection using `hdbscan`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eff5bca", + "metadata": {}, + "outputs": [], + "source": [ + "clusterable_projection = umap.umap_project(\n", + " embeddings, {\n", + " 'n_neighbors': 30,\n", + " 'min_dist': 0,\n", + " 'n_components': 2,\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "583830d0", + "metadata": {}, + "outputs": [], + "source": [ + "clusters = hdbscan.hdbscan_cluster(clusterable_projection, {\n", + " 'min_samples': 10,\n", + " 'min_cluster_size': 60\n", + " })" + ] + }, + { + "cell_type": "markdown", + "id": "87c4ad19", + "metadata": {}, + "source": [ + "Then, we'll project the embeddings again for plotting. For efficient computation, Weave isn't actually executing anything until we call `weave.use` or `weave.show` on the built-up computation (or \"compute graph\")." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21f5917b", + "metadata": {}, + "outputs": [], + "source": [ + "projection = umap.umap_project(embeddings, {})" + ] + }, + { + "cell_type": "markdown", + "id": "e3b7f76c", + "metadata": {}, + "source": [ + "## Combine data for plotting" + ] + }, + { + "cell_type": "markdown", + "id": "1d017836", + "metadata": {}, + "source": [ + "Finally, we'll combine our data into one big table so we can display the Pokemon name and types alongside our embedding. Here, we're calling `weave.use` on each weave object to execute the computation, and then we're merging them into a new list of dictionaries. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d642110d", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show([{'x': x, 'y': y, 'k':k, 'd':d} for (x,y),k,d in \n", + " zip(weave.use(projection),\n", + " weave.use(clusters),\n", + " weave.use(data))])" + ] + }, + { + "cell_type": "markdown", + "id": "1cf9b267", + "metadata": {}, + "source": [ + "## Build and publish an interactive dashboard" + ] + }, + { + "cell_type": "markdown", + "id": "202f5f66-a3c4-41ea-b1ca-cef26f98f26b", + "metadata": {}, + "source": [ + "Some Weave UI tips to build an interactive dashboard in-line to explore and get insights into your embeddings:\n", + "* **add new panels**: click the + button on the drawer that pops out on the right hand side of the cell\n", + "* **change panel types**: as we did above to change the `Table` panel named `pokemon_data` into a `Plot` panel\n", + "* **refer to other panels by their name**: add a new `plot` panel that uses `table.selected_data` from the first panel. You can click the pencil to edit the configuration of your `plot`. " + ] + }, + { + "attachments": { + "image.png": { + "image/png": "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Y29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTY3MDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj43MDQ8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xNDQvMTwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MTQ0LzE8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpZQ7xWAABAAElEQVR4AeydB2BUVfbGT3ovkEoJhN57B1FAEHsHe/evrrqra9t1177uuuvaXdvae1lRERRBivTeewuhJZDee8L/fDe8YZJMwkxISPuODvPmlfvu+81M5r77neJ2TE1oJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACzZ6Ae7O/Ql4gCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAIUBhiB8EEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmghBCgMtZA3mpdJAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAhSG+BkgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggRZCgMJQC3mjeZkkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQGGInwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaCEEKAy1kDeal0kCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACnq4gSE9Pl7y8PFcO4b4kQAIkQAIkQAIkcMoEMo+5ya4St1Nuhw2QAAmQAAmQAAmQgKsEov19pKt7mTksPDzc1cO5PwmQAAmQAAmQAAk0OgIuCUMlJSVSWFjY6C6CHSIBEiABEiABEmjeBHLK3CSpmMJQ836XeXUkQAIkQAIk0DgJ+Hl4SJFHSePsHHtFAiRAAiRAAiRAArUg4JIwZLXfuXNna5HPJEACJEACJEACJFCvBOLi4uq1fTZOAiRAAiRAAiRAAiRAAiRAAiRAAiRAAi2JQK2EoZYEiNdKAiRAAiRAAiTQuAh0DPSXyTGRjatT7A0JkAAJkAAJkECzIrA/J09mH0xqVtfEiyEBEiABEiABEiABi4C7tcBnEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCB5k2AwlDzfn95dSRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRgI0BhyIaCCyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQvAlQGGre7y+vjgRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARsBCgM2VBwgQRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgASaNwEKQ837/eXVkQAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkICNAIUhGwoukAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEDzJkBhqHm/v7w6EiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABErARoDBkQ8EFEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmjeBCgMNe/3l1dHAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAjYCFIZsKLhAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAs2bAIWh5v3+8upIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIwEaAwpANBRdIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIoHkToDDUvN9fXh0JkAAJkAAJkAAJkAAJ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t1Xtd333i+2TAAmQAAnULQFXRCHrzJY4hHRyR5OSTHo5pJlraGuyNYYaGhzPTwIkQAIkQAIkcHoINKYaQ6gT+Mgjj8jdd999ei6eZyEBEiABEiABEmgQAo2lxlDli7evOXTu5EmCYtsNaaaOYWamoHZQeFiYSZvjbH+Q7i1dRZtQFYVQm8cVQ72htLQ0KTtWJiHBIZri90ThcrRToBNuqamp4u3lrXWjWzlVM9qV89e0L5hkKhPUTEINxqAg1iCuiRe3kQAJkEBTIXBEU6b+oLXwkF5+zOhRMnBAf5e6bl9zKDoq0jhRIOq0oaxiTG9D9YLnJQESIAESIAESIIFGTCBYvV+HDRsmGRkZ0r59+0bcU3aNBEiABEiABEigOROwTyu3cuWqBheGMKGFaB88XDVTD6idn6uHmf0RhdOmTXS1x/pqXYd2bdtWu70+N4AJxC48aCRAAiRAAs2HAKJ9ykrLaiUKgYIVOTRj5s+SkpIqRUVFLjlU1DVJCkN1TZTtkQAJkAAJkAAJNDsCPXv2lDfffLPZXRcviARIgARIgARIoOkRgDgUGBQknh4eTa/z7DEJkAAJkAAJNFEC/bXOXdcunTXKtWKUqiuXA3EI6VYLCgoaVBRCnykMufLOcV8SIAESIAESIAESIAESIAESIAESIAESaGACsR07NHAPeHoSIAESIAESaFkEEBF6KqKQRcvd3V38/V1LoWodW5fP7nXZGNsiARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJovAQoDDXe94Y9IwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE6JUBhqE5xsjESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaLwEKAw13veGPSMBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBOiVAYahOcbIxEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmi8BCgMNd73hj0jARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggTolQGGoTnGyMRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJovAQoDDXe94Y9IwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE6JUBhqE5xsjESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaLwEPBtv19gzEiABEiABEiABEjhBINz9mAz0OiZSnCNxcTknNnCJBEiABEiABEiABOqBwEAvN9lQ7FYPLbNJEiABEiABEiABEmhYArUShuLi4hq21zw7CZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAywRqJQy5fBYeQAIkQAIkQAIkQAKnSCClzE3mFrpJx0B/mRwTeYqt8XASIAESIAESIAESqJ7A/pw82XAwqfoduIUESIAESIAESIAEmjCBWglDnTt3bsKXzK6TAAmQAAmQAAk0JQKMVG5K7xb7SgIkQAIkQAIkQAIkQAIkQAIkQAIk0NgJuDf2DrJ/JEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACdUOAwlDdcGQrJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJNDoCVAYavRvETtIAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAnVDgMJQ3XBkKyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQ6AlQGGr0bxE7SAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAJ1Q4DCUN1wZCskQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk0OgJUBhq9G8RO0gCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACdUOAwlDdcGQrJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJNDoCVAYavRvETtIAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAnVDgMJQ3XBkKyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQ6Al4NvoesoMkQAIkQAIkQAIk0EwIlJaWSkpKiqxdu17mzp0r/v7+8uCDf5RWrVqZKywsLJStW7fJ9z9Mly1btkhOTo4MGjRQrpo6RQYMGCCenhy6NZOPAi+DBEiABEiABEiABEiABEiABEiABBqMQIufXTh27JgUl5RImU7U+Pj4iJubm8M3o6ysTEp0P5i3t7fDfbiSBEiABEiABEiABGoikJubKytWrpJv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AZg7Z9bgHfQgGllwcGsOqMLCImRkZEouzqo+HZ32p/1q/2pBQUF92j87IwESIAESaEuAHkNtefAVCZAACZAACZAACRgCgQEBWH7DjTh65Bj+z//8D15fuw6JiWPgMGoU0tIvIj09HeMkhvyKW26Gu4R5oZEACZAACZAACZAACQxeArm5eWi5PLxRcj+XOCYBixYugKOj4+Ad9CAamYYcDA0NRZ54XZWXl5tHfw0vNDSkxyEO+2ss7JcESIAEhisBCkPD9cryvEiABEiABEiABK6JgE4YjBfh579++J9Yu+4NvPHGG9i2fbvpU8PG3Xbbajzx+GNISkq6puOwMQmQAAmQAAmQAAkMZwKLFi3EgvnzzSk6Og5c4Jro6Cjcv+YeaLhgf3+/fs2TM1yvZ1BQINzd3SScXImEWq5BfX1Dn52qi4sz3NzcobmiPD09+6xfdkQCJEACJGCfAIUh+1xYSgIkQAIkQAIkQAKGgK5Y/PKXnsEXPv8USkpKoHmF/P39oYmXVTyikQAJkAAJkAAJkAAJdE7A0cEB+hhoc5AxBAUGDvQwhvzxVbShcDPkLyNPgARIgARAYYhvAhIgARIgARIgARK4CgEVgDQpcVhY2FVqcjcJkAAJkAAJkAAJkAAJkAAJkAAJkAAJDG4CA79kY3Dz4ehIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIYNgQoDA0bC4lT4QESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEuiZAYahrPtxLAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAsOGAIWhYXMpeSIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk0DUBCkNd8+FeEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEhg2BCgMDZtLyRMhARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgga4JOHW9m3ubm5uxb/9+bNr0ESoqKjFmzGg8+MD98PX1JRwSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGFIE6DF0lcv17ob38M1vfgtZmVnw8fHGu+9uwP/89OdXacXdJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJDD4CNBjqItrUlZWju/927/j0YcfFnHoOTg4OKCktBROTsTWBTbuIgESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGKQEqHB0cWH+8corcHJ0wuOPf86IQlrV38+vixbcRQIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAKDlwCFoS6uzeHDhzFp4kQ4Ojpg9+49qG+oR1xsHKJjouEwalQXLbmLBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABAYfAQpDXVyTjIwshIQE49e//i0qKiuQmZkNV1cXfO2rX8GMGdPbtNy9ezcqKyvblFleNDU1mc2amhoUFhZaivncTQIWfsq3vr6+m61YrT2BhoYGU8T3YHsy3Xvd3NxMft1D1Wkty99yVVXVgPwt19XVdTo27iABEiABEiABEiABEiABEiABEiABEiABEiCBkUKAwlAnV7qlpcWIQaWlJbj33nswcUKyCD9V+I/v/wA/+e//g3VrX2vT8vTp0ygoKGhT1v6Fihrl5eXti/m6mwRqa2uhD9q1EeB7kPyujcC1t1aRXB8DYZorjkYCJEACJEACJEACJEACJEACJEACJEACJEACI5kAhaFOrv4oCRUXEhyMuLg4rFp5q4STczQ1ly1dip//8pcyqVkLd3c3a+uHH34YKibZM12l/uKLL8LHx8f0Z68OyzonoGJQbm4uAgICDMPOa3JPVwSUobLU9zSt5wQyMjKgf+Ex0dE9b8wWhoDlbzkwMBDe3t7XnYqK9wMlSF33k+UBSYAESIAESIAESIAESIAESIAESIAESIAESKATAhSGOgGjxYljEnHy1Mk2gk+wiEWODo5obNSwXFeEIVdX1y56at2lYhNXq18VU4cKFmb6bNnuUIkF3SZAht1G1aGiZhYjvw5Yul2gn4FqA/VZaDl+twfMiiRAAiRAAiRAAiRAAiRAAiRAAoaAJTw9cZAACZDA9STg7Ox8PQ83oo7FmDpdXO41a+5FRkYmPtq82YSAy8/Px85duzBlypQBWe3exVC5iwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgASuSoAeQ10gmj59Gp584nH88Y9/ws6du1FRUQ4Vh55//htdtOIuEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEhicBCgMdXFdNGTUF7/4eUyeMgkXL16Ck+QZmjhpIiZPmtRFK+4iARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggcFJgMLQVa6Lu7s7li1diqamJlPTyYnIroKMu0mABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABAYpAaoc3bgwmrCcglA3QLEKCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZDAoCbgMKhHx8GRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn0GQEKQ32Gkh2RAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQwOAmQGFocF8fjo4ESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAE+owAhaE+Q8mOSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGBwE6AwNLivD0dHAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAn1GgMJQn6FkRyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiQwuAlQGBrc14ejIwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE+I+DUZz2xIxIgARIgARIgARIggT4j0NzcjIqKShQUFMDNzRVhYWFwcuKtW58BZkckQAIkQAIkQAIkQAIkQAIkQAIkMEIJ0GNohF54njYJkAAJkAAJkMDgJlBRUYHNmzfjP77/A7y+dp2IRBWDe8AcHQmQAAmQAAmQAAmQAAmQAAmQAAmQwJAgwGWnQ+IycZAkQAIkQAIkQAIjiUBjYyNSU1Px4aaPkJmZhcTEMWhqahpJCHiuJEACJEACJEACJEACJDDgBGpr6/D+xg86HcecObMQFRnZYf/uPfvQ0tKCWTNnmH37DxzEqFGjMG/u7A51r1Zw7P+zdx/gcV3ngfdfYDAVvTcWgL03sVMkJVnNkizJjiXHJYos2/LGibNer+PYcZyN7c9Z50m8jr2bxLHTY8exZMmSVaguqrCKvYkNBEACRCF6H9TvvBecwQCYIQpnQGDwP88znDvnnnvuPb87AMl55z2noka8XT2yMj9THDa+4x/MazzeR48el/NFRcG6k/j4eLn7rjuC7qMSgWgRIDAULXeScSCAAAIIIIBA1AhU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JEACJEACJEACJEACJEACJEACJEACJEACJEAC3kaANYa87Yhyf0iABEiABEiABEiABEiABEiABEiABEjAwwj07t1LevboYXrt7884Zg87fOwuCZAACZCAhxGgY8jDDhi7SwIkQAIkQAIkQAIkQAIkQAIkQAIkQALeRsDfz0/wopEACZAACZAACbieAK+4rmfMFkiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjALQjQMeQWh4GdIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAHXE6BjyPWM2QIJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJuAUBOobc4jCwEyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTgegIBrm+CLZAACZAACZAACZCA8wicSM+Qj/Yfd94GuSUSIAESIAESIAESIAESIAESIAESIAES8CECt+QYiouL8yFE3FUSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAES8A4ClJLzjuPIvSABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBYglU+EGt2KW4AAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn4KIH4+Hiz5+Hh4T5KgLvt6wSys7N9HQH3nwRI4DYQqFix4m1o1TeavCUpOd9Aw70kARIgARIgARIgARIgARIgARIgARIgARIgARLg4Cx/AyRAAiTgXQSYMeRdx5N7QwIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIOCbDGkEM0nEECJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC3kWAjiHvOp7cGxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJwSICOIYdoOIMESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEvIsAHUPedTy5NyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTgkAAdQw7RcAYJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJeBcBOoa863hyb0iABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAIQE6hhyi4QwSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAES8C4CdAx51/Hk3pAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAQwJ0DDlEwxkkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk4F0E6BjyruPJvSEBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABhwToGHKIhjNIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIwLsI0DHkXceTe0MCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACDgnQMeQQDWeQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQgHcRoGPIu44n94YESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEHBKgY8ghGs4gARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAe8i8P/kFu9JFZ12fgAAAABJRU5ErkJggg==" + } + }, + "cell_type": "markdown", + "id": "0cf240cf", + "metadata": {}, + "source": [ + "![image.png](attachment:image.png)" + ] + }, + { + "cell_type": "markdown", + "id": "4b9fc573", + "metadata": {}, + "source": [ + "To share your creation, you can click \"open in new tab\" in the right-hand drawer, and then click \"Publish\" to save it to wandb." + ] + }, + { + "cell_type": "markdown", + "id": "9a4092e3", + "metadata": {}, + "source": [ + "### Bonus: Weaving everything together" + ] + }, + { + "cell_type": "markdown", + "id": "4f211f9e", + "metadata": {}, + "source": [ + "Here's a **Weave Board** for all these panels, defined in code. We're actively working on additional examples and tutorials to explain these concepts in more detail. To inspire some initial exploration, you can:\n", + "* **open the panel in a new window** from the right-hand drawer on hover, to view a full-page Weave Board\n", + "* **see the details of selected data** by clicking the mouse button in the bottom right of the PanelPlot, then clicking and dragging to select a subregion of points\n", + "* **customize the Board** by adding, changing, and interconnecting panels\n", + "* **save and share your work** by renaming the Board and publishing it to W&B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e194b3ce", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'data': data,\n", + " 'limit': 1005,\n", + " 'limited_data': lambda data, limit: data.limit(limit),\n", + " 'embeddings': lambda limited_data: openai.openai_embed(limited_data['name'], {\"model\": \"text-embedding-ada-002\"}),\n", + " 'clusterable_projection': lambda embeddings: umap.umap_project(\n", + " embeddings, {\n", + " 'n_neighbors': 30,\n", + " 'min_dist': 0,\n", + " 'n_components': 2,\n", + " }\n", + " ),\n", + " 'clusters': lambda clusterable_projection: hdbscan.hdbscan_cluster(clusterable_projection, {\n", + " 'min_samples': 10,\n", + " 'min_cluster_size': 50\n", + " }),\n", + " 'projection': lambda embeddings: umap.umap_project(embeddings, {})\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda limited_data, projection, clusters: weave.legacy.weave.panels.Plot(\n", + " limited_data,\n", + " x=lambda row, index: projection[index][0],\n", + " y=lambda row, index: projection[index][1],\n", + " label=lambda row, index: clusters[index],\n", + " ),\n", + " id='projection_plot',\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda projection_plot: projection_plot.selected_data(),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " )\n", + " ]\n", + ")" + ] } - }, - "cell_type": "markdown", - "id": "7619577f", - "metadata": {}, - "source": [ - "You can change the type of the displayed weave panel by changing `Table` to `Plot` and it will intelligently change to display a plot of the data.\n", - "![image.png](attachment:image.png)\n", - "\n", - "You've now created a Weave `Plot` Panel, or **Weave PanelPlot**. The default PanelPlot will show the HP (hit points or health points) of all the Pokemon versus their total stats. From the resulting PanelPlot, you can \n", - "* hover your mouse over a point to see the full data for it in the tooltip\n", - "* click on the magnifuing glass in the bottom right corner, then click and drag over a region of the plot to rescale and zoom to the selected region\n", - "* double-click on the plot area to reset to the original zoom level" - ] - }, - { - "cell_type": "markdown", - "id": "356dc2a9", - "metadata": {}, - "source": [ - "## Embed and cluster the data" - ] - }, - { - "cell_type": "markdown", - "id": "4fd47c57", - "metadata": {}, - "source": [ - "Now we can pass a column from this data to another Weave Op to embed it. This will turn each row in the column `name` into a vector." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "43cec2d8", - "metadata": {}, - "outputs": [], - "source": [ - "embeddings = openai.openai_embed(data['name'], {\"model\": \"text-embedding-ada-002\"})" - ] - }, - { - "cell_type": "markdown", - "id": "6715de4a", - "metadata": {}, - "source": [ - "Now, we'll project this to 2 dimensions for clustering, and then we'll cluster this projection using `hdbscan`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5eff5bca", - "metadata": {}, - "outputs": [], - "source": [ - "clusterable_projection = umap.umap_project(\n", - " embeddings, {\n", - " 'n_neighbors': 30,\n", - " 'min_dist': 0,\n", - " 'n_components': 2,\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "583830d0", - "metadata": {}, - "outputs": [], - "source": [ - "clusters = hdbscan.hdbscan_cluster(clusterable_projection, {\n", - " 'min_samples': 10,\n", - " 'min_cluster_size': 60\n", - " })" - ] - }, - { - "cell_type": "markdown", - "id": "87c4ad19", - "metadata": {}, - "source": [ - "Then, we'll project the embeddings again for plotting. For efficient computation, Weave isn't actually executing anything until we call `weave.use` or `weave.show` on the built-up computation (or \"compute graph\")." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21f5917b", - "metadata": {}, - "outputs": [], - "source": [ - "projection = umap.umap_project(embeddings, {})" - ] - }, - { - "cell_type": "markdown", - "id": "e3b7f76c", - "metadata": {}, - "source": [ - "## Combine data for plotting" - ] - }, - { - "cell_type": "markdown", - "id": "1d017836", - "metadata": {}, - "source": [ - "Finally, we'll combine our data into one big table so we can display the Pokemon name and types alongside our embedding. Here, we're calling `weave.use` on each weave object to execute the computation, and then we're merging them into a new list of dictionaries. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d642110d", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show([{'x': x, 'y': y, 'k':k, 'd':d} for (x,y),k,d in \n", - " zip(weave.use(projection),\n", - " weave.use(clusters),\n", - " weave.use(data))])" - ] - }, - { - "cell_type": "markdown", - "id": "1cf9b267", - "metadata": {}, - "source": [ - "## Build and publish an interactive dashboard" - ] - }, - { - "cell_type": "markdown", - "id": "202f5f66-a3c4-41ea-b1ca-cef26f98f26b", - "metadata": {}, - "source": [ - "Some Weave UI tips to build an interactive dashboard in-line to explore and get insights into your embeddings:\n", - "* **add new panels**: click the + button on the drawer that pops out on the right hand side of the cell\n", - "* **change panel types**: as we did above to change the `Table` panel named `pokemon_data` into a `Plot` panel\n", - "* **refer to other panels by their name**: add a new `plot` panel that uses `table.selected_data` from the first panel. You can click the pencil to edit the configuration of your `plot`. " - ] - }, - { - "attachments": { - "image.png": { - "image/png": "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Y29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTY3MDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj43MDQ8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xNDQvMTwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MTQ0LzE8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpZQ7xWAABAAElEQVR4AeydB2BUVfbGT3ovkEoJhN57B1FAEHsHe/evrrqra9t1177uuuvaXdvae1lRERRBivTeewuhJZDee8L/fDe8YZJMwkxISPuODvPmlfvu+81M5r77neJ2TE1oJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACzZ6Ae7O/Ql4gCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAIUBhiB8EEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmghBCgMtZA3mpdJAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAhSG+BkgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggRZCgMJQC3mjeZkkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQGGInwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaCEEKAy1kDeal0kCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACnq4gSE9Pl7y8PFcO4b4kQAIkQAIkQAIkcMoEMo+5ya4St1Nuhw2QAAmQAAmQAAmQgKsEov19pKt7mTksPDzc1cO5PwmQAAmQAAmQAAk0OgIuCUMlJSVSWFjY6C6CHSIBEiABEiABEmjeBHLK3CSpmMJQ836XeXUkQAIkQAIk0DgJ+Hl4SJFHSePsHHtFAiRAAiRAAiRAArUg4JIwZLXfuXNna5HPJEACJEACJEACJFCvBOLi4uq1fTZOAiRAAiRAAiRAAiRAAiRAAiRAAiRAAi2JQK2EoZYEiNdKAiRAAiRAAiTQuAh0DPSXyTGRjatT7A0JkAAJkAAJkECzIrA/J09mH0xqVtfEiyEBEiABEiABEiABi4C7tcBnEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCB5k2AwlDzfn95dSRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRgI0BhyIaCCyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQvAlQGGre7y+vjgRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARsBCgM2VBwgQRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgASaNwEKQ837/eXVkQAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkICNAIUhGwoukAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEDzJkBhqHm/v7w6EiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABErARoDBkQ8EFEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmjeBCgMNe/3l1dHAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAjYCFIZsKLhAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAs2bAIWh5v3+8upIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIwEaAwpANBRdIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIoHkToDDUvN9fXh0JkAAJkAAJkAAJkAAJ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t1Xtd333i+2TAAmQAAnULQFXRCHrzJY4hHRyR5OSTHo5pJlraGuyNYYaGhzPTwIkQAIkQAIkcHoINKYaQ6gT+Mgjj8jdd999ei6eZyEBEiABEiABEmgQAo2lxlDli7evOXTu5EmCYtsNaaaOYWamoHZQeFiYSZvjbH+Q7i1dRZtQFYVQm8cVQ72htLQ0KTtWJiHBIZri90ThcrRToBNuqamp4u3lrXWjWzlVM9qV89e0L5hkKhPUTEINxqAg1iCuiRe3kQAJkEBTIXBEU6b+oLXwkF5+zOhRMnBAf5e6bl9zKDoq0jhRIOq0oaxiTG9D9YLnJQESIAESIAESIIFGTCBYvV+HDRsmGRkZ0r59+0bcU3aNBEiABEiABEigOROwTyu3cuWqBheGMKGFaB88XDVTD6idn6uHmf0RhdOmTXS1x/pqXYd2bdtWu70+N4AJxC48aCRAAiRAAs2HAKJ9ykrLaiUKgYIVOTRj5s+SkpIqRUVFLjlU1DVJCkN1TZTtkQAJkAAJkAAJNDsCPXv2lDfffLPZXRcviARIgARIgARIoOkRgDgUGBQknh4eTa/z7DEJkAAJkAAJNFEC/bXOXdcunTXKtWKUqiuXA3EI6VYLCgoaVBRCnykMufLOcV8SIAESIAESIAESIAESIAESIAESIAESaGACsR07NHAPeHoSIAESIAESaFkEEBF6KqKQRcvd3V38/V1LoWodW5fP7nXZGNsiARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJovAQoDDXe94Y9IwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE6JUBhqE5xsjESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaLwEKAw13veGPSMBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBOiVAYahOcbIxEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmi8BCgMNd73hj0jARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggTolQGGoTnGyMRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJovAQoDDXe94Y9IwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE6JUBhqE5xsjESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESaLwEPBtv19gzEiABEiABEiABEjhBINz9mAz0OiZSnCNxcTknNnCJBEiABEiABEiABOqBwEAvN9lQ7FYPLbNJEiABEiABEiABEmhYArUShuLi4hq21zw7CZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAywRqJQy5fBYeQAIkQAIkQAIkQAKnSCClzE3mFrpJx0B/mRwTeYqt8XASIAESIAESIAESqJ7A/pw82XAwqfoduIUESIAESIAESIAEmjCBWglDnTt3bsKXzK6TAAmQAAmQAAk0JQKMVG5K7xb7SgIkQAIkQAIkQAIkQAIkQAIkQAIk0NgJuDf2DrJ/JEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACdUOAwlDdcGQrJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJNDoCVAYavRvETtIAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAnVDgMJQ3XBkKyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQ6AlQGGr0bxE7SAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAJ1Q4DCUN1wZCskQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk0OgJUBhq9G8RO0gCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACdUOAwlDdcGQrJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJNDoCVAYavRvETtIAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAnVDgMJQ3XBkKyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQ6Al4NvoesoMkQAIkQAIkQAIk0EwIlJaWSkpKiqxdu17mzp0r/v7+8uCDf5RWrVqZKywsLJStW7fJ9z9Mly1btkhOTo4MGjRQrpo6RQYMGCCenhy6NZOPAi+DBEiABEiABEiABEiABEiABEiABBqMQIufXTh27JgUl5RImU7U+Pj4iJubm8M3o6ysTEp0P5i3t7fDfbiSBEiABEiABEiABGoikJubKytWrpJv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AZg7Z9bgHfQgGllwcGsOqMLCImRkZEouzqo+HZ32p/1q/2pBQUF92j87IwESIAESaEuAHkNtefAVCZAACZAACZAACRgCgQEBWH7DjTh65Bj+z//8D15fuw6JiWPgMGoU0tIvIj09HeMkhvyKW26Gu4R5oZEACZAACZAACZAACQxeArm5eWi5PLxRcj+XOCYBixYugKOj4+Ad9CAamYYcDA0NRZ54XZWXl5tHfw0vNDSkxyEO+2ss7JcESIAEhisBCkPD9cryvEiABEiABEiABK6JgE4YjBfh579++J9Yu+4NvPHGG9i2fbvpU8PG3Xbbajzx+GNISkq6puOwMQmQAAmQAAmQAAkMZwKLFi3EgvnzzSk6Og5c4Jro6Cjcv+YeaLhgf3+/fs2TM1yvZ1BQINzd3SScXImEWq5BfX1Dn52qi4sz3NzcobmiPD09+6xfdkQCJEACJGCfAIUh+1xYSgIkQAIkQAIkQAKGgK5Y/PKXnsEXPv8USkpKoHmF/P39oYmXVTyikQAJkAAJkAAJkAAJdE7A0cEB+hhoc5AxBAUGDvQwhvzxVbShcDPkLyNPgARIgARAYYhvAhIgARIgARIgARK4CgEVgDQpcVhY2FVqcjcJkAAJkAAJkAAJkAAJkAAJkAAJkAAJDG4CA79kY3Dz4ehIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIYNgQoDA0bC4lT4QESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEuiZAYahrPtxLAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAsOGAIWhYXMpeSIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk0DUBCkNd8+FeEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEhg2BCgMDZtLyRMhARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgga4JOHW9m3ubm5uxb/9+bNr0ESoqKjFmzGg8+MD98PX1JRwSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGFIE6DF0lcv17ob38M1vfgtZmVnw8fHGu+9uwP/89OdXacXdJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJDD4CNBjqItrUlZWju/927/j0YcfFnHoOTg4OKCktBROTsTWBTbuIgESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGKQEqHB0cWH+8corcHJ0wuOPf86IQlrV38+vixbcRQIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAKDlwCFoS6uzeHDhzFp4kQ4Ojpg9+49qG+oR1xsHKJjouEwalQXLbmLBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABAYfAQpDXVyTjIwshIQE49e//i0qKiuQmZkNV1cXfO2rX8GMGdPbtNy9ezcqKyvblFleNDU1mc2amhoUFhZaivncTQIWfsq3vr6+m61YrT2BhoYGU8T3YHsy3Xvd3NxMft1D1Wkty99yVVXVgPwt19XVdTo27iABEiABEiABEiABEiABEiABEiABEiABEiCBkUKAwlAnV7qlpcWIQaWlJbj33nswcUKyCD9V+I/v/wA/+e//g3VrX2vT8vTp0ygoKGhT1v6Fihrl5eXti/m6mwRqa2uhD9q1EeB7kPyujcC1t1aRXB8DYZorjkYCJEACJEACJEACJEACJEACJEACJEACJEACI5kAhaFOrv4oCRUXEhyMuLg4rFp5q4STczQ1ly1dip//8pcyqVkLd3c3a+uHH34YKibZM12l/uKLL8LHx8f0Z68OyzonoGJQbm4uAgICDMPOa3JPVwSUobLU9zSt5wQyMjKgf+Ex0dE9b8wWhoDlbzkwMBDe3t7XnYqK9wMlSF33k+UBSYAESIAESIAESIAESIAESIAESIAESIAESKATAhSGOgGjxYljEnHy1Mk2gk+wiEWODo5obNSwXFeEIVdX1y56at2lYhNXq18VU4cKFmb6bNnuUIkF3SZAht1G1aGiZhYjvw5Yul2gn4FqA/VZaDl+twfMiiRAAiRAAiRAAiRAAiRAAiRAAoaAJTw9cZAACZDA9STg7Ox8PQ83oo7FmDpdXO41a+5FRkYmPtq82YSAy8/Px85duzBlypQBWe3exVC5iwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgASuSoAeQ10gmj59Gp584nH88Y9/ws6du1FRUQ4Vh55//htdtOIuEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEhicBCgMdXFdNGTUF7/4eUyeMgkXL16Ck+QZmjhpIiZPmtRFK+4iARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggcFJgMLQVa6Lu7s7li1diqamJlPTyYnIroKMu0mABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABAYpAaoc3bgwmrCcglA3QLEKCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZDAoCbgMKhHx8GRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn0GQEKQ32Gkh2RAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQwOAmQGFocF8fjo4ESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAE+owAhaE+Q8mOSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESGBwE6AwNLivD0dHAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAn1GgMJQn6FkRyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiQwuAlQGBrc14ejIwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIE+I+DUZz2xIxIgARIgARIgARIggT4j0NzcjIqKShQUFMDNzRVhYWFwcuKtW58BZkckQAIkQAIkQAIkQAIkQAIkQAIkMEIJ0GNohF54njYJkAAJkAAJkMDgJlBRUYHNmzfjP77/A7y+dp2IRBWDe8AcHQmQAAmQAAmQAAmQAAmQAAmQAAmQwJAgwGWnQ+IycZAkQAIkQAIkQAIjiUBjYyNSU1Px4aaPkJmZhcTEMWhqahpJCHiuJEACJEACJEACJEACJDDgBGpr6/D+xg86HcecObMQFRnZYf/uPfvQ0tKCWTNnmH37DxzEqFGjMG/u7A51r1Zw7P+zdx/gcV3ngfdfYDAVvTcWgL03sVMkJVnNkizJjiXHJYos2/LGibNer+PYcZyN7c9Z50m8jr2bxLHTY8exZMmSVaguqrCKvYkNBEACRCF6H9TvvBecwQCYIQpnQGDwP88znDvnnnvuPb87AMl55z2noka8XT2yMj9THDa+4x/MazzeR48el/NFRcG6k/j4eLn7rjuC7qMSgWgRIDAULXeScSCAAAIIIIBA1AhU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JEACJEACJEACJEACJEACJEACJEACJEACJEAC3kaANYa87Yhyf0iABEiABEiABEiABEiABEiABEiABEjAwwj07t1LevboYXrt7884Zg87fOwuCZAACZCAhxGgY8jDDhi7SwIkQAIkQAIkQAIkQAIkQAIkQAIkQALeRsDfz0/wopEACZAACZAACbieAK+4rmfMFkiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjALQjQMeQWh4GdIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAHXE6BjyPWM2QIJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJuAUBOobc4jCwEyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTgegIBrm+CLZAACZAACZAACZCA8wicSM+Qj/Yfd94GuSUSIAESIAESIAESIAESIAESIAESIAES8CECt+QYiouL8yFE3FUSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAES8A4ClJLzjuPIvSABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBYglU+EGt2KW4AAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn4KIH4+Hiz5+Hh4T5KgLvt6wSys7N9HQH3nwRI4DYQqFix4m1o1TeavCUpOd9Aw70kARIgARIgARIgARIgARIgARIgARIgARIgARLg4Cx/AyRAAiTgXQSYMeRdx5N7QwIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIOCbDGkEM0nEECJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC3kWAjiHvOp7cGxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJwSICOIYdoOIMESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEvIsAHUPedTy5NyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTgkAAdQw7RcAYJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJeBcBOoa863hyb0iABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAIQE6hhyi4QwSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAES8C4CdAx51/Hk3pAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAQwJ0DDlEwxkkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk4F0E6BjyruPJvSEBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABhwToGHKIhjNIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIwLsI0DHkXceTe0MCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACDgnQMeQQDWeQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQgHcRoGPIu44n94YESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEHBKgY8ghGs4gARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAe8i8P/kFu9JFZ12fgAAAABJRU5ErkJggg==" + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" } - }, - "cell_type": "markdown", - "id": "0cf240cf", - "metadata": {}, - "source": [ - "![image.png](attachment:image.png)" - ] - }, - { - "cell_type": "markdown", - "id": "4b9fc573", - "metadata": {}, - "source": [ - "To share your creation, you can click \"open in new tab\" in the right-hand drawer, and then click \"Publish\" to save it to wandb." - ] - }, - { - "cell_type": "markdown", - "id": "9a4092e3", - "metadata": {}, - "source": [ - "### Bonus: Weaving everything together" - ] - }, - { - "cell_type": "markdown", - "id": "4f211f9e", - "metadata": {}, - "source": [ - "Here's a **Weave Board** for all these panels, defined in code. We're actively working on additional examples and tutorials to explain these concepts in more detail. To inspire some initial exploration, you can:\n", - "* **open the panel in a new window** from the right-hand drawer on hover, to view a full-page Weave Board\n", - "* **see the details of selected data** by clicking the mouse button in the bottom right of the PanelPlot, then clicking and dragging to select a subregion of points\n", - "* **customize the Board** by adding, changing, and interconnecting panels\n", - "* **save and share your work** by renaming the Board and publishing it to W&B" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e194b3ce", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " 'data': data,\n", - " 'limit': 1005,\n", - " 'limited_data': lambda data, limit: data.limit(limit),\n", - " 'embeddings': lambda limited_data: openai.openai_embed(limited_data['name'], {\"model\": \"text-embedding-ada-002\"}),\n", - " 'clusterable_projection': lambda embeddings: umap.umap_project(\n", - " embeddings, {\n", - " 'n_neighbors': 30,\n", - " 'min_dist': 0,\n", - " 'n_components': 2,\n", - " }\n", - " ),\n", - " 'clusters': lambda clusterable_projection: hdbscan.hdbscan_cluster(clusterable_projection, {\n", - " 'min_samples': 10,\n", - " 'min_cluster_size': 50\n", - " }),\n", - " 'projection': lambda embeddings: umap.umap_project(embeddings, {})\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda limited_data, projection, clusters: weave.legacy.panels.Plot(\n", - " limited_data,\n", - " x=lambda row, index: projection[index][0],\n", - " y=lambda row, index: projection[index][1],\n", - " label=lambda row, index: clusters[index],\n", - " ),\n", - " id='projection_plot',\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda projection_plot: projection_plot.selected_data(),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " )\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/AutoBoard.ipynb b/weave/legacy/examples/experimental/AutoBoard.ipynb index 0af2db14d81..239f4c53478 100644 --- a/weave/legacy/examples/experimental/AutoBoard.ipynb +++ b/weave/legacy/examples/experimental/AutoBoard.ipynb @@ -13,79 +13,6 @@ "from weave.legacy.panels_py import panel_autoboard" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6b988de", - "metadata": {}, - "outputs": [], - "source": [ - "predictions = weave.save(syndata_mon.random_predictions(10), 'predictions')\n", - "#predictions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64aef598", - "metadata": {}, - "outputs": [], - "source": [ - "panel_autoboard.auto_panels(predictions)\n", - "#weave.legacy.ops.project('shawn', 'fasion-sweep').runs().history().concat()['_step'].type" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78a3a155", - "metadata": {}, - "outputs": [], - "source": [ - "metrics = weave.save(syndata.random_metrics(10000, 10), 'syndata')\n", - "#metrics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77e03fd6", - "metadata": {}, - "outputs": [], - "source": [ - "#panel_autoboard.auto_panels(metrics)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b417fd2", - "metadata": {}, - "outputs": [], - "source": [ - "#panel_autoboard.auto_panels(weave.legacy.ops.project('shawn', 'oai-api5').run('test4').history2())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/Mutations.ipynb b/weave/legacy/examples/experimental/Mutations.ipynb index cc49a537286..9e402df99fb 100644 --- a/weave/legacy/examples/experimental/Mutations.ipynb +++ b/weave/legacy/examples/experimental/Mutations.ipynb @@ -1,70 +1,70 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "dd93a251", - "metadata": {}, - "outputs": [], - "source": [ - "import weave" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "dd93a251", + "metadata": {}, + "outputs": [], + "source": [ + "import weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddcbd7da", + "metadata": {}, + "outputs": [], + "source": [ + "# Only run this cell the first time\n", + "table_data = weave.save([{'a': 5, 'b': 6, 'c': 'hello'}, {'a': 7, 'b': 9, 'c': 'goodbye'}], name='my-table:latest')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0063dcd0", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = weave.legacy.weave.ops.get(\"local-artifact:///my-table:latest/obj\")\n", + "dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b310adec", + "metadata": {}, + "outputs": [], + "source": [ + "# You can mutate data in the UI.\n", + "# In the panel above, switch the c column Panel to StringEditor,\n", + "# then edit one of the cells.\n", + "# To see the changes were saved, rerun cell 3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf92cd4e", + "metadata": {}, + "outputs": [], + "source": [ + "# Or you can mutate data from Python. You can press the \"play\" icon in the UI to make it live update.\n", + "for i in range(10):\n", + " dataset.append({'a': 0, 'b': i, 'c': 'k'})" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "ddcbd7da", - "metadata": {}, - "outputs": [], - "source": [ - "# Only run this cell the first time\n", - "table_data = weave.save([{'a': 5, 'b': 6, 'c': 'hello'}, {'a': 7, 'b': 9, 'c': 'goodbye'}], name='my-table:latest')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0063dcd0", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = weave.legacy.ops.get(\"local-artifact:///my-table:latest/obj\")\n", - "dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b310adec", - "metadata": {}, - "outputs": [], - "source": [ - "# You can mutate data in the UI.\n", - "# In the panel above, switch the c column Panel to StringEditor,\n", - "# then edit one of the cells.\n", - "# To see the changes were saved, rerun cell 3." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cf92cd4e", - "metadata": {}, - "outputs": [], - "source": [ - "# Or you can mutate data from Python. You can press the \"play\" icon in the UI to make it live update.\n", - "for i in range(10):\n", - " dataset.append({'a': 0, 'b': i, 'c': 'k'})" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/ProductionMonitoring/ProductionMonitoringConceptualOverview.ipynb b/weave/legacy/examples/experimental/ProductionMonitoring/ProductionMonitoringConceptualOverview.ipynb index 84fd549f7c7..0a4bd3d3ac2 100644 --- a/weave/legacy/examples/experimental/ProductionMonitoring/ProductionMonitoringConceptualOverview.ipynb +++ b/weave/legacy/examples/experimental/ProductionMonitoring/ProductionMonitoringConceptualOverview.ipynb @@ -1,358 +1,358 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "36a9f92a", - "metadata": {}, - "source": [ - "# W&B Production Monitoring Overview\n", - "\n", - "This notebook demonstrates how to monitor production models with W&B through an illustrative example. We will train a model to correctly identify handwritten digits, then monitor a locally deployed version of the model. We create a gradio app which runs in the notebook and lets a user draw/\"handwrite\" characters with the mouse and give live feedback by labeling the character as the digit 0-9.\n", - "\n", - "_Note: To keep the example focused on important code, much of the dataset manipulation, modelling, and other utilities are packaged in local files and imported here_\n", - "\n", - "# Step 0: Setup & import dependencies" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "36a9f92a", + "metadata": {}, + "source": [ + "# W&B Production Monitoring Overview\n", + "\n", + "This notebook demonstrates how to monitor production models with W&B through an illustrative example. We will train a model to correctly identify handwritten digits, then monitor a locally deployed version of the model. We create a gradio app which runs in the notebook and lets a user draw/\"handwrite\" characters with the mouse and give live feedback by labeling the character as the digit 0-9.\n", + "\n", + "_Note: To keep the example focused on important code, much of the dataset manipulation, modelling, and other utilities are packaged in local files and imported here_\n", + "\n", + "# Step 0: Setup & import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3af52605", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install tensorflow\n", + "!pip install gradio\n", + "!pip install weave" + ] + }, + { + "cell_type": "markdown", + "id": "55afcd6c", + "metadata": {}, + "source": [ + "Log in to W&B to sync these examples to your W&B account, where you can view, interact with, and customize the resulting Tables and Boards." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9e3e332", + "metadata": {}, + "outputs": [], + "source": [ + "import wandb\n", + "wandb.login()" + ] + }, + { + "cell_type": "markdown", + "id": "48354a22", + "metadata": {}, + "source": [ + "Set your W&B entity (username or team name) and optionally rename the destination project." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "424dc619", + "metadata": {}, + "outputs": [], + "source": [ + "WB_ENTITY = \"shawn\"\n", + "WB_PROJECT = \"prodmon_mnist\"" + ] + }, + { + "cell_type": "markdown", + "id": "8daaf18d", + "metadata": {}, + "source": [ + "# Step 1: Get data\n", + "In this example, we will use `keras.datasets.mnist.load_data()` to load in the MNIST dataset. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de553636", + "metadata": {}, + "outputs": [], + "source": [ + "import model_util\n", + "\n", + "dataset = model_util.get_dataset()\n", + "model_util.image_from_array(dataset[0][0])" + ] + }, + { + "cell_type": "markdown", + "id": "6822a544", + "metadata": {}, + "source": [ + "# Step 2: Train model\n", + "Next we will train a classic NN to predict the digits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39bf1bc2", + "metadata": {}, + "outputs": [], + "source": [ + "model = model_util.train_model(*dataset, conv_layers=0, epochs=1) # 1 epoch so we can actually see some errors" + ] + }, + { + "cell_type": "markdown", + "id": "68a38ddd", + "metadata": {}, + "source": [ + "# Step 3: Query model\n", + "Now, let's query the model! Normally there is a little pre- and post- processing needed to make a prediction - we will write a short function to handle this for us." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b8afe99", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import json\n", + "\n", + "def simple_predict(image_arr):\n", + " # Prepare image for model\n", + " tensor = (image_arr.astype(\"float32\")).reshape(1, 28, 28, 1)\n", + "\n", + " # Make the prediction\n", + " prediction = model.predict(tensor, verbose=False)\n", + "\n", + " # In this application, we need to reshape the output:\n", + " raw_predictions = prediction[0].tolist()\n", + " logits = {\n", + " str(k): v for k, v in zip(range(10), raw_predictions)\n", + " }\n", + " \n", + " prediction = np.argmax(raw_predictions).tolist()\n", + " \n", + " return {\"logits\": logits, \"prediction\": prediction}\n", + "\n", + "_, _, x_test, y_test = dataset\n", + "for i in range(10):\n", + " image_arr = x_test[i]\n", + " truth = y_test[i]\n", + " preds = simple_predict(image_arr)\n", + " \n", + " print(f\"Input: {truth}\")\n", + " display(model_util.image_from_array(image_arr))\n", + " print(f\"Prediction: {preds['prediction']}\")\n", + " print(f\"Logits: {json.dumps(preds['logits'], indent=2)}\")\n", + " print(\"\")\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "8826acf8", + "metadata": {}, + "source": [ + "# Step 3A: Save predictions with W&B Weave using StreamTable\n", + "With W&B's Weave library, we can stream any data to W&B for storage and further analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c59bccb", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.monitoring import StreamTable\n", + "\n", + "# Initialize a stream table\n", + "# (optionally change the name argument to any string\n", + "# that follows the wandbentity_name/project_name/table_name format)\n", + "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/logged_predictions\")\n", + "_, _, x_test, y_test = dataset\n", + "for i in range(100):\n", + " image_arr = x_test[i]\n", + " truth = y_test[i].tolist()\n", + " preds = simple_predict(image_arr)\n", + " \n", + " # Log the data\n", + " st.log({\n", + " **preds,\n", + " \"image\": model_util.image_from_array(image_arr),\n", + " \"truth\": truth\n", + " })\n", + "\n", + "# Optional: wait for the logs to finish uploading (nicer for live demos)\n", + "st.finish()\n", + "\n", + "# Show the StreamTable\n", + "st " + ] + }, + { + "cell_type": "markdown", + "id": "dcb0cc29", + "metadata": {}, + "source": [ + "# Step 3B: Save predictions with W&B Weave using `monitor` decorator\n", + "This pattern of logging inputs and outputs of a functions is so common, that we provide a decorator which automatically logs a function's I/O." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90ab5fe4", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.monitoring import monitor\n", + "import numpy as np\n", + "\n", + "mon = monitor.init_monitor(f\"{WB_ENTITY}/{WB_PROJECT}/monitor_predict_function\")\n", + "\n", + "def preprocess(span):\n", + " span.inputs['image'] = model_util.image_from_array(span.inputs['image_arr'])\n", + " del span.inputs['image_arr']\n", + "\n", + "@mon.trace(\n", + " # An preprocessor allows the function arguments to be pre-processed before logging.\n", + " preprocess = preprocess\n", + ")\n", + "def monitor_predict(image_arr):\n", + " # Prepare image for model\n", + " tensor = (image_arr.astype(\"float32\")).reshape(1, 28, 28, 1)\n", + "\n", + " # Make the prediction\n", + " prediction = model.predict(tensor, verbose=False)\n", + "\n", + " # In this application, we need to reshape the output:\n", + " raw_predictions = prediction[0].tolist()\n", + " logits = {\n", + " str(k): v for k, v in zip(range(10), raw_predictions)\n", + " }\n", + " \n", + " prediction = np.argmax(raw_predictions).tolist()\n", + " \n", + " return {\"logits\": logits, \"prediction\": prediction}\n", + "\n", + "_, _, x_test, y_test = dataset\n", + "for i in range(100):\n", + " image_arr = x_test[i]\n", + " truth = y_test[i].tolist()\n", + " # Use the added monitor_attributes argument to add additional data\n", + " preds = monitor_predict(image_arr, monitor_attributes={'truth': truth})" + ] + }, + { + "cell_type": "markdown", + "id": "d3896993", + "metadata": {}, + "source": [ + "# Step 4: End-to-end example\n", + "Typically a production application will contain a prediction service that provides predictions to a client. To demonstrate this in a notebook, we will create a `PredictionService` and an `AppUI`: a small interface which lets the user to draw an image, view the prediction, and give feedback on a result (in this case, correctly label a handdrawn digit 0-9). These communicate via `predict` and `record_feedback` methods. \n", + "\n", + "Note: this is purely for example purposes—your production systems may widely vary in structure_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efc97d6a", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: not yet working with new API\n", + "\n", + "# import app_util\n", + "# from weave.legacy.weave.monitoring import monitor\n", + "# import PIL\n", + "# import numpy as np\n", + "\n", + "# class PredictionService(app_util.PredictionServiceInterface):\n", + "# def __init__(self, model):\n", + "# self.model = model\n", + "# self.last_prediction = {}\n", + " \n", + "# @monitor(auto_log = False, entity_name=WB_ENTITY, project_name=WB_PROJECT)\n", + "# def _raw_predict(self, pil_image: PIL.Image) -> dict:\n", + "# # Prepare image for model\n", + "# tensor = (np.array(pil_image.resize((28, 28))).astype(\"float32\") / 255).reshape(1, 28, 28, 1)\n", + "\n", + "# # Make the prediction\n", + "# prediction = self.model.predict(tensor, verbose=False)\n", + "\n", + "# # In this application, we need to reshape the output:\n", + "# raw_predictions = prediction[0].tolist()\n", + "# logits = {\n", + "# str(k): v for k, v in zip(range(10), raw_predictions)\n", + "# }\n", + "\n", + "# prediction = np.argmax(raw_predictions).tolist()\n", + "\n", + "# return {\"logits\": logits, \"prediction\": prediction}\n", + " \n", + "# def _update_last_prediction(self, prediction) -> None:\n", + "# if len(self.last_prediction) > 0:\n", + "# last_pred = self.last_prediction.pop(list(self.last_prediction.keys())[0])\n", + "# last_pred.finalize()\n", + "# self.last_prediction[prediction.id] = prediction\n", + "\n", + " \n", + "# def predict(self, pil_image: PIL.Image) -> app_util.Prediction:\n", + "# record = self._raw_predict(pil_image)\n", + " \n", + "# # Cache the last prediction for ground_truth recording\n", + "# self._update_last_prediction(record)\n", + " \n", + "# # Return the prediction\n", + "# return app_util.Prediction(record.get()['logits'], record.id)\n", + " \n", + "# def record_feedback(self, prediction_id: str, feedback: int) -> None:\n", + "# if prediction_id not in self.last_prediction:\n", + "# return\n", + "\n", + "# # Get the past prediction\n", + "# prediction = self.last_prediction.pop(prediction_id)\n", + " \n", + "# # Save the user feedback\n", + "# prediction.add_data({'user_feedback': feedback})\n", + " \n", + "# # Log the results\n", + "# prediction.finalize()\n", + " \n", + "# app_util.render_app(PredictionService(model))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "3af52605", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install tensorflow\n", - "!pip install gradio\n", - "!pip install weave" - ] - }, - { - "cell_type": "markdown", - "id": "55afcd6c", - "metadata": {}, - "source": [ - "Log in to W&B to sync these examples to your W&B account, where you can view, interact with, and customize the resulting Tables and Boards." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c9e3e332", - "metadata": {}, - "outputs": [], - "source": [ - "import wandb\n", - "wandb.login()" - ] - }, - { - "cell_type": "markdown", - "id": "48354a22", - "metadata": {}, - "source": [ - "Set your W&B entity (username or team name) and optionally rename the destination project." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "424dc619", - "metadata": {}, - "outputs": [], - "source": [ - "WB_ENTITY = \"shawn\"\n", - "WB_PROJECT = \"prodmon_mnist\"" - ] - }, - { - "cell_type": "markdown", - "id": "8daaf18d", - "metadata": {}, - "source": [ - "# Step 1: Get data\n", - "In this example, we will use `keras.datasets.mnist.load_data()` to load in the MNIST dataset. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de553636", - "metadata": {}, - "outputs": [], - "source": [ - "import model_util\n", - "\n", - "dataset = model_util.get_dataset()\n", - "model_util.image_from_array(dataset[0][0])" - ] - }, - { - "cell_type": "markdown", - "id": "6822a544", - "metadata": {}, - "source": [ - "# Step 2: Train model\n", - "Next we will train a classic NN to predict the digits" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "39bf1bc2", - "metadata": {}, - "outputs": [], - "source": [ - "model = model_util.train_model(*dataset, conv_layers=0, epochs=1) # 1 epoch so we can actually see some errors" - ] - }, - { - "cell_type": "markdown", - "id": "68a38ddd", - "metadata": {}, - "source": [ - "# Step 3: Query model\n", - "Now, let's query the model! Normally there is a little pre- and post- processing needed to make a prediction - we will write a short function to handle this for us." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b8afe99", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import json\n", - "\n", - "def simple_predict(image_arr):\n", - " # Prepare image for model\n", - " tensor = (image_arr.astype(\"float32\")).reshape(1, 28, 28, 1)\n", - "\n", - " # Make the prediction\n", - " prediction = model.predict(tensor, verbose=False)\n", - "\n", - " # In this application, we need to reshape the output:\n", - " raw_predictions = prediction[0].tolist()\n", - " logits = {\n", - " str(k): v for k, v in zip(range(10), raw_predictions)\n", - " }\n", - " \n", - " prediction = np.argmax(raw_predictions).tolist()\n", - " \n", - " return {\"logits\": logits, \"prediction\": prediction}\n", - "\n", - "_, _, x_test, y_test = dataset\n", - "for i in range(10):\n", - " image_arr = x_test[i]\n", - " truth = y_test[i]\n", - " preds = simple_predict(image_arr)\n", - " \n", - " print(f\"Input: {truth}\")\n", - " display(model_util.image_from_array(image_arr))\n", - " print(f\"Prediction: {preds['prediction']}\")\n", - " print(f\"Logits: {json.dumps(preds['logits'], indent=2)}\")\n", - " print(\"\")\n", - " " - ] - }, - { - "cell_type": "markdown", - "id": "8826acf8", - "metadata": {}, - "source": [ - "# Step 3A: Save predictions with W&B Weave using StreamTable\n", - "With W&B's Weave library, we can stream any data to W&B for storage and further analysis." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c59bccb", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.monitoring import StreamTable\n", - "\n", - "# Initialize a stream table\n", - "# (optionally change the name argument to any string\n", - "# that follows the wandbentity_name/project_name/table_name format)\n", - "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/logged_predictions\")\n", - "_, _, x_test, y_test = dataset\n", - "for i in range(100):\n", - " image_arr = x_test[i]\n", - " truth = y_test[i].tolist()\n", - " preds = simple_predict(image_arr)\n", - " \n", - " # Log the data\n", - " st.log({\n", - " **preds,\n", - " \"image\": model_util.image_from_array(image_arr),\n", - " \"truth\": truth\n", - " })\n", - "\n", - "# Optional: wait for the logs to finish uploading (nicer for live demos)\n", - "st.finish()\n", - "\n", - "# Show the StreamTable\n", - "st " - ] - }, - { - "cell_type": "markdown", - "id": "dcb0cc29", - "metadata": {}, - "source": [ - "# Step 3B: Save predictions with W&B Weave using `monitor` decorator\n", - "This pattern of logging inputs and outputs of a functions is so common, that we provide a decorator which automatically logs a function's I/O." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "90ab5fe4", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.monitoring import monitor\n", - "import numpy as np\n", - "\n", - "mon = monitor.init_monitor(f\"{WB_ENTITY}/{WB_PROJECT}/monitor_predict_function\")\n", - "\n", - "def preprocess(span):\n", - " span.inputs['image'] = model_util.image_from_array(span.inputs['image_arr'])\n", - " del span.inputs['image_arr']\n", - "\n", - "@mon.trace(\n", - " # An preprocessor allows the function arguments to be pre-processed before logging.\n", - " preprocess = preprocess\n", - ")\n", - "def monitor_predict(image_arr):\n", - " # Prepare image for model\n", - " tensor = (image_arr.astype(\"float32\")).reshape(1, 28, 28, 1)\n", - "\n", - " # Make the prediction\n", - " prediction = model.predict(tensor, verbose=False)\n", - "\n", - " # In this application, we need to reshape the output:\n", - " raw_predictions = prediction[0].tolist()\n", - " logits = {\n", - " str(k): v for k, v in zip(range(10), raw_predictions)\n", - " }\n", - " \n", - " prediction = np.argmax(raw_predictions).tolist()\n", - " \n", - " return {\"logits\": logits, \"prediction\": prediction}\n", - "\n", - "_, _, x_test, y_test = dataset\n", - "for i in range(100):\n", - " image_arr = x_test[i]\n", - " truth = y_test[i].tolist()\n", - " # Use the added monitor_attributes argument to add additional data\n", - " preds = monitor_predict(image_arr, monitor_attributes={'truth': truth})" - ] - }, - { - "cell_type": "markdown", - "id": "d3896993", - "metadata": {}, - "source": [ - "# Step 4: End-to-end example\n", - "Typically a production application will contain a prediction service that provides predictions to a client. To demonstrate this in a notebook, we will create a `PredictionService` and an `AppUI`: a small interface which lets the user to draw an image, view the prediction, and give feedback on a result (in this case, correctly label a handdrawn digit 0-9). These communicate via `predict` and `record_feedback` methods. \n", - "\n", - "Note: this is purely for example purposes—your production systems may widely vary in structure_" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efc97d6a", - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: not yet working with new API\n", - "\n", - "# import app_util\n", - "# from weave.legacy.monitoring import monitor\n", - "# import PIL\n", - "# import numpy as np\n", - "\n", - "# class PredictionService(app_util.PredictionServiceInterface):\n", - "# def __init__(self, model):\n", - "# self.model = model\n", - "# self.last_prediction = {}\n", - " \n", - "# @monitor(auto_log = False, entity_name=WB_ENTITY, project_name=WB_PROJECT)\n", - "# def _raw_predict(self, pil_image: PIL.Image) -> dict:\n", - "# # Prepare image for model\n", - "# tensor = (np.array(pil_image.resize((28, 28))).astype(\"float32\") / 255).reshape(1, 28, 28, 1)\n", - "\n", - "# # Make the prediction\n", - "# prediction = self.model.predict(tensor, verbose=False)\n", - "\n", - "# # In this application, we need to reshape the output:\n", - "# raw_predictions = prediction[0].tolist()\n", - "# logits = {\n", - "# str(k): v for k, v in zip(range(10), raw_predictions)\n", - "# }\n", - "\n", - "# prediction = np.argmax(raw_predictions).tolist()\n", - "\n", - "# return {\"logits\": logits, \"prediction\": prediction}\n", - " \n", - "# def _update_last_prediction(self, prediction) -> None:\n", - "# if len(self.last_prediction) > 0:\n", - "# last_pred = self.last_prediction.pop(list(self.last_prediction.keys())[0])\n", - "# last_pred.finalize()\n", - "# self.last_prediction[prediction.id] = prediction\n", - "\n", - " \n", - "# def predict(self, pil_image: PIL.Image) -> app_util.Prediction:\n", - "# record = self._raw_predict(pil_image)\n", - " \n", - "# # Cache the last prediction for ground_truth recording\n", - "# self._update_last_prediction(record)\n", - " \n", - "# # Return the prediction\n", - "# return app_util.Prediction(record.get()['logits'], record.id)\n", - " \n", - "# def record_feedback(self, prediction_id: str, feedback: int) -> None:\n", - "# if prediction_id not in self.last_prediction:\n", - "# return\n", - "\n", - "# # Get the past prediction\n", - "# prediction = self.last_prediction.pop(prediction_id)\n", - " \n", - "# # Save the user feedback\n", - "# prediction.add_data({'user_feedback': feedback})\n", - " \n", - "# # Log the results\n", - "# prediction.finalize()\n", - " \n", - "# app_util.render_app(PredictionService(model))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/ProductionMonitoring/StreamTable.md b/weave/legacy/examples/experimental/ProductionMonitoring/StreamTable.md index c2878b9e37c..b353164c22d 100644 --- a/weave/legacy/examples/experimental/ProductionMonitoring/StreamTable.md +++ b/weave/legacy/examples/experimental/ProductionMonitoring/StreamTable.md @@ -11,7 +11,7 @@ A Weave StreamTable object enables continuous streaming of data from an applicat The only required argument to create a StreamTable is the name of the StreamTable object. ```python -from weave.legacy.monitoring import StreamTable +from weave.legacy.weave.monitoring import StreamTable st = StreamTable("my_entity_name/my_project_name/my_table_name") ``` diff --git a/weave/legacy/examples/experimental/ProductionMonitoring/stream_table_api.ipynb b/weave/legacy/examples/experimental/ProductionMonitoring/stream_table_api.ipynb index ec47a481774..6b106621520 100644 --- a/weave/legacy/examples/experimental/ProductionMonitoring/stream_table_api.ipynb +++ b/weave/legacy/examples/experimental/ProductionMonitoring/stream_table_api.ipynb @@ -1,160 +1,160 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "eccb44d3", - "metadata": {}, - "source": [ - "# Weave StreamTable Usage\n", - "\n", - "This notebook demonstrates basic Weave StreamTable usage with interactive examples.\n", - "\n", - "## Step 0: Setup\n", - "\n", - "All the StreamTables created in this notebook will be saved to the WB_PROJECT under the WB_ENTITY account on the public W&B cloud. \n", - "\n", - "**Please login to W&B and set your WB_ENTITY** before running this demo. If entity is not provided explicitly, this will attempt to default to the current logged-in entity if one is available." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "eccb44d3", + "metadata": {}, + "source": [ + "# Weave StreamTable Usage\n", + "\n", + "This notebook demonstrates basic Weave StreamTable usage with interactive examples.\n", + "\n", + "## Step 0: Setup\n", + "\n", + "All the StreamTables created in this notebook will be saved to the WB_PROJECT under the WB_ENTITY account on the public W&B cloud. \n", + "\n", + "**Please login to W&B and set your WB_ENTITY** before running this demo. If entity is not provided explicitly, this will attempt to default to the current logged-in entity if one is available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e034bf1", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install weave\n", + "import wandb\n", + "wandb.login()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51c30e7a", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.monitoring import StreamTable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5684f0b8", + "metadata": {}, + "outputs": [], + "source": [ + "STREAM_TABLE_NAME = \"my_stream_table\"\n", + "WB_PROJECT = \"mesa\"\n", + "WB_ENTITY = \"\"" + ] + }, + { + "cell_type": "markdown", + "id": "6f676d96", + "metadata": {}, + "source": [ + "## Step 1: Define a StreamTable\n", + "\n", + "StreamTable has a single required argument: the name of the StreamTable object.\n", + "\n", + "```python\n", + "st = StreamTable(\"stacey/mesa/my_stream_table\")\n", + "```\n", + "\n", + "This takes the form `my_wb_entity/my_wb_project_name/my_stream_table_name` where you can modify the component names to the relevant strings (e.g. your W&B username or shared W&B team name, a new or existing W&B project name).\n", + "\n", + "## Step 2: Log some data\n", + "\n", + "To add rows to the StreamTable, call `.log()` on the StreamTable object. \n", + "`.log()` accepts a single dictionary or a list of dictionaries, where each dictionary entry corresponds to one row of the table. In each dictionary, the keys are column names and the values are the corresponding cell values.\n", + "\n", + "```python\n", + "st.log({\"one_column_name\" : \"value_a\", \"another_column_name\" : 7})\n", + "st.log([{\"one_column_name\" : \"value_b\", \"another_column_name\" : 19},\n", + " {\"one_column_name\" : \"value_c\", \"another_column_name\" : 28},\n", + " {\"one_column_name\" : \"value_d\", \"another_column_name\" : 36}]\n", + "```\n", + "\n", + "The first call to `.log()` will return a Weave Panel URL, where you can view, edit, and save the resulting StreamTable as a Weave Board, of the form:\n", + "\n", + "View data at: https://weave.wandb.ai/?exp=get%28%0A++++%22wandb-artifact%3A%2F%2F%2Fstacey%2Fmesa%2Fmy_stream_table%3Alatest%2Fobj%22%29%0A++.rows" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b928c2e0", + "metadata": {}, + "outputs": [], + "source": [ + "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/{STREAM_TABLE_NAME}\")\n", + "\n", + "# log data to the StreamTable as a dictionary or list of dictionaries\n", + "st.log({\"col_a\" : \"1\", \"col_b\" : \"17\", \"col_c\" : \"42\"})\n", + "\n", + "# show the StreamTable\n", + "st" + ] + }, + { + "cell_type": "markdown", + "id": "83cec916", + "metadata": {}, + "source": [ + "All log calls on a given StreamTable instance will append the given rows to that instance.\n", + "\n", + "In a notebook, the StreamTable variable on a line by itself will return a Weave Panel view of the StreamTable. The StreamTable will contain all the logged columns and their values, as well as a `timestamp` column indicating when the row was logged. By default, rows will be ordered by oldest first. You can modify a StreamTable Panel from the UI to sort by columns, group by column values, filter for specific ranges or values, etc.\n", + "\n", + "**Note:** If you would like to customize and save a specific view of a StreamTable Panel, open the StreamTable Panel in a new window as a Board and edit/save a Board from this seed panel. There are two options to achieve this:\n", + "* via the weave.wandb.ai/?exp=... URL\n", + "* via \"Open in new tab\" arrow button, revealed in the menu when you hover on the right side of a StreamTable panel displayed in the notebok)\n", + "\n", + "## Step 3: Log more data & explore the results!\n", + "\n", + "Continue logging as much data as you like to any StreamTable instance. You can keep a reference to a given Python StreamTable object in your notebook session or script, and you can reconnect to the same StreamTable instance across multiple sessions/runs of your script via the StreamTable's unique name (e.g. `st = StreamTable(\"stacey/mesa/my_stream_table\")` ) and keep adding rows. Multiple/parallel processes writing to the same StreamTable are also supported—the server will use a queue to order any concurrent messages.\n", + "\n", + "If you save the StreamTable Panel as a Board, the Board will continue to update as you send more data to the same StreamTable instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "294d68c0", + "metadata": {}, + "outputs": [], + "source": [ + "st.log({\"col_a\" : 5, \"col_b\" : -24, \"col_c\" : \"hello\"})\n", + "st.log([{\"col_a\" : 255, \"col_b\" : 3.1415926, \"col_c\" : \"hi!\"}])\n", + "\n", + "# optional: wait for all the rows to finish logging before loading\n", + "st.finish()\n", + "\n", + "st" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e034bf1", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install weave\n", - "import wandb\n", - "wandb.login()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51c30e7a", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.monitoring import StreamTable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5684f0b8", - "metadata": {}, - "outputs": [], - "source": [ - "STREAM_TABLE_NAME = \"my_stream_table\"\n", - "WB_PROJECT = \"mesa\"\n", - "WB_ENTITY = \"\"" - ] - }, - { - "cell_type": "markdown", - "id": "6f676d96", - "metadata": {}, - "source": [ - "## Step 1: Define a StreamTable\n", - "\n", - "StreamTable has a single required argument: the name of the StreamTable object.\n", - "\n", - "```python\n", - "st = StreamTable(\"stacey/mesa/my_stream_table\")\n", - "```\n", - "\n", - "This takes the form `my_wb_entity/my_wb_project_name/my_stream_table_name` where you can modify the component names to the relevant strings (e.g. your W&B username or shared W&B team name, a new or existing W&B project name).\n", - "\n", - "## Step 2: Log some data\n", - "\n", - "To add rows to the StreamTable, call `.log()` on the StreamTable object. \n", - "`.log()` accepts a single dictionary or a list of dictionaries, where each dictionary entry corresponds to one row of the table. In each dictionary, the keys are column names and the values are the corresponding cell values.\n", - "\n", - "```python\n", - "st.log({\"one_column_name\" : \"value_a\", \"another_column_name\" : 7})\n", - "st.log([{\"one_column_name\" : \"value_b\", \"another_column_name\" : 19},\n", - " {\"one_column_name\" : \"value_c\", \"another_column_name\" : 28},\n", - " {\"one_column_name\" : \"value_d\", \"another_column_name\" : 36}]\n", - "```\n", - "\n", - "The first call to `.log()` will return a Weave Panel URL, where you can view, edit, and save the resulting StreamTable as a Weave Board, of the form:\n", - "\n", - "View data at: https://weave.wandb.ai/?exp=get%28%0A++++%22wandb-artifact%3A%2F%2F%2Fstacey%2Fmesa%2Fmy_stream_table%3Alatest%2Fobj%22%29%0A++.rows" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b928c2e0", - "metadata": {}, - "outputs": [], - "source": [ - "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/{STREAM_TABLE_NAME}\")\n", - "\n", - "# log data to the StreamTable as a dictionary or list of dictionaries\n", - "st.log({\"col_a\" : \"1\", \"col_b\" : \"17\", \"col_c\" : \"42\"})\n", - "\n", - "# show the StreamTable\n", - "st" - ] - }, - { - "cell_type": "markdown", - "id": "83cec916", - "metadata": {}, - "source": [ - "All log calls on a given StreamTable instance will append the given rows to that instance.\n", - "\n", - "In a notebook, the StreamTable variable on a line by itself will return a Weave Panel view of the StreamTable. The StreamTable will contain all the logged columns and their values, as well as a `timestamp` column indicating when the row was logged. By default, rows will be ordered by oldest first. You can modify a StreamTable Panel from the UI to sort by columns, group by column values, filter for specific ranges or values, etc.\n", - "\n", - "**Note:** If you would like to customize and save a specific view of a StreamTable Panel, open the StreamTable Panel in a new window as a Board and edit/save a Board from this seed panel. There are two options to achieve this:\n", - "* via the weave.wandb.ai/?exp=... URL\n", - "* via \"Open in new tab\" arrow button, revealed in the menu when you hover on the right side of a StreamTable panel displayed in the notebok)\n", - "\n", - "## Step 3: Log more data & explore the results!\n", - "\n", - "Continue logging as much data as you like to any StreamTable instance. You can keep a reference to a given Python StreamTable object in your notebook session or script, and you can reconnect to the same StreamTable instance across multiple sessions/runs of your script via the StreamTable's unique name (e.g. `st = StreamTable(\"stacey/mesa/my_stream_table\")` ) and keep adding rows. Multiple/parallel processes writing to the same StreamTable are also supported—the server will use a queue to order any concurrent messages.\n", - "\n", - "If you save the StreamTable Panel as a Board, the Board will continue to update as you send more data to the same StreamTable instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "294d68c0", - "metadata": {}, - "outputs": [], - "source": [ - "st.log({\"col_a\" : 5, \"col_b\" : -24, \"col_c\" : \"hello\"})\n", - "st.log([{\"col_a\" : 255, \"col_b\" : 3.1415926, \"col_c\" : \"hi!\"}])\n", - "\n", - "# optional: wait for all the rows to finish logging before loading\n", - "st.finish()\n", - "\n", - "st" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/Table Summary Panel.ipynb b/weave/legacy/examples/experimental/Table Summary Panel.ipynb index 11f63f7ba68..75897f7f465 100644 --- a/weave/legacy/examples/experimental/Table Summary Panel.ipynb +++ b/weave/legacy/examples/experimental/Table Summary Panel.ipynb @@ -1,95 +1,95 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9f32c0f", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "import xgboost\n", - "from weave.legacy.ecosystem import sklearn" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e9f32c0f", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "import xgboost\n", + "from weave.legacy.weave.ecosystem import sklearn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50ff1335", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = weave.use(sklearn.ca_housing_dataset(1))\n", + "dataset_arrow = weave.legacy.weave.ops.dataframe_to_arrow(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f9dcd73", + "metadata": {}, + "outputs": [], + "source": [ + "import typing\n", + "\n", + "@weave.op()\n", + "def table_summary(table: typing.Any) -> list[weave.legacy.weave.panels.Panel]:\n", + " if not table:\n", + " # TODO: type\n", + " return\n", + " col_names = list(table[0].keys())\n", + " with weave.legacy.weave.context_state.lazy_execution():\n", + " cols = {col_name: weave.legacy.weave.ops.legacy.ops_arrow.dict.pick(table, col_name) for col_name in col_names}\n", + " panels = []\n", + " for col_name, col_values in cols.items():\n", + " panels.append(weave.legacy.weave.panels.LabeledItem(item=col_values, label=col_name))\n", + " return panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d464ef4", + "metadata": {}, + "outputs": [], + "source": [ + "data = weave.save(dataset_arrow, 'my-data')\n", + "table_summary(dataset_arrow)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47c4cdd9", + "metadata": {}, + "outputs": [], + "source": [ + "# Currently broken\n", + "#weave.publish(table_summary)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "50ff1335", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = weave.use(sklearn.ca_housing_dataset(1))\n", - "dataset_arrow = weave.legacy.ops.dataframe_to_arrow(dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f9dcd73", - "metadata": {}, - "outputs": [], - "source": [ - "import typing\n", - "\n", - "@weave.op()\n", - "def table_summary(table: typing.Any) -> list[weave.legacy.panels.Panel]:\n", - " if not table:\n", - " # TODO: type\n", - " return\n", - " col_names = list(table[0].keys())\n", - " with weave.legacy.context_state.lazy_execution():\n", - " cols = {col_name: weave.legacy.ops.legacy.ops_arrow.dict.pick(table, col_name) for col_name in col_names}\n", - " panels = []\n", - " for col_name, col_values in cols.items():\n", - " panels.append(weave.legacy.panels.LabeledItem(item=col_values, label=col_name))\n", - " return panels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d464ef4", - "metadata": {}, - "outputs": [], - "source": [ - "data = weave.save(dataset_arrow, 'my-data')\n", - "table_summary(dataset_arrow)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "47c4cdd9", - "metadata": {}, - "outputs": [], - "source": [ - "# Currently broken\n", - "#weave.publish(table_summary)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/app/Embeddings.ipynb b/weave/legacy/examples/experimental/app/Embeddings.ipynb index b777bf90fc8..221d85d77b9 100644 --- a/weave/legacy/examples/experimental/app/Embeddings.ipynb +++ b/weave/legacy/examples/experimental/app/Embeddings.ipynb @@ -1,123 +1,123 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "d3f10990", - "metadata": {}, - "source": [ - "## Embedding explorer\n", - "\n", - "- Embeds a string column using OpenAI Embeddings\n", - "- Follows the clustering / projection approach from here: https://umap-learn.readthedocs.io/en/latest/clustering.html" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "d3f10990", + "metadata": {}, + "source": [ + "## Embedding explorer\n", + "\n", + "- Embeds a string column using OpenAI Embeddings\n", + "- Follows the clustering / projection approach from here: https://umap-learn.readthedocs.io/en/latest/clustering.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13a3497f", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas\n", + "import weave\n", + "from weave.legacy.weave.ecosystem import openai\n", + "from weave.legacy.weave.ecosystem import umap\n", + "from weave.legacy.weave.ecosystem import hdbscan" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e68f6c86", + "metadata": {}, + "outputs": [], + "source": [ + "#raw_data = pandas.read_csv('/Users/shawn/datasets/wandb_export_2023-06-03T15_01_20.066-07_00.csv')\n", + "raw_data = pandas.read_csv('wandb_export_2023-07-25T11_43_18.362-04_00.csv')\n", + "\n", + "#raw_data['request_timestamp'] = pandas.to_datetime(raw_data['request_timestamp'], unit='ms', utc=True)\n", + "\n", + "data = weave.save(weave.legacy.weave.ops.dataframe_to_arrow(raw_data), 'data')\n", + "# from weave.legacy.weave.panels_py import panel_autoboard\n", + "# panel_autoboard.auto_panels(data)\n", + "#data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "500e19b1", + "metadata": {}, + "outputs": [], + "source": [ + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e194b3ce", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'data': data,\n", + " 'limit': 1005,\n", + " 'limited_data': lambda data, limit: data.limit(limit),\n", + " 'embeddings': lambda limited_data: openai.openai_embed(limited_data['question'], {\"model\": \"text-embedding-ada-002\"}),\n", + " 'clusterable_projection': lambda embeddings: umap.umap_project(\n", + " embeddings, {\n", + " 'n_neighbors': 30,\n", + " 'min_dist': 0,\n", + " 'n_components': 2,\n", + " }\n", + " ),\n", + " 'clusters': lambda clusterable_projection: hdbscan.hdbscan_cluster(clusterable_projection, {\n", + " 'min_samples': 10,\n", + " 'min_cluster_size': 50\n", + " }),\n", + " 'projection': lambda embeddings: umap.umap_project(embeddings, {})\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda limited_data, projection, clusters: weave.legacy.weave.panels.Plot(\n", + " limited_data,\n", + " x=lambda row, index: projection[index][0],\n", + " y=lambda row, index: projection[index][1],\n", + " label=lambda row, index: clusters[index],\n", + " ),\n", + " id='projection_plot',\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda projection_plot: projection_plot.selected_data(),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " )\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "13a3497f", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas\n", - "import weave\n", - "from weave.legacy.ecosystem import openai\n", - "from weave.legacy.ecosystem import umap\n", - "from weave.legacy.ecosystem import hdbscan" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e68f6c86", - "metadata": {}, - "outputs": [], - "source": [ - "#raw_data = pandas.read_csv('/Users/shawn/datasets/wandb_export_2023-06-03T15_01_20.066-07_00.csv')\n", - "raw_data = pandas.read_csv('wandb_export_2023-07-25T11_43_18.362-04_00.csv')\n", - "\n", - "#raw_data['request_timestamp'] = pandas.to_datetime(raw_data['request_timestamp'], unit='ms', utc=True)\n", - "\n", - "data = weave.save(weave.legacy.ops.dataframe_to_arrow(raw_data), 'data')\n", - "# from weave.legacy.panels_py import panel_autoboard\n", - "# panel_autoboard.auto_panels(data)\n", - "#data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "500e19b1", - "metadata": {}, - "outputs": [], - "source": [ - "data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e194b3ce", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " 'data': data,\n", - " 'limit': 1005,\n", - " 'limited_data': lambda data, limit: data.limit(limit),\n", - " 'embeddings': lambda limited_data: openai.openai_embed(limited_data['question'], {\"model\": \"text-embedding-ada-002\"}),\n", - " 'clusterable_projection': lambda embeddings: umap.umap_project(\n", - " embeddings, {\n", - " 'n_neighbors': 30,\n", - " 'min_dist': 0,\n", - " 'n_components': 2,\n", - " }\n", - " ),\n", - " 'clusters': lambda clusterable_projection: hdbscan.hdbscan_cluster(clusterable_projection, {\n", - " 'min_samples': 10,\n", - " 'min_cluster_size': 50\n", - " }),\n", - " 'projection': lambda embeddings: umap.umap_project(embeddings, {})\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda limited_data, projection, clusters: weave.legacy.panels.Plot(\n", - " limited_data,\n", - " x=lambda row, index: projection[index][0],\n", - " y=lambda row, index: projection[index][1],\n", - " label=lambda row, index: clusters[index],\n", - " ),\n", - " id='projection_plot',\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda projection_plot: projection_plot.selected_data(),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " )\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/app/RunChain.ipynb b/weave/legacy/examples/experimental/app/RunChain.ipynb index 4c4a82a6338..02b02a89b63 100644 --- a/weave/legacy/examples/experimental/app/RunChain.ipynb +++ b/weave/legacy/examples/experimental/app/RunChain.ipynb @@ -1,177 +1,177 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "d6a6e7cf", - "metadata": {}, - "source": [ - "## Example of plotting RunChains\n", - "\n", - "As described here: https://github.com/wandb/runchain" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "d6a6e7cf", + "metadata": {}, + "source": [ + "## Example of plotting RunChains\n", + "\n", + "As described here: https://github.com/wandb/runchain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd34fae3", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baa0a8ac", + "metadata": {}, + "outputs": [], + "source": [ + "# big project, maybe 41 deep\n", + "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/2ji8emz2')\n", + "\n", + "# big project, 61 deep\n", + "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/sf0vvene')\n", + "\n", + "# big project, 141 deep\n", + "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/hja8k932')\n", + "\n", + "# 3 deep, 200 metrics, 10k steps each (the default, this is fast for CI)\n", + "chain = wandb.run_chain('shawn/run_chain_20_200_10000/h4vz7n29')\n", + "\n", + "# 14 deep, 200 metrics, 10k steps each\n", + "#chain = wandb.run_chain('shawn/run_chain_20_200_10000/6wdyzlq9')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0431fff0", + "metadata": {}, + "outputs": [], + "source": [ + "# display the chain information\n", + "chain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fd52674", + "metadata": {}, + "outputs": [], + "source": [ + "# or fetch the chain object\n", + "weave.use(chain)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee1bbb90", + "metadata": {}, + "outputs": [], + "source": [ + "weave.use(chain.segments.count())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55b0b5c3", + "metadata": {}, + "outputs": [], + "source": [ + "# view the chain's metrics table\n", + "chain.history()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67caf99a", + "metadata": {}, + "outputs": [], + "source": [ + "# Display plots from a RunChain. You need to do some resizing to seem them all currently.\n", + "\n", + "def plot_metric(run, metric_name):\n", + " return weave.legacy.weave.panels.Plot(\n", + " run.history(),\n", + " series=[\n", + " # Use two plot series, one for the avg line, and one for\n", + " # min/max area. We could reduce duplication here a bit more\n", + " # by updating the Plot API to allow common features of series\n", + " # to be factored out.\n", + " weave.legacy.weave.panels.Series(\n", + " run.history(),\n", + " select_functions={\n", + " 'x': lambda row, index: index.bin(\n", + " weave.legacy.weave.ops.numbers_bins_equal(\n", + " weave.legacy.weave.ops.make_list(a=0, b=run.history().count()), 250))[\"start\"],\n", + " 'y': lambda row: row[metric_name].avg()\n", + " },\n", + " groupby_dims=['x'],\n", + " constants=weave.legacy.weave.panels.PlotConstants(\n", + " mark='line'\n", + " )\n", + " ),\n", + " weave.legacy.weave.panels.Series(\n", + " run.history(),\n", + " select_functions={\n", + " 'x': lambda row, index: index.bin(\n", + " weave.legacy.weave.ops.numbers_bins_equal(\n", + " weave.legacy.weave.ops.make_list(a=0, b=run.history().count()), 250))[\"start\"],\n", + " 'y': lambda row: row[metric_name].min(),\n", + " 'y2': lambda row: row[metric_name].max(),\n", + " },\n", + " groupby_dims=['x'],\n", + " constants=weave.legacy.weave.panels.PlotConstants(\n", + " mark='area'\n", + " )\n", + " )\n", + " ],\n", + " )\n", + "\n", + "board = weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'run_chain': chain,\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda run_chain: weave.legacy.weave.panels.Group(\n", + " layoutMode='flow',\n", + " #gridConfig=bank.flow_nxn(2, 3),\n", + " items={k: plot_metric(run_chain, k) for k in ['metric' + str(i) for i in range(10)]}\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " )\n", + " ]\n", + ")\n", + "board" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd34fae3", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import wandb" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "baa0a8ac", - "metadata": {}, - "outputs": [], - "source": [ - "# big project, maybe 41 deep\n", - "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/2ji8emz2')\n", - "\n", - "# big project, 61 deep\n", - "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/sf0vvene')\n", - "\n", - "# big project, 141 deep\n", - "#chain = wandb.run_chain('shawn/branchtest_400_200_1000/hja8k932')\n", - "\n", - "# 3 deep, 200 metrics, 10k steps each (the default, this is fast for CI)\n", - "chain = wandb.run_chain('shawn/run_chain_20_200_10000/h4vz7n29')\n", - "\n", - "# 14 deep, 200 metrics, 10k steps each\n", - "#chain = wandb.run_chain('shawn/run_chain_20_200_10000/6wdyzlq9')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0431fff0", - "metadata": {}, - "outputs": [], - "source": [ - "# display the chain information\n", - "chain" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fd52674", - "metadata": {}, - "outputs": [], - "source": [ - "# or fetch the chain object\n", - "weave.use(chain)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee1bbb90", - "metadata": {}, - "outputs": [], - "source": [ - "weave.use(chain.segments.count())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55b0b5c3", - "metadata": {}, - "outputs": [], - "source": [ - "# view the chain's metrics table\n", - "chain.history()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67caf99a", - "metadata": {}, - "outputs": [], - "source": [ - "# Display plots from a RunChain. You need to do some resizing to seem them all currently.\n", - "\n", - "def plot_metric(run, metric_name):\n", - " return weave.legacy.panels.Plot(\n", - " run.history(),\n", - " series=[\n", - " # Use two plot series, one for the avg line, and one for\n", - " # min/max area. We could reduce duplication here a bit more\n", - " # by updating the Plot API to allow common features of series\n", - " # to be factored out.\n", - " weave.legacy.panels.Series(\n", - " run.history(),\n", - " select_functions={\n", - " 'x': lambda row, index: index.bin(\n", - " weave.legacy.ops.numbers_bins_equal(\n", - " weave.legacy.ops.make_list(a=0, b=run.history().count()), 250))[\"start\"],\n", - " 'y': lambda row: row[metric_name].avg()\n", - " },\n", - " groupby_dims=['x'],\n", - " constants=weave.legacy.panels.PlotConstants(\n", - " mark='line'\n", - " )\n", - " ),\n", - " weave.legacy.panels.Series(\n", - " run.history(),\n", - " select_functions={\n", - " 'x': lambda row, index: index.bin(\n", - " weave.legacy.ops.numbers_bins_equal(\n", - " weave.legacy.ops.make_list(a=0, b=run.history().count()), 250))[\"start\"],\n", - " 'y': lambda row: row[metric_name].min(),\n", - " 'y2': lambda row: row[metric_name].max(),\n", - " },\n", - " groupby_dims=['x'],\n", - " constants=weave.legacy.panels.PlotConstants(\n", - " mark='area'\n", - " )\n", - " )\n", - " ],\n", - " )\n", - "\n", - "board = weave.legacy.panels.Board(\n", - " vars={\n", - " 'run_chain': chain,\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda run_chain: weave.legacy.panels.Group(\n", - " layoutMode='flow',\n", - " #gridConfig=bank.flow_nxn(2, 3),\n", - " items={k: plot_metric(run_chain, k) for k in ['metric' + str(i) for i in range(10)]}\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " )\n", - " ]\n", - ")\n", - "board" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/app/scenario_compare.ipynb b/weave/legacy/examples/experimental/app/scenario_compare.ipynb index befb14b8f0a..338ef5ae813 100644 --- a/weave/legacy/examples/experimental/app/scenario_compare.ipynb +++ b/weave/legacy/examples/experimental/app/scenario_compare.ipynb @@ -1,176 +1,176 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "67551526", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem import scenario" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "67551526", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem import scenario" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e12d2d0", + "metadata": {}, + "outputs": [], + "source": [ + "# a = weave.save(5)\n", + "# b = a + 2\n", + "# weave.type_of(b)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4882d74b", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "def make_run(n_scenarios, n) -> list[scenario.ScenarioResult]:\n", + " return [\n", + " scenario.ScenarioResult(\n", + " scenario_id=str(i),\n", + " metric1=random.expovariate(n / 33),\n", + " metric2=random.gauss(42, n / 100),\n", + " metric3=random.triangular(n, 11),\n", + " metric4=random.random() * random.random() * n,\n", + " metric5=random.random(),\n", + " metric6=random.betavariate(0.01, n / 2),\n", + " )\n", + " for i in range(n_scenarios)\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "218e28e2", + "metadata": {}, + "outputs": [], + "source": [ + "#panel_input = weave.save({'baseline': make_run(10), 'candidate': make_run(10)}, name='panel_input')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "984bcb3c", + "metadata": {}, + "outputs": [], + "source": [ + "#panel_input" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e1890ca", + "metadata": {}, + "outputs": [], + "source": [ + "# make some runs\n", + "\n", + "weave.save(make_run(100, 1), name='run1:latest')\n", + "weave.save(make_run(100, 2), name='run2:latest')\n", + "weave.save(make_run(100, 3), name='run3:latest')\n", + "weave.save(make_run(100, 4), name='run4:latest')\n", + "weave.save(make_run(100, 5), name='run5:latest')\n", + "pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f2b3a72", + "metadata": {}, + "outputs": [], + "source": [ + "# Ugly. We need to compute the Weave type right now.\n", + "# TODO: Fix\n", + "from weave.legacy.weave import infer_types\n", + "run_type = weave.types.List(infer_types.python_type_to_type(scenario.ScenarioResult))\n", + "\n", + "import time\n", + "runs = weave.legacy.weave.ops.objects(run_type, 'latest', int(time.time()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89e4b718", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " items={\n", + " \"sidebar\": weave.legacy.weave.panels.Group(\n", + " style=\"width: 200px; padding: 16px;\",\n", + " items={\n", + " \"baseline\": weave.legacy.weave.panels.ObjectPicker(runs, label='baseline'),\n", + " \"candidate\": weave.legacy.weave.panels.ObjectPicker(runs, label='candidate')\n", + " }\n", + " ),\n", + " \"main\": lambda sidebar: weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"tables\": weave.legacy.weave.panels.Group(\n", + " style=\"height: 300px;\",\n", + " preferHorizontal=True,\n", + " equalSize=True,\n", + " items={\n", + " \"baseline_table\": weave.legacy.weave.panels.LabeledItem(\n", + " label=\"Baseline Table\",\n", + " item=weave.legacy.weave.ops.execute(sidebar.config.items['baseline'].config.choice).get()\n", + " ),\n", + " \"candidate_table\": weave.legacy.weave.panels.LabeledItem(\n", + " label=\"Candidate Table\",\n", + " item=weave.legacy.weave.ops.execute(sidebar.config.items['candidate'].config.choice).get()\n", + " )\n", + " }),\n", + " \"plots\": \n", + " weave.legacy.weave.panels.LabeledItem(\n", + " label='Scenario plots',\n", + " item=scenario.MetricsBankPanel(\n", + " weave.legacy.weave.ops.dict_(\n", + " baseline=weave.legacy.weave.ops.execute(sidebar.config.items['baseline'].config.choice).get(),\n", + " candidate=weave.legacy.weave.ops.execute(sidebar.config.items['candidate'].config.choice).get()\n", + " ))\n", + " )\n", + "\n", + " }\n", + " )\n", + " })\n", + "panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "6e12d2d0", - "metadata": {}, - "outputs": [], - "source": [ - "# a = weave.save(5)\n", - "# b = a + 2\n", - "# weave.type_of(b)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4882d74b", - "metadata": {}, - "outputs": [], - "source": [ - "import math\n", - "def make_run(n_scenarios, n) -> list[scenario.ScenarioResult]:\n", - " return [\n", - " scenario.ScenarioResult(\n", - " scenario_id=str(i),\n", - " metric1=random.expovariate(n / 33),\n", - " metric2=random.gauss(42, n / 100),\n", - " metric3=random.triangular(n, 11),\n", - " metric4=random.random() * random.random() * n,\n", - " metric5=random.random(),\n", - " metric6=random.betavariate(0.01, n / 2),\n", - " )\n", - " for i in range(n_scenarios)\n", - " ]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "218e28e2", - "metadata": {}, - "outputs": [], - "source": [ - "#panel_input = weave.save({'baseline': make_run(10), 'candidate': make_run(10)}, name='panel_input')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "984bcb3c", - "metadata": {}, - "outputs": [], - "source": [ - "#panel_input" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e1890ca", - "metadata": {}, - "outputs": [], - "source": [ - "# make some runs\n", - "\n", - "weave.save(make_run(100, 1), name='run1:latest')\n", - "weave.save(make_run(100, 2), name='run2:latest')\n", - "weave.save(make_run(100, 3), name='run3:latest')\n", - "weave.save(make_run(100, 4), name='run4:latest')\n", - "weave.save(make_run(100, 5), name='run5:latest')\n", - "pass" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f2b3a72", - "metadata": {}, - "outputs": [], - "source": [ - "# Ugly. We need to compute the Weave type right now.\n", - "# TODO: Fix\n", - "from weave.legacy import infer_types\n", - "run_type = weave.types.List(infer_types.python_type_to_type(scenario.ScenarioResult))\n", - "\n", - "import time\n", - "runs = weave.legacy.ops.objects(run_type, 'latest', int(time.time()))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "89e4b718", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " items={\n", - " \"sidebar\": weave.legacy.panels.Group(\n", - " style=\"width: 200px; padding: 16px;\",\n", - " items={\n", - " \"baseline\": weave.legacy.panels.ObjectPicker(runs, label='baseline'),\n", - " \"candidate\": weave.legacy.panels.ObjectPicker(runs, label='candidate')\n", - " }\n", - " ),\n", - " \"main\": lambda sidebar: weave.legacy.panels.Group(\n", - " items={\n", - " \"tables\": weave.legacy.panels.Group(\n", - " style=\"height: 300px;\",\n", - " preferHorizontal=True,\n", - " equalSize=True,\n", - " items={\n", - " \"baseline_table\": weave.legacy.panels.LabeledItem(\n", - " label=\"Baseline Table\",\n", - " item=weave.legacy.ops.execute(sidebar.config.items['baseline'].config.choice).get()\n", - " ),\n", - " \"candidate_table\": weave.legacy.panels.LabeledItem(\n", - " label=\"Candidate Table\",\n", - " item=weave.legacy.ops.execute(sidebar.config.items['candidate'].config.choice).get()\n", - " )\n", - " }),\n", - " \"plots\": \n", - " weave.legacy.panels.LabeledItem(\n", - " label='Scenario plots',\n", - " item=scenario.MetricsBankPanel(\n", - " weave.legacy.ops.dict_(\n", - " baseline=weave.legacy.ops.execute(sidebar.config.items['baseline'].config.choice).get(),\n", - " candidate=weave.legacy.ops.execute(sidebar.config.items['candidate'].config.choice).get()\n", - " ))\n", - " )\n", - "\n", - " }\n", - " )\n", - " })\n", - "panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/bert_viz.ipynb b/weave/legacy/examples/experimental/bert_viz.ipynb index a971cf76964..869a4961b13 100644 --- a/weave/legacy/examples/experimental/bert_viz.ipynb +++ b/weave/legacy/examples/experimental/bert_viz.ipynb @@ -1,71 +1,71 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "f4fcc7fa", - "metadata": {}, - "source": [ - "# BertViz + HuggingFace: Visualize Attention for Language Models\n", - "\n", - "Integrates the amazing [bertviz package](https://github.com/jessevig/bertviz) to visualize the attention for a provided language model (here loaded in from Hugging Face).\n", - "\n", - "## Settings to explore\n", - "\n", - "* model name: select a different model from HuggingFace (or add your own :)\n", - "* input text: edit the text sample on which to visualize attention \n", - "* visualization type: try toggling the output panel type to see the full attention map across heads and layers :)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d85f7aaa", - "metadata": { - "execution": { - "iopub.execute_input": "2023-06-02T01:46:20.864046Z", - "iopub.status.busy": "2023-06-02T01:46:20.863903Z", - "iopub.status.idle": "2023-06-02T01:46:26.125190Z", - "shell.execute_reply": "2023-06-02T01:46:26.124715Z" + "cells": [ + { + "cell_type": "markdown", + "id": "f4fcc7fa", + "metadata": {}, + "source": [ + "# BertViz + HuggingFace: Visualize Attention for Language Models\n", + "\n", + "Integrates the amazing [bertviz package](https://github.com/jessevig/bertviz) to visualize the attention for a provided language model (here loaded in from Hugging Face).\n", + "\n", + "## Settings to explore\n", + "\n", + "* model name: select a different model from HuggingFace (or add your own :)\n", + "* input text: edit the text sample on which to visualize attention \n", + "* visualization type: try toggling the output panel type to see the full attention map across heads and layers :)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d85f7aaa", + "metadata": { + "execution": { + "iopub.execute_input": "2023-06-02T01:46:20.864046Z", + "iopub.status.busy": "2023-06-02T01:46:20.863903Z", + "iopub.status.idle": "2023-06-02T01:46:26.125190Z", + "shell.execute_reply": "2023-06-02T01:46:26.124715Z" + } + }, + "outputs": [], + "source": [ + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem.huggingface import huggingface\n", + "\n", + "# Call a hugging face model\n", + "hf_model = huggingface().model('gpt2')\n", + "model_output = hf_model.call('The cat sat on the mat')\n", + "\n", + "# The type of .attention() is huggingface.ModelOutputType. Our bertviz panel is\n", + "# registered as the only handler for that type, so it will automatically get rendered.\n", + "\n", + "# model_output.attention()\n", + "model_output" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" } - }, - "outputs": [], - "source": [ - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem.huggingface import huggingface\n", - "\n", - "# Call a hugging face model\n", - "hf_model = huggingface().model('gpt2')\n", - "model_output = hf_model.call('The cat sat on the mat')\n", - "\n", - "# The type of .attention() is huggingface.ModelOutputType. Our bertviz panel is\n", - "# registered as the only handler for that type, so it will automatically get rendered.\n", - "\n", - "# model_output.attention()\n", - "model_output" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/composite_histograms.ipynb b/weave/legacy/examples/experimental/composite_histograms.ipynb index 08cce9ad62e..e74e0c47130 100644 --- a/weave/legacy/examples/experimental/composite_histograms.ipynb +++ b/weave/legacy/examples/experimental/composite_histograms.ipynb @@ -1,136 +1,136 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "d70e6c48", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import lens" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "d70e6c48", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import lens" + ] + }, + { + "cell_type": "markdown", + "id": "c3d89416", + "metadata": {}, + "source": [ + "# Composite Histograms\n", + "\n", + "Let's plot some histograms.\n", + "\n", + "## Base case: One series, no customization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e739d513", + "metadata": {}, + "outputs": [], + "source": [ + "lens.histogram([[1.0, 2, 3, -1.4]], 1.0, \"Composite Histogram\", ['series'])" + ] + }, + { + "cell_type": "markdown", + "id": "81fd5769", + "metadata": {}, + "source": [ + "## Two series with customization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "301be405", + "metadata": {}, + "outputs": [], + "source": [ + "lens.histogram([[-1.8, -0.4, 0.1, -0.9, -0.77, 0.5, 0.7, 0.2, 0.1, 0.8],[0.9, 0.4, 0.5, 0.6, 1.2, 1.5, 1.6, 1.7, 2, 2.4, 2.3, 2.1]], 0.5, \"My Histogram\", [\"series A\", \"series B\"])" + ] + }, + { + "cell_type": "markdown", + "id": "ee794dd4", + "metadata": {}, + "source": [ + "## Three series" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fba3acec", + "metadata": {}, + "outputs": [], + "source": [ + "lens.histogram([[1.0, 2, 3, -1.4],[8.1, 2.2, 3.3, 5.6, 1.1], [1.6, 1.7, 1.8, -2.0, 5]], 0.5, 'Composite histogram', ['a', 'b', 'c'])" + ] + }, + { + "cell_type": "markdown", + "id": "ac8ee3eb", + "metadata": {}, + "source": [ + "## More than three series :)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "070314da", + "metadata": {}, + "outputs": [], + "source": [ + "lens.histogram([[1.0, -20.0, -25.0, -15.0, 2.0, 3.0, 2.0, 2.0, 2.0, 2.0, 4.0, 5.0, 6.0], [1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 3.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0],[16.2, 17.9, 18.2, 2.3, -5.2, 6.0, 7.2, -10.9, 8.2,41.0], [1.3, 2.2, 5.3, 7.7, 8.8, 9.0, 2.0, 3.2, 43.7, 4.2, 5.1, 4.5, 6.9], [1.0, 2.1, 3.0, 4.1, 19.0, 6.0, 12.2, 14.9, 15.0, 16.1, 17.2]], 5.0, 'Composite histogram', ['a', 'b', 'c', 'd', 'e'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2245a600", + "metadata": {}, + "outputs": [], + "source": [ + "# weave-test-skip: in-notebook ops don't yet work with server\n", + "# super simple example for now to illustrate fetching W&B data & plotting histograms\n", + "from wandb.apis import public as wandb_api\n", + "from PIL import Image\n", + "\n", + "@weave.op(render_info={\"type\": \"function\"})\n", + "def run_accuracy_barchart(runs: list[wandb_api.Run]) -> Image.Image:\n", + " runs = list(runs) #[:100] # could shorten this list\n", + " acc = [r.summary_metrics.get(\"acc\") or 0.0 for r in runs]\n", + " car_acc = [r.summary_metrics.get(\"car_acc\") or 0.0 for r in runs]\n", + " traffic_acc = [r.summary_metrics.get(\"traffic_acc\") or 0.0 for r in runs]\n", + " road_acc = [r.summary_metrics.get(\"road_acc\") or 0.0 for r in runs]\n", + " \n", + " # filter out NaNs\n", + " for l in [acc, car_acc, traffic_acc, road_acc]:\n", + " if \"NaN\" in l:\n", + " l = l.remove(\"NaN\")\n", + "\n", + " return weave.use(lens.histogram([acc, car_acc, traffic_acc, road_acc],\n", + " bin_size=0.05,\n", + " chart_title=\"Semantic Segmentation Accuracy Across Experiments by Label Class\", \n", + " series_names=[\"Overall acc\", \"Car acc\", \"Traffic acc\", \"Road acc\"]\n", + " )\n", + " )\n", + "\n", + "runs = weave.legacy.weave.ops.project('stacey', 'deep-drive').runs()\n", + "run_accuracy_barchart(runs)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "markdown", - "id": "c3d89416", - "metadata": {}, - "source": [ - "# Composite Histograms\n", - "\n", - "Let's plot some histograms.\n", - "\n", - "## Base case: One series, no customization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e739d513", - "metadata": {}, - "outputs": [], - "source": [ - "lens.histogram([[1.0, 2, 3, -1.4]], 1.0, \"Composite Histogram\", ['series'])" - ] - }, - { - "cell_type": "markdown", - "id": "81fd5769", - "metadata": {}, - "source": [ - "## Two series with customization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "301be405", - "metadata": {}, - "outputs": [], - "source": [ - "lens.histogram([[-1.8, -0.4, 0.1, -0.9, -0.77, 0.5, 0.7, 0.2, 0.1, 0.8],[0.9, 0.4, 0.5, 0.6, 1.2, 1.5, 1.6, 1.7, 2, 2.4, 2.3, 2.1]], 0.5, \"My Histogram\", [\"series A\", \"series B\"])" - ] - }, - { - "cell_type": "markdown", - "id": "ee794dd4", - "metadata": {}, - "source": [ - "## Three series" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fba3acec", - "metadata": {}, - "outputs": [], - "source": [ - "lens.histogram([[1.0, 2, 3, -1.4],[8.1, 2.2, 3.3, 5.6, 1.1], [1.6, 1.7, 1.8, -2.0, 5]], 0.5, 'Composite histogram', ['a', 'b', 'c'])" - ] - }, - { - "cell_type": "markdown", - "id": "ac8ee3eb", - "metadata": {}, - "source": [ - "## More than three series :)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "070314da", - "metadata": {}, - "outputs": [], - "source": [ - "lens.histogram([[1.0, -20.0, -25.0, -15.0, 2.0, 3.0, 2.0, 2.0, 2.0, 2.0, 4.0, 5.0, 6.0], [1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 3.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0],[16.2, 17.9, 18.2, 2.3, -5.2, 6.0, 7.2, -10.9, 8.2,41.0], [1.3, 2.2, 5.3, 7.7, 8.8, 9.0, 2.0, 3.2, 43.7, 4.2, 5.1, 4.5, 6.9], [1.0, 2.1, 3.0, 4.1, 19.0, 6.0, 12.2, 14.9, 15.0, 16.1, 17.2]], 5.0, 'Composite histogram', ['a', 'b', 'c', 'd', 'e'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2245a600", - "metadata": {}, - "outputs": [], - "source": [ - "# weave-test-skip: in-notebook ops don't yet work with server\n", - "# super simple example for now to illustrate fetching W&B data & plotting histograms\n", - "from wandb.apis import public as wandb_api\n", - "from PIL import Image\n", - "\n", - "@weave.op(render_info={\"type\": \"function\"})\n", - "def run_accuracy_barchart(runs: list[wandb_api.Run]) -> Image.Image:\n", - " runs = list(runs) #[:100] # could shorten this list\n", - " acc = [r.summary_metrics.get(\"acc\") or 0.0 for r in runs]\n", - " car_acc = [r.summary_metrics.get(\"car_acc\") or 0.0 for r in runs]\n", - " traffic_acc = [r.summary_metrics.get(\"traffic_acc\") or 0.0 for r in runs]\n", - " road_acc = [r.summary_metrics.get(\"road_acc\") or 0.0 for r in runs]\n", - " \n", - " # filter out NaNs\n", - " for l in [acc, car_acc, traffic_acc, road_acc]:\n", - " if \"NaN\" in l:\n", - " l = l.remove(\"NaN\")\n", - "\n", - " return weave.use(lens.histogram([acc, car_acc, traffic_acc, road_acc],\n", - " bin_size=0.05,\n", - " chart_title=\"Semantic Segmentation Accuracy Across Experiments by Label Class\", \n", - " series_names=[\"Overall acc\", \"Car acc\", \"Traffic acc\", \"Road acc\"]\n", - " )\n", - " )\n", - "\n", - "runs = weave.legacy.ops.project('stacey', 'deep-drive').runs()\n", - "run_accuracy_barchart(runs)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/dir_browsing.ipynb b/weave/legacy/examples/experimental/dir_browsing.ipynb index eaa36563837..2e5d0e3d569 100644 --- a/weave/legacy/examples/experimental/dir_browsing.ipynb +++ b/weave/legacy/examples/experimental/dir_browsing.ipynb @@ -1,26 +1,26 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "79d64dea", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import weave\n", - "\n", - "#weave.legacy.ops.local_path(os.path.abspath('../weave/testdata'))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "79d64dea", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import weave\n", + "\n", + "#weave.legacy.weave.ops.local_path(os.path.abspath('../weave/testdata'))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/ecosystem.ipynb b/weave/legacy/examples/experimental/ecosystem.ipynb index b8f6c86266d..2adc1728b23 100644 --- a/weave/legacy/examples/experimental/ecosystem.ipynb +++ b/weave/legacy/examples/experimental/ecosystem.ipynb @@ -1,25 +1,25 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "3d5d0d34", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import ecosystem\n", - "ecosystem()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "3d5d0d34", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import ecosystem\n", + "ecosystem()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/huggingface_datasets.ipynb b/weave/legacy/examples/experimental/huggingface_datasets.ipynb index 76e7365cc2d..8f8074494b8 100644 --- a/weave/legacy/examples/experimental/huggingface_datasets.ipynb +++ b/weave/legacy/examples/experimental/huggingface_datasets.ipynb @@ -1,89 +1,89 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "897e0314", - "metadata": {}, - "source": [ - "# HuggingFace Datasets\n", - "\n", - "A quick overview of exploring [Datasets from HuggingFace](https://huggingface.co/docs/datasets/index).\n", - "\n", - "### Known issues\n", - "\n", - "- Using a dataset often reboots the weave server the first time, causing the first request to fail. Run again and it'll work\n", - "- We only load 100 items" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "897e0314", + "metadata": {}, + "source": [ + "# HuggingFace Datasets\n", + "\n", + "A quick overview of exploring [Datasets from HuggingFace](https://huggingface.co/docs/datasets/index).\n", + "\n", + "### Known issues\n", + "\n", + "- Using a dataset often reboots the weave server the first time, causing the first request to fail. Run again and it'll work\n", + "- We only load 100 items" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba78a68c", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem import huggingface as hf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e3b90c5", + "metadata": {}, + "outputs": [], + "source": [ + "hf.hf_datasets()" + ] + }, + { + "cell_type": "markdown", + "id": "d82a6ac3", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b861a911", + "metadata": {}, + "outputs": [], + "source": [ + "hf.dataset('app_reviews')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9935abb0", + "metadata": {}, + "outputs": [], + "source": [ + "hf.dataset('fashion_mnist')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba78a68c", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem import huggingface as hf" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e3b90c5", - "metadata": {}, - "outputs": [], - "source": [ - "hf.hf_datasets()" - ] - }, - { - "cell_type": "markdown", - "id": "d82a6ac3", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b861a911", - "metadata": {}, - "outputs": [], - "source": [ - "hf.dataset('app_reviews')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9935abb0", - "metadata": {}, - "outputs": [], - "source": [ - "hf.dataset('fashion_mnist')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/huggingface_models.ipynb b/weave/legacy/examples/experimental/huggingface_models.ipynb index 8cc3c98028a..c28d270f5dd 100644 --- a/weave/legacy/examples/experimental/huggingface_models.ipynb +++ b/weave/legacy/examples/experimental/huggingface_models.ipynb @@ -1,131 +1,131 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "d46881ea", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem.huggingface import huggingface as hf" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "d46881ea", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem.huggingface import huggingface as hf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d181896", + "metadata": {}, + "outputs": [], + "source": [ + "# Models\n", + "#hf().models()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb3fb536", + "metadata": {}, + "outputs": [], + "source": [ + "# Model card\n", + "text_gen_model = hf().model('gpt2')\n", + "text_gen_model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5961a5c5", + "metadata": {}, + "outputs": [], + "source": [ + "# Calling a text generation model\n", + "text_gen_output = text_gen_model.call('This is the start')\n", + "text_gen_output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "974e1a18", + "metadata": {}, + "outputs": [], + "source": [ + "# Bertviz for attention\n", + "text_gen_output.attention()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9ab742c", + "metadata": {}, + "outputs": [], + "source": [ + "# Model card\n", + "classification_model = hf().model('bhadresh-savani/distilbert-base-uncased-emotion')\n", + "classification_model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "493448cc", + "metadata": {}, + "outputs": [], + "source": [ + "classification_output = classification_model.call(\"I'm feeling just swell\")\n", + "classification_output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36305bb0", + "metadata": {}, + "outputs": [], + "source": [ + "classification_output.attention()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1aa95d3", + "metadata": {}, + "outputs": [], + "source": [ + "# Text classification models can be explained with shap\n", + "\n", + "# TODO: we wan't chain shap_explain in Python right now. But we can in JS. Fix!\n", + "from weave.legacy.weave.ecosystem import shap\n", + "shap.shap_explain(classification_output)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d181896", - "metadata": {}, - "outputs": [], - "source": [ - "# Models\n", - "#hf().models()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb3fb536", - "metadata": {}, - "outputs": [], - "source": [ - "# Model card\n", - "text_gen_model = hf().model('gpt2')\n", - "text_gen_model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5961a5c5", - "metadata": {}, - "outputs": [], - "source": [ - "# Calling a text generation model\n", - "text_gen_output = text_gen_model.call('This is the start')\n", - "text_gen_output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "974e1a18", - "metadata": {}, - "outputs": [], - "source": [ - "# Bertviz for attention\n", - "text_gen_output.attention()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b9ab742c", - "metadata": {}, - "outputs": [], - "source": [ - "# Model card\n", - "classification_model = hf().model('bhadresh-savani/distilbert-base-uncased-emotion')\n", - "classification_model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "493448cc", - "metadata": {}, - "outputs": [], - "source": [ - "classification_output = classification_model.call(\"I'm feeling just swell\")\n", - "classification_output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36305bb0", - "metadata": {}, - "outputs": [], - "source": [ - "classification_output.attention()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1aa95d3", - "metadata": {}, - "outputs": [], - "source": [ - "# Text classification models can be explained with shap\n", - "\n", - "# TODO: we wan't chain shap_explain in Python right now. But we can in JS. Fix!\n", - "from weave.legacy.ecosystem import shap\n", - "shap.shap_explain(classification_output)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/image_gen_craiyon.ipynb b/weave/legacy/examples/experimental/image_gen_craiyon.ipynb index a2535d4f58b..36eab9de975 100644 --- a/weave/legacy/examples/experimental/image_gen_craiyon.ipynb +++ b/weave/legacy/examples/experimental/image_gen_craiyon.ipynb @@ -1,52 +1,52 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2088ebdf", - "metadata": {}, - "outputs": [], - "source": [ - "# weave-test-skip-all: The craiyon API takes at least 60s per prediction!\n", - "import weave" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2088ebdf", + "metadata": {}, + "outputs": [], + "source": [ + "# weave-test-skip-all: The craiyon API takes at least 60s per prediction!\n", + "import weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6e6882b", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.ecosystem import craiyon\n", + "craiyon.generate('hello')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3322e1c5", + "metadata": {}, + "outputs": [], + "source": [ + "prompts = ['cats', 'dogs', 'catdogs']\n", + "panel = weave.legacy.weave.panels.Table(prompts,\n", + " columns=[\n", + " lambda prompt: prompt,\n", + " lambda prompt: craiyon.generate(prompt)\n", + " ]\n", + " )\n", + "weave.show(panel)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "b6e6882b", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.ecosystem import craiyon\n", - "craiyon.generate('hello')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3322e1c5", - "metadata": {}, - "outputs": [], - "source": [ - "prompts = ['cats', 'dogs', 'catdogs']\n", - "panel = weave.legacy.panels.Table(prompts,\n", - " columns=[\n", - " lambda prompt: prompt,\n", - " lambda prompt: craiyon.generate(prompt)\n", - " ]\n", - " )\n", - "weave.show(panel)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/image_gen_ops.ipynb b/weave/legacy/examples/experimental/image_gen_ops.ipynb index 673994b3ae9..5f3078c5fd2 100644 --- a/weave/legacy/examples/experimental/image_gen_ops.ipynb +++ b/weave/legacy/examples/experimental/image_gen_ops.ipynb @@ -1,126 +1,126 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "22034460", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import io\n", - "from PIL import Image\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import random\n", - "from weave.legacy import ops\n", - "\n", - "def plt_image():\n", - " \"\"\"Return current matplotlib figure as PIL Image\"\"\"\n", - " img_buf = io.BytesIO()\n", - " plt.savefig(img_buf, format='png')\n", - " im = Image.open(img_buf)\n", - " return im" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "22034460", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import io\n", + "from PIL import Image\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import random\n", + "from weave.legacy.weave import ops\n", + "\n", + "def plt_image():\n", + " \"\"\"Return current matplotlib figure as PIL Image\"\"\"\n", + " img_buf = io.BytesIO()\n", + " plt.savefig(img_buf, format='png')\n", + " im = Image.open(img_buf)\n", + " return im" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d03dc328", + "metadata": {}, + "outputs": [], + "source": [ + "# Declare an op that plots a sin weave using matplotlib, and returns the plot as an Image\n", + "\n", + "@weave.op(render_info={\"type\": \"function\"})\n", + "def sin_image(f: int) -> Image.Image: \n", + " x = np.arange(0, f * np.pi, 0.1)\n", + " y = np.sin(x)\n", + " plt.rcParams[\"figure.figsize\"] = (22, 7)\n", + " plt.plot(x, y)\n", + " \n", + " return plt_image()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "217d0cd2", + "metadata": {}, + "outputs": [], + "source": [ + "x = weave.save(7, name='my-number')\n", + "\n", + "im = sin_image(x + 2)\n", + "im" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8d06148", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from weave.legacy.weave.ops_domain import wb_domain_types\n", + "from weave import gql_op_plugin\n", + "import wandb\n", + "\n", + "\n", + "@weave.op(\n", + " render_info={\"type\": \"function\"},\n", + " plugins=gql_op_plugin.wb_gql_op_plugin(\n", + " lambda inputs, inner: \"project {id name entity {id name}}\"\n", + " )\n", + ")\n", + "def run_accuracy_barchart(runs: list[wb_domain_types.Run]) -> Image.Image:\n", + " runs = list(runs)[:10]\n", + " runs = [wandb.Api().run(f'{r[\"project\"][\"entity\"][\"name\"]}/{r[\"project\"][\"name\"]}/{r[\"name\"]}') for r in runs]\n", + " names = [r.name for r in runs]\n", + " xs = range(len(runs))\n", + " ys = [r.summary_metrics.get(\"acc\") or 0 for r in runs]\n", + "\n", + " plt.rcParams[\"figure.figsize\"] = (15, 5)\n", + " plt.bar(xs, ys, align=\"center\", alpha=0.5)\n", + " plt.xticks(xs, names)\n", + " plt.ylabel(\"acc\")\n", + " plt.title(\"Run accuracies\")\n", + "\n", + " return plt_image()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4dc62a2", + "metadata": {}, + "outputs": [], + "source": [ + "runs = weave.legacy.weave.ops.project('shawn', 'fasion-sweep').runs()\n", + "run_accuracy_barchart(runs)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "d03dc328", - "metadata": {}, - "outputs": [], - "source": [ - "# Declare an op that plots a sin weave using matplotlib, and returns the plot as an Image\n", - "\n", - "@weave.op(render_info={\"type\": \"function\"})\n", - "def sin_image(f: int) -> Image.Image: \n", - " x = np.arange(0, f * np.pi, 0.1)\n", - " y = np.sin(x)\n", - " plt.rcParams[\"figure.figsize\"] = (22, 7)\n", - " plt.plot(x, y)\n", - " \n", - " return plt_image()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "217d0cd2", - "metadata": {}, - "outputs": [], - "source": [ - "x = weave.save(7, name='my-number')\n", - "\n", - "im = sin_image(x + 2)\n", - "im" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8d06148", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "from weave.legacy.ops_domain import wb_domain_types\n", - "from weave import gql_op_plugin\n", - "import wandb\n", - "\n", - "\n", - "@weave.op(\n", - " render_info={\"type\": \"function\"},\n", - " plugins=gql_op_plugin.wb_gql_op_plugin(\n", - " lambda inputs, inner: \"project {id name entity {id name}}\"\n", - " )\n", - ")\n", - "def run_accuracy_barchart(runs: list[wb_domain_types.Run]) -> Image.Image:\n", - " runs = list(runs)[:10]\n", - " runs = [wandb.Api().run(f'{r[\"project\"][\"entity\"][\"name\"]}/{r[\"project\"][\"name\"]}/{r[\"name\"]}') for r in runs]\n", - " names = [r.name for r in runs]\n", - " xs = range(len(runs))\n", - " ys = [r.summary_metrics.get(\"acc\") or 0 for r in runs]\n", - "\n", - " plt.rcParams[\"figure.figsize\"] = (15, 5)\n", - " plt.bar(xs, ys, align=\"center\", alpha=0.5)\n", - " plt.xticks(xs, names)\n", - " plt.ylabel(\"acc\")\n", - " plt.title(\"Run accuracies\")\n", - "\n", - " return plt_image()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a4dc62a2", - "metadata": {}, - "outputs": [], - "source": [ - "runs = weave.legacy.ops.project('shawn', 'fasion-sweep').runs()\n", - "run_accuracy_barchart(runs)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/layout_panels.ipynb b/weave/legacy/examples/experimental/layout_panels.ipynb index a7536f0191f..c7bd996af26 100644 --- a/weave/legacy/examples/experimental/layout_panels.ipynb +++ b/weave/legacy/examples/experimental/layout_panels.ipynb @@ -1,206 +1,206 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "cfebbf95", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import panels" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "cfebbf95", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e427d65a", + "metadata": {}, + "outputs": [], + "source": [ + "# LabeledItem example\n", + "# panels.LabeledItem(item='item', label='label')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9852987", + "metadata": {}, + "outputs": [], + "source": [ + "# Group example\n", + "# panels.Group(\n", + "# items=[\n", + "# panels.Group(\n", + "# prefer_horizontal=True,\n", + "# items=[\n", + "# panels.LabeledItem(item='item1', label='label1'),\n", + "# panels.LabeledItem(item='item2', label='label2'),\n", + "# panels.LabeledItem(item='item3', label='label3'),\n", + "# ]\n", + "# ),\n", + "# panels.LabeledItem(item='item4', label='label4'),\n", + "# panels.LabeledItem(item='item5', label='label5')\n", + "# ]\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ee2951b", + "metadata": {}, + "outputs": [], + "source": [ + "# Card example\n", + "\n", + "panel = panels.Card(\n", + " title='GPT-3',\n", + " subtitle='OpenAI',\n", + " content=[\n", + " panels.CardTab(\n", + " name='Overview',\n", + " content=panels.Group(\n", + " items={\n", + " '0': panels.Group(\n", + " preferHorizontal=True,\n", + " items={\n", + " 'a': panels.LabeledItem(item='item1', label='label1'),\n", + " 'b': panels.LabeledItem(item='item2', label='label2'),\n", + " 'c': panels.LabeledItem(item='item3', label='label3'),\n", + " }\n", + " ),\n", + " '1': panels.LabeledItem(item='item4', label='label4'),\n", + " '2': panels.LabeledItem(item='item5', label='label5')\n", + " }\n", + " )\n", + " ),\n", + " panels.CardTab(\n", + " name='Limitations & Use',\n", + " content=panels.LabeledItem(item='tab2', label='tab2-label')\n", + " ),\n", + " \n", + " ]\n", + ")\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "762467e8", + "metadata": {}, + "outputs": [], + "source": [ + "# Table in Card example\n", + "\n", + "# panels.Card(\n", + "# title='GPT-3',\n", + "# subtitle='OpenAI',\n", + "# content=[\n", + "# panels.CardTab(\n", + "# name='Overview',\n", + "# content=panels.Group(\n", + "# items=[\n", + "# panels.Group(\n", + "# prefer_horizontal=True,\n", + "# items=[\n", + "# panels.LabeledItem(item='item1', label='label1'),\n", + "# panels.LabeledItem(item='item2', label='label2'),\n", + "# panels.LabeledItem(item='item3', label='label3'),\n", + "# ]\n", + "# ),\n", + "# panels.LabeledItem(item='item4', label='label4'),\n", + "# panels.LabeledItem(\n", + "# item=[\n", + "# {\"a\": 5, \"b\": 6},\n", + "# {\"a\": 14, \"b\": 9},\n", + "# {\"a\": 4, \"b\": 22},\n", + "# ],\n", + "# height=400,\n", + "# label=\"Example\",\n", + "# ),\n", + "# ]\n", + "# )\n", + "# ),\n", + "# panels.CardTab(\n", + "# name='Limitations & Use',\n", + "# content=panels.LabeledItem(item='tab2', label='tab2-label')\n", + "# ),\n", + " \n", + "# ]\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81c83f19", + "metadata": {}, + "outputs": [], + "source": [ + "# If you want full control over table columns\n", + "\n", + "# table = panels.Table([\n", + "# {\"a\": 5, \"b\": 6},\n", + "# {\"a\": 14, \"b\": 9},\n", + "# {\"a\": 4, \"b\": 22},\n", + "# ])\n", + "# table.append_column(lambda row: row['a'] + 3, name=\"a name\")\n", + "# table.append_column(lambda row: row['a'] + row['b']) # Will have an automatic name\n", + "\n", + "# panels.LabeledItem(\n", + "# item=table,\n", + "# height=400,\n", + "# label=\"Example\",\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eaea1261", + "metadata": {}, + "outputs": [], + "source": [ + "# Plot in Labeled item (you can do the same in a Card)\n", + "\n", + "# plot = panels.Plot(\n", + "# input_node=[\n", + "# {\"a\": 5, \"b\": 6},\n", + "# {\"a\": 14, \"b\": 9},\n", + "# {\"a\": 4, \"b\": 22}],\n", + "# x=lambda row: row['a'],\n", + "# y=lambda row: row['b']\n", + "# )\n", + "\n", + "# panels.LabeledItem(\n", + "# item=plot,\n", + "# label=\"Example\",\n", + "# )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "e427d65a", - "metadata": {}, - "outputs": [], - "source": [ - "# LabeledItem example\n", - "# panels.LabeledItem(item='item', label='label')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9852987", - "metadata": {}, - "outputs": [], - "source": [ - "# Group example\n", - "# panels.Group(\n", - "# items=[\n", - "# panels.Group(\n", - "# prefer_horizontal=True,\n", - "# items=[\n", - "# panels.LabeledItem(item='item1', label='label1'),\n", - "# panels.LabeledItem(item='item2', label='label2'),\n", - "# panels.LabeledItem(item='item3', label='label3'),\n", - "# ]\n", - "# ),\n", - "# panels.LabeledItem(item='item4', label='label4'),\n", - "# panels.LabeledItem(item='item5', label='label5')\n", - "# ]\n", - "# )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6ee2951b", - "metadata": {}, - "outputs": [], - "source": [ - "# Card example\n", - "\n", - "panel = panels.Card(\n", - " title='GPT-3',\n", - " subtitle='OpenAI',\n", - " content=[\n", - " panels.CardTab(\n", - " name='Overview',\n", - " content=panels.Group(\n", - " items={\n", - " '0': panels.Group(\n", - " preferHorizontal=True,\n", - " items={\n", - " 'a': panels.LabeledItem(item='item1', label='label1'),\n", - " 'b': panels.LabeledItem(item='item2', label='label2'),\n", - " 'c': panels.LabeledItem(item='item3', label='label3'),\n", - " }\n", - " ),\n", - " '1': panels.LabeledItem(item='item4', label='label4'),\n", - " '2': panels.LabeledItem(item='item5', label='label5')\n", - " }\n", - " )\n", - " ),\n", - " panels.CardTab(\n", - " name='Limitations & Use',\n", - " content=panels.LabeledItem(item='tab2', label='tab2-label')\n", - " ),\n", - " \n", - " ]\n", - ")\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "762467e8", - "metadata": {}, - "outputs": [], - "source": [ - "# Table in Card example\n", - "\n", - "# panels.Card(\n", - "# title='GPT-3',\n", - "# subtitle='OpenAI',\n", - "# content=[\n", - "# panels.CardTab(\n", - "# name='Overview',\n", - "# content=panels.Group(\n", - "# items=[\n", - "# panels.Group(\n", - "# prefer_horizontal=True,\n", - "# items=[\n", - "# panels.LabeledItem(item='item1', label='label1'),\n", - "# panels.LabeledItem(item='item2', label='label2'),\n", - "# panels.LabeledItem(item='item3', label='label3'),\n", - "# ]\n", - "# ),\n", - "# panels.LabeledItem(item='item4', label='label4'),\n", - "# panels.LabeledItem(\n", - "# item=[\n", - "# {\"a\": 5, \"b\": 6},\n", - "# {\"a\": 14, \"b\": 9},\n", - "# {\"a\": 4, \"b\": 22},\n", - "# ],\n", - "# height=400,\n", - "# label=\"Example\",\n", - "# ),\n", - "# ]\n", - "# )\n", - "# ),\n", - "# panels.CardTab(\n", - "# name='Limitations & Use',\n", - "# content=panels.LabeledItem(item='tab2', label='tab2-label')\n", - "# ),\n", - " \n", - "# ]\n", - "# )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81c83f19", - "metadata": {}, - "outputs": [], - "source": [ - "# If you want full control over table columns\n", - "\n", - "# table = panels.Table([\n", - "# {\"a\": 5, \"b\": 6},\n", - "# {\"a\": 14, \"b\": 9},\n", - "# {\"a\": 4, \"b\": 22},\n", - "# ])\n", - "# table.append_column(lambda row: row['a'] + 3, name=\"a name\")\n", - "# table.append_column(lambda row: row['a'] + row['b']) # Will have an automatic name\n", - "\n", - "# panels.LabeledItem(\n", - "# item=table,\n", - "# height=400,\n", - "# label=\"Example\",\n", - "# )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eaea1261", - "metadata": {}, - "outputs": [], - "source": [ - "# Plot in Labeled item (you can do the same in a Card)\n", - "\n", - "# plot = panels.Plot(\n", - "# input_node=[\n", - "# {\"a\": 5, \"b\": 6},\n", - "# {\"a\": 14, \"b\": 9},\n", - "# {\"a\": 4, \"b\": 22}],\n", - "# x=lambda row: row['a'],\n", - "# y=lambda row: row['b']\n", - "# )\n", - "\n", - "# panels.LabeledItem(\n", - "# item=plot,\n", - "# label=\"Example\",\n", - "# )" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/llm_monitor_helper.ipynb b/weave/legacy/examples/experimental/llm_monitor_helper.ipynb index d2cfe85b752..48e92a18e90 100644 --- a/weave/legacy/examples/experimental/llm_monitor_helper.ipynb +++ b/weave/legacy/examples/experimental/llm_monitor_helper.ipynb @@ -1,198 +1,198 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ENTITY = 'timssweeney'\n", - "PROJECT = 'weave'\n", - "STREAM = 'custom_llm_monitoring_example'" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ENTITY = 'timssweeney'\n", + "PROJECT = 'weave'\n", + "STREAM = 'custom_llm_monitoring_example'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "import time\n", + "import typing\n", + "from datetime import datetime \n", + "\n", + "from weave.stream_data_interfaces import LLMCompletionDict, _LLMCompletionInputs, _LLMCompletionOutput, _LLMCompletionSummary, _LLMCompletionMessage, _LLMCompletionChoice\n", + "import tiktoken\n", + "\n", + "def count_chat_completion_tokens(\n", + " model_name: typing.Optional[str] = None,\n", + " prompt_input_messages: typing.List[_LLMCompletionMessage] = [],\n", + " completion_choices: typing.List[_LLMCompletionChoice] = []\n", + ") -> dict:\n", + " \n", + " summary = {}\n", + " if not model_name:\n", + " encoding = tiktoken.get_encoding(\"cl100k_base\")\n", + " else:\n", + " encoding = tiktoken.encoding_for_model(model_name)\n", + "\n", + " prompt_tokens = (encoding.encode(m[\"content\"]) for m in prompt_input_messages)\n", + " summary[\"prompt_tokens\"] = sum(len(c) for c in prompt_tokens)\n", + "\n", + " completion_tokens = (\n", + " encoding.encode(c[\"message\"][\"content\"]) for c in completion_choices\n", + " )\n", + " summary[\"completion_tokens\"] = sum(len(c) for c in completion_tokens)\n", + " summary[\"total_tokens\"] = summary[\"prompt_tokens\"] + summary[\"completion_tokens\"]\n", + " return summary\n", + "\n", + "def create_llm_record(\n", + "\n", + " # The end-format is that of `_LLMCompletionInputs`, but we can add helper processing to convert from other formats\n", + " inputs: typing.Union[_LLMCompletionInputs, typing.List[_LLMCompletionMessage], typing.List[str], str] = None,\n", + "\n", + " # the end-format is that of `_LLMCompletionOutput`, but we can add helper processing to convert from other formats\n", + " output: typing.Union[_LLMCompletionOutput, str] = None,\n", + "\n", + " # Required for cost analysis\n", + " model_name: str = \"\",\n", + "\n", + " # Optional fields\n", + " span_id: typing.Optional[str] = None,\n", + " name: typing.Optional[str] = None,\n", + " trace_id: typing.Optional[str] = None,\n", + " status_code: typing.Optional[str] = None,\n", + " start_time_s: typing.Optional[float] = None,\n", + " end_time_s: typing.Optional[float] = None,\n", + " parent_id: typing.Optional[str] = None,\n", + "\n", + " # Must contain dictionaries, lists, and primitives (ie json serializable)\n", + " attributes: typing.Optional[typing.Dict[str, typing.Any]] = None,\n", + "\n", + " summary: typing.Optional[_LLMCompletionSummary] = None,\n", + "\n", + " exception: typing.Optional[str] = None\n", + "):\n", + " span_id = span_id or str(uuid.uuid4())\n", + " name = name or \"llm_completion\"\n", + " trace_id = trace_id or span_id\n", + " status_code = status_code or \"UNSET\"\n", + " start_time_s = start_time_s or time.time()\n", + " end_time_s = end_time_s or (start_time_s + 1)\n", + " latency_s = end_time_s - start_time_s\n", + " # parent_id can be None\n", + " attributes = attributes or {}\n", + " \n", + "\n", + " # Input handling\n", + " if isinstance(inputs, dict):\n", + " # Assume correct format\n", + " inputs = inputs\n", + " elif isinstance(inputs, list):\n", + " messages = []\n", + " for item in inputs:\n", + " if isinstance(item, str):\n", + " messages.append(_LLMCompletionMessage(content=item))\n", + " elif isinstance(item, dict):\n", + " # Assume correct format\n", + " messages.append(item)\n", + " else:\n", + " raise ValueError(f\"Invalid type for item in inputs: {type(item)}\")\n", + " inputs = _LLMCompletionInputs(messages=messages)\n", + " elif isinstance(inputs, str):\n", + " inputs = _LLMCompletionInputs(messages=[_LLMCompletionMessage(content=inputs)])\n", + " else:\n", + " raise ValueError(f\"Invalid type for inputs: {type(inputs)}\")\n", + " \n", + "\n", + " # Output handling\n", + " if isinstance(output, dict):\n", + " # Assume correct format\n", + " output = output\n", + " elif isinstance(output, str):\n", + " output = _LLMCompletionOutput(model=model_name, choices=[_LLMCompletionChoice(message=_LLMCompletionMessage(content=output))])\n", + " else:\n", + " raise ValueError(f\"Invalid type for output: {type(output)}\")\n", + "\n", + " # Sort of odd, but we need at least one key for now\n", + " summary = summary or {}\n", + " summary = {\n", + " **(summary or {}),\n", + " \"latency_s\": latency_s,\n", + " **count_chat_completion_tokens(\n", + " model_name,\n", + " inputs['messages'],\n", + " output['choices'])\n", + " }\n", + " \n", + "\n", + " # exception can be None\n", + "\n", + " assert status_code in [\"SUCCESS\", \"ERROR\", \"UNSET\"]\n", + "\n", + " return LLMCompletionDict(\n", + " span_id = span_id,\n", + " name = name,\n", + " trace_id = trace_id,\n", + " status_code = status_code,\n", + " start_time_s = start_time_s,\n", + " end_time_s = end_time_s,\n", + " parent_id = parent_id,\n", + " attributes = attributes,\n", + " inputs = inputs,\n", + " output = output,\n", + " summary = summary,\n", + " exception = exception,\n", + " # Manually set timestamp - else it will be set to the time of the function call\n", + " timestamp = datetime.fromtimestamp(start_time_s)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.monitoring import StreamTable\n", + "st = StreamTable(f\"{ENTITY}/{PROJECT}/{STREAM}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Base Case\n", + "record = create_llm_record(\n", + " inputs=\"hello\",\n", + " output=\"world\"\n", + ")\n", + "st.log(record)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "wandb-weave", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import uuid\n", - "import time\n", - "import typing\n", - "from datetime import datetime \n", - "\n", - "from weave.stream_data_interfaces import LLMCompletionDict, _LLMCompletionInputs, _LLMCompletionOutput, _LLMCompletionSummary, _LLMCompletionMessage, _LLMCompletionChoice\n", - "import tiktoken\n", - "\n", - "def count_chat_completion_tokens(\n", - " model_name: typing.Optional[str] = None,\n", - " prompt_input_messages: typing.List[_LLMCompletionMessage] = [],\n", - " completion_choices: typing.List[_LLMCompletionChoice] = []\n", - ") -> dict:\n", - " \n", - " summary = {}\n", - " if not model_name:\n", - " encoding = tiktoken.get_encoding(\"cl100k_base\")\n", - " else:\n", - " encoding = tiktoken.encoding_for_model(model_name)\n", - "\n", - " prompt_tokens = (encoding.encode(m[\"content\"]) for m in prompt_input_messages)\n", - " summary[\"prompt_tokens\"] = sum(len(c) for c in prompt_tokens)\n", - "\n", - " completion_tokens = (\n", - " encoding.encode(c[\"message\"][\"content\"]) for c in completion_choices\n", - " )\n", - " summary[\"completion_tokens\"] = sum(len(c) for c in completion_tokens)\n", - " summary[\"total_tokens\"] = summary[\"prompt_tokens\"] + summary[\"completion_tokens\"]\n", - " return summary\n", - "\n", - "def create_llm_record(\n", - "\n", - " # The end-format is that of `_LLMCompletionInputs`, but we can add helper processing to convert from other formats\n", - " inputs: typing.Union[_LLMCompletionInputs, typing.List[_LLMCompletionMessage], typing.List[str], str] = None,\n", - "\n", - " # the end-format is that of `_LLMCompletionOutput`, but we can add helper processing to convert from other formats\n", - " output: typing.Union[_LLMCompletionOutput, str] = None,\n", - "\n", - " # Required for cost analysis\n", - " model_name: str = \"\",\n", - "\n", - " # Optional fields\n", - " span_id: typing.Optional[str] = None,\n", - " name: typing.Optional[str] = None,\n", - " trace_id: typing.Optional[str] = None,\n", - " status_code: typing.Optional[str] = None,\n", - " start_time_s: typing.Optional[float] = None,\n", - " end_time_s: typing.Optional[float] = None,\n", - " parent_id: typing.Optional[str] = None,\n", - "\n", - " # Must contain dictionaries, lists, and primitives (ie json serializable)\n", - " attributes: typing.Optional[typing.Dict[str, typing.Any]] = None,\n", - "\n", - " summary: typing.Optional[_LLMCompletionSummary] = None,\n", - "\n", - " exception: typing.Optional[str] = None\n", - "):\n", - " span_id = span_id or str(uuid.uuid4())\n", - " name = name or \"llm_completion\"\n", - " trace_id = trace_id or span_id\n", - " status_code = status_code or \"UNSET\"\n", - " start_time_s = start_time_s or time.time()\n", - " end_time_s = end_time_s or (start_time_s + 1)\n", - " latency_s = end_time_s - start_time_s\n", - " # parent_id can be None\n", - " attributes = attributes or {}\n", - " \n", - "\n", - " # Input handling\n", - " if isinstance(inputs, dict):\n", - " # Assume correct format\n", - " inputs = inputs\n", - " elif isinstance(inputs, list):\n", - " messages = []\n", - " for item in inputs:\n", - " if isinstance(item, str):\n", - " messages.append(_LLMCompletionMessage(content=item))\n", - " elif isinstance(item, dict):\n", - " # Assume correct format\n", - " messages.append(item)\n", - " else:\n", - " raise ValueError(f\"Invalid type for item in inputs: {type(item)}\")\n", - " inputs = _LLMCompletionInputs(messages=messages)\n", - " elif isinstance(inputs, str):\n", - " inputs = _LLMCompletionInputs(messages=[_LLMCompletionMessage(content=inputs)])\n", - " else:\n", - " raise ValueError(f\"Invalid type for inputs: {type(inputs)}\")\n", - " \n", - "\n", - " # Output handling\n", - " if isinstance(output, dict):\n", - " # Assume correct format\n", - " output = output\n", - " elif isinstance(output, str):\n", - " output = _LLMCompletionOutput(model=model_name, choices=[_LLMCompletionChoice(message=_LLMCompletionMessage(content=output))])\n", - " else:\n", - " raise ValueError(f\"Invalid type for output: {type(output)}\")\n", - "\n", - " # Sort of odd, but we need at least one key for now\n", - " summary = summary or {}\n", - " summary = {\n", - " **(summary or {}),\n", - " \"latency_s\": latency_s,\n", - " **count_chat_completion_tokens(\n", - " model_name,\n", - " inputs['messages'],\n", - " output['choices'])\n", - " }\n", - " \n", - "\n", - " # exception can be None\n", - "\n", - " assert status_code in [\"SUCCESS\", \"ERROR\", \"UNSET\"]\n", - "\n", - " return LLMCompletionDict(\n", - " span_id = span_id,\n", - " name = name,\n", - " trace_id = trace_id,\n", - " status_code = status_code,\n", - " start_time_s = start_time_s,\n", - " end_time_s = end_time_s,\n", - " parent_id = parent_id,\n", - " attributes = attributes,\n", - " inputs = inputs,\n", - " output = output,\n", - " summary = summary,\n", - " exception = exception,\n", - " # Manually set timestamp - else it will be set to the time of the function call\n", - " timestamp = datetime.fromtimestamp(start_time_s)\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.monitoring import StreamTable\n", - "st = StreamTable(f\"{ENTITY}/{PROJECT}/{STREAM}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Base Case\n", - "record = create_llm_record(\n", - " inputs=\"hello\",\n", - " output=\"world\"\n", - ")\n", - "st.log(record)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "wandb-weave", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/weave/legacy/examples/experimental/mnist_train.ipynb b/weave/legacy/examples/experimental/mnist_train.ipynb index f6cb2821cc0..664459dcae5 100644 --- a/weave/legacy/examples/experimental/mnist_train.ipynb +++ b/weave/legacy/examples/experimental/mnist_train.ipynb @@ -1,82 +1,82 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "37f6d676", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem import torchvision, torch_mnist_model_example" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "37f6d676", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem import torchvision, torch_mnist_model_example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b450f4df", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: Fix WeaveJS to automatically pick the more specific dataset_card Panel\n", + "mnist_dataset = torchvision.mnist(100) # Or try mnist.food101(100), but currently broken\n", + "mnist_dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "664f3447", + "metadata": {}, + "outputs": [], + "source": [ + "hyperparams = {\n", + " 'fc_layer_size': 256,\n", + " 'dropout': 0.5,\n", + " 'epochs': 5,\n", + " 'learning_rate': 0.005,\n", + " 'batch_size': 128,\n", + "}\n", + "train_split = mnist_dataset['data']['train']\n", + "#train_split.pick('image')\n", + "model = torch_mnist_model_example.train(\n", + " train_split.pick('image'), train_split.pick('label'), hyperparams)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d204664", + "metadata": {}, + "outputs": [], + "source": [ + "test_split_images = mnist_dataset['data']['test']['image']\n", + "preds = model.predict(test_split_images)\n", + "preds" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "b450f4df", - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: Fix WeaveJS to automatically pick the more specific dataset_card Panel\n", - "mnist_dataset = torchvision.mnist(100) # Or try mnist.food101(100), but currently broken\n", - "mnist_dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "664f3447", - "metadata": {}, - "outputs": [], - "source": [ - "hyperparams = {\n", - " 'fc_layer_size': 256,\n", - " 'dropout': 0.5,\n", - " 'epochs': 5,\n", - " 'learning_rate': 0.005,\n", - " 'batch_size': 128,\n", - "}\n", - "train_split = mnist_dataset['data']['train']\n", - "#train_split.pick('image')\n", - "model = torch_mnist_model_example.train(\n", - " train_split.pick('image'), train_split.pick('label'), hyperparams)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d204664", - "metadata": {}, - "outputs": [], - "source": [ - "test_split_images = mnist_dataset['data']['test']['image']\n", - "preds = model.predict(test_split_images)\n", - "preds" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/object_version_compare.ipynb b/weave/legacy/examples/experimental/object_version_compare.ipynb index c74bf916295..216aba6973b 100644 --- a/weave/legacy/examples/experimental/object_version_compare.ipynb +++ b/weave/legacy/examples/experimental/object_version_compare.ipynb @@ -1,116 +1,116 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e114a840", - "metadata": {}, - "outputs": [], - "source": [ - "import math\n", - "import weave\n", - "import typing" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e114a840", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import weave\n", + "import typing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47ae117d", + "metadata": {}, + "outputs": [], + "source": [ + "@weave.op(render_info={'type': 'function'})\n", + "def weave_range(n: int) -> list[int]:\n", + " return list(range(n))\n", + "\n", + "class Point(typing.TypedDict):\n", + " x: float\n", + " y: float\n", + "\n", + "@weave.op()\n", + "def compute_points(xs: list[int], freq: float) -> list[Point]:\n", + " res: list[Point] = []\n", + " for x in xs:\n", + " res.append({\"x\": x, \"y\": math.sin(freq * x)})\n", + " return res" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6db91c9", + "metadata": {}, + "outputs": [], + "source": [ + "xs = weave_range(1000)\n", + "points = compute_points(xs, 0.16)\n", + "points = weave.save(points, 'points')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c88c35e", + "metadata": {}, + "outputs": [], + "source": [ + "plot = weave.legacy.weave.panels.Plot(points,\n", + " x=lambda row: row['x'],\n", + " y=lambda row: row['y'])\n", + "plot\n", + "\n", + "# NOTE! There is a UI bug here. You need to click the gear icon and\n", + "# then \"Reset & Automate Plot\" -> \"OK\" to make it render correctly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db404b2e", + "metadata": {}, + "outputs": [], + "source": [ + "for version in weave.versions(points):\n", + " print(weave.expr(version))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caa2daef", + "metadata": {}, + "outputs": [], + "source": [ + "plot = weave.legacy.weave.panels.Plot(weave.legacy.weave.ops.compare_versions(points))\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['version'])\n", + "weave.show(plot)\n", + "\n", + "# NOTE! There is a UI bug here. You need to click the gear icon and\n", + "# then \"Reset & Automate Plot\" -> \"OK\" to make it render correctly" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "47ae117d", - "metadata": {}, - "outputs": [], - "source": [ - "@weave.op(render_info={'type': 'function'})\n", - "def weave_range(n: int) -> list[int]:\n", - " return list(range(n))\n", - "\n", - "class Point(typing.TypedDict):\n", - " x: float\n", - " y: float\n", - "\n", - "@weave.op()\n", - "def compute_points(xs: list[int], freq: float) -> list[Point]:\n", - " res: list[Point] = []\n", - " for x in xs:\n", - " res.append({\"x\": x, \"y\": math.sin(freq * x)})\n", - " return res" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e6db91c9", - "metadata": {}, - "outputs": [], - "source": [ - "xs = weave_range(1000)\n", - "points = compute_points(xs, 0.16)\n", - "points = weave.save(points, 'points')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5c88c35e", - "metadata": {}, - "outputs": [], - "source": [ - "plot = weave.legacy.panels.Plot(points,\n", - " x=lambda row: row['x'],\n", - " y=lambda row: row['y'])\n", - "plot\n", - "\n", - "# NOTE! There is a UI bug here. You need to click the gear icon and\n", - "# then \"Reset & Automate Plot\" -> \"OK\" to make it render correctly" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "db404b2e", - "metadata": {}, - "outputs": [], - "source": [ - "for version in weave.versions(points):\n", - " print(weave.expr(version))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "caa2daef", - "metadata": {}, - "outputs": [], - "source": [ - "plot = weave.legacy.panels.Plot(weave.legacy.ops.compare_versions(points))\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['version'])\n", - "weave.show(plot)\n", - "\n", - "# NOTE! There is a UI bug here. You need to click the gear icon and\n", - "# then \"Reset & Automate Plot\" -> \"OK\" to make it render correctly" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/old_openai_monitoring.ipynb b/weave/legacy/examples/experimental/old_openai_monitoring.ipynb index 5cc04cbf307..038a1932e79 100644 --- a/weave/legacy/examples/experimental/old_openai_monitoring.ipynb +++ b/weave/legacy/examples/experimental/old_openai_monitoring.ipynb @@ -1,226 +1,226 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "30ccfdbc", - "metadata": {}, - "source": [ - "This notebook shows how to use our openai logging integration to monitor api calls" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "30ccfdbc", + "metadata": {}, + "source": [ + "This notebook shows how to use our openai logging integration to monitor api calls" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8107732a-fb90-45f8-8377-6381bd28475d", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.monitoring import openai, init_monitor\n", + "\n", + "OPENAI_MODEL = 'gpt-3.5-turbo'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba2b2070", + "metadata": {}, + "outputs": [], + "source": [ + "# Pass //\n", + "m = init_monitor('shawn/oai-mon/test21')\n", + "\n", + "# Do an initial request, otherwise we don't have a type on which to recommend the OpenAI board!\n", + "# We need at least 2 requests for the Board to work, otherwise we get divide by zero errors.\n", + "# TODO: fix this onboarding issue\n", + "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{\"role\": \"user\", \"content\": f\"hello world!\"}])\n", + "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{\"role\": \"user\", \"content\": f\"what is 2+2?\"}])" + ] + }, + { + "cell_type": "markdown", + "id": "fb51abb4", + "metadata": {}, + "source": [ + "Click the link above to go to the Weave UI for the table we're logging to.\n", + "\n", + "From there you can click \"OpenAI Monitor Board\" to create a Weave Board for this data stream." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e711d521", + "metadata": {}, + "outputs": [], + "source": [ + "# Monitor ChatCompletion requests\n", + "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[\n", + " {\"role\": \"user\", \"content\": f\"who won the world series in 2006?\"},\n", + " ])\n", + "r['choices'][0]['message']['content']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7930e774", + "metadata": {}, + "outputs": [], + "source": [ + "# To keep track of prompts and parameters, add them to attributes on the logged\n", + "# record.\n", + "\n", + "system_prompt = \"you always write in bullet points\"\n", + "prompt = 'solve the following equation step by step: {equation}'\n", + "params = {'equation': '4 * (3 - 1)'}\n", + "openai.ChatCompletion.create(model=OPENAI_MODEL,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": prompt.format(**params)},\n", + " ],\n", + " # you can add additional attributes to the logged record\n", + " # see the monitor_api notebook for more examples\n", + " monitor_attributes={\n", + " 'system_prompt': system_prompt,\n", + " 'prompt': prompt,\n", + " 'params': params\n", + " })" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8b423df", + "metadata": {}, + "outputs": [], + "source": [ + "# Monitor streaming requests\n", + "# TODO: we don't get token counts here yet.\n", + "from weave.legacy.weave.monitoring.openai import message_from_stream\n", + "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[\n", + " {\"role\": \"system\", \"content\": \"Your are a robot and only speak in robot, like beep bloop bop.\"},\n", + " {\"role\": \"user\", \"content\": f\"Tell me a 50 word story.\"},\n", + " ], stream=True)\n", + "for s in message_from_stream(r):\n", + " print(s, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bca62fdb", + "metadata": {}, + "outputs": [], + "source": [ + "# Render table inline\n", + "#m.rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bdae9ba5-aa2b-4191-9a42-c1078ba0698a", + "metadata": {}, + "outputs": [], + "source": [ + "# Render board inline\n", + "# from weave.legacy.weave.panels_py import panel_llm_monitor\n", + "# board = panel_llm_monitor.board.raw_resolve_fn(m.rows())\n", + "# board" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d237a885", + "metadata": {}, + "outputs": [], + "source": [ + "# Other examples\n", + "\n", + "# TODO: restore async variant\n", + "\n", + "# result = await monitored_a_create(model=\"gpt-3.5-turbo\", messages=[\n", + "# {\"role\": \"system\", \"content\": \"You are a world-class machine learning researcher.\"},\n", + "# {\"role\": \"user\", \"content\": f\"Please provide a simple, fact-based question to send to an AI system. Do not say anything other than the question itself. Use this random number as inspiration: {random.random()}.\"},\n", + "# ])\n", + "# result\n", + "\n", + "# TODO: ensure works with function calls\n", + "# functions = [\n", + "# {\n", + "# \"name\": \"get_current_weather\",\n", + "# \"description\": \"Get the current weather\",\n", + "# \"parameters\": {\n", + "# \"type\": \"object\",\n", + "# \"properties\": {\n", + "# \"location\": {\n", + "# \"type\": \"string\",\n", + "# \"description\": \"The city and state, e.g. San Francisco, CA\",\n", + "# },\n", + "# \"format\": {\n", + "# \"type\": \"string\",\n", + "# \"enum\": [\"celsius\", \"fahrenheit\"],\n", + "# \"description\": \"The temperature unit to use. Infer this from the users location.\",\n", + "# },\n", + "# },\n", + "# \"required\": [\"location\", \"format\"],\n", + "# },\n", + "# },\n", + "# {\n", + "# \"name\": \"get_n_day_weather_forecast\",\n", + "# \"description\": \"Get an N-day weather forecast\",\n", + "# \"parameters\": {\n", + "# \"type\": \"object\",\n", + "# \"properties\": {\n", + "# \"location\": {\n", + "# \"type\": \"string\",\n", + "# \"description\": \"The city and state, e.g. San Francisco, CA\",\n", + "# },\n", + "# \"format\": {\n", + "# \"type\": \"string\",\n", + "# \"enum\": [\"celsius\", \"fahrenheit\"],\n", + "# \"description\": \"The temperature unit to use. Infer this from the users location.\",\n", + "# },\n", + "# \"num_days\": {\n", + "# \"type\": \"integer\",\n", + "# \"description\": \"The number of days to forecast\",\n", + "# }\n", + "# },\n", + "# \"required\": [\"location\", \"format\", \"num_days\"]\n", + "# },\n", + "# },\n", + "# ]\n", + "\n", + "# result = openai.ChatCompletion.create(model=\"gpt-3.5-turbo\", functions=functions, messages=[\n", + "# {\"role\": \"system\", \"content\": \"You love to call functions.\"},\n", + "# {\"role\": \"user\", \"content\": f\"what's the weather today\"},\n", + "# ])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "8107732a-fb90-45f8-8377-6381bd28475d", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.monitoring import openai, init_monitor\n", - "\n", - "OPENAI_MODEL = 'gpt-3.5-turbo'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba2b2070", - "metadata": {}, - "outputs": [], - "source": [ - "# Pass //\n", - "m = init_monitor('shawn/oai-mon/test21')\n", - "\n", - "# Do an initial request, otherwise we don't have a type on which to recommend the OpenAI board!\n", - "# We need at least 2 requests for the Board to work, otherwise we get divide by zero errors.\n", - "# TODO: fix this onboarding issue\n", - "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{\"role\": \"user\", \"content\": f\"hello world!\"}])\n", - "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{\"role\": \"user\", \"content\": f\"what is 2+2?\"}])" - ] - }, - { - "cell_type": "markdown", - "id": "fb51abb4", - "metadata": {}, - "source": [ - "Click the link above to go to the Weave UI for the table we're logging to.\n", - "\n", - "From there you can click \"OpenAI Monitor Board\" to create a Weave Board for this data stream." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e711d521", - "metadata": {}, - "outputs": [], - "source": [ - "# Monitor ChatCompletion requests\n", - "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[\n", - " {\"role\": \"user\", \"content\": f\"who won the world series in 2006?\"},\n", - " ])\n", - "r['choices'][0]['message']['content']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7930e774", - "metadata": {}, - "outputs": [], - "source": [ - "# To keep track of prompts and parameters, add them to attributes on the logged\n", - "# record.\n", - "\n", - "system_prompt = \"you always write in bullet points\"\n", - "prompt = 'solve the following equation step by step: {equation}'\n", - "params = {'equation': '4 * (3 - 1)'}\n", - "openai.ChatCompletion.create(model=OPENAI_MODEL,\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": prompt.format(**params)},\n", - " ],\n", - " # you can add additional attributes to the logged record\n", - " # see the monitor_api notebook for more examples\n", - " monitor_attributes={\n", - " 'system_prompt': system_prompt,\n", - " 'prompt': prompt,\n", - " 'params': params\n", - " })" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b8b423df", - "metadata": {}, - "outputs": [], - "source": [ - "# Monitor streaming requests\n", - "# TODO: we don't get token counts here yet.\n", - "from weave.legacy.monitoring.openai import message_from_stream\n", - "r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[\n", - " {\"role\": \"system\", \"content\": \"Your are a robot and only speak in robot, like beep bloop bop.\"},\n", - " {\"role\": \"user\", \"content\": f\"Tell me a 50 word story.\"},\n", - " ], stream=True)\n", - "for s in message_from_stream(r):\n", - " print(s, end='')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bca62fdb", - "metadata": {}, - "outputs": [], - "source": [ - "# Render table inline\n", - "#m.rows()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bdae9ba5-aa2b-4191-9a42-c1078ba0698a", - "metadata": {}, - "outputs": [], - "source": [ - "# Render board inline\n", - "# from weave.legacy.panels_py import panel_llm_monitor\n", - "# board = panel_llm_monitor.board.raw_resolve_fn(m.rows())\n", - "# board" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d237a885", - "metadata": {}, - "outputs": [], - "source": [ - "# Other examples\n", - "\n", - "# TODO: restore async variant\n", - "\n", - "# result = await monitored_a_create(model=\"gpt-3.5-turbo\", messages=[\n", - "# {\"role\": \"system\", \"content\": \"You are a world-class machine learning researcher.\"},\n", - "# {\"role\": \"user\", \"content\": f\"Please provide a simple, fact-based question to send to an AI system. Do not say anything other than the question itself. Use this random number as inspiration: {random.random()}.\"},\n", - "# ])\n", - "# result\n", - "\n", - "# TODO: ensure works with function calls\n", - "# functions = [\n", - "# {\n", - "# \"name\": \"get_current_weather\",\n", - "# \"description\": \"Get the current weather\",\n", - "# \"parameters\": {\n", - "# \"type\": \"object\",\n", - "# \"properties\": {\n", - "# \"location\": {\n", - "# \"type\": \"string\",\n", - "# \"description\": \"The city and state, e.g. San Francisco, CA\",\n", - "# },\n", - "# \"format\": {\n", - "# \"type\": \"string\",\n", - "# \"enum\": [\"celsius\", \"fahrenheit\"],\n", - "# \"description\": \"The temperature unit to use. Infer this from the users location.\",\n", - "# },\n", - "# },\n", - "# \"required\": [\"location\", \"format\"],\n", - "# },\n", - "# },\n", - "# {\n", - "# \"name\": \"get_n_day_weather_forecast\",\n", - "# \"description\": \"Get an N-day weather forecast\",\n", - "# \"parameters\": {\n", - "# \"type\": \"object\",\n", - "# \"properties\": {\n", - "# \"location\": {\n", - "# \"type\": \"string\",\n", - "# \"description\": \"The city and state, e.g. San Francisco, CA\",\n", - "# },\n", - "# \"format\": {\n", - "# \"type\": \"string\",\n", - "# \"enum\": [\"celsius\", \"fahrenheit\"],\n", - "# \"description\": \"The temperature unit to use. Infer this from the users location.\",\n", - "# },\n", - "# \"num_days\": {\n", - "# \"type\": \"integer\",\n", - "# \"description\": \"The number of days to forecast\",\n", - "# }\n", - "# },\n", - "# \"required\": [\"location\", \"format\", \"num_days\"]\n", - "# },\n", - "# },\n", - "# ]\n", - "\n", - "# result = openai.ChatCompletion.create(model=\"gpt-3.5-turbo\", functions=functions, messages=[\n", - "# {\"role\": \"system\", \"content\": \"You love to call functions.\"},\n", - "# {\"role\": \"user\", \"content\": f\"what's the weather today\"},\n", - "# ])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/openai_monitoring.ipynb b/weave/legacy/examples/experimental/openai_monitoring.ipynb index 0db4b93e1c3..686b881dc0d 100644 --- a/weave/legacy/examples/experimental/openai_monitoring.ipynb +++ b/weave/legacy/examples/experimental/openai_monitoring.ipynb @@ -1,239 +1,239 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## OpenAI Monitoring Examples\n", - "\n", - "This updates our monitoring code to:\n", - "1. Be compatible with `openai>=1.0.0`\n", - "2. Use patching (instead of forcing the user to import from our custom impl)\n", - "3. Use a callback system to add new functionality before/after certain stages" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "import weave\n", - "from weave.legacy.monitoring.openai import patch, unpatch\n", - "\n", - "weave.legacy.monitoring.openai.patch()\n", - "# weave.legacy.monitoring.openai.unpatch()\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advanced Setup\n", - "You can customize callbacks (e.g. which StreamTable to log to) by explicitly passing callbacks to `patch`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# from weave.legacy.monitoring.openai import ReassembleStream, LogToStreamTable\n", - "\n", - "# weave.legacy.monitoring.openai.patch(\n", - "# callbacks=[LogToStreamTable.from_stream_name(\"stream\", \"project\", \"entity\")]\n", - "# )\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Module-level Sync Completion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result = openai.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sync Completion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client = openai.OpenAI()\n", - "\n", - "result = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sync Completion (Streaming)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client = openai.OpenAI()\n", - "\n", - "stream = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", - " stream=True\n", - ")\n", - "\n", - "for x in stream:\n", - " print(x)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Async Completion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client = openai.AsyncOpenAI()\n", - "\n", - "result = await client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Async Completion (Streaming)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "client = openai.AsyncOpenAI()\n", - "\n", - "stream = await client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", - " stream=True\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "async for x in stream:\n", - " ...\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "weave310", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - } - }, - "nbformat": 4, - "nbformat_minor": 2 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## OpenAI Monitoring Examples\n", + "\n", + "This updates our monitoring code to:\n", + "1. Be compatible with `openai>=1.0.0`\n", + "2. Use patching (instead of forcing the user to import from our custom impl)\n", + "3. Use a callback system to add new functionality before/after certain stages" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "import weave\n", + "from weave.legacy.weave.monitoring.openai import patch, unpatch\n", + "\n", + "weave.legacy.weave.monitoring.openai.patch()\n", + "# weave.legacy.weave.monitoring.openai.unpatch()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advanced Setup\n", + "You can customize callbacks (e.g. which StreamTable to log to) by explicitly passing callbacks to `patch`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# from weave.legacy.weave.monitoring.openai import ReassembleStream, LogToStreamTable\n", + "\n", + "# weave.legacy.weave.monitoring.openai.patch(\n", + "# callbacks=[LogToStreamTable.from_stream_name(\"stream\", \"project\", \"entity\")]\n", + "# )\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Module-level Sync Completion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = openai.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sync Completion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client = openai.OpenAI()\n", + "\n", + "result = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sync Completion (Streaming)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client = openai.OpenAI()\n", + "\n", + "stream = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", + " stream=True\n", + ")\n", + "\n", + "for x in stream:\n", + " print(x)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Async Completion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client = openai.AsyncOpenAI()\n", + "\n", + "result = await client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Async Completion (Streaming)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client = openai.AsyncOpenAI()\n", + "\n", + "stream = await client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"system\", \"content\": \"Tell me a joke\"}],\n", + " stream=True\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "async for x in stream:\n", + " ...\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "weave310", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/weave/legacy/examples/experimental/prompts_dev/generate_synth_mon_board.ipynb b/weave/legacy/examples/experimental/prompts_dev/generate_synth_mon_board.ipynb index 96454814a2f..2ee6b8f6d03 100644 --- a/weave/legacy/examples/experimental/prompts_dev/generate_synth_mon_board.ipynb +++ b/weave/legacy/examples/experimental/prompts_dev/generate_synth_mon_board.ipynb @@ -1,192 +1,192 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "4549831c", - "metadata": {}, - "source": [ - "\"Weights\n", - "\n", - "\n", - "\n", - " \"Open\n", - "\n", - "\n", - "# Generate Synthetic Data for Large-Scale LLM Monitoring Demo\n", - "\n", - "This notebook generates synthetic data (Shakespearean dialogue) in the format used by the monitoring.openai integration.\n", - "See the openai_monitoring notebook for instructions for logging real OpenAI API calls.\n", - "\n", - "# Step 0: Setup\n", - "\n", - "Import dependencies" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "4549831c", + "metadata": {}, + "source": [ + "\"Weights\n", + "\n", + "\n", + "\n", + " \"Open\n", + "\n", + "\n", + "# Generate Synthetic Data for Large-Scale LLM Monitoring Demo\n", + "\n", + "This notebook generates synthetic data (Shakespearean dialogue) in the format used by the monitoring.openai integration.\n", + "See the openai_monitoring notebook for instructions for logging real OpenAI API calls.\n", + "\n", + "# Step 0: Setup\n", + "\n", + "Import dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00647082", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install -qqq weave\n", + "\n", + "import uuid\n", + "from datetime import timedelta\n", + "import weave\n", + "from weave.legacy.weave import ops_arrow\n", + "from weave.legacy.weave.monitoring import monitor\n", + "from weave.syndata_mon import random_predictions" + ] + }, + { + "cell_type": "markdown", + "id": "a8b49310-4d7a-4f3f-a901-e1f9e87baff8", + "metadata": {}, + "source": [ + "# Step 1: Generate random predictions and montior call spans" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93653c45", + "metadata": {}, + "outputs": [], + "source": [ + "preds = random_predictions(100)\n", + "\n", + "# Convert synthetic data into the format used by the weave.legacy.weave.monitoring.openai integration\n", + "\n", + "# convert model_version in the synthetic data to an openai model version\n", + "# this makes it so that there's a new API key that has appeared in our logs recently, and that key\n", + "# has started using gpt-4 which makes a cost spike\n", + "versions = sorted(preds.column('model_version').unique())\n", + "version_map = {}\n", + "for i, v in enumerate(reversed(versions)):\n", + " api_key = 'sk-U4...yK7z'\n", + " model = 'gpt-3.5-turbo-0613'\n", + " if i == 1 or i == 2:\n", + " # second and third most recent versions use a different api key\n", + " api_key = 'sk-U9...a22c'\n", + " if i == 1:\n", + " # second most recent version uses gpt-4\n", + " model = 'gpt-4-0613'\n", + " version_map[v] = (api_key, model)\n", + " \n", + "spans = [] \n", + "for i, pred in enumerate(preds):\n", + " api_key, model = version_map[pred['model_version']]\n", + " latency_mult = 1\n", + " if model == 'gpt-4-0613':\n", + " latency_mult = 3\n", + " span = monitor.Span('openai.api_resources.chat_completion.type.create',\n", + " inputs={\n", + " 'messages':[\n", + " {\"role\": \"user\", \"content\": pred['prompt']}\n", + " ]\n", + " },\n", + " output={\n", + " 'id': 'chatcmpl-%s' % uuid.uuid4(),\n", + " 'object': 'chat.completion',\n", + " 'created': pred['timestamp'].timestamp(),\n", + " 'model': model,\n", + " 'choices': [\n", + " {\n", + " 'index': 0,\n", + " 'message': {\n", + " 'role': 'assistant',\n", + " 'content': pred['completion']\n", + " },\n", + " 'finish_reason': 'stop'\n", + " }\n", + " ],\n", + "\n", + " },\n", + " attributes={\n", + " 'api_key': api_key,\n", + " 'username': pred['username']\n", + " },\n", + " summary={\n", + " 'prompt_tokens': pred['prompt_tokens'],\n", + " 'completion_tokens': pred['completion_tokens'],\n", + " 'total_tokens': (pred['prompt_tokens'] + pred['completion_tokens'])\n", + " })\n", + " span.start_time = pred['timestamp']\n", + " span.end_time = pred['timestamp'] + timedelta(seconds=pred['latency'] * latency_mult)\n", + " spans.append({'timestamp': pred['timestamp'], **span.asdict()})" + ] + }, + { + "cell_type": "markdown", + "id": "88479dbd-6fae-4353-974e-5e13704f720f", + "metadata": {}, + "source": [ + "# Step 2: Save and view synthetic data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9548c9aa", + "metadata": {}, + "outputs": [], + "source": [ + "# Save as a local table\n", + "oai_data = weave.save(ops_arrow.to_arrow(spans), 'oai_data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b59be0b5", + "metadata": {}, + "outputs": [], + "source": [ + "oai_data" + ] + }, + { + "cell_type": "markdown", + "id": "46669b4f-c569-40ee-a6fe-c5f2fdf64eb7", + "metadata": {}, + "source": [ + "# Step 3: View LLM Monitoring Board with synthetic data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7045f1eb", + "metadata": {}, + "outputs": [], + "source": [ + "# Use the llm monitoring template to visualize the data\n", + "from weave.legacy.weave.panels_py import panel_llm_monitor\n", + "board = panel_llm_monitor.board.raw_resolve_fn(oai_data)\n", + "board" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "00647082", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install -qqq weave\n", - "\n", - "import uuid\n", - "from datetime import timedelta\n", - "import weave\n", - "from weave.legacy import ops_arrow\n", - "from weave.legacy.monitoring import monitor\n", - "from weave.syndata_mon import random_predictions" - ] - }, - { - "cell_type": "markdown", - "id": "a8b49310-4d7a-4f3f-a901-e1f9e87baff8", - "metadata": {}, - "source": [ - "# Step 1: Generate random predictions and montior call spans" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93653c45", - "metadata": {}, - "outputs": [], - "source": [ - "preds = random_predictions(100)\n", - "\n", - "# Convert synthetic data into the format used by the weave.legacy.monitoring.openai integration\n", - "\n", - "# convert model_version in the synthetic data to an openai model version\n", - "# this makes it so that there's a new API key that has appeared in our logs recently, and that key\n", - "# has started using gpt-4 which makes a cost spike\n", - "versions = sorted(preds.column('model_version').unique())\n", - "version_map = {}\n", - "for i, v in enumerate(reversed(versions)):\n", - " api_key = 'sk-U4...yK7z'\n", - " model = 'gpt-3.5-turbo-0613'\n", - " if i == 1 or i == 2:\n", - " # second and third most recent versions use a different api key\n", - " api_key = 'sk-U9...a22c'\n", - " if i == 1:\n", - " # second most recent version uses gpt-4\n", - " model = 'gpt-4-0613'\n", - " version_map[v] = (api_key, model)\n", - " \n", - "spans = [] \n", - "for i, pred in enumerate(preds):\n", - " api_key, model = version_map[pred['model_version']]\n", - " latency_mult = 1\n", - " if model == 'gpt-4-0613':\n", - " latency_mult = 3\n", - " span = monitor.Span('openai.api_resources.chat_completion.type.create',\n", - " inputs={\n", - " 'messages':[\n", - " {\"role\": \"user\", \"content\": pred['prompt']}\n", - " ]\n", - " },\n", - " output={\n", - " 'id': 'chatcmpl-%s' % uuid.uuid4(),\n", - " 'object': 'chat.completion',\n", - " 'created': pred['timestamp'].timestamp(),\n", - " 'model': model,\n", - " 'choices': [\n", - " {\n", - " 'index': 0,\n", - " 'message': {\n", - " 'role': 'assistant',\n", - " 'content': pred['completion']\n", - " },\n", - " 'finish_reason': 'stop'\n", - " }\n", - " ],\n", - "\n", - " },\n", - " attributes={\n", - " 'api_key': api_key,\n", - " 'username': pred['username']\n", - " },\n", - " summary={\n", - " 'prompt_tokens': pred['prompt_tokens'],\n", - " 'completion_tokens': pred['completion_tokens'],\n", - " 'total_tokens': (pred['prompt_tokens'] + pred['completion_tokens'])\n", - " })\n", - " span.start_time = pred['timestamp']\n", - " span.end_time = pred['timestamp'] + timedelta(seconds=pred['latency'] * latency_mult)\n", - " spans.append({'timestamp': pred['timestamp'], **span.asdict()})" - ] - }, - { - "cell_type": "markdown", - "id": "88479dbd-6fae-4353-974e-5e13704f720f", - "metadata": {}, - "source": [ - "# Step 2: Save and view synthetic data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9548c9aa", - "metadata": {}, - "outputs": [], - "source": [ - "# Save as a local table\n", - "oai_data = weave.save(ops_arrow.to_arrow(spans), 'oai_data')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b59be0b5", - "metadata": {}, - "outputs": [], - "source": [ - "oai_data" - ] - }, - { - "cell_type": "markdown", - "id": "46669b4f-c569-40ee-a6fe-c5f2fdf64eb7", - "metadata": {}, - "source": [ - "# Step 3: View LLM Monitoring Board with synthetic data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7045f1eb", - "metadata": {}, - "outputs": [], - "source": [ - "# Use the llm monitoring template to visualize the data\n", - "from weave.legacy.panels_py import panel_llm_monitor\n", - "board = panel_llm_monitor.board.raw_resolve_fn(oai_data)\n", - "board" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/prompts_dev/synthetic_openai_data.ipynb b/weave/legacy/examples/experimental/prompts_dev/synthetic_openai_data.ipynb index c63c29d4311..6baaae82cfb 100644 --- a/weave/legacy/examples/experimental/prompts_dev/synthetic_openai_data.ipynb +++ b/weave/legacy/examples/experimental/prompts_dev/synthetic_openai_data.ipynb @@ -1,167 +1,167 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "4549831c", - "metadata": {}, - "source": [ - "\"Weights\n", - "\n", - "# Generate Synthetic Trace Data\n", - "\n", - "Generate some synthetic OpenAI data we can explore in a LLM Monitoring Debugging Board." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "4549831c", + "metadata": {}, + "source": [ + "\"Weights\n", + "\n", + "# Generate Synthetic Trace Data\n", + "\n", + "Generate some synthetic OpenAI data we can explore in a LLM Monitoring Debugging Board." + ] + }, + { + "cell_type": "markdown", + "id": "e3d9305e", + "metadata": {}, + "source": [ + "# Step 0: Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00647082", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "from datetime import timedelta\n", + "import weave\n", + "\n", + "from weave.legacy.weave import ops_arrow\n", + "from weave.legacy.weave.monitoring import monitor, StreamTable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93041e2e", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.syndata_mon import random_predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61b9c12a", + "metadata": {}, + "outputs": [], + "source": [ + "WB_ENTITY = # replace with your W&B username or team name\n", + "WB_PROJECT = \"weave\"\n", + "WB_STREAM = \"synthetic_openai_stream\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93653c45", + "metadata": {}, + "outputs": [], + "source": [ + "preds = random_predictions(10)\n", + "\n", + "# Convert synthetic data into the format used by the weave.legacy.weave.monitoring.openai integration\n", + "\n", + "# convert model_version in the synthetic data to an openai model version\n", + "# this makes it so that there's a new API key that has appeared in our logs recently, and that key\n", + "# has started using gpt-4 which makes a cost spike\n", + "versions = sorted(preds.column('model_version').unique())\n", + "version_map = {}\n", + "for i, v in enumerate(reversed(versions)):\n", + " api_key = 'sk-U4...yK7z'\n", + " model = 'gpt-3.5-turbo-0613'\n", + " if i == 1 or i == 2:\n", + " # second and third most recent versions use a different api key\n", + " api_key = 'sk-U9...a22c'\n", + " if i == 1:\n", + " # second most recent version uses gpt-4\n", + " model = 'gpt-4-0613'\n", + " version_map[v] = (api_key, model)\n", + " \n", + "spans = [] \n", + "for i, pred in enumerate(preds):\n", + " api_key, model = version_map[pred['model_version']]\n", + " latency_mult = 1\n", + " if model == 'gpt-4-0613':\n", + " latency_mult = 3\n", + " span = monitor.Span('openai.api_resources.chat_completion.type.create',\n", + " inputs={\n", + " 'messages':[\n", + " {\"role\": \"user\", \"content\": pred['prompt']}\n", + " ]\n", + " },\n", + " output={\n", + " 'id': 'chatcmpl-%s' % uuid.uuid4(),\n", + " 'object': 'chat.completion',\n", + " 'created': pred['timestamp'].timestamp(),\n", + " 'model': model,\n", + " 'choices': [\n", + " {\n", + " 'index': 0,\n", + " 'message': {\n", + " 'role': 'assistant',\n", + " 'content': pred['completion']\n", + " },\n", + " 'finish_reason': 'stop'\n", + " }\n", + " ],\n", + "\n", + " },\n", + " attributes={\n", + " 'api_key': api_key,\n", + " 'username': pred['username']\n", + " },\n", + " summary={\n", + " 'prompt_tokens': pred['prompt_tokens'],\n", + " 'completion_tokens': pred['completion_tokens'],\n", + " 'total_tokens': (pred['prompt_tokens'] + pred['completion_tokens'])\n", + " })\n", + " span.start_time = pred['timestamp']\n", + " span.end_time = pred['timestamp'] + timedelta(seconds=pred['latency'] * latency_mult)\n", + " spans.append({'timestamp': pred['timestamp'], **span.asdict()})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9548c9aa", + "metadata": {}, + "outputs": [], + "source": [ + "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/{WB_STREAM}\")\n", + "\n", + "for span in spans:\n", + " st.log(span)\n", + "\n", + "st.finish()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "markdown", - "id": "e3d9305e", - "metadata": {}, - "source": [ - "# Step 0: Setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00647082", - "metadata": {}, - "outputs": [], - "source": [ - "import uuid\n", - "from datetime import timedelta\n", - "import weave\n", - "\n", - "from weave.legacy import ops_arrow\n", - "from weave.legacy.monitoring import monitor, StreamTable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93041e2e", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.syndata_mon import random_predictions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "61b9c12a", - "metadata": {}, - "outputs": [], - "source": [ - "WB_ENTITY = # replace with your W&B username or team name\n", - "WB_PROJECT = \"weave\"\n", - "WB_STREAM = \"synthetic_openai_stream\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93653c45", - "metadata": {}, - "outputs": [], - "source": [ - "preds = random_predictions(10)\n", - "\n", - "# Convert synthetic data into the format used by the weave.legacy.monitoring.openai integration\n", - "\n", - "# convert model_version in the synthetic data to an openai model version\n", - "# this makes it so that there's a new API key that has appeared in our logs recently, and that key\n", - "# has started using gpt-4 which makes a cost spike\n", - "versions = sorted(preds.column('model_version').unique())\n", - "version_map = {}\n", - "for i, v in enumerate(reversed(versions)):\n", - " api_key = 'sk-U4...yK7z'\n", - " model = 'gpt-3.5-turbo-0613'\n", - " if i == 1 or i == 2:\n", - " # second and third most recent versions use a different api key\n", - " api_key = 'sk-U9...a22c'\n", - " if i == 1:\n", - " # second most recent version uses gpt-4\n", - " model = 'gpt-4-0613'\n", - " version_map[v] = (api_key, model)\n", - " \n", - "spans = [] \n", - "for i, pred in enumerate(preds):\n", - " api_key, model = version_map[pred['model_version']]\n", - " latency_mult = 1\n", - " if model == 'gpt-4-0613':\n", - " latency_mult = 3\n", - " span = monitor.Span('openai.api_resources.chat_completion.type.create',\n", - " inputs={\n", - " 'messages':[\n", - " {\"role\": \"user\", \"content\": pred['prompt']}\n", - " ]\n", - " },\n", - " output={\n", - " 'id': 'chatcmpl-%s' % uuid.uuid4(),\n", - " 'object': 'chat.completion',\n", - " 'created': pred['timestamp'].timestamp(),\n", - " 'model': model,\n", - " 'choices': [\n", - " {\n", - " 'index': 0,\n", - " 'message': {\n", - " 'role': 'assistant',\n", - " 'content': pred['completion']\n", - " },\n", - " 'finish_reason': 'stop'\n", - " }\n", - " ],\n", - "\n", - " },\n", - " attributes={\n", - " 'api_key': api_key,\n", - " 'username': pred['username']\n", - " },\n", - " summary={\n", - " 'prompt_tokens': pred['prompt_tokens'],\n", - " 'completion_tokens': pred['completion_tokens'],\n", - " 'total_tokens': (pred['prompt_tokens'] + pred['completion_tokens'])\n", - " })\n", - " span.start_time = pred['timestamp']\n", - " span.end_time = pred['timestamp'] + timedelta(seconds=pred['latency'] * latency_mult)\n", - " spans.append({'timestamp': pred['timestamp'], **span.asdict()})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9548c9aa", - "metadata": {}, - "outputs": [], - "source": [ - "st = StreamTable(f\"{WB_ENTITY}/{WB_PROJECT}/{WB_STREAM}\")\n", - "\n", - "for span in spans:\n", - " st.log(span)\n", - "\n", - "st.finish()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/04 Tutorial - Publishing Data and Ops.ipynb b/weave/legacy/examples/experimental/skip_test/04 Tutorial - Publishing Data and Ops.ipynb index 1e5591b2b11..e7355e75f3f 100644 --- a/weave/legacy/examples/experimental/skip_test/04 Tutorial - Publishing Data and Ops.ipynb +++ b/weave/legacy/examples/experimental/skip_test/04 Tutorial - Publishing Data and Ops.ipynb @@ -1,125 +1,125 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "b174bac3", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from typing import Any\n", - "\n", - "@weave.op()\n", - "def inc(a: int, delta: int = 1) -> int:\n", - " return a + delta\n", - "\n", - "# store x in local dir\n", - "x = weave.save(10, \"local-num\")\n", - "print(x)\n", - "# store y as a remote artifact - this is stateful and will create\n", - "# new versions if you've previously saved this artifact\n", - "y = weave.publish(100, \"remote-num\")\n", - "print(y)" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "b174bac3", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from typing import Any\n", + "\n", + "@weave.op()\n", + "def inc(a: int, delta: int = 1) -> int:\n", + " return a + delta\n", + "\n", + "# store x in local dir\n", + "x = weave.save(10, \"local-num\")\n", + "print(x)\n", + "# store y as a remote artifact - this is stateful and will create\n", + "# new versions if you've previously saved this artifact\n", + "y = weave.publish(100, \"remote-num\")\n", + "print(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1949651f", + "metadata": {}, + "outputs": [], + "source": [ + "# increment constant\n", + "print(weave.use(inc(1)))\n", + "# increment local number - DO NOT SAVE\n", + "print(weave.use(inc(x)))\n", + "# increment remote number - DO NOT SAVE\n", + "print(weave.use(inc(y)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efd9ed1c", + "metadata": {}, + "outputs": [], + "source": [ + "# this might take a minute since constructing w&b artifacts is a bit slow atm\n", + "for i in range(0, 5):\n", + " y = weave.use(inc(y, 1))\n", + " # publish result back to remote-num\n", + " weave.publish(y, \"remote-num\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35a0e593", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: this doesn't work yet, it only returns the current version\n", + "for version in weave.versions(y):\n", + " print(version)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d6a69fc", + "metadata": {}, + "outputs": [], + "source": [ + "# we can also publish ops\n", + "remote_op = weave.publish(inc)\n", + "print(remote_op)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7548b05f", + "metadata": {}, + "outputs": [], + "source": [ + "# this cell can be run from a full kernel restart and it should work\n", + "import weave\n", + "import shutil\n", + "from weave.legacy.weave.artifacts_local import LOCAL_ARTIFACT_DIR\n", + "from weave import uris\n", + "\n", + "try:\n", + " shutil.rmtree(LOCAL_ARTIFACT_DIR)\n", + "except FileNotFoundError:\n", + " pass\n", + "\n", + "remote_y = uris.WeaveURI.parse('wandb-artifact://jzhao/weave_ops/remote-num:v0').to_ref().get()\n", + "remote_inc = uris.WeaveURI.parse('wandb-artifact://jzhao/weave_ops/op-def-op-inc:v0').to_ref().get()\n", + "\n", + "remote_y" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "1949651f", - "metadata": {}, - "outputs": [], - "source": [ - "# increment constant\n", - "print(weave.use(inc(1)))\n", - "# increment local number - DO NOT SAVE\n", - "print(weave.use(inc(x)))\n", - "# increment remote number - DO NOT SAVE\n", - "print(weave.use(inc(y)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efd9ed1c", - "metadata": {}, - "outputs": [], - "source": [ - "# this might take a minute since constructing w&b artifacts is a bit slow atm\n", - "for i in range(0, 5):\n", - " y = weave.use(inc(y, 1))\n", - " # publish result back to remote-num\n", - " weave.publish(y, \"remote-num\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35a0e593", - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: this doesn't work yet, it only returns the current version\n", - "for version in weave.versions(y):\n", - " print(version)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d6a69fc", - "metadata": {}, - "outputs": [], - "source": [ - "# we can also publish ops\n", - "remote_op = weave.publish(inc)\n", - "print(remote_op)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7548b05f", - "metadata": {}, - "outputs": [], - "source": [ - "# this cell can be run from a full kernel restart and it should work\n", - "import weave\n", - "import shutil\n", - "from weave.legacy.artifacts_local import LOCAL_ARTIFACT_DIR\n", - "from weave import uris\n", - "\n", - "try:\n", - " shutil.rmtree(LOCAL_ARTIFACT_DIR)\n", - "except FileNotFoundError:\n", - " pass\n", - "\n", - "remote_y = uris.WeaveURI.parse('wandb-artifact://jzhao/weave_ops/remote-num:v0').to_ref().get()\n", - "remote_inc = uris.WeaveURI.parse('wandb-artifact://jzhao/weave_ops/op-def-op-inc:v0').to_ref().get()\n", - "\n", - "remote_y" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Composable Python panels.ipynb b/weave/legacy/examples/experimental/skip_test/Composable Python panels.ipynb index 22400d2130f..2a3d08c47be 100644 --- a/weave/legacy/examples/experimental/skip_test/Composable Python panels.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Composable Python panels.ipynb @@ -1,79 +1,79 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "6f2db98c", - "metadata": {}, - "source": [ - "## This demo is not currently working" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "6f2db98c", + "metadata": {}, + "source": [ + "## This demo is not currently working" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b582daf", + "metadata": {}, + "outputs": [], + "source": [ + "import weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06ea5a95", + "metadata": {}, + "outputs": [], + "source": [ + "container = weave.legacy.weave.panels.Container()\n", + "\n", + "# A couple variables to hold our slider values (this is unnecessary boilerplate that I can\n", + "# get rid of)\n", + "var_a = container.config.add_variable('slider_value_a', weave.types.Number(), 5)\n", + "var_b = container.config.add_variable('slider_value_b', weave.types.Number(), 19)\n", + "\n", + "# Create two sliders in the Container\n", + "container.config.add_panel(\n", + " weave.legacy.weave.panels.Slider(input_node=var_a, config=weave.legacy.weave.panels.SliderConfig(0, 100, 0.1)))\n", + "container.config.add_panel(\n", + " weave.legacy.weave.panels.Slider(input_node=var_b, config=weave.legacy.weave.panels.SliderConfig(-1000, 1000, 1)))\n", + "\n", + "# Use a Number panel to show the results of adding the two sliders together\n", + "container.config.add_panel(\n", + " weave.legacy.weave.panels.Number(input_node=var_a + var_b)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9423cf32", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(container)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b582daf", - "metadata": {}, - "outputs": [], - "source": [ - "import weave" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "06ea5a95", - "metadata": {}, - "outputs": [], - "source": [ - "container = weave.legacy.panels.Container()\n", - "\n", - "# A couple variables to hold our slider values (this is unnecessary boilerplate that I can\n", - "# get rid of)\n", - "var_a = container.config.add_variable('slider_value_a', weave.types.Number(), 5)\n", - "var_b = container.config.add_variable('slider_value_b', weave.types.Number(), 19)\n", - "\n", - "# Create two sliders in the Container\n", - "container.config.add_panel(\n", - " weave.legacy.panels.Slider(input_node=var_a, config=weave.legacy.panels.SliderConfig(0, 100, 0.1)))\n", - "container.config.add_panel(\n", - " weave.legacy.panels.Slider(input_node=var_b, config=weave.legacy.panels.SliderConfig(-1000, 1000, 1)))\n", - "\n", - "# Use a Number panel to show the results of adding the two sliders together\n", - "container.config.add_panel(\n", - " weave.legacy.panels.Number(input_node=var_a + var_b)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9423cf32", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(container)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Confusion Matrix.ipynb b/weave/legacy/examples/experimental/skip_test/Confusion Matrix.ipynb index 02e964017eb..2aabd645afa 100644 --- a/weave/legacy/examples/experimental/skip_test/Confusion Matrix.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Confusion Matrix.ipynb @@ -1,288 +1,288 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "51500f80", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import typing\n", - "import itertools\n", - "from PIL import Image\n", - "import weave\n", - "from weave.legacy import storage\n", - "from weave.legacy import weave_internal\n", - "#weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "51500f80", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import typing\n", + "import itertools\n", + "from PIL import Image\n", + "import weave\n", + "from weave.legacy.weave import storage\n", + "from weave.legacy.weave import weave_internal\n", + "#weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6e5e1c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Not really model predictions but it'll work for now\n", + "import string\n", + "import hashlib\n", + "\n", + "def simple_hash(n, b):\n", + " return int.from_bytes(hashlib.sha256(str(n).encode()).digest(), \"little\") % b\n", + "\n", + "def create_data(n_rows, n_extra_cols, images=False):\n", + " inner_count = int(n_rows / 25)\n", + " base_im = Image.linear_gradient('L').resize((32, 32))\n", + " x_choices = string.ascii_lowercase\n", + " extra_cols = [chr(ord('a') + i) for i in range(n_extra_cols)]\n", + " ims = []\n", + " for i, (rotate, shear, _) in enumerate(\n", + " itertools.product(range(5), range(5), range(inner_count))\n", + " ):\n", + " im ={\n", + " 'rotate': rotate,\n", + " 'shear': shear,\n", + " 'y': x_choices[simple_hash(i**13, 5)],\n", + " 'x': x_choices[simple_hash(i**13, 11)]\n", + " }\n", + " if images:\n", + " im['image'] = (base_im\n", + " .rotate(rotate * 4)\n", + " .transform((32, 32), Image.AFFINE, (1, shear / 10, 0, 0, 1, 0), Image.BICUBIC))\n", + " for j, col in enumerate(extra_cols):\n", + " im[col] = x_choices[simple_hash(i*13**j, 11)]\n", + " ims.append(im)\n", + " return ims\n", + "\n", + "ims = create_data(100, 1, True)\n", + "#ims = storage.to_arrow(ims)\n", + "#ims = weave.save(ims)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2da45914", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(ims)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49501ff4", + "metadata": {}, + "outputs": [], + "source": [ + "plot = weave.legacy.weave.panels.Plot(ims)\n", + "plot.set_x(lambda row: row['rotate'])\n", + "plot.set_y(lambda row: row['x'])\n", + "plot.set_tooltip(lambda row: row['image'])\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "markdown", + "id": "6583e01c", + "metadata": {}, + "source": [ + "## Facet is fun!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "090d8ab8", + "metadata": {}, + "outputs": [], + "source": [ + "# This is one way to build a multi-confusion matrix.\n", + "\n", + "facet = weave.legacy.weave.panels.Facet(\n", + " input_node=ims,\n", + " x=lambda im: im['rotate'],\n", + " y=lambda im: im['shear'],\n", + " select=lambda cell: weave.legacy.weave.panels.Plot(\n", + " input_node=cell.groupby(lambda row: row['y']),\n", + " x=lambda group: group.count(),\n", + " y=lambda group: group.key(),\n", + " label=lambda group: group.key(),\n", + " tooltip=lambda group: group.map(lambda r: r['image']),\n", + " mark='bar',\n", + " no_axes=True,\n", + " no_legend=True\n", + " )\n", + ")\n", + "\n", + "weave.show(facet)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e987f626", + "metadata": {}, + "outputs": [], + "source": [ + "# @weave.op()\n", + "# def confusion_matrix(inp: typing.Any, guess_col: str, truth_col: str, compare_col: str) -> weave.legacy.weave.panels.Facet:\n", + "# return weave.legacy.weave.panels.Facet(\n", + "# input_node=inp,\n", + "# x=lambda i: i[guess_col],\n", + "# y=lambda i: i[truth_col],\n", + "# select=lambda cell: weave.legacy.weave.panels.Plot(\n", + "# input_node=cell.groupby(\n", + "# weave.define_fn({'row': cell.type.object_type}, lambda row: row[compare_col])),\n", + "# x=lambda group: group.count(),\n", + "# y=lambda group: group.key(),\n", + "# label=lambda group: group.key(),\n", + "# mark='bar',\n", + "# no_axes=True,\n", + "# no_legend=True\n", + "# )\n", + "# )\n", + "\n", + "# An example of a Panel returning op. This (sort of) works but there are lots of \n", + "\n", + "@weave.op()\n", + "def confusion_matrix(inp: typing.Any, guess_col: str, truth_col: str, compare_col: str) -> weave.legacy.weave.panels.Facet:\n", + " return weave.legacy.weave.panels.Facet(\n", + " input_node=inp,\n", + " x=lambda i: i[guess_col],\n", + " y=lambda i: i[truth_col],\n", + " select=lambda cell: cell.count()\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c800bac", + "metadata": {}, + "outputs": [], + "source": [ + "demos.confusion_matrix(ims, 'rotate', 'x', 'y')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eff6907", + "metadata": {}, + "outputs": [], + "source": [ + "# Other explorations\n", + "\n", + "\n", + "# weave.show(facet)\n", + "\n", + "# # Not working yet, but playing with removing lambdas\n", + "# facet = weave.legacy.weave.panels.Facet(\n", + "# input_node=ims,\n", + "# x=ims['rotate'],\n", + "# y=ims['shear'],\n", + "# select=lambda cell: weave.legacy.weave.panels.Plot(\n", + "# input_node=cell.groupby(cell.row['y']),\n", + "# x=lambda group: group.count(),\n", + "# y=lambda group: group.key(),\n", + "# label=lambda group: group.key(),\n", + "# mark='bar',\n", + "# no_axes=True,\n", + "# no_legend=True\n", + "# )\n", + "# )\n", + "\n", + "# # Maybe another cool thing\n", + "# facet = weave.legacy.weave.panels.Facet(\n", + "# input_node=ims,\n", + "# x=lambda im: im['rotate'],\n", + "# y=lambda im: im['shear'],\n", + "# select=lambda cell_ims: cell_ims.groupby(cell.row['y'])\n", + "# .Plot(\n", + "# x=lambda group: group.count(),\n", + "# y=lambda group: group.key(),\n", + "# label=lambda group: group.key(),\n", + "# mark='bar',\n", + "# no_axes=True,\n", + "# no_legend=True\n", + "# )\n", + "# )\n", + "\n", + "# # OK actually its more like we just always want to drop the first argument\n", + "# # This is finally minimal, but we can't get autocomplete help.\n", + "# facet = ims.Facet(\n", + "# x=select('rotate'),\n", + "# y=select('shear'),\n", + "# select=groupby('y')\n", + "# .Plot(\n", + "# x=count(),\n", + "# y=group_key(),\n", + "# label=group_key(),\n", + "# mark=select('bar'),\n", + "# no_axes=True,\n", + "# no_legend=True\n", + "# )\n", + "# )\n", + "\n", + "# # Hmm.\n", + "# facet = ims\n", + "# .groupby(\n", + "# facet_x=select('rotate'),\n", + "# facet_y=select('shear'),\n", + "# y=select('run'))\n", + "# .count()\n", + "# .Plot()\n", + "\n", + "# facet = Plot(\n", + "# ims,\n", + "# facet_x=select('rotate'),\n", + "# facet_y=select('shear'),\n", + "# y=select('run')\n", + "# x=count()\n", + ")\n", + "\n", + "# TODO:\n", + "# - get rid of lambdas (ims.row could be a variable?)\n", + "# ... or we could just treat ims as the row variable when its assigned to a plot?\n", + "# - This is why react is a little nicer, component control flow isn't usually hidden\n", + "# away inside other components (framework style). Instead, you decide how you want to lay stuff\n", + "# out...\n", + "# - But we can achieve that here... We just need to make some lower level components.\n", + "# (Try IT!) Instead of PanelFacet, use PanelLayout or PanelGrid or something\n", + "# - see if we can get rid of groupby requirement in PanelPlot. You don't need it in Vega\n", + "# - also maybe look at Altair API?\n", + "#\n", + "# In react you'd do:\n", + "# const ConfusionMatrix = (ims) => (\n", + "# \n", + "# \n", + "# \n", + "# )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "b6e5e1c9", - "metadata": {}, - "outputs": [], - "source": [ - "# Not really model predictions but it'll work for now\n", - "import string\n", - "import hashlib\n", - "\n", - "def simple_hash(n, b):\n", - " return int.from_bytes(hashlib.sha256(str(n).encode()).digest(), \"little\") % b\n", - "\n", - "def create_data(n_rows, n_extra_cols, images=False):\n", - " inner_count = int(n_rows / 25)\n", - " base_im = Image.linear_gradient('L').resize((32, 32))\n", - " x_choices = string.ascii_lowercase\n", - " extra_cols = [chr(ord('a') + i) for i in range(n_extra_cols)]\n", - " ims = []\n", - " for i, (rotate, shear, _) in enumerate(\n", - " itertools.product(range(5), range(5), range(inner_count))\n", - " ):\n", - " im ={\n", - " 'rotate': rotate,\n", - " 'shear': shear,\n", - " 'y': x_choices[simple_hash(i**13, 5)],\n", - " 'x': x_choices[simple_hash(i**13, 11)]\n", - " }\n", - " if images:\n", - " im['image'] = (base_im\n", - " .rotate(rotate * 4)\n", - " .transform((32, 32), Image.AFFINE, (1, shear / 10, 0, 0, 1, 0), Image.BICUBIC))\n", - " for j, col in enumerate(extra_cols):\n", - " im[col] = x_choices[simple_hash(i*13**j, 11)]\n", - " ims.append(im)\n", - " return ims\n", - "\n", - "ims = create_data(100, 1, True)\n", - "#ims = storage.to_arrow(ims)\n", - "#ims = weave.save(ims)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2da45914", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(ims)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "49501ff4", - "metadata": {}, - "outputs": [], - "source": [ - "plot = weave.legacy.panels.Plot(ims)\n", - "plot.set_x(lambda row: row['rotate'])\n", - "plot.set_y(lambda row: row['x'])\n", - "plot.set_tooltip(lambda row: row['image'])\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "markdown", - "id": "6583e01c", - "metadata": {}, - "source": [ - "## Facet is fun!\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "090d8ab8", - "metadata": {}, - "outputs": [], - "source": [ - "# This is one way to build a multi-confusion matrix.\n", - "\n", - "facet = weave.legacy.panels.Facet(\n", - " input_node=ims,\n", - " x=lambda im: im['rotate'],\n", - " y=lambda im: im['shear'],\n", - " select=lambda cell: weave.legacy.panels.Plot(\n", - " input_node=cell.groupby(lambda row: row['y']),\n", - " x=lambda group: group.count(),\n", - " y=lambda group: group.key(),\n", - " label=lambda group: group.key(),\n", - " tooltip=lambda group: group.map(lambda r: r['image']),\n", - " mark='bar',\n", - " no_axes=True,\n", - " no_legend=True\n", - " )\n", - ")\n", - "\n", - "weave.show(facet)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e987f626", - "metadata": {}, - "outputs": [], - "source": [ - "# @weave.op()\n", - "# def confusion_matrix(inp: typing.Any, guess_col: str, truth_col: str, compare_col: str) -> weave.legacy.panels.Facet:\n", - "# return weave.legacy.panels.Facet(\n", - "# input_node=inp,\n", - "# x=lambda i: i[guess_col],\n", - "# y=lambda i: i[truth_col],\n", - "# select=lambda cell: weave.legacy.panels.Plot(\n", - "# input_node=cell.groupby(\n", - "# weave.define_fn({'row': cell.type.object_type}, lambda row: row[compare_col])),\n", - "# x=lambda group: group.count(),\n", - "# y=lambda group: group.key(),\n", - "# label=lambda group: group.key(),\n", - "# mark='bar',\n", - "# no_axes=True,\n", - "# no_legend=True\n", - "# )\n", - "# )\n", - "\n", - "# An example of a Panel returning op. This (sort of) works but there are lots of \n", - "\n", - "@weave.op()\n", - "def confusion_matrix(inp: typing.Any, guess_col: str, truth_col: str, compare_col: str) -> weave.legacy.panels.Facet:\n", - " return weave.legacy.panels.Facet(\n", - " input_node=inp,\n", - " x=lambda i: i[guess_col],\n", - " y=lambda i: i[truth_col],\n", - " select=lambda cell: cell.count()\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c800bac", - "metadata": {}, - "outputs": [], - "source": [ - "demos.confusion_matrix(ims, 'rotate', 'x', 'y')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7eff6907", - "metadata": {}, - "outputs": [], - "source": [ - "# Other explorations\n", - "\n", - "\n", - "# weave.show(facet)\n", - "\n", - "# # Not working yet, but playing with removing lambdas\n", - "# facet = weave.legacy.panels.Facet(\n", - "# input_node=ims,\n", - "# x=ims['rotate'],\n", - "# y=ims['shear'],\n", - "# select=lambda cell: weave.legacy.panels.Plot(\n", - "# input_node=cell.groupby(cell.row['y']),\n", - "# x=lambda group: group.count(),\n", - "# y=lambda group: group.key(),\n", - "# label=lambda group: group.key(),\n", - "# mark='bar',\n", - "# no_axes=True,\n", - "# no_legend=True\n", - "# )\n", - "# )\n", - "\n", - "# # Maybe another cool thing\n", - "# facet = weave.legacy.panels.Facet(\n", - "# input_node=ims,\n", - "# x=lambda im: im['rotate'],\n", - "# y=lambda im: im['shear'],\n", - "# select=lambda cell_ims: cell_ims.groupby(cell.row['y'])\n", - "# .Plot(\n", - "# x=lambda group: group.count(),\n", - "# y=lambda group: group.key(),\n", - "# label=lambda group: group.key(),\n", - "# mark='bar',\n", - "# no_axes=True,\n", - "# no_legend=True\n", - "# )\n", - "# )\n", - "\n", - "# # OK actually its more like we just always want to drop the first argument\n", - "# # This is finally minimal, but we can't get autocomplete help.\n", - "# facet = ims.Facet(\n", - "# x=select('rotate'),\n", - "# y=select('shear'),\n", - "# select=groupby('y')\n", - "# .Plot(\n", - "# x=count(),\n", - "# y=group_key(),\n", - "# label=group_key(),\n", - "# mark=select('bar'),\n", - "# no_axes=True,\n", - "# no_legend=True\n", - "# )\n", - "# )\n", - "\n", - "# # Hmm.\n", - "# facet = ims\n", - "# .groupby(\n", - "# facet_x=select('rotate'),\n", - "# facet_y=select('shear'),\n", - "# y=select('run'))\n", - "# .count()\n", - "# .Plot()\n", - "\n", - "# facet = Plot(\n", - "# ims,\n", - "# facet_x=select('rotate'),\n", - "# facet_y=select('shear'),\n", - "# y=select('run')\n", - "# x=count()\n", - ")\n", - "\n", - "# TODO:\n", - "# - get rid of lambdas (ims.row could be a variable?)\n", - "# ... or we could just treat ims as the row variable when its assigned to a plot?\n", - "# - This is why react is a little nicer, component control flow isn't usually hidden\n", - "# away inside other components (framework style). Instead, you decide how you want to lay stuff\n", - "# out...\n", - "# - But we can achieve that here... We just need to make some lower level components.\n", - "# (Try IT!) Instead of PanelFacet, use PanelLayout or PanelGrid or something\n", - "# - see if we can get rid of groupby requirement in PanelPlot. You don't need it in Vega\n", - "# - also maybe look at Altair API?\n", - "#\n", - "# In react you'd do:\n", - "# const ConfusionMatrix = (ims) => (\n", - "# \n", - "# \n", - "# \n", - "# )" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Custom ops.ipynb b/weave/legacy/examples/experimental/skip_test/Custom ops.ipynb index 0b0ee07a208..6392f1d34fd 100644 --- a/weave/legacy/examples/experimental/skip_test/Custom ops.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Custom ops.ipynb @@ -1,149 +1,149 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f6c524b9", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import math\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f6c524b9", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import math\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a257651b", + "metadata": {}, + "outputs": [], + "source": [ + "# Create our own useful op.\n", + "\n", + "import math\n", + "\n", + "@weave.op()\n", + "def my_func(x: float) -> float:\n", + " return math.sin(x / 0.2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89b6c0db", + "metadata": {}, + "outputs": [], + "source": [ + "# Now show it.\n", + "# Weave tries to guess the best way to show your data. Its first guess is not very good yet, so let's teach it!\n", + "# Click the gear icon to configure the Panel\n", + "# Try \"List of: Number\". You can page left to right using the controls that the bottom.\n", + "# Switch to \"Plot\"\n", + "# Pick \"row\" as the x value (todo this should be called \"i\" or \"item\" instead of row)\n", + "# Pick \"row * row\" as the y value.\n", + "# Cool, we made a curve!\n", + "\n", + "\n", + "x = [i / 1000.0 for i in range(0, 1000)]\n", + "\n", + "plot = weave.legacy.weave.panels.Plot(x)\n", + "plot.set_x(lambda row: row)\n", + "plot.set_y(lambda row: my_func(row))\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d97c0fd", + "metadata": {}, + "outputs": [], + "source": [ + "run_data = []\n", + "xs = [i / 1000.0 for i in range(0, 1000)]\n", + "for run in range(3):\n", + " run_data.append({\n", + " 'id': 'id-%s' % run,\n", + " 'x': xs,\n", + " 'y': [math.sin(10 * x / (run+1)) for x in xs]\n", + " })\n", + "plot = weave.legacy.weave.panels.Plot(run_data)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['id'])\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f9b0741", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame()\n", + "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", + "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", + "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", + "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", + "\n", + "# df.melt converts to a table with three columns: x, series, y\n", + "df = df.melt(id_vars=['x'], value_vars=['y1', 'y2', 'y3'], var_name='series', value_name='y')\n", + "\n", + "multiple_y = df.to_dict('records')\n", + "plot = weave.legacy.weave.panels.Plot(multiple_y)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['series'])\n", + "plot.set_mark('line')\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07752656", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame()\n", + "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", + "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", + "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", + "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", + "multiple_y = df.to_dict('records')\n", + "\n", + "plot = weave.legacy.weave.panels.Plot(multiple_y)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: [row['y1'], row['y2'], row['y3']])\n", + "plot.set_label(lambda row: ['y1', 'y2', 'y3'])\n", + "plot.set_mark('line')\n", + "weave.show(plot)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a257651b", - "metadata": {}, - "outputs": [], - "source": [ - "# Create our own useful op.\n", - "\n", - "import math\n", - "\n", - "@weave.op()\n", - "def my_func(x: float) -> float:\n", - " return math.sin(x / 0.2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "89b6c0db", - "metadata": {}, - "outputs": [], - "source": [ - "# Now show it.\n", - "# Weave tries to guess the best way to show your data. Its first guess is not very good yet, so let's teach it!\n", - "# Click the gear icon to configure the Panel\n", - "# Try \"List of: Number\". You can page left to right using the controls that the bottom.\n", - "# Switch to \"Plot\"\n", - "# Pick \"row\" as the x value (todo this should be called \"i\" or \"item\" instead of row)\n", - "# Pick \"row * row\" as the y value.\n", - "# Cool, we made a curve!\n", - "\n", - "\n", - "x = [i / 1000.0 for i in range(0, 1000)]\n", - "\n", - "plot = weave.legacy.panels.Plot(x)\n", - "plot.set_x(lambda row: row)\n", - "plot.set_y(lambda row: my_func(row))\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d97c0fd", - "metadata": {}, - "outputs": [], - "source": [ - "run_data = []\n", - "xs = [i / 1000.0 for i in range(0, 1000)]\n", - "for run in range(3):\n", - " run_data.append({\n", - " 'id': 'id-%s' % run,\n", - " 'x': xs,\n", - " 'y': [math.sin(10 * x / (run+1)) for x in xs]\n", - " })\n", - "plot = weave.legacy.panels.Plot(run_data)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['id'])\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f9b0741", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "df = pd.DataFrame()\n", - "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", - "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", - "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", - "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", - "\n", - "# df.melt converts to a table with three columns: x, series, y\n", - "df = df.melt(id_vars=['x'], value_vars=['y1', 'y2', 'y3'], var_name='series', value_name='y')\n", - "\n", - "multiple_y = df.to_dict('records')\n", - "plot = weave.legacy.panels.Plot(multiple_y)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['series'])\n", - "plot.set_mark('line')\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07752656", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "df = pd.DataFrame()\n", - "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", - "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", - "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", - "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", - "multiple_y = df.to_dict('records')\n", - "\n", - "plot = weave.legacy.panels.Plot(multiple_y)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: [row['y1'], row['y2'], row['y3']])\n", - "plot.set_label(lambda row: ['y1', 'y2', 'y3'])\n", - "plot.set_mark('line')\n", - "weave.show(plot)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Diffusion explore 2.ipynb b/weave/legacy/examples/experimental/skip_test/Diffusion explore 2.ipynb index 3337cda943e..00723093f18 100644 --- a/weave/legacy/examples/experimental/skip_test/Diffusion explore 2.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Diffusion explore 2.ipynb @@ -1,101 +1,101 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "00411857", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal\n", - "from weave.legacy.ecosystem.all import langchain\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "00411857", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "from weave.legacy.weave.ecosystem.all import langchain\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30c46465", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " \"animals\": weave.legacy.weave.panels.Query(\n", + " [\"koala\", \"giraffe\", \"leopard\"],\n", + " text=lambda item: item\n", + " ),\n", + " \"objects\": weave.legacy.weave.panels.Query(\n", + " [\"spoon\", \"guitar\"],\n", + " text=lambda item: item\n", + " ),\n", + " \"prompts\": weave.legacy.weave.panels.Query([\n", + " \"A {animal} with an {object}\",\n", + " \"A 20-word story about a {animal} with an {object}\",\n", + " \"You are creative writer. Describe a scene with a {animal} and a {object}. Choose an art style and use adjectives that evoke it.\"\n", + " ],\n", + " text=lambda item: item),\n", + " \"combos\": lambda animals, objects, prompts:\n", + " weave.legacy.weave.ops.cross_product(weave.legacy.weave.ops.dict_(\n", + " animal=animals.pinned_data(),\n", + " object=objects.pinned_data(),\n", + " prompt=prompts.pinned_data())),\n", + "\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda chat_model, combos: weave.legacy.weave.panels.Facet(\n", + " combos,\n", + " cell_size=(256, 256),\n", + " x=lambda item: item['animal'] + ' ' + item['object'],\n", + " y=lambda item: item['prompt'],\n", + " select=lambda items: \n", + " weave.legacy.weave.panels.Group(\n", + " vars={\n", + " 'gen_text': chat_model.predict(\n", + " items[0]['prompt'].format(weave.legacy.weave.ops.dict_(\n", + " animal=items[0]['animal'],\n", + " object=items[0]['object'],\n", + " ))),\n", + " },\n", + " items={\n", + " 'text': lambda gen_text: gen_text,\n", + " 'image': lambda gen_text: gen_text.stable_diffusion()\n", + " }\n", + " )\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=24)\n", + " )\n", + "# weave.legacy.weave.panels.BoardPanel(\n", + "# lambda col_names: weave.legacy.weave.panels.Table(col_names, columns=[lambda col: col]),\n", + "# layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", + "# ),\n", + "# weave.legacy.weave.panels.BoardPanel(\n", + "# lambda row_names: weave.legacy.weave.panels.Table(row_names, columns=[lambda row: row]),\n", + "# layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", + "# ),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8d3d23b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "30c46465", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " \"animals\": weave.legacy.panels.Query(\n", - " [\"koala\", \"giraffe\", \"leopard\"],\n", - " text=lambda item: item\n", - " ),\n", - " \"objects\": weave.legacy.panels.Query(\n", - " [\"spoon\", \"guitar\"],\n", - " text=lambda item: item\n", - " ),\n", - " \"prompts\": weave.legacy.panels.Query([\n", - " \"A {animal} with an {object}\",\n", - " \"A 20-word story about a {animal} with an {object}\",\n", - " \"You are creative writer. Describe a scene with a {animal} and a {object}. Choose an art style and use adjectives that evoke it.\"\n", - " ],\n", - " text=lambda item: item),\n", - " \"combos\": lambda animals, objects, prompts:\n", - " weave.legacy.ops.cross_product(weave.legacy.ops.dict_(\n", - " animal=animals.pinned_data(),\n", - " object=objects.pinned_data(),\n", - " prompt=prompts.pinned_data())),\n", - "\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda chat_model, combos: weave.legacy.panels.Facet(\n", - " combos,\n", - " cell_size=(256, 256),\n", - " x=lambda item: item['animal'] + ' ' + item['object'],\n", - " y=lambda item: item['prompt'],\n", - " select=lambda items: \n", - " weave.legacy.panels.Group(\n", - " vars={\n", - " 'gen_text': chat_model.predict(\n", - " items[0]['prompt'].format(weave.legacy.ops.dict_(\n", - " animal=items[0]['animal'],\n", - " object=items[0]['object'],\n", - " ))),\n", - " },\n", - " items={\n", - " 'text': lambda gen_text: gen_text,\n", - " 'image': lambda gen_text: gen_text.stable_diffusion()\n", - " }\n", - " )\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=24)\n", - " )\n", - "# weave.legacy.panels.BoardPanel(\n", - "# lambda col_names: weave.legacy.panels.Table(col_names, columns=[lambda col: col]),\n", - "# layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", - "# ),\n", - "# weave.legacy.panels.BoardPanel(\n", - "# lambda row_names: weave.legacy.panels.Table(row_names, columns=[lambda row: row]),\n", - "# layout=weave.legacy.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", - "# ),\n", - " ]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8d3d23b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Diffusion explore.ipynb b/weave/legacy/examples/experimental/skip_test/Diffusion explore.ipynb index a253ccdaae5..9289d57e217 100644 --- a/weave/legacy/examples/experimental/skip_test/Diffusion explore.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Diffusion explore.ipynb @@ -1,169 +1,169 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "6048a38a", - "metadata": {}, - "source": [ - "# Diffusion exploration\n", - "\n", - "Progammatically build a Weave Dashboard that generates a grid of increasingly magical images of unlikely pairs of animals.\n", - "\n", - "## Play with parameters\n", - "\n", - "* topic: change what the pairs are (animals? plants? minerals?)\n", - "* num_cols: change the number of levels/granularity of increasing magic\n", - "* num_rows: change how many groupings of the subject are generated :)\n", - "* prompts: edit the text of the prompt in col_names or row_names to modify this setup :)" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "6048a38a", + "metadata": {}, + "source": [ + "# Diffusion exploration\n", + "\n", + "Progammatically build a Weave Dashboard that generates a grid of increasingly magical images of unlikely pairs of animals.\n", + "\n", + "## Play with parameters\n", + "\n", + "* topic: change what the pairs are (animals? plants? minerals?)\n", + "* num_cols: change the number of levels/granularity of increasing magic\n", + "* num_rows: change how many groupings of the subject are generated :)\n", + "* prompts: edit the text of the prompt in col_names or row_names to modify this setup :)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00411857", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "from weave.legacy.weave.ecosystem.all import langchain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30c46465", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " \"topic\": \"animals\",\n", + " \"num_cols\": 2,\n", + " \"col_names\": lambda chat_model, num_cols: weave.legacy.weave.ops.range(0, num_cols, 1).map(\n", + " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", + " + i.toString()\n", + " + \" ordered descriptive phrases (3 words or fewer) that represent how magical something is, from least magical to most. Phrase \"\n", + " + i.toString()\n", + " + \" is:\")\n", + " ),\n", + " \"num_rows\": 2,\n", + " \"row_names\": lambda chat_model, topic, num_rows: weave.legacy.weave.ops.range(0, num_rows, 1).map(\n", + " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", + " + i.toString()\n", + " + \" \" + topic\n", + " + \" that are unlikely to be seen together. Number \"\n", + " + i.toString()\n", + " + \" is: \")\n", + " ),\n", + "\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda num_rows, num_cols, row_names, col_names: weave.legacy.weave.panels.Facet(\n", + " weave.legacy.weave.ops.range(0, num_rows * num_cols, 1),\n", + " cell_size=(192, 192),\n", + " x=lambda i: col_names[i % num_cols],\n", + " y=lambda i: row_names[(i / num_cols).floor()],\n", + " select=lambda cell_values: (\n", + " weave_internal.const(\"cute hd cartoon of \")\n", + " + row_names[(cell_values[0] / num_cols).floor()]\n", + " + \" \"\n", + " + col_names[(cell_values[0] % num_cols)]).stable_diffusion()\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda col_names: weave.legacy.weave.panels.Table(col_names, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda row_names: weave.legacy.weave.panels.Table(row_names, columns=[lambda row: row]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", + " ),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0f6057c", + "metadata": {}, + "outputs": [], + "source": [ + "weave.save(\n", + "\"\"\"\n", + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " \"num_cols\": 2,\n", + " \"col_names\": lambda chat_model, num_cols: weave.legacy.weave.ops.range(0, num_cols, 1).map(\n", + " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", + " + i.toString()\n", + " + \" ordered descriptive phrases (3 words or fewer) that represent how magical something is, from least magical to most. Phrase \"\n", + " + i.toString()\n", + " + \" is:\")\n", + " ),\n", + " \"num_rows\": 2,\n", + " \"row_names\": lambda chat_model, num_rows: weave.legacy.weave.ops.range(0, num_rows, 1).map(\n", + " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", + " + i.toString()\n", + " + \" animals that are unlikely to be seen together. Animal \"\n", + " + i.toString()\n", + " + \" is: \")\n", + " ),\n", + "\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda num_rows, num_cols, row_names, col_names: weave.legacy.weave.panels.Facet(\n", + " weave.legacy.weave.ops.range(0, num_rows * num_cols, 1),\n", + " cell_size=(192, 192),\n", + " x=lambda i: col_names[i % num_cols],\n", + " y=lambda i: row_names[(i / num_cols).floor()],\n", + " select=lambda cell_values: (\n", + " weave_internal.const(\"cute hd cartoon of \")\n", + " + row_names[(cell_values[0] / num_cols).floor()]\n", + " + \" \"\n", + " + col_names[(cell_values[0] % num_cols)]).stable_diffusion()\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda col_names: weave.legacy.weave.panels.Table(col_names, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda row_names: weave.legacy.weave.panels.Table(row_names, columns=[lambda row: row]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", + " ),\n", + " ]\n", + ")\n", + "\"\"\", 'stable_diffusion_board_str')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "00411857", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal\n", - "from weave.legacy.ecosystem.all import langchain" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "30c46465", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " \"topic\": \"animals\",\n", - " \"num_cols\": 2,\n", - " \"col_names\": lambda chat_model, num_cols: weave.legacy.ops.range(0, num_cols, 1).map(\n", - " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", - " + i.toString()\n", - " + \" ordered descriptive phrases (3 words or fewer) that represent how magical something is, from least magical to most. Phrase \"\n", - " + i.toString()\n", - " + \" is:\")\n", - " ),\n", - " \"num_rows\": 2,\n", - " \"row_names\": lambda chat_model, topic, num_rows: weave.legacy.ops.range(0, num_rows, 1).map(\n", - " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", - " + i.toString()\n", - " + \" \" + topic\n", - " + \" that are unlikely to be seen together. Number \"\n", - " + i.toString()\n", - " + \" is: \")\n", - " ),\n", - "\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda num_rows, num_cols, row_names, col_names: weave.legacy.panels.Facet(\n", - " weave.legacy.ops.range(0, num_rows * num_cols, 1),\n", - " cell_size=(192, 192),\n", - " x=lambda i: col_names[i % num_cols],\n", - " y=lambda i: row_names[(i / num_cols).floor()],\n", - " select=lambda cell_values: (\n", - " weave_internal.const(\"cute hd cartoon of \")\n", - " + row_names[(cell_values[0] / num_cols).floor()]\n", - " + \" \"\n", - " + col_names[(cell_values[0] % num_cols)]).stable_diffusion()\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda col_names: weave.legacy.panels.Table(col_names, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda row_names: weave.legacy.panels.Table(row_names, columns=[lambda row: row]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", - " ),\n", - " ]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c0f6057c", - "metadata": {}, - "outputs": [], - "source": [ - "weave.save(\n", - "\"\"\"\n", - "weave.legacy.panels.Board(\n", - " vars={\n", - " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " \"num_cols\": 2,\n", - " \"col_names\": lambda chat_model, num_cols: weave.legacy.ops.range(0, num_cols, 1).map(\n", - " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", - " + i.toString()\n", - " + \" ordered descriptive phrases (3 words or fewer) that represent how magical something is, from least magical to most. Phrase \"\n", - " + i.toString()\n", - " + \" is:\")\n", - " ),\n", - " \"num_rows\": 2,\n", - " \"row_names\": lambda chat_model, num_rows: weave.legacy.ops.range(0, num_rows, 1).map(\n", - " lambda i: chat_model.predict(weave_internal.const(\"You are a creative writer. You've come up with \")\n", - " + i.toString()\n", - " + \" animals that are unlikely to be seen together. Animal \"\n", - " + i.toString()\n", - " + \" is: \")\n", - " ),\n", - "\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda num_rows, num_cols, row_names, col_names: weave.legacy.panels.Facet(\n", - " weave.legacy.ops.range(0, num_rows * num_cols, 1),\n", - " cell_size=(192, 192),\n", - " x=lambda i: col_names[i % num_cols],\n", - " y=lambda i: row_names[(i / num_cols).floor()],\n", - " select=lambda cell_values: (\n", - " weave_internal.const(\"cute hd cartoon of \")\n", - " + row_names[(cell_values[0] / num_cols).floor()]\n", - " + \" \"\n", - " + col_names[(cell_values[0] % num_cols)]).stable_diffusion()\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda col_names: weave.legacy.panels.Table(col_names, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda row_names: weave.legacy.panels.Table(row_names, columns=[lambda row: row]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", - " ),\n", - " ]\n", - ")\n", - "\"\"\", 'stable_diffusion_board_str')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Diffusion story.ipynb b/weave/legacy/examples/experimental/skip_test/Diffusion story.ipynb index 93844909d88..4a9a040968d 100644 --- a/weave/legacy/examples/experimental/skip_test/Diffusion story.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Diffusion story.ipynb @@ -1,122 +1,122 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "00411857", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal\n", - "from weave.legacy.ecosystem.all import langchain\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "00411857", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "from weave.legacy.weave.ecosystem.all import langchain\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30c46465", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'gpt35': langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " 'gpt4': langchain.chat_openai(\"gpt-4\", 0.7),\n", + " 'chat_model': lambda gpt4, gpt35: gpt4,\n", + " 'director': 'Wes Anderson',\n", + " 'director_emotion': 'worldy',\n", + " 'film_style': 'rollerblading documentary',\n", + " 'number_of_panes': 4,\n", + " 'story_gen_prompt': \"\"\"\n", + " You are {director} creating the next {film_style} film. Today you're feeling {director_emotion}.\n", + " In initial sketches, you write a {number_of_panes} step sketch of the plot of your film.\n", + " You include specific details about how the plot proceeds.\n", + " What do you write? Since you are so creative, you can overcome any limitations you might\n", + " think you have when it comes to writing style. In this case, you write your outline as a json list\n", + " with one item for each step.\n", + " \"\"\",\n", + " 'prompt': lambda director, director_emotion, film_style, number_of_panes, story_gen_prompt: story_gen_prompt.format(weave.legacy.weave.ops.dict_(\n", + " director= director,\n", + " director_emotion=director_emotion,\n", + " film_style=film_style,\n", + " number_of_panes=number_of_panes)),\n", + " 'storyline': lambda prompt, chat_model: chat_model.predict(prompt),\n", + " 'storyline_parsed': lambda storyline: storyline.json_parse_list(),\n", + " 'panel_prompt': 'A storyboard pane for a movie by {director} for their next {film_style} film, The scene description is: {panel}',\n", + " 'storyboard_panes': lambda storyline_parsed, panel_prompt, director, film_style:\n", + " storyline_parsed.map(lambda row:\n", + " panel_prompt.format(weave.legacy.weave.ops.dict_(\n", + " panel=row.json_dumps(),\n", + " director=director,\n", + " film_style=film_style,\n", + " ))).stable_diffusion()\n", + " },\n", + " panels=[ \n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda storyline_parsed: storyline_parsed,\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda storyboard_panes: weave.legacy.weave.panels.Each(storyboard_panes),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", + " )\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8d3d23b", + "metadata": {}, + "outputs": [], + "source": [ + "# imperative version\n", + "\n", + "gpt35 = langchain.chat_openai(\"gpt-3.5-turbo\", 0.7)\n", + "gpt4 = langchain.chat_openai(\"gpt-4\", 0.7)\n", + "chat_model = gpt4\n", + "\n", + "director = 'Wes Anderson'\n", + "film_style = 'great american'\n", + "number_of_panes = 4\n", + "story_gen_prompt = weave.save(\"\"\"\n", + "You are {director} creating the next {film_style} film.\n", + "In initial sketches, you write a {number_of_panes} step sketch of the plot of your film.\n", + "You include specific details about how the plot proceeds.\n", + "What do you write? Since you are so creative, you can overcome any limitations you might\n", + "think you have when it comes to writing style. In this case, you write your outline as a json list\n", + "with one item for each step.\n", + "\"\"\",\n", + " name='story_gen_prompt')\n", + "storyline = gpt35.predict(story_gen_prompt.format({\n", + " 'director': director,\n", + " 'film_style': film_style,\n", + " 'number_of_panes': number_of_panes}))\n", + "storyline_parsed = storyline.json_parse()\n", + "#storyline_parsed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fddc9a8", + "metadata": {}, + "outputs": [], + "source": [ + "#weave.legacy.weave.panels.Each(storyline_parsed.map(lambda row: row.json_dumps()).stable_diffusion())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "30c46465", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " 'gpt35': langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " 'gpt4': langchain.chat_openai(\"gpt-4\", 0.7),\n", - " 'chat_model': lambda gpt4, gpt35: gpt4,\n", - " 'director': 'Wes Anderson',\n", - " 'director_emotion': 'worldy',\n", - " 'film_style': 'rollerblading documentary',\n", - " 'number_of_panes': 4,\n", - " 'story_gen_prompt': \"\"\"\n", - " You are {director} creating the next {film_style} film. Today you're feeling {director_emotion}.\n", - " In initial sketches, you write a {number_of_panes} step sketch of the plot of your film.\n", - " You include specific details about how the plot proceeds.\n", - " What do you write? Since you are so creative, you can overcome any limitations you might\n", - " think you have when it comes to writing style. In this case, you write your outline as a json list\n", - " with one item for each step.\n", - " \"\"\",\n", - " 'prompt': lambda director, director_emotion, film_style, number_of_panes, story_gen_prompt: story_gen_prompt.format(weave.legacy.ops.dict_(\n", - " director= director,\n", - " director_emotion=director_emotion,\n", - " film_style=film_style,\n", - " number_of_panes=number_of_panes)),\n", - " 'storyline': lambda prompt, chat_model: chat_model.predict(prompt),\n", - " 'storyline_parsed': lambda storyline: storyline.json_parse_list(),\n", - " 'panel_prompt': 'A storyboard pane for a movie by {director} for their next {film_style} film, The scene description is: {panel}',\n", - " 'storyboard_panes': lambda storyline_parsed, panel_prompt, director, film_style:\n", - " storyline_parsed.map(lambda row:\n", - " panel_prompt.format(weave.legacy.ops.dict_(\n", - " panel=row.json_dumps(),\n", - " director=director,\n", - " film_style=film_style,\n", - " ))).stable_diffusion()\n", - " },\n", - " panels=[ \n", - " weave.legacy.panels.BoardPanel(\n", - " lambda storyline_parsed: storyline_parsed,\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda storyboard_panes: weave.legacy.panels.Each(storyboard_panes),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12)\n", - " )\n", - " ]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8d3d23b", - "metadata": {}, - "outputs": [], - "source": [ - "# imperative version\n", - "\n", - "gpt35 = langchain.chat_openai(\"gpt-3.5-turbo\", 0.7)\n", - "gpt4 = langchain.chat_openai(\"gpt-4\", 0.7)\n", - "chat_model = gpt4\n", - "\n", - "director = 'Wes Anderson'\n", - "film_style = 'great american'\n", - "number_of_panes = 4\n", - "story_gen_prompt = weave.save(\"\"\"\n", - "You are {director} creating the next {film_style} film.\n", - "In initial sketches, you write a {number_of_panes} step sketch of the plot of your film.\n", - "You include specific details about how the plot proceeds.\n", - "What do you write? Since you are so creative, you can overcome any limitations you might\n", - "think you have when it comes to writing style. In this case, you write your outline as a json list\n", - "with one item for each step.\n", - "\"\"\",\n", - " name='story_gen_prompt')\n", - "storyline = gpt35.predict(story_gen_prompt.format({\n", - " 'director': director,\n", - " 'film_style': film_style,\n", - " 'number_of_panes': number_of_panes}))\n", - "storyline_parsed = storyline.json_parse()\n", - "#storyline_parsed" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6fddc9a8", - "metadata": {}, - "outputs": [], - "source": [ - "#weave.legacy.panels.Each(storyline_parsed.map(lambda row: row.json_dumps()).stable_diffusion())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Docbot.ipynb b/weave/legacy/examples/experimental/skip_test/Docbot.ipynb index 328117dfa9a..c754aef88f5 100644 --- a/weave/legacy/examples/experimental/skip_test/Docbot.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Docbot.ipynb @@ -1,329 +1,329 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "ef0c98ad", - "metadata": {}, - "outputs": [], - "source": [ - "import pathlib\n", - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ecosystem import langchain" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ef0c98ad", + "metadata": {}, + "outputs": [], + "source": [ + "import pathlib\n", + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ecosystem import langchain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f7192c3", + "metadata": {}, + "outputs": [], + "source": [ + "neg_feedback = [\"Is there a way that I can embed an iframe component in Weights and Biases report?\",\n", + " \"how do i create the pretty sweeps plot?\",\n", + " \"I'm getting error 400. What can I do?\",\n", + " \"How can I move a column in a table to the right?\",\n", + " \"In Prompts, how can I resize the Trace Timeline to make it bigger or full screen?\",\n", + " \"I have a question about exporting CSV files from a web panel. Whenever I do this, I always get two extra columns for MAX and MIN values, even if I only have one data curve. Does anyone know how to solve this issue?\",\n", + " \"artifacts cli command to upload a folder of images\",\n", + " \"Is there a recommended way to use Launch in an SLURM environment?\",\n", + " \"How to export a single chart's data using the API?\",\n", + " \"how can i login with a different wandb user?\"\n", + "]\n", + "\n", + "pos_feedback = [\"my logging doesn't seem to include errors when the training crashes, how do I change the logging level for wandb logging?\",\n", + " \"how can i get the data from my wandb run by querying my logs using python?\",\n", + " \"how do I fix an error with wandb Table construction from pandas dataframe: TypeError: Data row contained incompatible types\",\n", + " \"how can i make a heatmap using vega and plot it to wandb? what is the vegaspec?\",\n", + " \"is there a good way to join 2 tables together programmatically?\",\n", + " \"I have a question about sweeps. How can you constrain relationship between parameters. For example, I now that if num_layers * hidden_dim is large, I'll run out of GPU memory. So, also I would like to explore some hyperparameter space, there are some combination I know will fail. optuna as a way to do that: you can throw an special exception to cancel a run during a sweep, so that it is not recorded. Is there something similar in W&B, or another way of pruning unwanted combination of hyperparameters?\",\n", + " \"where can I find my run_id\",\n", + " \"How do I group runs?\",\n", + " \"I am using the Hugging Face trainer to train a GPT-2 model. How can I log in wandb the results of the model in each evaluation?\",\n", + " \"I am looking to finetune LLAMA on my own dataset using OpenAI, can you give me examples on how to do this?\"\n", + " ]\n", + "\n", + "all_qs = [\n", + " {'question': q, 'feedback': 'positive'} for q in pos_feedback] + [\n", + " {'question': q, 'feedback': 'negative'} for q in neg_feedback]\n", + "\n", + "questions = weave.save(all_qs, 'eval_questions')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03a68bf9", + "metadata": {}, + "outputs": [], + "source": [ + "questions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6e1c366", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.docstore.document import Document\n", + "from langchain.text_splitter import (\n", + " MarkdownTextSplitter,\n", + " PythonCodeTextSplitter,\n", + " TokenTextSplitter,\n", + ")\n", + "# Get markdown files from our docs repo\n", + "\n", + "# Checkout of our docs repo: https://github.com/wandb/docodile/\n", + "DOC_DIR = '/Users/shawn/code2/docodile'\n", + "DOC_SUFFIX = '.md'\n", + "\n", + "docs = []\n", + "for file in pathlib.Path(DOC_DIR).glob('**/*' + DOC_SUFFIX):\n", + " with file.open('r') as f:\n", + " # store them as langchain Document objects\n", + " docs.append(Document(page_content=f.read(), metadata={'path': file.name}))\n", + "docs = MarkdownTextSplitter().split_documents(docs)\n", + "docs = TokenTextSplitter().split_documents(docs)\n", + "\n", + "docs = weave.save(docs, 'wandb-docs')\n", + "docs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "624e0b9e", + "metadata": {}, + "outputs": [], + "source": [ + "# Vector store with langchain\n", + "from langchain.vectorstores import VectorStore, FAISS\n", + "from langchain.embeddings.openai import OpenAIEmbeddings\n", + "embeddings = OpenAIEmbeddings()\n", + "vector_store = FAISS.from_documents(weave.use(docs), embeddings)\n", + "\n", + "weave.save(vector_store)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a97a3a9c", + "metadata": {}, + "outputs": [], + "source": [ + "# Vector store with Weave\n", + "\n", + "# Stuff you can do:\n", + "# - .similarity_search()\n", + "# - but currently there is a crash because Row.Group tries to render and fails. Need to switch the\n", + "# panel to Table and then change column to row.__getattr__('page_content')\n", + "# - .document_embeddings\n", + "# - this gets the embeddings out of FAISS, and also performs FAISS' k-means with 20 clusters\n", + "# - switch to projection.plot\n", + "# TODO:\n", + "# - give control over k for k-means\n", + "\n", + "from weave.legacy.weave.ecosystem import langchain\n", + "\n", + "vector_store_node = langchain.faiss_from_documents(docs, langchain.openai_embeddings())\n", + "vector_store_node" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd31e988", + "metadata": {}, + "outputs": [], + "source": [ + "# With langchain\n", + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.chains import RetrievalQA\n", + "model_gpt_35_temp07 = RetrievalQA.from_chain_type(\n", + " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.7),\n", + " chain_type='stuff',\n", + " retriever=vector_store.as_retriever()\n", + " )\n", + "model = model_gpt_35_temp07\n", + "model = weave.save(model_gpt_35_temp07, 'mymodel')\n", + "\n", + "#model.run('hello')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "694ce5d6", + "metadata": {}, + "outputs": [], + "source": [ + "# With weave\n", + "from weave.legacy.weave.ecosystem import langchain\n", + "qa = langchain.retrieval_qa_from_chain_type(\n", + " langchain.chat_openai('gpt-3.5-turbo', 0.7),\n", + " 'stuff',\n", + " vector_store_node)\n", + "qa.run('hello')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27fc1618", + "metadata": {}, + "outputs": [], + "source": [ + "# from langchain.chat_models import ChatOpenAI\n", + "# from langchain.chains import RetrievalQA\n", + "model_02 = RetrievalQA.from_chain_type(\n", + " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.2),\n", + " chain_type='stuff',\n", + " retriever=vector_store.as_retriever()\n", + " )\n", + "model_07 = RetrievalQA.from_chain_type(\n", + " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.7),\n", + " chain_type='stuff',\n", + " retriever=vector_store.as_retriever()\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f18ae43f", + "metadata": {}, + "outputs": [], + "source": [ + "# Two with LC and weave.save\n", + "\n", + "models = [model_02, model_07]\n", + "models = weave.save(models, 'docbot-models')\n", + "models.run('hello')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34c3e345", + "metadata": {}, + "outputs": [], + "source": [ + "# You can map over questions\n", + "weave.legacy.weave.panels.Table(questions.limit(3), columns=[\n", + " lambda q: q['question'],\n", + " lambda q: models.run(q['question'])])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "876bc738", + "metadata": {}, + "outputs": [], + "source": [ + "# Interactive evaluation and exploration dashboard\n", + "\n", + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'documents': docs,\n", + " 'questions': questions.limit(2),\n", + " 'embeddings': langchain.openai_embeddings(),\n", + " 'vector_store': lambda embeddings, documents: langchain.faiss_from_documents(documents, embeddings),\n", + " 'doc_embeddings': lambda vector_store: vector_store.document_embeddings(),\n", + " #'models': models,\n", + " 'model_a': lambda vector_store: langchain.retrieval_qa_from_chain_type(\n", + " langchain.chat_openai('gpt-3.5-turbo', 0.2),\n", + " 'stuff',\n", + " vector_store),\n", + " 'model_b': lambda vector_store: langchain.retrieval_qa_from_chain_type(\n", + " langchain.chat_openai('gpt-3.5-turbo', 0.7),\n", + " 'stuff',\n", + " vector_store),\n", + " 'models': lambda model_a, model_b: weave.legacy.weave.ops.make_list(a=model_a, b=model_b),\n", + " 'projection': lambda doc_embeddings: doc_embeddings.projection2D(\n", + " 'pca',\n", + " 'single',\n", + " ['embedding'],\n", + " {'pca': {},\n", + " 'tsne': {\n", + " 'perplexity': 30,\n", + " 'learningRate': 10,\n", + " 'iterations': 25\n", + " },\n", + " 'umap': {\n", + " 'neighbors': 15,\n", + " 'minDist': 0.1,\n", + " 'spread': 1.0\n", + " }\n", + " }),\n", + " },\n", + " panels=[ \n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda models: weave.legacy.weave.panels.Each(models.run(\"What is Weave?\")),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda model_a, model_b: weave.legacy.weave.panels.Table(questions,\n", + " columns=[\n", + " lambda question: question['question'],\n", + " lambda question: question['feedback'],\n", + " weave.legacy.weave.panels.TableColumn(\n", + " lambda question: model_a.run(question['question']).result,\n", + " name='model_a'\n", + " ),\n", + " weave.legacy.weave.panels.TableColumn(\n", + " lambda question: model_b.run(question['question']).result,\n", + " name='model_b'\n", + " ),\n", + " ]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=6, w=24, h=6)\n", + " ), \n", + " weave.legacy.weave.panels.BoardPanel(\n", + " id='docs_projection',\n", + " panel=lambda projection: weave.legacy.weave.panels.Plot(\n", + " projection,\n", + " x=lambda row: row['projection.x'],\n", + " y=lambda row: row['projection.y'],\n", + " color=lambda row: row['source.cluster']\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda docs_projection: docs_projection.selected_data(),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda documents: weave.legacy.weave.panels.Table(documents,\n", + " columns=[\n", + " lambda doc: doc.page_content,\n", + " lambda doc: doc.metadata['path']\n", + " ]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=18, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda vector_store: weave.legacy.weave.panels.Table(vector_store.similarity_search('weave'),\n", + " columns=[\n", + " lambda doc: doc.page_content,\n", + " lambda doc: doc.metadata['path'] \n", + " ]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=18, w=12, h=6)\n", + " ),\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f7192c3", - "metadata": {}, - "outputs": [], - "source": [ - "neg_feedback = [\"Is there a way that I can embed an iframe component in Weights and Biases report?\",\n", - " \"how do i create the pretty sweeps plot?\",\n", - " \"I'm getting error 400. What can I do?\",\n", - " \"How can I move a column in a table to the right?\",\n", - " \"In Prompts, how can I resize the Trace Timeline to make it bigger or full screen?\",\n", - " \"I have a question about exporting CSV files from a web panel. Whenever I do this, I always get two extra columns for MAX and MIN values, even if I only have one data curve. Does anyone know how to solve this issue?\",\n", - " \"artifacts cli command to upload a folder of images\",\n", - " \"Is there a recommended way to use Launch in an SLURM environment?\",\n", - " \"How to export a single chart's data using the API?\",\n", - " \"how can i login with a different wandb user?\"\n", - "]\n", - "\n", - "pos_feedback = [\"my logging doesn't seem to include errors when the training crashes, how do I change the logging level for wandb logging?\",\n", - " \"how can i get the data from my wandb run by querying my logs using python?\",\n", - " \"how do I fix an error with wandb Table construction from pandas dataframe: TypeError: Data row contained incompatible types\",\n", - " \"how can i make a heatmap using vega and plot it to wandb? what is the vegaspec?\",\n", - " \"is there a good way to join 2 tables together programmatically?\",\n", - " \"I have a question about sweeps. How can you constrain relationship between parameters. For example, I now that if num_layers * hidden_dim is large, I'll run out of GPU memory. So, also I would like to explore some hyperparameter space, there are some combination I know will fail. optuna as a way to do that: you can throw an special exception to cancel a run during a sweep, so that it is not recorded. Is there something similar in W&B, or another way of pruning unwanted combination of hyperparameters?\",\n", - " \"where can I find my run_id\",\n", - " \"How do I group runs?\",\n", - " \"I am using the Hugging Face trainer to train a GPT-2 model. How can I log in wandb the results of the model in each evaluation?\",\n", - " \"I am looking to finetune LLAMA on my own dataset using OpenAI, can you give me examples on how to do this?\"\n", - " ]\n", - "\n", - "all_qs = [\n", - " {'question': q, 'feedback': 'positive'} for q in pos_feedback] + [\n", - " {'question': q, 'feedback': 'negative'} for q in neg_feedback]\n", - "\n", - "questions = weave.save(all_qs, 'eval_questions')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "03a68bf9", - "metadata": {}, - "outputs": [], - "source": [ - "questions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6e1c366", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.docstore.document import Document\n", - "from langchain.text_splitter import (\n", - " MarkdownTextSplitter,\n", - " PythonCodeTextSplitter,\n", - " TokenTextSplitter,\n", - ")\n", - "# Get markdown files from our docs repo\n", - "\n", - "# Checkout of our docs repo: https://github.com/wandb/docodile/\n", - "DOC_DIR = '/Users/shawn/code2/docodile'\n", - "DOC_SUFFIX = '.md'\n", - "\n", - "docs = []\n", - "for file in pathlib.Path(DOC_DIR).glob('**/*' + DOC_SUFFIX):\n", - " with file.open('r') as f:\n", - " # store them as langchain Document objects\n", - " docs.append(Document(page_content=f.read(), metadata={'path': file.name}))\n", - "docs = MarkdownTextSplitter().split_documents(docs)\n", - "docs = TokenTextSplitter().split_documents(docs)\n", - "\n", - "docs = weave.save(docs, 'wandb-docs')\n", - "docs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "624e0b9e", - "metadata": {}, - "outputs": [], - "source": [ - "# Vector store with langchain\n", - "from langchain.vectorstores import VectorStore, FAISS\n", - "from langchain.embeddings.openai import OpenAIEmbeddings\n", - "embeddings = OpenAIEmbeddings()\n", - "vector_store = FAISS.from_documents(weave.use(docs), embeddings)\n", - "\n", - "weave.save(vector_store)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a97a3a9c", - "metadata": {}, - "outputs": [], - "source": [ - "# Vector store with Weave\n", - "\n", - "# Stuff you can do:\n", - "# - .similarity_search()\n", - "# - but currently there is a crash because Row.Group tries to render and fails. Need to switch the\n", - "# panel to Table and then change column to row.__getattr__('page_content')\n", - "# - .document_embeddings\n", - "# - this gets the embeddings out of FAISS, and also performs FAISS' k-means with 20 clusters\n", - "# - switch to projection.plot\n", - "# TODO:\n", - "# - give control over k for k-means\n", - "\n", - "from weave.legacy.ecosystem import langchain\n", - "\n", - "vector_store_node = langchain.faiss_from_documents(docs, langchain.openai_embeddings())\n", - "vector_store_node" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd31e988", - "metadata": {}, - "outputs": [], - "source": [ - "# With langchain\n", - "from langchain.chat_models import ChatOpenAI\n", - "from langchain.chains import RetrievalQA\n", - "model_gpt_35_temp07 = RetrievalQA.from_chain_type(\n", - " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.7),\n", - " chain_type='stuff',\n", - " retriever=vector_store.as_retriever()\n", - " )\n", - "model = model_gpt_35_temp07\n", - "model = weave.save(model_gpt_35_temp07, 'mymodel')\n", - "\n", - "#model.run('hello')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "694ce5d6", - "metadata": {}, - "outputs": [], - "source": [ - "# With weave\n", - "from weave.legacy.ecosystem import langchain\n", - "qa = langchain.retrieval_qa_from_chain_type(\n", - " langchain.chat_openai('gpt-3.5-turbo', 0.7),\n", - " 'stuff',\n", - " vector_store_node)\n", - "qa.run('hello')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "27fc1618", - "metadata": {}, - "outputs": [], - "source": [ - "# from langchain.chat_models import ChatOpenAI\n", - "# from langchain.chains import RetrievalQA\n", - "model_02 = RetrievalQA.from_chain_type(\n", - " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.2),\n", - " chain_type='stuff',\n", - " retriever=vector_store.as_retriever()\n", - " )\n", - "model_07 = RetrievalQA.from_chain_type(\n", - " llm=ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.7),\n", - " chain_type='stuff',\n", - " retriever=vector_store.as_retriever()\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f18ae43f", - "metadata": {}, - "outputs": [], - "source": [ - "# Two with LC and weave.save\n", - "\n", - "models = [model_02, model_07]\n", - "models = weave.save(models, 'docbot-models')\n", - "models.run('hello')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34c3e345", - "metadata": {}, - "outputs": [], - "source": [ - "# You can map over questions\n", - "weave.legacy.panels.Table(questions.limit(3), columns=[\n", - " lambda q: q['question'],\n", - " lambda q: models.run(q['question'])])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "876bc738", - "metadata": {}, - "outputs": [], - "source": [ - "# Interactive evaluation and exploration dashboard\n", - "\n", - "weave.legacy.panels.Board(\n", - " vars={\n", - " 'documents': docs,\n", - " 'questions': questions.limit(2),\n", - " 'embeddings': langchain.openai_embeddings(),\n", - " 'vector_store': lambda embeddings, documents: langchain.faiss_from_documents(documents, embeddings),\n", - " 'doc_embeddings': lambda vector_store: vector_store.document_embeddings(),\n", - " #'models': models,\n", - " 'model_a': lambda vector_store: langchain.retrieval_qa_from_chain_type(\n", - " langchain.chat_openai('gpt-3.5-turbo', 0.2),\n", - " 'stuff',\n", - " vector_store),\n", - " 'model_b': lambda vector_store: langchain.retrieval_qa_from_chain_type(\n", - " langchain.chat_openai('gpt-3.5-turbo', 0.7),\n", - " 'stuff',\n", - " vector_store),\n", - " 'models': lambda model_a, model_b: weave.legacy.ops.make_list(a=model_a, b=model_b),\n", - " 'projection': lambda doc_embeddings: doc_embeddings.projection2D(\n", - " 'pca',\n", - " 'single',\n", - " ['embedding'],\n", - " {'pca': {},\n", - " 'tsne': {\n", - " 'perplexity': 30,\n", - " 'learningRate': 10,\n", - " 'iterations': 25\n", - " },\n", - " 'umap': {\n", - " 'neighbors': 15,\n", - " 'minDist': 0.1,\n", - " 'spread': 1.0\n", - " }\n", - " }),\n", - " },\n", - " panels=[ \n", - " weave.legacy.panels.BoardPanel(\n", - " lambda models: weave.legacy.panels.Each(models.run(\"What is Weave?\")),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda model_a, model_b: weave.legacy.panels.Table(questions,\n", - " columns=[\n", - " lambda question: question['question'],\n", - " lambda question: question['feedback'],\n", - " weave.legacy.panels.TableColumn(\n", - " lambda question: model_a.run(question['question']).result,\n", - " name='model_a'\n", - " ),\n", - " weave.legacy.panels.TableColumn(\n", - " lambda question: model_b.run(question['question']).result,\n", - " name='model_b'\n", - " ),\n", - " ]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=6, w=24, h=6)\n", - " ), \n", - " weave.legacy.panels.BoardPanel(\n", - " id='docs_projection',\n", - " panel=lambda projection: weave.legacy.panels.Plot(\n", - " projection,\n", - " x=lambda row: row['projection.x'],\n", - " y=lambda row: row['projection.y'],\n", - " color=lambda row: row['source.cluster']\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda docs_projection: docs_projection.selected_data(),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=12, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda documents: weave.legacy.panels.Table(documents,\n", - " columns=[\n", - " lambda doc: doc.page_content,\n", - " lambda doc: doc.metadata['path']\n", - " ]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=18, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda vector_store: weave.legacy.panels.Table(vector_store.similarity_search('weave'),\n", - " columns=[\n", - " lambda doc: doc.page_content,\n", - " lambda doc: doc.metadata['path'] \n", - " ]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=18, w=12, h=6)\n", - " ),\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Hack demo.ipynb b/weave/legacy/examples/experimental/skip_test/Hack demo.ipynb index 8df75d8efcc..9d4eea67e0a 100644 --- a/weave/legacy/examples/experimental/skip_test/Hack demo.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Hack demo.ipynb @@ -1,117 +1,117 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "6e89befe", - "metadata": {}, - "outputs": [], - "source": [ - "import typing\n", - "import weave\n", - "from weave.legacy.ecosystem import hackdemo\n", - "\n", - "PROJECT = 'hackdemo'" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6e89befe", + "metadata": {}, + "outputs": [], + "source": [ + "import typing\n", + "import weave\n", + "from weave.legacy.weave.ecosystem import hackdemo\n", + "\n", + "PROJECT = 'hackdemo'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0209514", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = hackdemo.ca_housing_dataset(25000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4893a4a8", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35d52005", + "metadata": {}, + "outputs": [], + "source": [ + "@weave.op()\n", + "def table_summary(table: typing.Any) -> list[weave.legacy.weave.panels.Panel]:\n", + " if not table:\n", + " return\n", + " col_names = list(table[0].keys())\n", + " with weave.lazy_execution():\n", + " cols = {col_name: table.pick(col_name) for col_name in col_names}\n", + " panels = []\n", + " for col_name, col_values in cols.items():\n", + " panels.append(weave.legacy.weave.panels.LabeledItem(item=col_values, label=col_name))\n", + " return panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42638af4", + "metadata": {}, + "outputs": [], + "source": [ + "data_card = hackdemo.Dataset(\n", + " name='California Housing',\n", + " created_by=hackdemo.User(name='Pace, R. Kelley'),\n", + " description='S&P Letters Data\\n\\nWe collected information on the variables using all the block groups in California from the 1990 Cens us. In this sample a block group on average includes 1425.5 individuals living in a geographically co mpact area. Naturally, the geographical area included varies inversely with the population density. W e computed distances among the centroids of each block group as measured in latitude and longitude. W e excluded all the block groups reporting zero entries for the independent and dependent variables. T he final data contained 20,640 observations on 9 variables. The dependent variable is ln(median house value).',\n", + " updated_at='August 25th 2021, at 3:36:29pm',\n", + " table=weave.use(dataset) \n", + ")\n", + "\n", + "saved_data_card = weave.save(data_card)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "578adec4", + "metadata": {}, + "outputs": [], + "source": [ + "#weave.publish(data_card, '%s/ca-housing-cadr' % PROJECT)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c35a4702", + "metadata": {}, + "outputs": [], + "source": [ + "hackdemo.ca_housing_dataset_card(saved_data_card)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0209514", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = hackdemo.ca_housing_dataset(25000)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4893a4a8", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d52005", - "metadata": {}, - "outputs": [], - "source": [ - "@weave.op()\n", - "def table_summary(table: typing.Any) -> list[weave.legacy.panels.Panel]:\n", - " if not table:\n", - " return\n", - " col_names = list(table[0].keys())\n", - " with weave.lazy_execution():\n", - " cols = {col_name: table.pick(col_name) for col_name in col_names}\n", - " panels = []\n", - " for col_name, col_values in cols.items():\n", - " panels.append(weave.legacy.panels.LabeledItem(item=col_values, label=col_name))\n", - " return panels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42638af4", - "metadata": {}, - "outputs": [], - "source": [ - "data_card = hackdemo.Dataset(\n", - " name='California Housing',\n", - " created_by=hackdemo.User(name='Pace, R. Kelley'),\n", - " description='S&P Letters Data\\n\\nWe collected information on the variables using all the block groups in California from the 1990 Cens us. In this sample a block group on average includes 1425.5 individuals living in a geographically co mpact area. Naturally, the geographical area included varies inversely with the population density. W e computed distances among the centroids of each block group as measured in latitude and longitude. W e excluded all the block groups reporting zero entries for the independent and dependent variables. T he final data contained 20,640 observations on 9 variables. The dependent variable is ln(median house value).',\n", - " updated_at='August 25th 2021, at 3:36:29pm',\n", - " table=weave.use(dataset) \n", - ")\n", - "\n", - "saved_data_card = weave.save(data_card)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "578adec4", - "metadata": {}, - "outputs": [], - "source": [ - "#weave.publish(data_card, '%s/ca-housing-cadr' % PROJECT)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c35a4702", - "metadata": {}, - "outputs": [], - "source": [ - "hackdemo.ca_housing_dataset_card(saved_data_card)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/KerasModel.ipynb b/weave/legacy/examples/experimental/skip_test/KerasModel.ipynb index 86de437c093..f03a31c6e65 100644 --- a/weave/legacy/examples/experimental/skip_test/KerasModel.ipynb +++ b/weave/legacy/examples/experimental/skip_test/KerasModel.ipynb @@ -1,305 +1,305 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "ad4e3073", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ecosystem import keras as weave_keras" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ad4e3073", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ecosystem import keras as weave_keras" + ] + }, + { + "cell_type": "markdown", + "id": "2f3cbb0d", + "metadata": {}, + "source": [ + "# Train Language Model\n", + "\n", + "From: https://keras.io/examples/nlp/text_classification_from_scratch/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec47e09b", + "metadata": {}, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d288f1b0", + "metadata": {}, + "outputs": [], + "source": [ + "!curl -O https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\n", + "!tar -xf aclImdb_v1.tar.gz\n", + "!rm -r aclImdb/train/unsup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e74e162", + "metadata": {}, + "outputs": [], + "source": [ + "batch_size = 32\n", + "raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", + " \"aclImdb/train\",\n", + " batch_size=batch_size,\n", + " validation_split=0.2,\n", + " subset=\"training\",\n", + " seed=1337,\n", + ")\n", + "raw_val_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", + " \"aclImdb/train\",\n", + " batch_size=batch_size,\n", + " validation_split=0.2,\n", + " subset=\"validation\",\n", + " seed=1337,\n", + ")\n", + "raw_test_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", + " \"aclImdb/test\", batch_size=batch_size\n", + ")\n", + "\n", + "print(f\"Number of batches in raw_train_ds: {raw_train_ds.cardinality()}\")\n", + "print(f\"Number of batches in raw_val_ds: {raw_val_ds.cardinality()}\")\n", + "print(f\"Number of batches in raw_test_ds: {raw_test_ds.cardinality()}\")\n", + "\n", + "from tensorflow.keras.layers import TextVectorization\n", + "import string\n", + "import re\n", + "\n", + "# Having looked at our data above, we see that the raw text contains HTML break\n", + "# tags of the form '
'. These tags will not be removed by the default\n", + "# standardizer (which doesn't strip HTML). Because of this, we will need to\n", + "# create a custom standardization function.\n", + "def custom_standardization(input_data):\n", + " lowercase = tf.strings.lower(input_data)\n", + " stripped_html = tf.strings.regex_replace(lowercase, \"
\", \" \")\n", + " return tf.strings.regex_replace(\n", + " stripped_html, f\"[{re.escape(string.punctuation)}]\", \"\"\n", + " )\n", + "\n", + "\n", + "# Model constants.\n", + "max_features = 20000\n", + "embedding_dim = 128\n", + "sequence_length = 500\n", + "\n", + "# Now that we have our custom standardization, we can instantiate our text\n", + "# vectorization layer. We are using this layer to normalize, split, and map\n", + "# strings to integers, so we set our 'output_mode' to 'int'.\n", + "# Note that we're using the default split function,\n", + "# and the custom standardization defined above.\n", + "# We also set an explicit maximum sequence length, since the CNNs later in our\n", + "# model won't support ragged sequences.\n", + "vectorize_layer = TextVectorization(\n", + " standardize=custom_standardization,\n", + " max_tokens=max_features,\n", + " output_mode=\"int\",\n", + " output_sequence_length=sequence_length,\n", + ")\n", + "\n", + "vectorize_layer_non_custom = TextVectorization(\n", + "# standardize=custom_standardization,\n", + " max_tokens=max_features,\n", + " output_mode=\"int\",\n", + " output_sequence_length=sequence_length,\n", + ")\n", + "\n", + "# Now that the vocab layer has been created, call `adapt` on a text-only\n", + "# dataset to create the vocabulary. You don't have to batch, but for very large\n", + "# datasets this means you're not keeping spare copies of the dataset in memory.\n", + "\n", + "# Let's make a text-only dataset (no labels):\n", + "text_ds = raw_train_ds.map(lambda x, y: x)\n", + "# Let's call `adapt`:\n", + "vectorize_layer.adapt(text_ds)\n", + "vectorize_layer_non_custom.adapt(text_ds)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e48486a", + "metadata": {}, + "outputs": [], + "source": [ + "def vectorize_text(text, label):\n", + " text = tf.expand_dims(text, -1)\n", + " return vectorize_layer(text), label\n", + "\n", + "\n", + "# Vectorize the data.\n", + "train_ds = raw_train_ds.map(vectorize_text)\n", + "val_ds = raw_val_ds.map(vectorize_text)\n", + "test_ds = raw_test_ds.map(vectorize_text)\n", + "\n", + "# Do async prefetching / buffering of the data for best performance on GPU.\n", + "train_ds = train_ds.cache().prefetch(buffer_size=10)\n", + "val_ds = val_ds.cache().prefetch(buffer_size=10)\n", + "test_ds = test_ds.cache().prefetch(buffer_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f053ba5", + "metadata": {}, + "outputs": [], + "source": [ + "from tensorflow.keras import layers\n", + "\n", + "# A integer input for vocab indices.\n", + "inputs = tf.keras.Input(shape=(None,), dtype=\"int64\")\n", + "\n", + "# Next, we add a layer to map those vocab indices into a space of dimensionality\n", + "# 'embedding_dim'.\n", + "x = layers.Embedding(max_features, embedding_dim)(inputs)\n", + "x = layers.Dropout(0.5)(x)\n", + "\n", + "# Conv1D + global max pooling\n", + "x = layers.Conv1D(128, 7, padding=\"valid\", activation=\"relu\", strides=3)(x)\n", + "x = layers.Conv1D(128, 7, padding=\"valid\", activation=\"relu\", strides=3)(x)\n", + "x = layers.GlobalMaxPooling1D()(x)\n", + "\n", + "# We add a vanilla hidden layer:\n", + "x = layers.Dense(128, activation=\"relu\")(x)\n", + "x = layers.Dropout(0.5)(x)\n", + "\n", + "# We project onto a single unit output layer, and squash it with a sigmoid:\n", + "predictions = layers.Dense(1, activation=\"sigmoid\", name=\"predictions\")(x)\n", + "\n", + "text_model = tf.keras.Model(inputs, predictions)\n", + "\n", + "# Compile the model with binary crossentropy loss and an adam optimizer.\n", + "text_model.compile(loss=\"binary_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f177875d", + "metadata": {}, + "outputs": [], + "source": [ + "epochs = 1\n", + "\n", + "# Fit the model using the train and test datasets.\n", + "text_model.fit(train_ds, validation_data=val_ds, epochs=epochs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a276fc85", + "metadata": {}, + "outputs": [], + "source": [ + "# A string input\n", + "inputs = tf.keras.Input(shape=(1,), dtype=\"string\")\n", + "# Turn strings into vocab indices\n", + "indices = vectorize_layer_non_custom(inputs)\n", + "# Turn vocab indices into predictions\n", + "outputs = text_model(indices)\n", + "\n", + "# Our end to end model\n", + "end_to_end_model = tf.keras.Model(inputs, outputs)\n", + "end_to_end_model.compile(\n", + " loss=\"binary_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06493feb", + "metadata": {}, + "outputs": [], + "source": [ + "saved_text_model = weave.save(end_to_end_model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3040ac71", + "metadata": {}, + "outputs": [], + "source": [ + "weave_keras.call_string(saved_text_model, \"single test string\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "027e8660", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Table([\n", + " {'input_str': \"This is a test\"},\n", + " {'input_str': \"This is a horrible test\"},\n", + " {'input_str': \"I love weave\"},\n", + " {'input_str': \"please work!\"},\n", + " {'input_str': \"I am a person in a house\"},\n", + "], columns=[\n", + " lambda row: row['input_str'],\n", + " lambda row: weave_keras.call_string(saved_text_model, row['input_str'])\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0e435bc", + "metadata": {}, + "outputs": [], + "source": [ + "# published_text_model = weave.save(end_to_end_model)\n", + "published_text_model = weave.publish(end_to_end_model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8691e38a", + "metadata": {}, + "outputs": [], + "source": [ + "weave_keras.call_string(published_text_model, \"single test string\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "markdown", - "id": "2f3cbb0d", - "metadata": {}, - "source": [ - "# Train Language Model\n", - "\n", - "From: https://keras.io/examples/nlp/text_classification_from_scratch/" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ec47e09b", - "metadata": {}, - "outputs": [], - "source": [ - "import tensorflow as tf\n", - "import numpy as np" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d288f1b0", - "metadata": {}, - "outputs": [], - "source": [ - "!curl -O https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\n", - "!tar -xf aclImdb_v1.tar.gz\n", - "!rm -r aclImdb/train/unsup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4e74e162", - "metadata": {}, - "outputs": [], - "source": [ - "batch_size = 32\n", - "raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", - " \"aclImdb/train\",\n", - " batch_size=batch_size,\n", - " validation_split=0.2,\n", - " subset=\"training\",\n", - " seed=1337,\n", - ")\n", - "raw_val_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", - " \"aclImdb/train\",\n", - " batch_size=batch_size,\n", - " validation_split=0.2,\n", - " subset=\"validation\",\n", - " seed=1337,\n", - ")\n", - "raw_test_ds = tf.keras.preprocessing.text_dataset_from_directory(\n", - " \"aclImdb/test\", batch_size=batch_size\n", - ")\n", - "\n", - "print(f\"Number of batches in raw_train_ds: {raw_train_ds.cardinality()}\")\n", - "print(f\"Number of batches in raw_val_ds: {raw_val_ds.cardinality()}\")\n", - "print(f\"Number of batches in raw_test_ds: {raw_test_ds.cardinality()}\")\n", - "\n", - "from tensorflow.keras.layers import TextVectorization\n", - "import string\n", - "import re\n", - "\n", - "# Having looked at our data above, we see that the raw text contains HTML break\n", - "# tags of the form '
'. These tags will not be removed by the default\n", - "# standardizer (which doesn't strip HTML). Because of this, we will need to\n", - "# create a custom standardization function.\n", - "def custom_standardization(input_data):\n", - " lowercase = tf.strings.lower(input_data)\n", - " stripped_html = tf.strings.regex_replace(lowercase, \"
\", \" \")\n", - " return tf.strings.regex_replace(\n", - " stripped_html, f\"[{re.escape(string.punctuation)}]\", \"\"\n", - " )\n", - "\n", - "\n", - "# Model constants.\n", - "max_features = 20000\n", - "embedding_dim = 128\n", - "sequence_length = 500\n", - "\n", - "# Now that we have our custom standardization, we can instantiate our text\n", - "# vectorization layer. We are using this layer to normalize, split, and map\n", - "# strings to integers, so we set our 'output_mode' to 'int'.\n", - "# Note that we're using the default split function,\n", - "# and the custom standardization defined above.\n", - "# We also set an explicit maximum sequence length, since the CNNs later in our\n", - "# model won't support ragged sequences.\n", - "vectorize_layer = TextVectorization(\n", - " standardize=custom_standardization,\n", - " max_tokens=max_features,\n", - " output_mode=\"int\",\n", - " output_sequence_length=sequence_length,\n", - ")\n", - "\n", - "vectorize_layer_non_custom = TextVectorization(\n", - "# standardize=custom_standardization,\n", - " max_tokens=max_features,\n", - " output_mode=\"int\",\n", - " output_sequence_length=sequence_length,\n", - ")\n", - "\n", - "# Now that the vocab layer has been created, call `adapt` on a text-only\n", - "# dataset to create the vocabulary. You don't have to batch, but for very large\n", - "# datasets this means you're not keeping spare copies of the dataset in memory.\n", - "\n", - "# Let's make a text-only dataset (no labels):\n", - "text_ds = raw_train_ds.map(lambda x, y: x)\n", - "# Let's call `adapt`:\n", - "vectorize_layer.adapt(text_ds)\n", - "vectorize_layer_non_custom.adapt(text_ds)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e48486a", - "metadata": {}, - "outputs": [], - "source": [ - "def vectorize_text(text, label):\n", - " text = tf.expand_dims(text, -1)\n", - " return vectorize_layer(text), label\n", - "\n", - "\n", - "# Vectorize the data.\n", - "train_ds = raw_train_ds.map(vectorize_text)\n", - "val_ds = raw_val_ds.map(vectorize_text)\n", - "test_ds = raw_test_ds.map(vectorize_text)\n", - "\n", - "# Do async prefetching / buffering of the data for best performance on GPU.\n", - "train_ds = train_ds.cache().prefetch(buffer_size=10)\n", - "val_ds = val_ds.cache().prefetch(buffer_size=10)\n", - "test_ds = test_ds.cache().prefetch(buffer_size=10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f053ba5", - "metadata": {}, - "outputs": [], - "source": [ - "from tensorflow.keras import layers\n", - "\n", - "# A integer input for vocab indices.\n", - "inputs = tf.keras.Input(shape=(None,), dtype=\"int64\")\n", - "\n", - "# Next, we add a layer to map those vocab indices into a space of dimensionality\n", - "# 'embedding_dim'.\n", - "x = layers.Embedding(max_features, embedding_dim)(inputs)\n", - "x = layers.Dropout(0.5)(x)\n", - "\n", - "# Conv1D + global max pooling\n", - "x = layers.Conv1D(128, 7, padding=\"valid\", activation=\"relu\", strides=3)(x)\n", - "x = layers.Conv1D(128, 7, padding=\"valid\", activation=\"relu\", strides=3)(x)\n", - "x = layers.GlobalMaxPooling1D()(x)\n", - "\n", - "# We add a vanilla hidden layer:\n", - "x = layers.Dense(128, activation=\"relu\")(x)\n", - "x = layers.Dropout(0.5)(x)\n", - "\n", - "# We project onto a single unit output layer, and squash it with a sigmoid:\n", - "predictions = layers.Dense(1, activation=\"sigmoid\", name=\"predictions\")(x)\n", - "\n", - "text_model = tf.keras.Model(inputs, predictions)\n", - "\n", - "# Compile the model with binary crossentropy loss and an adam optimizer.\n", - "text_model.compile(loss=\"binary_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f177875d", - "metadata": {}, - "outputs": [], - "source": [ - "epochs = 1\n", - "\n", - "# Fit the model using the train and test datasets.\n", - "text_model.fit(train_ds, validation_data=val_ds, epochs=epochs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a276fc85", - "metadata": {}, - "outputs": [], - "source": [ - "# A string input\n", - "inputs = tf.keras.Input(shape=(1,), dtype=\"string\")\n", - "# Turn strings into vocab indices\n", - "indices = vectorize_layer_non_custom(inputs)\n", - "# Turn vocab indices into predictions\n", - "outputs = text_model(indices)\n", - "\n", - "# Our end to end model\n", - "end_to_end_model = tf.keras.Model(inputs, outputs)\n", - "end_to_end_model.compile(\n", - " loss=\"binary_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "06493feb", - "metadata": {}, - "outputs": [], - "source": [ - "saved_text_model = weave.save(end_to_end_model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3040ac71", - "metadata": {}, - "outputs": [], - "source": [ - "weave_keras.call_string(saved_text_model, \"single test string\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "027e8660", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Table([\n", - " {'input_str': \"This is a test\"},\n", - " {'input_str': \"This is a horrible test\"},\n", - " {'input_str': \"I love weave\"},\n", - " {'input_str': \"please work!\"},\n", - " {'input_str': \"I am a person in a house\"},\n", - "], columns=[\n", - " lambda row: row['input_str'],\n", - " lambda row: weave_keras.call_string(saved_text_model, row['input_str'])\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f0e435bc", - "metadata": {}, - "outputs": [], - "source": [ - "# published_text_model = weave.save(end_to_end_model)\n", - "published_text_model = weave.publish(end_to_end_model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8691e38a", - "metadata": {}, - "outputs": [], - "source": [ - "weave_keras.call_string(published_text_model, \"single test string\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/LLMs Via Weave.ipynb b/weave/legacy/examples/experimental/skip_test/LLMs Via Weave.ipynb index cf781c31cce..f974b36f8ef 100644 --- a/weave/legacy/examples/experimental/skip_test/LLMs Via Weave.ipynb +++ b/weave/legacy/examples/experimental/skip_test/LLMs Via Weave.ipynb @@ -1,111 +1,111 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2dc04fd0", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2dc04fd0", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "615e5163", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave\n", + "panels = weave.legacy.weave.panels\n", + "\n", + "project = \"tim-test-llms-test-all-final-colab-01\"\n", + "entity = \"timssweeney\"\n", + "\n", + "# Create a Weave Query to get Traces\n", + "traces = (\n", + " weave.legacy.weave.ops\n", + " .project(entity, project)\n", + " .runs()\n", + " .history().map(\n", + " lambda row: row['langchain_trace'].dropna()\n", + " )\n", + " .concat()\n", + " .dropna()\n", + " .sort(\n", + " lambda row: weave.legacy.weave.ops.make_list(a=row.startTime()), \n", + " ['asc']\n", + " )\n", + ")\n", + "\n", + "# Create a table from the query\n", + "table = panels.Table(traces)\n", + "table_state = table.config.tableState\n", + "\n", + "# Add columns, set sorting, and specify panels for columns\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"isSuccess\"], \"Success\")\n", + "start_time_col_id = table_state.add_column(lambda row: row.traceSummaryDict()[\"startTime\"].toTimestamp(), \"Timestamp\")\n", + "table_state.enable_sort(start_time_col_id, \"desc\")\n", + "\n", + "markdown_panel = panels.table_state.PanelDef('string', {'mode': 'markdown'})\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedInput\"], \"Input\", markdown_panel)\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedOutput\"], \"Output\", markdown_panel)\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedChain\"], \"Chain\", markdown_panel)\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"error\"], \"Error\")\n", + "table_state.add_column(lambda row: row.traceSummaryDict()[\"modelHash\"], \"Model ID\")\n", + "\n", + "# Compose into a group with detail view below\n", + "dash = panels.Group(\n", + " items={\n", + " \"all_traces\": table,\n", + " \"selected_details\": lambda all_traces: panels.Card(\n", + " title='Trace Viewer',\n", + " subtitle='',\n", + " content=[\n", + " panels.CardTab(\n", + " name='Trace Timeline',\n", + " content=panels.PanelWBTraceTreeTraceViewer(all_traces.active_data())\n", + " ),\n", + " panels.CardTab(\n", + " name='Model Architecture',\n", + " content=panels.PanelWBTraceTreeModelViewer(all_traces.active_data())\n", + " ),\n", + "\n", + " ]\n", + " )\n", + " }\n", + ")\n", + "\n", + "# Publish to W&B\n", + "# weave.publish(dash, \"weave_ops/prompts_in_weave1\")\n", + "\n", + "# View locally\n", + "dash" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "615e5163", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy\n", - "panels = weave.legacy.panels\n", - "\n", - "project = \"tim-test-llms-test-all-final-colab-01\"\n", - "entity = \"timssweeney\"\n", - "\n", - "# Create a Weave Query to get Traces\n", - "traces = (\n", - " weave.legacy.ops\n", - " .project(entity, project)\n", - " .runs()\n", - " .history().map(\n", - " lambda row: row['langchain_trace'].dropna()\n", - " )\n", - " .concat()\n", - " .dropna()\n", - " .sort(\n", - " lambda row: weave.legacy.ops.make_list(a=row.startTime()), \n", - " ['asc']\n", - " )\n", - ")\n", - "\n", - "# Create a table from the query\n", - "table = panels.Table(traces)\n", - "table_state = table.config.tableState\n", - "\n", - "# Add columns, set sorting, and specify panels for columns\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"isSuccess\"], \"Success\")\n", - "start_time_col_id = table_state.add_column(lambda row: row.traceSummaryDict()[\"startTime\"].toTimestamp(), \"Timestamp\")\n", - "table_state.enable_sort(start_time_col_id, \"desc\")\n", - "\n", - "markdown_panel = panels.table_state.PanelDef('string', {'mode': 'markdown'})\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedInput\"], \"Input\", markdown_panel)\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedOutput\"], \"Output\", markdown_panel)\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"formattedChain\"], \"Chain\", markdown_panel)\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"error\"], \"Error\")\n", - "table_state.add_column(lambda row: row.traceSummaryDict()[\"modelHash\"], \"Model ID\")\n", - "\n", - "# Compose into a group with detail view below\n", - "dash = panels.Group(\n", - " items={\n", - " \"all_traces\": table,\n", - " \"selected_details\": lambda all_traces: panels.Card(\n", - " title='Trace Viewer',\n", - " subtitle='',\n", - " content=[\n", - " panels.CardTab(\n", - " name='Trace Timeline',\n", - " content=panels.PanelWBTraceTreeTraceViewer(all_traces.active_data())\n", - " ),\n", - " panels.CardTab(\n", - " name='Model Architecture',\n", - " content=panels.PanelWBTraceTreeModelViewer(all_traces.active_data())\n", - " ),\n", - "\n", - " ]\n", - " )\n", - " }\n", - ")\n", - "\n", - "# Publish to W&B\n", - "# weave.publish(dash, \"weave_ops/prompts_in_weave1\")\n", - "\n", - "# View locally\n", - "dash" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Model Cards.ipynb b/weave/legacy/examples/experimental/skip_test/Model Cards.ipynb index f00044eebfd..73760222547 100644 --- a/weave/legacy/examples/experimental/skip_test/Model Cards.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Model Cards.ipynb @@ -1,79 +1,79 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c6b2e19b", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ecosystem import models" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c6b2e19b", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ecosystem import models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b51e4d5", + "metadata": {}, + "outputs": [], + "source": [ + "# Declare an instance of a ModelCard\n", + "model_card = models.ModelCard(\n", + " model_name='GPT-3',\n", + " created_by=models.User(name='OpenAI'),\n", + " updated_at='August 25th 2021, at 3:36:29pm', # TODO: timestamp\n", + " model_type='Language model',\n", + " primary_metric=models.TargetMetric(name='acc', direction='up'),\n", + " application='Our API has been deployed in thousands of applications with tasks ranging from helping people learn new languages to solving complex classification problems',\n", + " example='...Slightly better example...'\n", + ")\n", + "model_card_ref = weave.save(model_card, 'my-model-card')\n", + "models.model_card_panel(model_card_ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba518a7e", + "metadata": {}, + "outputs": [], + "source": [ + "# Publish it to W&B\n", + "model_card_wbref = weave.publish(model_card, name='my-model')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32dd7d50", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(model_card_wbref)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b51e4d5", - "metadata": {}, - "outputs": [], - "source": [ - "# Declare an instance of a ModelCard\n", - "model_card = models.ModelCard(\n", - " model_name='GPT-3',\n", - " created_by=models.User(name='OpenAI'),\n", - " updated_at='August 25th 2021, at 3:36:29pm', # TODO: timestamp\n", - " model_type='Language model',\n", - " primary_metric=models.TargetMetric(name='acc', direction='up'),\n", - " application='Our API has been deployed in thousands of applications with tasks ranging from helping people learn new languages to solving complex classification problems',\n", - " example='...Slightly better example...'\n", - ")\n", - "model_card_ref = weave.save(model_card, 'my-model-card')\n", - "models.model_card_panel(model_card_ref)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba518a7e", - "metadata": {}, - "outputs": [], - "source": [ - "# Publish it to W&B\n", - "model_card_wbref = weave.publish(model_card, name='my-model')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32dd7d50", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(model_card_wbref)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Models.ipynb b/weave/legacy/examples/experimental/skip_test/Models.ipynb index d54666776e4..176f5767541 100644 --- a/weave/legacy/examples/experimental/skip_test/Models.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Models.ipynb @@ -1,103 +1,103 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0c235474", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import xgboost\n", - "from weave.legacy.ecosystem import shap\n", - "#weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0c235474", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import xgboost\n", + "from weave.legacy.weave.ecosystem import shap\n", + "#weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63a29a5f", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = weave.use(shap.ca_housing_dataset(1))\n", + "dataset_arrow = weave.legacy.weave.ops.dataframe_to_arrow(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5633045", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(dataset_arrow)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6edbb55", + "metadata": {}, + "outputs": [], + "source": [ + "weave.publish(dataset_arrow, 'my-dataset-transform')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dcccca4b", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40f37c5a", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.get('wandb-artifact://shawn/weave_ops/my-dataset-transform:8c478776a5e01c3c051d')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a0d6be5", + "metadata": {}, + "outputs": [], + "source": [ + "@weave.op()\n", + "def some_plotly_op():\n", + " return Plotly.plot(whatever)\n", + "\n", + "weave.name(some_plotly_op(), name='my-cool-plot')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "63a29a5f", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = weave.use(shap.ca_housing_dataset(1))\n", - "dataset_arrow = weave.legacy.ops.dataframe_to_arrow(dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c5633045", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(dataset_arrow)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b6edbb55", - "metadata": {}, - "outputs": [], - "source": [ - "weave.publish(dataset_arrow, 'my-dataset-transform')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dcccca4b", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40f37c5a", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.get('wandb-artifact://shawn/weave_ops/my-dataset-transform:8c478776a5e01c3c051d')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a0d6be5", - "metadata": {}, - "outputs": [], - "source": [ - "@weave.op()\n", - "def some_plotly_op():\n", - " return Plotly.plot(whatever)\n", - "\n", - "weave.name(some_plotly_op(), name='my-cool-plot')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Monitor2.ipynb b/weave/legacy/examples/experimental/skip_test/Monitor2.ipynb index 27fad384dec..3b3cb66ccaf 100644 --- a/weave/legacy/examples/experimental/skip_test/Monitor2.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Monitor2.ipynb @@ -1,310 +1,310 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "039f298a", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ops_arrow.list_ import dataframe_to_arrow\n", - "from weave.legacy.ecosystem import wandb" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "039f298a", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ops_arrow.list_ import dataframe_to_arrow\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e0d6245", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import random\n", + "from faker import Faker\n", + "from datetime import timedelta, time\n", + "\n", + "# Generate the version schedule\n", + "def generate_version_schedule(start_date, end_date):\n", + " current_date = start_date\n", + " versions = ['1.0']\n", + " version_schedule = {}\n", + " while current_date <= end_date:\n", + " date_versions = []\n", + " for version in versions:\n", + " service_percent = random.uniform(0, 1)\n", + " date_versions.append((version, service_percent))\n", + "\n", + " version_schedule[current_date.date()] = date_versions\n", + " current_date += timedelta(days=1)\n", + " if random.random() < 0.10: # 5% chance to introduce a new version each day\n", + " new_version = f'{float(versions[-1])+0.1:.1f}'\n", + " versions.append(new_version)\n", + " if len(versions) > 1 and random.random() < 0.10:\n", + " versions.pop(0)\n", + " return version_schedule\n", + "\n", + "# Generate the latency schedule\n", + "def generate_latency_schedule(start_date, end_date):\n", + " latency_schedule = {}\n", + " for current_date in pd.date_range(start_date, end_date):\n", + " base_latency = random.uniform(0.1, 1)\n", + " day_factor = random.uniform(0.5, 1.5)\n", + " month_factor = random.uniform(0.5, 1.5)\n", + " latency = base_latency * day_factor * month_factor\n", + " latency_schedule[current_date.date()] = latency\n", + " return latency_schedule\n", + "\n", + "# Generate the cost schedule\n", + "def generate_cost_schedule(start_date, end_date, cost_change_date):\n", + " cost_schedule = {}\n", + " current_date = start_date\n", + " cost_per_token = 0.01\n", + " while current_date <= end_date:\n", + " if current_date >= cost_change_date:\n", + " cost_per_token = 0.005\n", + " cost_schedule[current_date.date()] = cost_per_token\n", + " current_date += timedelta(days=1)\n", + " return cost_schedule\n", + "\n", + "def generate_user_usage_schedule(start_date, end_date, users):\n", + " user_usage_schedule = []\n", + " for user in users:\n", + " current_date = start_date + timedelta(days=random.randrange(90))\n", + " while current_date <= end_date:\n", + " usage_periods = random.randint(1, 30)\n", + " for _ in range(usage_periods):\n", + " period_length_timedelta = timedelta(hours=random.randint(1, 24 * 7))\n", + " rate = random.uniform(0.1, 10)\n", + " user_usage_schedule.append((current_date, user, period_length_timedelta, rate))\n", + " current_date += period_length_timedelta # Increment current_date\n", + " if current_date > end_date:\n", + " break\n", + " return user_usage_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d6a0318", + "metadata": {}, + "outputs": [], + "source": [ + "# Define our fake users\n", + "fake = Faker()\n", + "users = [fake.user_name() for _ in range(100)]\n", + "\n", + "# Read the file and generate prompts\n", + "with open('/Users/shawn/Downloads/t8.shakespeare.txt', 'r') as f:\n", + " lines = f.read().split('\\n')\n", + "\n", + "# Define the time range\n", + "start_date = pd.to_datetime('2023-01-01', utc=True)\n", + "end_date = pd.to_datetime('2023-03-31', utc=True)\n", + "cost_change_date = pd.to_datetime('2023-02-15', utc=True)\n", + "\n", + "# Generate the schedules\n", + "version_schedule = generate_version_schedule(start_date, end_date)\n", + "latency_schedule = generate_latency_schedule(start_date, end_date)\n", + "cost_schedule = generate_cost_schedule(start_date, end_date, cost_change_date)\n", + "user_usage_schedule = generate_user_usage_schedule(start_date, end_date, users)\n", + "\n", + "# Helper function to generate a random completion\n", + "def generate_completion(prompt):\n", + " words = prompt.split()\n", + " completion = ' '.join(random.choices(words, k=int(len(words)* (random.random() + 0.1) * 10)))\n", + " return completion\n", + "\n", + "#latency_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a672c782", + "metadata": {}, + "outputs": [], + "source": [ + "import tqdm\n", + "data = []\n", + "for usage in tqdm.tqdm(user_usage_schedule):\n", + " usage_date, user, usage_period, rate = usage\n", + " \n", + " end_date = usage_date + usage_period\n", + " increment = timedelta(hours=rate)\n", + " \n", + " while usage_date < end_date:\n", + " # Find the version that was active during this usage\n", + " if usage_date.date() not in version_schedule:\n", + " break\n", + " active_versions = version_schedule[usage_date.date()]\n", + " #active_versions = [(version, percent) for date, version, percent in version_schedule if date.date() == usage_date.date()]\n", + " # Normalize the service percentages\n", + " total_percent = sum([percent for version, percent in active_versions])\n", + " if total_percent == 0:\n", + " continue\n", + " normalized_percentages = [percent / total_percent for version, percent in active_versions]\n", + "\n", + " version = np.random.choice([v for v, p in active_versions], p=normalized_percentages)\n", + " \n", + " # Find the cost during this usage\n", + " cost_per_token = cost_schedule[usage_date.date()]\n", + " \n", + " # Find the average latency during this usage\n", + " latency = latency_schedule[usage_date.date()]\n", + " latency *= (0.9 + random.random() *.2)\n", + " \n", + " prompt = ' '.join(random.sample(lines, 10)) # Increase prompt size\n", + " completion = generate_completion(prompt)\n", + " prompt_tokens = len(prompt.split())\n", + " completion_tokens = len(completion.split())\n", + " api_cost = (prompt_tokens + completion_tokens) * cost_per_token\n", + "\n", + " data.append([usage_date, user, version, prompt, completion, \n", + " prompt_tokens, completion_tokens, api_cost, latency])\n", + " \n", + " usage_date += increment\n", + "\n", + "df = pd.DataFrame(data, columns=['timestamp', 'username', 'model_version', 'prompt', 'completion', \n", + " 'prompt_tokens', 'completion_tokens', 'api_cost', 'latency'])\n", + "\n", + "predictions = weave.save(dataframe_to_arrow(df), 'predictions')\n", + "predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57fc86e0", + "metadata": {}, + "outputs": [], + "source": [ + "predictions = weave.legacy.weave.ops.get(\"local-artifact:///predictions:latest/obj\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8049b6dd", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'all_preds': predictions,\n", + " 'x_max': weave.legacy.weave.ops.date_parse(str(end_date)),\n", + " 'x_min': lambda x_max: x_max - weave.legacy.weave.ops.days(5),\n", + " 'predictions': lambda x_min: predictions.filter(lambda pred: pred['timestamp'] > x_min)\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " label=lambda item: item[\"username\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=0, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"api_cost\"].sum(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"api_cost\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"prompt_tokens\"].sum() + preds['completion_tokens'].sum(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"total_tokens\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=8, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"latency\"].avg(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"avg_latency\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=16, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['api_cost'],\n", + " bin_size=0.2\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['prompt_tokens'] + pred['completion_tokens'],\n", + " bin_size=25\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=8, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['latency'],\n", + " bin_size=0.05\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=16, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions: predictions,\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=18, w=24, h=12)\n", + " )\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e0d6245", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import random\n", - "from faker import Faker\n", - "from datetime import timedelta, time\n", - "\n", - "# Generate the version schedule\n", - "def generate_version_schedule(start_date, end_date):\n", - " current_date = start_date\n", - " versions = ['1.0']\n", - " version_schedule = {}\n", - " while current_date <= end_date:\n", - " date_versions = []\n", - " for version in versions:\n", - " service_percent = random.uniform(0, 1)\n", - " date_versions.append((version, service_percent))\n", - "\n", - " version_schedule[current_date.date()] = date_versions\n", - " current_date += timedelta(days=1)\n", - " if random.random() < 0.10: # 5% chance to introduce a new version each day\n", - " new_version = f'{float(versions[-1])+0.1:.1f}'\n", - " versions.append(new_version)\n", - " if len(versions) > 1 and random.random() < 0.10:\n", - " versions.pop(0)\n", - " return version_schedule\n", - "\n", - "# Generate the latency schedule\n", - "def generate_latency_schedule(start_date, end_date):\n", - " latency_schedule = {}\n", - " for current_date in pd.date_range(start_date, end_date):\n", - " base_latency = random.uniform(0.1, 1)\n", - " day_factor = random.uniform(0.5, 1.5)\n", - " month_factor = random.uniform(0.5, 1.5)\n", - " latency = base_latency * day_factor * month_factor\n", - " latency_schedule[current_date.date()] = latency\n", - " return latency_schedule\n", - "\n", - "# Generate the cost schedule\n", - "def generate_cost_schedule(start_date, end_date, cost_change_date):\n", - " cost_schedule = {}\n", - " current_date = start_date\n", - " cost_per_token = 0.01\n", - " while current_date <= end_date:\n", - " if current_date >= cost_change_date:\n", - " cost_per_token = 0.005\n", - " cost_schedule[current_date.date()] = cost_per_token\n", - " current_date += timedelta(days=1)\n", - " return cost_schedule\n", - "\n", - "def generate_user_usage_schedule(start_date, end_date, users):\n", - " user_usage_schedule = []\n", - " for user in users:\n", - " current_date = start_date + timedelta(days=random.randrange(90))\n", - " while current_date <= end_date:\n", - " usage_periods = random.randint(1, 30)\n", - " for _ in range(usage_periods):\n", - " period_length_timedelta = timedelta(hours=random.randint(1, 24 * 7))\n", - " rate = random.uniform(0.1, 10)\n", - " user_usage_schedule.append((current_date, user, period_length_timedelta, rate))\n", - " current_date += period_length_timedelta # Increment current_date\n", - " if current_date > end_date:\n", - " break\n", - " return user_usage_schedule" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d6a0318", - "metadata": {}, - "outputs": [], - "source": [ - "# Define our fake users\n", - "fake = Faker()\n", - "users = [fake.user_name() for _ in range(100)]\n", - "\n", - "# Read the file and generate prompts\n", - "with open('/Users/shawn/Downloads/t8.shakespeare.txt', 'r') as f:\n", - " lines = f.read().split('\\n')\n", - "\n", - "# Define the time range\n", - "start_date = pd.to_datetime('2023-01-01', utc=True)\n", - "end_date = pd.to_datetime('2023-03-31', utc=True)\n", - "cost_change_date = pd.to_datetime('2023-02-15', utc=True)\n", - "\n", - "# Generate the schedules\n", - "version_schedule = generate_version_schedule(start_date, end_date)\n", - "latency_schedule = generate_latency_schedule(start_date, end_date)\n", - "cost_schedule = generate_cost_schedule(start_date, end_date, cost_change_date)\n", - "user_usage_schedule = generate_user_usage_schedule(start_date, end_date, users)\n", - "\n", - "# Helper function to generate a random completion\n", - "def generate_completion(prompt):\n", - " words = prompt.split()\n", - " completion = ' '.join(random.choices(words, k=int(len(words)* (random.random() + 0.1) * 10)))\n", - " return completion\n", - "\n", - "#latency_schedule" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a672c782", - "metadata": {}, - "outputs": [], - "source": [ - "import tqdm\n", - "data = []\n", - "for usage in tqdm.tqdm(user_usage_schedule):\n", - " usage_date, user, usage_period, rate = usage\n", - " \n", - " end_date = usage_date + usage_period\n", - " increment = timedelta(hours=rate)\n", - " \n", - " while usage_date < end_date:\n", - " # Find the version that was active during this usage\n", - " if usage_date.date() not in version_schedule:\n", - " break\n", - " active_versions = version_schedule[usage_date.date()]\n", - " #active_versions = [(version, percent) for date, version, percent in version_schedule if date.date() == usage_date.date()]\n", - " # Normalize the service percentages\n", - " total_percent = sum([percent for version, percent in active_versions])\n", - " if total_percent == 0:\n", - " continue\n", - " normalized_percentages = [percent / total_percent for version, percent in active_versions]\n", - "\n", - " version = np.random.choice([v for v, p in active_versions], p=normalized_percentages)\n", - " \n", - " # Find the cost during this usage\n", - " cost_per_token = cost_schedule[usage_date.date()]\n", - " \n", - " # Find the average latency during this usage\n", - " latency = latency_schedule[usage_date.date()]\n", - " latency *= (0.9 + random.random() *.2)\n", - " \n", - " prompt = ' '.join(random.sample(lines, 10)) # Increase prompt size\n", - " completion = generate_completion(prompt)\n", - " prompt_tokens = len(prompt.split())\n", - " completion_tokens = len(completion.split())\n", - " api_cost = (prompt_tokens + completion_tokens) * cost_per_token\n", - "\n", - " data.append([usage_date, user, version, prompt, completion, \n", - " prompt_tokens, completion_tokens, api_cost, latency])\n", - " \n", - " usage_date += increment\n", - "\n", - "df = pd.DataFrame(data, columns=['timestamp', 'username', 'model_version', 'prompt', 'completion', \n", - " 'prompt_tokens', 'completion_tokens', 'api_cost', 'latency'])\n", - "\n", - "predictions = weave.save(dataframe_to_arrow(df), 'predictions')\n", - "predictions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "57fc86e0", - "metadata": {}, - "outputs": [], - "source": [ - "predictions = weave.legacy.ops.get(\"local-artifact:///predictions:latest/obj\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8049b6dd", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " 'all_preds': predictions,\n", - " 'x_max': weave.legacy.ops.date_parse(str(end_date)),\n", - " 'x_min': lambda x_max: x_max - weave.legacy.ops.days(5),\n", - " 'predictions': lambda x_min: predictions.filter(lambda pred: pred['timestamp'] > x_min)\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " label=lambda item: item[\"username\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=0, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"api_cost\"].sum(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"api_cost\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"prompt_tokens\"].sum() + preds['completion_tokens'].sum(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"total_tokens\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=8, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"latency\"].avg(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"avg_latency\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=16, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['api_cost'],\n", - " bin_size=0.2\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['prompt_tokens'] + pred['completion_tokens'],\n", - " bin_size=25\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=8, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['latency'],\n", - " bin_size=0.05\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=16, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions: predictions,\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=18, w=24, h=12)\n", - " )\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Monitor3.ipynb b/weave/legacy/examples/experimental/skip_test/Monitor3.ipynb index 36c7bd69d62..f6634410915 100644 --- a/weave/legacy/examples/experimental/skip_test/Monitor3.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Monitor3.ipynb @@ -1,320 +1,320 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "039f298a", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ops_arrow.list_ import dataframe_to_arrow\n", - "from weave.legacy.ecosystem import wandb" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "039f298a", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ops_arrow.list_ import dataframe_to_arrow\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e0d6245", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import random\n", + "from faker import Faker\n", + "from datetime import timedelta, time\n", + "\n", + "# Generate the version schedule\n", + "def generate_version_schedule(start_date, end_date):\n", + " current_date = start_date\n", + " versions = ['1.0']\n", + " version_schedule = {}\n", + " while current_date <= end_date:\n", + " date_versions = []\n", + " for version in versions:\n", + " service_percent = random.uniform(0, 1)\n", + " date_versions.append((version, service_percent))\n", + "\n", + " version_schedule[current_date.date()] = date_versions\n", + " current_date += timedelta(days=1)\n", + " if random.random() < 0.10: # 5% chance to introduce a new version each day\n", + " new_version = f'{float(versions[-1])+0.1:.1f}'\n", + " versions.append(new_version)\n", + " if len(versions) > 1 and random.random() < 0.10:\n", + " versions.pop(0)\n", + " return version_schedule\n", + "\n", + "# Generate the latency schedule\n", + "def generate_latency_schedule(start_date, end_date):\n", + " latency_schedule = {}\n", + " for current_date in pd.date_range(start_date, end_date):\n", + " base_latency = random.uniform(0.1, 1)\n", + " day_factor = random.uniform(0.5, 1.5)\n", + " month_factor = random.uniform(0.5, 1.5)\n", + " latency = base_latency * day_factor * month_factor\n", + " latency_schedule[current_date.date()] = latency\n", + " return latency_schedule\n", + "\n", + "# Generate the cost schedule\n", + "def generate_cost_schedule(start_date, end_date, cost_change_date):\n", + " cost_schedule = {}\n", + " current_date = start_date\n", + " cost_per_token = 0.01\n", + " while current_date <= end_date:\n", + " if current_date >= cost_change_date:\n", + " cost_per_token = 0.005\n", + " cost_schedule[current_date.date()] = cost_per_token\n", + " current_date += timedelta(days=1)\n", + " return cost_schedule\n", + "\n", + "def generate_user_usage_schedule(start_date, end_date, users):\n", + " user_usage_schedule = []\n", + " for user in users:\n", + " current_date = start_date + timedelta(days=random.randrange(90))\n", + " while current_date <= end_date:\n", + " usage_periods = random.randint(1, 30)\n", + " for _ in range(usage_periods):\n", + " period_length_timedelta = timedelta(hours=random.randint(1, 24 * 7))\n", + " rate = random.uniform(0.1, 10)\n", + " user_usage_schedule.append((current_date, user, period_length_timedelta, rate))\n", + " current_date += period_length_timedelta # Increment current_date\n", + " if current_date > end_date:\n", + " break\n", + " return user_usage_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d6a0318", + "metadata": {}, + "outputs": [], + "source": [ + "# Define our fake users\n", + "fake = Faker()\n", + "users = [fake.user_name() for _ in range(100)]\n", + "\n", + "# Read the file and generate prompts\n", + "with open('/Users/shawn/Downloads/t8.shakespeare.txt', 'r') as f:\n", + " lines = f.read().split('\\n')\n", + "\n", + "# Define the time range\n", + "start_date = pd.to_datetime('2023-01-01', utc=True)\n", + "end_date = pd.to_datetime('2023-03-31', utc=True)\n", + "cost_change_date = pd.to_datetime('2023-02-15', utc=True)\n", + "\n", + "# Generate the schedules\n", + "version_schedule = generate_version_schedule(start_date, end_date)\n", + "latency_schedule = generate_latency_schedule(start_date, end_date)\n", + "cost_schedule = generate_cost_schedule(start_date, end_date, cost_change_date)\n", + "user_usage_schedule = generate_user_usage_schedule(start_date, end_date, users)\n", + "\n", + "# Helper function to generate a random completion\n", + "def generate_completion(prompt):\n", + " words = prompt.split()\n", + " completion = ' '.join(random.choices(words, k=int(len(words)* (random.random() + 0.1) * 10)))\n", + " return completion\n", + "\n", + "#latency_schedule" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a672c782", + "metadata": {}, + "outputs": [], + "source": [ + "import tqdm\n", + "data = []\n", + "for usage in tqdm.tqdm(user_usage_schedule):\n", + " usage_date, user, usage_period, rate = usage\n", + " \n", + " end_date = usage_date + usage_period\n", + " increment = timedelta(hours=rate)\n", + " \n", + " while usage_date < end_date:\n", + " # Find the version that was active during this usage\n", + " if usage_date.date() not in version_schedule:\n", + " break\n", + " active_versions = version_schedule[usage_date.date()]\n", + " #active_versions = [(version, percent) for date, version, percent in version_schedule if date.date() == usage_date.date()]\n", + " # Normalize the service percentages\n", + " total_percent = sum([percent for version, percent in active_versions])\n", + " if total_percent == 0:\n", + " continue\n", + " normalized_percentages = [percent / total_percent for version, percent in active_versions]\n", + "\n", + " version = np.random.choice([v for v, p in active_versions], p=normalized_percentages)\n", + " \n", + " # Find the cost during this usage\n", + " cost_per_token = cost_schedule[usage_date.date()]\n", + " \n", + " # Find the average latency during this usage\n", + " latency = latency_schedule[usage_date.date()]\n", + " latency *= (0.9 + random.random() *.2)\n", + " \n", + " prompt = ' '.join(random.sample(lines, 10)) # Increase prompt size\n", + " completion = generate_completion(prompt)\n", + " prompt_tokens = len(prompt.split())\n", + " completion_tokens = len(completion.split())\n", + " api_cost = (prompt_tokens + completion_tokens) * cost_per_token\n", + "\n", + " data.append([usage_date, user, version, prompt, completion, \n", + " prompt_tokens, completion_tokens, api_cost, latency])\n", + " \n", + " usage_date += increment\n", + "\n", + "df = pd.DataFrame(data, columns=['timestamp', 'username', 'model_version', 'prompt', 'completion', \n", + " 'prompt_tokens', 'completion_tokens', 'api_cost', 'latency'])\n", + "\n", + "predictions = weave.save(dataframe_to_arrow(df), 'predictions')\n", + "predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57fc86e0", + "metadata": {}, + "outputs": [], + "source": [ + "predictions = weave.legacy.weave.ops.get(\"local-artifact:///predictions:latest/obj\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8049b6dd", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " #'all_preds': predictions,\n", + " 'x_max': weave.legacy.weave.ops.date_parse(str(end_date)),\n", + " 'x_min': lambda x_max: x_max - weave.legacy.weave.ops.days(5),\n", + " 'predictions_query': weave.legacy.weave.panels.Query(\n", + " predictions,\n", + " conditions=[\n", + " lambda query_input: weave.legacy.weave.panels.QueryCondition(\n", + " expression=query_input['username'],\n", + " editor=weave.legacy.weave.panels.SelectEditor(\n", + " choices=query_input['username']\n", + " )\n", + " )\n", + " ]),\n", + " 'predictions': lambda predictions_query: predictions_query.selected()\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " label=lambda item: item[\"username\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=0, w=12, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"api_cost\"].sum(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"api_cost\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"prompt_tokens\"].sum() + preds['completion_tokens'].sum(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"total_tokens\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=8, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", + " predictions,\n", + " x=lambda item: item[\"timestamp\"],\n", + " #label=lambda item: item[\"model_version\"],\n", + " agg=lambda preds: preds[\"latency\"].avg(),\n", + " min_x=x_min,\n", + " max_x=x_max,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"avg_latency\"},\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=16, y=6, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['api_cost'],\n", + " bin_size=0.2\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['prompt_tokens'] + pred['completion_tokens'],\n", + " bin_size=25\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=8, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, x_min, x_max: wandb.Distribution(\n", + " predictions,\n", + " value_fn=lambda pred: pred['latency'],\n", + " bin_size=0.05\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=16, y=12, w=8, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions: predictions,\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=18, w=24, h=12)\n", + " )\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e0d6245", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import random\n", - "from faker import Faker\n", - "from datetime import timedelta, time\n", - "\n", - "# Generate the version schedule\n", - "def generate_version_schedule(start_date, end_date):\n", - " current_date = start_date\n", - " versions = ['1.0']\n", - " version_schedule = {}\n", - " while current_date <= end_date:\n", - " date_versions = []\n", - " for version in versions:\n", - " service_percent = random.uniform(0, 1)\n", - " date_versions.append((version, service_percent))\n", - "\n", - " version_schedule[current_date.date()] = date_versions\n", - " current_date += timedelta(days=1)\n", - " if random.random() < 0.10: # 5% chance to introduce a new version each day\n", - " new_version = f'{float(versions[-1])+0.1:.1f}'\n", - " versions.append(new_version)\n", - " if len(versions) > 1 and random.random() < 0.10:\n", - " versions.pop(0)\n", - " return version_schedule\n", - "\n", - "# Generate the latency schedule\n", - "def generate_latency_schedule(start_date, end_date):\n", - " latency_schedule = {}\n", - " for current_date in pd.date_range(start_date, end_date):\n", - " base_latency = random.uniform(0.1, 1)\n", - " day_factor = random.uniform(0.5, 1.5)\n", - " month_factor = random.uniform(0.5, 1.5)\n", - " latency = base_latency * day_factor * month_factor\n", - " latency_schedule[current_date.date()] = latency\n", - " return latency_schedule\n", - "\n", - "# Generate the cost schedule\n", - "def generate_cost_schedule(start_date, end_date, cost_change_date):\n", - " cost_schedule = {}\n", - " current_date = start_date\n", - " cost_per_token = 0.01\n", - " while current_date <= end_date:\n", - " if current_date >= cost_change_date:\n", - " cost_per_token = 0.005\n", - " cost_schedule[current_date.date()] = cost_per_token\n", - " current_date += timedelta(days=1)\n", - " return cost_schedule\n", - "\n", - "def generate_user_usage_schedule(start_date, end_date, users):\n", - " user_usage_schedule = []\n", - " for user in users:\n", - " current_date = start_date + timedelta(days=random.randrange(90))\n", - " while current_date <= end_date:\n", - " usage_periods = random.randint(1, 30)\n", - " for _ in range(usage_periods):\n", - " period_length_timedelta = timedelta(hours=random.randint(1, 24 * 7))\n", - " rate = random.uniform(0.1, 10)\n", - " user_usage_schedule.append((current_date, user, period_length_timedelta, rate))\n", - " current_date += period_length_timedelta # Increment current_date\n", - " if current_date > end_date:\n", - " break\n", - " return user_usage_schedule" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d6a0318", - "metadata": {}, - "outputs": [], - "source": [ - "# Define our fake users\n", - "fake = Faker()\n", - "users = [fake.user_name() for _ in range(100)]\n", - "\n", - "# Read the file and generate prompts\n", - "with open('/Users/shawn/Downloads/t8.shakespeare.txt', 'r') as f:\n", - " lines = f.read().split('\\n')\n", - "\n", - "# Define the time range\n", - "start_date = pd.to_datetime('2023-01-01', utc=True)\n", - "end_date = pd.to_datetime('2023-03-31', utc=True)\n", - "cost_change_date = pd.to_datetime('2023-02-15', utc=True)\n", - "\n", - "# Generate the schedules\n", - "version_schedule = generate_version_schedule(start_date, end_date)\n", - "latency_schedule = generate_latency_schedule(start_date, end_date)\n", - "cost_schedule = generate_cost_schedule(start_date, end_date, cost_change_date)\n", - "user_usage_schedule = generate_user_usage_schedule(start_date, end_date, users)\n", - "\n", - "# Helper function to generate a random completion\n", - "def generate_completion(prompt):\n", - " words = prompt.split()\n", - " completion = ' '.join(random.choices(words, k=int(len(words)* (random.random() + 0.1) * 10)))\n", - " return completion\n", - "\n", - "#latency_schedule" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a672c782", - "metadata": {}, - "outputs": [], - "source": [ - "import tqdm\n", - "data = []\n", - "for usage in tqdm.tqdm(user_usage_schedule):\n", - " usage_date, user, usage_period, rate = usage\n", - " \n", - " end_date = usage_date + usage_period\n", - " increment = timedelta(hours=rate)\n", - " \n", - " while usage_date < end_date:\n", - " # Find the version that was active during this usage\n", - " if usage_date.date() not in version_schedule:\n", - " break\n", - " active_versions = version_schedule[usage_date.date()]\n", - " #active_versions = [(version, percent) for date, version, percent in version_schedule if date.date() == usage_date.date()]\n", - " # Normalize the service percentages\n", - " total_percent = sum([percent for version, percent in active_versions])\n", - " if total_percent == 0:\n", - " continue\n", - " normalized_percentages = [percent / total_percent for version, percent in active_versions]\n", - "\n", - " version = np.random.choice([v for v, p in active_versions], p=normalized_percentages)\n", - " \n", - " # Find the cost during this usage\n", - " cost_per_token = cost_schedule[usage_date.date()]\n", - " \n", - " # Find the average latency during this usage\n", - " latency = latency_schedule[usage_date.date()]\n", - " latency *= (0.9 + random.random() *.2)\n", - " \n", - " prompt = ' '.join(random.sample(lines, 10)) # Increase prompt size\n", - " completion = generate_completion(prompt)\n", - " prompt_tokens = len(prompt.split())\n", - " completion_tokens = len(completion.split())\n", - " api_cost = (prompt_tokens + completion_tokens) * cost_per_token\n", - "\n", - " data.append([usage_date, user, version, prompt, completion, \n", - " prompt_tokens, completion_tokens, api_cost, latency])\n", - " \n", - " usage_date += increment\n", - "\n", - "df = pd.DataFrame(data, columns=['timestamp', 'username', 'model_version', 'prompt', 'completion', \n", - " 'prompt_tokens', 'completion_tokens', 'api_cost', 'latency'])\n", - "\n", - "predictions = weave.save(dataframe_to_arrow(df), 'predictions')\n", - "predictions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "57fc86e0", - "metadata": {}, - "outputs": [], - "source": [ - "predictions = weave.legacy.ops.get(\"local-artifact:///predictions:latest/obj\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8049b6dd", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " #'all_preds': predictions,\n", - " 'x_max': weave.legacy.ops.date_parse(str(end_date)),\n", - " 'x_min': lambda x_max: x_max - weave.legacy.ops.days(5),\n", - " 'predictions_query': weave.legacy.panels.Query(\n", - " predictions,\n", - " conditions=[\n", - " lambda query_input: weave.legacy.panels.QueryCondition(\n", - " expression=query_input['username'],\n", - " editor=weave.legacy.panels.SelectEditor(\n", - " choices=query_input['username']\n", - " )\n", - " )\n", - " ]),\n", - " 'predictions': lambda predictions_query: predictions_query.selected()\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " label=lambda item: item[\"username\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=0, w=12, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"api_cost\"].sum(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"api_cost\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"prompt_tokens\"].sum() + preds['completion_tokens'].sum(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"total_tokens\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=8, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.TimeSeries(\n", - " predictions,\n", - " x=lambda item: item[\"timestamp\"],\n", - " #label=lambda item: item[\"model_version\"],\n", - " agg=lambda preds: preds[\"latency\"].avg(),\n", - " min_x=x_min,\n", - " max_x=x_max,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"avg_latency\"},\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=16, y=6, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['api_cost'],\n", - " bin_size=0.2\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['prompt_tokens'] + pred['completion_tokens'],\n", - " bin_size=25\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=8, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions, x_min, x_max: wandb.Distribution(\n", - " predictions,\n", - " value_fn=lambda pred: pred['latency'],\n", - " bin_size=0.05\n", - " ),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=16, y=12, w=8, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda predictions: predictions,\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=18, w=24, h=12)\n", - " )\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Multiple Y Axes.ipynb b/weave/legacy/examples/experimental/skip_test/Multiple Y Axes.ipynb index ef49ac5d28e..a3d9af12a7c 100644 --- a/weave/legacy/examples/experimental/skip_test/Multiple Y Axes.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Multiple Y Axes.ipynb @@ -1,184 +1,184 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "0903a3ef", - "metadata": {}, - "source": [ - "## Using this notebook to develop multiple y axes on Panel Plot" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "0903a3ef", + "metadata": {}, + "source": [ + "## Using this notebook to develop multiple y axes on Panel Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6c524b9", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import math\n", + "import random\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8d46616", + "metadata": {}, + "outputs": [], + "source": [ + "# PanelPlot supports multiple Y axes. Create a table with one row per series, with lists of x and y values.\n", + "data = []\n", + "for run in ['a1', 'a2', 'b1', 'b2', 'b3', 'c1']:\n", + " for x in range(100):\n", + " data.append({\n", + " 'group': run[0],\n", + " 'run': run,\n", + " 'step': x,\n", + " 'acc': random.random(),\n", + " })\n", + " \n", + "plot = weave.legacy.weave.panels.Plot(data)\n", + "plot.set_x(lambda row: row['step'])\n", + "plot.set_y(lambda row: row['acc'])\n", + "plot.set_label(lambda row: row['run'])\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d97c0fd", + "metadata": {}, + "outputs": [], + "source": [ + "# PanelPlot supports multiple Y axes. Create a table with one row per series, with lists of x and y values.\n", + "data = []\n", + "xs = [i / 1000.0 for i in range(0, 1000)]\n", + "for series in range(3):\n", + " data.append({\n", + " 'key': 'y%s' % series,\n", + " 'x': xs,\n", + " 'y': [math.sin(10 * x / (series+1)) for x in xs]\n", + " })\n", + "plot = weave.legacy.weave.panels.Plot(data)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['key'])\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00fdefb5", + "metadata": {}, + "outputs": [], + "source": [ + "# Or you can unnest the data yourself (one row per point)\n", + "data = []\n", + "xs = [i / 1000.0 for i in range(0, 1000)]\n", + "for series in range(3):\n", + " for x in xs:\n", + " data.append({\n", + " 'key': 'y%s' % series,\n", + " 'x': x,\n", + " 'y': math.sin(10 * x / (series+1))\n", + " })\n", + "plot = weave.legacy.weave.panels.Plot(data)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['key'])\n", + "plot.set_mark('line')\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f9b0741", + "metadata": {}, + "outputs": [], + "source": [ + "# If your series are column-oriented, you can use df.melt to orient the data to work with PanelPlot\n", + "\n", + "import pandas as pd\n", + "df = pd.DataFrame()\n", + "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", + "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", + "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", + "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", + "\n", + "# df.melt converts to a table with three columns: x, series, y\n", + "df = df.melt(id_vars=['x'], value_vars=['y1', 'y2', 'y3'], var_name='series', value_name='y')\n", + "\n", + "multiple_y = df.to_dict('records')\n", + "plot = weave.legacy.weave.panels.Plot(multiple_y)\n", + "plot.set_x(lambda row: row['x'])\n", + "plot.set_y(lambda row: row['y'])\n", + "plot.set_label(lambda row: row['series'])\n", + "plot.set_mark('line')\n", + "weave.show(plot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07752656", + "metadata": {}, + "outputs": [], + "source": [ + "# Or, leave the table in columnar form, and return arrays of the values you want for each series for PanelPlot\n", + "# x and label dimensions.\n", + "\n", + "# This relies on changes in this branch.\n", + "\n", + "# Note ExpressionEditor doesn't render list literals so it looks weird in the panel config. And we'll probably want\n", + "# to make the config editor more helpful, so that you choose labels along-side each y axis choice, or so that\n", + "# if you return a dict for Y we use the keys as the labels.\n", + "\n", + "import pandas as pd\n", + "df = pd.DataFrame()\n", + "df['x'] = [i / 100.0 for i in range(0, 100)]\n", + "df['x1'] = [i / 100.0 for i in range(0, 100)]\n", + "df['x2'] = [2 * i / 100.0 for i in range(0, 100)]\n", + "df['x3'] = [3 * i / 100.0 for i in range(0, 100)]\n", + "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", + "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", + "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", + "multiple_y = df.to_dict('records')\n", + "\n", + "plot = weave.legacy.weave.panels.Plot(multiple_y)\n", + "plot.set_x(lambda row: row['x'])\n", + "\n", + "# Return lists for y and label\n", + "plot.set_y(lambda row: [row['y1'], row['y2'], row['y3']])\n", + "plot.set_label(lambda row: ['y1', 'y2', 'y3'])\n", + "plot.set_mark('line')\n", + "weave.show(plot)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6c524b9", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import math\n", - "import random\n", - "weave.use_frontend_devmode()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8d46616", - "metadata": {}, - "outputs": [], - "source": [ - "# PanelPlot supports multiple Y axes. Create a table with one row per series, with lists of x and y values.\n", - "data = []\n", - "for run in ['a1', 'a2', 'b1', 'b2', 'b3', 'c1']:\n", - " for x in range(100):\n", - " data.append({\n", - " 'group': run[0],\n", - " 'run': run,\n", - " 'step': x,\n", - " 'acc': random.random(),\n", - " })\n", - " \n", - "plot = weave.legacy.panels.Plot(data)\n", - "plot.set_x(lambda row: row['step'])\n", - "plot.set_y(lambda row: row['acc'])\n", - "plot.set_label(lambda row: row['run'])\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d97c0fd", - "metadata": {}, - "outputs": [], - "source": [ - "# PanelPlot supports multiple Y axes. Create a table with one row per series, with lists of x and y values.\n", - "data = []\n", - "xs = [i / 1000.0 for i in range(0, 1000)]\n", - "for series in range(3):\n", - " data.append({\n", - " 'key': 'y%s' % series,\n", - " 'x': xs,\n", - " 'y': [math.sin(10 * x / (series+1)) for x in xs]\n", - " })\n", - "plot = weave.legacy.panels.Plot(data)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['key'])\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00fdefb5", - "metadata": {}, - "outputs": [], - "source": [ - "# Or you can unnest the data yourself (one row per point)\n", - "data = []\n", - "xs = [i / 1000.0 for i in range(0, 1000)]\n", - "for series in range(3):\n", - " for x in xs:\n", - " data.append({\n", - " 'key': 'y%s' % series,\n", - " 'x': x,\n", - " 'y': math.sin(10 * x / (series+1))\n", - " })\n", - "plot = weave.legacy.panels.Plot(data)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['key'])\n", - "plot.set_mark('line')\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f9b0741", - "metadata": {}, - "outputs": [], - "source": [ - "# If your series are column-oriented, you can use df.melt to orient the data to work with PanelPlot\n", - "\n", - "import pandas as pd\n", - "df = pd.DataFrame()\n", - "df['x'] = [i / 1000.0 for i in range(0, 1000)]\n", - "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", - "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", - "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", - "\n", - "# df.melt converts to a table with three columns: x, series, y\n", - "df = df.melt(id_vars=['x'], value_vars=['y1', 'y2', 'y3'], var_name='series', value_name='y')\n", - "\n", - "multiple_y = df.to_dict('records')\n", - "plot = weave.legacy.panels.Plot(multiple_y)\n", - "plot.set_x(lambda row: row['x'])\n", - "plot.set_y(lambda row: row['y'])\n", - "plot.set_label(lambda row: row['series'])\n", - "plot.set_mark('line')\n", - "weave.show(plot)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07752656", - "metadata": {}, - "outputs": [], - "source": [ - "# Or, leave the table in columnar form, and return arrays of the values you want for each series for PanelPlot\n", - "# x and label dimensions.\n", - "\n", - "# This relies on changes in this branch.\n", - "\n", - "# Note ExpressionEditor doesn't render list literals so it looks weird in the panel config. And we'll probably want\n", - "# to make the config editor more helpful, so that you choose labels along-side each y axis choice, or so that\n", - "# if you return a dict for Y we use the keys as the labels.\n", - "\n", - "import pandas as pd\n", - "df = pd.DataFrame()\n", - "df['x'] = [i / 100.0 for i in range(0, 100)]\n", - "df['x1'] = [i / 100.0 for i in range(0, 100)]\n", - "df['x2'] = [2 * i / 100.0 for i in range(0, 100)]\n", - "df['x3'] = [3 * i / 100.0 for i in range(0, 100)]\n", - "df['y1'] = df['x'].map(lambda x: math.sin(10 * x / 1))\n", - "df['y2'] = df['x'].map(lambda x: math.sin(10 * x / 2))\n", - "df['y3'] = df['x'].map(lambda x: math.sin(10 * x / 3))\n", - "multiple_y = df.to_dict('records')\n", - "\n", - "plot = weave.legacy.panels.Plot(multiple_y)\n", - "plot.set_x(lambda row: row['x'])\n", - "\n", - "# Return lists for y and label\n", - "plot.set_y(lambda row: [row['y1'], row['y2'], row['y3']])\n", - "plot.set_label(lambda row: ['y1', 'y2', 'y3'])\n", - "plot.set_mark('line')\n", - "weave.show(plot)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Mutation - Code Editor.ipynb b/weave/legacy/examples/experimental/skip_test/Mutation - Code Editor.ipynb index cd57106914e..937f2c0a27c 100644 --- a/weave/legacy/examples/experimental/skip_test/Mutation - Code Editor.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Mutation - Code Editor.ipynb @@ -1,55 +1,55 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "b856b674", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "weave.capture_weave_server_logs()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "b856b674", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "weave.capture_weave_server_logs()" + ] + }, + { + "cell_type": "markdown", + "id": "e056ac84", + "metadata": {}, + "source": [ + "Run the cell below, then click the gear icon to change to TextEditor. Your changes will be reflected in train.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c71000cc", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.local_path('../weave/testdata/train.py')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "markdown", - "id": "e056ac84", - "metadata": {}, - "source": [ - "Run the cell below, then click the gear icon to change to TextEditor. Your changes will be reflected in train.py" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c71000cc", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.local_path('../weave/testdata/train.py')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Mutation.ipynb b/weave/legacy/examples/experimental/skip_test/Mutation.ipynb index a6c5bbf6796..454d25c6e5f 100644 --- a/weave/legacy/examples/experimental/skip_test/Mutation.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Mutation.ipynb @@ -1,54 +1,54 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "b856b674", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.capture_weave_server_logs()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "b856b674", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.capture_weave_server_logs()" + ] + }, + { + "cell_type": "markdown", + "id": "e056ac84", + "metadata": {}, + "source": [ + "Change the \"name\" column in the panel below to StringEditor, then click a cell to edit the underlying csv." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c71000cc", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.local_path('../weave/testdata/cereal.csv').readcsv" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "markdown", - "id": "e056ac84", - "metadata": {}, - "source": [ - "Change the \"name\" column in the panel below to StringEditor, then click a cell to edit the underlying csv." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c71000cc", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.local_path('../weave/testdata/cereal.csv').readcsv" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/OpenAI.ipynb b/weave/legacy/examples/experimental/skip_test/OpenAI.ipynb index 76e86685c1e..dae4d5b5262 100644 --- a/weave/legacy/examples/experimental/skip_test/OpenAI.ipynb +++ b/weave/legacy/examples/experimental/skip_test/OpenAI.ipynb @@ -1,106 +1,106 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "94f2344d", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import openai" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "94f2344d", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import openai" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1abed8ce", + "metadata": {}, + "outputs": [], + "source": [ + "# Construct a dataset, we'll see if GPT-3 can do simple arithmetic\n", + "data = []\n", + "for i in range (101):\n", + " a = i\n", + " b = i % 9\n", + " r = a + b\n", + " data.append({'id': i, 'prompt': '%s + %s =' % (a, b), 'completion': ' %s' % r})\n", + "data = weave.save(data, name='openai-dataset')\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "348d63f0", + "metadata": {}, + "outputs": [], + "source": [ + "# Fine tune gpt-3 on our dataset.\n", + "\n", + "# Change this to \"openai.finetune_gpt3\" to run a real OpenAI fine-tune job\n", + "fine_tune = openai.finetune_gpt3_demo(data, {'n_epochs' : 2})\n", + "fine_tune" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe3d0872", + "metadata": {}, + "outputs": [], + "source": [ + "model = fine_tune.model()\n", + "model = weave.save(model, name='openai-model')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d656075", + "metadata": {}, + "outputs": [], + "source": [ + "# Show all the models we've trained, and \n", + "for version in weave.versions(model):\n", + " print(weave.expr(version))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89bebbff", + "metadata": {}, + "outputs": [], + "source": [ + "# make some predictions using our new model\n", + "\n", + "panel = weave.legacy.weave.panels.Table(['1 + 9 =', '2 + 14 ='])\n", + "panel.table_query.add_column(lambda row: row)\n", + "panel.table_query.add_column(lambda row: model.complete(row)['choices'][0]['text'])\n", + "weave.show(panel)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "1abed8ce", - "metadata": {}, - "outputs": [], - "source": [ - "# Construct a dataset, we'll see if GPT-3 can do simple arithmetic\n", - "data = []\n", - "for i in range (101):\n", - " a = i\n", - " b = i % 9\n", - " r = a + b\n", - " data.append({'id': i, 'prompt': '%s + %s =' % (a, b), 'completion': ' %s' % r})\n", - "data = weave.save(data, name='openai-dataset')\n", - "data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "348d63f0", - "metadata": {}, - "outputs": [], - "source": [ - "# Fine tune gpt-3 on our dataset.\n", - "\n", - "# Change this to \"openai.finetune_gpt3\" to run a real OpenAI fine-tune job\n", - "fine_tune = openai.finetune_gpt3_demo(data, {'n_epochs' : 2})\n", - "fine_tune" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fe3d0872", - "metadata": {}, - "outputs": [], - "source": [ - "model = fine_tune.model()\n", - "model = weave.save(model, name='openai-model')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7d656075", - "metadata": {}, - "outputs": [], - "source": [ - "# Show all the models we've trained, and \n", - "for version in weave.versions(model):\n", - " print(weave.expr(version))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "89bebbff", - "metadata": {}, - "outputs": [], - "source": [ - "# make some predictions using our new model\n", - "\n", - "panel = weave.legacy.panels.Table(['1 + 9 =', '2 + 14 ='])\n", - "panel.table_query.add_column(lambda row: row)\n", - "panel.table_query.add_column(lambda row: model.complete(row)['choices'][0]['text'])\n", - "weave.show(panel)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Oxford-IIIT Pet Dataset.ipynb b/weave/legacy/examples/experimental/skip_test/Oxford-IIIT Pet Dataset.ipynb index 4a0836dccac..5b84ccbda13 100644 --- a/weave/legacy/examples/experimental/skip_test/Oxford-IIIT Pet Dataset.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Oxford-IIIT Pet Dataset.ipynb @@ -1,115 +1,115 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2cfb0934", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2cfb0934", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b81fda7c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "im_names = os.listdir('/Users/shawn/datasets/oxford-iiit-pet-dataset/images')\n", + "breeds = set(i.rsplit('_', 1)[0] for i in im_names)\n", + "#sorted(breeds)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e3925df", + "metadata": {}, + "outputs": [], + "source": [ + "import typing\n", + "type(typing.Optional[int])\n", + "type(typing._UnionGenericAlias)\n", + "# isinstance(typing.Optional, typing._SpecialForm)\n", + "# typing._SpecialForm.__class__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d20c10d", + "metadata": {}, + "outputs": [], + "source": [ + "#weave.legacy.weave.ops.local_path('/Users/shawn/datasets')\n", + "# :(, broken right now" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f668d48", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.ecosystem.shawn import petdataset\n", + "\n", + "data = petdataset.petdataset('/Users/shawn/datasets/oxford-iiit-pet-dataset')\n", + "#data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "981fc71f", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.ecosystem import wandb\n", + "wandb.MultiDistribution(data, value_fn=lambda item: item['breed'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b123d4f9", + "metadata": {}, + "outputs": [], + "source": [ + "# A PanelPlot only histogram\n", + "plot = weave.legacy.weave.panels.Plot(\n", + " data,\n", + " y=lambda item: item['breed'],\n", + " groupby_y=True,\n", + " x=lambda group: group.count(),\n", + " mark='bar')\n", + "#plot" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "b81fda7c", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "im_names = os.listdir('/Users/shawn/datasets/oxford-iiit-pet-dataset/images')\n", - "breeds = set(i.rsplit('_', 1)[0] for i in im_names)\n", - "#sorted(breeds)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e3925df", - "metadata": {}, - "outputs": [], - "source": [ - "import typing\n", - "type(typing.Optional[int])\n", - "type(typing._UnionGenericAlias)\n", - "# isinstance(typing.Optional, typing._SpecialForm)\n", - "# typing._SpecialForm.__class__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d20c10d", - "metadata": {}, - "outputs": [], - "source": [ - "#weave.legacy.ops.local_path('/Users/shawn/datasets')\n", - "# :(, broken right now" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f668d48", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.ecosystem.shawn import petdataset\n", - "\n", - "data = petdataset.petdataset('/Users/shawn/datasets/oxford-iiit-pet-dataset')\n", - "#data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "981fc71f", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.ecosystem import wandb\n", - "wandb.MultiDistribution(data, value_fn=lambda item: item['breed'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b123d4f9", - "metadata": {}, - "outputs": [], - "source": [ - "# A PanelPlot only histogram\n", - "plot = weave.legacy.panels.Plot(\n", - " data,\n", - " y=lambda item: item['breed'],\n", - " groupby_y=True,\n", - " x=lambda group: group.count(),\n", - " mark='bar')\n", - "#plot" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Performance profiling.ipynb b/weave/legacy/examples/experimental/skip_test/Performance profiling.ipynb index d128c91d462..c69c3f119d6 100644 --- a/weave/legacy/examples/experimental/skip_test/Performance profiling.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Performance profiling.ipynb @@ -1,153 +1,153 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c87095b6", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import numpy as np\n", - "import pyarrow as pa\n", - "import pyarrow.parquet as pq\n", - "\n", - "!rm -rf /tmp/local-artifacts\n", - "\n", - "t = pa.table({d: list(range(400)) * 1000 for d in \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"})\n", - "weave_t = weave.legacy.ops.ArrowWeaveList(t)\n", - "weave.use(weave_t.groupby(lambda x: x['A']).map(lambda row: row.key()))" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c87095b6", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import numpy as np\n", + "import pyarrow as pa\n", + "import pyarrow.parquet as pq\n", + "\n", + "!rm -rf /tmp/local-artifacts\n", + "\n", + "t = pa.table({d: list(range(400)) * 1000 for d in \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"})\n", + "weave_t = weave.legacy.weave.ops.ArrowWeaveList(t)\n", + "weave.use(weave_t.groupby(lambda x: x['A']).map(lambda row: row.key()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a7726e4", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import cProfile\n", + "import weave\n", + "from weave.legacy.weave import storage\n", + "import random\n", + "import time\n", + "import pyarrow as pa\n", + "from PIL import Image\n", + "\n", + "def create_arrow_data(n_rows):\n", + " inner_count = int(n_rows / 25)\n", + " rotates = []\n", + " shears = []\n", + " x = []\n", + " y = []\n", + " random.seed(0)\n", + " for rotate in range(5):\n", + " for shear in range(5):\n", + " for i in range(inner_count):\n", + " rotates.append(rotate)\n", + " shears.append(shear)\n", + " x.append(random.choice(['a', 'b', 'c']))\n", + " y.append(random.randrange(10))\n", + " table = pa.table({\n", + " 'rotate': rotates,\n", + " 'shear': shears,\n", + " 'x': x,\n", + " 'y': y,\n", + " })\n", + " table_list = weave.legacy.weave.ops.ArrowTableList(table)\n", + "\n", + " return storage.save(table_list)\n", + "\n", + "def create_data(n_rows):\n", + " inner_count = int(n_rows / 25)\n", + " ims = []\n", + " base_im = Image.linear_gradient(\"L\")\n", + " random.seed(0)\n", + " for rotate in range(5):\n", + " for shear in range(5):\n", + " for i in range(inner_count):\n", + " ims.append(\n", + " {\n", + " \"rotate\": rotate,\n", + " \"shear\": shear,\n", + " \"y\": random.choice([\"a\", \"b\", \"c\"]),\n", + " \"x\": random.randrange(10),\n", + "# \"image\": base_im.rotate(rotate * 4).transform(\n", + "# (256, 256),\n", + "# Image.AFFINE,\n", + "# (1, shear / 10, 0, 0, 1, 0),\n", + "# Image.BICUBIC,\n", + "# ),\n", + " }\n", + " )\n", + "\n", + " return storage.save(ims)\n", + "\n", + "\n", + "def run(ref):\n", + "# node = (\n", + "# weave.get(ref)\n", + "# .groupby(lambda row: weave.legacy.weave.ops.dict_(rotate=row[\"rotate\"], shear=row[\"shear\"]))\n", + "# .map(lambda row: row.groupby(lambda row: row[\"y\"]))\n", + "# .dropna()\n", + "# .count()\n", + "# )\n", + " node = (\n", + " weave.get(ref)\n", + " # .map(lambda row: row['rotate'] + 1)\n", + " # .map(lambda row: row + 9)\n", + " .groupby(lambda row: weave.legacy.weave.ops.dict_(rotate=row[\"rotate\"], shear=row[\"shear\"]))\n", + " [1]\n", + " .count()\n", + " )\n", + " result = weave.use(node)\n", + " print('Run result: %s' % result)\n", + "\n", + "\n", + "os.system(\"rm -rf /tmp/local-artifacts/*\")\n", + "ref = create_data(100000)\n", + "print(\"Data ref: \", ref)\n", + "start_time = time.time()\n", + "cProfile.run('run(\"%s\")' % ref, \"profile_stats\")\n", + "print('Run time: %ss' % (time.time() - start_time))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44f3f7ba", + "metadata": {}, + "outputs": [], + "source": [ + "import pstats\n", + "from pstats import SortKey\n", + "p = pstats.Stats('profile_stats')\n", + "#p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_stats()\n", + "p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_callers('type_of')\n", + "#p.strip_dirs().sort_stats(SortKey.TIME).print_stats()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a7726e4", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import cProfile\n", - "import weave\n", - "from weave.legacy import storage\n", - "import random\n", - "import time\n", - "import pyarrow as pa\n", - "from PIL import Image\n", - "\n", - "def create_arrow_data(n_rows):\n", - " inner_count = int(n_rows / 25)\n", - " rotates = []\n", - " shears = []\n", - " x = []\n", - " y = []\n", - " random.seed(0)\n", - " for rotate in range(5):\n", - " for shear in range(5):\n", - " for i in range(inner_count):\n", - " rotates.append(rotate)\n", - " shears.append(shear)\n", - " x.append(random.choice(['a', 'b', 'c']))\n", - " y.append(random.randrange(10))\n", - " table = pa.table({\n", - " 'rotate': rotates,\n", - " 'shear': shears,\n", - " 'x': x,\n", - " 'y': y,\n", - " })\n", - " table_list = weave.legacy.ops.ArrowTableList(table)\n", - "\n", - " return storage.save(table_list)\n", - "\n", - "def create_data(n_rows):\n", - " inner_count = int(n_rows / 25)\n", - " ims = []\n", - " base_im = Image.linear_gradient(\"L\")\n", - " random.seed(0)\n", - " for rotate in range(5):\n", - " for shear in range(5):\n", - " for i in range(inner_count):\n", - " ims.append(\n", - " {\n", - " \"rotate\": rotate,\n", - " \"shear\": shear,\n", - " \"y\": random.choice([\"a\", \"b\", \"c\"]),\n", - " \"x\": random.randrange(10),\n", - "# \"image\": base_im.rotate(rotate * 4).transform(\n", - "# (256, 256),\n", - "# Image.AFFINE,\n", - "# (1, shear / 10, 0, 0, 1, 0),\n", - "# Image.BICUBIC,\n", - "# ),\n", - " }\n", - " )\n", - "\n", - " return storage.save(ims)\n", - "\n", - "\n", - "def run(ref):\n", - "# node = (\n", - "# weave.get(ref)\n", - "# .groupby(lambda row: weave.legacy.ops.dict_(rotate=row[\"rotate\"], shear=row[\"shear\"]))\n", - "# .map(lambda row: row.groupby(lambda row: row[\"y\"]))\n", - "# .dropna()\n", - "# .count()\n", - "# )\n", - " node = (\n", - " weave.get(ref)\n", - " # .map(lambda row: row['rotate'] + 1)\n", - " # .map(lambda row: row + 9)\n", - " .groupby(lambda row: weave.legacy.ops.dict_(rotate=row[\"rotate\"], shear=row[\"shear\"]))\n", - " [1]\n", - " .count()\n", - " )\n", - " result = weave.use(node)\n", - " print('Run result: %s' % result)\n", - "\n", - "\n", - "os.system(\"rm -rf /tmp/local-artifacts/*\")\n", - "ref = create_data(100000)\n", - "print(\"Data ref: \", ref)\n", - "start_time = time.time()\n", - "cProfile.run('run(\"%s\")' % ref, \"profile_stats\")\n", - "print('Run time: %ss' % (time.time() - start_time))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "44f3f7ba", - "metadata": {}, - "outputs": [], - "source": [ - "import pstats\n", - "from pstats import SortKey\n", - "p = pstats.Stats('profile_stats')\n", - "#p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_stats()\n", - "p.strip_dirs().sort_stats(SortKey.CUMULATIVE).print_callers('type_of')\n", - "#p.strip_dirs().sort_stats(SortKey.TIME).print_stats()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/ProdMonStreamTableIntro.ipynb b/weave/legacy/examples/experimental/skip_test/ProdMonStreamTableIntro.ipynb index 878d9804b6e..f8233308156 100644 --- a/weave/legacy/examples/experimental/skip_test/ProdMonStreamTableIntro.ipynb +++ b/weave/legacy/examples/experimental/skip_test/ProdMonStreamTableIntro.ipynb @@ -1,135 +1,135 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "9617c234", - "metadata": {}, - "source": [ - "# StreamTable API" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "9617c234", + "metadata": {}, + "source": [ + "# StreamTable API" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "307ad3b7", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.monitoring import StreamTable\n", + "import PIL\n", + "import numpy as np\n", + "\n", + "def make_image():\n", + " imarray = np.random.rand(100, 100, 3) * 255\n", + " return PIL.Image.fromarray(imarray.astype(\"uint8\")).convert(\"RGBA\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c7b6c63", + "metadata": {}, + "outputs": [], + "source": [ + "table = StreamTable(\"pm_demo_all_2\", entity_name=\"timssweeney\", project_name=\"prodmon\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643f46e3", + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(10):\n", + " table.log({\n", + " 'number': 1, \n", + " 'string': 'hello', \n", + " 'bool': True, \n", + " 'nested': {'data': 5}, \n", + " 'lists': list(range(10)), \n", + " 'custom': make_image()})\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "56c50afb", + "metadata": {}, + "source": [ + "# Monitor Decorator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7db9358", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents import load_tools\n", + "from langchain.agents import initialize_agent\n", + "from langchain.agents import AgentType\n", + "from langchain.llms import OpenAI\n", + "\n", + "llm = OpenAI(temperature=0)\n", + "tools = load_tools([\"llm-math\"], llm=llm)\n", + "agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90aba081", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.monitoring import monitor\n", + "\n", + "@monitor(entity_name=\"timssweeney\", project_name=\"prodmon\")\n", + "def ask_llm_calculator(prompt, question):\n", + " return agent.run(prompt + \" \" + question)\n", + "\n", + "\n", + "ask_llm_calculator(\n", + " \"Please accurately answer the following question:\", \n", + " \"Find the square root of 5.4.\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f49b9b31", + "metadata": {}, + "outputs": [], + "source": [ + "# This API will change\n", + "ask_llm_calculator._stream_table._stream_table.rows()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "307ad3b7", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.monitoring import StreamTable\n", - "import PIL\n", - "import numpy as np\n", - "\n", - "def make_image():\n", - " imarray = np.random.rand(100, 100, 3) * 255\n", - " return PIL.Image.fromarray(imarray.astype(\"uint8\")).convert(\"RGBA\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c7b6c63", - "metadata": {}, - "outputs": [], - "source": [ - "table = StreamTable(\"pm_demo_all_2\", entity_name=\"timssweeney\", project_name=\"prodmon\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "643f46e3", - "metadata": {}, - "outputs": [], - "source": [ - "for _ in range(10):\n", - " table.log({\n", - " 'number': 1, \n", - " 'string': 'hello', \n", - " 'bool': True, \n", - " 'nested': {'data': 5}, \n", - " 'lists': list(range(10)), \n", - " 'custom': make_image()})\n", - "table" - ] - }, - { - "cell_type": "markdown", - "id": "56c50afb", - "metadata": {}, - "source": [ - "# Monitor Decorator" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7db9358", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.agents import load_tools\n", - "from langchain.agents import initialize_agent\n", - "from langchain.agents import AgentType\n", - "from langchain.llms import OpenAI\n", - "\n", - "llm = OpenAI(temperature=0)\n", - "tools = load_tools([\"llm-math\"], llm=llm)\n", - "agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "90aba081", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.monitoring import monitor\n", - "\n", - "@monitor(entity_name=\"timssweeney\", project_name=\"prodmon\")\n", - "def ask_llm_calculator(prompt, question):\n", - " return agent.run(prompt + \" \" + question)\n", - "\n", - "\n", - "ask_llm_calculator(\n", - " \"Please accurately answer the following question:\", \n", - " \"Find the square root of 5.4.\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f49b9b31", - "metadata": {}, - "outputs": [], - "source": [ - "# This API will change\n", - "ask_llm_calculator._stream_table._stream_table.rows()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Shap.ipynb b/weave/legacy/examples/experimental/skip_test/Shap.ipynb index f0885b69e2d..5ca6196df6d 100644 --- a/weave/legacy/examples/experimental/skip_test/Shap.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Shap.ipynb @@ -1,60 +1,60 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "eb58c451", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import shap" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "eb58c451", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import shap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bac8abef", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = shap.ca_housing_dataset(1)\n", + "xy = shap.split_labels(dataset, 'MedHouseVal')\n", + "model = shap.xgboost_train(xy, {'learning_rate': 0.02})\n", + "\n", + "explain_res = model.shap_explain(xy['X'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa726585", + "metadata": {}, + "outputs": [], + "source": [ + "explain_res.summary_plot()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "bac8abef", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = shap.ca_housing_dataset(1)\n", - "xy = shap.split_labels(dataset, 'MedHouseVal')\n", - "model = shap.xgboost_train(xy, {'learning_rate': 0.02})\n", - "\n", - "explain_res = model.shap_explain(xy['X'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aa726585", - "metadata": {}, - "outputs": [], - "source": [ - "explain_res.summary_plot()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/StreamTables.ipynb b/weave/legacy/examples/experimental/skip_test/StreamTables.ipynb index e83bc3909b9..78e5331bec8 100644 --- a/weave/legacy/examples/experimental/skip_test/StreamTables.ipynb +++ b/weave/legacy/examples/experimental/skip_test/StreamTables.ipynb @@ -1,92 +1,92 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "1333d291", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1333d291", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17a80a08", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "058fa5af", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "entity_name = \"timssweeney\"\n", + "project_name = \"stream_tables\"\n", + "run_name = \"tt\" + str(int(time.time()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0691851", + "metadata": {}, + "outputs": [], + "source": [ + "st = StreamTable(run_name, project_name, entity_name)\n", + "\n", + "for i in range(10):\n", + " st.log({'a': i})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0a597a5", + "metadata": {}, + "outputs": [], + "source": [ + "st" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb42732f", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.project(entity_name, project_name).run(run_name).history2()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "17a80a08", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.wandb_interface.wandb_stream_table import StreamTable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "058fa5af", - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "entity_name = \"timssweeney\"\n", - "project_name = \"stream_tables\"\n", - "run_name = \"tt\" + str(int(time.time()))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f0691851", - "metadata": {}, - "outputs": [], - "source": [ - "st = StreamTable(run_name, project_name, entity_name)\n", - "\n", - "for i in range(10):\n", - " st.log({'a': i})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0a597a5", - "metadata": {}, - "outputs": [], - "source": [ - "st" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb42732f", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.project(entity_name, project_name).run(run_name).history2()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/TimeSeries.ipynb b/weave/legacy/examples/experimental/skip_test/TimeSeries.ipynb index 251def88910..73e03f7079a 100644 --- a/weave/legacy/examples/experimental/skip_test/TimeSeries.ipynb +++ b/weave/legacy/examples/experimental/skip_test/TimeSeries.ipynb @@ -1,148 +1,148 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e4885744", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy import ops_arrow\n", - "from weave.legacy.ecosystem import wandb" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e4885744", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave import ops_arrow\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50e46344", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "import random\n", + "random.seed(1)\n", + "\n", + "min_x = datetime.datetime(2023, 3, 22, 11, 0, 0)\n", + "max_x = min_x + datetime.timedelta(hours=1)\n", + "\n", + "users = ['nick', 'shawn', 'stacey', 'tim', 'danny']\n", + "model_versions = ['v1', 'v2']\n", + "\n", + "num_requests = 10000\n", + "\n", + "def random_datetime_in_range() -> datetime.datetime:\n", + " min_timestamp = min_x.timestamp()\n", + " max_timestamp = max_x.timestamp()\n", + " random_timestamp = random.uniform(min_timestamp, max_timestamp)\n", + " return datetime.datetime.fromtimestamp(random_timestamp)\n", + "\n", + "def random_suggestion(n: int) -> str:\n", + " result = ''\n", + " for i in range(n):\n", + " result += random.choice('abcdefghijklmnopqrstuvwxyz')\n", + " return result\n", + "\n", + "\n", + "items = weave.save(\n", + " ops_arrow.to_arrow([\n", + " {\n", + " \"pred\": random_suggestion(7),\n", + " \"time\": random_datetime_in_range(),\n", + " \"user\": random.choice(users),\n", + " \"version\": random.choice(model_versions),\n", + " \"accepted\": random.choice([1, 1, 1, 0, 0]),\n", + " } \n", + " for _ in range(num_requests)\n", + " ])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb863d87", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"num_preds\": weave.legacy.weave.panels.LabeledItem(\n", + " label=\"Predictions by version over time\",\n", + " item=wandb.TimeSeries(\n", + " items,\n", + " x=lambda item: item[\"time\"],\n", + " label=lambda item: item[\"version\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=min_x,\n", + " max_x=max_x,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " ),\n", + " \"bottom_row\": weave.legacy.weave.panels.Group(\n", + " style=\"height: 400px;\",\n", + " preferHorizontal=True,\n", + " items={\n", + " \"preds_by_user\": weave.legacy.weave.panels.LabeledItem(\n", + " label=\"Predictions by user over time\",\n", + " item=wandb.TimeSeries(\n", + " items,\n", + " x=lambda item: item[\"time\"],\n", + " label=lambda item: item[\"user\"],\n", + " agg=lambda preds: preds.count(),\n", + " min_x=min_x,\n", + " max_x=max_x,\n", + " mark=\"bar\",\n", + " axis_labels={\"y\": \"num_preds\"},\n", + " ),\n", + " ),\n", + " \"acceptance_rate\": weave.legacy.weave.panels.LabeledItem(\n", + " label=\"Acceptance rate over time by version\",\n", + " item=wandb.TimeSeries(\n", + " items,\n", + " x=lambda item: item[\"time\"],\n", + " label=lambda item: item[\"version\"],\n", + " agg=lambda preds: preds[\"accepted\"].sum() / preds.count(),\n", + " min_x=min_x,\n", + " max_x=max_x,\n", + " mark=\"line\",\n", + " axis_labels={\"y\": \"acceptance_rate\"},\n", + " ),\n", + " ),\n", + " },\n", + " ),\n", + " }\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35c7e310", + "metadata": {}, + "outputs": [], + "source": [ + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50b1be3e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "50e46344", - "metadata": {}, - "outputs": [], - "source": [ - "import datetime\n", - "import random\n", - "random.seed(1)\n", - "\n", - "min_x = datetime.datetime(2023, 3, 22, 11, 0, 0)\n", - "max_x = min_x + datetime.timedelta(hours=1)\n", - "\n", - "users = ['nick', 'shawn', 'stacey', 'tim', 'danny']\n", - "model_versions = ['v1', 'v2']\n", - "\n", - "num_requests = 10000\n", - "\n", - "def random_datetime_in_range() -> datetime.datetime:\n", - " min_timestamp = min_x.timestamp()\n", - " max_timestamp = max_x.timestamp()\n", - " random_timestamp = random.uniform(min_timestamp, max_timestamp)\n", - " return datetime.datetime.fromtimestamp(random_timestamp)\n", - "\n", - "def random_suggestion(n: int) -> str:\n", - " result = ''\n", - " for i in range(n):\n", - " result += random.choice('abcdefghijklmnopqrstuvwxyz')\n", - " return result\n", - "\n", - "\n", - "items = weave.save(\n", - " ops_arrow.to_arrow([\n", - " {\n", - " \"pred\": random_suggestion(7),\n", - " \"time\": random_datetime_in_range(),\n", - " \"user\": random.choice(users),\n", - " \"version\": random.choice(model_versions),\n", - " \"accepted\": random.choice([1, 1, 1, 0, 0]),\n", - " } \n", - " for _ in range(num_requests)\n", - " ])\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb863d87", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " items={\n", - " \"num_preds\": weave.legacy.panels.LabeledItem(\n", - " label=\"Predictions by version over time\",\n", - " item=wandb.TimeSeries(\n", - " items,\n", - " x=lambda item: item[\"time\"],\n", - " label=lambda item: item[\"version\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=min_x,\n", - " max_x=max_x,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " ),\n", - " \"bottom_row\": weave.legacy.panels.Group(\n", - " style=\"height: 400px;\",\n", - " preferHorizontal=True,\n", - " items={\n", - " \"preds_by_user\": weave.legacy.panels.LabeledItem(\n", - " label=\"Predictions by user over time\",\n", - " item=wandb.TimeSeries(\n", - " items,\n", - " x=lambda item: item[\"time\"],\n", - " label=lambda item: item[\"user\"],\n", - " agg=lambda preds: preds.count(),\n", - " min_x=min_x,\n", - " max_x=max_x,\n", - " mark=\"bar\",\n", - " axis_labels={\"y\": \"num_preds\"},\n", - " ),\n", - " ),\n", - " \"acceptance_rate\": weave.legacy.panels.LabeledItem(\n", - " label=\"Acceptance rate over time by version\",\n", - " item=wandb.TimeSeries(\n", - " items,\n", - " x=lambda item: item[\"time\"],\n", - " label=lambda item: item[\"version\"],\n", - " agg=lambda preds: preds[\"accepted\"].sum() / preds.count(),\n", - " min_x=min_x,\n", - " max_x=max_x,\n", - " mark=\"line\",\n", - " axis_labels={\"y\": \"acceptance_rate\"},\n", - " ),\n", - " ),\n", - " },\n", - " ),\n", - " }\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35c7e310", - "metadata": {}, - "outputs": [], - "source": [ - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "50b1be3e", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Untitled.ipynb b/weave/legacy/examples/experimental/skip_test/Untitled.ipynb index a3755ff5feb..ff38e5faef8 100644 --- a/weave/legacy/examples/experimental/skip_test/Untitled.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Untitled.ipynb @@ -1,51 +1,51 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "5ca8d4be", - "metadata": {}, - "outputs": [], - "source": [ - "import csv\n", - "import weave" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "5ca8d4be", + "metadata": {}, + "outputs": [], + "source": [ + "import csv\n", + "import weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "492a6655", + "metadata": {}, + "outputs": [], + "source": [ + "#help(csv.reader)\n", + "# reader = csv.reader(open('/Users/shawn/Downloads/wandb_export_2023-03-30T15_21_00.493-04_00.csv'))\n", + "# for row in reader:\n", + "# print(row)\n", + "#help(csv.reader)\n", + "weave.legacy.weave.ops.local_path('/Users/shawn/code2/weave/weave/testdata/cereal.csv').readcsv()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "492a6655", - "metadata": {}, - "outputs": [], - "source": [ - "#help(csv.reader)\n", - "# reader = csv.reader(open('/Users/shawn/Downloads/wandb_export_2023-03-30T15_21_00.493-04_00.csv'))\n", - "# for row in reader:\n", - "# print(row)\n", - "#help(csv.reader)\n", - "weave.legacy.ops.local_path('/Users/shawn/code2/weave/weave/testdata/cereal.csv').readcsv()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Vectorizing.ipynb b/weave/legacy/examples/experimental/skip_test/Vectorizing.ipynb index 456c64262a8..488c7f42ff5 100644 --- a/weave/legacy/examples/experimental/skip_test/Vectorizing.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Vectorizing.ipynb @@ -1,91 +1,91 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "5f10d941", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import time\n", - "from weave.legacy import storage\n", - "from weave.legacy.ops_primitives import geom" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "5f10d941", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import time\n", + "from weave.legacy.weave import storage\n", + "from weave.legacy.weave.ops_primitives import geom" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f3026bd", + "metadata": {}, + "outputs": [], + "source": [ + "segs = []\n", + "for i in range(1000000):\n", + " segs.append(geom.LineSegment(i*0.1, i+0.2, i+0.3, i*0.5))\n", + "l = weave.save(segs)\n", + "arr = weave.legacy.weave.ops.ArrowArrayList(storage.to_arrow(segs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7544d866", + "metadata": {}, + "outputs": [], + "source": [ + "# pure python\n", + "start_time = time.time()\n", + "sum(seg.length.resolve_fn(seg) for seg in segs)\n", + "print('TIME: ', time.time() - start_time)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11c2a9b6", + "metadata": {}, + "outputs": [], + "source": [ + "# weave list\n", + "start_time = time.time()\n", + "weave.use(l.map(lambda seg: seg.length()).sum())\n", + "print('TIME: ', time.time() - start_time)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71816731", + "metadata": {}, + "outputs": [], + "source": [ + "# weave vectorized arrow\n", + "start_time = time.time()\n", + "weave.use(arr.map(lambda seg: seg.length()).sum())\n", + "print('TIME: ', time.time() - start_time)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f3026bd", - "metadata": {}, - "outputs": [], - "source": [ - "segs = []\n", - "for i in range(1000000):\n", - " segs.append(geom.LineSegment(i*0.1, i+0.2, i+0.3, i*0.5))\n", - "l = weave.save(segs)\n", - "arr = weave.legacy.ops.ArrowArrayList(storage.to_arrow(segs))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7544d866", - "metadata": {}, - "outputs": [], - "source": [ - "# pure python\n", - "start_time = time.time()\n", - "sum(seg.length.resolve_fn(seg) for seg in segs)\n", - "print('TIME: ', time.time() - start_time)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "11c2a9b6", - "metadata": {}, - "outputs": [], - "source": [ - "# weave list\n", - "start_time = time.time()\n", - "weave.use(l.map(lambda seg: seg.length()).sum())\n", - "print('TIME: ', time.time() - start_time)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "71816731", - "metadata": {}, - "outputs": [], - "source": [ - "# weave vectorized arrow\n", - "start_time = time.time()\n", - "weave.use(arr.map(lambda seg: seg.length()).sum())\n", - "print('TIME: ', time.time() - start_time)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/WB data.ipynb b/weave/legacy/examples/experimental/skip_test/WB data.ipynb index ce5c1633310..a7a545ffa0a 100644 --- a/weave/legacy/examples/experimental/skip_test/WB data.ipynb +++ b/weave/legacy/examples/experimental/skip_test/WB data.ipynb @@ -1,80 +1,80 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "bb76d9d8", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "bb76d9d8", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54243e21", + "metadata": {}, + "outputs": [], + "source": [ + "project = weave.legacy.weave.ops.project('shawn', 'fasion-sweep')\n", + "runs = project.runs()\n", + "\n", + "panel = weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " items={\n", + " \"sidebar\": weave.legacy.weave.panels.Group(\n", + " style=\"width: 200px; padding: 16px;\",\n", + " items={\n", + " \"run\": weave.legacy.weave.panels.ObjectPicker(runs, label='run'),\n", + " }\n", + " ),\n", + " #\"main\": lambda sidebar: project.run(sidebar.config.items['run_id'].config.choice).history()\n", + " \"main\": lambda sidebar: sidebar.config.items['run'].config.choice.history()\n", + " }\n", + ")\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a771433", + "metadata": {}, + "outputs": [], + "source": [ + "project = weave.legacy.weave.ops.project('shawn', 'fasion-sweep')\n", + "run_ids = project.runs().id()\n", + "\n", + "panel = weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " items={\n", + " \"sidebar\": weave.legacy.weave.panels.Group(\n", + " style=\"width: 200px; padding: 16px;\",\n", + " items={\n", + " \"run_id\": weave.legacy.weave.panels.ObjectPicker(run_ids, label='run_id'),\n", + " }\n", + " ),\n", + " #\"main\": lambda sidebar: project.run(sidebar.config.items['run_id'].config.choice).history()\n", + " \"main\": lambda sidebar: weave.legacy.weave.panels.Plot(\n", + " project.run(sidebar.config.items['run_id'].config.choice).history(),\n", + " x=lambda row: row['_step'],\n", + " y=lambda row: row['loss'],\n", + " )\n", + " }\n", + ")\n", + "#panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "54243e21", - "metadata": {}, - "outputs": [], - "source": [ - "project = weave.legacy.ops.project('shawn', 'fasion-sweep')\n", - "runs = project.runs()\n", - "\n", - "panel = weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " items={\n", - " \"sidebar\": weave.legacy.panels.Group(\n", - " style=\"width: 200px; padding: 16px;\",\n", - " items={\n", - " \"run\": weave.legacy.panels.ObjectPicker(runs, label='run'),\n", - " }\n", - " ),\n", - " #\"main\": lambda sidebar: project.run(sidebar.config.items['run_id'].config.choice).history()\n", - " \"main\": lambda sidebar: sidebar.config.items['run'].config.choice.history()\n", - " }\n", - ")\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a771433", - "metadata": {}, - "outputs": [], - "source": [ - "project = weave.legacy.ops.project('shawn', 'fasion-sweep')\n", - "run_ids = project.runs().id()\n", - "\n", - "panel = weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " items={\n", - " \"sidebar\": weave.legacy.panels.Group(\n", - " style=\"width: 200px; padding: 16px;\",\n", - " items={\n", - " \"run_id\": weave.legacy.panels.ObjectPicker(run_ids, label='run_id'),\n", - " }\n", - " ),\n", - " #\"main\": lambda sidebar: project.run(sidebar.config.items['run_id'].config.choice).history()\n", - " \"main\": lambda sidebar: weave.legacy.panels.Plot(\n", - " project.run(sidebar.config.items['run_id'].config.choice).history(),\n", - " x=lambda row: row['_step'],\n", - " y=lambda row: row['loss'],\n", - " )\n", - " }\n", - ")\n", - "#panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/Weave geo data.ipynb b/weave/legacy/examples/experimental/skip_test/Weave geo data.ipynb index 7b63ffb245e..fba602aec01 100644 --- a/weave/legacy/examples/experimental/skip_test/Weave geo data.ipynb +++ b/weave/legacy/examples/experimental/skip_test/Weave geo data.ipynb @@ -1,106 +1,106 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "8d5be6da", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import pandas as pd\n", - "from weave.legacy.ecosystem import wandb\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8d5be6da", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import pandas as pd\n", + "from weave.legacy.weave.ecosystem import wandb\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90518a5e", + "metadata": {}, + "outputs": [], + "source": [ + "data = pd.read_csv(\"/Users/shawn/code2/weave/database.csv\").to_dict(orient='records')\n", + "data = data[:1000]\n", + "weave.show(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f643bb3f", + "metadata": {}, + "outputs": [], + "source": [ + "#wandb.Scatter(data, x_fn=lambda item: item[\"Longitude\"], y_fn=lambda item: item[\"Latitude\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b94b992b", + "metadata": {}, + "outputs": [], + "source": [ + "# wandb.plotly_geo([\n", + "# {'lat': 35, 'long': 39, 'color': 0.1},\n", + "# {'lat': 19, 'long': 24, 'color': 100}])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b31859b", + "metadata": {}, + "outputs": [], + "source": [ + "#wandb.Geo(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "872cb8d0", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Group(\n", + " items={\n", + " 'geo': wandb.Geo(data,\n", + " x_fn=lambda item: item['Longitude'],\n", + " y_fn=lambda item: item[\"Latitude\"],\n", + " color_fn=lambda item: item[\"Magnitude\"]),\n", + " 'table': lambda geo: weave.legacy.weave.panels.LabeledItem(\n", + " label='Selected items',\n", + " item=weave.legacy.weave.panels.Group(\n", + " style=\"height: 400px;\",\n", + " preferHorizontal=True,\n", + " items={\n", + " 'table': geo.selected()\n", + " }))\n", + " })\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "90518a5e", - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv(\"/Users/shawn/code2/weave/database.csv\").to_dict(orient='records')\n", - "data = data[:1000]\n", - "weave.show(data)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f643bb3f", - "metadata": {}, - "outputs": [], - "source": [ - "#wandb.Scatter(data, x_fn=lambda item: item[\"Longitude\"], y_fn=lambda item: item[\"Latitude\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b94b992b", - "metadata": {}, - "outputs": [], - "source": [ - "# wandb.plotly_geo([\n", - "# {'lat': 35, 'long': 39, 'color': 0.1},\n", - "# {'lat': 19, 'long': 24, 'color': 100}])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b31859b", - "metadata": {}, - "outputs": [], - "source": [ - "#wandb.Geo(data)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "872cb8d0", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Group(\n", - " items={\n", - " 'geo': wandb.Geo(data,\n", - " x_fn=lambda item: item['Longitude'],\n", - " y_fn=lambda item: item[\"Latitude\"],\n", - " color_fn=lambda item: item[\"Magnitude\"]),\n", - " 'table': lambda geo: weave.legacy.panels.LabeledItem(\n", - " label='Selected items',\n", - " item=weave.legacy.panels.Group(\n", - " style=\"height: 400px;\",\n", - " preferHorizontal=True,\n", - " items={\n", - " 'table': geo.selected()\n", - " }))\n", - " })\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/art_explore.ipynb b/weave/legacy/examples/experimental/skip_test/art_explore.ipynb index b591b5a163e..bf693b598ea 100644 --- a/weave/legacy/examples/experimental/skip_test/art_explore.ipynb +++ b/weave/legacy/examples/experimental/skip_test/art_explore.ipynb @@ -1,141 +1,141 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "3df06c92", - "metadata": {}, - "source": [ - "# Art Exploration Dashboard\n", - "\n", - "This quick example programmatically buids a Weave Dashboard to explore and remix visual art.\n", - "* generate creative descriptions of artwork given some famous artists as reference points/inspiration\n", - "* create a Table (one column of text) of these art prompts (named \"style_riff\")\n", - "* use Stable Diffusion to generate art based on these prompts (\"riff_art\" column)\n", - "* inverse-generate a prompt which would lead to that image (and perhaps describe it better? \"art_desc\" column)\n", - "* imagine a perfect title for an artwork with that description (\"desc title\")\n", - "* finally, generate a remixed artwork for that title (\"remixed_art\"), except with more of the generated color specified in the horizontal panel (itself generated from the RGB values in the left sidebar)\n", - "\n", - "## Play with these parameters\n", - "\n", - "* the artist names and artist_id for which artist is the starting point\n", - "* the \"palette\" colors—these are the rows in the Dashboard\n", - "* the number of works (must be <= the number of colors in the palette and the number of artists\n", - "* the RGB values (0, 255)—these are combined to generate the \"color\" which is amplified in the remix art. Sometimes the color names are returned with extra words :)\n", - "* any of the prompts used to generate images or titles!\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "3df06c92", + "metadata": {}, + "source": [ + "# Art Exploration Dashboard\n", + "\n", + "This quick example programmatically buids a Weave Dashboard to explore and remix visual art.\n", + "* generate creative descriptions of artwork given some famous artists as reference points/inspiration\n", + "* create a Table (one column of text) of these art prompts (named \"style_riff\")\n", + "* use Stable Diffusion to generate art based on these prompts (\"riff_art\" column)\n", + "* inverse-generate a prompt which would lead to that image (and perhaps describe it better? \"art_desc\" column)\n", + "* imagine a perfect title for an artwork with that description (\"desc title\")\n", + "* finally, generate a remixed artwork for that title (\"remixed_art\"), except with more of the generated color specified in the horizontal panel (itself generated from the RGB values in the left sidebar)\n", + "\n", + "## Play with these parameters\n", + "\n", + "* the artist names and artist_id for which artist is the starting point\n", + "* the \"palette\" colors—these are the rows in the Dashboard\n", + "* the number of works (must be <= the number of colors in the palette and the number of artists\n", + "* the RGB values (0, 255)—these are combined to generate the \"color\" which is amplified in the remix art. Sometimes the color names are returned with extra words :)\n", + "* any of the prompts used to generate images or titles!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ef13bdf", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "from weave.legacy.weave.ecosystem.all import langchain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c765a214", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " \"artists\" : [\"René Magritte\", \"Josephine Wall\", \"Android Jones\", \"Alphonse Mucha\", \"Claude Monet\"],\n", + " \"artist_id\" : 0,\n", + " \"num_works\": 3,\n", + " \"palette\" : [\"red\", \"orange\", \"yellow\", \"green\", \"blue\", \"violet\"],\n", + " \"style_riff\": \n", + " lambda chat_model, num_works, artists, palette, artist_id:\n", + " weave.legacy.weave.ops.range(0, num_works, 1).map(\n", + " lambda row:\n", + " chat_model.predict(weave_internal.const(\"You are an expert art historian. In a sophisticated, avant-garde art museum, you see pieces in \")\n", + " + palette[row] + \" by \" + artists[artist_id]\n", + " + \", which has a fantastic contrast and synergy with the work of\")\n", + " \n", + " ),\n", + " \"riff_art\":\n", + " lambda style_riff, num_works:\n", + " weave.legacy.weave.ops.range(0, num_works, 1).map(\n", + " lambda row: style_riff[row]).stable_diffusion(),\n", + " \"art_desc\":\n", + " lambda riff_art, num_works:\n", + " weave.legacy.weave.ops.range(0, num_works, 1).map(\n", + " lambda row: riff_art[row]).img2prompt(),\n", + " \"desc_title\":\n", + " lambda art_desc, num_works, chat_model:\n", + " weave.legacy.weave.ops.range(0, num_works, 1).map(\n", + " lambda row: chat_model.predict(weave_internal.const(\"Art description: \")\n", + " + art_desc[row] + \". Perfect title:\")),\n", + " \"R\" : 168,\n", + " \"G\" : 156,\n", + " \"B\" : 234,\n", + " \"color\" : lambda chat_model, R, G, B:\n", + " chat_model.predict(weave_internal.const(\"RGB value: (\")\n", + " + R.toString() + \",\" + G.toString() + \",\" + B.toString()\n", + " + \"). Precise color name:\"),\n", + " \"remixed_art\":\n", + " lambda desc_title, num_works, color:\n", + " weave.legacy.weave.ops.range(0, num_works, 1).map(\n", + " lambda row: weave_internal.const(\"a beautiful painting titled \")\n", + " + desc_title[row] + \" with more \" + color).stable_diffusion()\n", + " \n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda style_riff: weave.legacy.weave.panels.Table(style_riff, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=4, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda riff_art: weave.legacy.weave.panels.Table(riff_art, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=4, y=0, w=6, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda art_desc: weave.legacy.weave.panels.Table(art_desc, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=10, y=0, w=4, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda desc_title: weave.legacy.weave.panels.Table(desc_title, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=14, y=0, w=4, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda remixed_art: weave.legacy.weave.panels.Table(remixed_art, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=18, y=0, w=6, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda color: weave.legacy.weave.panels.StringEditor(color),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=7, y=16, w=10, h=4)\n", + " ),\n", + " \n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "1ef13bdf", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal\n", - "from weave.legacy.ecosystem.all import langchain" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c765a214", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " \"chat_model\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " \"artists\" : [\"René Magritte\", \"Josephine Wall\", \"Android Jones\", \"Alphonse Mucha\", \"Claude Monet\"],\n", - " \"artist_id\" : 0,\n", - " \"num_works\": 3,\n", - " \"palette\" : [\"red\", \"orange\", \"yellow\", \"green\", \"blue\", \"violet\"],\n", - " \"style_riff\": \n", - " lambda chat_model, num_works, artists, palette, artist_id:\n", - " weave.legacy.ops.range(0, num_works, 1).map(\n", - " lambda row:\n", - " chat_model.predict(weave_internal.const(\"You are an expert art historian. In a sophisticated, avant-garde art museum, you see pieces in \")\n", - " + palette[row] + \" by \" + artists[artist_id]\n", - " + \", which has a fantastic contrast and synergy with the work of\")\n", - " \n", - " ),\n", - " \"riff_art\":\n", - " lambda style_riff, num_works:\n", - " weave.legacy.ops.range(0, num_works, 1).map(\n", - " lambda row: style_riff[row]).stable_diffusion(),\n", - " \"art_desc\":\n", - " lambda riff_art, num_works:\n", - " weave.legacy.ops.range(0, num_works, 1).map(\n", - " lambda row: riff_art[row]).img2prompt(),\n", - " \"desc_title\":\n", - " lambda art_desc, num_works, chat_model:\n", - " weave.legacy.ops.range(0, num_works, 1).map(\n", - " lambda row: chat_model.predict(weave_internal.const(\"Art description: \")\n", - " + art_desc[row] + \". Perfect title:\")),\n", - " \"R\" : 168,\n", - " \"G\" : 156,\n", - " \"B\" : 234,\n", - " \"color\" : lambda chat_model, R, G, B:\n", - " chat_model.predict(weave_internal.const(\"RGB value: (\")\n", - " + R.toString() + \",\" + G.toString() + \",\" + B.toString()\n", - " + \"). Precise color name:\"),\n", - " \"remixed_art\":\n", - " lambda desc_title, num_works, color:\n", - " weave.legacy.ops.range(0, num_works, 1).map(\n", - " lambda row: weave_internal.const(\"a beautiful painting titled \")\n", - " + desc_title[row] + \" with more \" + color).stable_diffusion()\n", - " \n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda style_riff: weave.legacy.panels.Table(style_riff, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=4, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda riff_art: weave.legacy.panels.Table(riff_art, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=4, y=0, w=6, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda art_desc: weave.legacy.panels.Table(art_desc, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=10, y=0, w=4, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda desc_title: weave.legacy.panels.Table(desc_title, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=14, y=0, w=4, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda remixed_art: weave.legacy.panels.Table(remixed_art, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=18, y=0, w=6, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda color: weave.legacy.panels.StringEditor(color),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=7, y=16, w=10, h=4)\n", - " ),\n", - " \n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/branching_runs.ipynb b/weave/legacy/examples/experimental/skip_test/branching_runs.ipynb index 86b3c167e31..ac3106dfdc9 100644 --- a/weave/legacy/examples/experimental/skip_test/branching_runs.ipynb +++ b/weave/legacy/examples/experimental/skip_test/branching_runs.ipynb @@ -1,215 +1,215 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "930f7487", - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "import weave\n", - "from weave.legacy.ops_primitives import ArrowWeaveList\n", - "from weave.legacy.ops_domain import RunSegment\n", - "from weave.legacy import storage\n", - "from weave import publish, type_of\n", - "from weave.legacy.weave_types import List\n", - "import typing\n", - "import time\n", - "import sys\n", - "import numpy as np\n", - "from weave.legacy.ops import to_arrow\n", - "\n", - "import logging\n", - "import pyarrow as pa\n", - "\n", - "\n", - "logger = logging.getLogger(\"run_segment\")\n", - "handler = logging.StreamHandler(stream=sys.stdout)\n", - "handler.setFormatter(logging.Formatter(\"%(asctime)s - %(message)s\"))\n", - "logger.addHandler(handler)\n", - "\n", - "# set to logging.INFO for more verbose profiling\n", - "logger.setLevel(logging.ERROR)\n", - "\n", - "# serializer = publish # uses w&b artifacts intead of local artifacts\n", - "serializer = storage.save\n", - "\n", - "N_NUMERIC_METRICS = 99 # number of numerical columns in the metrics table\n", - "\n", - "\n", - "def random_metrics(n: int = 10, starting_step: int = 0, delta_step: int = 1):\n", - " \"\"\"Create an array of metrics of length n starting from step starting_index.\"\"\"\n", - " if n <= 0:\n", - " raise ValueError(\"n must be at least 1\")\n", - " if starting_step < 0:\n", - " raise ValueError(\"starting index must be at least 0\")\n", - " if delta_step < 1:\n", - " raise ValueError(\"delta_step must be an integer greater than or equal to 1.\")\n", - " data = {\n", - " \"step\": np.arange(starting_step, starting_step + n * delta_step, delta_step),\n", - " \"string_col\": np.random.choice(list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"), n),\n", - " }\n", - " for j in range(N_NUMERIC_METRICS):\n", - " data[f\"metric{j}\"] = np.random.random(n) + np.sin(data['step'] * np.pi / 1e5)\n", - " data[f\"metric{j}\"][n // 10] = 10.\n", - " \n", - " return ArrowWeaveList(pa.table(data))\n", - "\n", - "\n", - "def create_branch(\n", - " name: str,\n", - " previous_segment: typing.Optional[RunSegment] = None,\n", - " length=10,\n", - " previous_segment_branch_frac=0.8,\n", - ") -> RunSegment:\n", - " \"\"\"Create a new segment and optionally attach it to a previous segment.\n", - "\n", - " Parameters\n", - " ----------\n", - " name: str\n", - " The name of the segment.\n", - " previous_segment: Optional[RunSegment], default None.\n", - " The parent run segment. If this is a root run segment, use None.\n", - " length: int, default = 10\n", - " The number of history rows to generate for the segment.\n", - " previous_segment_branch_frac: float satisfying 0 < branch_frac <= 1.\n", - " Parameter describing where in the previous segment to set the branch point.\n", - " A previous_segment_branch_frac of 0 sets the branch point at the previous\n", - " segment's root, whereas a previous_segment_branch_frac of 1 sets the branch\n", - " point at the end of the previous segment. A previous_segment_branch_frac of\n", - " 0.5 would include half of the previous segment's metric rows.\n", - "\n", - " Returns\n", - " -------\n", - " segment: RunSegment\n", - " The new segment.\n", - " \"\"\"\n", - " if not (0 < previous_segment_branch_frac <= 1):\n", - " raise ValueError(\"branch_frac must satisfy 0 < branch_frac <= 1\")\n", - "\n", - " if length <= 0:\n", - " raise ValueError(\"Length must be greater than 0.\")\n", - "\n", - " if previous_segment:\n", - " previous_metrics = previous_segment.metrics\n", - " n_previous_metrics = len(previous_metrics)\n", - " if n_previous_metrics > 0:\n", - " previous_segment_branch_index = (\n", - " int(previous_segment_branch_frac * n_previous_metrics) - 1\n", - " )\n", - "\n", - " # this run segment has a different root than the previous one\n", - " if previous_segment_branch_index < 0:\n", - " raise ValueError(\n", - " f\"Invalid branch point on RunSegment: previous_segment_branch_index \"\n", - " f\"{previous_segment_branch_index} must be between 0 and {len(previous_metrics) - 1}\"\n", - " )\n", - "\n", - " previous_segment_branch_step = (\n", - " previous_metrics._index(0)[\"step\"] + previous_segment_branch_index\n", - " )\n", - "\n", - " ref = storage.save(previous_segment)\n", - " new_metrics = random_metrics(\n", - " n=length, starting_step=previous_segment_branch_step + 1\n", - " )\n", - "\n", - " return RunSegment(name, ref.uri, previous_segment_branch_index, new_metrics)\n", - " return RunSegment(name, None, 0, random_metrics(length, 0))\n", - "\n", - "\n", - "def create_experiment(\n", - " num_steps: int, num_runs: int, branch_frac: float = 0.8\n", - ") -> typing.Optional[RunSegment]:\n", - " num_steps_per_run = num_steps // num_runs\n", - " segment = None\n", - " for i in range(num_runs):\n", - " segment = create_branch(\n", - " f\"branch {i}\",\n", - " segment,\n", - " length=num_steps_per_run,\n", - " previous_segment_branch_frac=branch_frac,\n", - " )\n", - " return segment\n", - "\n", - "last_segment = create_experiment(1000000, 100, 0.8)" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "930f7487", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import weave\n", + "from weave.legacy.weave.ops_primitives import ArrowWeaveList\n", + "from weave.legacy.weave.ops_domain import RunSegment\n", + "from weave.legacy.weave import storage\n", + "from weave import publish, type_of\n", + "from weave.legacy.weave.weave_types import List\n", + "import typing\n", + "import time\n", + "import sys\n", + "import numpy as np\n", + "from weave.legacy.weave.ops import to_arrow\n", + "\n", + "import logging\n", + "import pyarrow as pa\n", + "\n", + "\n", + "logger = logging.getLogger(\"run_segment\")\n", + "handler = logging.StreamHandler(stream=sys.stdout)\n", + "handler.setFormatter(logging.Formatter(\"%(asctime)s - %(message)s\"))\n", + "logger.addHandler(handler)\n", + "\n", + "# set to logging.INFO for more verbose profiling\n", + "logger.setLevel(logging.ERROR)\n", + "\n", + "# serializer = publish # uses w&b artifacts intead of local artifacts\n", + "serializer = storage.save\n", + "\n", + "N_NUMERIC_METRICS = 99 # number of numerical columns in the metrics table\n", + "\n", + "\n", + "def random_metrics(n: int = 10, starting_step: int = 0, delta_step: int = 1):\n", + " \"\"\"Create an array of metrics of length n starting from step starting_index.\"\"\"\n", + " if n <= 0:\n", + " raise ValueError(\"n must be at least 1\")\n", + " if starting_step < 0:\n", + " raise ValueError(\"starting index must be at least 0\")\n", + " if delta_step < 1:\n", + " raise ValueError(\"delta_step must be an integer greater than or equal to 1.\")\n", + " data = {\n", + " \"step\": np.arange(starting_step, starting_step + n * delta_step, delta_step),\n", + " \"string_col\": np.random.choice(list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"), n),\n", + " }\n", + " for j in range(N_NUMERIC_METRICS):\n", + " data[f\"metric{j}\"] = np.random.random(n) + np.sin(data['step'] * np.pi / 1e5)\n", + " data[f\"metric{j}\"][n // 10] = 10.\n", + " \n", + " return ArrowWeaveList(pa.table(data))\n", + "\n", + "\n", + "def create_branch(\n", + " name: str,\n", + " previous_segment: typing.Optional[RunSegment] = None,\n", + " length=10,\n", + " previous_segment_branch_frac=0.8,\n", + ") -> RunSegment:\n", + " \"\"\"Create a new segment and optionally attach it to a previous segment.\n", + "\n", + " Parameters\n", + " ----------\n", + " name: str\n", + " The name of the segment.\n", + " previous_segment: Optional[RunSegment], default None.\n", + " The parent run segment. If this is a root run segment, use None.\n", + " length: int, default = 10\n", + " The number of history rows to generate for the segment.\n", + " previous_segment_branch_frac: float satisfying 0 < branch_frac <= 1.\n", + " Parameter describing where in the previous segment to set the branch point.\n", + " A previous_segment_branch_frac of 0 sets the branch point at the previous\n", + " segment's root, whereas a previous_segment_branch_frac of 1 sets the branch\n", + " point at the end of the previous segment. A previous_segment_branch_frac of\n", + " 0.5 would include half of the previous segment's metric rows.\n", + "\n", + " Returns\n", + " -------\n", + " segment: RunSegment\n", + " The new segment.\n", + " \"\"\"\n", + " if not (0 < previous_segment_branch_frac <= 1):\n", + " raise ValueError(\"branch_frac must satisfy 0 < branch_frac <= 1\")\n", + "\n", + " if length <= 0:\n", + " raise ValueError(\"Length must be greater than 0.\")\n", + "\n", + " if previous_segment:\n", + " previous_metrics = previous_segment.metrics\n", + " n_previous_metrics = len(previous_metrics)\n", + " if n_previous_metrics > 0:\n", + " previous_segment_branch_index = (\n", + " int(previous_segment_branch_frac * n_previous_metrics) - 1\n", + " )\n", + "\n", + " # this run segment has a different root than the previous one\n", + " if previous_segment_branch_index < 0:\n", + " raise ValueError(\n", + " f\"Invalid branch point on RunSegment: previous_segment_branch_index \"\n", + " f\"{previous_segment_branch_index} must be between 0 and {len(previous_metrics) - 1}\"\n", + " )\n", + "\n", + " previous_segment_branch_step = (\n", + " previous_metrics._index(0)[\"step\"] + previous_segment_branch_index\n", + " )\n", + "\n", + " ref = storage.save(previous_segment)\n", + " new_metrics = random_metrics(\n", + " n=length, starting_step=previous_segment_branch_step + 1\n", + " )\n", + "\n", + " return RunSegment(name, ref.uri, previous_segment_branch_index, new_metrics)\n", + " return RunSegment(name, None, 0, random_metrics(length, 0))\n", + "\n", + "\n", + "def create_experiment(\n", + " num_steps: int, num_runs: int, branch_frac: float = 0.8\n", + ") -> typing.Optional[RunSegment]:\n", + " num_steps_per_run = num_steps // num_runs\n", + " segment = None\n", + " for i in range(num_runs):\n", + " segment = create_branch(\n", + " f\"branch {i}\",\n", + " segment,\n", + " length=num_steps_per_run,\n", + " previous_segment_branch_frac=branch_frac,\n", + " )\n", + " return segment\n", + "\n", + "last_segment = create_experiment(1000000, 100, 0.8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "139bc4d0", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(last_segment)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ca43fb6", + "metadata": {}, + "outputs": [], + "source": [ + "weave.show(last_segment.experiment())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d8b6719", + "metadata": {}, + "outputs": [], + "source": [ + "plot = weave.legacy.weave.panels.Plot(last_segment.experiment())\n", + "series = plot.series[-1]\n", + "series2 = series.clone()\n", + "\n", + "plot.add_series(series2)\n", + "plot.set_x(lambda row: weave.legacy.weave.ops.number_bin(row['step'], weave.legacy.weave.ops.numbers_bins_equal([1, 2000], 2)))\n", + "\n", + "series.set_y(lambda row: weave.legacy.weave.ops.numbers_avg(row['metric0']))\n", + "series.set_mark_constant('line')\n", + "\n", + "series2.set_y(lambda row: weave.legacy.weave.ops.numbers_min(row['metric0']))\n", + "series2.set_y2(lambda row: weave.legacy.weave.ops.numbers_max(row['metric0']))\n", + "series2.set_mark_constant('area')\n", + "\n", + "plot.groupby_x()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee4656e4", + "metadata": {}, + "outputs": [], + "source": [ + "plot" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "139bc4d0", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(last_segment)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ca43fb6", - "metadata": {}, - "outputs": [], - "source": [ - "weave.show(last_segment.experiment())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d8b6719", - "metadata": {}, - "outputs": [], - "source": [ - "plot = weave.legacy.panels.Plot(last_segment.experiment())\n", - "series = plot.series[-1]\n", - "series2 = series.clone()\n", - "\n", - "plot.add_series(series2)\n", - "plot.set_x(lambda row: weave.legacy.ops.number_bin(row['step'], weave.legacy.ops.numbers_bins_equal([1, 2000], 2)))\n", - "\n", - "series.set_y(lambda row: weave.legacy.ops.numbers_avg(row['metric0']))\n", - "series.set_mark_constant('line')\n", - "\n", - "series2.set_y(lambda row: weave.legacy.ops.numbers_min(row['metric0']))\n", - "series2.set_y2(lambda row: weave.legacy.ops.numbers_max(row['metric0']))\n", - "series2.set_mark_constant('area')\n", - "\n", - "plot.groupby_x()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee4656e4", - "metadata": {}, - "outputs": [], - "source": [ - "plot" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/image_gen_replicate.ipynb b/weave/legacy/examples/experimental/skip_test/image_gen_replicate.ipynb index dad519c4e85..8d4c05d0b14 100644 --- a/weave/legacy/examples/experimental/skip_test/image_gen_replicate.ipynb +++ b/weave/legacy/examples/experimental/skip_test/image_gen_replicate.ipynb @@ -1,77 +1,77 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "a1d79cb9", - "metadata": {}, - "source": [ - "# Image Generation with Replicate\n", - "\n", - "Convert a text prompt to an image and back to text with img2prompt or clip captioning." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "a1d79cb9", + "metadata": {}, + "source": [ + "# Image Generation with Replicate\n", + "\n", + "Convert a text prompt to an image and back to text with img2prompt or clip captioning." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c49c962", + "metadata": {}, + "outputs": [], + "source": [ + "# weave-test-skip-all: replicate API is slow and fails sometimes\n", + "import os\n", + "from weave.legacy.weave.ecosystem import replicate as weave_replicate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38f878f2", + "metadata": {}, + "outputs": [], + "source": [ + "weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e848393", + "metadata": {}, + "outputs": [], + "source": [ + "weave_replicate.img2prompt(weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50596314", + "metadata": {}, + "outputs": [], + "source": [ + "weave_replicate.clip_prefix_caption(weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\"))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "7c49c962", - "metadata": {}, - "outputs": [], - "source": [ - "# weave-test-skip-all: replicate API is slow and fails sometimes\n", - "import os\n", - "from weave.legacy.ecosystem import replicate as weave_replicate" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38f878f2", - "metadata": {}, - "outputs": [], - "source": [ - "weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6e848393", - "metadata": {}, - "outputs": [], - "source": [ - "weave_replicate.img2prompt(weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "50596314", - "metadata": {}, - "outputs": [], - "source": [ - "weave_replicate.clip_prefix_caption(weave_replicate.stable_diffusion(\"Your life is a sheep herd. You're the driver.\"))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/langchain docbot.ipynb b/weave/legacy/examples/experimental/skip_test/langchain docbot.ipynb index f9fe0650898..ef4eeb10d91 100644 --- a/weave/legacy/examples/experimental/skip_test/langchain docbot.ipynb +++ b/weave/legacy/examples/experimental/skip_test/langchain docbot.ipynb @@ -1,89 +1,89 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0d6220cd", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave import trace\n", - "weave.use_frontend_devmode()\n", - "from weave.legacy.ecosystem.all import langchain" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0d6220cd", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave import trace\n", + "weave.use_frontend_devmode()\n", + "from weave.legacy.weave.ecosystem.all import langchain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a674fd63", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chat_models import ChatOpenAI\n", + "#ChatOpenAI(model_name='gpt-3.5-turbo').query('hey whats up')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8cff0cd", + "metadata": {}, + "outputs": [], + "source": [ + "weave.save([\n", + " 'how do I use weave?',\n", + " 'what products does W&B have?',\n", + " 'how do I get a sweep ID from a run?'\n", + "], 'questions')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a47df334", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.docstore.document import Document\n", + "documents = weave.legacy.weave.ops.local_path('/Users/shawn/Downloads/documents.jsonl').readjsonl().limit(1000)\n", + "docs = weave.save([Document(**d) for d in weave.use(documents)], 'documents:latest')\n", + "#docs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "865af8ce", + "metadata": {}, + "outputs": [], + "source": [ + "db = langchain.faiss_from_documents(docs, langchain.openai_embeddings())\n", + "llm = langchain.chat_openai('gpt-3.5-turbo', 0.7)\n", + "qa = langchain.retrieval_qa_from_chain_type(llm, 'stuff', db)\n", + "qa.run('lets try again 2')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a674fd63", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chat_models import ChatOpenAI\n", - "#ChatOpenAI(model_name='gpt-3.5-turbo').query('hey whats up')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8cff0cd", - "metadata": {}, - "outputs": [], - "source": [ - "weave.save([\n", - " 'how do I use weave?',\n", - " 'what products does W&B have?',\n", - " 'how do I get a sweep ID from a run?'\n", - "], 'questions')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a47df334", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.docstore.document import Document\n", - "documents = weave.legacy.ops.local_path('/Users/shawn/Downloads/documents.jsonl').readjsonl().limit(1000)\n", - "docs = weave.save([Document(**d) for d in weave.use(documents)], 'documents:latest')\n", - "#docs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "865af8ce", - "metadata": {}, - "outputs": [], - "source": [ - "db = langchain.faiss_from_documents(docs, langchain.openai_embeddings())\n", - "llm = langchain.chat_openai('gpt-3.5-turbo', 0.7)\n", - "qa = langchain.retrieval_qa_from_chain_type(llm, 'stuff', db)\n", - "qa.run('lets try again 2')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/plot_selection.ipynb b/weave/legacy/examples/experimental/skip_test/plot_selection.ipynb index 624fa155ee2..dac241c03bc 100644 --- a/weave/legacy/examples/experimental/skip_test/plot_selection.ipynb +++ b/weave/legacy/examples/experimental/skip_test/plot_selection.ipynb @@ -1,63 +1,63 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c09f7649", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "# weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c09f7649", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "# weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6b83817", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"plot\": weave.legacy.weave.panels.Plot(weave.legacy.weave.ops.range(1, 100, 1),\n", + " x=lambda row: row,\n", + " y=lambda row: row ** 2,\n", + " ),\n", + " \"table\": lambda plot: weave.legacy.weave.panels.panel_plot.selected_rows(plot)\n", + " })\n", + "\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55f43427", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6b83817", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " items={\n", - " \"plot\": weave.legacy.panels.Plot(weave.legacy.ops.range(1, 100, 1),\n", - " x=lambda row: row,\n", - " y=lambda row: row ** 2,\n", - " ),\n", - " \"table\": lambda plot: weave.legacy.panels.panel_plot.selected_rows(plot)\n", - " })\n", - "\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55f43427", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/slurm.ipynb b/weave/legacy/examples/experimental/skip_test/slurm.ipynb index cf1ebf69670..4429a7285d8 100644 --- a/weave/legacy/examples/experimental/skip_test/slurm.ipynb +++ b/weave/legacy/examples/experimental/skip_test/slurm.ipynb @@ -1,66 +1,66 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "8817764c", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import slurm" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8817764c", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import slurm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cda9e9a3", + "metadata": {}, + "outputs": [], + "source": [ + "#slurm.slurm('http://localhost:8899').jobs()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bee4444", + "metadata": {}, + "outputs": [], + "source": [ + "#slurm.slurm('http://localhost:8899').nodes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e69a2f48", + "metadata": {}, + "outputs": [], + "source": [ + "slurm.slurm('http://localhost:8899')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "cda9e9a3", - "metadata": {}, - "outputs": [], - "source": [ - "#slurm.slurm('http://localhost:8899').jobs()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bee4444", - "metadata": {}, - "outputs": [], - "source": [ - "#slurm.slurm('http://localhost:8899').nodes()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e69a2f48", - "metadata": {}, - "outputs": [], - "source": [ - "slurm.slurm('http://localhost:8899')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/weave_engine_tracing.ipynb b/weave/legacy/examples/experimental/skip_test/weave_engine_tracing.ipynb index d409656ab68..f395535b7de 100644 --- a/weave/legacy/examples/experimental/skip_test/weave_engine_tracing.ipynb +++ b/weave/legacy/examples/experimental/skip_test/weave_engine_tracing.ipynb @@ -1,126 +1,126 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "de9a4837", - "metadata": {}, - "source": [ - "## Visualize Weave engine traces using Weave\n", - "\n", - "To produce data, run the Weave server with Datadog logging on, and WEAVE_TRACE_STREAM=//" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "de9a4837", + "metadata": {}, + "source": [ + "## Visualize Weave engine traces using Weave\n", + "\n", + "To produce data, run the Weave server with Datadog logging on, and WEAVE_TRACE_STREAM=//
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c62364f5", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d099de8", + "metadata": {}, + "outputs": [], + "source": [ + "# set this to the StreamTable your server logs to. Or run as is to use public test data.\n", + "spans = StreamTable('shawn/weave-trace-test2/test15').rows()\n", + "\n", + "board = weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'all_spans': spans,\n", + " 'trace_id': '',\n", + " 'selected_spans': lambda trace_id, all_spans: all_spans.filter(lambda row: row['trace_id'] == trace_id)\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " # TODO: have to compute all columns we want here, there's no way to\n", + " # tell js or python to do auto-columns behavior.\n", + " lambda all_spans: all_spans,\n", + " id='table',\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " # TODO: have to compute all columns we want here, there's no way to\n", + " # tell js or python to do auto-columns behavior.\n", + " lambda all_spans: weave.legacy.weave.panels.Table(\n", + " all_spans,\n", + " columns=[\n", + " weave.legacy.weave.panels.TableColumn(\n", + " lambda row: row['trace_id'],\n", + " groupby=True,\n", + " ),\n", + " weave.legacy.weave.panels.TableColumn(\n", + " lambda row: row.count(),\n", + " name='span count'\n", + " ),\n", + " weave.legacy.weave.panels.TableColumn(\n", + " # TODO: not exact, we need to add duration to get end time, but we don't have an add\n", + " # for 2 vectors yet.\n", + " lambda row: (row['start_time_ms'].max() - row['start_time_ms'].min()) / 1000,\n", + " name='duration_s'\n", + " )\n", + " ]\n", + " ),\n", + " id='traces',\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " # TODO: have to compute all columns we want here, there's no way to\n", + " # tell js or python to do auto-columns behavior.\n", + " lambda all_spans: weave.legacy.weave.panels.Table(\n", + " all_spans,\n", + " columns=[\n", + " weave.legacy.weave.panels.TableColumn(\n", + " lambda row: row['name'],\n", + " groupby=True,\n", + " ),\n", + " lambda row: row.count(),\n", + " weave.legacy.weave.panels.TableColumn(\n", + " # TODO: not exact, we need to add duration to get end time, but we don't have an add\n", + " # for 2 vectors yet.\n", + " lambda row: (row['start_time_ms'].max() - row['start_time_ms'].min()) / 1000,\n", + " name='duration_s'\n", + " )\n", + " ]\n", + " ),\n", + " id='spans',\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda selected_spans: weave.legacy.weave.panels.Trace(selected_spans),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=6, w=24, h=6)\n", + " )\n", + " ]\n", + ")\n", + "board" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "c62364f5", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.wandb_interface.wandb_stream_table import StreamTable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d099de8", - "metadata": {}, - "outputs": [], - "source": [ - "# set this to the StreamTable your server logs to. Or run as is to use public test data.\n", - "spans = StreamTable('shawn/weave-trace-test2/test15').rows()\n", - "\n", - "board = weave.legacy.panels.Board(\n", - " vars={\n", - " 'all_spans': spans,\n", - " 'trace_id': '',\n", - " 'selected_spans': lambda trace_id, all_spans: all_spans.filter(lambda row: row['trace_id'] == trace_id)\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " # TODO: have to compute all columns we want here, there's no way to\n", - " # tell js or python to do auto-columns behavior.\n", - " lambda all_spans: all_spans,\n", - " id='table',\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " # TODO: have to compute all columns we want here, there's no way to\n", - " # tell js or python to do auto-columns behavior.\n", - " lambda all_spans: weave.legacy.panels.Table(\n", - " all_spans,\n", - " columns=[\n", - " weave.legacy.panels.TableColumn(\n", - " lambda row: row['trace_id'],\n", - " groupby=True,\n", - " ),\n", - " weave.legacy.panels.TableColumn(\n", - " lambda row: row.count(),\n", - " name='span count'\n", - " ),\n", - " weave.legacy.panels.TableColumn(\n", - " # TODO: not exact, we need to add duration to get end time, but we don't have an add\n", - " # for 2 vectors yet.\n", - " lambda row: (row['start_time_ms'].max() - row['start_time_ms'].min()) / 1000,\n", - " name='duration_s'\n", - " )\n", - " ]\n", - " ),\n", - " id='traces',\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " # TODO: have to compute all columns we want here, there's no way to\n", - " # tell js or python to do auto-columns behavior.\n", - " lambda all_spans: weave.legacy.panels.Table(\n", - " all_spans,\n", - " columns=[\n", - " weave.legacy.panels.TableColumn(\n", - " lambda row: row['name'],\n", - " groupby=True,\n", - " ),\n", - " lambda row: row.count(),\n", - " weave.legacy.panels.TableColumn(\n", - " # TODO: not exact, we need to add duration to get end time, but we don't have an add\n", - " # for 2 vectors yet.\n", - " lambda row: (row['start_time_ms'].max() - row['start_time_ms'].min()) / 1000,\n", - " name='duration_s'\n", - " )\n", - " ]\n", - " ),\n", - " id='spans',\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda selected_spans: weave.legacy.panels.Trace(selected_spans),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=6, w=24, h=6)\n", - " )\n", - " ]\n", - ")\n", - "board" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/skip_test/weave_scifi.ipynb b/weave/legacy/examples/experimental/skip_test/weave_scifi.ipynb index e08c7cc5ed2..1c6d8b02736 100644 --- a/weave/legacy/examples/experimental/skip_test/weave_scifi.ipynb +++ b/weave/legacy/examples/experimental/skip_test/weave_scifi.ipynb @@ -1,80 +1,80 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "3c4790a9", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal\n", - "from weave.legacy.ecosystem.all import langchain\n", - "weave.use_frontend_devmode()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "3c4790a9", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal\n", + "from weave.legacy.weave.ecosystem.all import langchain\n", + "weave.use_frontend_devmode()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e9fe6dd", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " \"gpt\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", + " \"viz_style\" : [\"vintage art deco travel poster\",\n", + " \"futuristic science fiction movie\",\n", + " \"cute retro space anime\"],\n", + " \"viz_id\" : 0,\n", + " \"num_rows\": 2,\n", + " \"ml_terms\" : [\"models\", \"datasets\"],\n", + " \"prompts\": \n", + " lambda gpt, num_rows, ml_terms:\n", + " weave.legacy.weave.ops.range(0, num_rows, 1).map(\n", + " lambda row:\n", + " gpt.predict(weave_internal.const(\"You are an expert AGI architect describing the most powerful and flexible user interface for working with \")\n", + " + ml_terms[row] + \"in machine learning and AI. The three most important features for this are:\")\n", + " \n", + " ),\n", + " \"dream_view\":\n", + " lambda prompts, num_rows, viz_style, viz_id:\n", + " weave.legacy.weave.ops.range(0, num_rows, 1).map(\n", + " lambda row: weave_internal.const(\"a \") + viz_style[viz_id] + \" of a \" + prompts[row]).stable_diffusion(),\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda prompts: weave.legacy.weave.panels.Table(prompts, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=12, h=16)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda dream_view: weave.legacy.weave.panels.Table(dream_view, columns=[lambda col: col]),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=12, y=0, w=12, h=16)\n", + " ),\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e9fe6dd", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " \"gpt\": langchain.chat_openai(\"gpt-3.5-turbo\", 0.7),\n", - " \"viz_style\" : [\"vintage art deco travel poster\",\n", - " \"futuristic science fiction movie\",\n", - " \"cute retro space anime\"],\n", - " \"viz_id\" : 0,\n", - " \"num_rows\": 2,\n", - " \"ml_terms\" : [\"models\", \"datasets\"],\n", - " \"prompts\": \n", - " lambda gpt, num_rows, ml_terms:\n", - " weave.legacy.ops.range(0, num_rows, 1).map(\n", - " lambda row:\n", - " gpt.predict(weave_internal.const(\"You are an expert AGI architect describing the most powerful and flexible user interface for working with \")\n", - " + ml_terms[row] + \"in machine learning and AI. The three most important features for this are:\")\n", - " \n", - " ),\n", - " \"dream_view\":\n", - " lambda prompts, num_rows, viz_style, viz_id:\n", - " weave.legacy.ops.range(0, num_rows, 1).map(\n", - " lambda row: weave_internal.const(\"a \") + viz_style[viz_id] + \" of a \" + prompts[row]).stable_diffusion(),\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda prompts: weave.legacy.panels.Table(prompts, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=12, h=16)\n", - " ),\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda dream_view: weave.legacy.panels.Table(dream_view, columns=[lambda col: col]),\n", - " layout=weave.legacy.panels.BoardPanelLayout(x=12, y=0, w=12, h=16)\n", - " ),\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/spacy_demo.ipynb b/weave/legacy/examples/experimental/spacy_demo.ipynb index b5340e51112..bb8b4a4560f 100644 --- a/weave/legacy/examples/experimental/spacy_demo.ipynb +++ b/weave/legacy/examples/experimental/spacy_demo.ipynb @@ -1,52 +1,52 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "cd35f2be", - "metadata": {}, - "outputs": [], - "source": [ - "# Run the following installation if you have not already\n", - "#!python -m spacy download en_core_web_sm" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "cd35f2be", + "metadata": {}, + "outputs": [], + "source": [ + "# Run the following installation if you have not already\n", + "#!python -m spacy download en_core_web_sm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ede77c85", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.ecosystem.spacy import spacy\n", + "\n", + "text = \"\"\"Welcome to the spaCy Demo.\\\n", + "We currently support the small English language model, \\\n", + "and will add support for different types in the near future!\"\"\"\n", + "\n", + "spacy(text)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "ede77c85", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy.ecosystem.spacy import spacy\n", - "\n", - "text = \"\"\"Welcome to the spaCy Demo.\\\n", - "We currently support the small English language model, \\\n", - "and will add support for different types in the near future!\"\"\"\n", - "\n", - "spacy(text)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/table_data_passing.ipynb b/weave/legacy/examples/experimental/table_data_passing.ipynb index de674e689ff..66cedc7efbd 100644 --- a/weave/legacy/examples/experimental/table_data_passing.ipynb +++ b/weave/legacy/examples/experimental/table_data_passing.ipynb @@ -1,82 +1,82 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c09f7649", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy.panels\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c09f7649", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave.panels\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6b83817", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"table\": weave.legacy.weave.panels.Table(weave.legacy.weave.ops.range(1, 100, 1), \n", + " columns=[\n", + " lambda row: row,\n", + " lambda row: row ** 2,\n", + " ],\n", + " ),\n", + " \"all_rows\": lambda table: weave.legacy.weave.panels.Plot(table.all_rows(),\n", + " x=lambda row: row['c_0'],\n", + " y=lambda row: row['c_1']\n", + " ),\n", + " \"derived\": lambda table: weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " layoutMode=\"horizontal\",\n", + " items={\n", + " \"rows\": weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"pinned_rows\": weave.legacy.weave.panels.Plot(table.pinned_rows(),\n", + " x=lambda row: row['c_0'],\n", + " y=lambda row: row['c_1']\n", + " ),\n", + " \"active_row\": table.active_row(),\n", + " }\n", + " ),\n", + " \"data\": weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"pinned_data\": table.pinned_data(),\n", + " \"active_data\": table.active_data(),\n", + " }\n", + " )\n", + " }) \n", + " })\n", + "\n", + "panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6b83817", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " items={\n", - " \"table\": weave.legacy.panels.Table(weave.legacy.ops.range(1, 100, 1), \n", - " columns=[\n", - " lambda row: row,\n", - " lambda row: row ** 2,\n", - " ],\n", - " ),\n", - " \"all_rows\": lambda table: weave.legacy.panels.Plot(table.all_rows(),\n", - " x=lambda row: row['c_0'],\n", - " y=lambda row: row['c_1']\n", - " ),\n", - " \"derived\": lambda table: weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " layoutMode=\"horizontal\",\n", - " items={\n", - " \"rows\": weave.legacy.panels.Group(\n", - " items={\n", - " \"pinned_rows\": weave.legacy.panels.Plot(table.pinned_rows(),\n", - " x=lambda row: row['c_0'],\n", - " y=lambda row: row['c_1']\n", - " ),\n", - " \"active_row\": table.active_row(),\n", - " }\n", - " ),\n", - " \"data\": weave.legacy.panels.Group(\n", - " items={\n", - " \"pinned_data\": table.pinned_data(),\n", - " \"active_data\": table.active_data(),\n", - " }\n", - " )\n", - " }) \n", - " })\n", - "\n", - "panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/experimental/tag_search.ipynb b/weave/legacy/examples/experimental/tag_search.ipynb index d28245ee8ad..e3e7ef3de53 100644 --- a/weave/legacy/examples/experimental/tag_search.ipynb +++ b/weave/legacy/examples/experimental/tag_search.ipynb @@ -1,110 +1,110 @@ { - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "550a2af8", - "metadata": {}, - "source": [ - "## An example implementation of tag search\n", - "\n", - "This shows how to achieve a UI for tag search.\n", - "\n", - "Its not ideal in these ways:\n", - "- we're missing an intersection op.\n", - "- we'll need to be sure the pattern vectorizes\n", - "- list of tag strings may not be the best representation for performance at scale\n", - "- we don't have a nice way to display list of strings as tags\n", - "- no centralized tag color control\n", - "- you should be able to click on a tag and filter the table down to that tag\n", - "- varbar construction of controls is still pretty odd\n", - "\n", - "But it works!\n" - ] + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "550a2af8", + "metadata": {}, + "source": [ + "## An example implementation of tag search\n", + "\n", + "This shows how to achieve a UI for tag search.\n", + "\n", + "Its not ideal in these ways:\n", + "- we're missing an intersection op.\n", + "- we'll need to be sure the pattern vectorizes\n", + "- list of tag strings may not be the best representation for performance at scale\n", + "- we don't have a nice way to display list of strings as tags\n", + "- no centralized tag color control\n", + "- you should be able to click on a tag and filter the table down to that tag\n", + "- varbar construction of controls is still pretty odd\n", + "\n", + "But it works!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c690522", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import weave_internal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd9ad6f9", + "metadata": {}, + "outputs": [], + "source": [ + "data = weave.save([\n", + " {'x': 15, 'tags': ['a', 'b']},\n", + " {'x': 23.9, 'tags': ['b', 'c']},\n", + " {'x': -8, 'tags': ['a']}\n", + "], 'taggeddata')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34fb2bba", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={\n", + " # The strange that we need to use two variables for structuring our tag picker and its value.\n", + " 'filter_tags': [],\n", + " 'tag_picker': lambda filter_tags: weave.legacy.weave.panels.SelectEditor(\n", + " filter_tags,\n", + " choices=weave_internal.const(['a', 'b', 'c'])\n", + " ),\n", + " 'data': data,\n", + " 'filtered_data': lambda data, filter_tags: data.filter(\n", + " # We don't currently have an intersection op, so we have to write the logic for it here.\n", + " # This will probably not vectorize currently, so will be slower on large amounts of data.\n", + " lambda row: weave.legacy.weave.ops.List.concat(weave.legacy.weave.ops.make_list(a=row['tags'], b=filter_tags)).unique().count()\n", + " < (row['tags'].count() + filter_tags.count())\n", + " )\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda filtered_data: weave.legacy.weave.panels.Table(\n", + " filtered_data,\n", + " columns=[\n", + " lambda row: row['x'],\n", + " # Use json_dumps to get a better view for now.\n", + " lambda row: row['tags'].json_dumps()\n", + " ]\n", + " )\n", + " )\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c690522", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import weave_internal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd9ad6f9", - "metadata": {}, - "outputs": [], - "source": [ - "data = weave.save([\n", - " {'x': 15, 'tags': ['a', 'b']},\n", - " {'x': 23.9, 'tags': ['b', 'c']},\n", - " {'x': -8, 'tags': ['a']}\n", - "], 'taggeddata')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34fb2bba", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={\n", - " # The strange that we need to use two variables for structuring our tag picker and its value.\n", - " 'filter_tags': [],\n", - " 'tag_picker': lambda filter_tags: weave.legacy.panels.SelectEditor(\n", - " filter_tags,\n", - " choices=weave_internal.const(['a', 'b', 'c'])\n", - " ),\n", - " 'data': data,\n", - " 'filtered_data': lambda data, filter_tags: data.filter(\n", - " # We don't currently have an intersection op, so we have to write the logic for it here.\n", - " # This will probably not vectorize currently, so will be slower on large amounts of data.\n", - " lambda row: weave.legacy.ops.List.concat(weave.legacy.ops.make_list(a=row['tags'], b=filter_tags)).unique().count()\n", - " < (row['tags'].count() + filter_tags.count())\n", - " )\n", - " },\n", - " panels=[\n", - " weave.legacy.panels.BoardPanel(\n", - " lambda filtered_data: weave.legacy.panels.Table(\n", - " filtered_data,\n", - " columns=[\n", - " lambda row: row['x'],\n", - " # Use json_dumps to get a better view for now.\n", - " lambda row: row['tags'].json_dumps()\n", - " ]\n", - " )\n", - " )\n", - " ]\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/get_started.ipynb b/weave/legacy/examples/get_started.ipynb index b8b5a4f101a..8b8ffbd2bbb 100644 --- a/weave/legacy/examples/get_started.ipynb +++ b/weave/legacy/examples/get_started.ipynb @@ -1,301 +1,301 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "81c835f8", - "metadata": {}, - "source": [ - "# Get Started building with Weave\n", - "\n", - "In this tutorial, you will learn how to use Weave to build an interactive dashboard to help you explore, understand, and evaluate machine learning models.\n", - "\n", - "After this tutorials, you'll know how to build a dashboard to:\n", - "\n", - "- explore inputs, outputs & labels of your models\n", - "- calculate and plot metrics about your models\n", - "- interactively build live, connected plots to drill deeper into your data\n", - "- publish sharable interactive dashboards to enable colleagues to build on your work\n", - "\n", - "# Hello, World\n", - "\n", - "First, install `weave`:\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "81c835f8", + "metadata": {}, + "source": [ + "# Get Started building with Weave\n", + "\n", + "In this tutorial, you will learn how to use Weave to build an interactive dashboard to help you explore, understand, and evaluate machine learning models.\n", + "\n", + "After this tutorials, you'll know how to build a dashboard to:\n", + "\n", + "- explore inputs, outputs & labels of your models\n", + "- calculate and plot metrics about your models\n", + "- interactively build live, connected plots to drill deeper into your data\n", + "- publish sharable interactive dashboards to enable colleagues to build on your work\n", + "\n", + "# Hello, World\n", + "\n", + "First, install `weave`:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e45d5578", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install weave -qqq" + ] + }, + { + "cell_type": "markdown", + "id": "56c791d0", + "metadata": {}, + "source": [ + "Next, we'll use Weave to explore the Iris flower dataset.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2f76045", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.datasets import load_iris\n", + "\n", + "def get_iris_dataset():\n", + " iris = load_iris(as_frame=True)\n", + " data = iris['data']\n", + " data['target_id'] = iris['target']\n", + " data['target_name'] = iris['target_names'][iris['target']]\n", + "\n", + " def clean_column_name(c):\n", + " return c.replace(\" (cm)\", \"\").replace(\" \", \"_\")\n", + " data.columns = [clean_column_name(c) for c in data.columns]\n", + " return data\n", + "\n", + "iris_df = get_iris_dataset()" + ] + }, + { + "cell_type": "markdown", + "id": "ae1d5797", + "metadata": {}, + "source": [ + "Now that we have a dataframe, we can display it in an interactive table by calling `weave.show`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84f1a891", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.show import show\n", + "show(iris_df)" + ] + }, + { + "cell_type": "markdown", + "id": "091192b7-ede4-423f-a78d-b06a9b877bab", + "metadata": {}, + "source": [ + "This is already a lot more powerful that the static visualization of a pandas `DataFrame` because you can page through it——and we're just getting started.\n", + "\n", + "# Building plots\n", + "\n", + "Weave can dynamically create visualizations of your data.\n", + "\n", + "- At the top of the panel, click `Table`\n", + "- You should see a dropdown, click `Plot`\n", + "\n", + "You should now see a plot of `sepal_width` versus `sepal_length`, colored by `target_name`. Weave's visualization engine uses the type of each column to choose a reasonable way to display your data. You can edit this plot by clicking the pencil icon ✏️ in the top right of the panel to open the panel editor.\n", + "\n", + "Note: You can build these plots interactively using the UI or define them programmatically in Python. In this tutorial, we'll focus on building these dashboards using the UI and keep programmatic dashboard building for later tutorials.\n" + ] + }, + { + "cell_type": "markdown", + "id": "9866379b", + "metadata": {}, + "source": [ + "!['image'](https://raw.githubusercontent.com/wandb/weave/6936cd8233f382ae3fd165480dce5832c06c2453/docs/assets/get_started_0.png)\n" + ] + }, + { + "cell_type": "markdown", + "id": "bd62be8e", + "metadata": {}, + "source": [ + "## Exercise\n", + "\n", + "- Try changing the X and Y axes\n", + "- Try changing the color\n", + "\n", + "Tip: `row[\"\"]` is a **Weave Expression**. You can edit the column name inside the quotes to show the relevant column from your data.\n", + "\n", + "# Exploring Tables\n", + "\n", + "Let's display our data as a `Table` again:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "886c7c49-e7f8-4651-8a6a-b2a08d417dda", + "metadata": {}, + "outputs": [], + "source": [ + "show(iris_df)" + ] + }, + { + "cell_type": "markdown", + "id": "6d12837d", + "metadata": {}, + "source": [ + "You can add, remove and sort each column by using the three dots button ⋮ beside the column name.\n", + "\n", + "## Exercise\n", + "\n", + "- Try sorting by the `sepal_length` column\n", + "- Try removing the `target_id` column\n", + "\n", + "## Filtering\n", + "\n", + "You can filter tables using the funnel icon ▼ on the top left.\n", + "\n", + "For example, to filter the data to only display the `setosa` flowers:\n", + "\n", + "- Click the filter icon\n", + "- Add the expression `row[\"target_name\"] == \"setosa\"`\n", + "\n", + "# Transforming data\n", + "\n", + "When analyzing models, you often need to use your data to calculate metrics or statistics to get summaries for comparison.\n", + "\n", + "To enable this, Weave has built-in operations, or **Weave Ops**, to transform data. You can also configure the data displayed in each column as we did with the plot.\n", + "\n", + "For example, to display the sum of each examples' `sepal_width` and `sepal_length`:\n", + "\n", + "- Click a column to edit (e.g. `sepal_length`)\n", + "- Change the Cell expression to `row[\"sepal_width\"] + row[\"sepal_length\"]`\n", + "- Click anywhere outside the popover to apply the change\n", + "\n", + "# Group by columns to compare splits and calculate summary statistics\n", + "\n", + "Comparing performance across different data splits is vital to uncovering biases and edge cases. This is fast and visual in Weave using the **Group by** operation.\n", + "\n", + "For example, if we want to calculate summary statistics of each type of flower in our dataset, we can group by the `target_name` column.\n", + "\n", + "- Click the three dots button ⋮ on the `target_name` column\n", + "- Click `Group by`\n", + "\n", + "We now see that Weave has grouped our data by the `target_name` and made plots for each column. We can now use different operations on these columns because they're now _lists_ rather than individual elements.\n", + "\n", + "For example, we can calculate the average of each column by changing the cell expression to be `row[\"\"].avg` e.g. `row[\"sepal_length\"].avg`.\n", + "\n", + "## Exercise\n", + "\n", + "- Try calculating the maximum of each column\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0954b735", + "metadata": {}, + "source": [ + "!['image'](https://raw.githubusercontent.com/wandb/weave/4885095070704922189ba4d761029821c4e5da42/docs/assets/get_started_1.png)\n" + ] + }, + { + "cell_type": "markdown", + "id": "60acbe66", + "metadata": {}, + "source": [ + "## Drilldown with connected plots\n", + "\n", + "Finally, once you have found a split of data that is interesting, you often want to view individual elements of that data to better understand it. Weave makes it easy to connect multiple visualizations so that you can drilldown into selected data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "531013bb", + "metadata": {}, + "outputs": [], + "source": [ + "show(iris_df)" + ] + }, + { + "cell_type": "markdown", + "id": "2519db1c", + "metadata": {}, + "source": [ + "!['image'](https://raw.githubusercontent.com/wandb/weave/4885095070704922189ba4d761029821c4e5da42/docs/assets/get_started_2.png)\n" + ] + }, + { + "cell_type": "markdown", + "id": "ddad55c3", + "metadata": {}, + "source": [ + "For example, to select data from a plot and display it in a table:\n", + "\n", + "- Add a new panel from the drawer menu on the right (1)\n", + "- Using the panel name (2), refer to the `.selected_data` from the first panel in the expression editor (3)\n", + "- Use the selection tool (4) to display that data in the drilldown table\n", + "\n", + "Tip: You can also connect `Tables` to other plots. To select a row on a table, click the row number. To use that selection, use `.active_data`.\n", + "\n", + "# Publish your dashboard and share it\n", + "\n", + "Once you have something you're happy with, you can click the arrow ↗️ from the drawer menu on the right to open it in a new tab. From there, you can click \"Publish board\" to send your creation to the cloud and share it with the world. You can also use this area to add new panels and update your dashboard.\n", + "\n", + "# What's next?\n", + "\n", + "With what you've learned, try build something on your own data. Weave works with lots of different types of data and is performant at large scales!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2011a09", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy\n", + "from PIL import Image\n", + "\n", + "def random_image():\n", + " imarray = numpy.random.rand(100,100,3) * 255\n", + " return Image.fromarray(imarray.astype('uint8')).convert('RGBA')\n", + "\n", + "show([{'image_col': random_image(), 'text_col': 'Hello'}, \n", + " {'image_col': random_image(), 'text_col': 'World'}]*1000)" + ] + }, + { + "cell_type": "markdown", + "id": "6db51292", + "metadata": {}, + "source": [ + "We're heads down focusing on adding templates and improving our documentation. Weave is open source, and we're very excited to merge your contributions. If you have feedback, feature requests, or bugs, you can open an issue on the Weave Github repository.\n", + "\n", + "## Not finished learning?\n", + "\n", + "Check out our [examples](https://github.com/wandb/weave/tree/master/examples) to see more tutorials, a gallery of dashboards, and reference guides to different components.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "e45d5578", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install weave -qqq" - ] - }, - { - "cell_type": "markdown", - "id": "56c791d0", - "metadata": {}, - "source": [ - "Next, we'll use Weave to explore the Iris flower dataset.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2f76045", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.datasets import load_iris\n", - "\n", - "def get_iris_dataset():\n", - " iris = load_iris(as_frame=True)\n", - " data = iris['data']\n", - " data['target_id'] = iris['target']\n", - " data['target_name'] = iris['target_names'][iris['target']]\n", - "\n", - " def clean_column_name(c):\n", - " return c.replace(\" (cm)\", \"\").replace(\" \", \"_\")\n", - " data.columns = [clean_column_name(c) for c in data.columns]\n", - " return data\n", - "\n", - "iris_df = get_iris_dataset()" - ] - }, - { - "cell_type": "markdown", - "id": "ae1d5797", - "metadata": {}, - "source": [ - "Now that we have a dataframe, we can display it in an interactive table by calling `weave.show`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "84f1a891", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.show import show\n", - "show(iris_df)" - ] - }, - { - "cell_type": "markdown", - "id": "091192b7-ede4-423f-a78d-b06a9b877bab", - "metadata": {}, - "source": [ - "This is already a lot more powerful that the static visualization of a pandas `DataFrame` because you can page through it——and we're just getting started.\n", - "\n", - "# Building plots\n", - "\n", - "Weave can dynamically create visualizations of your data.\n", - "\n", - "- At the top of the panel, click `Table`\n", - "- You should see a dropdown, click `Plot`\n", - "\n", - "You should now see a plot of `sepal_width` versus `sepal_length`, colored by `target_name`. Weave's visualization engine uses the type of each column to choose a reasonable way to display your data. You can edit this plot by clicking the pencil icon ✏️ in the top right of the panel to open the panel editor.\n", - "\n", - "Note: You can build these plots interactively using the UI or define them programmatically in Python. In this tutorial, we'll focus on building these dashboards using the UI and keep programmatic dashboard building for later tutorials.\n" - ] - }, - { - "cell_type": "markdown", - "id": "9866379b", - "metadata": {}, - "source": [ - "!['image'](https://raw.githubusercontent.com/wandb/weave/6936cd8233f382ae3fd165480dce5832c06c2453/docs/assets/get_started_0.png)\n" - ] - }, - { - "cell_type": "markdown", - "id": "bd62be8e", - "metadata": {}, - "source": [ - "## Exercise\n", - "\n", - "- Try changing the X and Y axes\n", - "- Try changing the color\n", - "\n", - "Tip: `row[\"\"]` is a **Weave Expression**. You can edit the column name inside the quotes to show the relevant column from your data.\n", - "\n", - "# Exploring Tables\n", - "\n", - "Let's display our data as a `Table` again:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "886c7c49-e7f8-4651-8a6a-b2a08d417dda", - "metadata": {}, - "outputs": [], - "source": [ - "show(iris_df)" - ] - }, - { - "cell_type": "markdown", - "id": "6d12837d", - "metadata": {}, - "source": [ - "You can add, remove and sort each column by using the three dots button ⋮ beside the column name.\n", - "\n", - "## Exercise\n", - "\n", - "- Try sorting by the `sepal_length` column\n", - "- Try removing the `target_id` column\n", - "\n", - "## Filtering\n", - "\n", - "You can filter tables using the funnel icon ▼ on the top left.\n", - "\n", - "For example, to filter the data to only display the `setosa` flowers:\n", - "\n", - "- Click the filter icon\n", - "- Add the expression `row[\"target_name\"] == \"setosa\"`\n", - "\n", - "# Transforming data\n", - "\n", - "When analyzing models, you often need to use your data to calculate metrics or statistics to get summaries for comparison.\n", - "\n", - "To enable this, Weave has built-in operations, or **Weave Ops**, to transform data. You can also configure the data displayed in each column as we did with the plot.\n", - "\n", - "For example, to display the sum of each examples' `sepal_width` and `sepal_length`:\n", - "\n", - "- Click a column to edit (e.g. `sepal_length`)\n", - "- Change the Cell expression to `row[\"sepal_width\"] + row[\"sepal_length\"]`\n", - "- Click anywhere outside the popover to apply the change\n", - "\n", - "# Group by columns to compare splits and calculate summary statistics\n", - "\n", - "Comparing performance across different data splits is vital to uncovering biases and edge cases. This is fast and visual in Weave using the **Group by** operation.\n", - "\n", - "For example, if we want to calculate summary statistics of each type of flower in our dataset, we can group by the `target_name` column.\n", - "\n", - "- Click the three dots button ⋮ on the `target_name` column\n", - "- Click `Group by`\n", - "\n", - "We now see that Weave has grouped our data by the `target_name` and made plots for each column. We can now use different operations on these columns because they're now _lists_ rather than individual elements.\n", - "\n", - "For example, we can calculate the average of each column by changing the cell expression to be `row[\"\"].avg` e.g. `row[\"sepal_length\"].avg`.\n", - "\n", - "## Exercise\n", - "\n", - "- Try calculating the maximum of each column\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "0954b735", - "metadata": {}, - "source": [ - "!['image'](https://raw.githubusercontent.com/wandb/weave/4885095070704922189ba4d761029821c4e5da42/docs/assets/get_started_1.png)\n" - ] - }, - { - "cell_type": "markdown", - "id": "60acbe66", - "metadata": {}, - "source": [ - "## Drilldown with connected plots\n", - "\n", - "Finally, once you have found a split of data that is interesting, you often want to view individual elements of that data to better understand it. Weave makes it easy to connect multiple visualizations so that you can drilldown into selected data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "531013bb", - "metadata": {}, - "outputs": [], - "source": [ - "show(iris_df)" - ] - }, - { - "cell_type": "markdown", - "id": "2519db1c", - "metadata": {}, - "source": [ - "!['image'](https://raw.githubusercontent.com/wandb/weave/4885095070704922189ba4d761029821c4e5da42/docs/assets/get_started_2.png)\n" - ] - }, - { - "cell_type": "markdown", - "id": "ddad55c3", - "metadata": {}, - "source": [ - "For example, to select data from a plot and display it in a table:\n", - "\n", - "- Add a new panel from the drawer menu on the right (1)\n", - "- Using the panel name (2), refer to the `.selected_data` from the first panel in the expression editor (3)\n", - "- Use the selection tool (4) to display that data in the drilldown table\n", - "\n", - "Tip: You can also connect `Tables` to other plots. To select a row on a table, click the row number. To use that selection, use `.active_data`.\n", - "\n", - "# Publish your dashboard and share it\n", - "\n", - "Once you have something you're happy with, you can click the arrow ↗️ from the drawer menu on the right to open it in a new tab. From there, you can click \"Publish board\" to send your creation to the cloud and share it with the world. You can also use this area to add new panels and update your dashboard.\n", - "\n", - "# What's next?\n", - "\n", - "With what you've learned, try build something on your own data. Weave works with lots of different types of data and is performant at large scales!\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2011a09", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy\n", - "from PIL import Image\n", - "\n", - "def random_image():\n", - " imarray = numpy.random.rand(100,100,3) * 255\n", - " return Image.fromarray(imarray.astype('uint8')).convert('RGBA')\n", - "\n", - "show([{'image_col': random_image(), 'text_col': 'Hello'}, \n", - " {'image_col': random_image(), 'text_col': 'World'}]*1000)" - ] - }, - { - "cell_type": "markdown", - "id": "6db51292", - "metadata": {}, - "source": [ - "We're heads down focusing on adding templates and improving our documentation. Weave is open source, and we're very excited to merge your contributions. If you have feedback, feature requests, or bugs, you can open an issue on the Weave Github repository.\n", - "\n", - "## Not finished learning?\n", - "\n", - "Check out our [examples](https://github.com/wandb/weave/tree/master/examples) to see more tutorials, a gallery of dashboards, and reference guides to different components.\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/WB_API.ipynb b/weave/legacy/examples/reference/WB_API.ipynb index 04747e092f6..d97e0eb952c 100644 --- a/weave/legacy/examples/reference/WB_API.ipynb +++ b/weave/legacy/examples/reference/WB_API.ipynb @@ -1,89 +1,89 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "eae3e8dd", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy.panels\n", - "import weave.legacy.ops\n", - "from weave.legacy.show import show" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "eae3e8dd", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave.panels\n", + "import weave.legacy.weave.ops\n", + "from weave.legacy.weave.show import show" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "887d88a4", + "metadata": {}, + "outputs": [], + "source": [ + "project = weave.legacy.weave.ops.project('stacey', \"mendeleev\")\n", + "#project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7705fb7", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Table(weave.legacy.weave.ops.project('stacey', \"mendeleev\").artifactType('test_results').artifacts(),\n", + " columns=[\n", + " lambda art: weave.legacy.weave.ops.artifact_collection_ops.artifact_name(art), # hack to get around shadowed `name` op\n", + " lambda art: art.versions().count()\n", + " ])\n", + "show(panel)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58b26e21", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.project('stacey', \"mendeleev\").artifactType('test_results').artifacts()[0].versions()[0].file('test_results.table.json')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "debbb5d4", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.ops.project('shawn', \"dsviz-simple-tables\").artifactType('dataset').artifacts()[0].versions()[5].file('my-table.table.json')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "vscode": { + "interpreter": { + "hash": "cdaa5b4f919dd2bcc1e1b23efc336c7178fbd1227e862ece13b7d448295c4271" + } + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "887d88a4", - "metadata": {}, - "outputs": [], - "source": [ - "project = weave.legacy.ops.project('stacey', \"mendeleev\")\n", - "#project" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7705fb7", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Table(weave.legacy.ops.project('stacey', \"mendeleev\").artifactType('test_results').artifacts(),\n", - " columns=[\n", - " lambda art: weave.legacy.ops.artifact_collection_ops.artifact_name(art), # hack to get around shadowed `name` op\n", - " lambda art: art.versions().count()\n", - " ])\n", - "show(panel)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "58b26e21", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.project('stacey', \"mendeleev\").artifactType('test_results').artifacts()[0].versions()[0].file('test_results.table.json')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "debbb5d4", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.ops.project('shawn', \"dsviz-simple-tables\").artifactType('dataset').artifacts()[0].versions()[5].file('my-table.table.json')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - }, - "vscode": { - "interpreter": { - "hash": "cdaa5b4f919dd2bcc1e1b23efc336c7178fbd1227e862ece13b7d448295c4271" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/confusion_matrix.ipynb b/weave/legacy/examples/reference/confusion_matrix.ipynb index 415c75a743a..e0305d4fd6f 100644 --- a/weave/legacy/examples/reference/confusion_matrix.ipynb +++ b/weave/legacy/examples/reference/confusion_matrix.ipynb @@ -1,234 +1,234 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "562cca2e-fd23-4725-9c04-416fde40aadc", - "metadata": {}, - "source": [ - "# Create an Interactive Confusion Matrix in Weave\n", - "\n", - "In this tutorial, we will set up an interactive confusion matrix Weave panel using Weave expressions and Python):\n", - "* pick an existing W&B Table of predictions\n", - "* load this data into Weave locally\n", - "* programmatically configure two Weave Panels: a Facet panel for the confusion matrix grid and an active selection panel to show the details of items in that cell of the confusion matrix\n", - "\n", - "\n", - "\n", - "# Step 0: Setup\n", - "\n", - "Install dependencies and login to W&B to save & publish your work." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "562cca2e-fd23-4725-9c04-416fde40aadc", + "metadata": {}, + "source": [ + "# Create an Interactive Confusion Matrix in Weave\n", + "\n", + "In this tutorial, we will set up an interactive confusion matrix Weave panel using Weave expressions and Python):\n", + "* pick an existing W&B Table of predictions\n", + "* load this data into Weave locally\n", + "* programmatically configure two Weave Panels: a Facet panel for the confusion matrix grid and an active selection panel to show the details of items in that cell of the confusion matrix\n", + "\n", + "\n", + "\n", + "# Step 0: Setup\n", + "\n", + "Install dependencies and login to W&B to save & publish your work." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8544cf72", + "metadata": {}, + "outputs": [], + "source": [ + "# if not already installed\n", + "!pip install -qqq weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b86972f-d8d7-4f5a-bf5a-9a6be887978c", + "metadata": {}, + "outputs": [], + "source": [ + "import wandb\n", + "wandb.login()\n", + "import weave\n", + "import weave.legacy.weave.panels" + ] + }, + { + "cell_type": "markdown", + "id": "b35a8e31-3f51-43a9-bf89-543ed6fe62e8", + "metadata": {}, + "source": [ + "Use our public example or substitute your own W&B Tables by replacing the fields below. You can find the relevant information by navigating to a project in W&B, clicking on the \"Artifacts\" tab, and browsing using the sidebar until you find the artifact type and corresponding table file of interest (e.g. [this one used in the example](https://wandb.ai/stacey/mendeleev/artifacts/test_results/test_res_1fwmcd3q/v0/files/test_results.table.json)).\n", + "\n", + "**Note:** the index '0' after `artifacts()` and `versions()` will take the first element in those lists." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01183f54-efa6-405a-be6a-142f75540913", + "metadata": {}, + "outputs": [], + "source": [ + "ENTITY = \"stacey\"\n", + "PROJECT = \"mendeleev\"\n", + "ARTIFACT_TYPE = \"test_results\"\n", + "TABLE_FILE = \"test_results.table.json\"" + ] + }, + { + "cell_type": "markdown", + "id": "2c910260-a5c0-4c99-bd00-1b9b95ddde54", + "metadata": {}, + "source": [ + "# Step 1: Compose Weave expression to load W&B Table\n", + "\n", + "Navigate the W&B object tree programmatically with Weave\n", + "* start with the specified W&B project\n", + "* fetch all artifacts of the specified type and take the first artifact\n", + "* fetch all the versions of that artifact and take the first one\n", + "* load the specified W&B Table file associated with that artifact version\n", + "* convert that file into a Weave table (with `.table.rows()`)\n", + "\n", + "The second cell below will display an interactive Weave Panel of the corresponding Table. You can interact with this panel in the notebook as you would with the underlying W&B Table in the cloud UI: sort, filter, group, etc. You can also expand the panel to open it in a Weave Board in order to iterate further/save and publish your work." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f5447d2", + "metadata": {}, + "outputs": [], + "source": [ + "predictions = (weave.legacy.weave.ops.project(ENTITY, PROJECT)\n", + " .artifactType(ARTIFACT_TYPE)\n", + " .artifacts()[0]\n", + " .versions()[0]\n", + " .file(TABLE_FILE)\n", + " .table()\n", + " .rows())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e414990d", + "metadata": {}, + "outputs": [], + "source": [ + "predictions" + ] + }, + { + "cell_type": "markdown", + "id": "9ee223ef-1a97-46f6-9933-24cdb78d2488", + "metadata": {}, + "source": [ + "# Step 2: Configure a Weave Panel Group in code\n", + "\n", + "We write a function to create a group of panels in Weave:\n", + "* Group panel: this is a container to arrange a set of panels — in our case, in equal sizes horizontally\n", + "* Facet panel: this is the main 2D grid of the confusion matrix, showing the `truth` column (correct label) on the x-axis and the `guess` column (model's prediction) on the y-axis\n", + "* each cell of the Facet Panel is again a Group with two layers of panels: 1) a Number panel showing the total number of rows/model predictions in that cell of the confusion matrix, and 2) a background Color panel with the count from 1) rescaled on a blue hue, so the more saturated the blue, the more items in that cell\n", + "* a final Table panel to show the full rows/details of any selected cell from the main Facet panel." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a940dab4", + "metadata": {}, + "outputs": [], + "source": [ + "def confusion_matrix(table_data):\n", + " conf_mat = weave.legacy.weave.panels.Group(\n", + " equalSize=True,\n", + " preferHorizontal=True,\n", + " items={\n", + " 'confusion': weave.legacy.weave.panels.Facet(\n", + " table_data,\n", + " x=lambda row: row[\"truth\"],\n", + " y=lambda row: row[\"guess\"],\n", + " select=lambda row: weave.legacy.weave.panels.Group(\n", + " layoutMode='layer',\n", + " items={\n", + " 'count': row.count(),\n", + " 'color': weave.legacy.weave.panels.Color(row.count() / 50)\n", + " }\n", + " )\n", + " ),\n", + " 'selected': lambda confusion: confusion.selected()\n", + " }\n", + " )\n", + " return conf_mat" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccf9a5a9-07b0-4967-a5ee-9255f9660ee3", + "metadata": {}, + "outputs": [], + "source": [ + "confusion_matrix(predictions)" + ] + }, + { + "cell_type": "markdown", + "id": "050f6119-7933-4084-9a9c-edb829c6c075", + "metadata": {}, + "source": [ + "# Step 3: Filter for incorrect predictions\n", + "\n", + "We can add a filter predicate to the Weave expression to select only the rows where the model's prediction doesn't match the correct label. Note that the highest-count / most-blue squares are absent in this version.\n", + "\n", + "Try opening this panel as a Weave Board by hovering over the right side and clicking \"Open in new tab\". You can explore more cells, add more panels, and share your work. Let us know if you find any interesting mistakes or patterns—for example, the mammal confused for an arachnid :)\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75d50c33-9712-4ffe-9971-5552d32648bc", + "metadata": {}, + "outputs": [], + "source": [ + "mistakes_only = predictions.filter(lambda row: row[\"truth\"] != row[\"guess\"])\n", + "confusion_matrix(mistakes_only)" + ] + }, + { + "cell_type": "markdown", + "id": "9e4195fe-09cb-48e8-99a0-e01acf75e6c2", + "metadata": {}, + "source": [ + "# Bonus: Make a confusion matrix from the UI only\n", + "\n", + "To create a confusion matrix from the UI only:\n", + "1. Start with a Weave Table object saved with `weave.save()` and loaded into a panel with an expression of the form `get(\"wandb-artifact:///your_entity/your_project/obj:0123456789abcde/obj\")`.\n", + "2. Click the pencil \"Edit\" icon on the panel to open the panel config menu.\n", + "3. Change the Panel type to \"Facet\"—this is the main confusion matrix layout.\n", + "4. Set the X and Y axes for the intended confusion matrix, e.g to `row[\"truth\"]` and `row[\"guess\"]` for this example.\n", + "5. In the lower \"Properties\" section, change the child panel type from \"Expression\" to \"Group\", then change the layout from \"vertical\" to \"layer\" and select \"Add child\"—each child is a cell of the confusion matrix which \"groups\" or \"layers\" the count of items in that cell and the corresponding background color of the cell.\n", + "6. The \"Input\" of one child can be the count: enter `row.count` as the Weave expression. When you hit \"Run\", \"Panel type\" should update to \"Number\" and you should see numbers render in the confusion matrix panel.\n", + "7. Click \"Add Child\" and optionally add the cell background. Change \"Panel type\" to \"Color\" and enter `row.count / N` as the \"Input\", where N is some normalizing constant to scale your values from 0 for white to ~1.5 for the highest-saturation blue.\n", + "\n", + "Your settings menu should look something like this:\n", + "\n", + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "8544cf72", - "metadata": {}, - "outputs": [], - "source": [ - "# if not already installed\n", - "!pip install -qqq weave" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b86972f-d8d7-4f5a-bf5a-9a6be887978c", - "metadata": {}, - "outputs": [], - "source": [ - "import wandb\n", - "wandb.login()\n", - "import weave\n", - "import weave.legacy.panels" - ] - }, - { - "cell_type": "markdown", - "id": "b35a8e31-3f51-43a9-bf89-543ed6fe62e8", - "metadata": {}, - "source": [ - "Use our public example or substitute your own W&B Tables by replacing the fields below. You can find the relevant information by navigating to a project in W&B, clicking on the \"Artifacts\" tab, and browsing using the sidebar until you find the artifact type and corresponding table file of interest (e.g. [this one used in the example](https://wandb.ai/stacey/mendeleev/artifacts/test_results/test_res_1fwmcd3q/v0/files/test_results.table.json)).\n", - "\n", - "**Note:** the index '0' after `artifacts()` and `versions()` will take the first element in those lists." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01183f54-efa6-405a-be6a-142f75540913", - "metadata": {}, - "outputs": [], - "source": [ - "ENTITY = \"stacey\"\n", - "PROJECT = \"mendeleev\"\n", - "ARTIFACT_TYPE = \"test_results\"\n", - "TABLE_FILE = \"test_results.table.json\"" - ] - }, - { - "cell_type": "markdown", - "id": "2c910260-a5c0-4c99-bd00-1b9b95ddde54", - "metadata": {}, - "source": [ - "# Step 1: Compose Weave expression to load W&B Table\n", - "\n", - "Navigate the W&B object tree programmatically with Weave\n", - "* start with the specified W&B project\n", - "* fetch all artifacts of the specified type and take the first artifact\n", - "* fetch all the versions of that artifact and take the first one\n", - "* load the specified W&B Table file associated with that artifact version\n", - "* convert that file into a Weave table (with `.table.rows()`)\n", - "\n", - "The second cell below will display an interactive Weave Panel of the corresponding Table. You can interact with this panel in the notebook as you would with the underlying W&B Table in the cloud UI: sort, filter, group, etc. You can also expand the panel to open it in a Weave Board in order to iterate further/save and publish your work." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f5447d2", - "metadata": {}, - "outputs": [], - "source": [ - "predictions = (weave.legacy.ops.project(ENTITY, PROJECT)\n", - " .artifactType(ARTIFACT_TYPE)\n", - " .artifacts()[0]\n", - " .versions()[0]\n", - " .file(TABLE_FILE)\n", - " .table()\n", - " .rows())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e414990d", - "metadata": {}, - "outputs": [], - "source": [ - "predictions" - ] - }, - { - "cell_type": "markdown", - "id": "9ee223ef-1a97-46f6-9933-24cdb78d2488", - "metadata": {}, - "source": [ - "# Step 2: Configure a Weave Panel Group in code\n", - "\n", - "We write a function to create a group of panels in Weave:\n", - "* Group panel: this is a container to arrange a set of panels — in our case, in equal sizes horizontally\n", - "* Facet panel: this is the main 2D grid of the confusion matrix, showing the `truth` column (correct label) on the x-axis and the `guess` column (model's prediction) on the y-axis\n", - "* each cell of the Facet Panel is again a Group with two layers of panels: 1) a Number panel showing the total number of rows/model predictions in that cell of the confusion matrix, and 2) a background Color panel with the count from 1) rescaled on a blue hue, so the more saturated the blue, the more items in that cell\n", - "* a final Table panel to show the full rows/details of any selected cell from the main Facet panel." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a940dab4", - "metadata": {}, - "outputs": [], - "source": [ - "def confusion_matrix(table_data):\n", - " conf_mat = weave.legacy.panels.Group(\n", - " equalSize=True,\n", - " preferHorizontal=True,\n", - " items={\n", - " 'confusion': weave.legacy.panels.Facet(\n", - " table_data,\n", - " x=lambda row: row[\"truth\"],\n", - " y=lambda row: row[\"guess\"],\n", - " select=lambda row: weave.legacy.panels.Group(\n", - " layoutMode='layer',\n", - " items={\n", - " 'count': row.count(),\n", - " 'color': weave.legacy.panels.Color(row.count() / 50)\n", - " }\n", - " )\n", - " ),\n", - " 'selected': lambda confusion: confusion.selected()\n", - " }\n", - " )\n", - " return conf_mat" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ccf9a5a9-07b0-4967-a5ee-9255f9660ee3", - "metadata": {}, - "outputs": [], - "source": [ - "confusion_matrix(predictions)" - ] - }, - { - "cell_type": "markdown", - "id": "050f6119-7933-4084-9a9c-edb829c6c075", - "metadata": {}, - "source": [ - "# Step 3: Filter for incorrect predictions\n", - "\n", - "We can add a filter predicate to the Weave expression to select only the rows where the model's prediction doesn't match the correct label. Note that the highest-count / most-blue squares are absent in this version.\n", - "\n", - "Try opening this panel as a Weave Board by hovering over the right side and clicking \"Open in new tab\". You can explore more cells, add more panels, and share your work. Let us know if you find any interesting mistakes or patterns—for example, the mammal confused for an arachnid :)\n", - "\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75d50c33-9712-4ffe-9971-5552d32648bc", - "metadata": {}, - "outputs": [], - "source": [ - "mistakes_only = predictions.filter(lambda row: row[\"truth\"] != row[\"guess\"])\n", - "confusion_matrix(mistakes_only)" - ] - }, - { - "cell_type": "markdown", - "id": "9e4195fe-09cb-48e8-99a0-e01acf75e6c2", - "metadata": {}, - "source": [ - "# Bonus: Make a confusion matrix from the UI only\n", - "\n", - "To create a confusion matrix from the UI only:\n", - "1. Start with a Weave Table object saved with `weave.save()` and loaded into a panel with an expression of the form `get(\"wandb-artifact:///your_entity/your_project/obj:0123456789abcde/obj\")`.\n", - "2. Click the pencil \"Edit\" icon on the panel to open the panel config menu.\n", - "3. Change the Panel type to \"Facet\"—this is the main confusion matrix layout.\n", - "4. Set the X and Y axes for the intended confusion matrix, e.g to `row[\"truth\"]` and `row[\"guess\"]` for this example.\n", - "5. In the lower \"Properties\" section, change the child panel type from \"Expression\" to \"Group\", then change the layout from \"vertical\" to \"layer\" and select \"Add child\"—each child is a cell of the confusion matrix which \"groups\" or \"layers\" the count of items in that cell and the corresponding background color of the cell.\n", - "6. The \"Input\" of one child can be the count: enter `row.count` as the Weave expression. When you hit \"Run\", \"Panel type\" should update to \"Number\" and you should see numbers render in the confusion matrix panel.\n", - "7. Click \"Add Child\" and optionally add the cell background. Change \"Panel type\" to \"Color\" and enter `row.count / N` as the \"Input\", where N is some normalizing constant to scale your values from 0 for white to ~1.5 for the highest-saturation blue.\n", - "\n", - "Your settings menu should look something like this:\n", - "\n", - "" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/control/Object Picker.ipynb b/weave/legacy/examples/reference/control/Object Picker.ipynb index 779f9eac812..abe0dca0244 100644 --- a/weave/legacy/examples/reference/control/Object Picker.ipynb +++ b/weave/legacy/examples/reference/control/Object Picker.ipynb @@ -1,121 +1,121 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f9712d25", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy.panels" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f9712d25", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave.panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a60bff3", + "metadata": {}, + "outputs": [], + "source": [ + "ints = weave.save([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2185cbbc", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " showExpressions=False,\n", + " items={\n", + " \"a\": weave.legacy.weave.panels.ObjectPicker(ints, label=\"a\"),\n", + " \"b\": weave.legacy.weave.panels.ObjectPicker(ints, label=\"b\"),\n", + " \"panel\": lambda a, b: a.config.choice + b.config.choice\n", + " })\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0f2f998", + "metadata": {}, + "outputs": [], + "source": [ + "# A more complex example with sidebar\n", + "\n", + "# ints = weave.legacy.weave.ops.objects(weave.types.Int(), 'latest', 9)\n", + "# weave.use(ints[0])\n", + "ints = weave.save([1, 2, 3], name='ints:latest')\n", + "panel = weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " equalSize=True,\n", + " items={\n", + " \"sidebar\": weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"a\": weave.legacy.weave.panels.ObjectPicker(ints, label=\"A\"),\n", + " \"b\": weave.legacy.weave.panels.ObjectPicker(ints, label=\"B\")\n", + " }\n", + " ),\n", + " \"main\": lambda sidebar: weave.legacy.weave.panels.Group(\n", + " items={\n", + " \"choice_values\": weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " style=\"height:150px\",\n", + " items={\n", + " 'choice_a': weave.legacy.weave.panels.LabeledItem(\n", + " label=\"A\",\n", + " item=sidebar.config.items['a'].config.choice.execute()\n", + " ),\n", + " 'choice_b': weave.legacy.weave.panels.LabeledItem(\n", + " label=\"B\",\n", + " item=sidebar.config.items['b'].config.choice.execute()\n", + " )\n", + " }\n", + " ),\n", + " \"aggregates\": weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " style=\"height:150px\",\n", + " items={\n", + " 'sum': weave.legacy.weave.panels.LabeledItem(\n", + " label='sum',\n", + " item=sidebar.config.items['a'].config.choice + sidebar.config.items['b'].config.choice\n", + " ),\n", + " 'product': weave.legacy.weave.panels.LabeledItem(\n", + " label='product',\n", + " item=sidebar.config.items['a'].config.choice * sidebar.config.items['b'].config.choice\n", + " )\n", + " }\n", + " )\n", + " }\n", + " )\n", + " })\n", + "panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a60bff3", - "metadata": {}, - "outputs": [], - "source": [ - "ints = weave.save([1, 2, 3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2185cbbc", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " showExpressions=False,\n", - " items={\n", - " \"a\": weave.legacy.panels.ObjectPicker(ints, label=\"a\"),\n", - " \"b\": weave.legacy.panels.ObjectPicker(ints, label=\"b\"),\n", - " \"panel\": lambda a, b: a.config.choice + b.config.choice\n", - " })\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a0f2f998", - "metadata": {}, - "outputs": [], - "source": [ - "# A more complex example with sidebar\n", - "\n", - "# ints = weave.legacy.ops.objects(weave.types.Int(), 'latest', 9)\n", - "# weave.use(ints[0])\n", - "ints = weave.save([1, 2, 3], name='ints:latest')\n", - "panel = weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " equalSize=True,\n", - " items={\n", - " \"sidebar\": weave.legacy.panels.Group(\n", - " items={\n", - " \"a\": weave.legacy.panels.ObjectPicker(ints, label=\"A\"),\n", - " \"b\": weave.legacy.panels.ObjectPicker(ints, label=\"B\")\n", - " }\n", - " ),\n", - " \"main\": lambda sidebar: weave.legacy.panels.Group(\n", - " items={\n", - " \"choice_values\": weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " style=\"height:150px\",\n", - " items={\n", - " 'choice_a': weave.legacy.panels.LabeledItem(\n", - " label=\"A\",\n", - " item=sidebar.config.items['a'].config.choice.execute()\n", - " ),\n", - " 'choice_b': weave.legacy.panels.LabeledItem(\n", - " label=\"B\",\n", - " item=sidebar.config.items['b'].config.choice.execute()\n", - " )\n", - " }\n", - " ),\n", - " \"aggregates\": weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " style=\"height:150px\",\n", - " items={\n", - " 'sum': weave.legacy.panels.LabeledItem(\n", - " label='sum',\n", - " item=sidebar.config.items['a'].config.choice + sidebar.config.items['b'].config.choice\n", - " ),\n", - " 'product': weave.legacy.panels.LabeledItem(\n", - " label='product',\n", - " item=sidebar.config.items['a'].config.choice * sidebar.config.items['b'].config.choice\n", - " )\n", - " }\n", - " )\n", - " }\n", - " )\n", - " })\n", - "panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/control/Slider.ipynb b/weave/legacy/examples/reference/control/Slider.ipynb index 434f22dddf9..43664e12d6d 100644 --- a/weave/legacy/examples/reference/control/Slider.ipynb +++ b/weave/legacy/examples/reference/control/Slider.ipynb @@ -1,75 +1,75 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "83fee798", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy import panels" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "83fee798", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c85e3b7", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncontrolled: panel creates its own state slot\n", + "panel = panels.Group(\n", + " items={\n", + " \"my_slider\": panels.Slider(),\n", + " \"val\": lambda my_slider: my_slider.value()\n", + " })\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0dd8dd51", + "metadata": {}, + "outputs": [], + "source": [ + "# Controlled: state is external to panel\n", + "\n", + "panel = panels.Group(\n", + " vars={'slider_val': 5},\n", + " items={\n", + " 'state_value': lambda slider_val: slider_val,\n", + " 'slider': lambda slider_val: panels.Group(\n", + " items={\n", + " \"my_slider\": panels.Slider(slider_val),\n", + " \"val\": lambda my_slider: my_slider.value(),\n", + " \"my_slider2\": panels.Slider(slider_val)\n", + " })\n", + " })\n", + "panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c85e3b7", - "metadata": {}, - "outputs": [], - "source": [ - "# Uncontrolled: panel creates its own state slot\n", - "panel = panels.Group(\n", - " items={\n", - " \"my_slider\": panels.Slider(),\n", - " \"val\": lambda my_slider: my_slider.value()\n", - " })\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0dd8dd51", - "metadata": {}, - "outputs": [], - "source": [ - "# Controlled: state is external to panel\n", - "\n", - "panel = panels.Group(\n", - " vars={'slider_val': 5},\n", - " items={\n", - " 'state_value': lambda slider_val: slider_val,\n", - " 'slider': lambda slider_val: panels.Group(\n", - " items={\n", - " \"my_slider\": panels.Slider(slider_val),\n", - " \"val\": lambda my_slider: my_slider.value(),\n", - " \"my_slider2\": panels.Slider(slider_val)\n", - " })\n", - " })\n", - "panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/create_plots_ui_guide.ipynb b/weave/legacy/examples/reference/create_plots_ui_guide.ipynb index 468507e3b2e..faf00a0b6d3 100644 --- a/weave/legacy/examples/reference/create_plots_ui_guide.ipynb +++ b/weave/legacy/examples/reference/create_plots_ui_guide.ipynb @@ -1,330 +1,330 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "1305d15c-9677-4f65-8cd8-338d1cb7aa82", - "metadata": {}, - "source": [ - "# Weave Plot: Interactively visualize your data\n", - "\n", - "In this tutorial, we will visualize data with the **Weave Plot Panel**:\n", - "* load a Pandas DataFrame or other tabular data into Weave \n", - "* visualize your data as a 2D plot of (x, y) points with **Weave Plot**\n", - "* customize the plot to filter or annotate the data\n", - "\n", - "We use an example dataset of [notable machine learning models](https://docs.google.com/spreadsheets/d/1AAIebjNsnJj_uKALHbXNfn3_YsT6sHXtCU0q7OIPuc4/edit#gid=0) to get started—you can try your own CSV file, Pandas DataFrame, or any public Google Sheets url.\n", - "\n", - "# 0: Setup\n", - "Import dependencies & login to W&B to save your work and share any visualizations you create." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "1305d15c-9677-4f65-8cd8-338d1cb7aa82", + "metadata": {}, + "source": [ + "# Weave Plot: Interactively visualize your data\n", + "\n", + "In this tutorial, we will visualize data with the **Weave Plot Panel**:\n", + "* load a Pandas DataFrame or other tabular data into Weave \n", + "* visualize your data as a 2D plot of (x, y) points with **Weave Plot**\n", + "* customize the plot to filter or annotate the data\n", + "\n", + "We use an example dataset of [notable machine learning models](https://docs.google.com/spreadsheets/d/1AAIebjNsnJj_uKALHbXNfn3_YsT6sHXtCU0q7OIPuc4/edit#gid=0) to get started—you can try your own CSV file, Pandas DataFrame, or any public Google Sheets url.\n", + "\n", + "# 0: Setup\n", + "Import dependencies & login to W&B to save your work and share any visualizations you create." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43053b45-eed1-445c-8bbd-05f113302060", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install -qqq weave\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d5645e7", + "metadata": {}, + "outputs": [], + "source": [ + "import wandb\n", + "wandb.login()\n", + "import weave\n", + "from weave.legacy.weave.show import show" + ] + }, + { + "cell_type": "markdown", + "id": "cb79fff5-f4fd-45db-a48e-c8581e0e5ad1", + "metadata": {}, + "source": [ + "# 1: Load your data as an interactive table\n", + "\n", + "Load a sample dataset of notable ML publications (560 rows, 33 columns as of Aug 8 2023). Feel free to edit these variables to\n", + "* load in your own Pandas DataFrame\n", + "* load a different CSV file\n", + "* modify the Google Sheets URL and sheet id to work with a different public spreadsheet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cf0eddd", + "metadata": {}, + "outputs": [], + "source": [ + "GOOGLE_SHEETS_URL = \"https://docs.google.com/spreadsheets/d/1AAIebjNsnJj_uKALHbXNfn3_YsT6sHXtCU0q7OIPuc4\"\n", + "SHEET_ID = \"0\"\n", + "CSV_SOURCE = f\"{GOOGLE_SHEETS_URL}/export?format=csv&gid={SHEET_ID}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c52f0700", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(CSV_SOURCE)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "6e48d6d1-e935-4244-b9ea-500fdb18e0bd", + "metadata": {}, + "source": [ + "View an interactive panel with your data in one line [1]. As with any Weave Table Panel, you can sort or group by any column to explore and build some intuition for the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1e62e5a-6a56-4581-a8e6-4f5d3bcc4d0e", + "metadata": {}, + "outputs": [], + "source": [ + "show(df)" + ] + }, + { + "cell_type": "markdown", + "id": "ad96850c-93e7-4b1f-a479-1e4f3655aebc", + "metadata": { + "scrolled": true + }, + "source": [ + "# 2: Plot the data in Weave\n", + "\n", + "You can convert any Weave Table into a **Weave Plot**. Change the panel type in the expression at the top of the panel from `table` to `plot`:\n", + "\n", + "\n", + "\n", + "Weave infers a reasonable view of your data based on the column types:\n", + "* two numerical columns x and y become the scatter plot axes\n", + "* each row is rendered as an (x, y) point on the resulting 2D grid, with a tooltip showing details on hover\n", + "* if available, the first string-type column becomes the label / the color shown in the legend.\n", + "\n", + "Before we dive into the detailed customization of a Weave Plot, how can we build with and iterate on this starter plot?\n", + "\n", + "## Full-screen a plot: Open as a Weave Board\n", + "\n", + "If you want more visual space or screen real estate to explore—zoom into details, zoom out for more context, iterate on multiple panel views in parallel—open any Weave Panel in a new browser tab. Mouse over the right-hand side and select \"Open in a new tab\"\n", + "\n", + "\n", + "## Multiple views in parallel: Refer to source data as you customize the plot\n", + "\n", + "Duplicate a panel so you can keep one copy in the `table` state and convert the second into a `plot`: click on the horizontal three-dot icon in the top right corner and select \"Duplicate\".\n", + "\n", + "\n", + "\n", + "## Resize one panel to adjust layout & iterate incrementally\n", + "\n", + "Combine these UX moves to iterate quickly on a neat layout: duplicate panels, resize one panel from a corner to a smaller portion of the grid to accommodate more panels, and independently modify individual panels until you're happy with the latest version.\n", + "\n", + "\n", + "Try the whole process starting from the single Table panel below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b85268d4-ef89-4bf5-9aed-2b8650fcd701", + "metadata": {}, + "outputs": [], + "source": [ + "show(df)" + ] + }, + { + "cell_type": "markdown", + "id": "16cc1620-4d10-41a1-9220-55ca238d5015", + "metadata": {}, + "source": [ + "# 3. Explore a plot with zoom and interactive selection\n", + "\n", + "Interactively explore PanelPlot data with zoom and region highlighting (subset selection):\n", + "\n", + "## Zoom level: Click + drag to zoom into a seleted rectangle, double-click to reset\n", + "To zoom into a region of the plot:\n", + "* click on the magnifying glass icon in the bottom right corner\n", + "* click, hold, and drag to select a set of points—a gray rectangle shows the active selected region\n", + "* optionally repeat as you find new/different points of interest\n", + "* double-click anywhere on the plot area to reset to the original zoom level\n", + "\n", + "## Selected data: Use the .selected_data Weave op to show point details\n", + "To see the full row details for selected points:\n", + "* given one plot panel named `panelN`, create another `panelN+1` and enter `panelN.selected_data` as the new panel's expression. `PanelN+1` will now show any points highlighted in `panelN`\n", + "* to select points from `panelN`, first click on the pointer icon in the bottom right corner of `panelN`\n", + "* click, hold, and drag to select a set of points in `panelN`—a gray rectangle shows the active selected region\n", + "* view the full details for those points in panelN+1\n", + "\n", + "\n", + "\n", + "# 4. Customize Weave Plots: Choose columns or compose Weave expressions\n", + "\n", + "Weave plot panels for scatter, line, and bar charts share most of their configuration parameters. To customize a Weave Plot, click on the `Edit` pencil icon in the top right corner to open the settings menu.\n", + "\n", + "## X axis & Y axis\n", + "\n", + "Define the X and Y dimensions of a plot:\n", + "* choose a column from your source data:`row[\"your column name here\"]` (with helpful suggestions in the dropdown showing available column names as you type)\n", + "* further customize the Weave expression using arithmetic (`row[\"x\"] * 2`), combined columns (`row[\"a\"] + row[\"b\"]`), or more advanced Weave ops (`row[\"cost_per_month\"].avg`)\n", + "\n", + "Try modifying the X and Y of the starter plot: for example, is there a correlation between publication impact (citations) and compute costs? Try setting X = `row[\"Training compute cost (2020 USD)\"]` and Y = `row[\"Citations\"]`\n", + " \n", + "## Colors\n", + "\n", + "Point color is set by one of two input methods:\n", + "* default `Enter a Weave Expression` method: takes the [tableau10 color palette](https://vega.github.io/vega/docs/schemes/#tableau10) and cycles its 10 colors over the distinct values of the result of the Weave Expression, e.g the values of a string column. In our sample plot, the `Color` field defaults to `row[\"System\"]`, and the legend in the top right of the plot shows the [tableau10 colors]((https://vega.github.io/vega/docs/schemes/#tableau10)) repeating over the full list of unique \"System\" names in the source data. Try editing this to `row[\"Domain\"]` to see publication trends by field (the Games and Language models seem to have the highest compute costs).\n", + "* `Select via Dropdown` -> `Encode from series` : this option defaults to one blue color for a single series, and otherwise cycles through the same tableau10 color palette for multiple series\n", + "\n", + "## Tooltip\n", + "\n", + "The tooltip field determines the content displayed when the mouse hovers over a data point. This defaults to a subset of columns from the underlying Weave Table and can be configured via Weave Expression to select one or more columns and optionally link them with string literals for readability/formatting. The following expression might be a useful summary for our sample data to show the authors and date for each model in addition to the system name: `row[\"System\"] + \" - \" + row[\"Authors\"] + \"; \" + row[\"Publication date\"]`\n", + "\n", + "## Labels\n", + "\n", + "This menu (at the very bottom of the plot settings) optionally sets the titles of the X axis, Y axis, and Color legend to the provided text. In our sample, we might condense the X axis title to \"Compute cost\", expand the Color legend title as \"ML task type\", etc.\n", + "\n", + "Here is a [sample side-by-side Weave Board](https://weave.wandb.ai/?exp=get%28%0A++++%22wandb-artifact%3A%2F%2F%2Fstacey%2Fpivot%2Fdefault_plot_with_mods%3Alatest%2Fobj%22%29) of the default starter plot and the final state with all the above modifications (note that the tooltip appears to the bottom right of the cursor/corresponds to the point on the upper left corner of the textbox)\n", + "\n", + "\n", + "## Multiple series\n", + "\n", + "Use the \"New Series +\" button to add one or more series of points. You can treat each of the Series 1, 2, 3... in a plot as an independent group/collection of settings with a separate instance of the specifications described above. When a plot contains multiple series, all of the series are overlayed/rendered on the same plot area when visualized and their settings combined in the legend/titles/etc. With our sample dataset, we may want to configure a different series/visual settings for each domain: language models in one series, computer vision in another, etc.\n", + "\n", + "## Advanced configuration\n", + "\n", + "### Switch linear scale to log scale\n", + "\n", + "There are two ways to convert a plot axis to log scale:\n", + "\n", + "1. Use the **Advanced properties editor**: click the pencil \"edit\" icon on a plot to expand the config menu on the right, then select \"Advanced properties\" at the end of the first 'Properties\" section. Set x- and y- axes to be log scale instead of linear in the dropdown menu.\n", + "\n", + "2. Convert the Weave expression to **log base 10 mathematically**: e.g. convert `row[\"Parameters\"]` to `row[\"Parameters\"] ** 0.1`. This will not adust the grid scale markers like the first option, but it will apply to any expression/dimension (not only x- and y- axes) and can be easier to try in the flow of editing charts.\n", + "\n", + "\n", + "### Filter source data to remove outliers\n", + "\n", + "Sometimes a log scale or zooming in is still insufficient. You can remove outliers by filtering the input data based on a range of column values. Use the `.filter` Weave op on the `Input` field to plot only the points that meet the specified condition. For example, compare the default starter plot with dropping any models that have >= 1e12 parameters:\n", + "\n", + "\n", + "Try editing some of these settings from the Table below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b56735b-f35f-4bb7-bcb6-ce315a16ba30", + "metadata": {}, + "outputs": [], + "source": [ + "show(df)" + ] + }, + { + "cell_type": "markdown", + "id": "dd71e9f7-9c37-440b-8acf-7997baa60065", + "metadata": {}, + "source": [ + "# 5. Scatter plot: Customize point shape and size via explicit assignment or enumeration over option list\n", + "\n", + "The `Mark` setting intially determines the plot style: scatter plot, line plot, bar chart, etc. This defaults to \"auto\" and picks the best option based on the incoming data types. If `Mark` is set explicitly to `point`, this reveals controls for the shape and size of the points.\n", + "\n", + "## Point shape\n", + "\n", + "* defaults to \"Enter a Weave Expression\": as with the other config fields, write a Weave Expression that returns a list, where each distinct option in that list will cycle throught the built-in list of shape options\n", + "* \"Select via dropdown\" -> \"circle\", \"square\", \"cross\", \"diamond\", etc: this input method sets a specific literal point shape from the list of available options\n", + "* \"Select via dropdown\" -> \"Encode from series\": cycle the shape options over the multiple series in the plot\n", + "\n", + "In our example scenario, we could look at how compute sponsorship compares for notable papers: set `Shape` to `row[\"Compute Sponsor Categorization\"]` and observe circles for academia and squares for industry.\n", + "\n", + "## Point size (with approximately-bucketed enumeration)\n", + "\n", + "* default 100 / user-specified number: point size for scatter plots defaults to 100 and can be set to any other numerical value [2]\n", + "* Weave expression: as in other settings, the list of distinct values resulting from the user-specified Weave expression will cycle through five perceptually-distinguishable point sizes from smallest to larges\n", + "\n", + "In this sample plot, try log-scaling point size with compute costs: set `Size` to `row[\"Training compute cost (2020 USD)\"] ** 0.1`. \n", + "\n", + "### Combining all of these suggestions\n", + "\n", + "Here's a static view of the original plot with the modifications described—one more chance to try the fully-interactive and customizable version below\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06b8483f-1db0-4c2d-a27d-e0592a6ce160", + "metadata": {}, + "outputs": [], + "source": [ + "show(df)" + ] + }, + { + "cell_type": "markdown", + "id": "cfc8055c-0406-4b4c-a545-11f8c870d2d4", + "metadata": {}, + "source": [ + "# 6. Inspiration for many possible data exploration workflows\n", + "\n", + "Weave is a maximally general toolkit, and the path of any interactive visual exploration will depend on the data, context, interests, goals, etc. We've described the main options and useful features of Weave Plot to both illustrate concrete steps and hopefully spark your own questions and insights. In follow-up tutorials, we will cover settings specific to line plots and bar charts. We'd love to hear if you discover something interesting in this or your own iterations!\n", + "\n", + "# 7. End notes\n", + "\n", + "## [1] Viewing data in Weave\n", + "\n", + "When starting with a Pandas DataFrame, you have two options for getting data into Weave:\n", + "\n", + "### weave.show(my_dataframe)\n", + "\n", + "`weave.show(my_dataframe)` returns an interactive Weave Panel with the Pandas DataFrame as a Table. This is the fastest and simplest way to load an interactive panel with your data.\n", + "\n", + "### weave.save(weave.from_pandas(my_dataframe), name=\"my_dataframe\")\n", + "\n", + "If you'd like to save the DataFrame as a local object, first wrap it a in format Weave can parse using the `weave.from_pandas` op:\n", + "\n", + "```python\n", + "my_data = weave.save(weave.from_pandas(dataframe), name=\"my_dataframe\")\n", + "my_data\n", + "```\n", + "\n", + "## [2] Configuring point size\n", + "\n", + "Point size is currently \"perceptually clamped\" to around five distinguishable sizes: tiny, small, medium, larger, largest. Increasing/decreasing the literal number will not perceptually increase the biggest points or add more perceptible gradations of size. It may make the smallest points effectively invisible." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "43053b45-eed1-445c-8bbd-05f113302060", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install -qqq weave\n", - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d5645e7", - "metadata": {}, - "outputs": [], - "source": [ - "import wandb\n", - "wandb.login()\n", - "import weave\n", - "from weave.legacy.show import show" - ] - }, - { - "cell_type": "markdown", - "id": "cb79fff5-f4fd-45db-a48e-c8581e0e5ad1", - "metadata": {}, - "source": [ - "# 1: Load your data as an interactive table\n", - "\n", - "Load a sample dataset of notable ML publications (560 rows, 33 columns as of Aug 8 2023). Feel free to edit these variables to\n", - "* load in your own Pandas DataFrame\n", - "* load a different CSV file\n", - "* modify the Google Sheets URL and sheet id to work with a different public spreadsheet" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8cf0eddd", - "metadata": {}, - "outputs": [], - "source": [ - "GOOGLE_SHEETS_URL = \"https://docs.google.com/spreadsheets/d/1AAIebjNsnJj_uKALHbXNfn3_YsT6sHXtCU0q7OIPuc4\"\n", - "SHEET_ID = \"0\"\n", - "CSV_SOURCE = f\"{GOOGLE_SHEETS_URL}/export?format=csv&gid={SHEET_ID}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c52f0700", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(CSV_SOURCE)\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "id": "6e48d6d1-e935-4244-b9ea-500fdb18e0bd", - "metadata": {}, - "source": [ - "View an interactive panel with your data in one line [1]. As with any Weave Table Panel, you can sort or group by any column to explore and build some intuition for the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1e62e5a-6a56-4581-a8e6-4f5d3bcc4d0e", - "metadata": {}, - "outputs": [], - "source": [ - "show(df)" - ] - }, - { - "cell_type": "markdown", - "id": "ad96850c-93e7-4b1f-a479-1e4f3655aebc", - "metadata": { - "scrolled": true - }, - "source": [ - "# 2: Plot the data in Weave\n", - "\n", - "You can convert any Weave Table into a **Weave Plot**. Change the panel type in the expression at the top of the panel from `table` to `plot`:\n", - "\n", - "\n", - "\n", - "Weave infers a reasonable view of your data based on the column types:\n", - "* two numerical columns x and y become the scatter plot axes\n", - "* each row is rendered as an (x, y) point on the resulting 2D grid, with a tooltip showing details on hover\n", - "* if available, the first string-type column becomes the label / the color shown in the legend.\n", - "\n", - "Before we dive into the detailed customization of a Weave Plot, how can we build with and iterate on this starter plot?\n", - "\n", - "## Full-screen a plot: Open as a Weave Board\n", - "\n", - "If you want more visual space or screen real estate to explore—zoom into details, zoom out for more context, iterate on multiple panel views in parallel—open any Weave Panel in a new browser tab. Mouse over the right-hand side and select \"Open in a new tab\"\n", - "\n", - "\n", - "## Multiple views in parallel: Refer to source data as you customize the plot\n", - "\n", - "Duplicate a panel so you can keep one copy in the `table` state and convert the second into a `plot`: click on the horizontal three-dot icon in the top right corner and select \"Duplicate\".\n", - "\n", - "\n", - "\n", - "## Resize one panel to adjust layout & iterate incrementally\n", - "\n", - "Combine these UX moves to iterate quickly on a neat layout: duplicate panels, resize one panel from a corner to a smaller portion of the grid to accommodate more panels, and independently modify individual panels until you're happy with the latest version.\n", - "\n", - "\n", - "Try the whole process starting from the single Table panel below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b85268d4-ef89-4bf5-9aed-2b8650fcd701", - "metadata": {}, - "outputs": [], - "source": [ - "show(df)" - ] - }, - { - "cell_type": "markdown", - "id": "16cc1620-4d10-41a1-9220-55ca238d5015", - "metadata": {}, - "source": [ - "# 3. Explore a plot with zoom and interactive selection\n", - "\n", - "Interactively explore PanelPlot data with zoom and region highlighting (subset selection):\n", - "\n", - "## Zoom level: Click + drag to zoom into a seleted rectangle, double-click to reset\n", - "To zoom into a region of the plot:\n", - "* click on the magnifying glass icon in the bottom right corner\n", - "* click, hold, and drag to select a set of points—a gray rectangle shows the active selected region\n", - "* optionally repeat as you find new/different points of interest\n", - "* double-click anywhere on the plot area to reset to the original zoom level\n", - "\n", - "## Selected data: Use the .selected_data Weave op to show point details\n", - "To see the full row details for selected points:\n", - "* given one plot panel named `panelN`, create another `panelN+1` and enter `panelN.selected_data` as the new panel's expression. `PanelN+1` will now show any points highlighted in `panelN`\n", - "* to select points from `panelN`, first click on the pointer icon in the bottom right corner of `panelN`\n", - "* click, hold, and drag to select a set of points in `panelN`—a gray rectangle shows the active selected region\n", - "* view the full details for those points in panelN+1\n", - "\n", - "\n", - "\n", - "# 4. Customize Weave Plots: Choose columns or compose Weave expressions\n", - "\n", - "Weave plot panels for scatter, line, and bar charts share most of their configuration parameters. To customize a Weave Plot, click on the `Edit` pencil icon in the top right corner to open the settings menu.\n", - "\n", - "## X axis & Y axis\n", - "\n", - "Define the X and Y dimensions of a plot:\n", - "* choose a column from your source data:`row[\"your column name here\"]` (with helpful suggestions in the dropdown showing available column names as you type)\n", - "* further customize the Weave expression using arithmetic (`row[\"x\"] * 2`), combined columns (`row[\"a\"] + row[\"b\"]`), or more advanced Weave ops (`row[\"cost_per_month\"].avg`)\n", - "\n", - "Try modifying the X and Y of the starter plot: for example, is there a correlation between publication impact (citations) and compute costs? Try setting X = `row[\"Training compute cost (2020 USD)\"]` and Y = `row[\"Citations\"]`\n", - " \n", - "## Colors\n", - "\n", - "Point color is set by one of two input methods:\n", - "* default `Enter a Weave Expression` method: takes the [tableau10 color palette](https://vega.github.io/vega/docs/schemes/#tableau10) and cycles its 10 colors over the distinct values of the result of the Weave Expression, e.g the values of a string column. In our sample plot, the `Color` field defaults to `row[\"System\"]`, and the legend in the top right of the plot shows the [tableau10 colors]((https://vega.github.io/vega/docs/schemes/#tableau10)) repeating over the full list of unique \"System\" names in the source data. Try editing this to `row[\"Domain\"]` to see publication trends by field (the Games and Language models seem to have the highest compute costs).\n", - "* `Select via Dropdown` -> `Encode from series` : this option defaults to one blue color for a single series, and otherwise cycles through the same tableau10 color palette for multiple series\n", - "\n", - "## Tooltip\n", - "\n", - "The tooltip field determines the content displayed when the mouse hovers over a data point. This defaults to a subset of columns from the underlying Weave Table and can be configured via Weave Expression to select one or more columns and optionally link them with string literals for readability/formatting. The following expression might be a useful summary for our sample data to show the authors and date for each model in addition to the system name: `row[\"System\"] + \" - \" + row[\"Authors\"] + \"; \" + row[\"Publication date\"]`\n", - "\n", - "## Labels\n", - "\n", - "This menu (at the very bottom of the plot settings) optionally sets the titles of the X axis, Y axis, and Color legend to the provided text. In our sample, we might condense the X axis title to \"Compute cost\", expand the Color legend title as \"ML task type\", etc.\n", - "\n", - "Here is a [sample side-by-side Weave Board](https://weave.wandb.ai/?exp=get%28%0A++++%22wandb-artifact%3A%2F%2F%2Fstacey%2Fpivot%2Fdefault_plot_with_mods%3Alatest%2Fobj%22%29) of the default starter plot and the final state with all the above modifications (note that the tooltip appears to the bottom right of the cursor/corresponds to the point on the upper left corner of the textbox)\n", - "\n", - "\n", - "## Multiple series\n", - "\n", - "Use the \"New Series +\" button to add one or more series of points. You can treat each of the Series 1, 2, 3... in a plot as an independent group/collection of settings with a separate instance of the specifications described above. When a plot contains multiple series, all of the series are overlayed/rendered on the same plot area when visualized and their settings combined in the legend/titles/etc. With our sample dataset, we may want to configure a different series/visual settings for each domain: language models in one series, computer vision in another, etc.\n", - "\n", - "## Advanced configuration\n", - "\n", - "### Switch linear scale to log scale\n", - "\n", - "There are two ways to convert a plot axis to log scale:\n", - "\n", - "1. Use the **Advanced properties editor**: click the pencil \"edit\" icon on a plot to expand the config menu on the right, then select \"Advanced properties\" at the end of the first 'Properties\" section. Set x- and y- axes to be log scale instead of linear in the dropdown menu.\n", - "\n", - "2. Convert the Weave expression to **log base 10 mathematically**: e.g. convert `row[\"Parameters\"]` to `row[\"Parameters\"] ** 0.1`. This will not adust the grid scale markers like the first option, but it will apply to any expression/dimension (not only x- and y- axes) and can be easier to try in the flow of editing charts.\n", - "\n", - "\n", - "### Filter source data to remove outliers\n", - "\n", - "Sometimes a log scale or zooming in is still insufficient. You can remove outliers by filtering the input data based on a range of column values. Use the `.filter` Weave op on the `Input` field to plot only the points that meet the specified condition. For example, compare the default starter plot with dropping any models that have >= 1e12 parameters:\n", - "\n", - "\n", - "Try editing some of these settings from the Table below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b56735b-f35f-4bb7-bcb6-ce315a16ba30", - "metadata": {}, - "outputs": [], - "source": [ - "show(df)" - ] - }, - { - "cell_type": "markdown", - "id": "dd71e9f7-9c37-440b-8acf-7997baa60065", - "metadata": {}, - "source": [ - "# 5. Scatter plot: Customize point shape and size via explicit assignment or enumeration over option list\n", - "\n", - "The `Mark` setting intially determines the plot style: scatter plot, line plot, bar chart, etc. This defaults to \"auto\" and picks the best option based on the incoming data types. If `Mark` is set explicitly to `point`, this reveals controls for the shape and size of the points.\n", - "\n", - "## Point shape\n", - "\n", - "* defaults to \"Enter a Weave Expression\": as with the other config fields, write a Weave Expression that returns a list, where each distinct option in that list will cycle throught the built-in list of shape options\n", - "* \"Select via dropdown\" -> \"circle\", \"square\", \"cross\", \"diamond\", etc: this input method sets a specific literal point shape from the list of available options\n", - "* \"Select via dropdown\" -> \"Encode from series\": cycle the shape options over the multiple series in the plot\n", - "\n", - "In our example scenario, we could look at how compute sponsorship compares for notable papers: set `Shape` to `row[\"Compute Sponsor Categorization\"]` and observe circles for academia and squares for industry.\n", - "\n", - "## Point size (with approximately-bucketed enumeration)\n", - "\n", - "* default 100 / user-specified number: point size for scatter plots defaults to 100 and can be set to any other numerical value [2]\n", - "* Weave expression: as in other settings, the list of distinct values resulting from the user-specified Weave expression will cycle through five perceptually-distinguishable point sizes from smallest to larges\n", - "\n", - "In this sample plot, try log-scaling point size with compute costs: set `Size` to `row[\"Training compute cost (2020 USD)\"] ** 0.1`. \n", - "\n", - "### Combining all of these suggestions\n", - "\n", - "Here's a static view of the original plot with the modifications described—one more chance to try the fully-interactive and customizable version below\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "06b8483f-1db0-4c2d-a27d-e0592a6ce160", - "metadata": {}, - "outputs": [], - "source": [ - "show(df)" - ] - }, - { - "cell_type": "markdown", - "id": "cfc8055c-0406-4b4c-a545-11f8c870d2d4", - "metadata": {}, - "source": [ - "# 6. Inspiration for many possible data exploration workflows\n", - "\n", - "Weave is a maximally general toolkit, and the path of any interactive visual exploration will depend on the data, context, interests, goals, etc. We've described the main options and useful features of Weave Plot to both illustrate concrete steps and hopefully spark your own questions and insights. In follow-up tutorials, we will cover settings specific to line plots and bar charts. We'd love to hear if you discover something interesting in this or your own iterations!\n", - "\n", - "# 7. End notes\n", - "\n", - "## [1] Viewing data in Weave\n", - "\n", - "When starting with a Pandas DataFrame, you have two options for getting data into Weave:\n", - "\n", - "### weave.show(my_dataframe)\n", - "\n", - "`weave.show(my_dataframe)` returns an interactive Weave Panel with the Pandas DataFrame as a Table. This is the fastest and simplest way to load an interactive panel with your data.\n", - "\n", - "### weave.save(weave.from_pandas(my_dataframe), name=\"my_dataframe\")\n", - "\n", - "If you'd like to save the DataFrame as a local object, first wrap it a in format Weave can parse using the `weave.from_pandas` op:\n", - "\n", - "```python\n", - "my_data = weave.save(weave.from_pandas(dataframe), name=\"my_dataframe\")\n", - "my_data\n", - "```\n", - "\n", - "## [2] Configuring point size\n", - "\n", - "Point size is currently \"perceptually clamped\" to around five distinguishable sizes: tiny, small, medium, larger, largest. Increasing/decreasing the literal number will not perceptually increase the biggest points or add more perceptible gradations of size. It may make the smallest points effectively invisible." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/layout/Group & LabeledItem.ipynb b/weave/legacy/examples/reference/layout/Group & LabeledItem.ipynb index 8f59c238d43..890e9b76bd3 100644 --- a/weave/legacy/examples/reference/layout/Group & LabeledItem.ipynb +++ b/weave/legacy/examples/reference/layout/Group & LabeledItem.ipynb @@ -1,48 +1,48 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2e4a39ce", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy.panels" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2e4a39ce", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave.panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32953f12", + "metadata": {}, + "outputs": [], + "source": [ + "# Group example\n", + "weave.legacy.weave.panels.Group(\n", + " items={\n", + " '0': weave.legacy.weave.panels.Group(\n", + " preferHorizontal=True,\n", + " items={\n", + " '0': weave.legacy.weave.panels.LabeledItem(item='item1', label='label1'),\n", + " '1': weave.legacy.weave.panels.LabeledItem(item='item2', label='label2'),\n", + " '2': weave.legacy.weave.panels.LabeledItem(item='item3', label='label3'),\n", + " }\n", + " ),\n", + " '1': weave.legacy.weave.panels.LabeledItem(item='item4', label='label4'),\n", + " '2': weave.legacy.weave.panels.LabeledItem(item='item5', label='label5')\n", + " }\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "32953f12", - "metadata": {}, - "outputs": [], - "source": [ - "# Group example\n", - "weave.legacy.panels.Group(\n", - " items={\n", - " '0': weave.legacy.panels.Group(\n", - " preferHorizontal=True,\n", - " items={\n", - " '0': weave.legacy.panels.LabeledItem(item='item1', label='label1'),\n", - " '1': weave.legacy.panels.LabeledItem(item='item2', label='label2'),\n", - " '2': weave.legacy.panels.LabeledItem(item='item3', label='label3'),\n", - " }\n", - " ),\n", - " '1': weave.legacy.panels.LabeledItem(item='item4', label='label4'),\n", - " '2': weave.legacy.panels.LabeledItem(item='item5', label='label5')\n", - " }\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/markdown.ipynb b/weave/legacy/examples/reference/markdown.ipynb index ee612bac66e..a047c4186fb 100644 --- a/weave/legacy/examples/reference/markdown.ipynb +++ b/weave/legacy/examples/reference/markdown.ipynb @@ -1,50 +1,50 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "9357be91", - "metadata": {}, - "outputs": [], - "source": [ - "import weave" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "9357be91", + "metadata": {}, + "outputs": [], + "source": [ + "import weave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42e421dd", + "metadata": {}, + "outputs": [], + "source": [ + "weave.legacy.weave.panels.Board(\n", + " vars={},\n", + " panels=[weave.legacy.weave.panels.BoardPanel(\n", + " weave.legacy.weave.panels.PanelString(\"## I am markdown\\n\\nHear me roar\", mode=\"markdown\"),\n", + " id='some_markdown')\n", + " ])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "42e421dd", - "metadata": {}, - "outputs": [], - "source": [ - "weave.legacy.panels.Board(\n", - " vars={},\n", - " panels=[weave.legacy.panels.BoardPanel(\n", - " weave.legacy.panels.PanelString(\"## I am markdown\\n\\nHear me roar\", mode=\"markdown\"),\n", - " id='some_markdown')\n", - " ])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/panels/plot/synced_axes.ipynb b/weave/legacy/examples/reference/panels/plot/synced_axes.ipynb index b3d9d43b410..7da13d9b81d 100644 --- a/weave/legacy/examples/reference/panels/plot/synced_axes.ipynb +++ b/weave/legacy/examples/reference/panels/plot/synced_axes.ipynb @@ -1,15 +1,152 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f1022622", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "import weave.legacy.panels" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f1022622", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "import weave.legacy.weave.panels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af978245", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.scripts import syndata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a2a4713", + "metadata": {}, + "outputs": [], + "source": [ + "data = weave.save(syndata.random_metrics(100, 100))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85e9d219", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave import ops_arrow\n", + "from weave.legacy.weave.ecosystem import wandb\n", + "import datetime\n", + "import random\n", + "random.seed(1)\n", + "\n", + "min_x = datetime.datetime(2023, 3, 22, 11, 0, 0)\n", + "max_x = min_x + datetime.timedelta(hours=1)\n", + "\n", + "users = ['nick', 'shawn', 'stacey', 'tim', 'danny']\n", + "model_versions = ['v1', 'v2']\n", + "\n", + "num_requests = 100\n", + "\n", + "def random_datetime_in_range() -> datetime.datetime:\n", + " min_timestamp = min_x.timestamp()\n", + " max_timestamp = max_x.timestamp()\n", + " random_timestamp = random.uniform(min_timestamp, max_timestamp)\n", + " return datetime.datetime.fromtimestamp(random_timestamp)\n", + "\n", + "def random_suggestion(n: int) -> str:\n", + " result = ''\n", + " for i in range(n):\n", + " result += random.choice('abcdefghijklmnopqrstuvwxyz')\n", + " return result\n", + "\n", + "\n", + "items = weave.save(\n", + " ops_arrow.to_arrow(sorted([\n", + " {\n", + " \"pred\": random_suggestion(7),\n", + " \"time\": random_datetime_in_range(),\n", + " \"user\": random.choice(users),\n", + " \"version\": random.choice(model_versions),\n", + " \"accepted\": random.choice([1, 1, 1, 0, 0]),\n", + " \"metric\": random.uniform(0, 1)\n", + " } \n", + " for _ in range(num_requests)\n", + " ], key=lambda d: d['time']))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d5919ee", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave.panels.panel_plot import Signals, AxisSelections, Plot\n", + "from weave.legacy.weave import weave_internal\n", + "\n", + "\n", + "domain_node = weave_internal.const(None)\n", + "#domain_node = weave_internal.save(None)\n", + "\n", + "board = weave.legacy.weave.panels.Board(\n", + " vars={\n", + " 'predictions': items,\n", + " 'domain': domain_node,\n", + " },\n", + " panels=[\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, domain: Plot(\n", + " predictions,\n", + " x=lambda row: row[\"time\"],\n", + " y=lambda row: row[\"metric\"],\n", + " label=lambda row: row['user'],\n", + " mark=\"point\",\n", + " domain_x=domain,\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=12, h=12)\n", + " ),\n", + " weave.legacy.weave.panels.BoardPanel(\n", + " lambda predictions, domain: Plot(\n", + " predictions,\n", + " x=lambda row: row[\"time\"],\n", + " y=lambda row: row[\"metric\"] * 2,\n", + " label=lambda row: row['version'],\n", + " mark=\"point\",\n", + " domain_x=domain,\n", + " ),\n", + " layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=12, h=12)\n", + " ), \n", + " ]\n", + ")\n", + "board" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, { "cell_type": "code", diff --git a/weave/legacy/examples/reference/vis/Distribution.ipynb b/weave/legacy/examples/reference/vis/Distribution.ipynb index 3248599762f..ea96aa5fc5a 100644 --- a/weave/legacy/examples/reference/vis/Distribution.ipynb +++ b/weave/legacy/examples/reference/vis/Distribution.ipynb @@ -1,138 +1,138 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "726127b0", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import weave\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\\n\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem import wandb" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "726127b0", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import weave\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\\n\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9875d92", + "metadata": {}, + "outputs": [], + "source": [ + "items = weave.save([\n", + " {'name': 'x',\n", + " 'loss1': [random.gauss(5, 2) for i in range(500)],\n", + " 'loss2': [random.gauss(5, 2) for i in range(500)],\n", + " 'str_val': [random.choice(['a', 'b', 'c']) for i in range(500)]},\n", + " {'name': 'y',\n", + " 'loss1': [random.gauss(9, 4) for i in range(500)],\n", + " 'loss2': [random.gauss(-1, 2) for i in range(500)],\n", + " 'str_val': [random.choice(['a', 'b', 'c']) for i in range(500)]},\n", + " ])\n", + "items" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a92bcc0e", + "metadata": {}, + "outputs": [], + "source": [ + "from weave.legacy.weave import weave_internal\n", + "# Numeric distribution\n", + "dist = wandb.Distribution(\n", + " items,\n", + " value_fn=lambda x: x['loss1'],\n", + " bin_size=0.1\n", + ")\n", + "dist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb380da9", + "metadata": {}, + "outputs": [], + "source": [ + "# String distribution\n", + "wandb.Distribution(\n", + " items,\n", + " value_fn=lambda x: x['str_val'],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6db75ca1", + "metadata": {}, + "outputs": [], + "source": [ + "# Colored numeric distribution\n", + "wandb.Distribution(\n", + " items,\n", + " value_fn=lambda x: x['loss1'],\n", + " label_fn=lambda x: x['name'],\n", + " bin_size=0.2\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4eee28b9", + "metadata": {}, + "outputs": [], + "source": [ + "# Colored string distribution\n", + "wandb.Distribution(\n", + " items,\n", + " value_fn=lambda x: x['str_val'],\n", + " label_fn=lambda x: x['name'],\n", + " bin_size=0.2\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30ab60fb", + "metadata": {}, + "outputs": [], + "source": [ + "# Labeled numeric distribution by nested value\n", + "wandb.Distribution(\n", + " items,\n", + " value_fn=lambda x: x['loss1'],\n", + " label_fn=lambda x: x['str_val'],\n", + " bin_size=0.2\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a9875d92", - "metadata": {}, - "outputs": [], - "source": [ - "items = weave.save([\n", - " {'name': 'x',\n", - " 'loss1': [random.gauss(5, 2) for i in range(500)],\n", - " 'loss2': [random.gauss(5, 2) for i in range(500)],\n", - " 'str_val': [random.choice(['a', 'b', 'c']) for i in range(500)]},\n", - " {'name': 'y',\n", - " 'loss1': [random.gauss(9, 4) for i in range(500)],\n", - " 'loss2': [random.gauss(-1, 2) for i in range(500)],\n", - " 'str_val': [random.choice(['a', 'b', 'c']) for i in range(500)]},\n", - " ])\n", - "items" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a92bcc0e", - "metadata": {}, - "outputs": [], - "source": [ - "from weave.legacy import weave_internal\n", - "# Numeric distribution\n", - "dist = wandb.Distribution(\n", - " items,\n", - " value_fn=lambda x: x['loss1'],\n", - " bin_size=0.1\n", - ")\n", - "dist" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb380da9", - "metadata": {}, - "outputs": [], - "source": [ - "# String distribution\n", - "wandb.Distribution(\n", - " items,\n", - " value_fn=lambda x: x['str_val'],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6db75ca1", - "metadata": {}, - "outputs": [], - "source": [ - "# Colored numeric distribution\n", - "wandb.Distribution(\n", - " items,\n", - " value_fn=lambda x: x['loss1'],\n", - " label_fn=lambda x: x['name'],\n", - " bin_size=0.2\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4eee28b9", - "metadata": {}, - "outputs": [], - "source": [ - "# Colored string distribution\n", - "wandb.Distribution(\n", - " items,\n", - " value_fn=lambda x: x['str_val'],\n", - " label_fn=lambda x: x['name'],\n", - " bin_size=0.2\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "30ab60fb", - "metadata": {}, - "outputs": [], - "source": [ - "# Labeled numeric distribution by nested value\n", - "wandb.Distribution(\n", - " items,\n", - " value_fn=lambda x: x['loss1'],\n", - " label_fn=lambda x: x['str_val'],\n", - " bin_size=0.2\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/vis/Scatter.ipynb b/weave/legacy/examples/reference/vis/Scatter.ipynb index 7584ee9b4cd..6d11b709d1b 100644 --- a/weave/legacy/examples/reference/vis/Scatter.ipynb +++ b/weave/legacy/examples/reference/vis/Scatter.ipynb @@ -1,101 +1,101 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "81e1af41", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import weave\n", - "import weave.legacy.panels\n", - "from weave.legacy.show import show\n", - "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", - "weave.use_lazy_execution()\n", - "from weave.legacy.ecosystem import wandb" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "81e1af41", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "import weave\n", + "import weave.legacy.weave.panels\n", + "from weave.legacy.weave.show import show\n", + "# Weave package now defaults to eager mode, but lazy mode required for this example notebook for now.\n", + "weave.use_lazy_execution()\n", + "from weave.legacy.weave.ecosystem import wandb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6687922", + "metadata": {}, + "outputs": [], + "source": [ + "data = weave.save([{\n", + " 'a': random.gauss(5, 2),\n", + " 'b': random.gauss(0, 9),\n", + " 'c': random.gauss(15, 0.9),\n", + " 'd': random.random(),\n", + " 'e': random.choice(['a', 'b'])} for i in range(500)])\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "726b9294", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " items={\n", + " 'scatter': wandb.Scatter(data,\n", + " x_fn=lambda item: item['a'],\n", + " y_fn=lambda item: item['b'],\n", + " label_fn=lambda item: item['e']\n", + " ),\n", + " 'detail': lambda scatter: weave.legacy.weave.panels.Group(\n", + " style=\"height: 400px;\",\n", + " preferHorizontal=True,\n", + " items={\n", + " 'a_dist': weave.legacy.weave.panels.LabeledItem(\n", + " label='Distribution for metric a in selection',\n", + " item=wandb.Distribution(scatter.selected(),\n", + " value_fn=lambda item: item['a'],\n", + " label_fn=lambda item: item['e'],\n", + " bin_size=0.1)),\n", + " 'b_dist': weave.legacy.weave.panels.LabeledItem(\n", + " label='Distribution for metric b in selection',\n", + " item=wandb.Distribution(scatter.selected(),\n", + " value_fn=lambda item: item['b'],\n", + " label_fn=lambda item: item['e'],\n", + " bin_size=0.1))\n", + " }),\n", + " 'table': lambda scatter: weave.legacy.weave.panels.LabeledItem(\n", + " label='Selected items',\n", + " item=weave.legacy.weave.panels.Group(\n", + " style=\"height: 400px;\",\n", + " preferHorizontal=True,\n", + " items={\n", + " 'table': scatter.selected()\n", + " }))\n", + " }\n", + ")\n", + "show(panel, height=1000)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "b6687922", - "metadata": {}, - "outputs": [], - "source": [ - "data = weave.save([{\n", - " 'a': random.gauss(5, 2),\n", - " 'b': random.gauss(0, 9),\n", - " 'c': random.gauss(15, 0.9),\n", - " 'd': random.random(),\n", - " 'e': random.choice(['a', 'b'])} for i in range(500)])\n", - "data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "726b9294", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " items={\n", - " 'scatter': wandb.Scatter(data,\n", - " x_fn=lambda item: item['a'],\n", - " y_fn=lambda item: item['b'],\n", - " label_fn=lambda item: item['e']\n", - " ),\n", - " 'detail': lambda scatter: weave.legacy.panels.Group(\n", - " style=\"height: 400px;\",\n", - " preferHorizontal=True,\n", - " items={\n", - " 'a_dist': weave.legacy.panels.LabeledItem(\n", - " label='Distribution for metric a in selection',\n", - " item=wandb.Distribution(scatter.selected(),\n", - " value_fn=lambda item: item['a'],\n", - " label_fn=lambda item: item['e'],\n", - " bin_size=0.1)),\n", - " 'b_dist': weave.legacy.panels.LabeledItem(\n", - " label='Distribution for metric b in selection',\n", - " item=wandb.Distribution(scatter.selected(),\n", - " value_fn=lambda item: item['b'],\n", - " label_fn=lambda item: item['e'],\n", - " bin_size=0.1))\n", - " }),\n", - " 'table': lambda scatter: weave.legacy.panels.LabeledItem(\n", - " label='Selected items',\n", - " item=weave.legacy.panels.Group(\n", - " style=\"height: 400px;\",\n", - " preferHorizontal=True,\n", - " items={\n", - " 'table': scatter.selected()\n", - " }))\n", - " }\n", - ")\n", - "show(panel, height=1000)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/examples/reference/vis/derived_plots_from_tables.ipynb b/weave/legacy/examples/reference/vis/derived_plots_from_tables.ipynb index 200c338d869..638de728518 100644 --- a/weave/legacy/examples/reference/vis/derived_plots_from_tables.ipynb +++ b/weave/legacy/examples/reference/vis/derived_plots_from_tables.ipynb @@ -1,111 +1,111 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "14b320aa", - "metadata": {}, - "outputs": [], - "source": [ - "import weave\n", - "from weave.legacy.ecosystem import wandb\n", - "\n", - "# Group tags are broken with caching\n", - "# import os \n", - "# os.environ[\"WEAVE_NO_CACHE\"] = 'true'" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "14b320aa", + "metadata": {}, + "outputs": [], + "source": [ + "import weave\n", + "from weave.legacy.weave.ecosystem import wandb\n", + "\n", + "# Group tags are broken with caching\n", + "# import os \n", + "# os.environ[\"WEAVE_NO_CACHE\"] = 'true'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfc9678e", + "metadata": {}, + "outputs": [], + "source": [ + "table = weave.legacy.weave.panels.Table(list(range(100)))\n", + "\n", + "# Add Filter\n", + "table.config.tableState.set_filter_fn(lambda i: i < 50)\n", + "\n", + "# Add Grouping\n", + "table.config.tableState.add_column(lambda i: i % 3, 'col_name_a')\n", + "group_col_id = table.config.tableState.order[0]\n", + "# table.config.tableState.enable_groupby(group_col_id)\n", + "\n", + "# Add Selections (Note: Important to add after grouping so `i` is correctly typed)\n", + "table.config.tableState.add_column(lambda i: i ** 2, 'col_name_b')\n", + "# table.config.tableState.add_column(lambda i: i.max(), 'col_name_c')\n", + "\n", + "# Add Sort\n", + "table.config.tableState.enable_sort(group_col_id)\n", + "\n", + "\n", + "panel = weave.legacy.weave.panels.Group(\n", + " layoutMode='horizontal',\n", + " items={\n", + " \"table\": table,\n", + "# \"table_2\": lambda table: table.rows()[0]['col_name_a']\n", + " \"plot\": lambda table: weave.legacy.weave.panels.Plot(\n", + " table.all_rows(),\n", + " x=lambda row: row['col_name_a'],\n", + " y=lambda row: row['col_name_b'],\n", + " )\n", + " }\n", + ")\n", + "panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf475571", + "metadata": {}, + "outputs": [], + "source": [ + "panel = weave.legacy.weave.panels.Group(\n", + " layoutMode='horizontal',\n", + " items={\n", + " \"table\": weave.legacy.weave.panels.Table(list(range(100)), columns=[\n", + " lambda i: i,\n", + " lambda i: i**2,\n", + " lambda i: i * 0 + 1\n", + " ]),\n", + " \"table_2\": lambda table: weave.legacy.weave.panels.Table(table.all_rows(), columns=[\n", + " lambda i: table.all_rows()['c_0'].max() - i['c_0'],\n", + " lambda i: i['c_1'] / 2,\n", + " lambda i: i['c_2'] - 2\n", + " ]),\n", + " \"plot\": lambda table, table_2: weave.legacy.weave.panels.Plot(\n", + " weave.legacy.weave.ops.make_list(a=table.all_rows(), b=table_2.all_rows()).concat(),\n", + " x=lambda row: row['c_0'],\n", + " y=lambda row: row['c_1'],\n", + " color=lambda row: row['c_2']\n", + " )\n", + " }\n", + ")\n", + "panel" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfc9678e", - "metadata": {}, - "outputs": [], - "source": [ - "table = weave.legacy.panels.Table(list(range(100)))\n", - "\n", - "# Add Filter\n", - "table.config.tableState.set_filter_fn(lambda i: i < 50)\n", - "\n", - "# Add Grouping\n", - "table.config.tableState.add_column(lambda i: i % 3, 'col_name_a')\n", - "group_col_id = table.config.tableState.order[0]\n", - "# table.config.tableState.enable_groupby(group_col_id)\n", - "\n", - "# Add Selections (Note: Important to add after grouping so `i` is correctly typed)\n", - "table.config.tableState.add_column(lambda i: i ** 2, 'col_name_b')\n", - "# table.config.tableState.add_column(lambda i: i.max(), 'col_name_c')\n", - "\n", - "# Add Sort\n", - "table.config.tableState.enable_sort(group_col_id)\n", - "\n", - "\n", - "panel = weave.legacy.panels.Group(\n", - " layoutMode='horizontal',\n", - " items={\n", - " \"table\": table,\n", - "# \"table_2\": lambda table: table.rows()[0]['col_name_a']\n", - " \"plot\": lambda table: weave.legacy.panels.Plot(\n", - " table.all_rows(),\n", - " x=lambda row: row['col_name_a'],\n", - " y=lambda row: row['col_name_b'],\n", - " )\n", - " }\n", - ")\n", - "panel" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf475571", - "metadata": {}, - "outputs": [], - "source": [ - "panel = weave.legacy.panels.Group(\n", - " layoutMode='horizontal',\n", - " items={\n", - " \"table\": weave.legacy.panels.Table(list(range(100)), columns=[\n", - " lambda i: i,\n", - " lambda i: i**2,\n", - " lambda i: i * 0 + 1\n", - " ]),\n", - " \"table_2\": lambda table: weave.legacy.panels.Table(table.all_rows(), columns=[\n", - " lambda i: table.all_rows()['c_0'].max() - i['c_0'],\n", - " lambda i: i['c_1'] / 2,\n", - " lambda i: i['c_2'] - 2\n", - " ]),\n", - " \"plot\": lambda table, table_2: weave.legacy.panels.Plot(\n", - " weave.legacy.ops.make_list(a=table.all_rows(), b=table_2.all_rows()).concat(),\n", - " x=lambda row: row['c_0'],\n", - " y=lambda row: row['c_1'],\n", - " color=lambda row: row['c_2']\n", - " )\n", - " }\n", - ")\n", - "panel" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/weave/legacy/ops_arrow/__init__.py b/weave/legacy/ops_arrow/__init__.py deleted file mode 100644 index 6b954627d8b..00000000000 --- a/weave/legacy/ops_arrow/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from weave.legacy import context_state as _context_state - -_loading_builtins_token = _context_state.set_loading_built_ins() - -from weave.legacy.arrow.arrow import * -from weave.legacy.arrow.convert import * -from weave.legacy.arrow.list_ import * -from weave.legacy.ops_arrow import ops -from weave.legacy.ops_arrow.list_range import range -from weave.legacy.ops_arrow.vectorize import vectorize - -_context_state.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ops_arrow/ops.py b/weave/legacy/ops_arrow/ops.py deleted file mode 100644 index b51ad06934c..00000000000 --- a/weave/legacy/ops_arrow/ops.py +++ /dev/null @@ -1,11 +0,0 @@ -from weave.legacy.ops_arrow.arraylist_ops import * -from weave.legacy.ops_arrow.boolean import * -from weave.legacy.ops_arrow.convert_ops import * -from weave.legacy.ops_arrow.date import * -from weave.legacy.ops_arrow.dict import * -from weave.legacy.ops_arrow.list_join import * -from weave.legacy.ops_arrow.list_ops import * # type: ignore -from weave.legacy.ops_arrow.number import * -from weave.legacy.ops_arrow.obj import * -from weave.legacy.ops_arrow.ref_ops import * -from weave.legacy.ops_arrow.string import * diff --git a/weave/legacy/ops_primitives/test_list_range.py b/weave/legacy/ops_primitives/test_list_range.py deleted file mode 100644 index ce98b09fbae..00000000000 --- a/weave/legacy/ops_primitives/test_list_range.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest - -import weave - - -def test_range(): - assert weave.use(weave.legacy.ops.range(0, 3, 1)).to_pylist_tagged() == [0, 1, 2] diff --git a/weave/legacy/panels/__init__.py b/weave/legacy/panels/__init__.py deleted file mode 100644 index 8652aaabea1..00000000000 --- a/weave/legacy/panels/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -from weave.legacy import context_state as _context_state - -_loading_builtins_token = _context_state.set_loading_built_ins() - -from weave.legacy.panels.panel_auto import * - -# basic -from weave.legacy.panels.panel_basic import * - -# top level board -from weave.legacy.panels.panel_board import Board, BoardPanel, BoardPanelLayout -from weave.legacy.panels.panel_card import Card, CardTab -from weave.legacy.panels.panel_color import Color -from weave.legacy.panels.panel_daterange import DateRange - -# domain -from weave.legacy.panels.panel_domain import * -from weave.legacy.panels.panel_dropdown import Dropdown, DropdownConfig -from weave.legacy.panels.panel_each import Each -from weave.legacy.panels.panel_each_column import EachColumn - -# special -from weave.legacy.panels.panel_expression import * -from weave.legacy.panels.panel_facet import Facet -from weave.legacy.panels.panel_facet_tabs import FacetTabs -from weave.legacy.panels.panel_filter_editor import FilterEditor -from weave.legacy.panels.panel_function_editor import ( - FunctionEditor, - FunctionEditorConfig, -) -from weave.legacy.panels.panel_group import ( - Group, - GroupLayoutFlow, - GroupPanel, - GroupPanelLayout, -) -from weave.legacy.panels.panel_grouping_editor import GroupingEditor - -# Incomplete -from weave.legacy.panels.panel_histogram import * -from weave.legacy.panels.panel_html import PanelHtml - -# layout -from weave.legacy.panels.panel_labeled_item import LabeledItem - -# legacy -from weave.legacy.panels.panel_legacy import * -from weave.legacy.panels.panel_markdown import PanelMarkdown - -# Non-standard editor (todo: update) -from weave.legacy.panels.panel_object_picker import ObjectPicker, ObjectPickerConfig -from weave.legacy.panels.panel_plot import Plot, PlotConstants, Series - -# sidebar specific -from weave.legacy.panels.panel_query import Query, QueryCondition, QueryConfig -from weave.legacy.panels.panel_sections import Sections -from weave.legacy.panels.panel_select import SelectEditor, SelectEditorConfig - -# editors -from weave.legacy.panels.panel_slider import Slider, SliderConfig -from weave.legacy.panels.panel_string import PanelString -from weave.legacy.panels.panel_string_editor import StringEditor -from weave.legacy.panels.panel_table import ColumnDef, Table, TableColumn -from weave.legacy.panels.panel_trace import Trace - -# navigation -from weave.legacy.panels.panel_weavelink import WeaveLink -from weave.legacy.panel import Panel - -_context_state.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/scripts/clear_cache.py b/weave/legacy/scripts/clear_cache.py index 2d7b9da1dcd..1fb6d737754 100644 --- a/weave/legacy/scripts/clear_cache.py +++ b/weave/legacy/scripts/clear_cache.py @@ -1,7 +1,7 @@ import os import time -from weave.legacy import cache +from weave.legacy.weave import cache # TODO: This should be split out into a scripts dir # Script to run to delete expired caches diff --git a/weave/legacy/tests/test_access.py b/weave/legacy/tests/test_access.py index 15b1157581d..f15627af727 100644 --- a/weave/legacy/tests/test_access.py +++ b/weave/legacy/tests/test_access.py @@ -1,7 +1,7 @@ import pytest import weave -from weave.legacy import ( +from weave.legacy.weave import ( artifact_fs, artifact_local, environment, @@ -25,7 +25,7 @@ def public_env(): def test_access_file(public_env): with pytest.raises(errors.WeaveAccessDeniedError): - weave.use(weave.legacy.ops.local_path("/tmp/bad.json")) + weave.use(weave.legacy.weave.ops.local_path("/tmp/bad.json")) @pytest.mark.parametrize("path", ["..", "/tmp", "//tmp", "//tmp/bad.json", "/tmp/.../"]) diff --git a/weave/legacy/tests/test_api.py b/weave/legacy/tests/test_api.py index 75111a6fc8c..25ca58a9d8b 100644 --- a/weave/legacy/tests/test_api.py +++ b/weave/legacy/tests/test_api.py @@ -1,8 +1,8 @@ import shutil -from weave.legacy import api as weave +from weave.legacy.weave import api as weave -from ...legacy.show import _show_params +from ...legacy.weave.show import _show_params def test_print_save_val(): diff --git a/weave/legacy/tests/test_arrow.py b/weave/legacy/tests/test_arrow.py index afad3e65b2d..b9966221b94 100644 --- a/weave/legacy/tests/test_arrow.py +++ b/weave/legacy/tests/test_arrow.py @@ -7,8 +7,8 @@ import pytest from PIL import Image -from weave.legacy import api as weave -from weave.legacy import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import ( box, context_state, errors, @@ -21,20 +21,20 @@ # If you're thinking of import vectorize here, don't! Put your # tests in test_arrow_vectorizer.py instead -from weave.legacy import ops_arrow as arrow -from weave.legacy import weave_types as types -from weave.legacy.arrow import constructors -from weave.legacy.arrow.arrow_tags import ( +from weave.legacy.weave import ops_arrow as arrow +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow import constructors +from weave.legacy.weave.arrow.arrow_tags import ( recursively_encode_pyarrow_strings_as_dictionaries, ) -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, ) -from weave.legacy.op_def import map_type -from weave.legacy.ops_domain import project_ops -from weave.legacy.ops_primitives import list_, make_list +from weave.legacy.weave.op_def import map_type +from weave.legacy.weave.ops_domain import project_ops +from weave.legacy.weave.ops_primitives import list_, make_list from .util import list_arrow_test_helpers as lath from weave.legacy.tests.util import tag_test_util as ttu @@ -1316,7 +1316,7 @@ def test_stddev(): def test_join_all_struct_val(): - from weave.legacy import ops_arrow + from weave.legacy.weave import ops_arrow t1 = arrow.to_arrow([{"a": 5, "b": {"c": 6}}]) t2 = arrow.to_arrow([{"a": 9, "b": {"c": 10}}, {"a": 5, "b": {"c": 11}}]) diff --git a/weave/legacy/tests/test_arrow_awl.py b/weave/legacy/tests/test_arrow_awl.py index 62babfc75bd..03e857e288f 100644 --- a/weave/legacy/tests/test_arrow_awl.py +++ b/weave/legacy/tests/test_arrow_awl.py @@ -3,10 +3,10 @@ import pytest import weave -import weave.legacy.weave_types as types -from weave.legacy import box, ops_arrow -from weave.legacy.arrow.convert import to_arrow -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +import weave.legacy.weave.weave_types as types +from weave.legacy.weave import box, ops_arrow +from weave.legacy.weave.arrow.convert import to_arrow +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type from weave.legacy.tests.util.concrete_tagged_value import ( TaggedValue, diff --git a/weave/legacy/tests/test_arrow_concat.py b/weave/legacy/tests/test_arrow_concat.py index 45317b3f39a..4e8f551475a 100644 --- a/weave/legacy/tests/test_arrow_concat.py +++ b/weave/legacy/tests/test_arrow_concat.py @@ -3,9 +3,9 @@ import pytest import weave -from weave.legacy import artifact_local, storage -from weave.legacy.ops_arrow import to_arrow -from weave.legacy.ops_domain import wbmedia +from weave.legacy.weave import artifact_local, storage +from weave.legacy.weave.ops_arrow import to_arrow +from weave.legacy.weave.ops_domain import wbmedia # This is not a valid artifact, but we need one to test. We set _read_dirname # so that the artifact's is_saved property is True, so that everything works diff --git a/weave/legacy/tests/test_arrow_perf.py b/weave/legacy/tests/test_arrow_perf.py index 2ee09f152ed..87f7c2c4576 100644 --- a/weave/legacy/tests/test_arrow_perf.py +++ b/weave/legacy/tests/test_arrow_perf.py @@ -5,7 +5,7 @@ import pytest import weave -from weave.legacy import ops_arrow, ops_primitives +from weave.legacy.weave import ops_arrow, ops_primitives @pytest.mark.skip(reason="Performance test") diff --git a/weave/legacy/tests/test_arrow_topy.py b/weave/legacy/tests/test_arrow_topy.py index 8e6d87e5c70..59317cc3ce8 100644 --- a/weave/legacy/tests/test_arrow_topy.py +++ b/weave/legacy/tests/test_arrow_topy.py @@ -4,7 +4,7 @@ import pytest import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow from weave.legacy.tests.util.concrete_tagged_value import ( TaggedValue, diff --git a/weave/legacy/tests/test_arrow_vectorizer.py b/weave/legacy/tests/test_arrow_vectorizer.py index 1bb224fdb4f..fc920d14c48 100644 --- a/weave/legacy/tests/test_arrow_vectorizer.py +++ b/weave/legacy/tests/test_arrow_vectorizer.py @@ -5,19 +5,19 @@ import pytest from pyarrow import compute as pc -from weave.legacy import api as weave -from weave.legacy import box, dispatch, errors, ops, weave_internal -from weave.legacy import ops_arrow as arrow -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import box, dispatch, errors, ops, weave_internal +from weave.legacy.weave import ops_arrow as arrow +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, ) -from weave.legacy.ops_arrow import arraylist_ops, convert_ops, util -from weave.legacy.ops_domain import run_ops -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_primitives import Boolean, Number, date, dict_, list_ +from weave.legacy.weave.ops_arrow import arraylist_ops, convert_ops, util +from weave.legacy.weave.ops_domain import run_ops +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_primitives import Boolean, Number, date, dict_, list_ string_ops_test_cases = [ ("eq-scalar", lambda x: x == "bc", [True, False, False]), diff --git a/weave/legacy/tests/test_artifact.py b/weave/legacy/tests/test_artifact.py index 2c62094c36e..5a05479a9d0 100644 --- a/weave/legacy/tests/test_artifact.py +++ b/weave/legacy/tests/test_artifact.py @@ -1,8 +1,8 @@ import pytest import weave -from weave.legacy import artifact_fs, artifact_local, storage -from weave.legacy import ops_arrow as arrow +from weave.legacy.weave import artifact_fs, artifact_local, storage +from weave.legacy.weave import ops_arrow as arrow def test_artifact(): diff --git a/weave/legacy/tests/test_artifact_metadata.py b/weave/legacy/tests/test_artifact_metadata.py index 30e8812d4dd..889eef4137b 100644 --- a/weave/legacy/tests/test_artifact_metadata.py +++ b/weave/legacy/tests/test_artifact_metadata.py @@ -1,8 +1,8 @@ import wandb import weave -from weave.legacy import artifact_local, artifact_wandb -from weave.legacy.wandb_interface.wandb_artifact_pusher import ( +from weave.legacy.weave import artifact_local, artifact_wandb +from weave.legacy.weave.wandb_interface.wandb_artifact_pusher import ( write_artifact_to_wandb, ) @@ -56,8 +56,8 @@ def test_artifact_metadata(user_by_api_key_in_env): } # Push an artifact to wandb and verify that the metadata is correct - remote_uri = weave.legacy.ops.publish_artifact( - weave.legacy.ops.get(local_art.uri + "/obj"), + remote_uri = weave.legacy.weave.ops.publish_artifact( + weave.legacy.weave.ops.get(local_art.uri + "/obj"), "test_artifact", "test_project", None, @@ -113,7 +113,7 @@ def test_artifact_files_count(user_by_api_key_in_env): run.finish() count_node = ( - weave.legacy.ops.project(run.entity, run.project) + weave.legacy.weave.ops.project(run.entity, run.project) .artifact("test") .membershipForAlias("v0") .artifactVersion() diff --git a/weave/legacy/tests/test_assignment.py b/weave/legacy/tests/test_assignment.py index dfc10703f07..6f321857939 100644 --- a/weave/legacy/tests/test_assignment.py +++ b/weave/legacy/tests/test_assignment.py @@ -7,7 +7,7 @@ @pytest.mark.parametrize("type_name, type_cls", [(t.name, t) for t in types]) def test_const_assignment(type_name, type_cls): - from weave.legacy.ops_domain import wb_domain_types as wdt + from weave.legacy.weave.ops_domain import wb_domain_types as wdt params = [] # Quick list of types that don't work with this parametrization diff --git a/weave/legacy/tests/test_async.py b/weave/legacy/tests/test_async.py index e0a9632154e..a7601ceda3e 100644 --- a/weave/legacy/tests/test_async.py +++ b/weave/legacy/tests/test_async.py @@ -1,6 +1,6 @@ import pytest -from weave.legacy import api, async_demo, ops, runs, storage +from weave.legacy.weave import api, async_demo, ops, runs, storage def test_run_basic(): diff --git a/weave/legacy/tests/test_async_queue.py b/weave/legacy/tests/test_async_queue.py index 18c37511e38..5556f89b82a 100644 --- a/weave/legacy/tests/test_async_queue.py +++ b/weave/legacy/tests/test_async_queue.py @@ -4,7 +4,7 @@ import aioprocessing import pytest -from weave.legacy.async_queue import ProcessQueue, Queue, ThreadQueue +from weave.legacy.weave.async_queue import ProcessQueue, Queue, ThreadQueue async def process_producer(queue: Queue) -> None: diff --git a/weave/legacy/tests/test_basic_ops.py b/weave/legacy/tests/test_basic_ops.py index e8868da0e03..a40920b967d 100644 --- a/weave/legacy/tests/test_basic_ops.py +++ b/weave/legacy/tests/test_basic_ops.py @@ -1,8 +1,8 @@ -from weave.legacy import api as weave -from weave.legacy import box, ops -from weave.legacy.ops_primitives import number -from weave.legacy.ops_primitives.string import * -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import api as weave +from weave.legacy.weave import box, ops +from weave.legacy.weave.ops_primitives import number +from weave.legacy.weave.ops_primitives.string import * +from weave.legacy.weave.weave_internal import make_const_node def test_number_ops(): diff --git a/weave/legacy/tests/test_box.py b/weave/legacy/tests/test_box.py index 22a596cbdaf..4ece9ef3949 100644 --- a/weave/legacy/tests/test_box.py +++ b/weave/legacy/tests/test_box.py @@ -1,6 +1,6 @@ import datetime -from weave.legacy import box +from weave.legacy.weave import box def test_boxdatetime(): diff --git a/weave/legacy/tests/test_cache.py b/weave/legacy/tests/test_cache.py index 0fe6243504c..7f46a6723cb 100644 --- a/weave/legacy/tests/test_cache.py +++ b/weave/legacy/tests/test_cache.py @@ -2,7 +2,7 @@ import os import time -from weave.legacy import cache, environment +from weave.legacy.weave import cache, environment def test_lru_time_window_cache(): diff --git a/weave/legacy/tests/test_codify.py b/weave/legacy/tests/test_codify.py index 9d73a391f7c..fdffd2e4e51 100644 --- a/weave/legacy/tests/test_codify.py +++ b/weave/legacy/tests/test_codify.py @@ -2,7 +2,7 @@ import pytest import weave -from weave.legacy import panels +from weave.legacy.weave import panels # IMPORTANT: Do not import other symbols inside of weave # so that we ensure the produced code only relies on the weave symbol. @@ -19,24 +19,24 @@ ), ( panels.Group(), - "weave.legacy.panels.panel_group.Group()", + "weave.legacy.weave.panels.panel_group.Group()", ), ( lambda: panels.Table( - weave.legacy.ops.range(1, 100, 1).map( - lambda row: weave.legacy.ops_primitives.dict.dict_( + weave.legacy.weave.ops.range(1, 100, 1).map( + lambda row: weave.legacy.weave.ops_primitives.dict.dict_( x=row, - y=weave.legacy.ops_primitives.list_.make_list( + y=weave.legacy.weave.ops_primitives.list_.make_list( a=row, ), ) ) ), - lambda: """weave.legacy.panels.panel_table.Table( - weave.legacy.ops_arrow.list_range.range(1, 100, 1,).map( - lambda row: weave.legacy.ops_primitives.dict.dict_( + lambda: """weave.legacy.weave.panels.panel_table.Table( + weave.legacy.weave.ops_arrow.list_range.range(1, 100, 1,).map( + lambda row: weave.legacy.weave.ops_primitives.dict.dict_( x=row, - y=weave.legacy.ops_primitives.list_.make_list( + y=weave.legacy.weave.ops_primitives.list_.make_list( a=row, ), ), @@ -45,16 +45,16 @@ ), ( lambda: panels.Plot( - weave.legacy.ops.range(1, 100, 1).map( - lambda row: weave.legacy.ops_primitives.dict.dict_( + weave.legacy.weave.ops.range(1, 100, 1).map( + lambda row: weave.legacy.weave.ops_primitives.dict.dict_( x=row, y=row**2, ) ) ), - lambda: """weave.legacy.panels.panel_plot.Plot( - weave.legacy.ops_arrow.list_range.range(1, 100, 1,).map( - lambda row: weave.legacy.ops_primitives.dict.dict_( + lambda: """weave.legacy.weave.panels.panel_plot.Plot( + weave.legacy.weave.ops_arrow.list_range.range(1, 100, 1,).map( + lambda row: weave.legacy.weave.ops_primitives.dict.dict_( x=row, y=row.powBinary(2,), ), @@ -65,7 +65,7 @@ lambda: panels.Group( items={ "table": panels.Table( - weave.legacy.ops.range(1, 100, 1), + weave.legacy.weave.ops.range(1, 100, 1), columns=[ lambda row: row, lambda row: row**2, @@ -99,26 +99,26 @@ ), } ), - lambda: """weave.legacy.panels.panel_group.Group( + lambda: """weave.legacy.weave.panels.panel_group.Group( items={ - "table": weave.legacy.panels.panel_table.Table( - weave.legacy.ops_arrow.list_range.range(1, 100, 1,), + "table": weave.legacy.weave.panels.panel_table.Table( + weave.legacy.weave.ops_arrow.list_range.range(1, 100, 1,), columns=[ lambda row: row, lambda row: row.powBinary(2,), ], ), - "all_rows": lambda table: weave.legacy.panels.panel_plot.Plot( + "all_rows": lambda table: weave.legacy.weave.panels.panel_plot.Plot( table.all_rows(), x=lambda row: row["c_0"], y=lambda row: row["c_1"], ), - "derived": lambda table, all_rows: weave.legacy.panels.panel_group.Group( + "derived": lambda table, all_rows: weave.legacy.weave.panels.panel_group.Group( layoutMode="horizontal", items={ - "rows": weave.legacy.panels.panel_group.Group( + "rows": weave.legacy.weave.panels.panel_group.Group( items={ - "pinned_rows": weave.legacy.panels.panel_plot.Plot( + "pinned_rows": weave.legacy.weave.panels.panel_plot.Plot( table.pinned_rows(), x=lambda row: row["c_0"], y=lambda row: row["c_1"], @@ -126,7 +126,7 @@ "active_row": lambda pinned_rows: table.active_row(), }, ), - "data": lambda rows: weave.legacy.panels.panel_group.Group( + "data": lambda rows: weave.legacy.weave.panels.panel_group.Group( items={ "pinned_data": table.pinned_data(), "active_data": lambda pinned_data: table.active_data(), @@ -153,7 +153,7 @@ def test_group_case(cereal_csv, consistent_table_col_ids): panels.Group( items={ "plot": panels.Plot( - weave.legacy.ops.local_path(cereal_csv).readcsv(), + weave.legacy.weave.ops.local_path(cereal_csv).readcsv(), x=lambda row: row["protein"], y=lambda row: row["calories"], ), @@ -166,16 +166,16 @@ def test_group_case(cereal_csv, consistent_table_col_ids): ), } ), - '''weave.legacy.panels.panel_group.Group( + '''weave.legacy.weave.panels.panel_group.Group( items={ - "plot": weave.legacy.panels.panel_plot.Plot( - weave.legacy.ops.local_path("''' + "plot": weave.legacy.weave.panels.panel_plot.Plot( + weave.legacy.weave.ops.local_path("''' + cereal_csv + """",).readcsv(), x=lambda row: row["protein"], y=lambda row: row["calories"], ), - "table": lambda plot: weave.legacy.panels.panel_table.Table( + "table": lambda plot: weave.legacy.weave.panels.panel_table.Table( plot.selected_rows(), columns=[ lambda row: row["c_0"], @@ -191,12 +191,12 @@ def test_group_case(cereal_csv, consistent_table_col_ids): def test_plot_case(cereal_csv, consistent_table_col_ids): _test_object_codification( panels.Plot( - weave.legacy.ops.local_path(cereal_csv).readcsv(), + weave.legacy.weave.ops.local_path(cereal_csv).readcsv(), x=lambda row: row["protein"], y=lambda row: row["calories"], ), - f"""weave.legacy.panels.panel_plot.Plot( - weave.legacy.ops.local_path('{cereal_csv}',).readcsv(), + f"""weave.legacy.weave.panels.panel_plot.Plot( + weave.legacy.weave.ops.local_path('{cereal_csv}',).readcsv(), x=lambda row: row["protein"], y=lambda row: row["calories"], )""", @@ -206,14 +206,14 @@ def test_plot_case(cereal_csv, consistent_table_col_ids): def test_table_case(cereal_csv, consistent_table_col_ids): _test_object_codification( panels.Table( - weave.legacy.ops.local_path(cereal_csv).readcsv(), + weave.legacy.weave.ops.local_path(cereal_csv).readcsv(), columns=[ lambda row: row["protein"], lambda row: row["calories"], ], ), - f"""weave.legacy.panels.panel_table.Table( - weave.legacy.ops.local_path('{cereal_csv}',).readcsv(), + f"""weave.legacy.weave.panels.panel_table.Table( + weave.legacy.weave.ops.local_path('{cereal_csv}',).readcsv(), columns=[ lambda row: row["protein"], lambda row: row["calories"], @@ -223,7 +223,7 @@ def test_table_case(cereal_csv, consistent_table_col_ids): def _test_object_codification(panel, code=None): - panel_code = weave.legacy.codify.object_to_code(panel) + panel_code = weave.legacy.weave.codify.object_to_code(panel) generated_panel = eval(panel_code) diff --git a/weave/legacy/tests/test_compile.py b/weave/legacy/tests/test_compile.py index dc5679d769d..6e1895eff25 100644 --- a/weave/legacy/tests/test_compile.py +++ b/weave/legacy/tests/test_compile.py @@ -2,14 +2,14 @@ import wandb import weave -from weave.legacy import async_demo, compile, graph -from weave.legacy import weave_types as types -from weave.legacy.api import use -from weave.legacy.dispatch import RuntimeOutputNode -from weave.legacy.ops_arrow import to_arrow -from weave.legacy.ops_arrow.vectorize import raise_on_python_bailout -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable -from weave.legacy.weave_internal import const, define_fn, make_const_node +from weave.legacy.weave import async_demo, compile, graph +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import use +from weave.legacy.weave.dispatch import RuntimeOutputNode +from weave.legacy.weave.ops_arrow import to_arrow +from weave.legacy.weave.ops_arrow.vectorize import raise_on_python_bailout +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave.weave_internal import const, define_fn, make_const_node def test_automatic_await_compile(): @@ -144,21 +144,21 @@ def test_executing_js_multi_root(): def test_optimize_merge_empty_dict(): - non_empty_dict = weave.legacy.ops.dict_(a=5, b=2) + non_empty_dict = weave.legacy.weave.ops.dict_(a=5, b=2) assert ( compile.compile_simple_optimizations( - [weave.legacy.ops.TypedDict.merge(non_empty_dict, weave.legacy.ops.dict_())] + [weave.legacy.weave.ops.TypedDict.merge(non_empty_dict, weave.legacy.weave.ops.dict_())] )[0].to_json() == non_empty_dict.to_json() ) assert ( compile.compile_simple_optimizations( - [weave.legacy.ops.TypedDict.merge(weave.legacy.ops.dict_(), non_empty_dict)] + [weave.legacy.weave.ops.TypedDict.merge(weave.legacy.weave.ops.dict_(), non_empty_dict)] )[0].to_json() == non_empty_dict.to_json() ) - non_simplified_merge = weave.legacy.ops.TypedDict.merge( - weave.legacy.ops.dict_(j=3), non_empty_dict + non_simplified_merge = weave.legacy.weave.ops.TypedDict.merge( + weave.legacy.weave.ops.dict_(j=3), non_empty_dict ) assert ( compile.compile_simple_optimizations([non_simplified_merge])[0].to_json() @@ -167,12 +167,12 @@ def test_optimize_merge_empty_dict(): def test_compile_lambda_uniqueness(): - list_node_1 = weave.legacy.ops.make_list(a=make_const_node(weave.types.Number(), 1)) - list_node_2 = weave.legacy.ops.make_list(a=make_const_node(weave.types.Number(), 2)) + list_node_1 = weave.legacy.weave.ops.make_list(a=make_const_node(weave.types.Number(), 1)) + list_node_2 = weave.legacy.weave.ops.make_list(a=make_const_node(weave.types.Number(), 2)) fn_node = define_fn({"row": weave.types.Number()}, lambda row: row + 1) mapped_1 = list_node_1.map(fn_node) mapped_2 = list_node_2.map(fn_node) - combined = weave.legacy.ops.make_list(a=mapped_1, b=mapped_2) + combined = weave.legacy.weave.ops.make_list(a=mapped_1, b=mapped_2) concatted = combined.concat() # list node contains 2 nodes (const, list), x 2 = 4 @@ -204,7 +204,7 @@ def test_compile_lambda_uniqueness(): # This test demonstrates successful execution when there is an explicit # const function instead of a direct node (resulting in an intermediate execution op) # """ -# history_node = weave.legacy.ops.project(run.entity, run.project).run(run.id).history2() +# history_node = weave.legacy.weave.ops.project(run.entity, run.project).run(run.id).history2() # pick = const(history_node).pick("val") # res = weave.use(pick) # assert res.to_pylist_notags() == list(range(10)) @@ -223,7 +223,7 @@ def test_compile_through_function_call(user_by_api_key_in_env): fn_node = define_fn( {"entity_name": types.String()}, lambda entity_name: ( - weave.legacy.ops.project(entity_name, run.project).run(run.id).history2() + weave.legacy.weave.ops.project(entity_name, run.project).run(run.id).history2() ), ) called_node = fn_node(run.entity) @@ -237,7 +237,7 @@ def test_compile_list_flatten_to_awl_concat(): # When the outer list-structure is a list, we want to dispatch to concat, preferably AWL-concat # when the outer list-structure is an AWL, we want to dispatch ensure that we use AWL ops # list of lists - list_list_node = weave.legacy.ops.make_list(a=[1], b=[2]) + list_list_node = weave.legacy.weave.ops.make_list(a=[1], b=[2]) list_list_node_concat = list_list_node.concat() list_list_node_flatten = list_list_node.flatten() list_list_node_concat_compiled = compile.compile([list_list_node_concat])[0] @@ -245,7 +245,7 @@ def test_compile_list_flatten_to_awl_concat(): assert list_list_node_concat_compiled.from_op.name == "concat" assert list_list_node_flatten_compiled.from_op.name == "flatten" # list of awls - list_awl_node = weave.legacy.ops.make_list(a=to_arrow([1]), b=to_arrow([2])) + list_awl_node = weave.legacy.weave.ops.make_list(a=to_arrow([1]), b=to_arrow([2])) list_awl_node_concat = list_awl_node.concat() list_awl_node_flatten = list_awl_node.flatten() list_awl_node_concat_compiled = compile.compile([list_awl_node_concat])[0] diff --git a/weave/legacy/tests/test_complex_calls.py b/weave/legacy/tests/test_complex_calls.py index bb480e1b800..94bebf4cf9f 100644 --- a/weave/legacy/tests/test_complex_calls.py +++ b/weave/legacy/tests/test_complex_calls.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import weave_internal +from weave.legacy.weave import weave_internal def test_weave_fn_in_data(): diff --git a/weave/legacy/tests/test_cond.py b/weave/legacy/tests/test_cond.py index be9b7888e45..f6030535c74 100644 --- a/weave/legacy/tests/test_cond.py +++ b/weave/legacy/tests/test_cond.py @@ -1,12 +1,12 @@ import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow def test_cond_basic(): - assert weave.use(weave.legacy.ops.cond({"a": True}, {"a": 5})) == 5 - assert weave.use(weave.legacy.ops.cond({"a": False}, {"a": 5})) == None + assert weave.use(weave.legacy.weave.ops.cond({"a": True}, {"a": 5})) == 5 + assert weave.use(weave.legacy.weave.ops.cond({"a": False}, {"a": 5})) == None assert ( - weave.use(weave.legacy.ops.cond({"a": False, "b": True}, {"a": 5, "b": 6})) == 6 + weave.use(weave.legacy.weave.ops.cond({"a": False, "b": True}, {"a": 5, "b": 6})) == 6 ) @@ -47,9 +47,9 @@ def test_cond_vector(): ) assert weave.use( conds.map( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_(**{"a": row["a"], "b": row["b"]}), - weave.legacy.ops.dict_(**{"a": row["val_a"], "b": row["val_b"]}), + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_(**{"a": row["a"], "b": row["b"]}), + weave.legacy.weave.ops.dict_(**{"a": row["val_a"], "b": row["val_b"]}), ) ) ).to_pylist_raw() == [5, None, 10] @@ -68,9 +68,9 @@ def test_cond_vector_arr_value(): ) assert weave.use( conds.map( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_(**{"a": row["a"], "b": row["b"]}), - weave.legacy.ops.dict_(**{"a": row["val_a"], "b": row["val_b"]}), + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_(**{"a": row["a"], "b": row["b"]}), + weave.legacy.weave.ops.dict_(**{"a": row["val_a"], "b": row["val_b"]}), ) ) ).to_pylist_raw() == [[1, 2], None, [11, 12]] @@ -89,9 +89,9 @@ def test_cond_vector_mixed(): ) assert weave.use( conds.map( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_(**{"a": row["a"], "b": row["b"]}), - weave.legacy.ops.dict_(**{"a": row["val_a"], "b": 99}), + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_(**{"a": row["a"], "b": row["b"]}), + weave.legacy.weave.ops.dict_(**{"a": row["val_a"], "b": 99}), ) ) ).to_pylist_raw() == [1, None, 99] @@ -110,9 +110,9 @@ def test_cond_vector_mixed_arr_value(): ) assert weave.use( conds.map( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_(**{"a": row["a"], "b": row["b"]}), - weave.legacy.ops.dict_(**{"a": row["val_a"], "b": [99, 100]}), + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_(**{"a": row["a"], "b": row["b"]}), + weave.legacy.weave.ops.dict_(**{"a": row["val_a"], "b": [99, 100]}), ) ) ).to_pylist_raw() == [[1, 2], None, [99, 100]] diff --git a/weave/legacy/tests/test_const_type_mapper.py b/weave/legacy/tests/test_const_type_mapper.py index 792294572d0..ce2fccef3c0 100644 --- a/weave/legacy/tests/test_const_type_mapper.py +++ b/weave/legacy/tests/test_const_type_mapper.py @@ -1,7 +1,7 @@ import typing import weave -from weave.legacy import context_state, weave_internal +from weave.legacy.weave import context_state, weave_internal _loading_builtins_token = context_state.set_loading_built_ins() diff --git a/weave/legacy/tests/test_custom_types.py b/weave/legacy/tests/test_custom_types.py index 4ee201e8071..75a1aaeefdc 100644 --- a/weave/legacy/tests/test_custom_types.py +++ b/weave/legacy/tests/test_custom_types.py @@ -1,9 +1,9 @@ import pytest from PIL import Image -from weave.legacy import api as weave -from weave.legacy import context_state as _context -from weave.legacy import ops_arrow +from weave.legacy.weave import api as weave +from weave.legacy.weave import context_state as _context +from weave.legacy.weave import ops_arrow from ... import errors from weave.legacy.tests.util import geom diff --git a/weave/legacy/tests/test_datetime_timestamp.py b/weave/legacy/tests/test_datetime_timestamp.py index d878cff1d19..c53fc6c38ec 100644 --- a/weave/legacy/tests/test_datetime_timestamp.py +++ b/weave/legacy/tests/test_datetime_timestamp.py @@ -4,7 +4,7 @@ import pytest import weave -from weave.legacy.ops_primitives import date +from weave.legacy.weave.ops_primitives import date def assert_date_string( diff --git a/weave/legacy/tests/test_decorators.py b/weave/legacy/tests/test_decorators.py index 7682136833f..72ec915a0ae 100644 --- a/weave/legacy/tests/test_decorators.py +++ b/weave/legacy/tests/test_decorators.py @@ -1,7 +1,7 @@ -from weave.legacy import api as weave -from weave.legacy import storage -from weave.legacy import weave_types as types -from weave.legacy.decorator_op import op +from weave.legacy.weave import api as weave +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.decorator_op import op def test_function_op_name(): diff --git a/weave/legacy/tests/test_derive_op.py b/weave/legacy/tests/test_derive_op.py index 637255a8ca7..ee3dc7f6c03 100644 --- a/weave/legacy/tests/test_derive_op.py +++ b/weave/legacy/tests/test_derive_op.py @@ -1,6 +1,6 @@ -from weave.legacy import api as weave +from weave.legacy.weave import api as weave -from ...legacy import registry_mem +from ...legacy.weave import registry_mem def test_mapped_add(): diff --git a/weave/legacy/tests/test_dispatch.py b/weave/legacy/tests/test_dispatch.py index 253fb3a8b2c..b9c3eacd833 100644 --- a/weave/legacy/tests/test_dispatch.py +++ b/weave/legacy/tests/test_dispatch.py @@ -1,7 +1,7 @@ import pytest import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow def test_dispatch_lambda(): diff --git a/weave/legacy/tests/test_examples.py b/weave/legacy/tests/test_examples.py index 1fc1a60d773..01b390526e3 100644 --- a/weave/legacy/tests/test_examples.py +++ b/weave/legacy/tests/test_examples.py @@ -1,8 +1,8 @@ import math import typing -from weave.legacy import api as weave -from weave.legacy import context, context_state +from weave.legacy.weave import api as weave +from weave.legacy.weave import context, context_state class XOnly(typing.TypedDict): diff --git a/weave/legacy/tests/test_execute.py b/weave/legacy/tests/test_execute.py index 8c77e72a21e..bb8c4d73e9f 100644 --- a/weave/legacy/tests/test_execute.py +++ b/weave/legacy/tests/test_execute.py @@ -4,14 +4,14 @@ import pytest import weave -from weave.legacy import api, environment, execute, ops, weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import api, environment, execute, ops, weave_internal +from weave.legacy.weave import weave_types as types from . import test_wb execute_test_count_op_run_count = 0 -from weave.legacy import context_state as _context_state +from weave.legacy.weave import context_state as _context_state _loading_builtins_token = _context_state.set_loading_built_ins() @@ -173,12 +173,12 @@ def test_cache_column(): expected_result = [{"x": x, "y": x + 10000} for x in input_vals] l = weave.save(input_vals) - mapped = l.map(lambda x: weave.legacy.ops.dict_(x=x, y=expensive_op(x))) + mapped = l.map(lambda x: weave.legacy.weave.ops.dict_(x=x, y=expensive_op(x))) res = weave.use(mapped) assert res == expected_result latest_obj = weave.use( - weave.legacy.ops.get("local-artifact:///run-op-expensive_op:latest/obj") + weave.legacy.weave.ops.get("local-artifact:///run-op-expensive_op:latest/obj") ) assert len(latest_obj) == len(input_vals) assert len(weave.versions(latest_obj)) == 1 @@ -197,7 +197,7 @@ def test_none_not_cached(): assert res == expected_result latest_obj = weave.use( - weave.legacy.ops.get("local-artifact:///run-op-expensive_op:latest/obj") + weave.legacy.weave.ops.get("local-artifact:///run-op-expensive_op:latest/obj") ) assert len(latest_obj) == 1 # not 2! None not cached! assert len(weave.versions(latest_obj)) == 1 diff --git a/weave/legacy/tests/test_execute_fast.py b/weave/legacy/tests/test_execute_fast.py index 0208f29e662..01d26d43b18 100644 --- a/weave/legacy/tests/test_execute_fast.py +++ b/weave/legacy/tests/test_execute_fast.py @@ -1,6 +1,6 @@ import weave -from weave.legacy import dispatch, weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import dispatch, weave_internal +from weave.legacy.weave import weave_types as types from weave.legacy.tests.util import weavejs_ops @@ -35,7 +35,7 @@ def test_empty_list(): map_fn = weave_internal.define_fn( {"row": arr.type.object_type}, lambda row: row.merge( - weave.legacy.ops.dict_(output_classid=row["output_class"].id()) + weave.legacy.weave.ops.dict_(output_classid=row["output_class"].id()) ), ) diff --git a/weave/legacy/tests/test_execution_graphs.py b/weave/legacy/tests/test_execution_graphs.py index 37c6caee650..cf7faf64b8a 100644 --- a/weave/legacy/tests/test_execution_graphs.py +++ b/weave/legacy/tests/test_execution_graphs.py @@ -3,8 +3,8 @@ import os import zlib -from weave.legacy import serialize, storage -from weave.legacy.server import handle_request +from weave.legacy.weave import serialize, storage +from weave.legacy.weave.server import handle_request def test_graph_playback(dev_only_admin_env_override): diff --git a/weave/legacy/tests/test_file.py b/weave/legacy/tests/test_file.py index d8561dae6a0..e1b8299c419 100644 --- a/weave/legacy/tests/test_file.py +++ b/weave/legacy/tests/test_file.py @@ -1,7 +1,7 @@ import pytest import weave -from weave.legacy import api, context_state, environment, errors, ops +from weave.legacy.weave import api, context_state, environment, errors, ops def test_dir(): diff --git a/weave/legacy/tests/test_filesystem.py b/weave/legacy/tests/test_filesystem.py index 3fd8cde70cb..ab8599d9505 100644 --- a/weave/legacy/tests/test_filesystem.py +++ b/weave/legacy/tests/test_filesystem.py @@ -2,7 +2,7 @@ import pytest -from weave.legacy import environment, errors, filesystem +from weave.legacy.weave import environment, errors, filesystem @pytest.fixture() diff --git a/weave/legacy/tests/test_gql_to_weave.py b/weave/legacy/tests/test_gql_to_weave.py index e3dc869d9bf..8046b0bb81d 100644 --- a/weave/legacy/tests/test_gql_to_weave.py +++ b/weave/legacy/tests/test_gql_to_weave.py @@ -1,5 +1,5 @@ -from weave.legacy import gql_to_weave -from weave.legacy import weave_types as types +from weave.legacy.weave import gql_to_weave +from weave.legacy.weave import weave_types as types def test_simple_query(): diff --git a/weave/legacy/tests/test_graph.py b/weave/legacy/tests/test_graph.py index ca04c2722d5..3f1c7ce886f 100644 --- a/weave/legacy/tests/test_graph.py +++ b/weave/legacy/tests/test_graph.py @@ -1,6 +1,6 @@ import weave -from weave.legacy import graph, weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import graph, weave_internal +from weave.legacy.weave import weave_types as types def test_map_dag_produces_same_len(): diff --git a/weave/legacy/tests/test_graph_debug.py b/weave/legacy/tests/test_graph_debug.py index 957e58699f9..ebe81d6c295 100644 --- a/weave/legacy/tests/test_graph_debug.py +++ b/weave/legacy/tests/test_graph_debug.py @@ -1,6 +1,6 @@ import typing -from weave.legacy import decorator_op, graph, graph_debug +from weave.legacy.weave import decorator_op, graph, graph_debug class RowType(typing.TypedDict): diff --git a/weave/legacy/tests/test_hypothesis.py b/weave/legacy/tests/test_hypothesis.py index 915711acfac..90ed51b1626 100644 --- a/weave/legacy/tests/test_hypothesis.py +++ b/weave/legacy/tests/test_hypothesis.py @@ -44,9 +44,9 @@ from hypothesis.strategies import composite import weave -from weave.legacy import artifact_local, ops_arrow, ops_primitives, storage -from weave.legacy.arrow import convert -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave import artifact_local, ops_arrow, ops_primitives, storage +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.language_features.tagging import tag_store # Jack this up to find more bugs. EXAMPLES_PER_TEST = 100 diff --git a/weave/legacy/tests/test_infer_types.py b/weave/legacy/tests/test_infer_types.py index 137af307186..3d08b266378 100644 --- a/weave/legacy/tests/test_infer_types.py +++ b/weave/legacy/tests/test_infer_types.py @@ -1,6 +1,6 @@ import typing -from weave.legacy import graph, infer_types, weave_types +from weave.legacy.weave import graph, infer_types, weave_types def test_node_with_generic(): diff --git a/weave/legacy/tests/test_io_service.py b/weave/legacy/tests/test_io_service.py index 053ee5d19cd..52d29e22a2c 100644 --- a/weave/legacy/tests/test_io_service.py +++ b/weave/legacy/tests/test_io_service.py @@ -2,7 +2,7 @@ import pytest -from weave.legacy import filesystem, io_service +from weave.legacy.weave import filesystem, io_service @pytest.mark.timeout(10) diff --git a/weave/legacy/tests/test_join.py b/weave/legacy/tests/test_join.py index ba6dd4bf70f..f3c06cc956b 100644 --- a/weave/legacy/tests/test_join.py +++ b/weave/legacy/tests/test_join.py @@ -1,6 +1,6 @@ import weave -from weave.legacy import ops_arrow -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave import ops_arrow +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable def make_stream_table(*args, **kwargs): @@ -24,7 +24,7 @@ def test_join_awls_with_stitch(user_by_api_key_in_env): feedback_st.log(row) feedback_st.finish() - joined = weave.legacy.ops.join_2( + joined = weave.legacy.weave.ops.join_2( dataset_st.rows(), feedback_st.rows(), lambda row: row["id"], diff --git a/weave/legacy/tests/test_js_compat.py b/weave/legacy/tests/test_js_compat.py index ba37ee1f65e..9dfa277e439 100644 --- a/weave/legacy/tests/test_js_compat.py +++ b/weave/legacy/tests/test_js_compat.py @@ -7,9 +7,9 @@ # Note: This file is not yet complete, there are existing fixups in the # weave Python code that I haven't documented here. -from weave.legacy import partial_object, weavejs_fixes -from weave.legacy import weave_types as types -from weave.legacy.ops_domain import wb_domain_types +from weave.legacy.weave import partial_object, weavejs_fixes +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_domain import wb_domain_types def test_const_serialization(): diff --git a/weave/legacy/tests/test_language.py b/weave/legacy/tests/test_language.py index 48277d2fcae..7c225403e2b 100644 --- a/weave/legacy/tests/test_language.py +++ b/weave/legacy/tests/test_language.py @@ -3,8 +3,8 @@ import typing import weave -from weave.legacy import box, context_state -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import box, context_state +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, diff --git a/weave/legacy/tests/test_levenshtein.py b/weave/legacy/tests/test_levenshtein.py index 8807cb7dcea..d39d74d8d1e 100644 --- a/weave/legacy/tests/test_levenshtein.py +++ b/weave/legacy/tests/test_levenshtein.py @@ -1,6 +1,6 @@ import pytest -from weave.legacy.ops_primitives.string import _levenshtein +from weave.legacy.weave.ops_primitives.string import _levenshtein @pytest.mark.parametrize( diff --git a/weave/legacy/tests/test_list_arrow_compat.py b/weave/legacy/tests/test_list_arrow_compat.py index d689161abe2..401a1ae7a5c 100644 --- a/weave/legacy/tests/test_list_arrow_compat.py +++ b/weave/legacy/tests/test_list_arrow_compat.py @@ -3,16 +3,16 @@ import numpy as np import pytest -from weave.legacy import api as weave -from weave.legacy import box, ops, weave_internal -from weave.legacy import ops_arrow as arrow -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import box, ops, weave_internal +from weave.legacy.weave import ops_arrow as arrow +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, ) -from weave.legacy.ops_primitives import dict_, list_ +from weave.legacy.weave.ops_primitives import dict_, list_ from weave.legacy.tests.util import tag_test_util as ttu from weave.legacy.tests.util import list_arrow_test_helpers as lath @@ -636,7 +636,7 @@ def test_tag_pushdown_on_list_of_lists(use_arrow): if use_arrow: list_node = arrow.ops.list_to_arrow(list_node) - from weave.legacy import context_state + from weave.legacy.weave import context_state _loading_builtins_token = context_state.set_loading_built_ins() diff --git a/weave/legacy/tests/test_list_indexing.py b/weave/legacy/tests/test_list_indexing.py index 95290dd3a11..5d76d397b8f 100644 --- a/weave/legacy/tests/test_list_indexing.py +++ b/weave/legacy/tests/test_list_indexing.py @@ -1,7 +1,7 @@ import pytest import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow LIST_CONSTRUCTORS = [ lambda x: x, diff --git a/weave/legacy/tests/test_logging.py b/weave/legacy/tests/test_logging.py index 6ef1abea8b5..0da877d2cd5 100644 --- a/weave/legacy/tests/test_logging.py +++ b/weave/legacy/tests/test_logging.py @@ -5,7 +5,7 @@ import requests from weave import weave_server -from weave.legacy import api, context, logs, ops, server +from weave.legacy.weave import api, context, logs, ops, server def test_logfile_created(fresh_server_logfile): diff --git a/weave/legacy/tests/test_mappability.py b/weave/legacy/tests/test_mappability.py index 335eff5722e..475bebb4490 100644 --- a/weave/legacy/tests/test_mappability.py +++ b/weave/legacy/tests/test_mappability.py @@ -1,10 +1,10 @@ import weave -from weave.legacy import context_state as _context -from weave.legacy import graph -from weave.legacy import weave_types as types -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import context_state as _context +from weave.legacy.weave import graph +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.weave_internal import make_const_node -from ...legacy import registry_mem +from ...legacy.weave import registry_mem _loading_builtins_token = _context.set_loading_built_ins() @@ -61,10 +61,10 @@ def test_non_mapped_use(): def test_non_mapped_serialized(): - node = weave.legacy.weave_internal.make_output_node( + node = weave.legacy.weave.weave_internal.make_output_node( weave.types.Int(), _test_add_one.name, - {"x": weave.legacy.graph.ConstNode(weave.types.Int(), 1)}, + {"x": weave.legacy.weave.graph.ConstNode(weave.types.Int(), 1)}, ) assert weave.use(node) == 2 @@ -82,11 +82,11 @@ def test_mapped_nullable_use(): def test_mapped_serialized(): - node = weave.legacy.weave_internal.make_output_node( + node = weave.legacy.weave.weave_internal.make_output_node( weave.types.Int(), _test_add_one.name, { - "x": weave.legacy.graph.ConstNode( + "x": weave.legacy.weave.graph.ConstNode( weave.types.List(weave.types.Int()), [1, 2, 3] ) }, @@ -101,10 +101,10 @@ def test_mapped_empty_use(): def test_mapped_empty_serialized(): - node = weave.legacy.weave_internal.make_output_node( + node = weave.legacy.weave.weave_internal.make_output_node( weave.types.Int(), _test_add_one.name, - {"x": weave.legacy.graph.ConstNode(weave.types.List(weave.types.Int()), [])}, + {"x": weave.legacy.weave.graph.ConstNode(weave.types.List(weave.types.Int()), [])}, ) assert weave.use(node) == [] @@ -123,7 +123,7 @@ def test_fn(self, a: int) -> int: node = TestType().test_fn(1) assert weave.use(node) == 2 - node_list = weave.legacy.ops.make_list(**{"0": TestType(), "1": TestType()}) + node_list = weave.legacy.weave.ops.make_list(**{"0": TestType(), "1": TestType()}) node = node_list.test_fn(1) assert weave.use(node) == [2, 2] diff --git a/weave/legacy/tests/test_mappers_arrow.py b/weave/legacy/tests/test_mappers_arrow.py index ff380abdf19..18db29708b1 100644 --- a/weave/legacy/tests/test_mappers_arrow.py +++ b/weave/legacy/tests/test_mappers_arrow.py @@ -2,8 +2,8 @@ import pyarrow as pa -from weave.legacy import mappers_arrow -from weave.legacy import weave_types as types +from weave.legacy.weave import mappers_arrow +from weave.legacy.weave import weave_types as types def test_map_list(): diff --git a/weave/legacy/tests/test_mappers_python.py b/weave/legacy/tests/test_mappers_python.py index 61cd78e0c81..3b98d6fd5be 100644 --- a/weave/legacy/tests/test_mappers_python.py +++ b/weave/legacy/tests/test_mappers_python.py @@ -1,7 +1,7 @@ import math -from weave.legacy import api, context, mappers_python, val_const, weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import api, context, mappers_python, val_const, weave_internal +from weave.legacy.weave import weave_types as types def test_map_typed_dict(): diff --git a/weave/legacy/tests/test_media.py b/weave/legacy/tests/test_media.py index 502679692c1..e9d3a6708a9 100644 --- a/weave/legacy/tests/test_media.py +++ b/weave/legacy/tests/test_media.py @@ -3,8 +3,8 @@ import numpy as np import pytest -from weave.legacy import storage -from weave.legacy import weave_types as types +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types def test_nparray(): diff --git a/weave/legacy/tests/test_media_user.py b/weave/legacy/tests/test_media_user.py index 8bb37deb4f4..c7cf985fdd3 100644 --- a/weave/legacy/tests/test_media_user.py +++ b/weave/legacy/tests/test_media_user.py @@ -1,8 +1,8 @@ from PIL import Image -from weave.legacy import api as weave -from weave.legacy import context_state -from weave.legacy.ops_primitives import geom as media_user +from weave.legacy.weave import api as weave +from weave.legacy.weave import context_state +from weave.legacy.weave.ops_primitives import geom as media_user def test_im_with_metadata(): diff --git a/weave/legacy/tests/test_monitoring.py b/weave/legacy/tests/test_monitoring.py index 73fd0170fd8..d86a32260fe 100644 --- a/weave/legacy/tests/test_monitoring.py +++ b/weave/legacy/tests/test_monitoring.py @@ -5,7 +5,7 @@ import pytest import weave -from weave.legacy.monitoring import monitor +from weave.legacy.weave.monitoring import monitor ### Skipping some tests here. They are for features that no longer exist. Since we're # iterating on the API, I'm not removing them yet. diff --git a/weave/legacy/tests/test_monitoring_openai.py b/weave/legacy/tests/test_monitoring_openai.py index cc7a1b71d20..01df0bfe8a3 100644 --- a/weave/legacy/tests/test_monitoring_openai.py +++ b/weave/legacy/tests/test_monitoring_openai.py @@ -12,13 +12,13 @@ from openai.types.completion_usage import CompletionUsage import weave -from weave.legacy.monitoring import init_monitor -from weave.legacy.monitoring.openai import util -from weave.legacy.monitoring.openai.models import * -from weave.legacy.monitoring.openai.models import Context -from weave.legacy.monitoring.openai.openai import patch, unpatch -from weave.legacy.monitoring.openai.util import Context -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave.monitoring import init_monitor +from weave.legacy.weave.monitoring.openai import util +from weave.legacy.weave.monitoring.openai.models import * +from weave.legacy.weave.monitoring.openai.models import Context +from weave.legacy.weave.monitoring.openai.openai import patch, unpatch +from weave.legacy.weave.monitoring.openai.util import Context +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable @pytest.fixture @@ -453,7 +453,7 @@ def test_log_to_span_basic( streamtable = make_stream_table( stream_name, project_name=project, entity_name=entity ) - chat_completions = weave.legacy.monitoring.openai.openai.ChatCompletions( + chat_completions = weave.legacy.weave.monitoring.openai.openai.ChatCompletions( mocked_create ) create_input = dict( @@ -481,7 +481,7 @@ def test_log_to_span_streaming( reassembled_chat_completion_message, client, ): - chat_completions = weave.legacy.monitoring.openai.openai.ChatCompletions( + chat_completions = weave.legacy.weave.monitoring.openai.openai.ChatCompletions( mocked_streaming_create ) create_input = dict( @@ -512,7 +512,7 @@ async def test_log_to_span_async_streaming( reassembled_chat_completion_message, client, ): - chat_completions = weave.legacy.monitoring.openai.openai.AsyncChatCompletions( + chat_completions = weave.legacy.weave.monitoring.openai.openai.AsyncChatCompletions( mocked_async_streaming_create ) create_input = dict( diff --git a/weave/legacy/tests/test_mutation2.py b/weave/legacy/tests/test_mutation2.py index 00f5619423d..d9e28fadfb7 100644 --- a/weave/legacy/tests/test_mutation2.py +++ b/weave/legacy/tests/test_mutation2.py @@ -1,22 +1,22 @@ import weave -from weave.legacy import ref_base, uris, weave_internal +from weave.legacy.weave import ref_base, uris, weave_internal def test_mutation_set_direct_call(): - val = weave.legacy.ops.TypedDict.pick({"a": {"b": 5}}, "a")["b"] - set_result = weave.legacy.ops.set(val, 9) + val = weave.legacy.weave.ops.TypedDict.pick({"a": {"b": 5}}, "a")["b"] + set_result = weave.legacy.weave.ops.set(val, 9) assert set_result == {"a": {"b": 9}} def test_mutation_set_dispatch(): - val = weave.legacy.ops.TypedDict.pick({"a": {"b": 5}}, "a")["b"] + val = weave.legacy.weave.ops.TypedDict.pick({"a": {"b": 5}}, "a")["b"] set_result = val.set(9) assert set_result == {"a": {"b": 9}} def test_mutation_artifact(): weave.save([1, 2, 3], "art:main") - art = weave.legacy.ops.get("local-artifact:///art:main/obj") + art = weave.legacy.weave.ops.get("local-artifact:///art:main/obj") art.append(4) new_art = weave.storage.get("local-artifact:///art:main/obj") assert new_art == [1, 2, 3, 4] @@ -25,12 +25,12 @@ def test_mutation_artifact(): def test_mutation_lazy(): # This is how weavejs does it. weave.save([1, 2, 3], "art:main") - art = weave.legacy.ops.get("local-artifact:///art:main/obj") + art = weave.legacy.weave.ops.get("local-artifact:///art:main/obj") # Quote lhs expr, so that it is not evaluated. quoted_art = weave_internal.const(art) - expr = weave.legacy.ops.append.lazy_call(quoted_art, 4, {}) + expr = weave.legacy.weave.ops.append.lazy_call(quoted_art, 4, {}) weave.use(expr) new_art = weave.storage.get("local-artifact:///art:main/obj") @@ -40,9 +40,9 @@ def test_mutation_lazy(): def test_mutation_lazy_works_without_quoting(): # This relies on "auto quoting behavior". See compile_quote weave.save([1, 2, 3], "art:main") - art = weave.legacy.ops.get("local-artifact:///art:main/obj") + art = weave.legacy.weave.ops.get("local-artifact:///art:main/obj") - expr = weave.legacy.ops.append.lazy_call(art, 4, {}) + expr = weave.legacy.weave.ops.append.lazy_call(art, 4, {}) weave.use(expr) new_art = weave.storage.get("local-artifact:///art:main/obj") @@ -51,14 +51,14 @@ def test_mutation_lazy_works_without_quoting(): def test_merge(): weave.save({"a": 5, "b": 6}, "my-dict:latest") - dict_obj = weave.legacy.ops.get("local-artifact:///my-dict:latest/obj") - weave.legacy.ops.set(dict_obj["a"], 17, root_args={"branch": "my-branch"}) - modified_dict_obj = weave.legacy.ops.get("local-artifact:///my-dict:my-branch/obj") - new_uri = weave.legacy.ops.merge_artifact(modified_dict_obj) - dict_obj_node = weave.legacy.ops.get(new_uri) + dict_obj = weave.legacy.weave.ops.get("local-artifact:///my-dict:latest/obj") + weave.legacy.weave.ops.set(dict_obj["a"], 17, root_args={"branch": "my-branch"}) + modified_dict_obj = weave.legacy.weave.ops.get("local-artifact:///my-dict:my-branch/obj") + new_uri = weave.legacy.weave.ops.merge_artifact(modified_dict_obj) + dict_obj_node = weave.legacy.weave.ops.get(new_uri) assert ( weave.use(dict_obj_node) - == weave.use(weave.legacy.ops.get("local-artifact:///my-dict:latest/obj")) + == weave.use(weave.legacy.weave.ops.get("local-artifact:///my-dict:latest/obj")) == {"a": 17, "b": 6} ) @@ -68,24 +68,24 @@ def test_merge_no_version(): uri = get_node.from_op.inputs["uri"].val # type: ignore # uri now has a direct commit hash for the version - dict_obj = weave.legacy.ops.get(uri) - weave.legacy.ops.set(dict_obj["a"], 17, root_args={"branch": "my-branch"}) - modified_dict_obj = weave.legacy.ops.get("local-artifact:///my-dict:my-branch/obj") - new_uri = weave.legacy.ops.merge_artifact(modified_dict_obj) - dict_obj_node = weave.legacy.ops.get(new_uri) + dict_obj = weave.legacy.weave.ops.get(uri) + weave.legacy.weave.ops.set(dict_obj["a"], 17, root_args={"branch": "my-branch"}) + modified_dict_obj = weave.legacy.weave.ops.get("local-artifact:///my-dict:my-branch/obj") + new_uri = weave.legacy.weave.ops.merge_artifact(modified_dict_obj) + dict_obj_node = weave.legacy.weave.ops.get(new_uri) assert weave.use(dict_obj_node) == {"a": 17, "b": 6} def test_merge_list_type(): - from weave.legacy import object_context + from weave.legacy.weave import object_context weave.save([], "my-list:latest") - obj = weave.legacy.ops.get("local-artifact:///my-list:latest/obj") + obj = weave.legacy.weave.ops.get("local-artifact:///my-list:latest/obj") with object_context.object_context(): obj.append({"a": "x"}, {}) obj.append([1], {}) - assert weave.use(weave.legacy.ops.get("local-artifact:///my-list:latest/obj")) == [ + assert weave.use(weave.legacy.weave.ops.get("local-artifact:///my-list:latest/obj")) == [ {"a": "x"}, [1], ] @@ -95,7 +95,7 @@ def test_artifact_history_local(): num_versions = 4 uri = "local-artifact:///art:main/obj" weave.save([0], "art:main") - art = weave.legacy.ops.get(uri) + art = weave.legacy.weave.ops.get(uri) for i in range(num_versions): art.append(i + 1) @@ -105,7 +105,7 @@ def test_artifact_history_local(): assert new_art == total_list for i in range(num_versions): - new_uri = weave.legacy.ops.undo_artifact(weave.legacy.ops.get(uri)) + new_uri = weave.legacy.weave.ops.undo_artifact(weave.legacy.weave.ops.get(uri)) # We expect these to be the same since the branch pointer changed assert new_uri == uri res = weave.storage.get(uri) @@ -116,12 +116,12 @@ def test_artifact_history_local_from_hash(): num_versions = 4 uri = "local-artifact:///art:main/obj" weave.save([0], "art:main") - art = weave.legacy.ops.get(uri) + art = weave.legacy.weave.ops.get(uri) for i in range(num_versions): art.append(i + 1) - hash_uri = weave.legacy.uris.WeaveURI.parse(uri).to_ref().artifact.uri + hash_uri = weave.legacy.weave.uris.WeaveURI.parse(uri).to_ref().artifact.uri assert "main" not in hash_uri total_list = list(range(num_versions + 1)) @@ -130,7 +130,7 @@ def test_artifact_history_local_from_hash(): new_uri = hash_uri for i in range(num_versions): - new_uri = weave.legacy.ops.undo_artifact(weave.legacy.ops.get(new_uri)) + new_uri = weave.legacy.weave.ops.undo_artifact(weave.legacy.weave.ops.get(new_uri)) assert "main" not in new_uri res = weave.storage.get(new_uri + "/obj") assert res == total_list[: num_versions - i] @@ -140,16 +140,16 @@ def test_artifact_history_remote_with_branch(user_by_api_key_in_env): num_versions = 2 uri = "local-artifact:///art:main/obj" weave.save([0], "art:main") - art = weave.legacy.ops.get(uri) - published_art_uri = weave.legacy.ops.publish_artifact(art, "art", None, None) + art = weave.legacy.weave.ops.get(uri) + published_art_uri = weave.legacy.weave.ops.publish_artifact(art, "art", None, None) - art = weave.legacy.ops.get( + art = weave.legacy.weave.ops.get( f"wandb-artifact:///{user_by_api_key_in_env.username}/weave/art:latest/obj" ) for i in range(num_versions): res_uri = art.append(i + 1) - art = weave.legacy.ops.get(res_uri) + art = weave.legacy.weave.ops.get(res_uri) new_uri = res_uri total_list = list(range(num_versions + 1)) @@ -157,7 +157,7 @@ def test_artifact_history_remote_with_branch(user_by_api_key_in_env): assert new_art == total_list for i in range(num_versions): - new_uri = weave.legacy.ops.undo_artifact(weave.legacy.ops.get(new_uri)) + new_uri = weave.legacy.weave.ops.undo_artifact(weave.legacy.weave.ops.get(new_uri)) res = weave.storage.get(new_uri) if i == num_versions - 1: assert new_uri.startswith("wandb") @@ -170,16 +170,16 @@ def test_artifact_history_remote_with_hash(user_by_api_key_in_env): num_versions = 2 uri = "local-artifact:///art:main/obj" weave.save([0], "art:main") - art = weave.legacy.ops.get(uri) - published_art_uri = weave.legacy.ops.publish_artifact(art, "art", None, None) + art = weave.legacy.weave.ops.get(uri) + published_art_uri = weave.legacy.weave.ops.publish_artifact(art, "art", None, None) assert "latest" not in published_art_uri assert "main" not in published_art_uri - art = weave.legacy.ops.get(published_art_uri) + art = weave.legacy.weave.ops.get(published_art_uri) for i in range(num_versions): res_uri = art.append(i + 1) - art = weave.legacy.ops.get(res_uri) + art = weave.legacy.weave.ops.get(res_uri) new_uri = res_uri total_list = list(range(num_versions + 1)) @@ -187,7 +187,7 @@ def test_artifact_history_remote_with_hash(user_by_api_key_in_env): assert new_art == total_list for i in range(num_versions): - new_uri = weave.legacy.ops.undo_artifact(weave.legacy.ops.get(new_uri)) + new_uri = weave.legacy.weave.ops.undo_artifact(weave.legacy.weave.ops.get(new_uri)) res = weave.storage.get(new_uri) if i == num_versions - 1: assert new_uri.startswith("wandb") diff --git a/weave/legacy/tests/test_mutations.py b/weave/legacy/tests/test_mutations.py index 453d8af7ec5..abdc289cb73 100644 --- a/weave/legacy/tests/test_mutations.py +++ b/weave/legacy/tests/test_mutations.py @@ -1,5 +1,5 @@ -from weave.legacy import api as weave -from weave.legacy import ops, storage, weave_internal +from weave.legacy.weave import api as weave +from weave.legacy.weave import ops, storage, weave_internal def test_autocommit(cereal_csv): diff --git a/weave/legacy/tests/test_node_ref.py b/weave/legacy/tests/test_node_ref.py index 0a8d56f1e7e..75216f629ad 100644 --- a/weave/legacy/tests/test_node_ref.py +++ b/weave/legacy/tests/test_node_ref.py @@ -1,7 +1,7 @@ -from weave.legacy import api as weave -from weave.legacy import graph +from weave.legacy.weave import api as weave +from weave.legacy.weave import graph -from ...legacy import node_ref +from ...legacy.weave import node_ref def test_node_to_ref(): diff --git a/weave/legacy/tests/test_nullability.py b/weave/legacy/tests/test_nullability.py index df8b08be96a..9d07569b799 100644 --- a/weave/legacy/tests/test_nullability.py +++ b/weave/legacy/tests/test_nullability.py @@ -3,8 +3,8 @@ import pytest import weave -from weave.legacy import context_state as _context -from weave.legacy.weave_internal import make_const_node, make_output_node +from weave.legacy.weave import context_state as _context +from weave.legacy.weave.weave_internal import make_const_node, make_output_node _loading_builtins_token = _context.set_loading_built_ins() @@ -57,7 +57,7 @@ def test_basic_nullability(): def test_basic_nullability_in_mappability(): b_arr = weave.save([2]) maybe_int_arr = weave.save( - weave.legacy.graph.ConstNode( + weave.legacy.weave.graph.ConstNode( weave.types.List(weave.types.optional(weave.types.Int())), [1, None] ) ) diff --git a/weave/legacy/tests/test_number_bin.py b/weave/legacy/tests/test_number_bin.py index 22a5a5cd72a..1475ef9c49d 100644 --- a/weave/legacy/tests/test_number_bin.py +++ b/weave/legacy/tests/test_number_bin.py @@ -4,8 +4,8 @@ def test_number_bin_vector(): - awl = weave.legacy.ops.to_weave_arrow([1, 2, 3, 4, 5]) - mapped = awl.map(lambda x: x.bin(weave.legacy.ops.number_bins_fixed(2))) + awl = weave.legacy.weave.ops.to_weave_arrow([1, 2, 3, 4, 5]) + mapped = awl.map(lambda x: x.bin(weave.legacy.weave.ops.number_bins_fixed(2))) res = weave.use(mapped).to_pylist_tagged() assert res == [ {"start": 0.0, "stop": 2.0}, diff --git a/weave/legacy/tests/test_numpy.py b/weave/legacy/tests/test_numpy.py index 99275de1e7d..740c281aa9e 100644 --- a/weave/legacy/tests/test_numpy.py +++ b/weave/legacy/tests/test_numpy.py @@ -1,10 +1,10 @@ import numpy as np -from weave.legacy import artifact_fs, artifact_wandb -from weave.legacy import weave_types as types -from weave.legacy.ops_domain import table +from weave.legacy.weave import artifact_fs, artifact_wandb +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_domain import table -from ...legacy import types_numpy as numpy_types +from ...legacy.weave import types_numpy as numpy_types def test_construct_numpy_type(): diff --git a/weave/legacy/tests/test_op.py b/weave/legacy/tests/test_op.py index dfd0636c0c9..8bb75326f50 100644 --- a/weave/legacy/tests/test_op.py +++ b/weave/legacy/tests/test_op.py @@ -2,10 +2,10 @@ import pytest -from weave.legacy import api as weave -from weave.legacy import context_state, graph, storage, uris, weave_internal -from weave.legacy import context_state as _context_state -from weave.legacy import weave_types as types +from weave.legacy.weave import api as weave +from weave.legacy.weave import context_state, graph, storage, uris, weave_internal +from weave.legacy.weave import context_state as _context_state +from weave.legacy.weave import weave_types as types from . import test_helpers diff --git a/weave/legacy/tests/test_op_behaviors.py b/weave/legacy/tests/test_op_behaviors.py index 4da75809cac..efbc05caf2a 100644 --- a/weave/legacy/tests/test_op_behaviors.py +++ b/weave/legacy/tests/test_op_behaviors.py @@ -12,7 +12,7 @@ import pytest import weave -from weave.legacy import ( +from weave.legacy.weave import ( graph, graph_debug, language_nullability, @@ -22,12 +22,12 @@ storage, weave_internal, ) -from weave.legacy.language_features.tagging import make_tag_getter_op -from weave.legacy.language_features.tagging.tagged_value_type import ( +from weave.legacy.weave.language_features.tagging import make_tag_getter_op +from weave.legacy.weave.language_features.tagging.tagged_value_type import ( TaggedValueType, ) -from ...legacy import registry_mem +from ...legacy.weave import registry_mem from weave.legacy.tests.util.concrete_tagged_value import ( TaggedValue, concrete_from_tagstore, @@ -83,7 +83,7 @@ def check_case(called: graph.Node, result_type: weave.types.Type, result: typing ), f"Expected op output type: {result_type}, but got {called.type}" # This is a way to save the final output, preserving tags. - result_ref = weave.use(weave.legacy.ops.save_to_ref(called, None)) + result_ref = weave.use(weave.legacy.weave.ops.save_to_ref(called, None)) if result_ref == None: actual_result = None else: diff --git a/weave/legacy/tests/test_op_coverage.py b/weave/legacy/tests/test_op_coverage.py index cede44a1109..f933b3e978d 100644 --- a/weave/legacy/tests/test_op_coverage.py +++ b/weave/legacy/tests/test_op_coverage.py @@ -1,4 +1,4 @@ -from ...legacy import registry_mem +from ...legacy.weave import registry_mem def make_error_message(missing_ops, section_name): diff --git a/weave/legacy/tests/test_op_def.py b/weave/legacy/tests/test_op_def.py index 343452305e8..dc8fa42a100 100644 --- a/weave/legacy/tests/test_op_def.py +++ b/weave/legacy/tests/test_op_def.py @@ -5,8 +5,8 @@ import pytest -from weave.legacy import api as weave -from weave.legacy import context_state +from weave.legacy.weave import api as weave +from weave.legacy.weave import context_state _loading_builtins_token = context_state.set_loading_built_ins() diff --git a/weave/legacy/tests/test_op_def_type.py b/weave/legacy/tests/test_op_def_type.py index 303705fac3b..d6eedd6ab0f 100644 --- a/weave/legacy/tests/test_op_def_type.py +++ b/weave/legacy/tests/test_op_def_type.py @@ -1,6 +1,6 @@ import typing -from weave.legacy import op_def_type +from weave.legacy.weave import op_def_type class MyTypedDict(typing.TypedDict): diff --git a/weave/legacy/tests/test_op_dispatching.py b/weave/legacy/tests/test_op_dispatching.py index 7123a19e553..4d583e5cf86 100644 --- a/weave/legacy/tests/test_op_dispatching.py +++ b/weave/legacy/tests/test_op_dispatching.py @@ -1,11 +1,11 @@ import pytest import weave -from weave.legacy import context_state as _context -from weave.legacy import graph, weave_internal -from weave.legacy.dispatch import _dispatch_first_arg, _resolve_op_ambiguity -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_domain import wb_domain_types +from weave.legacy.weave import context_state as _context +from weave.legacy.weave import graph, weave_internal +from weave.legacy.weave.dispatch import _dispatch_first_arg, _resolve_op_ambiguity +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_domain import wb_domain_types _loading_builtins_token = _context.set_loading_built_ins() @@ -71,7 +71,7 @@ def test_pick_map(): def test_json_pick_map(): - res = weave.legacy.graph.OutputNode.from_json( + res = weave.legacy.weave.graph.OutputNode.from_json( { "nodeType": "output", "type": {"type": "list", "objectType": "number"}, @@ -169,7 +169,7 @@ def test_nested_js_dict_pick(): def test_dispatch_of_ambiguous_ops(): - artifacts_node = weave.legacy.ops.project("a", "b").artifacts() + artifacts_node = weave.legacy.weave.ops.project("a", "b").artifacts() # just making this call is the test since it is # not clear if it should be artifact-project or # tag-project diff --git a/weave/legacy/tests/test_op_serialization.py b/weave/legacy/tests/test_op_serialization.py index 7c08a59b982..c666770a379 100644 --- a/weave/legacy/tests/test_op_serialization.py +++ b/weave/legacy/tests/test_op_serialization.py @@ -1,9 +1,9 @@ import pytest import weave -import weave.legacy +import weave.legacy.weave -ops = weave.legacy.registry_mem.memory_registry.list_ops() +ops = weave.legacy.weave.registry_mem.memory_registry.list_ops() def output_type_dict_is_const_function_node(output_type_dict): diff --git a/weave/legacy/tests/test_panel_coverage.py b/weave/legacy/tests/test_panel_coverage.py index b811196a18a..f1518968b5b 100644 --- a/weave/legacy/tests/test_panel_coverage.py +++ b/weave/legacy/tests/test_panel_coverage.py @@ -14,7 +14,7 @@ def inheritors(klass): def all_panels(): - return inheritors(weave.legacy.panel.Panel) + return inheritors(weave.legacy.weave.panel.Panel) def test_panel_coverage(): diff --git a/weave/legacy/tests/test_panel_time_series.py b/weave/legacy/tests/test_panel_time_series.py index 169b6bb40aa..6b0faab5cbe 100644 --- a/weave/legacy/tests/test_panel_time_series.py +++ b/weave/legacy/tests/test_panel_time_series.py @@ -3,7 +3,7 @@ import wandb import weave -from weave.legacy.ecosystem.wandb.panel_time_series import TimeSeries +from weave.legacy.weave.ecosystem.wandb.panel_time_series import TimeSeries def test_panel_timeseries(user_by_api_key_in_env): @@ -14,7 +14,7 @@ def test_panel_timeseries(user_by_api_key_in_env): run.finish() history_node = ( - weave.legacy.ops.project(run.entity, run.project).run(run.id).history2() + weave.legacy.weave.ops.project(run.entity, run.project).run(run.id).history2() ) panel = TimeSeries(history_node) init_config_node = panel.initialize() diff --git a/weave/legacy/tests/test_panels.py b/weave/legacy/tests/test_panels.py index 14f162987d1..1f309042751 100644 --- a/weave/legacy/tests/test_panels.py +++ b/weave/legacy/tests/test_panels.py @@ -1,11 +1,11 @@ import pytest import weave -from weave.legacy import storage, weave_internal -from weave.legacy.panels import panel_plot +from weave.legacy.weave import storage, weave_internal +from weave.legacy.weave.panels import panel_plot -from ...legacy.panels.panel_group import Group -from ...legacy.panels.panel_slider import Slider +from ...legacy.weave.panels.panel_group import Group +from ...legacy.weave.panels.panel_slider import Slider def test_panel_id(): @@ -82,7 +82,7 @@ def test_synced(): def test_object_picker_choice_type(): ints = weave.save([1, 2, 3], name="my-ints") - panel = weave.legacy.panels.ObjectPicker(ints) + panel = weave.legacy.weave.panels.ObjectPicker(ints) panel_node = weave_internal.make_var_node(weave.type_of(panel), "panel") choice = panel_node.config.choice assert choice.type == weave.types.Function({}, weave.types.Int()) @@ -92,17 +92,17 @@ def test_facet_selected(): data = weave.save( [{"guess": "dog", "truth": "cat"}, {"guess": "dog", "truth": "dog"}] ) - facet = weave.legacy.panels.Group( + facet = weave.legacy.weave.panels.Group( equalSize=True, items={ - "confusion": weave.legacy.panels.Facet( + "confusion": weave.legacy.weave.panels.Facet( data, x=lambda row: row["guess"], y=lambda row: row["truth"], - select=lambda row: weave.legacy.panels.Group( + select=lambda row: weave.legacy.weave.panels.Group( layered=True, items={ - "color": weave.legacy.panels.Color(row.count() / 50), + "color": weave.legacy.weave.panels.Color(row.count() / 50), "count": row.count(), }, ), @@ -115,11 +115,11 @@ def test_facet_selected(): def test_board(): # Just make sure it runs for now. - weave.legacy.panels.Board( + weave.legacy.weave.panels.Board( { - "nums": weave.legacy.ops.range(0, 3, 1), + "nums": weave.legacy.weave.ops.range(0, 3, 1), }, - [weave.legacy.panels.BoardPanel(id="panel0", panel=lambda nums: nums)], + [weave.legacy.weave.panels.BoardPanel(id="panel0", panel=lambda nums: nums)], ) @@ -130,6 +130,6 @@ def test_plot_constants_assign(): def test_plot_assign(): - assert weave.legacy.panels.Plot.WeaveType().assign_type( - weave.type_of(weave.legacy.panels.Plot([{"a": 5}])) + assert weave.legacy.weave.panels.Plot.WeaveType().assign_type( + weave.type_of(weave.legacy.weave.panels.Plot([{"a": 5}])) ) diff --git a/weave/legacy/tests/test_partial_object.py b/weave/legacy/tests/test_partial_object.py index 22eac741e3b..6a1955f23fe 100644 --- a/weave/legacy/tests/test_partial_object.py +++ b/weave/legacy/tests/test_partial_object.py @@ -1,10 +1,10 @@ -from weave.legacy import api as weave -from weave.legacy import compile, ops -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.project_ops import root_all_projects -from weave.legacy.ops_domain.report_ops import root_all_reports +from weave.legacy.weave import api as weave +from weave.legacy.weave import compile, ops +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.project_ops import root_all_projects +from weave.legacy.weave.ops_domain.report_ops import root_all_reports from .test_wb import table_mock1_no_display_name diff --git a/weave/legacy/tests/test_plot.py b/weave/legacy/tests/test_plot.py index f124eabf81d..93ce632dd1e 100644 --- a/weave/legacy/tests/test_plot.py +++ b/weave/legacy/tests/test_plot.py @@ -4,9 +4,9 @@ import pytest import weave -from weave.legacy import graph, storage -from weave.legacy import weave_types as types -from weave.legacy.panels.panel_plot import Plot, PlotConstants, Series +from weave.legacy.weave import graph, storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.panels.panel_plot import Plot, PlotConstants, Series from .test_run_segment import create_experiment @@ -38,11 +38,11 @@ def test_multi_series_plot_config_with_grouping(): weave.save(last_segment) plot = Plot(last_segment.experiment()) plot.set_x( - lambda row: weave.legacy.ops.number_bin( - row["step"], weave.legacy.ops.numbers_bins_equal([1, 2000], 2) + lambda row: weave.legacy.weave.ops.number_bin( + row["step"], weave.legacy.weave.ops.numbers_bins_equal([1, 2000], 2) ) ) - plot.set_y(lambda row: weave.legacy.ops.numbers_avg(row["metric0"])) + plot.set_y(lambda row: weave.legacy.weave.ops.numbers_avg(row["metric0"])) plot.groupby_x() plot.set_mark_constant("line") @@ -50,8 +50,8 @@ def test_multi_series_plot_config_with_grouping(): series2 = plot.config.series[0].clone() plot.add_series(series2) - series2.set_y(lambda row: weave.legacy.ops.numbers_min(row["metric0"])) - series2.set_y2(lambda row: weave.legacy.ops.numbers_max(row["metric0"])) + series2.set_y(lambda row: weave.legacy.weave.ops.numbers_min(row["metric0"])) + series2.set_y2(lambda row: weave.legacy.weave.ops.numbers_max(row["metric0"])) series2.set_mark_constant("area") plot.groupby_x() @@ -69,20 +69,20 @@ def test_multi_series_grouping(): weave.save(last_segment) plot = Plot(last_segment.experiment()) plot.set_x( - lambda row: weave.legacy.ops.number_bin( - row["step"], weave.legacy.ops.numbers_bins_equal([1, 2000], 2) + lambda row: weave.legacy.weave.ops.number_bin( + row["step"], weave.legacy.weave.ops.numbers_bins_equal([1, 2000], 2) ) ) plot.groupby_x() - plot.set_y(lambda row: weave.legacy.ops.numbers_avg(row["metric0"])) + plot.set_y(lambda row: weave.legacy.weave.ops.numbers_avg(row["metric0"])) plot.set_mark_constant("line") series2 = plot.config.series[0].clone() plot.add_series(series2) - series2.set_y(lambda row: weave.legacy.ops.numbers_min(row["metric0"])) - series2.set_y2(lambda row: weave.legacy.ops.numbers_max(row["metric0"])) + series2.set_y(lambda row: weave.legacy.weave.ops.numbers_min(row["metric0"])) + series2.set_y2(lambda row: weave.legacy.weave.ops.numbers_max(row["metric0"])) series2.set_mark_constant("area") plot2 = copy.deepcopy(plot) @@ -100,8 +100,8 @@ def test_multi_series_setting(): weave.save(last_segment) plot = Plot(last_segment.experiment()) plot.set_x( - lambda row: weave.legacy.ops.number_bin( - row["step"], weave.legacy.ops.numbers_bins_equal([1, 2000], 2) + lambda row: weave.legacy.weave.ops.number_bin( + row["step"], weave.legacy.weave.ops.numbers_bins_equal([1, 2000], 2) ) ) @@ -156,18 +156,18 @@ def test_actual_config_value(fixed_random_seed): weave.save(last_segment) plot = Plot(last_segment.experiment()) plot.set_x( - lambda row: weave.legacy.ops.number_bin( - row["step"], weave.legacy.ops.numbers_bins_equal([1, 2000], 2) + lambda row: weave.legacy.weave.ops.number_bin( + row["step"], weave.legacy.weave.ops.numbers_bins_equal([1, 2000], 2) ) ) - plot.set_y(lambda row: weave.legacy.ops.numbers_avg(row["metric0"])) + plot.set_y(lambda row: weave.legacy.weave.ops.numbers_avg(row["metric0"])) plot.set_mark_constant("line") series2 = plot.config.series[0].clone() plot.add_series(series2) - series2.set_y(lambda row: weave.legacy.ops.numbers_min(row["metric0"])) - series2.set_y2(lambda row: weave.legacy.ops.numbers_max(row["metric0"])) + series2.set_y(lambda row: weave.legacy.weave.ops.numbers_min(row["metric0"])) + series2.set_y2(lambda row: weave.legacy.weave.ops.numbers_max(row["metric0"])) series2.set_mark_constant("area") assert storage.to_python(plot.config) == { "_type": { @@ -1766,7 +1766,7 @@ def test_actual_config_value(fixed_random_seed): def test_panel_plot_scale_serialization(): # checks a problem case where scale would not be serialized correctly as an AxisScale object - plot = weave.legacy.panels.Plot( + plot = weave.legacy.weave.panels.Plot( [1, 2, 3, 4], x=lambda row: row, x_title="x", diff --git a/weave/legacy/tests/test_projection_timeout.py b/weave/legacy/tests/test_projection_timeout.py index b9fd2a7e21e..302b2247226 100644 --- a/weave/legacy/tests/test_projection_timeout.py +++ b/weave/legacy/tests/test_projection_timeout.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from weave.legacy.ops_primitives import projection_utils +from weave.legacy.weave.ops_primitives import projection_utils from ... import errors diff --git a/weave/legacy/tests/test_publish_flow.py b/weave/legacy/tests/test_publish_flow.py index 228fdd200d0..c7fe8c38172 100644 --- a/weave/legacy/tests/test_publish_flow.py +++ b/weave/legacy/tests/test_publish_flow.py @@ -3,20 +3,20 @@ import pytest import weave -from weave.legacy import panels, storage -from weave.legacy.artifact_fs import BranchPointType, FilesystemArtifactRef -from weave.legacy.artifact_local import ( +from weave.legacy.weave import panels, storage +from weave.legacy.weave.artifact_fs import BranchPointType, FilesystemArtifactRef +from weave.legacy.weave.artifact_local import ( LocalArtifact, LocalArtifactRef, WeaveLocalArtifactURI, ) -from weave.legacy.artifact_wandb import ( +from weave.legacy.weave.artifact_wandb import ( WandbArtifact, WandbArtifactRef, WeaveWBArtifactURI, likely_commit_hash, ) -from weave.legacy.uris import WeaveURI +from weave.legacy.weave.uris import WeaveURI def test_publish_values(user_by_api_key_in_env): @@ -26,7 +26,7 @@ def test_publish_values(user_by_api_key_in_env): def test_publish_panel(user_by_api_key_in_env): - from weave.legacy import panel_util + from weave.legacy.weave import panel_util table_obj = panels.Table( panel_util.make_node( @@ -321,12 +321,12 @@ def _perform_post_persist_assertions( expected_uri_with_branch = f"wandb-artifact:///{target_entity}/{target_project_name}/{target_artifact_name}:{branch_name}/obj" # Data Assertions - get_op = weave.legacy.ops.get(expected_uri_with_hash) + get_op = weave.legacy.weave.ops.get(expected_uri_with_hash) get_res = weave.use(get_op) assert p_ref.obj == get_res == data # Only check branch if expected_uri_with_branch is not None if expected_uri_with_branch: - get_op = weave.legacy.ops.get(expected_uri_with_branch) + get_op = weave.legacy.weave.ops.get(expected_uri_with_branch) get_res = weave.use(get_op) assert get_res == data @@ -379,7 +379,7 @@ def test_mutate_remote_with_commit_hash(user_by_api_key_in_env): assert p_ref.version == expected_commit_hash - obj = weave.legacy.ops.get(p_ref.uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.uri)[0] obj.set(new_data[0]) new_branch_name = f"user-{p_ref.version}" new_uri = f"local-artifact:///{target_artifact_name}:{new_branch_name}/obj" @@ -422,7 +422,7 @@ def test_mutate_remote_with_branch(user_by_api_key_in_env): assert p_ref.version == expected_commit_hash assert branch_name in p_ref.branch_uri - obj = weave.legacy.ops.get(p_ref.branch_uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.branch_uri)[0] obj.set(new_data[0]) new_branch_name = f"user-{branch_name}" new_p_ref = LocalArtifactRef.from_str( @@ -461,7 +461,7 @@ def test_mutate_local_with_commit_hash(user_by_api_key_in_env, new_branch_name=N assert p_ref.version == expected_commit_hash - obj = weave.legacy.ops.get(p_ref.uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.uri)[0] if new_branch_name == None: obj.set(new_data[0]) new_branch_name = new_commit_hash @@ -513,14 +513,14 @@ def test_mutate_local_with_commit_hash_and_branchpoint( assert p_ref.version == expected_commit_hash assert branch_name in p_ref.branch_uri - obj = weave.legacy.ops.get(p_ref.branch_uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.branch_uri)[0] obj.set("PLACEHOLDER") mod_ref = LocalArtifactRef.from_uri( WeaveLocalArtifactURI.parse( f"local-artifact:///{target_artifact_name}:user-{branch_name}/obj" ) ) - obj = weave.legacy.ops.get(mod_ref.branch_uri)[0] + obj = weave.legacy.weave.ops.get(mod_ref.branch_uri)[0] if new_branch_name == None: obj.set(new_data[0]) branch_name = None @@ -571,7 +571,7 @@ def test_mutate_local_with_branch(user_by_api_key_in_env, new_branch_name=None): assert p_ref.version == expected_commit_hash - obj = weave.legacy.ops.get(p_ref.branch_uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.branch_uri)[0] if new_branch_name == None: obj.set(new_data[0]) new_branch_name = new_commit_hash @@ -622,9 +622,9 @@ def test_mutate_local_with_branch_and_branchpoint(user_by_api_key_in_env): assert p_ref.version == expected_commit_hash assert branch_name in p_ref.branch_uri - obj = weave.legacy.ops.get(p_ref.branch_uri)[0] + obj = weave.legacy.weave.ops.get(p_ref.branch_uri)[0] obj.set("PLACEHOLDER") - obj = weave.legacy.ops.get( + obj = weave.legacy.weave.ops.get( f"local-artifact:///{target_artifact_name}:user-{branch_name}/obj" )[0] obj.set(new_data[0]) @@ -655,8 +655,8 @@ def test_merge_from_local_with_commit_hash_onto_remote_with_commit_hash( ): test_mutate_remote_with_commit_hash(user_by_api_key_in_env) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") ) assert merged_uri.startswith("wandb-artifact://") @@ -681,8 +681,8 @@ def test_merge_from_local_with_commit_hash_onto_remote_with_branch( ): test_mutate_remote_with_branch(user_by_api_key_in_env) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") ) assert merged_uri.startswith("wandb-artifact://") @@ -707,8 +707,8 @@ def test_merge_from_local_with_commit_hash_onto_local_with_commit_hash( ): test_mutate_local_with_commit_hash(user_by_api_key_in_env, "new_branch_name") - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") ) assert merged_uri.startswith("local-artifact://") @@ -739,8 +739,8 @@ def test_merge_from_local_with_commit_hash_onto_local_with_branch( ): test_mutate_local_with_branch(user_by_api_key_in_env, "new_branch_name") - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") ) assert merged_uri.startswith("local-artifact://") @@ -765,8 +765,8 @@ def test_merge_from_local_with_commit_hash_onto_local_with_branch_and_branchpoin user_by_api_key_in_env, "new_branch_name" ) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:61f78c8877df22942d23/obj") ) assert merged_uri.startswith("local-artifact://") @@ -788,7 +788,7 @@ def test_merge_from_local_with_commit_hash_onto_local_with_branch_and_branchpoin ), ) - merged_uri_2 = weave.legacy.ops.merge_artifact(weave.legacy.ops.get(merged_uri)) + merged_uri_2 = weave.legacy.weave.ops.merge_artifact(weave.legacy.weave.ops.get(merged_uri)) new_p_ref_2 = WandbArtifactRef.from_str(merged_uri_2) assert merged_uri_2.startswith("wandb-artifact://") @@ -816,8 +816,8 @@ def test_merge_from_local_with_branch_onto_remote_with_commit_hash( ): test_mutate_remote_with_commit_hash(user_by_api_key_in_env) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get( + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get( "local-artifact:///test_artifact:user-74d5ba98aca469b59e18/obj" ) ) @@ -842,8 +842,8 @@ def test_merge_from_local_with_branch_onto_remote_with_commit_hash( def test_merge_from_local_with_branch_onto_remote_with_branch(user_by_api_key_in_env): test_mutate_remote_with_branch(user_by_api_key_in_env) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:user-remote_branch/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:user-remote_branch/obj") ) assert merged_uri.startswith("wandb-artifact://") @@ -868,8 +868,8 @@ def test_merge_from_local_with_branch_onto_local_with_commit_hash( ): test_mutate_local_with_commit_hash(user_by_api_key_in_env, "new_branch_name") - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:new_branch_name/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:new_branch_name/obj") ) assert merged_uri.startswith("local-artifact://") @@ -898,8 +898,8 @@ def test_merge_from_local_with_branch_onto_local_with_commit_hash( def test_merge_from_local_with_branch_onto_local_with_branch(user_by_api_key_in_env): test_mutate_local_with_branch(user_by_api_key_in_env, "new_branch_name") - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:new_branch_name/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:new_branch_name/obj") ) assert merged_uri.startswith("local-artifact://") @@ -924,8 +924,8 @@ def test_merge_from_local_with_branch_onto_local_with_branch_and_branchpoint( user_by_api_key_in_env, "new_branch_name" ) - merged_uri = weave.legacy.ops.merge_artifact( - weave.legacy.ops.get("local-artifact:///test_artifact:new_branch_name/obj") + merged_uri = weave.legacy.weave.ops.merge_artifact( + weave.legacy.weave.ops.get("local-artifact:///test_artifact:new_branch_name/obj") ) assert merged_uri.startswith("local-artifact://") @@ -947,7 +947,7 @@ def test_merge_from_local_with_branch_onto_local_with_branch_and_branchpoint( ), ) - merged_uri_2 = weave.legacy.ops.merge_artifact(weave.legacy.ops.get(merged_uri)) + merged_uri_2 = weave.legacy.weave.ops.merge_artifact(weave.legacy.weave.ops.get(merged_uri)) new_p_ref_2 = WandbArtifactRef.from_str(merged_uri_2) assert merged_uri_2.startswith("wandb-artifact://") @@ -976,7 +976,7 @@ def test_publish_saved_node(user_by_api_key_in_env): assert saved.from_op.inputs["uri"].val.startswith("local-artifact://") assert weave.use(saved) == data - published_art_uri = weave.legacy.ops.publish_artifact(saved, "my_list", None, None) + published_art_uri = weave.legacy.weave.ops.publish_artifact(saved, "my_list", None, None) assert published_art_uri.startswith("wandb-artifact://") assert weave.use(weave.get(published_art_uri)) == data @@ -997,7 +997,7 @@ def _replace_uri_version(uri: str, new_version: str) -> str: return uri.replace(_get_uri_version(uri), new_version) -def _get_uri_from_get_node(node: weave.legacy.graph.Node) -> str: +def _get_uri_from_get_node(node: weave.legacy.weave.graph.Node) -> str: return node.from_op.inputs["uri"].val # type: ignore @@ -1011,44 +1011,44 @@ def test_end_to_end_save_and_publish_flow(user_by_api_key_in_env): assert weave.use(saved_node) == data # Step 2: Make a branching mutation. - weave.legacy.ops.set( + weave.legacy.weave.ops.set( saved_node[0], "test_publish_saved_node_execution_2", root_args={"branch": "my-branch"}, ) branched_data = ["test_publish_saved_node_execution_2"] branched_uri = saved_uri.replace(saved_version, "my-branch") - branched_node = weave.legacy.ops.get(branched_uri) + branched_node = weave.legacy.weave.ops.get(branched_uri) assert weave.use(branched_node) == branched_data # Step 3: Merge the change back into the main branch. - merged_uri = weave.legacy.ops.merge_artifact(branched_node) + merged_uri = weave.legacy.weave.ops.merge_artifact(branched_node) assert merged_uri.startswith("local-artifact://") assert merged_uri != saved_uri != branched_uri - merged_node = weave.legacy.ops.get(merged_uri) + merged_node = weave.legacy.weave.ops.get(merged_uri) assert weave.use(merged_node) == branched_data # Step 4: Publish the new version remotely - published_uri = weave.legacy.ops.publish_artifact( + published_uri = weave.legacy.weave.ops.publish_artifact( merged_node, "my_list", None, None ) assert published_uri.startswith("wandb-artifact://") assert weave.use(weave.get(published_uri)) == branched_data # Step 5: Modify the remote version - weave.legacy.ops.set( - weave.legacy.ops.get(published_uri)[0], + weave.legacy.weave.ops.set( + weave.legacy.weave.ops.get(published_uri)[0], "test_publish_saved_node_execution_3", root_args={"branch": "my-branch-2"}, ) published_branched_data = ["test_publish_saved_node_execution_3"] published_branched_uri = "local-artifact:///my_list:my-branch-2/obj" - published_branched_node = weave.legacy.ops.get(published_branched_uri) + published_branched_node = weave.legacy.weave.ops.get(published_branched_uri) assert weave.use(published_branched_node) == published_branched_data # Step 6: Merge the remote change back into the main branch. - published_merged_uri = weave.legacy.ops.merge_artifact(published_branched_node) + published_merged_uri = weave.legacy.weave.ops.merge_artifact(published_branched_node) assert published_merged_uri.startswith("wandb-artifact://") assert published_merged_uri != published_uri - published_merged_node = weave.legacy.ops.get(published_merged_uri) + published_merged_node = weave.legacy.weave.ops.get(published_merged_uri) assert weave.use(published_merged_node) == published_branched_data diff --git a/weave/legacy/tests/test_pydantic.py b/weave/legacy/tests/test_pydantic.py index 1a57eda18cb..720ec1c1d0b 100644 --- a/weave/legacy/tests/test_pydantic.py +++ b/weave/legacy/tests/test_pydantic.py @@ -3,7 +3,7 @@ import pydantic import weave -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types def test_pydantic_type_inference(): diff --git a/weave/legacy/tests/test_ref_tracking.py b/weave/legacy/tests/test_ref_tracking.py index 47d8ca60408..61b97d769be 100644 --- a/weave/legacy/tests/test_ref_tracking.py +++ b/weave/legacy/tests/test_ref_tracking.py @@ -1,6 +1,6 @@ import weave -from weave.legacy import storage -from weave.legacy import weave_types as types +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types def test_reffed_type(ref_tracking): diff --git a/weave/legacy/tests/test_refs.py b/weave/legacy/tests/test_refs.py index e890134d0ff..98bd7048bc5 100644 --- a/weave/legacy/tests/test_refs.py +++ b/weave/legacy/tests/test_refs.py @@ -2,8 +2,8 @@ import weave from weave.flow.obj import Object -from weave.legacy import artifact_local, storage -from weave.legacy import ops_arrow as arrow +from weave.legacy.weave import artifact_local, storage +from weave.legacy.weave import ops_arrow as arrow from weave.trace import ref_util from weave.trace_server.refs_internal import ( DICT_KEY_EDGE_NAME, diff --git a/weave/legacy/tests/test_relpath_no_syscalls.py b/weave/legacy/tests/test_relpath_no_syscalls.py index 412aed2b06e..66ce63b1af0 100644 --- a/weave/legacy/tests/test_relpath_no_syscalls.py +++ b/weave/legacy/tests/test_relpath_no_syscalls.py @@ -1,6 +1,6 @@ import pytest -from weave.legacy.util import relpath_no_syscalls +from weave.legacy.weave.util import relpath_no_syscalls @pytest.mark.parametrize( diff --git a/weave/legacy/tests/test_run_segment.py b/weave/legacy/tests/test_run_segment.py index fd92b66c552..cf918473b88 100644 --- a/weave/legacy/tests/test_run_segment.py +++ b/weave/legacy/tests/test_run_segment.py @@ -6,10 +6,10 @@ import pytest import weave -from weave.legacy import api, ops, storage, weave_internal -from weave.legacy import weave_types as types -from weave.legacy.ops_arrow import ArrowWeaveList, arrow_as_array -from weave.legacy.ops_domain.run_segment import RunSegment +from weave.legacy.weave import api, ops, storage, weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_arrow import ArrowWeaveList, arrow_as_array +from weave.legacy.weave.ops_domain.run_segment import RunSegment N_NUMERIC_METRICS = 99 # number of numerical columns in the metrics table diff --git a/weave/legacy/tests/test_serialize.py b/weave/legacy/tests/test_serialize.py index 94251cd1dd4..4a7869e9fe4 100644 --- a/weave/legacy/tests/test_serialize.py +++ b/weave/legacy/tests/test_serialize.py @@ -1,11 +1,11 @@ import pytest import weave -from weave.legacy import api as api -from weave.legacy import graph, op_args, ops, registry_mem, serialize, weave_internal -from weave.legacy import weave_types as types -from weave.legacy.ops_primitives import list_ -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import api as api +from weave.legacy.weave import graph, op_args, ops, registry_mem, serialize, weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_primitives import list_ +from weave.legacy.weave.weave_internal import make_const_node from ...tests import fixture_fakewandb as fwb diff --git a/weave/legacy/tests/test_show.py b/weave/legacy/tests/test_show.py index bee6ecd76dd..8d56c86cd0c 100644 --- a/weave/legacy/tests/test_show.py +++ b/weave/legacy/tests/test_show.py @@ -2,9 +2,9 @@ # The outputs of weave.show (the generated panel urls and their arguments) # need to match what javascript expects. -from weave.legacy import ops +from weave.legacy.weave import ops -from ...legacy.show import _show_params +from ...legacy.weave.show import _show_params from . import test_helpers diff --git a/weave/legacy/tests/test_stitch.py b/weave/legacy/tests/test_stitch.py index 0c34a27aacd..12e6deb26a5 100644 --- a/weave/legacy/tests/test_stitch.py +++ b/weave/legacy/tests/test_stitch.py @@ -3,12 +3,12 @@ import pytest import weave -from weave.legacy import compile_domain, compile_table, weave_internal -from weave.legacy import context_state as _context -from weave.legacy.language_features.tagging import make_tag_getter_op -from weave.legacy.ops_domain import run_ops +from weave.legacy.weave import compile_domain, compile_table, weave_internal +from weave.legacy.weave import context_state as _context +from weave.legacy.weave.language_features.tagging import make_tag_getter_op +from weave.legacy.weave.ops_domain import run_ops -from ...legacy import stitch +from ...legacy.weave import stitch from ...tests import fixture_fakewandb as fwb from . import test_wb @@ -122,7 +122,7 @@ def test_tag_access_in_filter_expr(): def test_traverse_dict(): obj_node = weave.save(_TestPlanObject("a", 1)) - p = stitch.stitch([weave.legacy.ops.dict_(x=obj_node)["x"].horse()]) + p = stitch.stitch([weave.legacy.weave.ops.dict_(x=obj_node)["x"].horse()]) obj_recorder = p.get_result(obj_node) assert len(obj_recorder.calls) == 1 assert obj_recorder.calls[0].node.from_op.name == "_TestPlanObject-horse" @@ -130,7 +130,7 @@ def test_traverse_dict(): def test_travese_groupby_dict(): obj_node = weave.save([{"o": {"a": 5}, "x": 1}]) - grouped = obj_node.groupby(lambda row: weave.legacy.ops.dict_(x=row["o"])) + grouped = obj_node.groupby(lambda row: weave.legacy.weave.ops.dict_(x=row["o"])) output = grouped[0]["x"] groupkey_output = grouped[0].groupkey()["x"]["a"] p = stitch.stitch([output, groupkey_output]) @@ -163,16 +163,16 @@ def test_zero_arg_ops(): def test_shared_fn_node(): - const_list_node = weave.legacy.ops.make_list(a=1, b=2) + const_list_node = weave.legacy.weave.ops.make_list(a=1, b=2) indexed_node = const_list_node[0] - arr_1_node = weave.legacy.ops.make_list(a=1, b=2, c=3) - arr_2_node = weave.legacy.ops.make_list(a=10, b=20, c=30) + arr_1_node = weave.legacy.weave.ops.make_list(a=1, b=2, c=3) + arr_2_node = weave.legacy.weave.ops.make_list(a=10, b=20, c=30) mapped_1_node = arr_1_node.map( - lambda row: weave.legacy.ops.dict_(item=row, const=indexed_node) + lambda row: weave.legacy.weave.ops.dict_(item=row, const=indexed_node) ) mapped_2_node = arr_2_node.map( - lambda row: weave.legacy.ops.dict_(item=row, const=indexed_node) + lambda row: weave.legacy.weave.ops.dict_(item=row, const=indexed_node) ) mapped_1_item_node = mapped_1_node["item"] @@ -183,7 +183,7 @@ def test_shared_fn_node(): mapped_2_item_add_node = mapped_2_item_node + 100 mapped_2_const_add_node = mapped_2_const_node + 100 - list_of_list_node = weave.legacy.ops.make_list( + list_of_list_node = weave.legacy.weave.ops.make_list( a=mapped_1_item_node, b=mapped_1_const_node, c=mapped_2_item_add_node, @@ -218,9 +218,9 @@ def assert_node_calls(node, expected_call_names): def test_stitch_keytypes_override_fetch_all_columns(fake_wandb): fake_wandb.fake_api.add_mock(test_wb.table_mock_filtered) - keytypes_node = weave.legacy.ops.object_keytypes( + keytypes_node = weave.legacy.weave.ops.object_keytypes( run_ops.run_tag_getter_op( - weave.legacy.ops.project("stacey", "mendeleev") + weave.legacy.weave.ops.project("stacey", "mendeleev") .filteredRuns("{}", "-createdAt") .limit(50) .summary() @@ -255,15 +255,15 @@ def test_stitch_overlapping_tags(fake_wandb): } } ) - project_node = weave.legacy.ops.project("stacey", "mendeleev") + project_node = weave.legacy.weave.ops.project("stacey", "mendeleev") filtered_runs_a_node = project_node.filteredRuns("{}", "-createdAt")[0] summary_a_node = filtered_runs_a_node.summary() - tagged_name_a = weave.legacy.ops.run_ops.run_tag_getter_op( + tagged_name_a = weave.legacy.weave.ops.run_ops.run_tag_getter_op( summary_a_node["a"] ).name() filtered_runs_b_node = project_node.filteredRuns("{}", "+createdAt")[0] summary_b_node = filtered_runs_b_node.summary() - tagged_id_b = weave.legacy.ops.run_ops.run_tag_getter_op(summary_b_node).id() + tagged_id_b = weave.legacy.weave.ops.run_ops.run_tag_getter_op(summary_b_node).id() p = stitch.stitch([tagged_name_a, tagged_id_b]) @@ -273,9 +273,9 @@ def test_stitch_overlapping_tags(fake_wandb): def test_refine_history_type_included_in_gql(): - project_node = weave.legacy.ops.project("stacey", "mendeleev") + project_node = weave.legacy.weave.ops.project("stacey", "mendeleev") runs_node = project_node.runs() - map_node = runs_node.map(lambda row: weave.legacy.ops.dict_(variant=row)) + map_node = runs_node.map(lambda row: weave.legacy.weave.ops.dict_(variant=row)) checkpoint_node = map_node.createIndexCheckpointTag() index_node = checkpoint_node[0] pick_node = index_node["variant"] @@ -286,7 +286,7 @@ def test_refine_history_type_included_in_gql(): def test_stitch_missing_key(): a_node = weave_internal.make_const_node(weave.types.String(), "a") - dict_node = weave.legacy.ops.dict_(a=a_node) + dict_node = weave.legacy.weave.ops.dict_(a=a_node) picked_valid = dict_node["a"] + "-suffix" picked_missing = dict_node["b"] + "-suffix" diff --git a/weave/legacy/tests/test_storage.py b/weave/legacy/tests/test_storage.py index 29dda1e8963..8183200fa3e 100644 --- a/weave/legacy/tests/test_storage.py +++ b/weave/legacy/tests/test_storage.py @@ -7,19 +7,19 @@ import pytest import wandb -from weave.legacy import api as weave -from weave.legacy import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import ( artifact_mem, artifact_wandb, mappers_python, ops_arrow, storage, ) -from weave.legacy import weave_types as types -from weave.legacy.arrow import list_ as arrow -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow import list_ as arrow +from weave.legacy.weave.weave_internal import make_const_node -from ...legacy.weavejs_fixes import recursively_unwrap_unions +from ...legacy.weave.weavejs_fixes import recursively_unwrap_unions from . import test_helpers diff --git a/weave/legacy/tests/test_stream_table.py b/weave/legacy/tests/test_stream_table.py index 393894a8aa3..7591331e979 100644 --- a/weave/legacy/tests/test_stream_table.py +++ b/weave/legacy/tests/test_stream_table.py @@ -1,7 +1,7 @@ import os import time -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable def main(table_name, project_name, entity_name, sample_limit, gap_ms): diff --git a/weave/legacy/tests/test_table_ops.py b/weave/legacy/tests/test_table_ops.py index 83e526edd03..2994235eb37 100644 --- a/weave/legacy/tests/test_table_ops.py +++ b/weave/legacy/tests/test_table_ops.py @@ -3,8 +3,8 @@ import pytest -from weave.legacy import api as weave -from weave.legacy import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import ( box, context, context_state, @@ -13,8 +13,8 @@ storage, weave_internal, ) -from weave.legacy import weave_types as types -from weave.legacy.ops_domain import table as table_ops +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_domain import table as table_ops from weave.legacy.tests.util import weavejs_ops diff --git a/weave/legacy/tests/test_tagging.py b/weave/legacy/tests/test_tagging.py index da17be75408..471205b4920 100644 --- a/weave/legacy/tests/test_tagging.py +++ b/weave/legacy/tests/test_tagging.py @@ -1,19 +1,19 @@ import pytest import weave -from weave.legacy import box, weave_internal -from weave.legacy import weave_types as types -from weave.legacy.artifact_fs import FilesystemArtifactFileType -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import box, weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.artifact_fs import FilesystemArtifactFileType +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, tagged_value_type_helpers, ) -from weave.legacy.ops_domain.run_ops import run_tag_getter_op -from weave.legacy.ops_domain.wb_domain_types import ProjectType, Run, RunType -from weave.legacy.ops_primitives import dict as dict_ops -from weave.legacy.ops_primitives import list_ as list_ops +from weave.legacy.weave.ops_domain.run_ops import run_tag_getter_op +from weave.legacy.weave.ops_domain.wb_domain_types import ProjectType, Run, RunType +from weave.legacy.weave.ops_primitives import dict as dict_ops +from weave.legacy.weave.ops_primitives import list_ as list_ops def test_tagged_value(): @@ -29,7 +29,7 @@ def test_tagged_types(): class _TestNumber: inner: int - from weave.legacy import context_state + from weave.legacy.weave import context_state _loading_builtins_token = context_state.set_loading_built_ins() diff --git a/weave/legacy/tests/test_templates.py b/weave/legacy/tests/test_templates.py index 76335e6f118..6c3e2289295 100644 --- a/weave/legacy/tests/test_templates.py +++ b/weave/legacy/tests/test_templates.py @@ -3,10 +3,10 @@ import wandb import weave -from weave.legacy import context_state as _context -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave import context_state as _context +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable -from ...legacy.panels_py import generator_templates +from ...legacy.weave.panels_py import generator_templates _loading_builtins_token = _context.set_loading_built_ins() @@ -34,17 +34,17 @@ class DummyBoardConfig: def dummy_board( input_node, config: typing.Optional[DummyBoardConfig] = None, -) -> weave.legacy.panels.Group: +) -> weave.legacy.weave.panels.Group: assert board_input_type.assign_type(input_node.type) control_items = [ - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( input_node, id="data", ), ] - return weave.legacy.panels.Board(vars=control_items, panels=[]) + return weave.legacy.weave.panels.Board(vars=control_items, panels=[]) generator_templates.template_registry.register( @@ -77,13 +77,13 @@ def _assert_valid_node_raw(node): output_group = weave.use(dummy_node) # Assert the out template is successfully generated - assert isinstance(output_group, weave.legacy.panels.Group) + assert isinstance(output_group, weave.legacy.weave.panels.Group) data_node = output_group.config.items["sidebar"].config.items["data"] # assert that the node sent to the generator is the same as the node that is used # Note: this is a heuristic, but probably close enough - assert isinstance(data_node, weave.legacy.graph.OutputNode) + assert isinstance(data_node, weave.legacy.weave.graph.OutputNode) assert data_node.from_op.name == node.from_op.name assert str(data_node) == str(node) @@ -102,7 +102,7 @@ def test_templates_for_run_logs_valid(user_by_api_key_in_env): run.finish() run_history_node = ( - weave.legacy.ops.project(run.entity, run.project).run(run.id).history() + weave.legacy.weave.ops.project(run.entity, run.project).run(run.id).history() ) assert_valid_node(run_history_node) @@ -114,7 +114,7 @@ def test_templates_for_run_logs_invalid(user_by_api_key_in_env): run.finish() run_history_node = ( - weave.legacy.ops.project(run.entity, run.project).run(run.id).history() + weave.legacy.weave.ops.project(run.entity, run.project).run(run.id).history() ) assert_invalid_node(run_history_node) @@ -126,7 +126,7 @@ def test_templates_for_logged_table_valid(user_by_api_key_in_env): run.finish() table_node = ( - weave.legacy.ops.project(run.entity, run.project) + weave.legacy.weave.ops.project(run.entity, run.project) .run(run.id) .summary()["table"] .table() @@ -142,7 +142,7 @@ def test_templates_for_logged_table_invalid(user_by_api_key_in_env): run.finish() table_node = ( - weave.legacy.ops.project(run.entity, run.project) + weave.legacy.weave.ops.project(run.entity, run.project) .run(run.id) .summary()["table"] .table() diff --git a/weave/legacy/tests/test_timestamp_bin.py b/weave/legacy/tests/test_timestamp_bin.py index 1243c2d3a3a..1e24617baeb 100644 --- a/weave/legacy/tests/test_timestamp_bin.py +++ b/weave/legacy/tests/test_timestamp_bin.py @@ -6,7 +6,7 @@ def test_timestamp_bins_fixed(): - bin_fn = weave.legacy.ops.timestamp_bins_fixed(60) + bin_fn = weave.legacy.weave.ops.timestamp_bins_fixed(60) ts = datetime(2020, 1, 1, 8, 30, 1) called = bin_fn(ts) @@ -19,7 +19,7 @@ def test_timestamp_bins_fixed(): def test_timestamp_bins_nice(): start_ts = datetime(2020, 1, 1, 8, 30, 1) stop_ts = datetime(2020, 1, 1, 12, 30, 0) - bin_fn = weave.legacy.ops.timestamp_bins_nice([start_ts, stop_ts], 100) + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice([start_ts, stop_ts], 100) ts = datetime(2020, 1, 1, 8, 45, 13) called = bin_fn(ts) @@ -32,7 +32,7 @@ def test_timestamp_bins_nice(): def test_timestamp_bin(): start_ts = datetime(2020, 1, 1, 8, 30, 1) stop_ts = datetime(2020, 1, 1, 12, 30, 0) - bin_fn = weave.legacy.ops.timestamp_bins_nice([start_ts, stop_ts], 100) + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice([start_ts, stop_ts], 100) ts = datetime(2020, 1, 1, 8, 45, 13) ts_node = weave.save(ts) @@ -45,10 +45,10 @@ def test_timestamp_bin(): def test_timestamp_bin_vector(): start_ts = datetime(2020, 1, 1, 8, 30, 1) stop_ts = datetime(2020, 1, 1, 12, 30, 0) - bin_fn = weave.legacy.ops.timestamp_bins_nice([start_ts, stop_ts], 100) + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice([start_ts, stop_ts], 100) ts = datetime(2020, 1, 1, 8, 45, 13, tzinfo=timezone.utc) - ts_node = weave.legacy.ops.to_weave_arrow([ts, ts + timedelta(seconds=90)]) + ts_node = weave.legacy.weave.ops.to_weave_arrow([ts, ts + timedelta(seconds=90)]) # This does not vectorize because mapped ops don't automatically # vectorize! diff --git a/weave/legacy/tests/test_trace.py b/weave/legacy/tests/test_trace.py index a0c308aab12..a809e10a654 100644 --- a/weave/legacy/tests/test_trace.py +++ b/weave/legacy/tests/test_trace.py @@ -1,10 +1,10 @@ import re -from weave.legacy import api as weave -from weave.legacy import graph, storage -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import api as weave +from weave.legacy.weave import graph, storage +from weave.legacy.weave.weave_internal import make_const_node -from ...legacy import trace_legacy +from ...legacy.weave import trace_legacy def test_node_expr(): diff --git a/weave/legacy/tests/test_typeddict_notrequired.py b/weave/legacy/tests/test_typeddict_notrequired.py index 0625b430aad..7b9c940184d 100644 --- a/weave/legacy/tests/test_typeddict_notrequired.py +++ b/weave/legacy/tests/test_typeddict_notrequired.py @@ -6,8 +6,8 @@ TypedDict, ) -from weave.legacy import infer_types -from weave.legacy import weave_types as types +from weave.legacy.weave import infer_types +from weave.legacy.weave import weave_types as types class _TestNotRequiredTypedDict(TypedDict): diff --git a/weave/legacy/tests/test_uris.py b/weave/legacy/tests/test_uris.py index ace2efcc36d..f544d9158a5 100644 --- a/weave/legacy/tests/test_uris.py +++ b/weave/legacy/tests/test_uris.py @@ -1,7 +1,7 @@ import pytest -from weave.legacy import uris -from weave.legacy.artifact_wandb import WeaveWBArtifactURI +from weave.legacy.weave import uris +from weave.legacy.weave.artifact_wandb import WeaveWBArtifactURI URIS = [ "op-get", diff --git a/weave/legacy/tests/test_wb.py b/weave/legacy/tests/test_wb.py index e3f3e96892d..b6cc5a11d5d 100644 --- a/weave/legacy/tests/test_wb.py +++ b/weave/legacy/tests/test_wb.py @@ -6,17 +6,17 @@ import pytest import wandb -from weave.legacy import api as weave -from weave.legacy import artifact_fs, artifact_wandb, compile, graph, ops, stitch, uris -from weave.legacy import ops_arrow as arrow -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_arrow import ArrowWeaveListType -from weave.legacy.ops_domain import artifact_membership_ops as amo -from weave.legacy.ops_domain import table, wb_util, wbmedia -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_primitives import dict_, list_ -from weave.legacy.ops_primitives.file import _as_w0_dict_ +from weave.legacy.weave import api as weave +from weave.legacy.weave import artifact_fs, artifact_wandb, compile, graph, ops, stitch, uris +from weave.legacy.weave import ops_arrow as arrow +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_arrow import ArrowWeaveListType +from weave.legacy.weave.ops_domain import artifact_membership_ops as amo +from weave.legacy.weave.ops_domain import table, wb_util, wbmedia +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_primitives import dict_, list_ +from weave.legacy.weave.ops_primitives.file import _as_w0_dict_ from .test_wb_domain_ops import assert_gql_str_equal from ...tests import fixture_fakewandb as fwb diff --git a/weave/legacy/tests/test_wb_data_types.py b/weave/legacy/tests/test_wb_data_types.py index ea41588b6c3..aae7baafdc2 100644 --- a/weave/legacy/tests/test_wb_data_types.py +++ b/weave/legacy/tests/test_wb_data_types.py @@ -12,14 +12,14 @@ from wandb.sdk.data_types._dtypes import TypeRegistry as SDKTypeRegistry import weave -from weave.legacy import artifact_fs -from weave.legacy import weave_types as types -from weave.legacy.artifact_wandb import WandbArtifact, WeaveWBArtifactURI -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_domain.wbmedia import ImageArtifactFileRefType -from weave.legacy.ops_primitives import file -from weave.legacy.wandb_client_api import wandb_gql_query -from weave.legacy.wandb_util import weave0_type_json_to_weave1_type +from weave.legacy.weave import artifact_fs +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.artifact_wandb import WandbArtifact, WeaveWBArtifactURI +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_domain.wbmedia import ImageArtifactFileRefType +from weave.legacy.weave.ops_primitives import file +from weave.legacy.weave.wandb_client_api import wandb_gql_query +from weave.legacy.weave.wandb_util import weave0_type_json_to_weave1_type from ...tests.fixture_fakewandb import FakeApi @@ -109,31 +109,31 @@ def make_molecule(clean_up=True): # ( make_image(), - weave.legacy.ops.ImageArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.ImageArtifactFileRef.WeaveType(), # type: ignore ), ( make_audio(), - weave.legacy.ops.AudioArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.AudioArtifactFileRef.WeaveType(), # type: ignore ), ( make_html(), - weave.legacy.ops.HtmlArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.HtmlArtifactFileRef.WeaveType(), # type: ignore ), ( make_bokeh(), - weave.legacy.ops.BokehArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.BokehArtifactFileRef.WeaveType(), # type: ignore ), ( make_video(), - weave.legacy.ops.VideoArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.VideoArtifactFileRef.WeaveType(), # type: ignore ), ( make_object3d(), - weave.legacy.ops.Object3DArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.Object3DArtifactFileRef.WeaveType(), # type: ignore ), ( make_molecule(), - weave.legacy.ops.MoleculeArtifactFileRef.WeaveType(), # type: ignore + weave.legacy.weave.ops.MoleculeArtifactFileRef.WeaveType(), # type: ignore ), # See comment in wandb_util.py - this may change in the future # Temporarily disabled until we can figure out how to mock @@ -630,7 +630,7 @@ def test_grouping_on_images(fake_wandb): table_node = file_node.table() table_rows = table_node.rows() grouped = table_rows.groupby( - lambda row: weave.legacy.ops.dict_(g_image=row["image"]) + lambda row: weave.legacy.weave.ops.dict_(g_image=row["image"]) ) raw_data = weave.use(grouped).to_pylist_notags() @@ -761,7 +761,7 @@ def make_join_table_row_nodes(fake_wandb): def test_join_all_on_images(fake_wandb): table_1_rows, table_2_rows = make_join_table_row_nodes(fake_wandb) - rows = weave.legacy.ops.make_list(a=table_1_rows, b=table_2_rows) + rows = weave.legacy.weave.ops.make_list(a=table_1_rows, b=table_2_rows) joined = rows.joinAll(lambda row: row["image"], True) @@ -855,12 +855,12 @@ def wait_for(): wait_for_x_times(wait_for) - run_node = weave.legacy.ops.project(run.entity, run.project).run(run.id) + run_node = weave.legacy.weave.ops.project(run.entity, run.project).run(run.id) for history_op_name in ["history3", "history"]: history_node = run_node._get_op(history_op_name)() mapped_node = history_node.map( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{key: row[key] for key in log_dict.keys()} ) ) diff --git a/weave/legacy/tests/test_wb_domain_ops.py b/weave/legacy/tests/test_wb_domain_ops.py index aefaa47c94c..89c12c8260a 100644 --- a/weave/legacy/tests/test_wb_domain_ops.py +++ b/weave/legacy/tests/test_wb_domain_ops.py @@ -4,12 +4,12 @@ import wandb import weave -from weave.legacy import ops -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.ops_domain import wb_domain_types -from weave.legacy.ops_primitives import _dict_utils +from weave.legacy.weave import ops +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.ops_domain import wb_domain_types +from weave.legacy.weave.ops_primitives import _dict_utils -from ...legacy import registry_mem +from ...legacy.weave import registry_mem from ...tests import fixture_fakewandb as fwb """ diff --git a/weave/legacy/tests/test_wb_domain_types.py b/weave/legacy/tests/test_wb_domain_types.py index ed550677062..5380e0347e3 100644 --- a/weave/legacy/tests/test_wb_domain_types.py +++ b/weave/legacy/tests/test_wb_domain_types.py @@ -1,7 +1,7 @@ -from weave.legacy import api as weave -from weave.legacy import storage -from weave.legacy import weave_types as types -from weave.legacy.ops_domain import wb_domain_types as wdt +from weave.legacy.weave import api as weave +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_domain import wb_domain_types as wdt def test_with_keys_assignability(): diff --git a/weave/legacy/tests/test_wb_end_to_end.py b/weave/legacy/tests/test_wb_end_to_end.py index 2232079248d..2902a34d1aa 100644 --- a/weave/legacy/tests/test_wb_end_to_end.py +++ b/weave/legacy/tests/test_wb_end_to_end.py @@ -1,7 +1,7 @@ import wandb import weave -from weave.legacy import compile +from weave.legacy.weave import compile # Example of end to end integration test @@ -11,17 +11,17 @@ def test_run_logging(user_by_api_key_in_env): run.finish() summary_node = ( - weave.legacy.ops.project(run.entity, run.project).run(run.id).summary()["a"] + weave.legacy.weave.ops.project(run.entity, run.project).run(run.id).summary()["a"] ) summary = weave.use(summary_node) assert summary == 1 - is_none_node = weave.legacy.ops.project(run.entity, run.project).isNone() + is_none_node = weave.legacy.weave.ops.project(run.entity, run.project).isNone() assert weave.use(is_none_node) == False - is_none_node = weave.legacy.ops.project( + is_none_node = weave.legacy.weave.ops.project( run.entity, "project_does_not_exist" ).isNone() @@ -60,7 +60,7 @@ def _test_basic_publish(user_fixture): uri == f"wandb-artifact:///{user_fixture.username}/weave/list:0cdf3358dc939f961ca9/obj" ) - assert weave.legacy.ref_base.Ref.from_str(uri).get() == [1, 2, 3] + assert weave.legacy.weave.ref_base.Ref.from_str(uri).get() == [1, 2, 3] # Example of end to end integration test @@ -74,7 +74,7 @@ def test_run_histories(user_by_api_key_in_env): run.finish() history_node = ( - weave.legacy.ops.project(run.entity, run.project).runs().history().concat()["a"] + weave.legacy.weave.ops.project(run.entity, run.project).runs().history().concat()["a"] ) history = weave.use(history_node) @@ -89,7 +89,7 @@ def test_run_history_count(user_by_api_key_in_env, cache_mode_minimal): run.log({"a": 2}) run.finish() - run_node = weave.legacy.ops.project(run.entity, run.project).run(run.id) + run_node = weave.legacy.weave.ops.project(run.entity, run.project).run(run.id) h_count_node = run_node.history().count() history_count = weave.use(h_count_node) assert history_count == 2 diff --git a/weave/legacy/tests/test_wb_history_loading_compatability.py b/weave/legacy/tests/test_wb_history_loading_compatability.py index 84edb18c1db..da2571fac1b 100644 --- a/weave/legacy/tests/test_wb_history_loading_compatability.py +++ b/weave/legacy/tests/test_wb_history_loading_compatability.py @@ -8,13 +8,13 @@ from PIL import Image import weave -from weave.legacy import context_state as _context -from weave.legacy.ops_domain.run_history.context import ( +from weave.legacy.weave import context_state as _context +from weave.legacy.weave.ops_domain.run_history.context import ( error_on_non_vectorized_history_transform, ) -from weave.legacy.ops_domain.run_history.history_op_common import _without_tags -from weave.legacy.wandb_client_api import wandb_gql_query -from weave.legacy.wandb_interface import wandb_stream_table +from weave.legacy.weave.ops_domain.run_history.history_op_common import _without_tags +from weave.legacy.weave.wandb_client_api import wandb_gql_query +from weave.legacy.weave.wandb_interface import wandb_stream_table HISTORY_OP_NAME = "history3" @@ -180,7 +180,7 @@ def do_batch_test(username, rows, do_assertion): row_accumulator, st, user_logged_keys = do_logging(username, rows) row_type = weave.types.TypeRegistry.type_of([{}, *row_accumulator]) - run_node = weave.legacy.ops.project(st._entity_name, st._project_name).run( + run_node = weave.legacy.weave.ops.project(st._entity_name, st._project_name).run( st._table_name ) @@ -392,7 +392,7 @@ def test_stream_table_perf(user_by_api_key_in_env, n_rows, n_cols): timings["log"] += time.time() print(f"Log Time: {timings['log']}") - run_node = weave.legacy.ops.project(st._entity_name, st._project_name).run( + run_node = weave.legacy.weave.ops.project(st._entity_name, st._project_name).run( st._table_name ) diff --git a/weave/legacy/tests/test_wb_stream_table.py b/weave/legacy/tests/test_wb_stream_table.py index 68301ad4dde..55bf16953ff 100644 --- a/weave/legacy/tests/test_wb_stream_table.py +++ b/weave/legacy/tests/test_wb_stream_table.py @@ -5,8 +5,8 @@ from PIL import Image import weave -from weave.legacy import context, execute, gql_json_cache, wandb_api, weave_types -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave import context, execute, gql_json_cache, wandb_api, weave_types +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable def make_stream_table(*args, **kwargs): @@ -26,7 +26,7 @@ def test_stream_logging(user_by_api_key_in_env): st.finish() hist_node = ( - weave.legacy.ops.project(user_by_api_key_in_env.username, "stream-tables") + weave.legacy.weave.ops.project(user_by_api_key_in_env.username, "stream-tables") .run("test_table") .history3() ) @@ -56,7 +56,7 @@ def test_bytes_read_from_arrow_reporting(user_by_api_key_in_env): st.finish() hist_node = ( - weave.legacy.ops.project(user_by_api_key_in_env.username, "stream-tables") + weave.legacy.weave.ops.project(user_by_api_key_in_env.username, "stream-tables") .run("test_table") .history3() ) @@ -90,7 +90,7 @@ def image(): time.sleep(5) hist_node = ( - weave.legacy.ops.project(user_by_api_key_in_env.username, "stream-tables") + weave.legacy.weave.ops.project(user_by_api_key_in_env.username, "stream-tables") .run("test_table-8") .history2() ) @@ -121,7 +121,7 @@ def test_multi_writers_sequential(user_by_api_key_in_env): def do_asserts(): hist_node = ( - weave.legacy.ops.project(user_by_api_key_in_env.username, "stream-tables") + weave.legacy.weave.ops.project(user_by_api_key_in_env.username, "stream-tables") .run("test_table") .history2() ) @@ -181,7 +181,7 @@ def test_multi_writers_parallel(user_by_api_key_in_env): st_2.finish() hist_node = ( - weave.legacy.ops.project(entity_name, "stream-tables") + weave.legacy.weave.ops.project(entity_name, "stream-tables") .run(table_name) .history2() ) diff --git a/weave/legacy/tests/test_wb_tables.py b/weave/legacy/tests/test_wb_tables.py index 0c89d128d29..3dca70237be 100644 --- a/weave/legacy/tests/test_wb_tables.py +++ b/weave/legacy/tests/test_wb_tables.py @@ -4,12 +4,12 @@ import wandb import weave -from weave.legacy.language_features.tagging import make_tag_getter_op -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_arrow.list_ops import filter -from weave.legacy.ops_domain import wbmedia -from weave.legacy.ops_domain.wandb_domain_gql import _make_alias -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave.language_features.tagging import make_tag_getter_op +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_arrow.list_ops import filter +from weave.legacy.weave.ops_domain import wbmedia +from weave.legacy.weave.ops_domain.wandb_domain_gql import _make_alias +from weave.legacy.weave.weave_internal import make_const_node def use_static_artifact_node( @@ -18,7 +18,7 @@ def use_static_artifact_node( project_name="test_project", collection_name="joined_table_artifact", version="latest", -) -> weave.legacy.graph.Node: +) -> weave.legacy.weave.graph.Node: fake_wandb.fake_api.add_mock( lambda q, ndx: { "project_5702147f0293fd7538d402af13069708": { @@ -53,7 +53,7 @@ def use_static_artifact_node( } } ) - return weave.legacy.ops.project(entity_name, project_name).artifactVersion( + return weave.legacy.weave.ops.project(entity_name, project_name).artifactVersion( collection_name, version ) @@ -242,9 +242,9 @@ def test_metric_table_join(fake_wandb): file_node = art_node.file("table.table.json") table_node = file_node.table() table_rows = table_node.rows().createIndexCheckpointTag() - grouped = table_rows.groupby(lambda row: weave.legacy.ops.dict_(label=row["label"])) + grouped = table_rows.groupby(lambda row: weave.legacy.weave.ops.dict_(label=row["label"])) sorted = grouped.sort( - lambda row: weave.legacy.ops.make_list(a=row.groupkey()["label"]), ["asc"] + lambda row: weave.legacy.weave.ops.make_list(a=row.groupkey()["label"]), ["asc"] ) group_col_0 = sorted[0].groupkey()["label"] group_col_1 = sorted[1].groupkey()["label"] @@ -265,9 +265,9 @@ def test_empty_table(fake_wandb): table_node = file_node.table() table_rows = table_node.rows().createIndexCheckpointTag() filtered = filter(table_rows, lambda row: row["label"] == "cat") - grouped = filtered.groupby(lambda row: weave.legacy.ops.dict_(label=row["label"])) + grouped = filtered.groupby(lambda row: weave.legacy.weave.ops.dict_(label=row["label"])) sorted = grouped.sort( - lambda row: weave.legacy.ops.make_list(a=row.groupkey()["label"]), ["asc"] + lambda row: weave.legacy.weave.ops.make_list(a=row.groupkey()["label"]), ["asc"] ) res = weave.use(sorted) assert res.to_pylist_raw() == [] @@ -306,14 +306,14 @@ def test_join_group_combo(fake_wandb): art_2_node = fake_wandb.mock_artifact_as_node(art_2) table_1_rows = art_1_node.file("table_1.table.json").table().rows() table_2_rows = art_2_node.file("table_2.table.json").table().rows() - list_of_tables = weave.legacy.ops.make_list(a=table_1_rows, b=table_2_rows).dropna() + list_of_tables = weave.legacy.weave.ops.make_list(a=table_1_rows, b=table_2_rows).dropna() joined_tables = list_of_tables.joinAll( - lambda row: weave.legacy.ops.make_list(a=row["id"]), False + lambda row: weave.legacy.weave.ops.make_list(a=row["id"]), False ) indexed = joined_tables.createIndexCheckpointTag() - grouped = indexed.groupby(lambda row: weave.legacy.ops.dict_(label=row["label"][0])) + grouped = indexed.groupby(lambda row: weave.legacy.weave.ops.dict_(label=row["label"][0])) sorted = grouped.sort( - lambda row: weave.legacy.ops.make_list(label=row.groupkey()["label"]), + lambda row: weave.legacy.weave.ops.make_list(label=row.groupkey()["label"]), ["asc"], ) assert weave.use(sorted.count()) == 3 @@ -326,7 +326,7 @@ def test_join_group_combo(fake_wandb): join_obj = sorted[0].joinObj()[0] assert weave.use(join_obj) == [1.0] - from weave.legacy import context_state + from weave.legacy.weave import context_state _loading_builtins_token = context_state.set_loading_built_ins() tag_getter_op = make_tag_getter_op.make_tag_getter_op( @@ -360,7 +360,7 @@ def test_group_by_const(fake_wandb): art_1_node = fake_wandb.mock_artifact_as_node(art_1) table_1_rows = art_1_node.file("table_1.table.json").table().rows() grouped = table_1_rows.groupby( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( a=make_const_node(weave.types.Boolean(), True) ) ) @@ -413,14 +413,14 @@ def test_column_sort(fake_wandb): for col in columns: sorted = rows.sort( - lambda row: weave.legacy.ops.make_list(label=row[col]), ["desc"] + lambda row: weave.legacy.weave.ops.make_list(label=row[col]), ["desc"] ) assert weave.use(sorted).to_pylist_notags() == [ dict(zip(columns, row)) for row in [row3, row2, row1] ] sorted = sorted.sort( - lambda row: weave.legacy.ops.make_list(label=row[col]), ["asc"] + lambda row: weave.legacy.weave.ops.make_list(label=row[col]), ["asc"] ) assert weave.use(sorted).to_pylist_notags() == [ dict(zip(columns, row)) for row in data @@ -428,7 +428,7 @@ def test_column_sort(fake_wandb): # Additional test sorting typed timestamps sorted = rows.sort( - lambda row: weave.legacy.ops.make_list(label=row["timestamp"].toTimestamp()), + lambda row: weave.legacy.weave.ops.make_list(label=row["timestamp"].toTimestamp()), ["desc"], ) assert weave.use(sorted).to_pylist_notags() == [ @@ -436,7 +436,7 @@ def test_column_sort(fake_wandb): ] sorted = sorted.sort( - lambda row: weave.legacy.ops.make_list(label=row["timestamp"].toTimestamp()), + lambda row: weave.legacy.weave.ops.make_list(label=row["timestamp"].toTimestamp()), ["asc"], ) assert weave.use(sorted).to_pylist_notags() == [ @@ -464,8 +464,8 @@ def test_group_avg_sort_combo(fake_wandb): art_1_node = fake_wandb.mock_artifact_as_node(art_1) rows = art_1_node.file("table_1.table.json").table().rows() - grouped = rows.groupby(lambda row: weave.legacy.ops.dict_(label=row["label"])) + grouped = rows.groupby(lambda row: weave.legacy.weave.ops.dict_(label=row["label"])) sorted = grouped.sort( - lambda row: weave.legacy.ops.make_list(label=row["score"].avg()), ["asc"] + lambda row: weave.legacy.weave.ops.make_list(label=row["score"].avg()), ["asc"] ) assert weave.use(sorted[2].groupkey()["label"]) == "C" diff --git a/weave/legacy/tests/test_weave_api.py b/weave/legacy/tests/test_weave_api.py index 90fd26ebb24..0e20c9acf37 100644 --- a/weave/legacy/tests/test_weave_api.py +++ b/weave/legacy/tests/test_weave_api.py @@ -3,8 +3,8 @@ import pytest import weave -import weave.legacy.context_state -import weave.legacy.wandb_api +import weave.legacy.weave.context_state +import weave.legacy.weave.wandb_api import weave.trace.weave_init @@ -12,25 +12,25 @@ def test_create_list_rename_delete(): os.environ["WEAVE_CACHE_MODE"] = "minimal" # create - art_node = weave.legacy.ops.get("local-artifact:///my-data:latest/obj") + art_node = weave.legacy.weave.ops.get("local-artifact:///my-data:latest/obj") art_node.set("foo") assert weave.use(art_node) == "foo" # list - arts = weave.use(weave.legacy.ops.local_artifacts()) + arts = weave.use(weave.legacy.weave.ops.local_artifacts()) assert len(arts) == 1 assert arts[0].name == "my-data" # rename - weave.legacy.ops.rename_artifact(art_node, "my-data2") - arts = weave.use(weave.legacy.ops.local_artifacts()) + weave.legacy.weave.ops.rename_artifact(art_node, "my-data2") + arts = weave.use(weave.legacy.weave.ops.local_artifacts()) assert len(arts) == 1 assert arts[0].name == "my-data2" # delete - art_node = weave.legacy.ops.get("local-artifact:///my-data2:latest/obj") - weave.legacy.ops.delete_artifact(art_node) - arts = weave.use(weave.legacy.ops.local_artifacts()) + art_node = weave.legacy.weave.ops.get("local-artifact:///my-data2:latest/obj") + weave.legacy.weave.ops.delete_artifact(art_node) + arts = weave.use(weave.legacy.weave.ops.local_artifacts()) assert len(arts) == 0 diff --git a/weave/legacy/tests/test_weave_types.py b/weave/legacy/tests/test_weave_types.py index 1c88c49d14f..070d1791465 100644 --- a/weave/legacy/tests/test_weave_types.py +++ b/weave/legacy/tests/test_weave_types.py @@ -3,12 +3,12 @@ import pytest import weave -import weave.legacy -import weave.legacy.weave_types -from weave.legacy import _dict_utils, runs -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.ops_domain import wbmedia +import weave.legacy.weave +import weave.legacy.weave.weave_types +from weave.legacy.weave import _dict_utils, runs +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.ops_domain import wbmedia from ... import errors @@ -155,23 +155,23 @@ def test_typeof_list_dict_merge(): def test_typeof_nested_dict_merge(): """Tests that nested merging is disabled.""" - t1 = weave.legacy.weave_types.TypedDict( - {"a": weave.legacy.weave_types.TypedDict({"b": types.Int()})} + t1 = weave.legacy.weave.weave_types.TypedDict( + {"a": weave.legacy.weave.weave_types.TypedDict({"b": types.Int()})} ) - t2 = weave.legacy.weave_types.TypedDict( - {"a": weave.legacy.weave_types.TypedDict({"c": types.String()})} + t2 = weave.legacy.weave.weave_types.TypedDict( + {"a": weave.legacy.weave.weave_types.TypedDict({"c": types.String()})} ) merged_type = _dict_utils.typeddict_merge_output_type({"self": t1, "other": t2}) - assert merged_type == weave.legacy.weave_types.TypedDict( - {"a": weave.legacy.weave_types.TypedDict({"c": types.String()})} + assert merged_type == weave.legacy.weave.weave_types.TypedDict( + {"a": weave.legacy.weave.weave_types.TypedDict({"c": types.String()})} ) def test_dict_without_key_type(): - fully_typed = weave.legacy.weave_types.TypeRegistry.type_from_dict( + fully_typed = weave.legacy.weave.weave_types.TypeRegistry.type_from_dict( {"type": "dict", "keyType": "string", "objectType": "number"} ) - partial_typed = weave.legacy.weave_types.TypeRegistry.type_from_dict( + partial_typed = weave.legacy.weave.weave_types.TypeRegistry.type_from_dict( {"type": "dict", "objectType": "number"} ) assert fully_typed.assign_type(partial_typed) @@ -179,24 +179,24 @@ def test_dict_without_key_type(): # def test_union_unknown(): # assert ( -# weave.legacy.weave_types.union( -# weave.legacy.weave_types.String(), weave.legacy.weave_types.UnknownType() +# weave.legacy.weave.weave_types.union( +# weave.legacy.weave.weave_types.String(), weave.legacy.weave.weave_types.UnknownType() # ) -# == weave.legacy.weave_types.String() +# == weave.legacy.weave.weave_types.String() # ) # assert ( -# weave.legacy.weave_types.union( -# weave.legacy.weave_types.UnknownType(), weave.legacy.weave_types.UnknownType() +# weave.legacy.weave.weave_types.union( +# weave.legacy.weave.weave_types.UnknownType(), weave.legacy.weave.weave_types.UnknownType() # ) -# == weave.legacy.weave_types.UnknownType() +# == weave.legacy.weave.weave_types.UnknownType() # ) # assert ( -# weave.legacy.weave_types.union( -# weave.legacy.weave_types.UnknownType(), -# weave.legacy.weave_types.UnknownType(), +# weave.legacy.weave.weave_types.union( +# weave.legacy.weave.weave_types.UnknownType(), +# weave.legacy.weave.weave_types.UnknownType(), # weave.types.String(), # ) -# == weave.legacy.weave_types.String() +# == weave.legacy.weave.weave_types.String() # ) @@ -204,78 +204,78 @@ def test_union_access(): ### Type return # Not all members have props - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), - weave.legacy.weave_types.List(weave.legacy.weave_types.String()), + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), + weave.legacy.weave.weave_types.List(weave.legacy.weave.weave_types.String()), ) with pytest.raises(AttributeError): unioned.object_type # Combined dicts - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.List(weave.legacy.weave_types.String()), - weave.legacy.weave_types.List(weave.legacy.weave_types.Number()), + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.List(weave.legacy.weave.weave_types.String()), + weave.legacy.weave.weave_types.List(weave.legacy.weave.weave_types.Number()), ) - assert unioned.object_type == weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), weave.legacy.weave_types.Number() + assert unioned.object_type == weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), weave.legacy.weave.weave_types.Number() ) # Nullable type - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.NoneType(), - weave.legacy.weave_types.List(weave.legacy.weave_types.String()), + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.NoneType(), + weave.legacy.weave.weave_types.List(weave.legacy.weave.weave_types.String()), ) - assert unioned.object_type == weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), weave.legacy.weave_types.NoneType() + assert unioned.object_type == weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), weave.legacy.weave.weave_types.NoneType() ) ### Dict Return # Not all members have props - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), - weave.legacy.weave_types.TypedDict({"a": weave.legacy.weave_types.String()}), + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), + weave.legacy.weave.weave_types.TypedDict({"a": weave.legacy.weave.weave_types.String()}), ) with pytest.raises(AttributeError): unioned.property_types # Combined dicts - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.TypedDict( + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.TypedDict( { - "same": weave.legacy.weave_types.Number(), - "solo_a": weave.legacy.weave_types.Number(), - "differ": weave.legacy.weave_types.Number(), + "same": weave.legacy.weave.weave_types.Number(), + "solo_a": weave.legacy.weave.weave_types.Number(), + "differ": weave.legacy.weave.weave_types.Number(), } ), - weave.legacy.weave_types.TypedDict( + weave.legacy.weave.weave_types.TypedDict( { - "same": weave.legacy.weave_types.Number(), - "solo_b": weave.legacy.weave_types.String(), - "differ": weave.legacy.weave_types.String(), + "same": weave.legacy.weave.weave_types.Number(), + "solo_b": weave.legacy.weave.weave_types.String(), + "differ": weave.legacy.weave.weave_types.String(), } ), ) assert unioned.property_types == { - "same": weave.legacy.weave_types.Number(), - "solo_a": weave.legacy.weave_types.union( - weave.legacy.weave_types.Number(), weave.legacy.weave_types.NoneType() + "same": weave.legacy.weave.weave_types.Number(), + "solo_a": weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.Number(), weave.legacy.weave.weave_types.NoneType() ), - "solo_b": weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), weave.legacy.weave_types.NoneType() + "solo_b": weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), weave.legacy.weave.weave_types.NoneType() ), - "differ": weave.legacy.weave_types.union( - weave.legacy.weave_types.Number(), weave.legacy.weave_types.String() + "differ": weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.Number(), weave.legacy.weave.weave_types.String() ), } # Nullable type - unioned = weave.legacy.weave_types.union( - weave.legacy.weave_types.NoneType(), - weave.legacy.weave_types.TypedDict({"a": weave.legacy.weave_types.String()}), + unioned = weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.NoneType(), + weave.legacy.weave.weave_types.TypedDict({"a": weave.legacy.weave.weave_types.String()}), ) assert unioned.property_types == { - "a": weave.legacy.weave_types.union( - weave.legacy.weave_types.String(), weave.legacy.weave_types.NoneType() + "a": weave.legacy.weave.weave_types.union( + weave.legacy.weave.weave_types.String(), weave.legacy.weave.weave_types.NoneType() ) } @@ -713,7 +713,7 @@ def test_deserializes_single_member_union(): def test_wbrun_not_assignable_to_weave_run(): - from weave.legacy.ops_domain import wb_domain_types + from weave.legacy.weave.ops_domain import wb_domain_types assert not weave.types.optional(wb_domain_types.Run().WeaveType()).assign_type( weave.types.RunType( diff --git a/weave/legacy/tests/test_weavejs_fixes.py b/weave/legacy/tests/test_weavejs_fixes.py index 94bc36039a8..9f6cf60b1f7 100644 --- a/weave/legacy/tests/test_weavejs_fixes.py +++ b/weave/legacy/tests/test_weavejs_fixes.py @@ -2,7 +2,7 @@ import pytest -from weave.legacy import ( +from weave.legacy.weave import ( api, context_state, mappers_python, @@ -10,7 +10,7 @@ weave_internal, weavejs_fixes, ) -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types @pytest.mark.skip( diff --git a/weave/legacy/tests/test_weavify.py b/weave/legacy/tests/test_weavify.py index d92fa838759..9335160bdac 100644 --- a/weave/legacy/tests/test_weavify.py +++ b/weave/legacy/tests/test_weavify.py @@ -1,9 +1,9 @@ import pytest -from weave.legacy import graph, ops, weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import graph, ops, weave_internal +from weave.legacy.weave import weave_types as types -from ...legacy import weavify +from ...legacy.weave import weavify from weave.legacy.tests.util import geom diff --git a/weave/legacy/tests/test_with_columns.py b/weave/legacy/tests/test_with_columns.py index 1d7404471d1..e68b8e10435 100644 --- a/weave/legacy/tests/test_with_columns.py +++ b/weave/legacy/tests/test_with_columns.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow def test_with_columns_basic(): diff --git a/weave/legacy/tests/util/concrete_tagged_value.py b/weave/legacy/tests/util/concrete_tagged_value.py index ff7d60442a0..403ab8fa21f 100644 --- a/weave/legacy/tests/util/concrete_tagged_value.py +++ b/weave/legacy/tests/util/concrete_tagged_value.py @@ -5,8 +5,8 @@ import dataclasses import typing -from weave.legacy import box -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave import box +from weave.legacy.weave.language_features.tagging import tag_store @dataclasses.dataclass diff --git a/weave/legacy/tests/util/geom.py b/weave/legacy/tests/util/geom.py index 93653728cc5..46cbdb7e44e 100644 --- a/weave/legacy/tests/util/geom.py +++ b/weave/legacy/tests/util/geom.py @@ -3,8 +3,8 @@ import typing import weave -from weave.legacy import context_state as _context_state -from weave.legacy import panels +from weave.legacy.weave import context_state as _context_state +from weave.legacy.weave import panels _loading_builtins_token = _context_state.set_loading_built_ins() diff --git a/weave/legacy/tests/util/list_arrow_test_helpers.py b/weave/legacy/tests/util/list_arrow_test_helpers.py index ab7e19d8eb1..c130ba5cde0 100644 --- a/weave/legacy/tests/util/list_arrow_test_helpers.py +++ b/weave/legacy/tests/util/list_arrow_test_helpers.py @@ -1,6 +1,6 @@ -from weave.legacy import api as weave -from weave.legacy import ops_arrow as arrow -from weave.legacy.ops_primitives import list_ +from weave.legacy.weave import api as weave +from weave.legacy.weave import ops_arrow as arrow +from weave.legacy.weave.ops_primitives import list_ class ListLikeNodeInterface: diff --git a/weave/legacy/tests/util/op_specs.py b/weave/legacy/tests/util/op_specs.py index 5e586cdd983..9924f60754b 100644 --- a/weave/legacy/tests/util/op_specs.py +++ b/weave/legacy/tests/util/op_specs.py @@ -3,9 +3,9 @@ import typing import weave -from weave.legacy import op_def, ops_primitives -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.timestamp import PY_DATETIME_MAX_MS, PY_DATETIME_MIN_MS +from weave.legacy.weave import op_def, ops_primitives +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.timestamp import PY_DATETIME_MAX_MS, PY_DATETIME_MIN_MS from .concrete_tagged_value import TaggedValue diff --git a/weave/legacy/tests/util/tag_test_util.py b/weave/legacy/tests/util/tag_test_util.py index b83074d0d71..bfed6da0b88 100644 --- a/weave/legacy/tests/util/tag_test_util.py +++ b/weave/legacy/tests/util/tag_test_util.py @@ -1,9 +1,9 @@ import weave -from weave.legacy import box, graph -from weave.legacy import context_state as _context_state -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import tag_store -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave import box, graph +from weave.legacy.weave import context_state as _context_state +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import tag_store +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType tag_adders = 0 @@ -34,6 +34,6 @@ def custom_tagger(obj): # type: ignore[no-untyped-def] def make_get_tag(tag_name: str): # type: ignore[no-untyped-def] - from weave.legacy.language_features.tagging import make_tag_getter_op + from weave.legacy.weave.language_features.tagging import make_tag_getter_op return make_tag_getter_op.make_tag_getter_op(f"_ct_{tag_name}", types.String()) diff --git a/weave/legacy/tests/util/weavejs_ops.py b/weave/legacy/tests/util/weavejs_ops.py index 82bceb9fba8..afaab26438b 100644 --- a/weave/legacy/tests/util/weavejs_ops.py +++ b/weave/legacy/tests/util/weavejs_ops.py @@ -1,10 +1,10 @@ # WeaveJS ops used for testing. These are not used in production. -from weave.legacy import graph, weave_internal -from weave.legacy import weave_types as types -from weave.legacy._dict_utils import typeddict_pick_output_type -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.language_features.tagging.tagging_op_logic import ( +from weave.legacy.weave import graph, weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave._dict_utils import typeddict_pick_output_type +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.language_features.tagging.tagging_op_logic import ( op_get_tag_type_resolver, op_make_type_tagged_resolver, ) diff --git a/weave/legacy/arrow/__init__.py b/weave/legacy/weave/__init__.py similarity index 100% rename from weave/legacy/arrow/__init__.py rename to weave/legacy/weave/__init__.py diff --git a/weave/legacy/_dict_utils.py b/weave/legacy/weave/_dict_utils.py similarity index 97% rename from weave/legacy/_dict_utils.py rename to weave/legacy/weave/_dict_utils.py index 744b62474ff..4636d46037f 100644 --- a/weave/legacy/_dict_utils.py +++ b/weave/legacy/weave/_dict_utils.py @@ -1,10 +1,10 @@ import typing -from weave.legacy import box -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave import box +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type -from weave.legacy import errors -from weave.legacy import weave_types as types +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types def typeddict_pick_output_type(input_types): diff --git a/weave/legacy/api.py b/weave/legacy/weave/api.py similarity index 78% rename from weave/legacy/api.py rename to weave/legacy/weave/api.py index ba13f7e09f8..13d34e60760 100644 --- a/weave/legacy/api.py +++ b/weave/legacy/weave/api.py @@ -2,50 +2,50 @@ import typing -from weave.legacy import graph as _graph -from weave.legacy.graph import Node +from weave.legacy.weave import graph as _graph +from weave.legacy.weave.graph import Node # If this is not imported, serialization of Weave Nodes is incorrect! -from weave.legacy import graph_mapper as _graph_mapper +from weave.legacy.weave import graph_mapper as _graph_mapper -from .. import storage as _storage +from . import storage as _storage from . import ref_base as _ref_base -from weave.legacy import wandb_api as _wandb_api +from weave.legacy.weave import wandb_api as _wandb_api -from weave.legacy import weave_internal as _weave_internal +from weave.legacy.weave import weave_internal as _weave_internal -from weave.legacy import util as _util +from weave.legacy.weave import util as _util -from weave.legacy import context as _context -from ..trace import weave_init as _weave_init -from ..trace import weave_client as _weave_client +from weave.legacy.weave import context as _context +from ...trace import weave_init as _weave_init +from ...trace import weave_client as _weave_client # exposed as part of api -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types # needed to enable automatic numpy serialization from . import types_numpy as _types_numpy -from weave.legacy import errors -from weave.legacy.decorators import weave_class, mutation, type +from weave.legacy.weave import errors +from weave.legacy.weave.decorators import weave_class, mutation, type -from weave.legacy import usage_analytics -from weave.legacy.context import ( +from weave.legacy.weave import usage_analytics +from weave.legacy.weave.context import ( use_fixed_server_port, use_frontend_devmode, # eager_execution, use_lazy_execution, ) -from weave.legacy.panel import Panel +from weave.legacy.weave.panel import Panel -from weave.legacy.arrow.list_ import ArrowWeaveList as WeaveList +from weave.legacy.weave.arrow.list_ import ArrowWeaveList as WeaveList # TODO: This is here because the op overloaded... from weave.trace.op import op # noqa: F401 def save(node_or_obj, name=None): # type: ignore - from weave.legacy.ops_primitives.weave_api import get, save + from weave.legacy.weave.ops_primitives.weave_api import get, save if isinstance(node_or_obj, _graph.Node): return save(node_or_obj, name=name) diff --git a/weave/legacy/ecosystem/twitter/__init__.py b/weave/legacy/weave/arrow/__init__.py similarity index 100% rename from weave/legacy/ecosystem/twitter/__init__.py rename to weave/legacy/weave/arrow/__init__.py diff --git a/weave/legacy/arrow/arrow.py b/weave/legacy/weave/arrow/arrow.py similarity index 97% rename from weave/legacy/arrow/arrow.py rename to weave/legacy/weave/arrow/arrow.py index 0c5576eaa99..b6cc10e8790 100644 --- a/weave/legacy/arrow/arrow.py +++ b/weave/legacy/weave/arrow/arrow.py @@ -11,9 +11,9 @@ py_type = type -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import artifact_fs, partial_object +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import artifact_fs, partial_object def arrow_type_to_weave_type(pa_type: pa.DataType) -> types.Type: @@ -184,7 +184,7 @@ def save_instance(self, obj, artifact, name): ) # v1 AWL format - # from weave.legacy.arrow import convert + # from weave.legacy.weave.arrow import convert # parquet_friendly = convert.to_parquet_friendly(obj) # table = pa.table({"arr": parquet_friendly._arrow_data}) @@ -223,7 +223,7 @@ def load_instance( with artifact.open(f"{name}.ArrowWeaveList.type.json") as f: object_type = json.load(f) object_type = types.TypeRegistry.type_from_dict(object_type) - from weave.legacy.arrow import list_ + from weave.legacy.weave.arrow import list_ if "_weave_awl_format" not in artifact.metadata: # v1 AWL format @@ -232,7 +232,7 @@ def load_instance( arr = table["arr"].combine_chunks() with list_.unsafe_awl_construction("load_from_parquet"): l = self.instance_class(arr, object_type=object_type, artifact=artifact) # type: ignore - from weave.legacy.arrow import convert + from weave.legacy.weave.arrow import convert res = convert.from_parquet_friendly(l) elif artifact.metadata["_weave_awl_format"] == 2: @@ -366,7 +366,7 @@ def rewrite_weavelist_refs(arrow_data, object_type, source_artifact, target_arti def _object_type_has_props(object_type): - from weave.legacy.language_features.tagging import tagged_value_type + from weave.legacy.weave.language_features.tagging import tagged_value_type return ( isinstance(object_type, types.TypedDict) @@ -376,7 +376,7 @@ def _object_type_has_props(object_type): def _object_type_prop_types(object_type): - from weave.legacy.language_features.tagging import tagged_value_type + from weave.legacy.weave.language_features.tagging import tagged_value_type if isinstance(object_type, tagged_value_type.TaggedValueType): return { diff --git a/weave/legacy/arrow/arrow_tags.py b/weave/legacy/weave/arrow/arrow_tags.py similarity index 93% rename from weave/legacy/arrow/arrow_tags.py rename to weave/legacy/weave/arrow/arrow_tags.py index 52ba674abca..3fb27314a00 100644 --- a/weave/legacy/arrow/arrow_tags.py +++ b/weave/legacy/weave/arrow/arrow_tags.py @@ -3,10 +3,10 @@ import pyarrow as pa from pyarrow import compute as pc -from weave.legacy import weave_types as types -from weave.legacy.arrow import convert -from weave.legacy.arrow.arrow import offsets_starting_at_zero -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.arrow import offsets_starting_at_zero +from weave.legacy.weave.language_features.tagging import ( process_opdef_output_type, tag_store, ) @@ -109,7 +109,7 @@ def awl_add_arrow_tags( new_object_type = process_opdef_output_type.op_make_type_tagged_resolver( l.object_type, tag_type ) - from weave.legacy.arrow.list_ import ArrowWeaveList + from weave.legacy.weave.arrow.list_ import ArrowWeaveList res: ArrowWeaveList = ArrowWeaveList(new_value, new_object_type, l._artifact) if tag_store.is_tagged(l): diff --git a/weave/legacy/arrow/concat.py b/weave/legacy/weave/arrow/concat.py similarity index 98% rename from weave/legacy/arrow/concat.py rename to weave/legacy/weave/arrow/concat.py index 46453d7de70..d383a9c09c9 100644 --- a/weave/legacy/arrow/concat.py +++ b/weave/legacy/weave/arrow/concat.py @@ -12,9 +12,9 @@ import pyarrow as pa from pyarrow import compute as pc -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.arrow.list_ import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow.list_ import ( ArrowWeaveList, ArrowWeaveListGeneric, is_list_arrowweavelist, @@ -26,11 +26,11 @@ offsets_starting_at_zero, unsafe_awl_construction, ) -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave.language_features.tagging import tagged_value_type DEBUG = False -from weave.legacy import engine_trace +from weave.legacy.weave import engine_trace tracer = engine_trace.tracer() diff --git a/weave/legacy/arrow/constructors.py b/weave/legacy/weave/arrow/constructors.py similarity index 90% rename from weave/legacy/arrow/constructors.py rename to weave/legacy/weave/arrow/constructors.py index 578643160c8..6beeb475d92 100644 --- a/weave/legacy/arrow/constructors.py +++ b/weave/legacy/weave/arrow/constructors.py @@ -3,13 +3,13 @@ import pyarrow as pa -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import artifact_base, box -from weave.legacy.arrow import arrow_tags, convert -from weave.legacy.arrow.arrow import ArrowWeaveListType, arrow_as_array -from weave.legacy.arrow.list_ import ArrowWeaveList -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import artifact_base, box +from weave.legacy.weave.arrow import arrow_tags, convert +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType, arrow_as_array +from weave.legacy.weave.arrow.list_ import ArrowWeaveList +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type def repeat(value: typing.Any, count: int) -> pa.Array: diff --git a/weave/legacy/arrow/convert.py b/weave/legacy/weave/arrow/convert.py similarity index 98% rename from weave/legacy/arrow/convert.py rename to weave/legacy/weave/arrow/convert.py index 95c22eb4f3d..a53a610fd01 100644 --- a/weave/legacy/arrow/convert.py +++ b/weave/legacy/weave/arrow/convert.py @@ -3,19 +3,19 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import api as api -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import errors, arrow_util, artifact_base, artifact_mem, box, mappers_arrow -from weave.legacy.arrow.arrow import ( +from weave.legacy.weave import api as api +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import errors, arrow_util, artifact_base, artifact_mem, box, mappers_arrow +from weave.legacy.weave.arrow.arrow import ( ArrowWeaveListType, ) -from weave.legacy.arrow.list_ import ( +from weave.legacy.weave.arrow.list_ import ( ArrowWeaveList, PathType, unsafe_awl_construction, ) -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type # Hmm... this doesn't work on ObjectType, which contains a Union of Struct... # We need that because our ImageFileArtifactRefType has a union of structs @@ -502,7 +502,7 @@ def simple_to_string(arr: pa.Array): def to_compare_safe(awl: ArrowWeaveList) -> ArrowWeaveList: """Converts any ArrowWeaveList to simple type that pa.compute.equal can compare.""" - from weave.legacy.ops_domain.wbmedia import ArtifactAssetType + from weave.legacy.weave.ops_domain.wbmedia import ArtifactAssetType # Returns a number of string arrow weave list, possibly with Nones def _to_compare_safe( diff --git a/weave/legacy/arrow/list_.py b/weave/legacy/weave/arrow/list_.py similarity index 98% rename from weave/legacy/arrow/list_.py rename to weave/legacy/weave/arrow/list_.py index f0855545fb5..6e3d0e85161 100644 --- a/weave/legacy/arrow/list_.py +++ b/weave/legacy/weave/arrow/list_.py @@ -10,8 +10,8 @@ import pyarrow.compute as pc import typing_extensions -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( weave_internal, errors, _dict_utils, @@ -25,7 +25,7 @@ node_ref, ref_base, ) -from weave.legacy.arrow.arrow import ( +from weave.legacy.weave.arrow.arrow import ( ArrowWeaveListType, arrow_as_array, arrow_zip, @@ -33,7 +33,7 @@ pretty_print_arrow_type, safe_is_null, ) -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.language_features.tagging import ( tag_store, tagged_value_type, ) @@ -494,7 +494,7 @@ def unsafe_awl_construction(reason: str): def dict_of_columns_to_awl(d: dict[str, typing.Any]) -> "ArrowWeaveList": - from weave.legacy.arrow import constructors + from weave.legacy.weave.arrow import constructors cols = {} for k, v in d.items(): @@ -522,7 +522,7 @@ def __init__( artifact: typing.Optional[artifact_base.Artifact] = None, invalid_reason=None, ) -> None: - from weave.legacy.arrow import constructors, convert + from weave.legacy.weave.arrow import constructors, convert # Do not dictionary decode this array! That will break performance. # Note we combine chunks here, to make the internal interface easy @@ -610,7 +610,7 @@ def __add__(self, other) -> "ArrowWeaveList": if not isinstance(other, ArrowWeaveList): raise TypeError(f"Expected list or ArrowWeaveList, got {type(other)}") - from weave.legacy.arrow import concat + from weave.legacy.weave.arrow import concat return concat.concatenate(self, other) @@ -901,7 +901,7 @@ def _map_column( type_codes, pa.scalar(i, pa.int8()), ) - from weave.legacy.arrow import concat + from weave.legacy.weave.arrow import concat members[i] = concat.concatenate(member_i, member_j) members[j] = None @@ -1499,12 +1499,12 @@ def apply( self, fn: typing.Union[typing.Callable[[typing.Any], typing.Any], graph.Node] ): fn = self._make_lambda_node(fn) - from weave.legacy.ops_arrow.vectorize import _apply_fn_node_with_tag_pushdown + from weave.legacy.weave.ops_arrow.vectorize import _apply_fn_node_with_tag_pushdown return _apply_fn_node_with_tag_pushdown(self, fn) # type: ignore def concat(self, other: "ArrowWeaveList") -> "ArrowWeaveList": - from weave.legacy.arrow import concat + from weave.legacy.weave.arrow import concat return concat.concatenate(self, other) @@ -1518,7 +1518,7 @@ def join2( leftOuter: bool = False, rightOuter: bool = False, ): - from weave.legacy.ops_arrow import list_join + from weave.legacy.weave.ops_arrow import list_join join1Fn = self._make_lambda_node(join1Fn) join2Fn = other._make_lambda_node(join2Fn) @@ -1666,7 +1666,7 @@ def make_vec_taggedvalue( def awl_zip(*arrs: ArrowWeaveList) -> ArrowWeaveList: if not arrs: raise ValueError("Cannot zip empty list") - from weave.legacy.arrow import convert + from weave.legacy.weave.arrow import convert arrs = convert.unify_types(*arrs) zipped = arrow_zip(*[a._arrow_data for a in arrs]) diff --git a/weave/legacy/arrow_util.py b/weave/legacy/weave/arrow_util.py similarity index 100% rename from weave/legacy/arrow_util.py rename to weave/legacy/weave/arrow_util.py diff --git a/weave/legacy/artifact_base.py b/weave/legacy/weave/artifact_base.py similarity index 95% rename from weave/legacy/artifact_base.py rename to weave/legacy/weave/artifact_base.py index ae98ed7bc84..3d1b56de86d 100644 --- a/weave/legacy/artifact_base.py +++ b/weave/legacy/weave/artifact_base.py @@ -7,8 +7,8 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import ref_base +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ref_base class Artifact: diff --git a/weave/legacy/artifact_fs.py b/weave/legacy/weave/artifact_fs.py similarity index 97% rename from weave/legacy/artifact_fs.py rename to weave/legacy/weave/artifact_fs.py index 36ab31229a8..37cc0a3b5ae 100644 --- a/weave/legacy/artifact_fs.py +++ b/weave/legacy/weave/artifact_fs.py @@ -6,14 +6,14 @@ import os import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import artifact_base, file_base, object_context, ref_base, uris -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import artifact_base, file_base, object_context, ref_base, uris +from weave.legacy.weave.language_features.tagging import tag_store from weave.trace import ref_util if typing.TYPE_CHECKING: - from weave.legacy import graph + from weave.legacy.weave import graph class FilesystemArtifactType(types.Type): @@ -85,7 +85,7 @@ def get(self, key: str, type_: types.Type) -> typing.Any: return self.ref_from_local_str(key, type_).get() def as_node(self) -> "graph.Node": - from weave.legacy.ops_primitives.weave_api import get as op_get + from weave.legacy.weave.ops_primitives.weave_api import get as op_get return op_get(str(self)) @@ -267,7 +267,7 @@ def type(self) -> types.Type: ot = self._outer_type if self.extra is not None: - from weave.legacy import types_numpy + from weave.legacy.weave import types_numpy if not types.is_list_like(ot) and isinstance( ot, types_numpy.NumpyArrayType @@ -451,7 +451,7 @@ def size(self) -> int: return self.artifact.size(self.path) def digest(self) -> typing.Optional[str]: - from weave.legacy.artifact_wandb import WandbArtifact + from weave.legacy.weave.artifact_wandb import WandbArtifact if isinstance(self.artifact, WandbArtifact): # we can get the digest from the manifest (much faster) diff --git a/weave/legacy/artifact_local.py b/weave/legacy/weave/artifact_local.py similarity index 99% rename from weave/legacy/artifact_local.py rename to weave/legacy/weave/artifact_local.py index a22b6ea30e9..fc49c4c187e 100644 --- a/weave/legacy/artifact_local.py +++ b/weave/legacy/weave/artifact_local.py @@ -9,9 +9,9 @@ import typing from datetime import datetime -from weave.legacy import filesystem -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import filesystem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( artifact_fs, artifact_wandb, environment, diff --git a/weave/legacy/artifact_mem.py b/weave/legacy/weave/artifact_mem.py similarity index 90% rename from weave/legacy/artifact_mem.py rename to weave/legacy/weave/artifact_mem.py index 3e01f70ccb3..44fb3f9ff0e 100644 --- a/weave/legacy/artifact_mem.py +++ b/weave/legacy/weave/artifact_mem.py @@ -1,9 +1,9 @@ import typing from . import artifact_base -from weave.legacy import weave_types as types -from weave.legacy import errors -from weave.legacy import ref_base +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import errors +from weave.legacy.weave import ref_base class MemArtifact(artifact_base.Artifact): diff --git a/weave/legacy/artifact_wandb.py b/weave/legacy/weave/artifact_wandb.py similarity index 99% rename from weave/legacy/artifact_wandb.py rename to weave/legacy/weave/artifact_wandb.py index eff48f0519d..a19a33af7ef 100644 --- a/weave/legacy/artifact_wandb.py +++ b/weave/legacy/weave/artifact_wandb.py @@ -13,8 +13,8 @@ from wandb.apis.public import api as wb_public from wandb.sdk.lib.hashutil import b64_to_hex_id, hex_to_b64_id -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( filesystem, urls, errors, @@ -28,10 +28,10 @@ wandb_client_api, util, ) -from weave.legacy.wandb_interface import wandb_artifact_pusher +from weave.legacy.weave.wandb_interface import wandb_artifact_pusher if typing.TYPE_CHECKING: - from weave.legacy.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun + from weave.legacy.weave.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun from .run_streamtable_span import RunStreamTableSpan @@ -417,7 +417,7 @@ def __init__( ] ] = None, ): - from weave.legacy import io_service + from weave.legacy.weave import io_service self.io_service = io_service.get_sync_client() self.name = name @@ -857,7 +857,7 @@ def from_uri(cls, uri: uris.WeaveURI) -> "WandbArtifactRef": @property def ui_url(self): root_type = self.type.root_type_class() - from weave.legacy.op_def_type import OpDefType + from weave.legacy.weave.op_def_type import OpDefType if issubclass(root_type, OpDefType): return urls.op_version_path( diff --git a/weave/legacy/async_demo.py b/weave/legacy/weave/async_demo.py similarity index 97% rename from weave/legacy/async_demo.py rename to weave/legacy/weave/async_demo.py index 9dcd34bd5f3..1e1f7a3d28a 100644 --- a/weave/legacy/async_demo.py +++ b/weave/legacy/weave/async_demo.py @@ -3,7 +3,7 @@ import time import weave -from weave.legacy import context_state as _context_state +from weave.legacy.weave import context_state as _context_state _loading_builtins_token = _context_state.set_loading_built_ins() diff --git a/weave/legacy/async_map.py b/weave/legacy/weave/async_map.py similarity index 100% rename from weave/legacy/async_map.py rename to weave/legacy/weave/async_map.py diff --git a/weave/legacy/async_queue.py b/weave/legacy/weave/async_queue.py similarity index 100% rename from weave/legacy/async_queue.py rename to weave/legacy/weave/async_queue.py diff --git a/weave/legacy/box.py b/weave/legacy/weave/box.py similarity index 99% rename from weave/legacy/box.py rename to weave/legacy/weave/box.py index b957fc6ba3f..a16725af94a 100644 --- a/weave/legacy/box.py +++ b/weave/legacy/weave/box.py @@ -5,7 +5,7 @@ import numpy as np from weave.trace import ref_util -from weave.legacy import context_state +from weave.legacy.weave import context_state def make_id() -> int: diff --git a/weave/legacy/cache.py b/weave/legacy/weave/cache.py similarity index 98% rename from weave/legacy/cache.py rename to weave/legacy/weave/cache.py index bb254ca9473..5db00184bfa 100644 --- a/weave/legacy/cache.py +++ b/weave/legacy/weave/cache.py @@ -6,8 +6,8 @@ import time import typing -from weave.legacy import errors -from weave.legacy import context_state, environment, wandb_api, engine_trace +from weave.legacy.weave import errors +from weave.legacy.weave import context_state, environment, wandb_api, engine_trace statsd = engine_trace.statsd() # type: ignore logger = logging.getLogger("root") diff --git a/weave/legacy/client.py b/weave/legacy/weave/client.py similarity index 91% rename from weave/legacy/client.py rename to weave/legacy/weave/client.py index e1a3a32ce0c..9c10cd2f455 100644 --- a/weave/legacy/client.py +++ b/weave/legacy/weave/client.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_types -from weave.legacy import storage +from weave.legacy.weave import weave_types +from weave.legacy.weave import storage class Client: diff --git a/weave/legacy/client_interface.py b/weave/legacy/weave/client_interface.py similarity index 100% rename from weave/legacy/client_interface.py rename to weave/legacy/weave/client_interface.py diff --git a/weave/legacy/codifiable_value_mixin.py b/weave/legacy/weave/codifiable_value_mixin.py similarity index 100% rename from weave/legacy/codifiable_value_mixin.py rename to weave/legacy/weave/codifiable_value_mixin.py diff --git a/weave/legacy/codify.py b/weave/legacy/weave/codify.py similarity index 91% rename from weave/legacy/codify.py rename to weave/legacy/weave/codify.py index ca1036dd826..0e6b5c273b6 100644 --- a/weave/legacy/codify.py +++ b/weave/legacy/weave/codify.py @@ -6,8 +6,8 @@ import black -from weave.legacy import weave_types -from weave.legacy import storage, graph, registry_mem +from weave.legacy.weave import weave_types +from weave.legacy.weave import storage, graph, registry_mem from . import codifiable_value_mixin @@ -97,7 +97,7 @@ def _try_otc_using_codifiable_mixin(obj: typing.Any) -> typing.Optional[str]: # assert weave_types.type_name_to_type(obj_type.name)() == obj_type # d = obj_type.instance_to_dict(obj) # obj_type_name = obj_type.name -# return f"""weave.legacy.codify.load_type({obj_type_name}, {d})""" +# return f"""weave.legacy.weave.codify.load_type({obj_type_name}, {d})""" # return None @@ -128,7 +128,7 @@ def _try_otc_using_dataclasses(obj: typing.Any) -> typing.Optional[str]: if class_type.__module__.startswith("weave.decorator_type") and issubclass( class_type, weave_types.Type ): - qualified_classpath = "weave.legacy.weave_types" + qualified_classpath = "weave.legacy.weave.weave_types" qualified_classname = f"type_name_to_type('{class_type.name}')" else: qualified_classpath = _module_name_corrections(class_type.__module__) @@ -162,7 +162,7 @@ def _try_otc_using_dataclasses(obj: typing.Any) -> typing.Optional[str]: def _otc_using_storage_fallback(obj: typing.Any) -> str: - return f"""weave.legacy.codify.load({storage.to_python(obj)})""" + return f"""weave.legacy.weave.codify.load({storage.to_python(obj)})""" # Helpers @@ -170,8 +170,8 @@ def _otc_using_storage_fallback(obj: typing.Any) -> str: # Hack: def _module_name_corrections(qualified_name: str) -> str: - if qualified_name == "weave.legacy.ops_primitives.file_local": - return "weave.legacy.ops" + if qualified_name == "weave.legacy.weave.ops_primitives.file_local": + return "weave.legacy.weave.ops" elif qualified_name.startswith("weave.decorator_class"): raise ValueError("Decorator classes are not supported.") elif qualified_name.startswith("weave.decorator_type"): @@ -201,7 +201,7 @@ def _node_to_code(node: graph.Node, wrap_const_node: bool = True) -> str: if current_frame is not None and node.name in current_frame: return node.name - return f"weave.legacy.weave_internal.make_var_node({_type_to_code(node.type)}, '{node.name}')" + return f"weave.legacy.weave.weave_internal.make_var_node({_type_to_code(node.type)}, '{node.name}')" elif isinstance(node, graph.ConstNode): if isinstance(node.type, weave_types.Function): vars = list(node.type.input_types.keys()) @@ -209,7 +209,7 @@ def _node_to_code(node: graph.Node, wrap_const_node: bool = True) -> str: else: val_as_code = object_to_code_no_format(node.val) if wrap_const_node: - return f"weave.legacy.weave_internal.const({val_as_code})" + return f"weave.legacy.weave.weave_internal.const({val_as_code})" else: return val_as_code elif isinstance(node, graph.OutputNode): @@ -226,7 +226,7 @@ def _node_to_code(node: graph.Node, wrap_const_node: bool = True) -> str: ) if len(node.from_op.inputs) > 0: args += "," - return f"weave.legacy.ops_primitives.dict.dict_({args})" + return f"weave.legacy.weave.ops_primitives.dict.dict_({args})" elif node.from_op.name == "list": args = ",".join( [ @@ -236,7 +236,7 @@ def _node_to_code(node: graph.Node, wrap_const_node: bool = True) -> str: ) if len(node.from_op.inputs) > 0: args += "," - return f"weave.legacy.ops_primitives.list_.make_list({args})" + return f"weave.legacy.weave.ops_primitives.list_.make_list({args})" is_root = len(inputs) == 0 or not isinstance( inputs[0], (graph.OutputNode, graph.VarNode) diff --git a/weave/legacy/compile.py b/weave/legacy/weave/compile.py similarity index 99% rename from weave/legacy/compile.py rename to weave/legacy/weave/compile.py index e55d66daf9b..318e980ee39 100644 --- a/weave/legacy/compile.py +++ b/weave/legacy/weave/compile.py @@ -5,8 +5,8 @@ import re import typing -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( weave_internal, errors, engine_trace, @@ -28,8 +28,8 @@ stitch, value_or_error, ) -from weave.legacy.language_features.tagging import tagged_value_type_helpers -from weave.legacy.op_def import OpDef +from weave.legacy.weave.language_features.tagging import tagged_value_type_helpers +from weave.legacy.weave.op_def import OpDef # These call_* functions must match the actual op implementations. # But we don't want to import the op definitions themselves here, since @@ -41,7 +41,7 @@ def _dispatch_error_is_client_error( op_name: str, input_types: dict[str, types.Type] ) -> bool: - from weave.legacy.ops_domain import wbmedia + from weave.legacy.weave.ops_domain import wbmedia if op_name in set( ( @@ -268,7 +268,7 @@ def _simple_optimizations(node: graph.Node) -> typing.Optional[graph.Node]: node.type, "ArrowWeaveListTypedDict-columnNames", {"self": awl_node} ) elif isinstance(node, graph.OutputNode) and node.from_op.name == "flatten": - from weave.legacy.arrow.arrow import ArrowWeaveListType + from weave.legacy.weave.arrow.arrow import ArrowWeaveListType from .ops_arrow.list_ops import _concat_output_type @@ -284,7 +284,7 @@ def _simple_optimizations(node: graph.Node) -> typing.Optional[graph.Node]: {"arr": arr_node}, ) elif isinstance(node, graph.OutputNode) and node.from_op.name == "concat": - from weave.legacy.arrow.arrow import ArrowWeaveListType + from weave.legacy.weave.arrow.arrow import ArrowWeaveListType from .ops_arrow.list_ops import flatten_return_type @@ -487,7 +487,7 @@ def compile_dedupe( nodes: dict[str, graph.Node] = {} def _dedupe(node: graph.Node) -> graph.Node: - from weave.legacy import serialize + from weave.legacy.weave import serialize node_id = serialize.node_id(node) if node_id in nodes: diff --git a/weave/legacy/compile_domain.py b/weave/legacy/weave/compile_domain.py similarity index 97% rename from weave/legacy/compile_domain.py rename to weave/legacy/weave/compile_domain.py index 65730e05ab3..fa002b254c3 100644 --- a/weave/legacy/compile_domain.py +++ b/weave/legacy/weave/compile_domain.py @@ -2,13 +2,13 @@ import graphql -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import gql_op_plugin, gql_to_weave, graph, op_args, stitch, registry_mem -from weave.legacy.input_provider import InputAndStitchProvider +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import gql_op_plugin, gql_to_weave, graph, op_args, stitch, registry_mem +from weave.legacy.weave.input_provider import InputAndStitchProvider if typing.TYPE_CHECKING: - from weave.legacy import op_def + from weave.legacy.weave import op_def def fragment_to_query(fragment: str) -> str: diff --git a/weave/legacy/compile_table.py b/weave/legacy/weave/compile_table.py similarity index 92% rename from weave/legacy/compile_table.py rename to weave/legacy/weave/compile_table.py index f0efcfc85ec..bbf9f868b49 100644 --- a/weave/legacy/compile_table.py +++ b/weave/legacy/weave/compile_table.py @@ -2,8 +2,8 @@ import typing -from weave.legacy import errors -from weave.legacy import stitch +from weave.legacy.weave import errors +from weave.legacy.weave import stitch KeyTree = typing.Dict[str, "KeyTree"] # type:ignore @@ -20,7 +20,7 @@ def tree_merge(a: KeyTree, b: KeyTree) -> None: def get_projection(obj: stitch.ObjectRecorder) -> KeyTree: """Given an object returned by stitch, return a tree of all accessed columns.""" - from weave.legacy import _dict_utils + from weave.legacy.weave import _dict_utils cols: KeyTree = {} all_keys = False diff --git a/weave/legacy/context.py b/weave/legacy/weave/context.py similarity index 88% rename from weave/legacy/context.py rename to weave/legacy/weave/context.py index ab01ec33727..c45c255f83b 100644 --- a/weave/legacy/context.py +++ b/weave/legacy/weave/context.py @@ -4,15 +4,15 @@ import typing from urllib.parse import urlparse -from weave.legacy import util -from weave.legacy import urls, client, context_state -from weave.legacy.client_interface import ClientInterface +from weave.legacy.weave import util +from weave.legacy.weave import urls, client, context_state +from weave.legacy.weave.client_interface import ClientInterface @contextlib.contextmanager def execution_client(): """Returns a client for use by the execution engine and op resolvers.""" - from weave.legacy import server + from weave.legacy.weave import server # Force in process execution with context_state.client(client.NonCachingClient(server.InProcessServer())): @@ -22,7 +22,7 @@ def execution_client(): @contextlib.contextmanager def local_http_client(): - from weave.legacy import server + from weave.legacy.weave import server s = server.HttpServer() s.start() @@ -34,7 +34,7 @@ def local_http_client(): @contextlib.contextmanager def weavejs_client(): - from weave.legacy import server + from weave.legacy.weave import server s = server.HttpServer() s.start() @@ -48,7 +48,7 @@ def use_fixed_server_port(): # s = server.HttpServer(port=9994) # s.start() # _weave_client.set(server.HttpServerClient(s.url)) - from weave.legacy import server + from weave.legacy.weave import server context_state.set_client(server.HttpServerClient("http://localhost:9994")) @@ -70,7 +70,7 @@ def use_lazy_execution(): def _make_default_client(): - from weave.legacy import server + from weave.legacy.weave import server if util.is_notebook(): serv = context_state.get_server() @@ -105,7 +105,7 @@ def get_client() -> typing.Optional[ClientInterface]: def get_frontend_url(): - from weave.legacy import server + from weave.legacy.weave import server url = os.environ.get("WEAVE_FRONTEND_URL", context_state.get_frontend_url()) if url is None: diff --git a/weave/legacy/context_state.py b/weave/legacy/weave/context_state.py similarity index 98% rename from weave/legacy/context_state.py rename to weave/legacy/weave/context_state.py index 15741b77987..4aef897ed5d 100644 --- a/weave/legacy/context_state.py +++ b/weave/legacy/weave/context_state.py @@ -3,11 +3,11 @@ import dataclasses import typing -from weave.legacy import server_interface +from weave.legacy.weave import server_interface if typing.TYPE_CHECKING: - from weave.legacy import client_interface, uris - from weave.legacy.graph_client import GraphClient + from weave.legacy.weave import client_interface, uris + from weave.legacy.weave.graph_client import GraphClient # colab currently runs ipykernel < 6.0. This resets context on every diff --git a/weave/legacy/weave/core_types/__init__.py b/weave/legacy/weave/core_types/__init__.py new file mode 100644 index 00000000000..0cee69ecdca --- /dev/null +++ b/weave/legacy/weave/core_types/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.core_types.stream_table_type import StreamTableType diff --git a/weave/legacy/core_types/stream_table_type.py b/weave/legacy/weave/core_types/stream_table_type.py similarity index 78% rename from weave/legacy/core_types/stream_table_type.py rename to weave/legacy/weave/core_types/stream_table_type.py index c8c3eb7802b..40a2250511e 100644 --- a/weave/legacy/core_types/stream_table_type.py +++ b/weave/legacy/weave/core_types/stream_table_type.py @@ -1,4 +1,4 @@ -from weave.legacy import decorator_type +from weave.legacy.weave import decorator_type @decorator_type.type( diff --git a/weave/legacy/debug.py b/weave/legacy/weave/debug.py similarity index 100% rename from weave/legacy/debug.py rename to weave/legacy/weave/debug.py diff --git a/weave/legacy/debug_compile.py b/weave/legacy/weave/debug_compile.py similarity index 97% rename from weave/legacy/debug_compile.py rename to weave/legacy/weave/debug_compile.py index 1f3ebcf0024..c5d75e8ff77 100644 --- a/weave/legacy/debug_compile.py +++ b/weave/legacy/weave/debug_compile.py @@ -1,5 +1,5 @@ from weave import types -from weave.legacy import graph +from weave.legacy.weave import graph from . import debug_types diff --git a/weave/legacy/debug_types.py b/weave/legacy/weave/debug_types.py similarity index 97% rename from weave/legacy/debug_types.py rename to weave/legacy/weave/debug_types.py index c2a77b2c0cf..4449bfef71c 100644 --- a/weave/legacy/debug_types.py +++ b/weave/legacy/weave/debug_types.py @@ -1,7 +1,7 @@ import textwrap -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.weave_types import * +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.weave_types import * def short_type(type: Type) -> str: diff --git a/weave/legacy/decorator_arrow_op.py b/weave/legacy/weave/decorator_arrow_op.py similarity index 96% rename from weave/legacy/decorator_arrow_op.py rename to weave/legacy/weave/decorator_arrow_op.py index 51869d03760..0b9ba138c69 100644 --- a/weave/legacy/decorator_arrow_op.py +++ b/weave/legacy/weave/decorator_arrow_op.py @@ -1,9 +1,9 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import op_def -from weave.legacy.arrow.arrow import ArrowWeaveListType -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import op_def +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType +from weave.legacy.weave.language_features.tagging import tagged_value_type from .decorator_op import op diff --git a/weave/legacy/decorator_class.py b/weave/legacy/weave/decorator_class.py similarity index 93% rename from weave/legacy/decorator_class.py rename to weave/legacy/weave/decorator_class.py index c2b2d5314d2..7f6d82f2728 100644 --- a/weave/legacy/decorator_class.py +++ b/weave/legacy/weave/decorator_class.py @@ -1,9 +1,9 @@ import inspect import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import context_state, derive_op, op_def, registry_mem +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import context_state, derive_op, op_def, registry_mem # Contrary to the way it is read, the weave.class() decorator runs AFTER the # inner methods are defined. Therefore, this function runs after the ops are diff --git a/weave/legacy/decorator_mutation.py b/weave/legacy/weave/decorator_mutation.py similarity index 100% rename from weave/legacy/decorator_mutation.py rename to weave/legacy/weave/decorator_mutation.py diff --git a/weave/legacy/decorator_op.py b/weave/legacy/weave/decorator_op.py similarity index 93% rename from weave/legacy/decorator_op.py rename to weave/legacy/weave/decorator_op.py index d0081d8494a..e7a156614d6 100644 --- a/weave/legacy/decorator_op.py +++ b/weave/legacy/weave/decorator_op.py @@ -4,11 +4,11 @@ from typing_extensions import ParamSpec -from weave.legacy import weave_types as types -from weave.legacy import context_state, derive_op, op_args, op_def, pyfunc_type_util, registry_mem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import context_state, derive_op, op_args, op_def, pyfunc_type_util, registry_mem if typing.TYPE_CHECKING: - from weave.legacy.gql_op_plugin import GqlOpPlugin + from weave.legacy.weave.gql_op_plugin import GqlOpPlugin # Important usability note @@ -104,7 +104,7 @@ def wrap(f: Callable[P, R]) -> Callable[P, R]: mutation=mutation, ) if weavify: - from weave.legacy.weavify import op_to_weave_fn + from weave.legacy.weave.weavify import op_to_weave_fn op.weave_fn = op_to_weave_fn(op) diff --git a/weave/legacy/decorator_type.py b/weave/legacy/weave/decorator_type.py similarity index 95% rename from weave/legacy/decorator_type.py rename to weave/legacy/weave/decorator_type.py index 2b761f2838f..1e437a30c2c 100644 --- a/weave/legacy/decorator_type.py +++ b/weave/legacy/weave/decorator_type.py @@ -2,9 +2,9 @@ import inspect import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import context_state, decorator_class, object_type_ref_util, infer_types +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import context_state, decorator_class, object_type_ref_util, infer_types _py_type = type @@ -93,7 +93,7 @@ def wrap(target): # and deserialize them along with the data attached to the object if relocatable: for name, member in inspect.getmembers(target): - from weave.legacy import op_def, op_def_type + from weave.legacy.weave import op_def, op_def_type if isinstance(member, op_def.BoundOpDef): static_property_types[name] = op_def_type.OpDefType() diff --git a/weave/legacy/decorator_type.pyi b/weave/legacy/weave/decorator_type.pyi similarity index 100% rename from weave/legacy/decorator_type.pyi rename to weave/legacy/weave/decorator_type.pyi diff --git a/weave/legacy/decorators.py b/weave/legacy/weave/decorators.py similarity index 100% rename from weave/legacy/decorators.py rename to weave/legacy/weave/decorators.py diff --git a/weave/legacy/derive_op.py b/weave/legacy/weave/derive_op.py similarity index 98% rename from weave/legacy/derive_op.py rename to weave/legacy/weave/derive_op.py index 797d16af9fa..05de8dc9309 100644 --- a/weave/legacy/derive_op.py +++ b/weave/legacy/weave/derive_op.py @@ -6,8 +6,8 @@ import inspect import typing -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( storage, weave_internal, errors, @@ -21,7 +21,7 @@ op_def, op_policy, ) -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave.language_features.tagging import tag_store USE_PARALLEL_DOWNLOAD = True USE_PARALLEL_REFINE = True @@ -195,7 +195,7 @@ def make_output_type(input_types): raise errors.WeaveMakeFunctionError( "output_type function must return a node." ) - from weave.legacy.ops_primitives.list_ import make_list + from weave.legacy.weave.ops_primitives.list_ import make_list return types.List.make( { diff --git a/weave/legacy/dispatch.py b/weave/legacy/weave/dispatch.py similarity index 98% rename from weave/legacy/dispatch.py rename to weave/legacy/weave/dispatch.py index 39baffd75a8..3017187613b 100644 --- a/weave/legacy/dispatch.py +++ b/weave/legacy/weave/dispatch.py @@ -5,11 +5,11 @@ import typing from dataclasses import dataclass -from weave.legacy import util -from weave.legacy import weave_types as types -from weave.legacy import graph, memo, op_args, op_def, pyfunc_type_util, registry_mem, errors -from weave.legacy.language_features.tagging.is_tag_getter import is_tag_getter -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph, memo, op_args, op_def, pyfunc_type_util, registry_mem, errors +from weave.legacy.weave.language_features.tagging.is_tag_getter import is_tag_getter +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType # I originally wrote this thinking that we could always choose the more specific diff --git a/weave/legacy/eager.py b/weave/legacy/weave/eager.py similarity index 93% rename from weave/legacy/eager.py rename to weave/legacy/weave/eager.py index 7b1b1935ffc..393e23ad23f 100644 --- a/weave/legacy/eager.py +++ b/weave/legacy/weave/eager.py @@ -1,9 +1,9 @@ import typing -from weave.legacy import context_state, graph +from weave.legacy.weave import context_state, graph -from weave.legacy import weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types WeaveIterObjectType = typing.TypeVar("WeaveIterObjectType") @@ -81,7 +81,7 @@ def __iter__(self) -> typing.Iterator[WeaveIterObjectType]: def select_all(node: graph.Node) -> graph.Node: if not types.TypedDict().assign_type(node.type): raise ValueError("only TypedDict supported for now") - from weave.legacy.ops_primitives import dict_ + from weave.legacy.weave.ops_primitives import dict_ node_type = typing.cast(types.TypedDict, node.type) return dict_(**{k: node[k] for k in node_type.property_types}) # type: ignore diff --git a/weave/legacy/weave/ecosystem/__init__.py b/weave/legacy/weave/ecosystem/__init__.py new file mode 100644 index 00000000000..2ba3a1f0da9 --- /dev/null +++ b/weave/legacy/weave/ecosystem/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.root import * diff --git a/weave/legacy/ecosystem/all.py b/weave/legacy/weave/ecosystem/all.py similarity index 90% rename from weave/legacy/ecosystem/all.py rename to weave/legacy/weave/ecosystem/all.py index 0fd564f8d46..e1e5c1d39f6 100644 --- a/weave/legacy/ecosystem/all.py +++ b/weave/legacy/weave/ecosystem/all.py @@ -4,7 +4,7 @@ import typing from collections import Counter, defaultdict -from weave.legacy import context_state +from weave.legacy.weave import context_state logger = logging.getLogger(__name__) @@ -38,11 +38,11 @@ LOAD_RESULTS: typing.DefaultDict[str, list[str]] = defaultdict(list[str]) try: - logger.info("Loading weave.legacy.ecosystem") + logger.info("Loading weave.legacy.weave.ecosystem") for module in ALL_MODULES: try: globals()[module] = importlib.import_module( - f"weave.legacy.ecosystem.{module}" + f"weave.legacy.weave.ecosystem.{module}" ) LOAD_RESULTS["loaded"].append(module) except ImportError as exc: diff --git a/weave/legacy/weave/ecosystem/bertviz/__init__.py b/weave/legacy/weave/ecosystem/bertviz/__init__.py new file mode 100644 index 00000000000..e220f65bfaf --- /dev/null +++ b/weave/legacy/weave/ecosystem/bertviz/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.bertviz.panels import * diff --git a/weave/legacy/ecosystem/bertviz/panels.py b/weave/legacy/weave/ecosystem/bertviz/panels.py similarity index 83% rename from weave/legacy/ecosystem/bertviz/panels.py rename to weave/legacy/weave/ecosystem/bertviz/panels.py index d4b03c4bf76..36a6a63c0c9 100644 --- a/weave/legacy/ecosystem/bertviz/panels.py +++ b/weave/legacy/weave/ecosystem/bertviz/panels.py @@ -1,11 +1,11 @@ import bertviz import weave -from weave.legacy.ecosystem import huggingface +from weave.legacy.weave.ecosystem import huggingface @weave.op() -def head_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.ops.Html: +def head_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.weave.ops.Html: # All the information we need is attached to the ModelOutputAttention object. # This is important. In Weave, types should "stand alone", meaning they should # contain references to any information that is necessary for their use. @@ -27,7 +27,7 @@ def head_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.ops.H # The .data attribute contains the html string. Wrap it in the weave Html type. # TODO: this would read better as weave.types.Html I think. - return weave.legacy.ops.Html(html.data) + return weave.legacy.weave.ops.Html(html.data) @weave.type() @@ -37,18 +37,18 @@ class BertvizHeadView(weave.Panel): input_node: weave.Node[huggingface.ModelOutputAttention] @weave.op() - def render(self) -> weave.legacy.panels.PanelHtml: + def render(self) -> weave.legacy.weave.panels.PanelHtml: # This is a lazy call! It doesn't execute anything html = head_view(self.input_node) # We add the lazy call as input to the returned Html panel. Nothing has been # computed so far. The UI's Html panel will perform a useNodeValue operation on its # input node. Only then will the head_view function finally be called. - return weave.legacy.panels.PanelHtml(html) + return weave.legacy.weave.panels.PanelHtml(html) @weave.op() -def model_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.ops.Html: +def model_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.weave.ops.Html: # Parallels head_view() to visualize the full matrix of attention heads as rows # and layers as columns for each attention map @@ -61,7 +61,7 @@ def model_view(attention: huggingface.ModelOutputAttention) -> weave.legacy.ops. html = bertviz.model_view(attention._attention, tokens, html_action="return") # TODO: this would read better as weave.types.Html I think. - return weave.legacy.ops.Html(html.data) + return weave.legacy.weave.ops.Html(html.data) @weave.type() @@ -71,6 +71,6 @@ class BertvizModelView(weave.Panel): input_node: weave.Node[huggingface.ModelOutputAttention] @weave.op() - def model_view_panel_render(self) -> weave.legacy.panels.PanelHtml: + def model_view_panel_render(self) -> weave.legacy.weave.panels.PanelHtml: html = model_view(self.input_node) - return weave.legacy.panels.PanelHtml(html) + return weave.legacy.weave.panels.PanelHtml(html) diff --git a/weave/legacy/weave/ecosystem/craiyon/__init__.py b/weave/legacy/weave/ecosystem/craiyon/__init__.py new file mode 100644 index 00000000000..dbcad02cfd5 --- /dev/null +++ b/weave/legacy/weave/ecosystem/craiyon/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.craiyon.ops import * diff --git a/weave/legacy/ecosystem/craiyon/ops.py b/weave/legacy/weave/ecosystem/craiyon/ops.py similarity index 100% rename from weave/legacy/ecosystem/craiyon/ops.py rename to weave/legacy/weave/ecosystem/craiyon/ops.py diff --git a/weave/legacy/ecosystem/hdbscan/__init__.py b/weave/legacy/weave/ecosystem/example/__init__.py similarity index 73% rename from weave/legacy/ecosystem/hdbscan/__init__.py rename to weave/legacy/weave/ecosystem/example/__init__.py index 4481af6f01c..2ae52ad30e0 100644 --- a/weave/legacy/ecosystem/hdbscan/__init__.py +++ b/weave/legacy/weave/ecosystem/example/__init__.py @@ -2,13 +2,13 @@ import logging -from weave.legacy import context_state +from weave.legacy.weave import context_state logging.getLogger("ecosystem_example").setLevel(logging.ERROR) loading_builtins_token = context_state.set_loading_built_ins() try: - from weave.legacy.ecosystem.hdbscan.ops import * + from weave.legacy.weave.ecosystem.example import ops finally: context_state.clear_loading_built_ins(loading_builtins_token) diff --git a/weave/legacy/ecosystem/example/ops.py b/weave/legacy/weave/ecosystem/example/ops.py similarity index 100% rename from weave/legacy/ecosystem/example/ops.py rename to weave/legacy/weave/ecosystem/example/ops.py diff --git a/weave/legacy/ecosystem/umap/__init__.py b/weave/legacy/weave/ecosystem/hdbscan/__init__.py similarity index 73% rename from weave/legacy/ecosystem/umap/__init__.py rename to weave/legacy/weave/ecosystem/hdbscan/__init__.py index 820a584fa5a..62b6fee8188 100644 --- a/weave/legacy/ecosystem/umap/__init__.py +++ b/weave/legacy/weave/ecosystem/hdbscan/__init__.py @@ -2,13 +2,13 @@ import logging -from weave.legacy import context_state +from weave.legacy.weave import context_state logging.getLogger("ecosystem_example").setLevel(logging.ERROR) loading_builtins_token = context_state.set_loading_built_ins() try: - from weave.legacy.ecosystem.umap.ops import * + from weave.legacy.weave.ecosystem.hdbscan.ops import * finally: context_state.clear_loading_built_ins(loading_builtins_token) diff --git a/weave/legacy/ecosystem/hdbscan/ops.py b/weave/legacy/weave/ecosystem/hdbscan/ops.py similarity index 95% rename from weave/legacy/ecosystem/hdbscan/ops.py rename to weave/legacy/weave/ecosystem/hdbscan/ops.py index 5dd0ae5184b..e12213661a9 100644 --- a/weave/legacy/ecosystem/hdbscan/ops.py +++ b/weave/legacy/weave/ecosystem/hdbscan/ops.py @@ -10,7 +10,7 @@ ) import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow class HDBSCANOptions(TypedDict): diff --git a/weave/legacy/weave/ecosystem/huggingface/__init__.py b/weave/legacy/weave/ecosystem/huggingface/__init__.py new file mode 100644 index 00000000000..8e6bc2c2ae7 --- /dev/null +++ b/weave/legacy/weave/ecosystem/huggingface/__init__.py @@ -0,0 +1,12 @@ +from weave.legacy.weave import context_state as _context + +_loading_builtins_token = _context.set_loading_built_ins() + +from weave.legacy.weave.ecosystem.huggingface.hfmodel import ModelOutputAttention +from weave.legacy.weave.ecosystem.huggingface.huggingface_datasets import * +from weave.legacy.weave.ecosystem.huggingface.huggingface_models import * +from weave.legacy.weave.ecosystem.huggingface.model_textclassification import ( + FullTextClassificationPipelineOutput, +) + +_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/huggingface/hfmodel.py b/weave/legacy/weave/ecosystem/huggingface/hfmodel.py similarity index 96% rename from weave/legacy/ecosystem/huggingface/hfmodel.py rename to weave/legacy/weave/ecosystem/huggingface/hfmodel.py index 65debd9f6e6..7a521299a8e 100644 --- a/weave/legacy/ecosystem/huggingface/hfmodel.py +++ b/weave/legacy/weave/ecosystem/huggingface/hfmodel.py @@ -7,8 +7,8 @@ import transformers import weave -from weave.legacy import op_def_type -from weave.legacy.ecosystem import pytorch +from weave.legacy.weave import op_def_type +from weave.legacy.weave.ecosystem import pytorch # This tells Weave how to serialize BaseModelOutput @@ -72,7 +72,7 @@ def tokenizer(self): return transformers.AutoTokenizer.from_pretrained(self._id) @weave.op() - def readme(self) -> weave.legacy.ops.Markdown: + def readme(self) -> weave.legacy.weave.ops.Markdown: readme = huggingface_hub.hf_hub_download(self._id, "README.md") # quick hack: remove the metadata header from the readme readme_contents = "" @@ -85,7 +85,7 @@ def readme(self) -> weave.legacy.ops.Markdown: # if we failed to parse out the header if len(readme_contents) < 1: readme_contents = open(readme).read() - return weave.legacy.ops.Markdown(readme_contents) + return weave.legacy.weave.ops.Markdown(readme_contents) @weave.op() def id(self) -> str: diff --git a/weave/legacy/ecosystem/huggingface/huggingface_datasets.py b/weave/legacy/weave/ecosystem/huggingface/huggingface_datasets.py similarity index 98% rename from weave/legacy/ecosystem/huggingface/huggingface_datasets.py rename to weave/legacy/weave/ecosystem/huggingface/huggingface_datasets.py index d47614eee07..f29bf154d78 100644 --- a/weave/legacy/ecosystem/huggingface/huggingface_datasets.py +++ b/weave/legacy/weave/ecosystem/huggingface/huggingface_datasets.py @@ -41,7 +41,7 @@ def hf_feature_type_to_type(type_): elif isinstance(type_, datasets.features.features.Sequence): return weave.types.List(hf_feature_type_to_type(type_.feature)) elif isinstance(type_, datasets.features.features.Image): - return weave.legacy.ops.PILImageType() + return weave.legacy.weave.ops.PILImageType() elif isinstance(type_, datasets.features.features.ClassLabel): # TODO: this should be a classes type!!!!! return weave.types.Int() diff --git a/weave/legacy/ecosystem/huggingface/huggingface_models.py b/weave/legacy/weave/ecosystem/huggingface/huggingface_models.py similarity index 78% rename from weave/legacy/ecosystem/huggingface/huggingface_models.py rename to weave/legacy/weave/ecosystem/huggingface/huggingface_models.py index f803fd446b9..8fa4865628b 100644 --- a/weave/legacy/ecosystem/huggingface/huggingface_models.py +++ b/weave/legacy/weave/ecosystem/huggingface/huggingface_models.py @@ -7,8 +7,8 @@ import transformers import weave -from weave.legacy.ecosystem import pytorch -from weave.legacy.ecosystem.huggingface import ( +from weave.legacy.weave.ecosystem import pytorch +from weave.legacy.weave.ecosystem.huggingface import ( hfmodel, model_textclassification, model_textgeneration, @@ -40,11 +40,11 @@ class HuggingfaceModelsPanel(weave.Panel): input_node: weave.Node[list[hfmodel.HFModel]] @weave.op() - def render(self) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( + def render(self) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( self.input_node, columns=[ - lambda model_row: weave.legacy.panels.WeaveLink( + lambda model_row: weave.legacy.weave.panels.WeaveLink( model_row.id(), to=lambda input: huggingface().model(input), # type: ignore ), @@ -64,24 +64,24 @@ class HuggingfaceModelPanel(weave.Panel): input_node: weave.Node[hfmodel.HFModel] @weave.op(pure=False) - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: model = typing.cast(hfmodel.HFModel, self.input_node) - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=model.id(), subtitle="HuggingFace Hub Model", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Model Card", - content=weave.legacy.panels.PanelMarkdown(model.readme()), # type: ignore + content=weave.legacy.weave.panels.PanelMarkdown(model.readme()), # type: ignore ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Metadata", - content=weave.legacy.panels.Group( + content=weave.legacy.weave.panels.Group( items={ - "id": weave.legacy.panels.LabeledItem( + "id": weave.legacy.weave.panels.LabeledItem( item=model.id(), label="ID" ), - "pipeline_tag": weave.legacy.panels.LabeledItem( + "pipeline_tag": weave.legacy.weave.panels.LabeledItem( item=model.pipeline_tag(), label="Pipeline tag" ), } @@ -89,10 +89,10 @@ def render(self) -> weave.legacy.panels.Card: ), # Broke in panel refactor. Don't have concrete op name available here so # can't get the right type for the output. - # weave.legacy.panels.CardTab( + # weave.legacy.weave.panels.CardTab( # name="Inference Logs", - # content=weave.legacy.panels.Table( - # weave.legacy.ops.used_by(model, model.call.op_name()), + # content=weave.legacy.weave.panels.Table( + # weave.legacy.weave.ops.used_by(model, model.call.op_name()), # columns=[ # lambda run: run.output.model_input, # lambda run: run.output.model_output[0]["generated_text"], @@ -141,12 +141,12 @@ class HuggingfacePackagePanel(weave.Panel): input_node: weave.Node[HuggingFacePackage] @weave.op() - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: pack = typing.cast(HuggingFacePackage, self.input_node) # type: ignore - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title="Huggingface Package", subtitle="Browse Models and Datasets", content=[ - weave.legacy.panels.CardTab(name="Models", content=pack.models()), # type: ignore + weave.legacy.weave.panels.CardTab(name="Models", content=pack.models()), # type: ignore ], ) diff --git a/weave/legacy/ecosystem/huggingface/model_textclassification.py b/weave/legacy/weave/ecosystem/huggingface/model_textclassification.py similarity index 89% rename from weave/legacy/ecosystem/huggingface/model_textclassification.py rename to weave/legacy/weave/ecosystem/huggingface/model_textclassification.py index 40a7a496109..4101b36849e 100644 --- a/weave/legacy/ecosystem/huggingface/model_textclassification.py +++ b/weave/legacy/weave/ecosystem/huggingface/model_textclassification.py @@ -4,7 +4,7 @@ import transformers import weave -from weave.legacy.ecosystem.huggingface import hfmodel +from weave.legacy.weave.ecosystem.huggingface import hfmodel # We have to forward-declare the Weave types to avoid circular reference # issues that weave.type() can't resolve yet. @@ -70,13 +70,13 @@ class ClassificationResultPanel(weave.Panel): input_node: weave.Node[list[ClassificationResult]] @weave.op() - def render(self) -> weave.legacy.panels.Table: - from weave.legacy.ecosystem.huggingface.huggingface_models import huggingface + def render(self) -> weave.legacy.weave.panels.Table: + from weave.legacy.weave.ecosystem.huggingface.huggingface_models import huggingface - return weave.legacy.panels.Table( + return weave.legacy.weave.panels.Table( self.input_node, columns=[ - lambda result_row: weave.legacy.panels.WeaveLink( + lambda result_row: weave.legacy.weave.panels.WeaveLink( result_row.model_name(), to=lambda input: huggingface().model(input), # type: ignore ), @@ -115,17 +115,17 @@ class FullTextClassificationResultPanel(weave.Panel): input_node: weave.Node[FullTextClassificationPipelineOutput] @weave.op() - def render(self) -> weave.legacy.panels.Group: + def render(self) -> weave.legacy.weave.panels.Group: output = typing.cast(FullTextClassificationPipelineOutput, self.input_node) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( preferHorizontal=True, items={ - "input": weave.legacy.panels.LabeledItem( + "input": weave.legacy.weave.panels.LabeledItem( label="input", item=output.model_input ), - "output": weave.legacy.panels.LabeledItem( + "output": weave.legacy.weave.panels.LabeledItem( label="output", - item=weave.legacy.panels.Plot( + item=weave.legacy.weave.panels.Plot( input_node=typing.cast(weave.Node, output.model_output), x=lambda class_score: class_score["score"], y=lambda class_score: class_score["label"], diff --git a/weave/legacy/ecosystem/huggingface/model_textgeneration.py b/weave/legacy/weave/ecosystem/huggingface/model_textgeneration.py similarity index 91% rename from weave/legacy/ecosystem/huggingface/model_textgeneration.py rename to weave/legacy/weave/ecosystem/huggingface/model_textgeneration.py index edbaa739cf2..a422e88f1c2 100644 --- a/weave/legacy/ecosystem/huggingface/model_textgeneration.py +++ b/weave/legacy/weave/ecosystem/huggingface/model_textgeneration.py @@ -4,7 +4,7 @@ import transformers import weave -from weave.legacy.ecosystem.huggingface import hfmodel +from weave.legacy.weave.ecosystem.huggingface import hfmodel # We have to forward-declare the Weave types to avoid circular reference # issues that weave.type() can't resolve yet. @@ -50,15 +50,15 @@ class FullTextGenerationPanel(weave.Panel): input_node: weave.Node[FullTextGenerationPipelineOutput] @weave.op() - def render(self) -> weave.legacy.panels.Group: + def render(self) -> weave.legacy.weave.panels.Group: output = typing.cast(FullTextGenerationPipelineOutput, self.input_node) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( preferHorizontal=True, items={ - "input": weave.legacy.panels.LabeledItem( + "input": weave.legacy.weave.panels.LabeledItem( label="input", item=output.model_input ), - "output": weave.legacy.panels.LabeledItem( + "output": weave.legacy.weave.panels.LabeledItem( label="output", item=output.model_output ), }, diff --git a/weave/legacy/ecosystem/spacy/__init__.py b/weave/legacy/weave/ecosystem/keras/__init__.py similarity index 51% rename from weave/legacy/ecosystem/spacy/__init__.py rename to weave/legacy/weave/ecosystem/keras/__init__.py index c0b0f5af429..c27c01c5e63 100644 --- a/weave/legacy/ecosystem/spacy/__init__.py +++ b/weave/legacy/weave/ecosystem/keras/__init__.py @@ -1,7 +1,6 @@ -from weave.legacy import context_state as _context +from weave.legacy.weave import context_state as _context _loading_builtins_token = _context.set_loading_built_ins() - -from weave.legacy.ecosystem.spacy.spacy import * +from weave.legacy.weave.ecosystem.keras.model import * _context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/keras/model.py b/weave/legacy/weave/ecosystem/keras/model.py similarity index 100% rename from weave/legacy/ecosystem/keras/model.py rename to weave/legacy/weave/ecosystem/keras/model.py diff --git a/weave/legacy/ecosystem/langchain/__init__.py b/weave/legacy/weave/ecosystem/langchain/__init__.py similarity index 67% rename from weave/legacy/ecosystem/langchain/__init__.py rename to weave/legacy/weave/ecosystem/langchain/__init__.py index 49a02f9cac1..104095d21f5 100644 --- a/weave/legacy/ecosystem/langchain/__init__.py +++ b/weave/legacy/weave/ecosystem/langchain/__init__.py @@ -1,12 +1,12 @@ import logging -from weave.legacy import context_state +from weave.legacy.weave import context_state logging.getLogger("langchain").setLevel(logging.ERROR) loading_builtins_token = context_state.set_loading_built_ins() try: - from weave.legacy.ecosystem.langchain.lc import * + from weave.legacy.weave.ecosystem.langchain.lc import * finally: context_state.clear_loading_built_ins(loading_builtins_token) diff --git a/weave/legacy/ecosystem/langchain/lc.py b/weave/legacy/weave/ecosystem/langchain/lc.py similarity index 98% rename from weave/legacy/ecosystem/langchain/lc.py rename to weave/legacy/weave/ecosystem/langchain/lc.py index 46fd02f6a65..51fc011c7fb 100644 --- a/weave/legacy/ecosystem/langchain/lc.py +++ b/weave/legacy/weave/ecosystem/langchain/lc.py @@ -41,12 +41,12 @@ from langchain.vectorstores.base import VectorStoreRetriever import weave -from weave.legacy import storage -from weave.legacy import artifact_base -from weave.legacy.artifact_fs import FilesystemArtifact -from weave.legacy.ecosystem.langchain import util -from weave.legacy.ops_domain import trace_tree -from weave.legacy.weave_types import Type +from weave.legacy.weave import storage +from weave.legacy.weave import artifact_base +from weave.legacy.weave.artifact_fs import FilesystemArtifact +from weave.legacy.weave.ecosystem.langchain import util +from weave.legacy.weave.ops_domain import trace_tree +from weave.legacy.weave.weave_types import Type class WeaveTracer(BaseTracer): diff --git a/weave/legacy/ecosystem/langchain/util.py b/weave/legacy/weave/ecosystem/langchain/util.py similarity index 99% rename from weave/legacy/ecosystem/langchain/util.py rename to weave/legacy/weave/ecosystem/langchain/util.py index 4341c64995f..b25b592f398 100644 --- a/weave/legacy/ecosystem/langchain/util.py +++ b/weave/legacy/weave/ecosystem/langchain/util.py @@ -13,7 +13,7 @@ from langchain.callbacks.tracers.schemas import Run, RunTypeEnum -from weave.legacy.ops_domain import trace_tree +from weave.legacy.weave.ops_domain import trace_tree if TYPE_CHECKING: from langchain.chains.base import Chain diff --git a/weave/legacy/weave/ecosystem/lens/__init__.py b/weave/legacy/weave/ecosystem/lens/__init__.py new file mode 100644 index 00000000000..4c04886f8c2 --- /dev/null +++ b/weave/legacy/weave/ecosystem/lens/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.lens.lens import * diff --git a/weave/legacy/ecosystem/lens/lens.py b/weave/legacy/weave/ecosystem/lens/lens.py similarity index 100% rename from weave/legacy/ecosystem/lens/lens.py rename to weave/legacy/weave/ecosystem/lens/lens.py diff --git a/weave/legacy/weave/ecosystem/py/__init__.py b/weave/legacy/weave/ecosystem/py/__init__.py new file mode 100644 index 00000000000..e535e18a474 --- /dev/null +++ b/weave/legacy/weave/ecosystem/py/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.py import pydoc diff --git a/weave/legacy/ecosystem/py/pydoc.py b/weave/legacy/weave/ecosystem/py/pydoc.py similarity index 76% rename from weave/legacy/ecosystem/py/pydoc.py rename to weave/legacy/weave/ecosystem/py/pydoc.py index 946b877cb60..e558f42a5e5 100644 --- a/weave/legacy/ecosystem/py/pydoc.py +++ b/weave/legacy/weave/ecosystem/py/pydoc.py @@ -51,8 +51,8 @@ def module_name(module: types.ModuleType) -> str: @weave.op() -def module_doc(module: types.ModuleType) -> weave.legacy.ops.Markdown: - return weave.legacy.ops.Markdown(module.__doc__ or "") +def module_doc(module: types.ModuleType) -> weave.legacy.weave.ops.Markdown: + return weave.legacy.weave.ops.Markdown(module.__doc__ or "") @weave.op() @@ -93,8 +93,8 @@ def pyclass_module(pyclass: type) -> types.ModuleType: @weave.op() -def pyclass_doc(pyclass: type) -> weave.legacy.ops.Markdown: - return weave.legacy.ops.Markdown(pyclass.__doc__ or "") +def pyclass_doc(pyclass: type) -> weave.legacy.weave.ops.Markdown: + return weave.legacy.weave.ops.Markdown(pyclass.__doc__ or "") @weave.op() @@ -126,8 +126,8 @@ def function_name(pyfunction: types.FunctionType) -> str: @weave.op() -def function_doc(pyfunction: types.FunctionType) -> weave.legacy.ops.Markdown: - return weave.legacy.ops.Markdown(pyfunction.__doc__ or "") +def function_doc(pyfunction: types.FunctionType) -> weave.legacy.weave.ops.Markdown: + return weave.legacy.weave.ops.Markdown(pyfunction.__doc__ or "") @weave.type() @@ -136,34 +136,34 @@ class ModulePanel(weave.Panel): input_node: weave.Node[types.ModuleType] @weave.op() - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: module = self.input_node - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=module.module_name(), # type: ignore subtitle="python module", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Description", - content=weave.legacy.panels.PanelMarkdown(module.module_doc()), # type: ignore + content=weave.legacy.weave.panels.PanelMarkdown(module.module_doc()), # type: ignore ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Classes", - content=weave.legacy.panels.Table( + content=weave.legacy.weave.panels.Table( module.module_classes(), # type: ignore columns=[ - lambda c: weave.legacy.panels.WeaveLink( + lambda c: weave.legacy.weave.panels.WeaveLink( c.class_name(), to=lambda inp: module.module_class(inp), # type: ignore ) ], ), ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Functions", - content=weave.legacy.panels.Table( + content=weave.legacy.weave.panels.Table( module.module_functions(), # type: ignore columns=[ - lambda c: weave.legacy.panels.WeaveLink( + lambda c: weave.legacy.weave.panels.WeaveLink( c.function_name(), to=lambda inp: module.module_function(inp), # type: ignore ) @@ -180,22 +180,22 @@ class ClassPanel(weave.Panel): input_node: weave.Node[type] @weave.op() - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: cls = self.input_node - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=cls.class_name(), # type: ignore subtitle="python class", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Description", - content=weave.legacy.panels.PanelMarkdown(cls.pyclass_doc()), # type: ignore + content=weave.legacy.weave.panels.PanelMarkdown(cls.pyclass_doc()), # type: ignore ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Methods", - content=weave.legacy.panels.Table( + content=weave.legacy.weave.panels.Table( cls.class_methods(), # type: ignore columns=[ - lambda m: weave.legacy.panels.WeaveLink( + lambda m: weave.legacy.weave.panels.WeaveLink( m.function_name(), to=lambda inp: cls.class_method(inp), # type: ignore ) @@ -212,15 +212,15 @@ class FunctionPanel(weave.Panel): input_node: weave.Node[types.FunctionType] @weave.op() - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: func = self.input_node - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=func.function_name(), # type: ignore subtitle="python function", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Description", - content=weave.legacy.panels.PanelMarkdown(func.function_doc()), # type: ignore + content=weave.legacy.weave.panels.PanelMarkdown(func.function_doc()), # type: ignore ), ], ) diff --git a/weave/legacy/weave/ecosystem/pytorch/__init__.py b/weave/legacy/weave/ecosystem/pytorch/__init__.py new file mode 100644 index 00000000000..3b5a12f7690 --- /dev/null +++ b/weave/legacy/weave/ecosystem/pytorch/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.pytorch.model import * diff --git a/weave/legacy/ecosystem/pytorch/model.py b/weave/legacy/weave/ecosystem/pytorch/model.py similarity index 100% rename from weave/legacy/ecosystem/pytorch/model.py rename to weave/legacy/weave/ecosystem/pytorch/model.py diff --git a/weave/legacy/ecosystem/replicate/__init__.py b/weave/legacy/weave/ecosystem/replicate/__init__.py similarity index 57% rename from weave/legacy/ecosystem/replicate/__init__.py rename to weave/legacy/weave/ecosystem/replicate/__init__.py index e859a88c467..f957e5b171a 100644 --- a/weave/legacy/ecosystem/replicate/__init__.py +++ b/weave/legacy/weave/ecosystem/replicate/__init__.py @@ -1,8 +1,8 @@ -from weave.legacy import context_state +from weave.legacy.weave import context_state loading_builtins_token = context_state.set_loading_built_ins() try: - from weave.legacy.ecosystem.replicate.rep import * + from weave.legacy.weave.ecosystem.replicate.rep import * finally: context_state.clear_loading_built_ins(loading_builtins_token) diff --git a/weave/legacy/ecosystem/replicate/rep.py b/weave/legacy/weave/ecosystem/replicate/rep.py similarity index 100% rename from weave/legacy/ecosystem/replicate/rep.py rename to weave/legacy/weave/ecosystem/replicate/rep.py diff --git a/weave/legacy/ecosystem/root.py b/weave/legacy/weave/ecosystem/root.py similarity index 96% rename from weave/legacy/ecosystem/root.py rename to weave/legacy/weave/ecosystem/root.py index 6e67b1e8413..38fac4b03d4 100644 --- a/weave/legacy/ecosystem/root.py +++ b/weave/legacy/weave/ecosystem/root.py @@ -1,9 +1,9 @@ import typing -from weave.legacy import api as weave +from weave.legacy.weave import api as weave # TODO: Fix, these should be available from weave -from weave.legacy import context_state, op_def, ops, panel, panels +from weave.legacy.weave import context_state, op_def, ops, panel, panels loading_builtins_token = context_state.set_loading_built_ins() @@ -22,7 +22,7 @@ """ ) -from weave.legacy import registry_mem +from weave.legacy.weave import registry_mem op_org_name = registry_mem.memory_registry.get_op("user-name") @@ -63,7 +63,7 @@ def ops(self) -> list[op_def.OpDef]: # objects. @weave.op(name="op-ecosystem", render_info={"type": "function"}) def ecosystem() -> Ecosystem: - from weave.legacy import registry_mem + from weave.legacy.weave import registry_mem return Ecosystem( _orgs=[], diff --git a/weave/legacy/ecosystem/scenario/__init__.py b/weave/legacy/weave/ecosystem/scenario/__init__.py similarity index 79% rename from weave/legacy/ecosystem/scenario/__init__.py rename to weave/legacy/weave/ecosystem/scenario/__init__.py index f011e37a728..d747eae19c6 100644 --- a/weave/legacy/ecosystem/scenario/__init__.py +++ b/weave/legacy/weave/ecosystem/scenario/__init__.py @@ -1,8 +1,8 @@ import typing import weave -import weave.legacy -from weave.legacy import val_const +import weave.legacy.weave +from weave.legacy.weave import val_const class ScenarioResult(typing.TypedDict): @@ -26,14 +26,14 @@ class MetricsBankPanel(weave.Panel): input_node: weave.Node[MetricsBankInput] @weave.op() - def render(self) -> weave.legacy.panels.Each: + def render(self) -> weave.legacy.weave.panels.Each: input = typing.cast(MetricsBankInput, self.input_node) baseline = input["baseline"] candidate = input["candidate"] - joined = weave.legacy.ops.join_all( - weave.legacy.ops.make_list(l0=baseline, l1=candidate), + joined = weave.legacy.weave.ops.join_all( + weave.legacy.weave.ops.make_list(l0=baseline, l1=candidate), lambda row: row["scenario_id"], False, ) @@ -42,17 +42,17 @@ def render(self) -> weave.legacy.panels.Each: joined_keys = joined[0].keys() # The output type of difference is List["metric1" | "metric2" | "metric3"] - metrics = weave.legacy.ops.difference( + metrics = weave.legacy.weave.ops.difference( joined_keys, [val_const.const("scenario_id")] ) # TODO: broken - return weave.legacy.panels.Each( + return weave.legacy.weave.panels.Each( metrics, - render=lambda metric_name: weave.legacy.panels.Group( + render=lambda metric_name: weave.legacy.weave.panels.Group( items={ "title": metric_name, - "plot": weave.legacy.panels.Plot( + "plot": weave.legacy.weave.panels.Plot( joined, # TODO: bring this back # title=metric_name, diff --git a/weave/legacy/ecosystem/shap/__init__.py b/weave/legacy/weave/ecosystem/shap/__init__.py similarity index 52% rename from weave/legacy/ecosystem/shap/__init__.py rename to weave/legacy/weave/ecosystem/shap/__init__.py index 02c6c8b5027..a22de778e34 100644 --- a/weave/legacy/ecosystem/shap/__init__.py +++ b/weave/legacy/weave/ecosystem/shap/__init__.py @@ -1,7 +1,7 @@ -from weave.legacy import context_state as _context +from weave.legacy.weave import context_state as _context _loading_builtins_token = _context.set_loading_built_ins() -from weave.legacy.ecosystem.shap.shap import * +from weave.legacy.weave.ecosystem.shap.shap import * _context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/shap/shap.py b/weave/legacy/weave/ecosystem/shap/shap.py similarity index 85% rename from weave/legacy/ecosystem/shap/shap.py rename to weave/legacy/weave/ecosystem/shap/shap.py index 8e364d32f61..812fcaaf918 100644 --- a/weave/legacy/ecosystem/shap/shap.py +++ b/weave/legacy/weave/ecosystem/shap/shap.py @@ -13,8 +13,8 @@ import xgboost import weave -from weave.legacy.ecosystem import huggingface as hf -from weave.legacy.ecosystem import xgboost as weave_xgb +from weave.legacy.weave.ecosystem import huggingface as hf +from weave.legacy.weave.ecosystem import xgboost as weave_xgb @weave.op( @@ -42,7 +42,7 @@ def summary_plot(self): path = "/tmp/shap-%s.png" % random.randrange(0, 1000000) plt.savefig(path) plt.close() - return weave.legacy.ops.LocalFile(path) + return weave.legacy.weave.ops.LocalFile(path) class ShapExplanationType(weave.types.Type): @@ -76,9 +76,9 @@ def shap_explain( @weave.op() -def shap_plot_text(shap_values: shap.Explanation) -> weave.legacy.ops.Html: +def shap_plot_text(shap_values: shap.Explanation) -> weave.legacy.weave.ops.Html: html = shap.plots.text(shap_values, display=False) - return weave.legacy.ops.Html(html) + return weave.legacy.weave.ops.Html(html) @weave.type() @@ -87,5 +87,5 @@ class ShapPlotText(weave.Panel): input_node: weave.Node[shap.Explanation] @weave.op() - def render(self) -> weave.legacy.panels.PanelHtml: - return weave.legacy.panels.PanelHtml(shap_plot_text(self.input_node)) + def render(self) -> weave.legacy.weave.panels.PanelHtml: + return weave.legacy.weave.panels.PanelHtml(shap_plot_text(self.input_node)) diff --git a/weave/legacy/weave/ecosystem/shawn/__init__.py b/weave/legacy/weave/ecosystem/shawn/__init__.py new file mode 100644 index 00000000000..53e96633366 --- /dev/null +++ b/weave/legacy/weave/ecosystem/shawn/__init__.py @@ -0,0 +1,7 @@ +from weave.legacy.weave import context_state as _context + +_loading_builtins_token = _context.set_loading_built_ins() + +from weave.legacy.weave.ecosystem.shawn import petdataset, scratch + +_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/shawn/petdataset.py b/weave/legacy/weave/ecosystem/shawn/petdataset.py similarity index 96% rename from weave/legacy/ecosystem/shawn/petdataset.py rename to weave/legacy/weave/ecosystem/shawn/petdataset.py index fc7becf7e53..9c5b85b66fb 100644 --- a/weave/legacy/ecosystem/shawn/petdataset.py +++ b/weave/legacy/weave/ecosystem/shawn/petdataset.py @@ -71,8 +71,8 @@ class PetDatasetPanel(weave.Panel): input_node: weave.Node[list[OxfordIIITPetDatasetItem]] @weave.op() - def render(self) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( + def render(self) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( self.input_node, columns=[ lambda item: item["id"], diff --git a/weave/legacy/ecosystem/shawn/scratch.py b/weave/legacy/weave/ecosystem/shawn/scratch.py similarity index 69% rename from weave/legacy/ecosystem/shawn/scratch.py rename to weave/legacy/weave/ecosystem/shawn/scratch.py index a200e6ff6b2..01614229fa4 100644 --- a/weave/legacy/ecosystem/shawn/scratch.py +++ b/weave/legacy/weave/ecosystem/shawn/scratch.py @@ -2,17 +2,17 @@ import typing import weave -from weave.legacy import panel, panel_util +from weave.legacy.weave import panel, panel_util @weave.op() def single_distribution( input_node: weave.Node[list[float]], -) -> weave.legacy.panels.Plot: +) -> weave.legacy.weave.panels.Plot: binned = input_node.groupby(lambda v: round(v * 10) / 10).map( # type: ignore - lambda group: weave.legacy.ops.dict_(value=group.key(), count=group.count()) + lambda group: weave.legacy.weave.ops.dict_(value=group.key(), count=group.count()) ) - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( binned, x=lambda row: row["value"], y=lambda row: row["count"], @@ -23,7 +23,7 @@ def single_distribution( @weave.type() class AdderConfig: operand: weave.Node[int] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.ConstNode(weave.types.Int(), 10) + default_factory=lambda: weave.legacy.weave.graph.ConstNode(weave.types.Int(), 10) ) @@ -42,14 +42,14 @@ def adder_default_config(config: typing.Optional[AdderConfig]) -> AdderConfig: @weave.op() def adder_config( input_node: weave.Node[int], config: AdderConfig -) -> weave.legacy.panels.LabeledItem: +) -> weave.legacy.weave.panels.LabeledItem: input_val = typing.cast(int, input_node) config = adder_default_config(config) - return weave.legacy.panels.LabeledItem( + return weave.legacy.weave.panels.LabeledItem( label="operand", - item=weave.legacy.panels.Slider( - config=weave.legacy.panels.SliderConfig( - weave.legacy.ops.execute(config.operand) + item=weave.legacy.weave.panels.Slider( + config=weave.legacy.weave.panels.SliderConfig( + weave.legacy.weave.ops.execute(config.operand) ) ), ) @@ -58,10 +58,10 @@ def adder_config( @weave.op() def adder( input_node: weave.Node[int], config: AdderConfig -) -> weave.legacy.panels.LabeledItem: +) -> weave.legacy.weave.panels.LabeledItem: input_val = typing.cast(int, input_node) config = adder_default_config(config) - return weave.legacy.panels.LabeledItem(label="output", item=input_val + config.operand) # type: ignore + return weave.legacy.weave.panels.LabeledItem(label="output", item=input_val + config.operand) # type: ignore @weave.type() diff --git a/weave/legacy/weave/ecosystem/sklearn/__init__.py b/weave/legacy/weave/ecosystem/sklearn/__init__.py new file mode 100644 index 00000000000..08e6e889266 --- /dev/null +++ b/weave/legacy/weave/ecosystem/sklearn/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.sklearn.datasets import * diff --git a/weave/legacy/ecosystem/sklearn/datasets.py b/weave/legacy/weave/ecosystem/sklearn/datasets.py similarity index 81% rename from weave/legacy/ecosystem/sklearn/datasets.py rename to weave/legacy/weave/ecosystem/sklearn/datasets.py index 0683b58cde1..6c5dc9e3d25 100644 --- a/weave/legacy/ecosystem/sklearn/datasets.py +++ b/weave/legacy/weave/ecosystem/sklearn/datasets.py @@ -7,7 +7,7 @@ @weave.op( name="shap-ca_housing_dataset", render_info={"type": "function"}, - output_type=weave.legacy.ops.DataFrameType(weave.types.TypedDict({})), + output_type=weave.legacy.weave.ops.DataFrameType(weave.types.TypedDict({})), ) def ca_housing_dataset(seed: int): housing = fetch_california_housing(as_frame=True) diff --git a/weave/legacy/weave/ecosystem/slack/__init__.py b/weave/legacy/weave/ecosystem/slack/__init__.py new file mode 100644 index 00000000000..a5018fce230 --- /dev/null +++ b/weave/legacy/weave/ecosystem/slack/__init__.py @@ -0,0 +1,2 @@ +from weave.legacy.weave.ecosystem.slack.panels import * +from weave.legacy.weave.ecosystem.slack.slack import * diff --git a/weave/legacy/ecosystem/slack/panels.py b/weave/legacy/weave/ecosystem/slack/panels.py similarity index 73% rename from weave/legacy/ecosystem/slack/panels.py rename to weave/legacy/weave/ecosystem/slack/panels.py index 679159a381c..41f2ee60014 100644 --- a/weave/legacy/ecosystem/slack/panels.py +++ b/weave/legacy/weave/ecosystem/slack/panels.py @@ -1,7 +1,7 @@ import typing import weave -from weave.legacy.ecosystem.slack import slack +from weave.legacy.weave.ecosystem.slack import slack @weave.type() @@ -10,9 +10,9 @@ class SlackMessagesPanel(weave.Panel): input_node: weave.Node[list[slack.Message]] @weave.op() - def render(self) -> weave.legacy.panels.Table: + def render(self) -> weave.legacy.weave.panels.Table: messages = typing.cast(list[slack.Message], self.input_node) # type: ignore - return weave.legacy.panels.Table( + return weave.legacy.weave.panels.Table( messages, columns=[ lambda message: message.user_id, @@ -27,8 +27,8 @@ class SlackChannelsPanel(weave.Panel): input_node: weave.Node[list[slack.Channel]] @weave.op() - def render(self) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( + def render(self) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( self.input_node, columns=[ lambda channel: channel.channel_name, @@ -43,13 +43,13 @@ class SlackChannelPanel(weave.Panel): input_node: weave.Node[slack.Channel] @weave.op() - def render(self) -> weave.legacy.panels.Card: + def render(self) -> weave.legacy.weave.panels.Card: channel = typing.cast(slack.Channel, self.input_node) # type: ignore - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=channel.channel_name, subtitle="Slack channel", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Messages", content=SlackMessagesPanel(channel.messages()) ), ], @@ -62,13 +62,13 @@ class SlackPanel(weave.Panel): input_node: weave.Node[slack.Slack] @weave.op() - def slack_render(self) -> weave.legacy.panels.Card: + def slack_render(self) -> weave.legacy.weave.panels.Card: s = typing.cast(slack.Slack, self.input_node) # type: ignore - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title="Slack export data", subtitle="", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Channels", content=SlackChannelsPanel(s.channels()) ), ], diff --git a/weave/legacy/ecosystem/slack/slack.py b/weave/legacy/weave/ecosystem/slack/slack.py similarity index 97% rename from weave/legacy/ecosystem/slack/slack.py rename to weave/legacy/weave/ecosystem/slack/slack.py index fb960284021..f542dfeb021 100644 --- a/weave/legacy/ecosystem/slack/slack.py +++ b/weave/legacy/weave/ecosystem/slack/slack.py @@ -1,6 +1,6 @@ import weave from weave.file_base import Dir -from weave.legacy.ecosystem.slack import slackapi_readexport +from weave.legacy.weave.ecosystem.slack import slackapi_readexport @weave.type() diff --git a/weave/legacy/ecosystem/slack/slackapi.py b/weave/legacy/weave/ecosystem/slack/slackapi.py similarity index 100% rename from weave/legacy/ecosystem/slack/slackapi.py rename to weave/legacy/weave/ecosystem/slack/slackapi.py diff --git a/weave/legacy/ecosystem/slack/slackapi_readexport.py b/weave/legacy/weave/ecosystem/slack/slackapi_readexport.py similarity index 100% rename from weave/legacy/ecosystem/slack/slackapi_readexport.py rename to weave/legacy/weave/ecosystem/slack/slackapi_readexport.py diff --git a/weave/legacy/weave/ecosystem/slurm/__init__.py b/weave/legacy/weave/ecosystem/slurm/__init__.py new file mode 100644 index 00000000000..e9bd4eae0cd --- /dev/null +++ b/weave/legacy/weave/ecosystem/slurm/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.slurm.ops import * diff --git a/weave/legacy/ecosystem/slurm/ops.py b/weave/legacy/weave/ecosystem/slurm/ops.py similarity index 79% rename from weave/legacy/ecosystem/slurm/ops.py rename to weave/legacy/weave/ecosystem/slurm/ops.py index 9611df88d73..4bd811a348f 100644 --- a/weave/legacy/ecosystem/slurm/ops.py +++ b/weave/legacy/weave/ecosystem/slurm/ops.py @@ -19,8 +19,8 @@ class SlurmJob: @weave.op() def jobs_render( jobs: weave.Node[list[SlurmJob]], -) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( +) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( jobs, columns=[ lambda job: job.job_id, @@ -43,8 +43,8 @@ class SlurmNode: @weave.op() def nodes_render( nodes: weave.Node[list[SlurmNode]], -) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( +) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( nodes, columns=[ lambda node: node.node_name, @@ -96,27 +96,27 @@ def slurm(restapi_url: str) -> Slurm: @weave.op() def slurm_render( slurm_node: weave.Node[Slurm], -) -> weave.legacy.panels.Card: +) -> weave.legacy.weave.panels.Card: slurm = typing.cast(Slurm, slurm_node) - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title="slurm", subtitle="", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Overview", - content=weave.legacy.panels.Group( + content=weave.legacy.weave.panels.Group( prefer_horizontal=True, items=[ - weave.legacy.panels.LabeledItem( + weave.legacy.weave.panels.LabeledItem( item=slurm.jobs().count(), label="Total jobs" ), - weave.legacy.panels.LabeledItem( + weave.legacy.weave.panels.LabeledItem( item=slurm.nodes().count(), label="Total nodes" ), ], ), ), - weave.legacy.panels.CardTab(name="Nodes", content=slurm.nodes()), - weave.legacy.panels.CardTab(name="Jobs", content=slurm.jobs()), + weave.legacy.weave.panels.CardTab(name="Nodes", content=slurm.nodes()), + weave.legacy.weave.panels.CardTab(name="Jobs", content=slurm.jobs()), ], ) diff --git a/weave/legacy/ecosystem/shawn/__init__.py b/weave/legacy/weave/ecosystem/spacy/__init__.py similarity index 51% rename from weave/legacy/ecosystem/shawn/__init__.py rename to weave/legacy/weave/ecosystem/spacy/__init__.py index 6c2526057a3..b0e2199ab0e 100644 --- a/weave/legacy/ecosystem/shawn/__init__.py +++ b/weave/legacy/weave/ecosystem/spacy/__init__.py @@ -1,7 +1,7 @@ -from weave.legacy import context_state as _context +from weave.legacy.weave import context_state as _context _loading_builtins_token = _context.set_loading_built_ins() -from weave.legacy.ecosystem.shawn import petdataset, scratch +from weave.legacy.weave.ecosystem.spacy.spacy import * _context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/spacy/spacy.py b/weave/legacy/weave/ecosystem/spacy/spacy.py similarity index 72% rename from weave/legacy/ecosystem/spacy/spacy.py rename to weave/legacy/weave/ecosystem/spacy/spacy.py index 9dd9823a385..03f5b0fea5e 100644 --- a/weave/legacy/ecosystem/spacy/spacy.py +++ b/weave/legacy/weave/ecosystem/spacy/spacy.py @@ -31,23 +31,23 @@ def spacy(text: str) -> spacy_lib.tokens.doc.Doc: @weave.op() def spacy_doc_dep_to_html( spacy_doc: spacy_lib.tokens.doc.Doc, -) -> weave.legacy.ops.Html: +) -> weave.legacy.weave.ops.Html: from spacy import displacy html = displacy.render( list(spacy_doc.sents), style="dep", jupyter=False, options={"compact": True} ) - return weave.legacy.ops.Html(html) + return weave.legacy.weave.ops.Html(html) @weave.op() def spacy_doc_ent_to_html( spacy_doc: spacy_lib.tokens.doc.Doc, -) -> weave.legacy.ops.Html: +) -> weave.legacy.weave.ops.Html: from spacy import displacy html = displacy.render(spacy_doc, style="ent", jupyter=False) - return weave.legacy.ops.Html(html) + return weave.legacy.weave.ops.Html(html) @weave.type() @@ -56,18 +56,18 @@ class SpacyDocPanel(weave.Panel): input_node: weave.Node[spacy_lib.tokens.doc.Doc] @weave.op() - def render(self) -> weave.legacy.panels.Card: - return weave.legacy.panels.Card( + def render(self) -> weave.legacy.weave.panels.Card: + return weave.legacy.weave.panels.Card( title="Spacy Visualization", subtitle="", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Dependencies", - content=weave.legacy.panels.PanelHtml(spacy_doc_dep_to_html(self.input_node)), # type: ignore + content=weave.legacy.weave.panels.PanelHtml(spacy_doc_dep_to_html(self.input_node)), # type: ignore ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Named Entities", - content=weave.legacy.panels.PanelHtml(spacy_doc_ent_to_html(self.input_node)), # type: ignore + content=weave.legacy.weave.panels.PanelHtml(spacy_doc_ent_to_html(self.input_node)), # type: ignore ), ], ) diff --git a/weave/legacy/ecosystem/test_notebook_ecosystem_executions.py b/weave/legacy/weave/ecosystem/test_notebook_ecosystem_executions.py similarity index 98% rename from weave/legacy/ecosystem/test_notebook_ecosystem_executions.py rename to weave/legacy/weave/ecosystem/test_notebook_ecosystem_executions.py index 9d4e9bbfebb..95817ef76f9 100644 --- a/weave/legacy/ecosystem/test_notebook_ecosystem_executions.py +++ b/weave/legacy/weave/ecosystem/test_notebook_ecosystem_executions.py @@ -1,8 +1,8 @@ -from weave.legacy.server import handle_request +from weave.legacy.weave.server import handle_request def test_playback(): - from weave.legacy import ecosystem + from weave.legacy.weave import ecosystem for payload in [execute_payloads[-1]]: res = handle_request(payload, True) diff --git a/weave/legacy/ecosystem/torch_mnist_model_example.py b/weave/legacy/weave/ecosystem/torch_mnist_model_example.py similarity index 93% rename from weave/legacy/ecosystem/torch_mnist_model_example.py rename to weave/legacy/weave/ecosystem/torch_mnist_model_example.py index ab09c6fc70f..8ce668a1ab8 100644 --- a/weave/legacy/ecosystem/torch_mnist_model_example.py +++ b/weave/legacy/weave/ecosystem/torch_mnist_model_example.py @@ -7,8 +7,8 @@ from torchvision import transforms import weave -from weave.legacy import context_state as _context -from weave.legacy.ecosystem import pytorch +from weave.legacy.weave import context_state as _context +from weave.legacy.weave.ecosystem import pytorch DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") @@ -54,7 +54,7 @@ class Model: output_type=weave.types.List( weave.types.TypedDict( { - "X": weave.legacy.ops.image.PILImageType(), # type: ignore + "X": weave.legacy.weave.ops.image.PILImageType(), # type: ignore "y": weave.types.Int(), } ) @@ -131,7 +131,7 @@ def train_epoch(network, loader, optimizer): @weave.op( render_info={"type": "function"}, input_type={ - "X": weave.types.List(weave.legacy.ops.image.PILImageType()), # type: ignore + "X": weave.types.List(weave.legacy.weave.ops.image.PILImageType()), # type: ignore "y": weave.types.List(weave.types.Int()), # TODO: class enum? }, # TODO: WeaveJS doesn't support callable output type yet. diff --git a/weave/legacy/weave/ecosystem/torchvision/__init__.py b/weave/legacy/weave/ecosystem/torchvision/__init__.py new file mode 100644 index 00000000000..b003b9b82da --- /dev/null +++ b/weave/legacy/weave/ecosystem/torchvision/__init__.py @@ -0,0 +1 @@ +from weave.legacy.weave.ecosystem.torchvision.datasets import * diff --git a/weave/legacy/ecosystem/torchvision/datasets.py b/weave/legacy/weave/ecosystem/torchvision/datasets.py similarity index 98% rename from weave/legacy/ecosystem/torchvision/datasets.py rename to weave/legacy/weave/ecosystem/torchvision/datasets.py index 46f74273f75..b62e5c9a126 100644 --- a/weave/legacy/ecosystem/torchvision/datasets.py +++ b/weave/legacy/weave/ecosystem/torchvision/datasets.py @@ -11,8 +11,8 @@ from torchvision import datasets import weave -from weave.legacy import infer_types -from weave.legacy import panels +from weave.legacy.weave import infer_types +from weave.legacy.weave import panels ExampleType = TypeVar("ExampleType") diff --git a/weave/legacy/language_features/__init__.py b/weave/legacy/weave/ecosystem/twitter/__init__.py similarity index 100% rename from weave/legacy/language_features/__init__.py rename to weave/legacy/weave/ecosystem/twitter/__init__.py diff --git a/weave/legacy/ecosystem/twitter/tweet.py b/weave/legacy/weave/ecosystem/twitter/tweet.py similarity index 100% rename from weave/legacy/ecosystem/twitter/tweet.py rename to weave/legacy/weave/ecosystem/twitter/tweet.py diff --git a/weave/legacy/ecosystem/example/__init__.py b/weave/legacy/weave/ecosystem/umap/__init__.py similarity index 74% rename from weave/legacy/ecosystem/example/__init__.py rename to weave/legacy/weave/ecosystem/umap/__init__.py index 1ac79cc09e3..e3feb039824 100644 --- a/weave/legacy/ecosystem/example/__init__.py +++ b/weave/legacy/weave/ecosystem/umap/__init__.py @@ -2,13 +2,13 @@ import logging -from weave.legacy import context_state +from weave.legacy.weave import context_state logging.getLogger("ecosystem_example").setLevel(logging.ERROR) loading_builtins_token = context_state.set_loading_built_ins() try: - from weave.legacy.ecosystem.example import ops + from weave.legacy.weave.ecosystem.umap.ops import * finally: context_state.clear_loading_built_ins(loading_builtins_token) diff --git a/weave/legacy/ecosystem/umap/ops.py b/weave/legacy/weave/ecosystem/umap/ops.py similarity index 96% rename from weave/legacy/ecosystem/umap/ops.py rename to weave/legacy/weave/ecosystem/umap/ops.py index 74951e7b7c4..893cba22ecb 100644 --- a/weave/legacy/ecosystem/umap/ops.py +++ b/weave/legacy/weave/ecosystem/umap/ops.py @@ -11,7 +11,7 @@ ) import weave -from weave.legacy import ops_arrow +from weave.legacy.weave import ops_arrow UMAP_LIB = None diff --git a/weave/legacy/weave/ecosystem/wandb/__init__.py b/weave/legacy/weave/ecosystem/wandb/__init__.py new file mode 100644 index 00000000000..9786a4f2489 --- /dev/null +++ b/weave/legacy/weave/ecosystem/wandb/__init__.py @@ -0,0 +1,13 @@ +from weave.legacy.weave import context_state as _context + +_loading_builtins_token = _context.set_loading_built_ins() + +from weave.legacy.weave.ecosystem.wandb import wandb_objs +from weave.legacy.weave.ecosystem.wandb.panel_distribution import * +from weave.legacy.weave.ecosystem.wandb.panel_geo import * +from weave.legacy.weave.ecosystem.wandb.panel_scatter import * +from weave.legacy.weave.ecosystem.wandb.panel_time_series import * +from weave.legacy.weave.ecosystem.wandb.run_chain import * +from weave.legacy.weave.ecosystem.wandb.weave_plotly import * + +_context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/wandb/gql_artifact_dag.py b/weave/legacy/weave/ecosystem/wandb/gql_artifact_dag.py similarity index 98% rename from weave/legacy/ecosystem/wandb/gql_artifact_dag.py rename to weave/legacy/weave/ecosystem/wandb/gql_artifact_dag.py index 4fecc2909e7..ca22e0b1801 100644 --- a/weave/legacy/ecosystem/wandb/gql_artifact_dag.py +++ b/weave/legacy/weave/ecosystem/wandb/gql_artifact_dag.py @@ -1,6 +1,6 @@ import gql -from weave.legacy import wandb_api +from weave.legacy.weave import wandb_api ARTIFACT_LINEAGE_QUERY = gql.gql( """ diff --git a/weave/legacy/ecosystem/wandb/panel_distribution.py b/weave/legacy/weave/ecosystem/wandb/panel_distribution.py similarity index 75% rename from weave/legacy/ecosystem/wandb/panel_distribution.py rename to weave/legacy/weave/ecosystem/wandb/panel_distribution.py index b7d53699e4a..387db6ece06 100644 --- a/weave/legacy/ecosystem/wandb/panel_distribution.py +++ b/weave/legacy/weave/ecosystem/wandb/panel_distribution.py @@ -2,9 +2,9 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import panel_util -from weave.legacy.ecosystem.wandb import weave_plotly +from weave.legacy.weave import weave_internal +from weave.legacy.weave import panel_util +from weave.legacy.weave.ecosystem.wandb import weave_plotly @weave.type() @@ -14,14 +14,14 @@ class DistributionConfig: # We should make a better type to represent this, so it can be # distinguished from an expression like bin_size. value_fn: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) label_fn: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) # This is an expression. It will be stored in the config as Node. bin_size: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) @@ -35,7 +35,7 @@ class Distribution(weave.Panel): @weave.op() def initialize(self) -> DistributionConfig: input_node = self.input_node - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) return DistributionConfig( value_fn=weave_internal.define_fn( {"item": unnested.type.object_type}, @@ -50,19 +50,19 @@ def initialize(self) -> DistributionConfig: ) @weave.op() - def render_config(self) -> weave.legacy.panels.Group: + def render_config(self) -> weave.legacy.weave.panels.Group: config = typing.cast(DistributionConfig, self.config) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( items={ - "value_fn": weave.legacy.panels.LabeledItem( + "value_fn": weave.legacy.weave.panels.LabeledItem( label="value", - item=weave.legacy.panels.FunctionEditor(config.value_fn), + item=weave.legacy.weave.panels.FunctionEditor(config.value_fn), ), - "label_fn": weave.legacy.panels.LabeledItem( + "label_fn": weave.legacy.weave.panels.LabeledItem( label="label", - item=weave.legacy.panels.FunctionEditor(config.label_fn), + item=weave.legacy.weave.panels.FunctionEditor(config.label_fn), ), - "bin_size": weave.legacy.panels.LabeledItem( + "bin_size": weave.legacy.weave.panels.LabeledItem( label="bin_size", # Must execute here because bin_size is an expression. # Editor panels take the actual type they edit (in this @@ -70,7 +70,7 @@ def render_config(self) -> weave.legacy.panels.Group: # const node directly in the config, or if the expression # refers to variables, the edit will be routed to the appropriate # owner. - item=weave.legacy.panels.Slider(config.bin_size.execute()), # type: ignore + item=weave.legacy.weave.panels.Slider(config.bin_size.execute()), # type: ignore ), } ) @@ -87,10 +87,10 @@ def render(self) -> weave_plotly.PanelPlotly: config.value_fn.type.output_type # type: ignore ): # TODO: need a nicer way to return error states - return weave.legacy.panels.PanelString("Invalid value_fn") # type: ignore + return weave.legacy.weave.panels.PanelString("Invalid value_fn") # type: ignore # We always unnest, so that we can compare across groups of items # easily. (the Distribution notebook) - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) bin_size = config.bin_size def bin_func(item): @@ -111,10 +111,10 @@ def bin_func(item): else: group_items["label"] = config.label_fn(item) - return weave.legacy.ops.dict_(**group_items) + return weave.legacy.weave.ops.dict_(**group_items) binned = unnested.groupby(lambda item: bin_func(item)).map( - lambda group: weave.legacy.ops.dict_( + lambda group: weave.legacy.weave.ops.dict_( value=group.groupkey()["value"], label=group.groupkey()["label"], count=group.count(), @@ -128,9 +128,9 @@ def bin_func(item): @weave.op() def distribution_panel_plot_render( input_node: weave.Node[list[typing.Any]], config: DistributionConfig -) -> weave.legacy.panels.Plot: - unnested = weave.legacy.ops.unnest(input_node) - bin_size = weave.legacy.ops.execute(config.bin_size) +) -> weave.legacy.weave.panels.Plot: + unnested = weave.legacy.weave.ops.unnest(input_node) + bin_size = weave.legacy.weave.ops.execute(config.bin_size) def bin_func(item): value_fn_output_type = config.value_fn.type.output_type @@ -146,18 +146,18 @@ def bin_func(item): else: group_items["label"] = config.label_fn(item) - res = weave.legacy.ops.dict_(**group_items) + res = weave.legacy.weave.ops.dict_(**group_items) return res binned = unnested.groupby(lambda item: bin_func(item)).map( - lambda group: weave.legacy.ops.dict_( + lambda group: weave.legacy.weave.ops.dict_( value=group.groupkey()["value"], label=group.groupkey()["label"], count=group.count(), ) ) - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( binned, x=lambda row: row["value"], y=lambda row: row["count"], diff --git a/weave/legacy/ecosystem/wandb/panel_geo.py b/weave/legacy/weave/ecosystem/wandb/panel_geo.py similarity index 77% rename from weave/legacy/ecosystem/wandb/panel_geo.py rename to weave/legacy/weave/ecosystem/wandb/panel_geo.py index f0be1990357..15c1f1c479d 100644 --- a/weave/legacy/ecosystem/wandb/panel_geo.py +++ b/weave/legacy/weave/ecosystem/wandb/panel_geo.py @@ -3,8 +3,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy.ecosystem.wandb import weave_plotly +from weave.legacy.weave import weave_internal +from weave.legacy.weave.ecosystem.wandb import weave_plotly # This is the panel's config (the state that is stored in the panel and configurable @@ -12,13 +12,13 @@ @weave.type() class GeoConfig: x_fn: weave.Node[float] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) y_fn: weave.Node[float] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) color_fn: weave.Node[float] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) @@ -69,10 +69,10 @@ def geo_default_config( def geo( input_node: weave.Node[list[typing.Any]], config: GeoConfig ) -> weave_plotly.PanelPlotly: - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) config = geo_default_config(config, unnested) plot_data = unnested.map( - lambda item: weave.legacy.ops.dict_( + lambda item: weave.legacy.weave.ops.dict_( long=config.x_fn(item), # type: ignore lat=config.y_fn(item), # type: ignore color=config.color_fn(item), # type: ignore @@ -86,27 +86,27 @@ def geo( @weave.op(name="Geo_config") def geo_config( input_node: weave.Node[list[typing.Any]], config: GeoConfig -) -> weave.legacy.panels.Group: - unnested = weave.legacy.ops.unnest(input_node) +) -> weave.legacy.weave.panels.Group: + unnested = weave.legacy.weave.ops.unnest(input_node) config = geo_default_config(config, unnested) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( items={ - "x_fn": weave.legacy.panels.LabeledItem( + "x_fn": weave.legacy.weave.panels.LabeledItem( label="x", - item=weave.legacy.panels.FunctionEditor( - config=weave.legacy.panels.FunctionEditorConfig(config.x_fn) + item=weave.legacy.weave.panels.FunctionEditor( + config=weave.legacy.weave.panels.FunctionEditorConfig(config.x_fn) ), ), - "y_fn": weave.legacy.panels.LabeledItem( + "y_fn": weave.legacy.weave.panels.LabeledItem( label="y", - item=weave.legacy.panels.FunctionEditor( - config=weave.legacy.panels.FunctionEditorConfig(config.y_fn) + item=weave.legacy.weave.panels.FunctionEditor( + config=weave.legacy.weave.panels.FunctionEditorConfig(config.y_fn) ), ), - "color_fn": weave.legacy.panels.LabeledItem( + "color_fn": weave.legacy.weave.panels.LabeledItem( label="color", - item=weave.legacy.panels.FunctionEditor( - config=weave.legacy.panels.FunctionEditorConfig(config.color_fn) + item=weave.legacy.weave.panels.FunctionEditor( + config=weave.legacy.weave.panels.FunctionEditorConfig(config.color_fn) ), ), } @@ -134,7 +134,7 @@ def __init__( if self.config is None: self.config = GeoConfig() - unnested = weave.legacy.ops.unnest(self.input_node) + unnested = weave.legacy.weave.ops.unnest(self.input_node) if "x_fn" in options: sig = inspect.signature(options["x_fn"]) param_name = list(sig.parameters.values())[0].name @@ -171,28 +171,28 @@ def __init__( @weave.op(output_type=lambda input_type: input_type["self"].input_node.output_type) def selected(self): # TODO: This function is not right! We need to do a range selection in polar space! - unnested = weave.legacy.ops.unnest(self.input_node) + unnested = weave.legacy.weave.ops.unnest(self.input_node) config = geo_default_config(self.config, unnested) filtered = unnested.filter( - lambda item: weave.legacy.ops.Boolean.bool_and( - weave.legacy.ops.Boolean.bool_and( - weave.legacy.ops.Boolean.bool_and( + lambda item: weave.legacy.weave.ops.Boolean.bool_and( + weave.legacy.weave.ops.Boolean.bool_and( + weave.legacy.weave.ops.Boolean.bool_and( config.x_fn(item) - > weave.legacy.ops.TypedDict.pick( + > weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "xMin" ), config.x_fn(item) - < weave.legacy.ops.TypedDict.pick( + < weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "xMax" ), ), config.y_fn(item) - > weave.legacy.ops.TypedDict.pick( + > weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "yMin" ), ), config.y_fn(item) - < weave.legacy.ops.TypedDict.pick( + < weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "yMax" ), ) diff --git a/weave/legacy/ecosystem/wandb/panel_scatter.py b/weave/legacy/weave/ecosystem/wandb/panel_scatter.py similarity index 65% rename from weave/legacy/ecosystem/wandb/panel_scatter.py rename to weave/legacy/weave/ecosystem/wandb/panel_scatter.py index 9794d847313..f670ae23c48 100644 --- a/weave/legacy/ecosystem/wandb/panel_scatter.py +++ b/weave/legacy/weave/ecosystem/wandb/panel_scatter.py @@ -3,20 +3,20 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy.ecosystem.wandb import weave_plotly +from weave.legacy.weave import weave_internal +from weave.legacy.weave.ecosystem.wandb import weave_plotly @weave.type() class ScatterConfig: x_fn: weave.Node[typing.Optional[typing.Any]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) y_fn: weave.Node[typing.Optional[typing.Any]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) label_fn: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) @@ -30,7 +30,7 @@ class Scatter(weave.Panel): @weave.op() def initialize(self) -> ScatterConfig: input_node = self.input_node - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) return ScatterConfig( x_fn=weave_internal.define_fn( {"item": unnested.type.object_type}, @@ -48,19 +48,19 @@ def initialize(self) -> ScatterConfig: # The config render op. This renders the config editor. @weave.op() - def render_config(self) -> weave.legacy.panels.Group: + def render_config(self) -> weave.legacy.weave.panels.Group: config = typing.cast(ScatterConfig, self.config) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( items={ - "x_fn": weave.legacy.panels.LabeledItem( - label="x", item=weave.legacy.panels.FunctionEditor(config.x_fn) + "x_fn": weave.legacy.weave.panels.LabeledItem( + label="x", item=weave.legacy.weave.panels.FunctionEditor(config.x_fn) ), - "y_fn": weave.legacy.panels.LabeledItem( - label="y", item=weave.legacy.panels.FunctionEditor(config.y_fn) + "y_fn": weave.legacy.weave.panels.LabeledItem( + label="y", item=weave.legacy.weave.panels.FunctionEditor(config.y_fn) ), - "label_fn": weave.legacy.panels.LabeledItem( + "label_fn": weave.legacy.weave.panels.LabeledItem( label="label", - item=weave.legacy.panels.FunctionEditor(config.label_fn), + item=weave.legacy.weave.panels.FunctionEditor(config.label_fn), ), } ) @@ -70,7 +70,7 @@ def render_config(self) -> weave.legacy.panels.Group: def render(self) -> weave_plotly.PanelPlotly: input_node = self.input_node config = typing.cast(ScatterConfig, self.config) - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) if ( not weave.types.optional(weave.types.Float()).assign_type(config.x_fn.type) or not weave.types.optional(weave.types.Float()).assign_type( @@ -80,14 +80,14 @@ def render(self) -> weave_plotly.PanelPlotly: config.label_fn.type ) ): - return weave.legacy.panels.PanelHtml(weave.legacy.ops.Html("No data")) # type: ignore + return weave.legacy.weave.panels.PanelHtml(weave.legacy.weave.ops.Html("No data")) # type: ignore if config.label_fn.type == weave.types.Invalid(): plot_data = unnested.map( - lambda item: weave.legacy.ops.dict_(x=config.x_fn(item), y=config.y_fn(item)) # type: ignore + lambda item: weave.legacy.weave.ops.dict_(x=config.x_fn(item), y=config.y_fn(item)) # type: ignore ) else: plot_data = unnested.map( - lambda item: weave.legacy.ops.dict_( + lambda item: weave.legacy.weave.ops.dict_( x=config.x_fn(item), y=config.y_fn(item), label=config.label_fn(item), @@ -100,28 +100,28 @@ def render(self) -> weave_plotly.PanelPlotly: # TODO: Fix @weave.op(output_type=lambda input_type: input_type["self"].input_node.output_type) def selected(self): - unnested = weave.legacy.ops.unnest(self.input_node) + unnested = weave.legacy.weave.ops.unnest(self.input_node) config = self.config filtered = unnested.filter( - lambda item: weave.legacy.ops.Boolean.bool_and( - weave.legacy.ops.Boolean.bool_and( - weave.legacy.ops.Boolean.bool_and( + lambda item: weave.legacy.weave.ops.Boolean.bool_and( + weave.legacy.weave.ops.Boolean.bool_and( + weave.legacy.weave.ops.Boolean.bool_and( config.x_fn(item) - > weave.legacy.ops.TypedDict.pick( + > weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "xMin" ), config.x_fn(item) - < weave.legacy.ops.TypedDict.pick( + < weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "xMax" ), ), config.y_fn(item) - > weave.legacy.ops.TypedDict.pick( + > weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "yMin" ), ), config.y_fn(item) - < weave.legacy.ops.TypedDict.pick( + < weave.legacy.weave.ops.TypedDict.pick( self._renderAsPanel.config.selected, "yMax" ), ) diff --git a/weave/legacy/ecosystem/wandb/panel_time_series.py b/weave/legacy/weave/ecosystem/wandb/panel_time_series.py similarity index 80% rename from weave/legacy/ecosystem/wandb/panel_time_series.py rename to weave/legacy/weave/ecosystem/wandb/panel_time_series.py index 5d793f3152d..406ff8f140d 100644 --- a/weave/legacy/ecosystem/wandb/panel_time_series.py +++ b/weave/legacy/weave/ecosystem/wandb/panel_time_series.py @@ -2,10 +2,10 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy.ecosystem.wandb import weave_plotly -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.panel_util import make_node +from weave.legacy.weave import weave_internal +from weave.legacy.weave.ecosystem.wandb import weave_plotly +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.panel_util import make_node TIME_SERIES_BIN_SIZES_SEC = [ # TODO: will need more steps along here for smooth zooming. @@ -53,27 +53,27 @@ @weave.type() class TimeSeriesConfig: x: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) agg: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) min_x: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) max_x: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) label: weave.Node[ typing.Optional[typing.Union[str, weave.types.InvalidPy]] - ] = dataclasses.field(default_factory=lambda: weave.legacy.graph.VoidNode()) + ] = dataclasses.field(default_factory=lambda: weave.legacy.weave.graph.VoidNode()) mark: weave.Node[str] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.ConstNode( + default_factory=lambda: weave.legacy.weave.graph.ConstNode( weave.types.String(), "bar" ) ) axis_labels: weave.Node[dict[str, str]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.ConstNode( + default_factory=lambda: weave.legacy.weave.graph.ConstNode( weave.types.Dict(weave.types.String(), weave.types.String()), {}, ) @@ -83,7 +83,7 @@ class TimeSeriesConfig: def first_column_of_type( node_type: weave.types.Type, desired_type: weave.types.Type, -) -> typing.Tuple[weave.legacy.graph.ConstNode, weave.legacy.graph.ConstNode]: +) -> typing.Tuple[weave.legacy.weave.graph.ConstNode, weave.legacy.weave.graph.ConstNode]: if isinstance(node_type, tagged_value_type.TaggedValueType): node_type = node_type.value if weave.types.List().assign_type(node_type): @@ -119,8 +119,8 @@ def first_column_of_type( {"item": object_type}, lambda item: item[key] ) # return weave_internal.define_fn( - # {"input_node": node_type}, weave.legacy.graph.VoidNode() - # ), weave_internal.define_fn({"item": object_type}, lambda _: weave.legacy.graph.VoidNode()) + # {"input_node": node_type}, weave.legacy.weave.graph.VoidNode() + # ), weave_internal.define_fn({"item": object_type}, lambda _: weave.legacy.weave.graph.VoidNode()) raise ValueError( f"Can't extract column with type {desired_type} from node of type {node_type}" ) @@ -160,7 +160,7 @@ def __init__(self, input_node, vars=None, config=None, **options): super().__init__(input_node=input_node, vars=vars) self.config = config - unnested = weave.legacy.ops.unnest(input_node) + unnested = weave.legacy.weave.ops.unnest(input_node) # TODO: add the ability to configure options here if self.config is None: @@ -168,7 +168,7 @@ def __init__(self, input_node, vars=None, config=None, **options): for attr in ["x", "min_x", "max_x", "label", "mark", "agg", "axis_labels"]: if attr in options: value = options[attr] - if not isinstance(value, weave.legacy.graph.Node): + if not isinstance(value, weave.legacy.weave.graph.Node): if attr in ["min_x", "max_x", "mark", "axis_labels"]: value = make_node(value) if attr in ["min_x", "max_x"]: @@ -186,7 +186,7 @@ def __init__(self, input_node, vars=None, config=None, **options): else: value = weave_internal.define_fn( {"item": unnested.type.object_type}, - lambda item: weave.legacy.graph.VoidNode(), + lambda item: weave.legacy.weave.graph.VoidNode(), ) setattr(self.config, attr, value) @@ -239,32 +239,32 @@ def initialize(self) -> TimeSeriesConfig: # The config render op. This renders the config editor. @weave.op() - def render_config(self) -> weave.legacy.panels.Group: + def render_config(self) -> weave.legacy.weave.panels.Group: input_node = self.input_node config = typing.cast(TimeSeriesConfig, self.config) - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( items={ - "x": weave.legacy.panels.LabeledItem( - label="x", item=weave.legacy.panels.FunctionEditor(config.x) + "x": weave.legacy.weave.panels.LabeledItem( + label="x", item=weave.legacy.weave.panels.FunctionEditor(config.x) ), - "label": weave.legacy.panels.LabeledItem( + "label": weave.legacy.weave.panels.LabeledItem( label="label", - item=weave.legacy.panels.FunctionEditor(config.label), + item=weave.legacy.weave.panels.FunctionEditor(config.label), ), - "min_x": weave.legacy.panels.LabeledItem( + "min_x": weave.legacy.weave.panels.LabeledItem( label="min_x", - item=weave.legacy.panels.FunctionEditor(config.min_x), + item=weave.legacy.weave.panels.FunctionEditor(config.min_x), ), - "max_x": weave.legacy.panels.LabeledItem( + "max_x": weave.legacy.weave.panels.LabeledItem( label="max_x", - item=weave.legacy.panels.FunctionEditor(config.max_x), + item=weave.legacy.weave.panels.FunctionEditor(config.max_x), ), - "agg": weave.legacy.panels.LabeledItem( - label="agg", item=weave.legacy.panels.FunctionEditor(config.agg) + "agg": weave.legacy.weave.panels.LabeledItem( + label="agg", item=weave.legacy.weave.panels.FunctionEditor(config.agg) ), - "mark": weave.legacy.panels.LabeledItem( + "mark": weave.legacy.weave.panels.LabeledItem( label="mark", - item=weave.legacy.panels.ObjectPicker( + item=weave.legacy.weave.panels.ObjectPicker( weave_internal.make_const_node( weave.types.List(weave.types.String()), [ @@ -273,7 +273,7 @@ def render_config(self) -> weave.legacy.panels.Group: "point", ], ), - config=weave.legacy.panels.ObjectPickerConfig( + config=weave.legacy.weave.panels.ObjectPickerConfig( choice=config.mark ), ), @@ -296,13 +296,13 @@ def render(self) -> weave_plotly.PanelPlotly: if not weave.types.optional(weave.types.Timestamp()).assign_type( min_x.type ) or not weave.types.optional(weave.types.Timestamp()).assign_type(max_x.type): - return weave.legacy.panels.PanelHtml(weave.legacy.ops.Html("No data")) # type: ignore + return weave.legacy.weave.panels.PanelHtml(weave.legacy.weave.ops.Html("No data")) # type: ignore exact_bin_size = ((max_x - min_x) / N_BINS).totalSeconds() # type: ignore bin_size_index = TIME_SERIES_BIN_SIZES_SEC_NODE.map( # type: ignore lambda x: ( (x - exact_bin_size).abs() - / weave.legacy.ops.make_list(a=x, b=exact_bin_size).min() + / weave.legacy.weave.ops.make_list(a=x, b=exact_bin_size).min() ) # lambda x: (x / exact_bin_size - 1).abs() # original ).argmin() @@ -323,25 +323,25 @@ def bin_fn(item): bin_start = bin_start_ms bin_end = bin_end_ms - group_items["bin"] = weave.legacy.ops.dict_(start=bin_start, stop=bin_end) + group_items["bin"] = weave.legacy.weave.ops.dict_(start=bin_start, stop=bin_end) if label_fn_output_type == weave.types.Invalid(): group_items["label"] = "no_label" else: group_items["label"] = config.label(item) - return weave.legacy.ops.dict_(**group_items) + return weave.legacy.weave.ops.dict_(**group_items) binned = ( unnested.filter( - lambda item: weave.legacy.ops.Boolean.bool_and( + lambda item: weave.legacy.weave.ops.Boolean.bool_and( config.x(item) <= max_x, config.x(item) >= min_x, # type: ignore ) ) .groupby(lambda item: bin_fn(item)) .map( - lambda group: weave.legacy.ops.dict_( + lambda group: weave.legacy.weave.ops.dict_( x=group.groupkey()["bin"], label=group.groupkey()["label"], y=config.agg(group), # type: ignore @@ -349,12 +349,12 @@ def bin_fn(item): ) # this is needed because otherwise the lines look like a scrambled mess .sort( - lambda item: weave.legacy.ops.make_list(a=item["x"]["start"]), + lambda item: weave.legacy.weave.ops.make_list(a=item["x"]["start"]), ["asc"], ) ) - default_labels = weave.legacy.ops.dict_( + default_labels = weave.legacy.weave.ops.dict_( # x=function_to_string(config.x), # y=function_to_string(config.agg), # label=function_to_string(config.label), diff --git a/weave/legacy/ecosystem/wandb/run_chain.py b/weave/legacy/weave/ecosystem/wandb/run_chain.py similarity index 91% rename from weave/legacy/ecosystem/wandb/run_chain.py rename to weave/legacy/weave/ecosystem/wandb/run_chain.py index e57a79da9ff..3283b1742b8 100644 --- a/weave/legacy/ecosystem/wandb/run_chain.py +++ b/weave/legacy/weave/ecosystem/wandb/run_chain.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import compile, op_def, ops_arrow -from weave.legacy.ecosystem.wandb import gql_artifact_dag +from weave.legacy.weave import compile, op_def, ops_arrow +from weave.legacy.weave.ecosystem.wandb import gql_artifact_dag @weave.type() @@ -20,7 +20,7 @@ class RunChain: def _history_node(self): with op_def.no_refine(): - proj = weave.legacy.ops.project(self.entity_name, self.project_name) + proj = weave.legacy.weave.ops.project(self.entity_name, self.project_name) history_nodes = [] for seg in self.segments: # This is the only use of the deprecated history2 op in the Weave @@ -32,8 +32,8 @@ def _history_node(self): history_nodes.append(hist_node) - history_node = weave.legacy.ops.List.concat( - weave.legacy.ops.make_list( + history_node = weave.legacy.weave.ops.List.concat( + weave.legacy.weave.ops.make_list( **{f"node{i}": n for i, n in enumerate(history_nodes)} ) ) @@ -56,7 +56,7 @@ def history(self): def run_chain(run_path: str) -> RunChain: with op_def.no_refine(): entity, project, run_id = run_path.split("/") - proj = weave.legacy.ops.project(entity, project) + proj = weave.legacy.weave.ops.project(entity, project) run = proj.run(run_id) # Get the checkpoint artifact for this one if one exists diff --git a/weave/legacy/ecosystem/wandb/test_panel_distribution.py b/weave/legacy/weave/ecosystem/wandb/test_panel_distribution.py similarity index 93% rename from weave/legacy/ecosystem/wandb/test_panel_distribution.py rename to weave/legacy/weave/ecosystem/wandb/test_panel_distribution.py index a75a862649c..17b5a7400f8 100644 --- a/weave/legacy/ecosystem/wandb/test_panel_distribution.py +++ b/weave/legacy/weave/ecosystem/wandb/test_panel_distribution.py @@ -1,8 +1,8 @@ import random import weave -from weave.legacy import weave_internal -from weave.legacy.ecosystem.wandb import panel_distribution +from weave.legacy.weave import weave_internal +from weave.legacy.weave.ecosystem.wandb import panel_distribution def test_flow(): diff --git a/weave/legacy/ecosystem/wandb/wandb_objs.py b/weave/legacy/weave/ecosystem/wandb/wandb_objs.py similarity index 73% rename from weave/legacy/ecosystem/wandb/wandb_objs.py rename to weave/legacy/weave/ecosystem/wandb/wandb_objs.py index 449d82ed786..38731a5af83 100644 --- a/weave/legacy/ecosystem/wandb/wandb_objs.py +++ b/weave/legacy/weave/ecosystem/wandb/wandb_objs.py @@ -1,8 +1,8 @@ import typing import weave -from weave.legacy import registry_mem -from weave.legacy.ops_domain import run_ops, wb_domain_types +from weave.legacy.weave import registry_mem +from weave.legacy.weave.ops_domain import run_ops, wb_domain_types # We can't chain ops called .name() because of a weird bug :( [its a field on VarNode]. # So we have to get the ops here and call them directly for now. @@ -23,7 +23,7 @@ def org_model(entity_name: str, model_name: str) -> FakeWandbModel: return FakeWandbModel(model_name) -GHOSTWRITE_MD = weave.legacy.ops.Markdown( +GHOSTWRITE_MD = weave.legacy.weave.ops.Markdown( """ # [ghostwrite.ai](https://ghostwrite.ai). @@ -35,19 +35,19 @@ def org_model(entity_name: str, model_name: str) -> FakeWandbModel: @weave.op() def fakewandbmodel_render( model_node: weave.Node[FakeWandbModel], -) -> weave.legacy.panels.Card: +) -> weave.legacy.weave.panels.Card: model = typing.cast(FakeWandbModel, model_node) - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=model.name, subtitle="", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Description", - content=weave.legacy.panels.PanelMarkdown(GHOSTWRITE_MD), # type: ignore + content=weave.legacy.weave.panels.PanelMarkdown(GHOSTWRITE_MD), # type: ignore ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Predictions", - content=weave.legacy.ops.project("shawn", "ghostwrite-test1") + content=weave.legacy.weave.ops.project("shawn", "ghostwrite-test1") .runs() .summary()["predictions"] .table() @@ -62,39 +62,39 @@ def fakewandbmodel_render( @weave.op() def entity_render( entity_node: weave.Node[wb_domain_types.Entity], -) -> weave.legacy.panels.Card: +) -> weave.legacy.weave.panels.Card: entity = typing.cast(wb_domain_types.Entity, entity_node) - return weave.legacy.panels.Card( + return weave.legacy.weave.panels.Card( title=entity_name_op(entity), subtitle="", content=[ - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Projects", - content=weave.legacy.panels.Table( + content=weave.legacy.weave.panels.Table( entity.projects(), # type: ignore columns=[ - lambda project: weave.legacy.panels.WeaveLink( + lambda project: weave.legacy.weave.panels.WeaveLink( project_name_op(project), vars={ "entity_name": entity_name_op(project.entity()), "project_name": project_name_op(project), }, - to=lambda input, vars: weave.legacy.ops.project( + to=lambda input, vars: weave.legacy.weave.ops.project( vars["entity_name"], vars["project_name"] ), ), ], ), ), - weave.legacy.panels.CardTab( + weave.legacy.weave.panels.CardTab( name="Registered Models", - content=weave.legacy.panels.Table( + content=weave.legacy.weave.panels.Table( weave.save( ["ghostwrite", "credit card predictor", "stopsigns3"], name="model_list", ), columns=[ - lambda model_name: weave.legacy.panels.WeaveLink( + lambda model_name: weave.legacy.weave.panels.WeaveLink( model_name, vars={ "entity_name": entity_name_op(entity), @@ -116,18 +116,18 @@ class ProjectRunsTable(weave.Panel): input_node: weave.Node[list[wb_domain_types.Run]] @weave.op() - def render(self) -> weave.legacy.panels.Table: - return weave.legacy.panels.Table( + def render(self) -> weave.legacy.weave.panels.Table: + return weave.legacy.weave.panels.Table( self.input_node, columns=[ - lambda run: weave.legacy.panels.WeaveLink( + lambda run: weave.legacy.weave.panels.WeaveLink( run.id(), vars={ "entity_name": entity_name_op(run.project().entity()), "project_name": project_name_op(run.project()), "run_id": run.id(), }, - to=lambda input, vars: weave.legacy.ops.project( + to=lambda input, vars: weave.legacy.weave.ops.project( vars["entity_name"], vars["project_name"] ).run(vars["run_id"]), ), @@ -143,28 +143,28 @@ class ProjectArtifactsTable(weave.Panel): input_node: weave.Node[list[wb_domain_types.ArtifactCollection]] @weave.op() - def render(self) -> weave.legacy.panels.Table: + def render(self) -> weave.legacy.weave.panels.Table: # This breaks if there is a variable in the node # types_names = weave.use(artifacts._get_op("type")().name().unique()) - # return weave.legacy.panels.Card( + # return weave.legacy.weave.panels.Card( # title="Artifacts", # subtitle="", # content=[ - # weave.legacy.panels.CardTab(name=type_name, content=[type_name]) + # weave.legacy.weave.panels.CardTab(name=type_name, content=[type_name]) # for type_name in types_names # ], # ) - return weave.legacy.panels.Table( + return weave.legacy.weave.panels.Table( self.input_node, columns=[ - lambda artifact: weave.legacy.panels.WeaveLink( + lambda artifact: weave.legacy.weave.panels.WeaveLink( artifact._get_op("name")(), vars={ "entity_name": entity_name_op(artifact.project().entity()), "project_name": project_name_op(artifact.project()), "artifact_name": artifact._get_op("name")(), }, - to=lambda input, vars: weave.legacy.ops.project( + to=lambda input, vars: weave.legacy.weave.ops.project( vars["entity_name"], vars["project_name"] ).artifact(vars["artifact_name"]), ), diff --git a/weave/legacy/ecosystem/wandb/weave_plotly.py b/weave/legacy/weave/ecosystem/wandb/weave_plotly.py similarity index 97% rename from weave/legacy/ecosystem/wandb/weave_plotly.py rename to weave/legacy/weave/ecosystem/wandb/weave_plotly.py index 0450a9dbd64..31f71fb54d3 100644 --- a/weave/legacy/ecosystem/wandb/weave_plotly.py +++ b/weave/legacy/weave/ecosystem/wandb/weave_plotly.py @@ -9,8 +9,8 @@ from plotly import graph_objs as go import weave -from weave.legacy import weave_internal -from weave.legacy import infer_types +from weave.legacy.weave import weave_internal +from weave.legacy.weave import infer_types class PlotlyType(weave.types.Type): @@ -178,7 +178,7 @@ def bin_center(bin): @weave.op() def plotly_scatter(data: list[ScatterData]) -> plotly.graph_objs.Figure: - from weave.legacy import storage + from weave.legacy.weave import storage data = storage.to_weavejs(data) color = None diff --git a/weave/legacy/ecosystem/xgboost/__init__.py b/weave/legacy/weave/ecosystem/xgboost/__init__.py similarity index 51% rename from weave/legacy/ecosystem/xgboost/__init__.py rename to weave/legacy/weave/ecosystem/xgboost/__init__.py index 361df7d9d64..af07721cc2a 100644 --- a/weave/legacy/ecosystem/xgboost/__init__.py +++ b/weave/legacy/weave/ecosystem/xgboost/__init__.py @@ -1,7 +1,7 @@ -from weave.legacy import context_state as _context +from weave.legacy.weave import context_state as _context _loading_builtins_token = _context.set_loading_built_ins() -from weave.legacy.ecosystem.xgboost.model import * +from weave.legacy.weave.ecosystem.xgboost.model import * _context.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ecosystem/xgboost/model.py b/weave/legacy/weave/ecosystem/xgboost/model.py similarity index 100% rename from weave/legacy/ecosystem/xgboost/model.py rename to weave/legacy/weave/ecosystem/xgboost/model.py diff --git a/weave/legacy/engine_trace.py b/weave/legacy/weave/engine_trace.py similarity index 98% rename from weave/legacy/engine_trace.py rename to weave/legacy/weave/engine_trace.py index 20ce84169e7..b4b6fca18ba 100644 --- a/weave/legacy/engine_trace.py +++ b/weave/legacy/weave/engine_trace.py @@ -18,7 +18,7 @@ import time import typing -from weave.legacy import environment, logs, stream_data_interfaces +from weave.legacy.weave import environment, logs, stream_data_interfaces # Thanks co-pilot! @@ -91,7 +91,7 @@ def current_root_span(self): # type: ignore def weave_trace_stream(): # type: ignore global _weave_trace_stream if _weave_trace_stream is None: - from weave.legacy.wandb_interface.wandb_stream_table import StreamTable + from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable _weave_trace_stream = StreamTable(os.getenv("WEAVE_TRACE_STREAM")) return _weave_trace_stream diff --git a/weave/legacy/environment.py b/weave/legacy/weave/environment.py similarity index 98% rename from weave/legacy/environment.py rename to weave/legacy/weave/environment.py index a2bc2d81852..f9418520a1e 100644 --- a/weave/legacy/environment.py +++ b/weave/legacy/weave/environment.py @@ -10,11 +10,11 @@ import typing from distutils.util import strtobool from urllib.parse import urlparse -from weave.legacy import errors -from weave.legacy import util +from weave.legacy.weave import errors +from weave.legacy.weave import util if typing.TYPE_CHECKING: - from weave.legacy import logs + from weave.legacy.weave import logs WANDB_ERROR_REPORTING = "WANDB_ERROR_REPORTING" WEAVE_USAGE_ANALYTICS = "WEAVE_USAGE_ANALYTICS" @@ -105,7 +105,7 @@ def is_public() -> bool: def weave_log_format(default: "logs.LogFormat") -> "logs.LogFormat": - from weave.legacy.logs import LogFormat + from weave.legacy.weave.logs import LogFormat return LogFormat(os.getenv("WEAVE_LOG_FORMAT", default)) diff --git a/weave/legacy/errors.py b/weave/legacy/weave/errors.py similarity index 100% rename from weave/legacy/errors.py rename to weave/legacy/weave/errors.py diff --git a/weave/legacy/execute.py b/weave/legacy/weave/execute.py similarity index 99% rename from weave/legacy/execute.py rename to weave/legacy/weave/execute.py index b4cc7a81570..42ff32ba230 100644 --- a/weave/legacy/execute.py +++ b/weave/legacy/weave/execute.py @@ -9,7 +9,7 @@ import typing from collections.abc import Mapping -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types # Configuration # Libraries @@ -17,7 +17,7 @@ # Ops # Trace / cache # Language Features -from weave.legacy import ( +from weave.legacy.weave import ( errors, engine_trace, environment, @@ -42,7 +42,7 @@ value_or_error, wandb_api, ) -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.language_features.tagging import ( opdef_util, process_opdef_resolve_fn, tag_store, diff --git a/weave/legacy/execute_fast.py b/weave/legacy/weave/execute_fast.py similarity index 97% rename from weave/legacy/execute_fast.py rename to weave/legacy/weave/execute_fast.py index f3069ffa886..b1bb0f2b87d 100644 --- a/weave/legacy/execute_fast.py +++ b/weave/legacy/weave/execute_fast.py @@ -1,7 +1,7 @@ import logging -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( weave_internal, errors, engine_trace, @@ -14,7 +14,7 @@ language_nullability, op_policy, ) -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave.language_features.tagging import tag_store # from . import object_context diff --git a/weave/legacy/file_base.py b/weave/legacy/weave/file_base.py similarity index 98% rename from weave/legacy/file_base.py rename to weave/legacy/weave/file_base.py index ff03c072471..835d7e1a2f6 100644 --- a/weave/legacy/file_base.py +++ b/weave/legacy/weave/file_base.py @@ -2,7 +2,7 @@ import dataclasses import typing -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types TRACE_EXT = "trace.json" diff --git a/weave/legacy/file_local.py b/weave/legacy/weave/file_local.py similarity index 98% rename from weave/legacy/file_local.py rename to weave/legacy/weave/file_local.py index e20a0a2248b..b68d0cd5c83 100644 --- a/weave/legacy/file_local.py +++ b/weave/legacy/weave/file_local.py @@ -2,7 +2,7 @@ import os import typing -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types from . import file_base, file_util diff --git a/weave/legacy/file_util.py b/weave/legacy/weave/file_util.py similarity index 88% rename from weave/legacy/file_util.py rename to weave/legacy/weave/file_util.py index 31da6fc6647..3946e1e5d57 100644 --- a/weave/legacy/file_util.py +++ b/weave/legacy/weave/file_util.py @@ -2,8 +2,8 @@ import pathlib import typing -from weave.legacy import filesystem -from weave.legacy import cache, environment, path_util +from weave.legacy.weave import filesystem +from weave.legacy.weave import cache, environment, path_util def get_allowed_dir() -> pathlib.Path: diff --git a/weave/legacy/filesystem.py b/weave/legacy/weave/filesystem.py similarity index 98% rename from weave/legacy/filesystem.py rename to weave/legacy/weave/filesystem.py index 559ecaa1bd8..c812090c67a 100644 --- a/weave/legacy/filesystem.py +++ b/weave/legacy/weave/filesystem.py @@ -20,7 +20,7 @@ from aiofiles.threadpool import binary as aiofiles_binary from aiofiles.threadpool import text as aiofiles_text -from weave.legacy import cache, engine_trace, environment, errors, util +from weave.legacy.weave import cache, engine_trace, environment, errors, util tracer = engine_trace.tracer() # type: ignore async_utime = aiofiles_os.wrap(os.utime) # type: ignore diff --git a/weave/legacy/forward_graph.py b/weave/legacy/weave/forward_graph.py similarity index 98% rename from weave/legacy/forward_graph.py rename to weave/legacy/weave/forward_graph.py index 7f3801e3024..e22ea9dfdfc 100644 --- a/weave/legacy/forward_graph.py +++ b/weave/legacy/weave/forward_graph.py @@ -3,9 +3,9 @@ import contextvars import typing -from weave.legacy import graph +from weave.legacy.weave import graph -from weave.legacy import errors +from weave.legacy.weave import errors ExecutableNode = typing.Union[graph.OutputNode, graph.ConstNode] diff --git a/weave/legacy/gql_json_cache.py b/weave/legacy/weave/gql_json_cache.py similarity index 100% rename from weave/legacy/gql_json_cache.py rename to weave/legacy/weave/gql_json_cache.py diff --git a/weave/legacy/gql_op_plugin.py b/weave/legacy/weave/gql_op_plugin.py similarity index 95% rename from weave/legacy/gql_op_plugin.py rename to weave/legacy/weave/gql_op_plugin.py index 090a1536c40..1b2af19c287 100644 --- a/weave/legacy/gql_op_plugin.py +++ b/weave/legacy/weave/gql_op_plugin.py @@ -1,8 +1,8 @@ import typing from dataclasses import dataclass -from weave.legacy import weave_types as types -from weave.legacy import input_provider, op_args, op_def +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import input_provider, op_args, op_def # A GQLOutputTypeFn is a function that is called during the refinement phase of the compile pass # to propagate the GQL keys of a node's input types to its output type. diff --git a/weave/legacy/gql_schema.py b/weave/legacy/weave/gql_schema.py similarity index 87% rename from weave/legacy/gql_schema.py rename to weave/legacy/weave/gql_schema.py index 6ee16b3cd7b..eb3181d3be1 100644 --- a/weave/legacy/gql_schema.py +++ b/weave/legacy/weave/gql_schema.py @@ -2,8 +2,8 @@ import graphql -from weave.legacy import environment -from weave.legacy import wandb_client_api +from weave.legacy.weave import environment +from weave.legacy.weave import wandb_client_api _GQL_SCHEMA_CACHE: dict[typing.Optional[str], graphql.GraphQLSchema] = {} diff --git a/weave/legacy/gql_to_weave.py b/weave/legacy/weave/gql_to_weave.py similarity index 97% rename from weave/legacy/gql_to_weave.py rename to weave/legacy/weave/gql_to_weave.py index 117e3511dea..0dae36abee8 100644 --- a/weave/legacy/gql_to_weave.py +++ b/weave/legacy/weave/gql_to_weave.py @@ -17,9 +17,9 @@ parse, ) -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import gql_schema +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import gql_schema def get_outermost_alias(query_str: str) -> str: diff --git a/weave/legacy/graph.py b/weave/legacy/weave/graph.py similarity index 98% rename from weave/legacy/graph.py rename to weave/legacy/weave/graph.py index 9517b687b34..71474069a46 100644 --- a/weave/legacy/graph.py +++ b/weave/legacy/weave/graph.py @@ -2,11 +2,11 @@ import json import typing -from weave.legacy import weave_types -from weave.legacy import uris, errors, storage +from weave.legacy.weave import weave_types +from weave.legacy.weave import uris, errors, storage if typing.TYPE_CHECKING: - from weave.legacy import weave_inspector + from weave.legacy.weave import weave_inspector T = typing.TypeVar("T") @@ -39,7 +39,7 @@ def to_json(self) -> dict: def _inspect(self) -> "weave_inspector.NodeInspector": """Only intended to be used by developers to help debug the graph.""" # Circular import, so we do it here. - from weave.legacy import weave_inspector + from weave.legacy.weave import weave_inspector return weave_inspector.NodeInspector(self) @@ -187,7 +187,7 @@ def __init__(self, type: weave_types.Type, val: typing.Any) -> None: @classmethod def from_json(cls, obj: dict) -> "ConstNode": - from weave.legacy import dispatch + from weave.legacy.weave import dispatch val = obj["val"] if isinstance(val, dict) and "nodeType" in val: @@ -239,7 +239,7 @@ def op_full_name(op: Op) -> str: def node_expr_str(node: Node) -> str: - from weave.legacy import partial_object + from weave.legacy.weave import partial_object if isinstance(node, OutputNode): param_names = list(node.from_op.inputs.keys()) diff --git a/weave/legacy/graph_debug.py b/weave/legacy/weave/graph_debug.py similarity index 98% rename from weave/legacy/graph_debug.py rename to weave/legacy/weave/graph_debug.py index d82a7353661..539ba38b8af 100644 --- a/weave/legacy/graph_debug.py +++ b/weave/legacy/weave/graph_debug.py @@ -2,8 +2,8 @@ import textwrap import typing -from weave.legacy import weave_types as types -from weave.legacy import forward_graph, graph +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import forward_graph, graph class _CombinedConstVal: @@ -123,7 +123,7 @@ def node_expr_str_full(node: graph.Node) -> str: This function is a copy/modification of of node_expr_str. """ - from weave.legacy import partial_object + from weave.legacy.weave import partial_object if isinstance(node, graph.OutputNode): if node.from_op.name == "dict": diff --git a/weave/legacy/graph_mapper.py b/weave/legacy/weave/graph_mapper.py similarity index 75% rename from weave/legacy/graph_mapper.py rename to weave/legacy/weave/graph_mapper.py index 1cf5a6c9e13..fab4bfc6cd2 100644 --- a/weave/legacy/graph_mapper.py +++ b/weave/legacy/weave/graph_mapper.py @@ -1,7 +1,7 @@ -from weave.legacy import ref_base -from weave.legacy import weave_types as types -from weave.legacy import graph, mappers, node_ref -from weave.legacy import mappers_python_def as mappers_python +from weave.legacy.weave import ref_base +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph, mappers, node_ref +from weave.legacy.weave import mappers_python_def as mappers_python class FunctionToPyFunction(mappers.Mapper): diff --git a/weave/legacy/infer_types.py b/weave/legacy/weave/infer_types.py similarity index 97% rename from weave/legacy/infer_types.py rename to weave/legacy/weave/infer_types.py index 9eedc3a4c03..018b485d00f 100644 --- a/weave/legacy/infer_types.py +++ b/weave/legacy/weave/infer_types.py @@ -6,9 +6,9 @@ import typing_extensions -from weave.legacy import errors, graph +from weave.legacy.weave import errors, graph -from weave.legacy import weave_types +from weave.legacy.weave import weave_types class TypedDictLike: diff --git a/weave/legacy/input_provider.py b/weave/legacy/weave/input_provider.py similarity index 100% rename from weave/legacy/input_provider.py rename to weave/legacy/weave/input_provider.py diff --git a/weave/legacy/io_service.py b/weave/legacy/weave/io_service.py similarity index 99% rename from weave/legacy/io_service.py rename to weave/legacy/weave/io_service.py index 9091573a914..8e19392040b 100644 --- a/weave/legacy/io_service.py +++ b/weave/legacy/weave/io_service.py @@ -22,7 +22,7 @@ import aioprocessing -from weave.legacy import ( +from weave.legacy.weave import ( weave_http, filesystem, errors, diff --git a/weave/legacy/language_autocall.py b/weave/legacy/weave/language_autocall.py similarity index 93% rename from weave/legacy/language_autocall.py rename to weave/legacy/weave/language_autocall.py index 73822279ed7..97f87e7f01b 100644 --- a/weave/legacy/language_autocall.py +++ b/weave/legacy/weave/language_autocall.py @@ -15,8 +15,8 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import op_args +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import op_args def update_input_types( diff --git a/weave/legacy/language_features/tagging/__init__.py b/weave/legacy/weave/language_features/__init__.py similarity index 100% rename from weave/legacy/language_features/tagging/__init__.py rename to weave/legacy/weave/language_features/__init__.py diff --git a/weave/legacy/ops_domain/run_history/__init__.py b/weave/legacy/weave/language_features/tagging/__init__.py similarity index 100% rename from weave/legacy/ops_domain/run_history/__init__.py rename to weave/legacy/weave/language_features/tagging/__init__.py diff --git a/weave/legacy/language_features/tagging/is_tag_getter.py b/weave/legacy/weave/language_features/tagging/is_tag_getter.py similarity index 84% rename from weave/legacy/language_features/tagging/is_tag_getter.py rename to weave/legacy/weave/language_features/tagging/is_tag_getter.py index d4d6b83c401..ccc4969f256 100644 --- a/weave/legacy/language_features/tagging/is_tag_getter.py +++ b/weave/legacy/weave/language_features/tagging/is_tag_getter.py @@ -1,11 +1,11 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import op_args -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import op_args +from weave.legacy.weave.language_features.tagging import tagged_value_type if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef # This is a heuristic that is used to determine if an op is a tag getter. diff --git a/weave/legacy/language_features/tagging/make_tag_getter_op.py b/weave/legacy/weave/language_features/tagging/make_tag_getter_op.py similarity index 87% rename from weave/legacy/language_features/tagging/make_tag_getter_op.py rename to weave/legacy/weave/language_features/tagging/make_tag_getter_op.py index 9cb06bd9f2f..cd807c2c2ac 100644 --- a/weave/legacy/language_features/tagging/make_tag_getter_op.py +++ b/weave/legacy/weave/language_features/tagging/make_tag_getter_op.py @@ -1,12 +1,12 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import context_state as _context_state -from weave.legacy import decorator_op -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import context_state as _context_state +from weave.legacy.weave import decorator_op +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef def make_tag_getter_op( @@ -26,7 +26,7 @@ def make_tag_getter_op( Returns: The op. """ - from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType + from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType _loading_builtins_token = _context_state.set_loading_built_ins() diff --git a/weave/legacy/language_features/tagging/opdef_util.py b/weave/legacy/weave/language_features/tagging/opdef_util.py similarity index 91% rename from weave/legacy/language_features/tagging/opdef_util.py rename to weave/legacy/weave/language_features/tagging/opdef_util.py index f52a748414b..2e5dee35dc4 100644 --- a/weave/legacy/language_features/tagging/opdef_util.py +++ b/weave/legacy/weave/language_features/tagging/opdef_util.py @@ -1,10 +1,10 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import tagged_value_type if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef # Determines if the op_def should ftag the output with the input. Currently, this is true diff --git a/weave/legacy/language_features/tagging/process_opdef_output_type.py b/weave/legacy/weave/language_features/tagging/process_opdef_output_type.py similarity index 85% rename from weave/legacy/language_features/tagging/process_opdef_output_type.py rename to weave/legacy/weave/language_features/tagging/process_opdef_output_type.py index b9e1a65b4cc..cc9c40913a6 100644 --- a/weave/legacy/language_features/tagging/process_opdef_output_type.py +++ b/weave/legacy/weave/language_features/tagging/process_opdef_output_type.py @@ -5,21 +5,21 @@ import typing -from weave.legacy import registry_mem -from weave.legacy import weave_types as types -from weave.legacy import graph -from weave.legacy.language_features.tagging.opdef_util import ( +from weave.legacy.weave import registry_mem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph +from weave.legacy.weave.language_features.tagging.opdef_util import ( should_flow_tags, should_tag_op_def_outputs, ) -from weave.legacy.language_features.tagging.tagging_op_logic import ( +from weave.legacy.weave.language_features.tagging.tagging_op_logic import ( op_get_tag_type_resolver, op_make_type_key_tag_resolver, op_make_type_tagged_resolver, ) if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef # The following 3 functions are used to get the ops without introducing circular references diff --git a/weave/legacy/language_features/tagging/process_opdef_resolve_fn.py b/weave/legacy/weave/language_features/tagging/process_opdef_resolve_fn.py similarity index 91% rename from weave/legacy/language_features/tagging/process_opdef_resolve_fn.py rename to weave/legacy/weave/language_features/tagging/process_opdef_resolve_fn.py index 720c8db3931..86e8275df33 100644 --- a/weave/legacy/language_features/tagging/process_opdef_resolve_fn.py +++ b/weave/legacy/weave/language_features/tagging/process_opdef_resolve_fn.py @@ -11,20 +11,20 @@ import typing_extensions from pyarrow import compute as pc -from weave.legacy import weave_types as types -from weave.legacy import box -from weave.legacy.arrow.arrow_tags import awl_add_arrow_tags -from weave.legacy.arrow.list_ import ArrowWeaveList -from weave.legacy.language_features.tagging import tag_store -from weave.legacy.language_features.tagging.opdef_util import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box +from weave.legacy.weave.arrow.arrow_tags import awl_add_arrow_tags +from weave.legacy.weave.arrow.list_ import ArrowWeaveList +from weave.legacy.weave.language_features.tagging import tag_store +from weave.legacy.weave.language_features.tagging.opdef_util import ( get_first_arg, should_flow_tags, should_tag_op_def_outputs, ) -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef def _is_tagged_value(val: types.Type) -> typing_extensions.TypeGuard[TaggedValueType]: @@ -38,7 +38,7 @@ def _is_optional_tagged_value( def _strip_tags(val: typing.Any) -> typing.Any: - from weave.legacy.ops_arrow import ArrowWeaveList + from weave.legacy.weave.ops_arrow import ArrowWeaveList if isinstance(val, ArrowWeaveList): if _is_tagged_value(val.object_type): diff --git a/weave/legacy/language_features/tagging/tag_store.py b/weave/legacy/weave/language_features/tagging/tag_store.py similarity index 98% rename from weave/legacy/language_features/tagging/tag_store.py rename to weave/legacy/weave/language_features/tagging/tag_store.py index f6fea94eb79..577a446cb8c 100644 --- a/weave/legacy/language_features/tagging/tag_store.py +++ b/weave/legacy/weave/language_features/tagging/tag_store.py @@ -22,9 +22,9 @@ from collections import defaultdict from contextlib import contextmanager -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import engine_trace, box +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import engine_trace, box statsd = engine_trace.statsd() # type: ignore diff --git a/weave/legacy/language_features/tagging/tagged_value_type.py b/weave/legacy/weave/language_features/tagging/tagged_value_type.py similarity index 98% rename from weave/legacy/language_features/tagging/tagged_value_type.py rename to weave/legacy/weave/language_features/tagging/tagged_value_type.py index 9ae900a2835..3a95169fba0 100644 --- a/weave/legacy/language_features/tagging/tagged_value_type.py +++ b/weave/legacy/weave/language_features/tagging/tagged_value_type.py @@ -30,13 +30,13 @@ import json import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import box, mappers, mappers_python -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box, mappers, mappers_python +from weave.legacy.weave.language_features.tagging import tag_store if typing.TYPE_CHECKING: - from weave.legacy import artifact_base, artifact_fs + from weave.legacy.weave import artifact_base, artifact_fs def flatten_tag_type_to_typed_dict(tag_type: types.Type) -> types.TypedDict: diff --git a/weave/legacy/language_features/tagging/tagged_value_type_helpers.py b/weave/legacy/weave/language_features/tagging/tagged_value_type_helpers.py similarity index 94% rename from weave/legacy/language_features/tagging/tagged_value_type_helpers.py rename to weave/legacy/weave/language_features/tagging/tagged_value_type_helpers.py index a5b1a83f446..2e3ab0b656c 100644 --- a/weave/legacy/language_features/tagging/tagged_value_type_helpers.py +++ b/weave/legacy/weave/language_features/tagging/tagged_value_type_helpers.py @@ -1,7 +1,7 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import tagged_value_type def push_down_tags_from_container_type_to_element_type( diff --git a/weave/legacy/language_features/tagging/tagging_op_logic.py b/weave/legacy/weave/language_features/tagging/tagging_op_logic.py similarity index 86% rename from weave/legacy/language_features/tagging/tagging_op_logic.py rename to weave/legacy/weave/language_features/tagging/tagging_op_logic.py index 2c2bcc04138..1d88dde0491 100644 --- a/weave/legacy/language_features/tagging/tagging_op_logic.py +++ b/weave/legacy/weave/language_features/tagging/tagging_op_logic.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType def op_get_tag_type_resolver(obj_type: types.Type) -> types.Type: diff --git a/weave/legacy/language_features/tagging/tagging_ops.py b/weave/legacy/weave/language_features/tagging/tagging_ops.py similarity index 84% rename from weave/legacy/language_features/tagging/tagging_ops.py rename to weave/legacy/weave/language_features/tagging/tagging_ops.py index abb6dfeb9aa..36e7e553d8b 100644 --- a/weave/legacy/language_features/tagging/tagging_ops.py +++ b/weave/legacy/weave/language_features/tagging/tagging_ops.py @@ -1,6 +1,6 @@ -from weave.legacy import weave_types as types -from weave.legacy import decorator_op -from weave.legacy.language_features.tagging import tagging_op_logic +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import decorator_op +from weave.legacy.weave.language_features.tagging import tagging_op_logic @decorator_op.op( diff --git a/weave/legacy/language_nullability.py b/weave/legacy/weave/language_nullability.py similarity index 83% rename from weave/legacy/language_nullability.py rename to weave/legacy/weave/language_nullability.py index 2541721b19a..c5ef29999cd 100644 --- a/weave/legacy/language_nullability.py +++ b/weave/legacy/weave/language_nullability.py @@ -1,11 +1,11 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import box -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box +from weave.legacy.weave.language_features.tagging import tagged_value_type if typing.TYPE_CHECKING: - from weave.legacy import op_def as OpDef + from weave.legacy.weave import op_def as OpDef def should_force_none_result( diff --git a/weave/legacy/logs.py b/weave/legacy/weave/logs.py similarity index 99% rename from weave/legacy/logs.py rename to weave/legacy/weave/logs.py index 30c3127834b..a28d7c2bbf3 100644 --- a/weave/legacy/logs.py +++ b/weave/legacy/weave/logs.py @@ -12,7 +12,7 @@ from pythonjsonlogger import jsonlogger -from weave.legacy import environment +from weave.legacy.weave import environment try: from flask.logging import wsgi_errors_stream diff --git a/weave/legacy/make_type.py b/weave/legacy/weave/make_type.py similarity index 81% rename from weave/legacy/make_type.py rename to weave/legacy/weave/make_type.py index 3a39f8aa14d..69a8eb786a7 100644 --- a/weave/legacy/make_type.py +++ b/weave/legacy/weave/make_type.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types # TODO: Consider if this should accept a *args, **kwargs? diff --git a/weave/legacy/mappers.py b/weave/legacy/weave/mappers.py similarity index 81% rename from weave/legacy/mappers.py rename to weave/legacy/weave/mappers.py index b7a25b0e605..28e4d4a785c 100644 --- a/weave/legacy/mappers.py +++ b/weave/legacy/weave/mappers.py @@ -1,8 +1,8 @@ import typing if typing.TYPE_CHECKING: - from weave.legacy import weave_types as types - from weave.legacy import artifact_local + from weave.legacy.weave import weave_types as types + from weave.legacy.weave import artifact_local class Mapper: diff --git a/weave/legacy/mappers_arrow.py b/weave/legacy/weave/mappers_arrow.py similarity index 98% rename from weave/legacy/mappers_arrow.py rename to weave/legacy/weave/mappers_arrow.py index f74e1ca2ba5..35d5cca157d 100644 --- a/weave/legacy/mappers_arrow.py +++ b/weave/legacy/weave/mappers_arrow.py @@ -5,9 +5,9 @@ import pyarrow as pa -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( arrow_util, artifact_base, box, @@ -17,9 +17,9 @@ partial_object, ref_base, ) -from weave.legacy import mappers_python_def as mappers_python -from weave.legacy.arrow import arrow -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import mappers_python_def as mappers_python +from weave.legacy.weave.arrow import arrow +from weave.legacy.weave.language_features.tagging import tagged_value_type _in_tagging_context = contextvars.ContextVar("in_tagging_context", default=False) diff --git a/weave/legacy/mappers_gql.py b/weave/legacy/weave/mappers_gql.py similarity index 93% rename from weave/legacy/mappers_gql.py rename to weave/legacy/weave/mappers_gql.py index 1f7da87687e..4abfbcc62b5 100644 --- a/weave/legacy/mappers_gql.py +++ b/weave/legacy/weave/mappers_gql.py @@ -1,10 +1,10 @@ import json -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import gql_json_cache, mappers -from weave.legacy.mappers_weave import UnionMapper -from weave.legacy.partial_object import PartialObjectType +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import gql_json_cache, mappers +from weave.legacy.weave.mappers_weave import UnionMapper +from weave.legacy.weave.partial_object import PartialObjectType from .mappers_python_def import ( BoolToPyBool, diff --git a/weave/legacy/mappers_publisher.py b/weave/legacy/weave/mappers_publisher.py similarity index 92% rename from weave/legacy/mappers_publisher.py rename to weave/legacy/weave/mappers_publisher.py index 7332f22c16a..2a53e30e3fa 100644 --- a/weave/legacy/mappers_publisher.py +++ b/weave/legacy/weave/mappers_publisher.py @@ -2,13 +2,13 @@ import dataclasses import typing -from weave.legacy import storage -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy.node_ref import ref_to_node +from weave.legacy.weave import storage +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.node_ref import ref_to_node -# from weave.legacy.ops_primitives import weave_api -from weave.legacy import ( +# from weave.legacy.weave.ops_primitives import weave_api +from weave.legacy.weave import ( artifact_local, box, context, @@ -19,9 +19,9 @@ ref_base, errors, ) -from weave.legacy.artifact_wandb import likely_commit_hash -from weave.legacy.language_features.tagging import tag_store, tagged_value_type -from weave.legacy.uris import WeaveURI +from weave.legacy.weave.artifact_wandb import likely_commit_hash +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave.uris import WeaveURI class RefToPyRef(mappers.Mapper): @@ -128,7 +128,7 @@ def map_to_python_remote_(type, mapper, artifact, path=[], mapper_options=None): def _node_publish_mapper(node: graph.Node) -> typing.Optional[graph.Node]: - from weave.legacy.ops_primitives import weave_api + from weave.legacy.weave.ops_primitives import weave_api if _node_is_op_get(node): node = typing.cast(graph.OutputNode, node) diff --git a/weave/legacy/mappers_python.py b/weave/legacy/weave/mappers_python.py similarity index 85% rename from weave/legacy/mappers_python.py rename to weave/legacy/weave/mappers_python.py index ad3f240eb35..30b41aee953 100644 --- a/weave/legacy/mappers_python.py +++ b/weave/legacy/weave/mappers_python.py @@ -3,8 +3,8 @@ import typing if typing.TYPE_CHECKING: - from weave.legacy import weave_types - from weave.legacy import artifact_base, mappers + from weave.legacy.weave import weave_types + from weave.legacy.weave import artifact_base, mappers def map_to_python( # type: ignore[empty-body] diff --git a/weave/legacy/mappers_python_def.py b/weave/legacy/weave/mappers_python_def.py similarity index 97% rename from weave/legacy/mappers_python_def.py rename to weave/legacy/weave/mappers_python_def.py index c50bdc648ac..7951b462060 100644 --- a/weave/legacy/mappers_python_def.py +++ b/weave/legacy/weave/mappers_python_def.py @@ -4,9 +4,9 @@ import math import typing -from weave.legacy import storage -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( artifact_fs, box, mappers, @@ -16,9 +16,9 @@ val_const, errors, ) -from weave.legacy import timestamp as weave_timestamp -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.partial_object import PartialObject, PartialObjectType +from weave.legacy.weave import timestamp as weave_timestamp +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.partial_object import PartialObject, PartialObjectType from weave.trace.client_context import weave_client as weave_client_context @@ -61,7 +61,7 @@ def apply(self, obj): class ObjectDictToObject(mappers_weave.ObjectMapper): def apply(self, obj): - from weave.legacy.op_def_type import OpDefType + from weave.legacy.weave.op_def_type import OpDefType # Only add keys that are accepted by the constructor. # This is used for Panels where we have an Class-level id attribute @@ -333,7 +333,7 @@ def __init__( self._use_stable_refs = use_stable_refs def apply(self, obj): - from weave.legacy import op_def + from weave.legacy.weave import op_def try: return self.type.instance_to_dict(obj) diff --git a/weave/legacy/mappers_weave.py b/weave/legacy/weave/mappers_weave.py similarity index 95% rename from weave/legacy/mappers_weave.py rename to weave/legacy/weave/mappers_weave.py index 133e1456cce..e33916f7670 100644 --- a/weave/legacy/mappers_weave.py +++ b/weave/legacy/weave/mappers_weave.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import mappers -from weave.legacy.partial_object import PartialObjectType +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import mappers +from weave.legacy.weave.partial_object import PartialObjectType class TypedDictMapper(mappers.Mapper): diff --git a/weave/legacy/memo.py b/weave/legacy/weave/memo.py similarity index 97% rename from weave/legacy/memo.py rename to weave/legacy/weave/memo.py index 4928bb9b062..379a76fd035 100644 --- a/weave/legacy/memo.py +++ b/weave/legacy/weave/memo.py @@ -2,7 +2,7 @@ import contextvars import typing -from weave.legacy import engine_trace +from weave.legacy.weave import engine_trace statsd = engine_trace.statsd() # type: ignore diff --git a/weave/legacy/monitoring/__init__.py b/weave/legacy/weave/monitoring/__init__.py similarity index 58% rename from weave/legacy/monitoring/__init__.py rename to weave/legacy/weave/monitoring/__init__.py index 8a0b5fd944e..e40a17557df 100644 --- a/weave/legacy/monitoring/__init__.py +++ b/weave/legacy/weave/monitoring/__init__.py @@ -2,11 +2,11 @@ # TODO: Notebook Walkthrough -from weave.legacy.monitoring.monitor import ( +from weave.legacy.weave.monitoring.monitor import ( default_monitor, deinit_monitor, init_monitor, ) -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable __all__ = ["StreamTable", "monitor", "openai"] diff --git a/weave/legacy/monitoring/langchain.py b/weave/legacy/weave/monitoring/langchain.py similarity index 94% rename from weave/legacy/monitoring/langchain.py rename to weave/legacy/weave/monitoring/langchain.py index 8e3afddc148..84371bbefee 100644 --- a/weave/legacy/monitoring/langchain.py +++ b/weave/legacy/weave/monitoring/langchain.py @@ -11,8 +11,8 @@ from langchain.callbacks.tracers.schemas import Run from wandb.sdk.data_types import trace_tree -from weave.legacy import stream_data_interfaces -from weave.legacy.monitoring import StreamTable +from weave.legacy.weave import stream_data_interfaces +from weave.legacy.weave.monitoring import StreamTable def _hash_id(s: str) -> str: diff --git a/weave/legacy/monitoring/monitor.py b/weave/legacy/weave/monitoring/monitor.py similarity index 98% rename from weave/legacy/monitoring/monitor.py rename to weave/legacy/weave/monitoring/monitor.py index 518383ab59f..440a72d1291 100644 --- a/weave/legacy/monitoring/monitor.py +++ b/weave/legacy/weave/monitoring/monitor.py @@ -11,13 +11,13 @@ import typing import uuid -from weave.legacy import errors -from weave.legacy import ( +from weave.legacy.weave import errors +from weave.legacy.weave import ( graph, run_streamtable_span, stream_data_interfaces ) -from weave.legacy.wandb_interface.wandb_stream_table import StreamTable +from weave.legacy.weave.wandb_interface.wandb_stream_table import StreamTable from weave.trace import call_context, context as trace_context from weave.trace.client_context import weave_client as weave_client_context diff --git a/weave/legacy/monitoring/openai/__init__.py b/weave/legacy/weave/monitoring/openai/__init__.py similarity index 100% rename from weave/legacy/monitoring/openai/__init__.py rename to weave/legacy/weave/monitoring/openai/__init__.py diff --git a/weave/legacy/monitoring/openai/models.py b/weave/legacy/weave/monitoring/openai/models.py similarity index 90% rename from weave/legacy/monitoring/openai/models.py rename to weave/legacy/weave/monitoring/openai/models.py index d9688f8b281..479d497c189 100644 --- a/weave/legacy/monitoring/openai/models.py +++ b/weave/legacy/weave/monitoring/openai/models.py @@ -7,8 +7,8 @@ ) from pydantic import BaseModel, Field -from weave.legacy import weave_types as types -from weave.legacy.monitoring import monitor +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.monitoring import monitor class ModelTokensConfig(BaseModel): diff --git a/weave/legacy/monitoring/openai/openai.py b/weave/legacy/weave/monitoring/openai/openai.py similarity index 98% rename from weave/legacy/monitoring/openai/openai.py rename to weave/legacy/weave/monitoring/openai/openai.py index f41f551d994..1e2b99a3951 100644 --- a/weave/legacy/monitoring/openai/openai.py +++ b/weave/legacy/weave/monitoring/openai/openai.py @@ -12,9 +12,9 @@ from packaging import version from weave.trace import call_context from weave.trace.client_context import weave_client as weave_client_context -from weave.legacy.monitoring.monitor import _get_global_monitor -from weave.legacy.monitoring.openai.models import * -from weave.legacy.monitoring.openai.util import * +from weave.legacy.weave.monitoring.monitor import _get_global_monitor +from weave.legacy.weave.monitoring.openai.models import * +from weave.legacy.weave.monitoring.openai.util import * from weave.trace.op import Op from weave.trace.op import op as op_deco diff --git a/weave/legacy/monitoring/openai/util.py b/weave/legacy/weave/monitoring/openai/util.py similarity index 98% rename from weave/legacy/monitoring/openai/util.py rename to weave/legacy/weave/monitoring/openai/util.py index e8772fa289d..e20f2fddbb0 100644 --- a/weave/legacy/monitoring/openai/util.py +++ b/weave/legacy/weave/monitoring/openai/util.py @@ -8,7 +8,7 @@ from openai.types.chat.chat_completion_chunk import ChatCompletionChunk, Choice from openai.types.completion_usage import CompletionUsage -from weave.legacy.monitoring.openai.models import * +from weave.legacy.weave.monitoring.openai.models import * T = TypeVar("T") diff --git a/weave/legacy/node_ref.py b/weave/legacy/weave/node_ref.py similarity index 97% rename from weave/legacy/node_ref.py rename to weave/legacy/weave/node_ref.py index 47edbc6804b..2a130af4749 100644 --- a/weave/legacy/node_ref.py +++ b/weave/legacy/weave/node_ref.py @@ -1,7 +1,7 @@ import copy import typing -from weave.legacy import graph +from weave.legacy.weave import graph from . import ref_base @@ -88,7 +88,7 @@ def ref_to_node(ref: ref_base.Ref) -> typing.Optional[graph.Node]: ref = copy.copy(ref) ref.extra = [] - from weave.legacy.ops_primitives import get + from weave.legacy.weave.ops_primitives import get node = get(str(ref)) for str_key in extra: diff --git a/weave/legacy/object_context.py b/weave/legacy/weave/object_context.py similarity index 95% rename from weave/legacy/object_context.py rename to weave/legacy/weave/object_context.py index 3ed119fa154..d062a02652c 100644 --- a/weave/legacy/object_context.py +++ b/weave/legacy/weave/object_context.py @@ -12,9 +12,9 @@ from typing import Any if typing.TYPE_CHECKING: - from weave.legacy import ref_base -from weave.legacy import weave_types as types -from weave.legacy import box, uris + from weave.legacy.weave import ref_base +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box, uris @dataclasses.dataclass @@ -90,7 +90,7 @@ def add_mutation( self.objects[target_uri].mutations.append(mutation) def finish_mutation(self, target_uri: str) -> None: - from weave.legacy import artifact_fs, artifact_local, artifact_wandb + from weave.legacy.weave import artifact_fs, artifact_local, artifact_wandb target_record = self.objects.get(target_uri) if target_record is None: @@ -115,7 +115,7 @@ def finish_mutation(self, target_uri: str) -> None: if target_branch is None: raise ValueError("No branch to finish mutation on") - from weave.legacy import artifact_wandb + from weave.legacy.weave import artifact_wandb # Hack: if the target branch looks like a commit hash, then we # don't want to use it as a branch - this is the case that we are diff --git a/weave/legacy/object_type_ref_util.py b/weave/legacy/weave/object_type_ref_util.py similarity index 97% rename from weave/legacy/object_type_ref_util.py rename to weave/legacy/weave/object_type_ref_util.py index 53787c4c454..d0bc6a1ee35 100644 --- a/weave/legacy/object_type_ref_util.py +++ b/weave/legacy/weave/object_type_ref_util.py @@ -1,7 +1,7 @@ import typing from weave.trace import ref_util -from weave.legacy import context_state +from weave.legacy.weave import context_state def make_object_getattribute( diff --git a/weave/legacy/op_aliases.py b/weave/legacy/weave/op_aliases.py similarity index 100% rename from weave/legacy/op_aliases.py rename to weave/legacy/weave/op_aliases.py diff --git a/weave/legacy/op_args.py b/weave/legacy/weave/op_args.py similarity index 98% rename from weave/legacy/op_args.py rename to weave/legacy/weave/op_args.py index 15fae9a443b..e0dd2400501 100644 --- a/weave/legacy/op_args.py +++ b/weave/legacy/weave/op_args.py @@ -1,8 +1,8 @@ import typing from dataclasses import dataclass -from weave.legacy import weave_types as types -from weave.legacy import debug_types +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import debug_types BT = typing.TypeVar("BT") diff --git a/weave/legacy/op_def.py b/weave/legacy/weave/op_def.py similarity index 97% rename from weave/legacy/op_def.py rename to weave/legacy/weave/op_def.py index 8fdfd881513..f8e397a375e 100644 --- a/weave/legacy/op_def.py +++ b/weave/legacy/weave/op_def.py @@ -6,8 +6,8 @@ import typing from typing import Iterator, Sequence -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( weave_internal, errors, context_state, @@ -22,18 +22,18 @@ pyfunc_type_util, uris, ) -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.language_features.tagging import ( opdef_util, process_opdef_output_type, process_opdef_resolve_fn, tagged_value_type, ) -from weave.legacy.run import Run -from weave.legacy.weavejs_fixes import fixup_node +from weave.legacy.weave.run import Run +from weave.legacy.weave.weavejs_fixes import fixup_node if typing.TYPE_CHECKING: from weave.trace import weave_client - from weave.legacy.run_streamtable_span import RunStreamTableSpan + from weave.legacy.weave.run_streamtable_span import RunStreamTableSpan _no_refine: contextvars.ContextVar[bool] = contextvars.ContextVar( @@ -289,7 +289,7 @@ def __call__(_self, *args, **kwargs): # convert arguments to Const nodes. There is no type checking. # May need to fix this, but the patterns in test_mutation2 work # now. - from weave.legacy import object_context + from weave.legacy.weave import object_context with object_context.object_context(): return _self.resolve_fn(*args, **kwargs) @@ -313,7 +313,7 @@ def unrefined_output_type_for_params(self, params: typing.Dict[str, graph.Node]) return self.output_type(new_input_type) def is_gql_root_resolver(self): - from weave.legacy import gql_op_plugin + from weave.legacy.weave import gql_op_plugin return self in gql_op_plugin._ROOT_RESOLVERS @@ -356,14 +356,14 @@ def _replace_var_with_val(n): tracer = engine_trace.tracer() # type: ignore with tracer.trace("refine.%s" % _self.uri): # api's use auto-creates client. TODO: Fix inline import - from weave.legacy import api as api + from weave.legacy.weave import api as api final_output_type = api.use(called_refine_output_type) # type: ignore if final_output_type == None: # This can happen due to nullability. In that case, accept the output type is null final_output_type = types.NoneType() # Have to deref if in case a ref came back... - from weave.legacy import storage + from weave.legacy.weave import storage final_output_type = storage.deref(final_output_type) @@ -374,7 +374,7 @@ def _replace_var_with_val(n): ) else: final_output_type = _self.unrefined_output_type_for_params(bound_params) - from weave.legacy import dispatch + from weave.legacy.weave import dispatch return dispatch.RuntimeOutputNode(final_output_type, _self.uri, bound_params) diff --git a/weave/legacy/op_def_type.py b/weave/legacy/weave/op_def_type.py similarity index 99% rename from weave/legacy/op_def_type.py rename to weave/legacy/weave/op_def_type.py index 8fd598aa0ca..472f1207d9f 100644 --- a/weave/legacy/op_def_type.py +++ b/weave/legacy/weave/op_def_type.py @@ -11,9 +11,9 @@ from _ast import AsyncFunctionDef, ExceptHandler from typing import Any -from weave.legacy import storage -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( artifact_fs, artifact_local, context_state, @@ -446,7 +446,7 @@ def load_instance(cls, artifact, name, extra=None): except FileNotFoundError: pass - from weave.legacy import artifact_wandb + from weave.legacy.weave import artifact_wandb is_wandb_artifact = False if isinstance(artifact, artifact_wandb.WandbArtifact): diff --git a/weave/legacy/op_execute.py b/weave/legacy/weave/op_execute.py similarity index 100% rename from weave/legacy/op_execute.py rename to weave/legacy/weave/op_execute.py diff --git a/weave/legacy/op_policy.py b/weave/legacy/weave/op_policy.py similarity index 100% rename from weave/legacy/op_policy.py rename to weave/legacy/weave/op_policy.py diff --git a/weave/legacy/ops.py b/weave/legacy/weave/ops.py similarity index 100% rename from weave/legacy/ops.py rename to weave/legacy/weave/ops.py diff --git a/weave/legacy/weave/ops_arrow/__init__.py b/weave/legacy/weave/ops_arrow/__init__.py new file mode 100644 index 00000000000..980f2fd2ce6 --- /dev/null +++ b/weave/legacy/weave/ops_arrow/__init__.py @@ -0,0 +1,12 @@ +from weave.legacy.weave import context_state as _context_state + +_loading_builtins_token = _context_state.set_loading_built_ins() + +from weave.legacy.weave.arrow.arrow import * +from weave.legacy.weave.arrow.convert import * +from weave.legacy.weave.arrow.list_ import * +from weave.legacy.weave.ops_arrow import ops +from weave.legacy.weave.ops_arrow.list_range import range +from weave.legacy.weave.ops_arrow.vectorize import vectorize + +_context_state.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/ops_arrow/arraylist_ops.py b/weave/legacy/weave/ops_arrow/arraylist_ops.py similarity index 96% rename from weave/legacy/ops_arrow/arraylist_ops.py rename to weave/legacy/weave/ops_arrow/arraylist_ops.py index cd0d4752f94..209cc8b0813 100644 --- a/weave/legacy/ops_arrow/arraylist_ops.py +++ b/weave/legacy/weave/ops_arrow/arraylist_ops.py @@ -3,11 +3,11 @@ import pyarrow as pa from pyarrow import compute as pc -from weave.legacy import weave_types as types -from weave.legacy.arrow.arrow import ArrowWeaveListType, arrow_as_array -from weave.legacy.arrow.list_ import ArrowWeaveList -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType, arrow_as_array +from weave.legacy.weave.arrow.list_ import ArrowWeaveList +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.language_features.tagging import tagged_value_type def _arrowweavelistlist_listindex_output_type(input_types): @@ -70,7 +70,7 @@ def listindex(self, index): def _list_op_output_object_type(input_types): self_type = input_types["self"] - from weave.legacy import op_def + from weave.legacy.weave import op_def def _remove_tags(t): if isinstance(t, tagged_value_type.TaggedValueType): diff --git a/weave/legacy/ops_arrow/boolean.py b/weave/legacy/weave/ops_arrow/boolean.py similarity index 93% rename from weave/legacy/ops_arrow/boolean.py rename to weave/legacy/weave/ops_arrow/boolean.py index 68b09327c61..5131f10544b 100644 --- a/weave/legacy/ops_arrow/boolean.py +++ b/weave/legacy/weave/ops_arrow/boolean.py @@ -1,11 +1,11 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.decorator_op import op -from weave.legacy.ops_arrow import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.ops_arrow import util unary_input_type = { "self": ArrowWeaveListType(types.Boolean()), diff --git a/weave/legacy/ops_arrow/convert_ops.py b/weave/legacy/weave/ops_arrow/convert_ops.py similarity index 71% rename from weave/legacy/ops_arrow/convert_ops.py rename to weave/legacy/weave/ops_arrow/convert_ops.py index b2538128b42..d67eaa348c4 100644 --- a/weave/legacy/ops_arrow/convert_ops.py +++ b/weave/legacy/weave/ops_arrow/convert_ops.py @@ -1,7 +1,7 @@ -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.arrow import convert -from weave.legacy.arrow.arrow import ArrowWeaveListType +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType @op( diff --git a/weave/legacy/ops_arrow/date.py b/weave/legacy/weave/ops_arrow/date.py similarity index 94% rename from weave/legacy/ops_arrow/date.py rename to weave/legacy/weave/ops_arrow/date.py index 9d93ee161d6..ce84f02cb52 100644 --- a/weave/legacy/ops_arrow/date.py +++ b/weave/legacy/weave/ops_arrow/date.py @@ -3,12 +3,12 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import timestamp as weave_timestamp -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.ops_arrow import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import timestamp as weave_timestamp +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.ops_arrow import util ARROW_WEAVE_LIST_TIMESTAMP_TYPE = ArrowWeaveListType(types.Timestamp()) ARROW_WEAVE_LIST_BOOLEAN_TYPE = ArrowWeaveListType(types.Boolean()) diff --git a/weave/legacy/ops_arrow/dict.py b/weave/legacy/weave/ops_arrow/dict.py similarity index 96% rename from weave/legacy/ops_arrow/dict.py rename to weave/legacy/weave/ops_arrow/dict.py index 996ef2e4ffc..883d2295a43 100644 --- a/weave/legacy/ops_arrow/dict.py +++ b/weave/legacy/weave/ops_arrow/dict.py @@ -3,23 +3,23 @@ import numpy as np import pyarrow as pa -from weave.legacy import weave_types as types -from weave.legacy import _dict_utils -from weave.legacy.arrow import convert -from weave.legacy.arrow.arrow import arrow_as_array, offsets_starting_at_zero -from weave.legacy.arrow.arrow_tags import direct_add_arrow_tags -from weave.legacy.arrow.constructors import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import _dict_utils +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.arrow import arrow_as_array, offsets_starting_at_zero +from weave.legacy.weave.arrow.arrow_tags import direct_add_arrow_tags +from weave.legacy.weave.arrow.constructors import ( vectorized_container_constructor_preprocessor, vectorized_input_types, ) -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.decorator_op import op -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.language_features.tagging import ( process_opdef_output_type, ) -from weave.legacy.op_args import OpVarArgs -from weave.legacy.ops_primitives import projection_utils +from weave.legacy.weave.op_args import OpVarArgs +from weave.legacy.weave.ops_primitives import projection_utils def typeddict_pick_output_type(input_types): diff --git a/weave/legacy/ops_arrow/list_join.py b/weave/legacy/weave/ops_arrow/list_join.py similarity index 93% rename from weave/legacy/ops_arrow/list_join.py rename to weave/legacy/weave/ops_arrow/list_join.py index 1f026ab61e9..2ba1ed3f158 100644 --- a/weave/legacy/ops_arrow/list_join.py +++ b/weave/legacy/weave/ops_arrow/list_join.py @@ -4,24 +4,24 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import engine_trace -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import graph -from weave.legacy.arrow import convert -from weave.legacy.arrow.arrow import ( +from weave.legacy.weave import engine_trace +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import graph +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.arrow import ( ArrowWeaveListType, safe_coalesce, ) -from weave.legacy.arrow.arrow_tags import pushdown_list_tags -from weave.legacy.arrow.list_ import ( +from weave.legacy.weave.arrow.arrow_tags import pushdown_list_tags +from weave.legacy.weave.arrow.list_ import ( ArrowWeaveList, awl_zip, make_vec_dict, make_vec_taggedvalue, ) -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.ops_primitives import list_ as primitive_list +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.ops_primitives import list_ as primitive_list tracer = engine_trace.tracer() # type: ignore diff --git a/weave/legacy/ops_arrow/list_ops.py b/weave/legacy/weave/ops_arrow/list_ops.py similarity index 97% rename from weave/legacy/ops_arrow/list_ops.py rename to weave/legacy/weave/ops_arrow/list_ops.py index 1f034738bf9..7fabe4421d4 100644 --- a/weave/legacy/ops_arrow/list_ops.py +++ b/weave/legacy/weave/ops_arrow/list_ops.py @@ -7,34 +7,34 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.api import op, type_of -from weave.legacy import op_args, op_def -from weave.legacy.arrow import arrow_tags, convert -from weave.legacy.arrow.arrow import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, type_of +from weave.legacy.weave import op_args, op_def +from weave.legacy.weave.arrow import arrow_tags, convert +from weave.legacy.weave.arrow.arrow import ( ArrowWeaveListType, arrow_as_array, offsets_starting_at_zero, ) -from weave.legacy.arrow.concat import concatenate_all -from weave.legacy.arrow.constructors import ( +from weave.legacy.weave.arrow.concat import concatenate_all +from weave.legacy.weave.arrow.constructors import ( vectorized_container_constructor_preprocessor, vectorized_input_types, ) -from weave.legacy.arrow.convert import to_compare_safe -from weave.legacy.arrow.list_ import ( +from weave.legacy.weave.arrow.convert import to_compare_safe +from weave.legacy.weave.arrow.list_ import ( ArrowWeaveList, PathType, is_list_arrowweavelist, is_taggedvalue_arrowweavelist, ) -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.language_features.tagging import ( tagged_value_type, tagged_value_type_helpers, ) -from weave.legacy.ops_arrow.vectorize import _apply_fn_node_with_tag_pushdown -from weave.legacy.ops_primitives import list_ as primitive_list +from weave.legacy.weave.ops_arrow.vectorize import _apply_fn_node_with_tag_pushdown +from weave.legacy.weave.ops_primitives import list_ as primitive_list FLATTEN_DELIMITER = "➡️" @@ -961,7 +961,7 @@ def flatten(arr): def _drop_tags_output_type(input_type): - from weave.legacy.op_def import map_type + from weave.legacy.weave.op_def import map_type return map_type( input_type["arr"], diff --git a/weave/legacy/ops_arrow/list_range.py b/weave/legacy/weave/ops_arrow/list_range.py similarity index 61% rename from weave/legacy/ops_arrow/list_range.py rename to weave/legacy/weave/ops_arrow/list_range.py index 064e8e29a22..3783eaa39b7 100644 --- a/weave/legacy/ops_arrow/list_range.py +++ b/weave/legacy/weave/ops_arrow/list_range.py @@ -2,9 +2,9 @@ import pyarrow as pa -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.arrow.list_ import ArrowWeaveList +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.arrow.list_ import ArrowWeaveList py_range = range diff --git a/weave/legacy/ops_arrow/number.py b/weave/legacy/weave/ops_arrow/number.py similarity index 97% rename from weave/legacy/ops_arrow/number.py rename to weave/legacy/weave/ops_arrow/number.py index 071b4d37ec9..2d424fd08e6 100644 --- a/weave/legacy/ops_arrow/number.py +++ b/weave/legacy/weave/ops_arrow/number.py @@ -4,12 +4,12 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import timestamp as weave_timestamp -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.ops_arrow import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import timestamp as weave_timestamp +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.ops_arrow import util ARROW_WEAVE_LIST_NUMBER_TYPE = ArrowWeaveListType(types.Number()) ARROW_WEAVE_LIST_BOOLEAN_TYPE = ArrowWeaveListType(types.Boolean()) diff --git a/weave/legacy/ops_arrow/obj.py b/weave/legacy/weave/ops_arrow/obj.py similarity index 72% rename from weave/legacy/ops_arrow/obj.py rename to weave/legacy/weave/ops_arrow/obj.py index 7e38e90297b..b94fd1ef576 100644 --- a/weave/legacy/ops_arrow/obj.py +++ b/weave/legacy/weave/ops_arrow/obj.py @@ -1,7 +1,7 @@ -from weave.legacy import weave_types as types -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.ops_primitives import obj as primitives_obj +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.ops_primitives import obj as primitives_obj @arrow_op( diff --git a/weave/legacy/weave/ops_arrow/ops.py b/weave/legacy/weave/ops_arrow/ops.py new file mode 100644 index 00000000000..452bebd2a93 --- /dev/null +++ b/weave/legacy/weave/ops_arrow/ops.py @@ -0,0 +1,11 @@ +from weave.legacy.weave.ops_arrow.arraylist_ops import * +from weave.legacy.weave.ops_arrow.boolean import * +from weave.legacy.weave.ops_arrow.convert_ops import * +from weave.legacy.weave.ops_arrow.date import * +from weave.legacy.weave.ops_arrow.dict import * +from weave.legacy.weave.ops_arrow.list_join import * +from weave.legacy.weave.ops_arrow.list_ops import * # type: ignore +from weave.legacy.weave.ops_arrow.number import * +from weave.legacy.weave.ops_arrow.obj import * +from weave.legacy.weave.ops_arrow.ref_ops import * +from weave.legacy.weave.ops_arrow.string import * diff --git a/weave/legacy/ops_arrow/ref_ops.py b/weave/legacy/weave/ops_arrow/ref_ops.py similarity index 83% rename from weave/legacy/ops_arrow/ref_ops.py rename to weave/legacy/weave/ops_arrow/ref_ops.py index d40ee05949c..9ea8f800bab 100644 --- a/weave/legacy/ops_arrow/ref_ops.py +++ b/weave/legacy/weave/ops_arrow/ref_ops.py @@ -1,11 +1,11 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.decorator_op import op -from weave.legacy.ops_arrow import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.ops_arrow import util nullable_binary_input_type = { "self": ArrowWeaveListType(types.optional(types.RefType())), @@ -22,7 +22,7 @@ output_type=ArrowWeaveListType(types.Boolean()), ) def ref_equal(self, other): - from weave.legacy import storage + from weave.legacy.weave import storage # Weave engine automatically derefs, so we need to undo that via # _get_ref here. diff --git a/weave/legacy/ops_arrow/string.py b/weave/legacy/weave/ops_arrow/string.py similarity index 97% rename from weave/legacy/ops_arrow/string.py rename to weave/legacy/weave/ops_arrow/string.py index 491ad640a6e..48a8d3f8c19 100644 --- a/weave/legacy/ops_arrow/string.py +++ b/weave/legacy/weave/ops_arrow/string.py @@ -3,12 +3,12 @@ import pyarrow as pa import pyarrow.compute as pc -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.arrow.arrow import ArrowWeaveListType, offsets_starting_at_zero -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.ops_arrow import util +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType, offsets_starting_at_zero +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.ops_arrow import util ARROW_WEAVE_LIST_STRING_TYPE = ArrowWeaveListType(types.String()) ARROW_WEAVE_LIST_BOOLEAN_TYPE = ArrowWeaveListType(types.Boolean()) diff --git a/weave/legacy/ops_arrow/util.py b/weave/legacy/weave/ops_arrow/util.py similarity index 91% rename from weave/legacy/ops_arrow/util.py rename to weave/legacy/weave/ops_arrow/util.py index 5d515d9984a..97b12773d82 100644 --- a/weave/legacy/ops_arrow/util.py +++ b/weave/legacy/weave/ops_arrow/util.py @@ -3,15 +3,15 @@ import pyarrow as pa from pyarrow import compute as pc -from weave.legacy import weave_types as types -from weave.legacy import graph +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph # Reimplementation of Weave0 `toSafeCall` which # converts media to their digest def _to_compare_safe_call(node: graph.OutputNode) -> graph.OutputNode: - from weave.legacy.ops_domain.wbmedia import ArtifactAssetType - from weave.legacy.ops_primitives.dict import dict_ + from weave.legacy.weave.ops_domain.wbmedia import ArtifactAssetType + from weave.legacy.weave.ops_primitives.dict import dict_ node_type = types.non_none(node.type) if ArtifactAssetType.assign_type(node_type): diff --git a/weave/legacy/ops_arrow/vectorize.py b/weave/legacy/weave/ops_arrow/vectorize.py similarity index 97% rename from weave/legacy/ops_arrow/vectorize.py rename to weave/legacy/weave/ops_arrow/vectorize.py index 002d8c1d86d..fedd1cc5305 100644 --- a/weave/legacy/ops_arrow/vectorize.py +++ b/weave/legacy/weave/ops_arrow/vectorize.py @@ -6,18 +6,18 @@ import numpy as np import pyarrow as pa -from weave.legacy import weave_internal -from weave.legacy import errors,weavify, registry_mem -from weave.legacy import weave_types as types -from weave.legacy.api import op, use -from weave.legacy import dispatch, graph, graph_debug, op_args, op_def -from weave.legacy.arrow import convert -from weave.legacy.arrow.arrow import ArrowWeaveListType -from weave.legacy.arrow.arrow_tags import pushdown_list_tags -from weave.legacy.arrow.list_ import ArrowWeaveList -from weave.legacy.ops_arrow import arraylist_ops, convert_ops -from weave.legacy.ops_arrow.dict import preprocess_merge -from weave.legacy.ops_primitives.dict import dict_ +from weave.legacy.weave import weave_internal +from weave.legacy.weave import errors,weavify, registry_mem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, use +from weave.legacy.weave import dispatch, graph, graph_debug, op_args, op_def +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType +from weave.legacy.weave.arrow.arrow_tags import pushdown_list_tags +from weave.legacy.weave.arrow.list_ import ArrowWeaveList +from weave.legacy.weave.ops_arrow import arraylist_ops, convert_ops +from weave.legacy.weave.ops_arrow.dict import preprocess_merge +from weave.legacy.weave.ops_primitives.dict import dict_ class VectorizeError(errors.WeaveBaseError): @@ -73,7 +73,7 @@ def _create_manually_mapped_op( mapped_inputs = {k: v for k, v in inputs.items() if k in vectorized_keys} rest_inputs = {k: v for k, v in inputs.items() if k not in vectorized_keys} - from weave.legacy.ops_arrow import dict + from weave.legacy.weave.ops_arrow import dict input_arr = dict.arrow_dict_(**mapped_inputs).to_py() @@ -643,7 +643,7 @@ def raise_on_python_bailout(): def _call_and_ensure_awl( awl: ArrowWeaveList, called: graph.OutputNode ) -> ArrowWeaveList: - from weave.legacy import compile + from weave.legacy.weave import compile with compile.disable_compile(): res = use(called) @@ -726,7 +726,7 @@ def _apply_fn_node(awl: ArrowWeaveList, fn: graph.OutputNode) -> ArrowWeaveList: ) return convert.to_arrow([], types.List(fn.type), artifact=awl._artifact) - from weave.legacy import execute_fast + from weave.legacy.weave import execute_fast fn = execute_fast._resolve_static_branches(fn) logging.info("Vectorizing. Static branch resolution complete.: %s", debug_str) diff --git a/weave/legacy/ops_domain/__init__.py b/weave/legacy/weave/ops_domain/__init__.py similarity index 93% rename from weave/legacy/ops_domain/__init__.py rename to weave/legacy/weave/ops_domain/__init__.py index c5760cdd35a..7cdc4cfcefb 100644 --- a/weave/legacy/ops_domain/__init__.py +++ b/weave/legacy/weave/ops_domain/__init__.py @@ -1,4 +1,4 @@ -from weave.legacy import context_state as _context_state +from weave.legacy.weave import context_state as _context_state _loading_builtins_token = _context_state.set_loading_built_ins() diff --git a/weave/legacy/ops_domain/artifact_alias_ops.py b/weave/legacy/weave/ops_domain/artifact_alias_ops.py similarity index 61% rename from weave/legacy/ops_domain/artifact_alias_ops.py rename to weave/legacy/weave/ops_domain/artifact_alias_ops.py index 140ffadfbaf..01acd1d6537 100644 --- a/weave/legacy/ops_domain/artifact_alias_ops.py +++ b/weave/legacy/weave/ops_domain/artifact_alias_ops.py @@ -1,11 +1,11 @@ -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/artifact_collection_ops.py b/weave/legacy/weave/ops_domain/artifact_collection_ops.py similarity index 94% rename from weave/legacy/ops_domain/artifact_collection_ops.py rename to weave/legacy/weave/ops_domain/artifact_collection_ops.py index aaf6231a37e..ccd8dc913ea 100644 --- a/weave/legacy/ops_domain/artifact_collection_ops.py +++ b/weave/legacy/weave/ops_domain/artifact_collection_ops.py @@ -1,12 +1,12 @@ import typing import urllib -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/artifact_membership_ops.py b/weave/legacy/weave/ops_domain/artifact_membership_ops.py similarity index 89% rename from weave/legacy/ops_domain/artifact_membership_ops.py rename to weave/legacy/weave/ops_domain/artifact_membership_ops.py index ab042265304..780ec6f93ac 100644 --- a/weave/legacy/ops_domain/artifact_membership_ops.py +++ b/weave/legacy/weave/ops_domain/artifact_membership_ops.py @@ -1,10 +1,10 @@ import urllib -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_direct_edge_op, gql_prop_op, ) diff --git a/weave/legacy/ops_domain/artifact_type_ops.py b/weave/legacy/weave/ops_domain/artifact_type_ops.py similarity index 89% rename from weave/legacy/ops_domain/artifact_type_ops.py rename to weave/legacy/weave/ops_domain/artifact_type_ops.py index 4b62b70929e..64a81170436 100644 --- a/weave/legacy/ops_domain/artifact_type_ops.py +++ b/weave/legacy/weave/ops_domain/artifact_type_ops.py @@ -1,10 +1,10 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( _make_alias, gql_connection_op, gql_direct_edge_op, diff --git a/weave/legacy/ops_domain/artifact_version_ops.py b/weave/legacy/weave/ops_domain/artifact_version_ops.py similarity index 96% rename from weave/legacy/ops_domain/artifact_version_ops.py rename to weave/legacy/weave/ops_domain/artifact_version_ops.py index 8203e289189..e9b8136c6f3 100644 --- a/weave/legacy/ops_domain/artifact_version_ops.py +++ b/weave/legacy/weave/ops_domain/artifact_version_ops.py @@ -2,14 +2,14 @@ import typing from urllib.parse import quote -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import artifact_fs, artifact_wandb, input_provider -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain import wb_util -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import artifact_fs, artifact_wandb, input_provider +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain import wb_util +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( _make_alias, gql_connection_op, gql_direct_edge_op, @@ -460,10 +460,10 @@ def artifact_version_weave_type( def _get_history_metrics( artifactVersion: wdt.ArtifactVersion, ) -> dict[str, typing.Any]: - from weave.legacy import weave_internal - from weave.legacy.compile import enable_compile - from weave.legacy.graph import ConstNode, OutputNode - from weave.legacy.ops_domain import wb_domain_types + from weave.legacy.weave import weave_internal + from weave.legacy.weave.compile import enable_compile + from weave.legacy.weave.graph import ConstNode, OutputNode + from weave.legacy.weave.ops_domain import wb_domain_types created_by = artifactVersion["createdBy"] if created_by == None or created_by["__typename"] != "Run": diff --git a/weave/legacy/ops_domain/entity_ops.py b/weave/legacy/weave/ops_domain/entity_ops.py similarity index 82% rename from weave/legacy/ops_domain/entity_ops.py rename to weave/legacy/weave/ops_domain/entity_ops.py index 89b45216863..08c433262a9 100644 --- a/weave/legacy/ops_domain/entity_ops.py +++ b/weave/legacy/weave/ops_domain/entity_ops.py @@ -1,11 +1,11 @@ -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/org_ops.py b/weave/legacy/weave/ops_domain/org_ops.py similarity index 85% rename from weave/legacy/ops_domain/org_ops.py rename to weave/legacy/weave/ops_domain/org_ops.py index c0ce7588ba0..867bc2d299a 100644 --- a/weave/legacy/ops_domain/org_ops.py +++ b/weave/legacy/weave/ops_domain/org_ops.py @@ -1,6 +1,6 @@ -from weave.legacy import weave_types as types -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/project_ops.py b/weave/legacy/weave/ops_domain/project_ops.py similarity index 93% rename from weave/legacy/ops_domain/project_ops.py rename to weave/legacy/weave/ops_domain/project_ops.py index b4463818c8a..297fea610b6 100644 --- a/weave/legacy/ops_domain/project_ops.py +++ b/weave/legacy/weave/ops_domain/project_ops.py @@ -1,16 +1,16 @@ import json import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import input_provider -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import input_provider +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/repo_insight_ops.py b/weave/legacy/weave/ops_domain/repo_insight_ops.py similarity index 94% rename from weave/legacy/ops_domain/repo_insight_ops.py rename to weave/legacy/weave/ops_domain/repo_insight_ops.py index 5baa89dec29..3ff32d81e1d 100644 --- a/weave/legacy/ops_domain/repo_insight_ops.py +++ b/weave/legacy/weave/ops_domain/repo_insight_ops.py @@ -1,12 +1,12 @@ import datetime import json -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_json_cache import use_json -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_json_cache import use_json +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( _make_alias, ) diff --git a/weave/legacy/ops_domain/report_ops.py b/weave/legacy/weave/ops_domain/report_ops.py similarity index 91% rename from weave/legacy/ops_domain/report_ops.py rename to weave/legacy/weave/ops_domain/report_ops.py index f0f0db1a61d..011768ac1f9 100644 --- a/weave/legacy/ops_domain/report_ops.py +++ b/weave/legacy/weave/ops_domain/report_ops.py @@ -2,12 +2,12 @@ import typing import urllib -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_direct_edge_op, gql_prop_op, ) diff --git a/weave/legacy/weave/ops_domain/run_history/__init__.py b/weave/legacy/weave/ops_domain/run_history/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/weave/legacy/ops_domain/run_history/context.py b/weave/legacy/weave/ops_domain/run_history/context.py similarity index 100% rename from weave/legacy/ops_domain/run_history/context.py rename to weave/legacy/weave/ops_domain/run_history/context.py diff --git a/weave/legacy/ops_domain/run_history/history_op_common.py b/weave/legacy/weave/ops_domain/run_history/history_op_common.py similarity index 94% rename from weave/legacy/ops_domain/run_history/history_op_common.py rename to weave/legacy/weave/ops_domain/run_history/history_op_common.py index 3712e488bfc..c38dad124b2 100644 --- a/weave/legacy/ops_domain/run_history/history_op_common.py +++ b/weave/legacy/weave/ops_domain/run_history/history_op_common.py @@ -4,9 +4,9 @@ import pyarrow as pa from pyarrow import parquet as pq -from weave.legacy import weave_types as types -from weave.legacy.api import use -from weave.legacy import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import use +from weave.legacy.weave import ( util, errors, engine_trace, @@ -21,17 +21,17 @@ io_service, op_args, ) -from weave.legacy.arrow.concat import concatenate_all -from weave.legacy.compile_domain import InputAndStitchProvider -from weave.legacy.compile_table import KeyTree -from weave.legacy.language_features.tagging.tagged_value_type import TaggedValueType -from weave.legacy.mappers_arrow import map_to_arrow -from weave.legacy.ops_arrow import ArrowWeaveList -from weave.legacy.ops_arrow.list_ops import concat -from weave.legacy.ops_domain import table, wb_util -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_primitives import make_list -from weave.legacy.wandb_interface import wandb_stream_table +from weave.legacy.weave.arrow.concat import concatenate_all +from weave.legacy.weave.compile_domain import InputAndStitchProvider +from weave.legacy.weave.compile_table import KeyTree +from weave.legacy.weave.language_features.tagging.tagged_value_type import TaggedValueType +from weave.legacy.weave.mappers_arrow import map_to_arrow +from weave.legacy.weave.ops_arrow import ArrowWeaveList +from weave.legacy.weave.ops_arrow.list_ops import concat +from weave.legacy.weave.ops_domain import table, wb_util +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_primitives import make_list +from weave.legacy.weave.wandb_interface import wandb_stream_table tracer = engine_trace.tracer() @@ -45,8 +45,8 @@ class TypeCount(typing.TypedDict): def history_key_type_count_to_weave_type(tc: TypeCount) -> types.Type: - from weave.legacy.ops_domain.trace_tree import WBTraceTree - from weave.legacy.ops_domain.wbmedia import ImageArtifactFileRefType + from weave.legacy.weave.ops_domain.trace_tree import WBTraceTree + from weave.legacy.weave.ops_domain.wbmedia import ImageArtifactFileRefType tc_type = tc["type"] if tc_type == "string": diff --git a/weave/legacy/ops_domain/run_history/run_history_v1_legacy_ops.py b/weave/legacy/weave/ops_domain/run_history/run_history_v1_legacy_ops.py similarity index 89% rename from weave/legacy/ops_domain/run_history/run_history_v1_legacy_ops.py rename to weave/legacy/weave/ops_domain/run_history/run_history_v1_legacy_ops.py index d12822b462b..c78fe031a41 100644 --- a/weave/legacy/ops_domain/run_history/run_history_v1_legacy_ops.py +++ b/weave/legacy/weave/ops_domain/run_history/run_history_v1_legacy_ops.py @@ -2,14 +2,14 @@ import pyarrow as pa -from weave.legacy import engine_trace -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import gql_json_cache -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain import wb_util -from weave.legacy.ops_domain.run_history import history_op_common +from weave.legacy.weave import engine_trace +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import gql_json_cache +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain import wb_util +from weave.legacy.weave.ops_domain.run_history import history_op_common tracer = engine_trace.tracer() diff --git a/weave/legacy/ops_domain/run_history/run_history_v2_parquet_media.py b/weave/legacy/weave/ops_domain/run_history/run_history_v2_parquet_media.py similarity index 90% rename from weave/legacy/ops_domain/run_history/run_history_v2_parquet_media.py rename to weave/legacy/weave/ops_domain/run_history/run_history_v2_parquet_media.py index db2c0a7a50f..113f9db6abe 100644 --- a/weave/legacy/ops_domain/run_history/run_history_v2_parquet_media.py +++ b/weave/legacy/weave/ops_domain/run_history/run_history_v2_parquet_media.py @@ -6,17 +6,17 @@ import pyarrow as pa -from weave.legacy import engine_trace -from weave.legacy import weave_types as types -from weave.legacy.api import op, use -from weave.legacy import artifact_mem, gql_json_cache -from weave.legacy.arrow import convert -from weave.legacy.arrow.list_ import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.ops_arrow.list_ops import concat -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain import wb_util, wbmedia -from weave.legacy.ops_domain.run_history import history_op_common +from weave.legacy.weave import engine_trace +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, use +from weave.legacy.weave import artifact_mem, gql_json_cache +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.ops_arrow.list_ops import concat +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain import wb_util, wbmedia +from weave.legacy.weave.ops_domain.run_history import history_op_common tracer = engine_trace.tracer() diff --git a/weave/legacy/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py b/weave/legacy/weave/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py similarity index 98% rename from weave/legacy/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py rename to weave/legacy/weave/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py index 0b7750ddf1b..3d8dc2fe8d1 100644 --- a/weave/legacy/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py +++ b/weave/legacy/weave/ops_domain/run_history/run_history_v3_parquet_stream_optimized.py @@ -5,10 +5,10 @@ import pyarrow as pa -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import ( +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import ( engine_trace, artifact_base, artifact_fs, @@ -16,8 +16,8 @@ gql_json_cache, io_service, ) -from weave.legacy.arrow import convert -from weave.legacy.arrow.list_ import ( +from weave.legacy.weave.arrow import convert +from weave.legacy.weave.arrow.list_ import ( ArrowWeaveList, ArrowWeaveListType, PathItemType, @@ -25,16 +25,16 @@ make_vec_none, weave_arrow_type_check, ) -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.op_def import map_type -from weave.legacy.ops_domain import trace_tree, wb_util, wbmedia -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.run_history import history_op_common -from weave.legacy.ops_domain.run_history.context import ( +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.op_def import map_type +from weave.legacy.weave.ops_domain import trace_tree, wb_util, wbmedia +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.run_history import history_op_common +from weave.legacy.weave.ops_domain.run_history.context import ( get_error_on_non_vectorized_history_transform, ) -from weave.legacy.ops_domain.table import _patch_legacy_image_file_types -from weave.legacy.wandb_interface import wandb_stream_table +from weave.legacy.weave.ops_domain.table import _patch_legacy_image_file_types +from weave.legacy.weave.wandb_interface import wandb_stream_table tracer = engine_trace.tracer() diff --git a/weave/legacy/ops_domain/run_ops.py b/weave/legacy/weave/ops_domain/run_ops.py similarity index 95% rename from weave/legacy/ops_domain/run_ops.py rename to weave/legacy/weave/ops_domain/run_ops.py index 542f21fb665..e3bc71c970b 100644 --- a/weave/legacy/ops_domain/run_ops.py +++ b/weave/legacy/weave/ops_domain/run_ops.py @@ -37,26 +37,26 @@ import json import typing -from weave.legacy import engine_trace -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import compile_table -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.input_provider import InputAndStitchProvider -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import engine_trace +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import compile_table +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.input_provider import InputAndStitchProvider +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain import wb_util +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain import wb_util # Important to re-export ops -from weave.legacy.ops_domain.run_history import ( +from weave.legacy.weave.ops_domain.run_history import ( history_op_common, run_history_v1_legacy_ops, run_history_v2_parquet_media, run_history_v3_parquet_stream_optimized, ) -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( _make_alias, gql_connection_op, gql_direct_edge_op, diff --git a/weave/legacy/ops_domain/run_queue_ops.py b/weave/legacy/weave/ops_domain/run_queue_ops.py similarity index 51% rename from weave/legacy/ops_domain/run_queue_ops.py rename to weave/legacy/weave/ops_domain/run_queue_ops.py index 4211cfdb5cd..77af41ac69b 100644 --- a/weave/legacy/ops_domain/run_queue_ops.py +++ b/weave/legacy/weave/ops_domain/run_queue_ops.py @@ -1,13 +1,13 @@ import json -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/run_segment.py b/weave/legacy/weave/ops_domain/run_segment.py similarity index 91% rename from weave/legacy/ops_domain/run_segment.py rename to weave/legacy/weave/ops_domain/run_segment.py index 9dff1c01eaa..7924ba22340 100644 --- a/weave/legacy/ops_domain/run_segment.py +++ b/weave/legacy/weave/ops_domain/run_segment.py @@ -3,11 +3,11 @@ from typing import Optional, cast import weave -from weave.legacy import weave_types as types -from weave.legacy.api import Node, get, use -from weave.legacy import context_state as _context -from weave.legacy import panels -from weave.legacy.ops_arrow import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import Node, get, use +from weave.legacy.weave import context_state as _context +from weave.legacy.weave import panels +from weave.legacy.weave.ops_arrow import ArrowWeaveList, ArrowWeaveListType _loading_builtins_token = _context.set_loading_built_ins() diff --git a/weave/legacy/ops_domain/stream_table_ops.py b/weave/legacy/weave/ops_domain/stream_table_ops.py similarity index 68% rename from weave/legacy/ops_domain/stream_table_ops.py rename to weave/legacy/weave/ops_domain/stream_table_ops.py index bc750f574e5..1aec716531b 100644 --- a/weave/legacy/ops_domain/stream_table_ops.py +++ b/weave/legacy/weave/ops_domain/stream_table_ops.py @@ -1,9 +1,9 @@ -from weave.legacy import weave_types -from weave.legacy.api import op -from weave.legacy import compile, op_def -from weave.legacy.arrow.arrow import ArrowWeaveListType -from weave.legacy.core_types import StreamTableType -from weave.legacy.ops_domain.project_ops import project +from weave.legacy.weave import weave_types +from weave.legacy.weave.api import op +from weave.legacy.weave import compile, op_def +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType +from weave.legacy.weave.core_types import StreamTableType +from weave.legacy.weave.ops_domain.project_ops import project def _get_history_node(stream_table: StreamTableType): diff --git a/weave/legacy/ops_domain/table.py b/weave/legacy/weave/ops_domain/table.py similarity index 99% rename from weave/legacy/ops_domain/table.py rename to weave/legacy/weave/ops_domain/table.py index bef6805ad1d..b977104219d 100644 --- a/weave/legacy/ops_domain/table.py +++ b/weave/legacy/weave/ops_domain/table.py @@ -5,10 +5,10 @@ import logging import typing -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class -from weave.legacy import ( +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class +from weave.legacy.weave import ( artifact_fs, artifact_wandb, io_service, @@ -18,8 +18,8 @@ errors, util, ) -from weave.legacy import timestamp as weave_timestamp -from weave.legacy.ops_domain import trace_tree, wbmedia +from weave.legacy.weave import timestamp as weave_timestamp +from weave.legacy.weave.ops_domain import trace_tree, wbmedia @dataclasses.dataclass(frozen=True) diff --git a/weave/legacy/ops_domain/trace_tree.py b/weave/legacy/weave/ops_domain/trace_tree.py similarity index 97% rename from weave/legacy/ops_domain/trace_tree.py rename to weave/legacy/weave/ops_domain/trace_tree.py index 595916d1028..9ff5637f956 100644 --- a/weave/legacy/ops_domain/trace_tree.py +++ b/weave/legacy/weave/ops_domain/trace_tree.py @@ -11,11 +11,11 @@ from wandb.sdk.data_types.trace_tree import Result as WBSpanResult from wandb.sdk.data_types.trace_tree import Span as WBSpan -from weave.legacy import api as weave -from weave.legacy import stream_data_interfaces -from weave.legacy import weave_types as types -from weave.legacy import op_def -from weave.legacy.decorator_op import op +from weave.legacy.weave import api as weave +from weave.legacy.weave import stream_data_interfaces +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import op_def +from weave.legacy.weave.decorator_op import op class StatusCode: diff --git a/weave/legacy/ops_domain/user_ops.py b/weave/legacy/weave/ops_domain/user_ops.py similarity index 77% rename from weave/legacy/ops_domain/user_ops.py rename to weave/legacy/weave/ops_domain/user_ops.py index 0c6527eea79..86e6d39a11e 100644 --- a/weave/legacy/ops_domain/user_ops.py +++ b/weave/legacy/weave/ops_domain/user_ops.py @@ -1,11 +1,11 @@ -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.language_features.tagging.make_tag_getter_op import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.language_features.tagging.make_tag_getter_op import ( make_tag_getter_op, ) -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.ops_domain.wandb_domain_gql import ( +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.ops_domain.wandb_domain_gql import ( gql_connection_op, gql_direct_edge_op, gql_prop_op, diff --git a/weave/legacy/ops_domain/wandb_domain_gql.py b/weave/legacy/weave/ops_domain/wandb_domain_gql.py similarity index 96% rename from weave/legacy/ops_domain/wandb_domain_gql.py rename to weave/legacy/weave/ops_domain/wandb_domain_gql.py index 554e52f0651..a05a40ece28 100644 --- a/weave/legacy/ops_domain/wandb_domain_gql.py +++ b/weave/legacy/weave/ops_domain/wandb_domain_gql.py @@ -4,14 +4,14 @@ import pyarrow as pa -from weave.legacy import weave_types -from weave.legacy.api import op -from weave.legacy import errors, gql_op_plugin, op_def, partial_object -from weave.legacy.decorator_arrow_op import arrow_op -from weave.legacy.gql_op_plugin import wb_gql_op_plugin -from weave.legacy.input_provider import InputProvider -from weave.legacy.ops_arrow import ArrowWeaveList, ArrowWeaveListType -from weave.legacy.ops_domain import wb_domain_types +from weave.legacy.weave import weave_types +from weave.legacy.weave.api import op +from weave.legacy.weave import errors, gql_op_plugin, op_def, partial_object +from weave.legacy.weave.decorator_arrow_op import arrow_op +from weave.legacy.weave.gql_op_plugin import wb_gql_op_plugin +from weave.legacy.weave.input_provider import InputProvider +from weave.legacy.weave.ops_arrow import ArrowWeaveList, ArrowWeaveListType +from weave.legacy.weave.ops_domain import wb_domain_types """ This file contains utilities for constructing GQL ops (used by all the ops in diff --git a/weave/legacy/ops_domain/wb_domain_types.py b/weave/legacy/weave/ops_domain/wb_domain_types.py similarity index 97% rename from weave/legacy/ops_domain/wb_domain_types.py rename to weave/legacy/weave/ops_domain/wb_domain_types.py index b1bf98bef0d..e99912883b6 100644 --- a/weave/legacy/ops_domain/wb_domain_types.py +++ b/weave/legacy/weave/ops_domain/wb_domain_types.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy.decorator_type import type as weave_type -from weave.legacy.partial_object import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.decorator_type import type as weave_type +from weave.legacy.weave.partial_object import ( PartialObject, PartialObjectTypeGeneratorType, ) diff --git a/weave/legacy/ops_domain/wb_util.py b/weave/legacy/weave/ops_domain/wb_util.py similarity index 87% rename from weave/legacy/ops_domain/wb_util.py rename to weave/legacy/weave/ops_domain/wb_util.py index 8ba2582a2ca..9265f24777b 100644 --- a/weave/legacy/ops_domain/wb_util.py +++ b/weave/legacy/weave/ops_domain/wb_util.py @@ -2,15 +2,15 @@ from dataclasses import dataclass from urllib import parse -from weave.legacy import weave_types as types -from weave.legacy import artifact_fs, decorator_type -from weave.legacy.artifact_wandb import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import artifact_fs, decorator_type +from weave.legacy.weave.artifact_wandb import ( WandbArtifact, WeaveWBLoggedArtifactURI, ) -from weave.legacy.ops_domain.run_history import history_op_common -from weave.legacy.wandb_interface import wandb_stream_table -from weave.legacy.runfiles_wandb import WandbRunFiles, WeaveWBRunFilesURI +from weave.legacy.weave.ops_domain.run_history import history_op_common +from weave.legacy.weave.wandb_interface import wandb_stream_table +from weave.legacy.weave.runfiles_wandb import WandbRunFiles, WeaveWBRunFilesURI @dataclass @@ -96,7 +96,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): return _filesystem_artifact_file_from_artifact_path(val["artifact_path"]) if val["_type"] == "image-file" and run_path is not None: - from weave.legacy.ops_domain import ImageArtifactFileRef + from weave.legacy.weave.ops_domain import ImageArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return ImageArtifactFileRef( @@ -110,7 +110,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): # masks=val.get("masks", {}), ) if val["_type"] == "audio-file" and run_path is not None: - from weave.legacy.ops_domain import AudioArtifactFileRef + from weave.legacy.weave.ops_domain import AudioArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return AudioArtifactFileRef( # type: ignore @@ -119,7 +119,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): sha256=val["sha256"], ) if val["_type"] == "bokeh-file" and run_path is not None: - from weave.legacy.ops_domain import BokehArtifactFileRef + from weave.legacy.weave.ops_domain import BokehArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return BokehArtifactFileRef( # type: ignore @@ -128,7 +128,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): sha256=val["sha256"], ) if val["_type"] == "video-file" and run_path is not None: - from weave.legacy.ops_domain import VideoArtifactFileRef + from weave.legacy.weave.ops_domain import VideoArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return VideoArtifactFileRef( # type: ignore @@ -137,7 +137,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): sha256=val["sha256"], ) if val["_type"] == "object3D-file" and run_path is not None: - from weave.legacy.ops_domain import Object3DArtifactFileRef + from weave.legacy.weave.ops_domain import Object3DArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return Object3DArtifactFileRef( # type: ignore @@ -146,7 +146,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): sha256=val["sha256"], ) if val["_type"] == "molecule-file" and run_path is not None: - from weave.legacy.ops_domain import MoleculeArtifactFileRef + from weave.legacy.weave.ops_domain import MoleculeArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return MoleculeArtifactFileRef( # type: ignore @@ -155,7 +155,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): sha256=val["sha256"], ) if val["_type"] == "html-file" and run_path is not None: - from weave.legacy.ops_domain import HtmlArtifactFileRef + from weave.legacy.weave.ops_domain import HtmlArtifactFileRef fs_artifact_file = _filesystem_runfiles_from_run_path(run_path, val["path"]) return HtmlArtifactFileRef( # type: ignore @@ -165,7 +165,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): ) if val["_type"] == "images/separated" and run_path is not None: - from weave.legacy.ops_domain import ImageArtifactFileRef + from weave.legacy.weave.ops_domain import ImageArtifactFileRef image_list: list[ImageArtifactFileRef] = [] @@ -184,7 +184,7 @@ def _process_run_dict_item(val, run_path: typing.Optional[RunPath] = None): return image_list if val["_type"] == "wb_trace_tree": - from weave.legacy.ops_domain.trace_tree import WBTraceTree + from weave.legacy.weave.ops_domain.trace_tree import WBTraceTree return WBTraceTree( root_span_dumps=val.get("root_span_dumps"), # type: ignore diff --git a/weave/legacy/ops_domain/wbgqlquery_op.py b/weave/legacy/weave/ops_domain/wbgqlquery_op.py similarity index 87% rename from weave/legacy/ops_domain/wbgqlquery_op.py rename to weave/legacy/weave/ops_domain/wbgqlquery_op.py index 4841ee249e3..74c50e2e61a 100644 --- a/weave/legacy/ops_domain/wbgqlquery_op.py +++ b/weave/legacy/weave/ops_domain/wbgqlquery_op.py @@ -1,13 +1,13 @@ import logging import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import engine_trace, environment, mappers_gql, partial_object -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.ops_domain import wb_domain_types as wdt -from weave.legacy.wandb_client_api import wandb_gql_query +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import engine_trace, environment, mappers_gql, partial_object +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.ops_domain import wb_domain_types as wdt +from weave.legacy.weave.wandb_client_api import wandb_gql_query def _wbgqlquery_output_type(input_types: dict[str, types.Type]) -> types.Type: diff --git a/weave/legacy/ops_domain/wbmedia.py b/weave/legacy/weave/ops_domain/wbmedia.py similarity index 97% rename from weave/legacy/ops_domain/wbmedia.py rename to weave/legacy/weave/ops_domain/wbmedia.py index 32e3fe4f937..4256c0872d2 100644 --- a/weave/legacy/ops_domain/wbmedia.py +++ b/weave/legacy/weave/ops_domain/wbmedia.py @@ -4,11 +4,11 @@ import json import typing -from weave.legacy import api as weave +from weave.legacy.weave import api as weave from weave import types -from weave.legacy import errors, engine_trace, artifact_fs, file_base -from weave.legacy.language_features.tagging.tag_store import isolated_tagging_context -from weave.legacy.ops_primitives import html, markdown +from weave.legacy.weave import errors, engine_trace, artifact_fs, file_base +from weave.legacy.weave.language_features.tagging.tag_store import isolated_tagging_context +from weave.legacy.weave.ops_primitives import html, markdown class LegacyImageArtifactFileRef: @@ -283,7 +283,7 @@ class LegacyTableNDArrayType(types.Type): # code can work with it. @weave.op() def html_file(html: html.Html) -> HtmlArtifactFileRef: - from weave.legacy import storage + from weave.legacy.weave import storage # This is a ref to the html object with isolated_tagging_context(): @@ -305,7 +305,7 @@ def html_file(html: html.Html) -> HtmlArtifactFileRef: ) ) def markdown_file(md: markdown.Markdown): - from weave.legacy import storage + from weave.legacy.weave import storage with isolated_tagging_context(): ref = storage.save(md) diff --git a/weave/legacy/ops_primitives/__init__.py b/weave/legacy/weave/ops_primitives/__init__.py similarity index 92% rename from weave/legacy/ops_primitives/__init__.py rename to weave/legacy/weave/ops_primitives/__init__.py index be21b91cb3a..41990e9c7e6 100644 --- a/weave/legacy/ops_primitives/__init__.py +++ b/weave/legacy/weave/ops_primitives/__init__.py @@ -28,7 +28,7 @@ from .html import * from .markdown import * from .op_def import * -from weave.legacy.language_features.tagging.tagging_ops import * +from weave.legacy.weave.language_features.tagging.tagging_ops import * from .list_tag_getters import * from .geom import * from .server import * diff --git a/weave/legacy/ops_primitives/_dict_utils.py b/weave/legacy/weave/ops_primitives/_dict_utils.py similarity index 97% rename from weave/legacy/ops_primitives/_dict_utils.py rename to weave/legacy/weave/ops_primitives/_dict_utils.py index 066c47b8616..780257c0638 100644 --- a/weave/legacy/ops_primitives/_dict_utils.py +++ b/weave/legacy/weave/ops_primitives/_dict_utils.py @@ -1,9 +1,9 @@ import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import box -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type def typeddict_pick_output_type(input_types): diff --git a/weave/legacy/ops_primitives/any.py b/weave/legacy/weave/ops_primitives/any.py similarity index 61% rename from weave/legacy/ops_primitives/any.py rename to weave/legacy/weave/ops_primitives/any.py index f1895ae6c2b..eb10ed9b0c1 100644 --- a/weave/legacy/ops_primitives/any.py +++ b/weave/legacy/weave/ops_primitives/any.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_types as types -from weave.legacy.decorator_op import op +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.decorator_op import op @op( diff --git a/weave/legacy/ops_primitives/artifacts.py b/weave/legacy/weave/ops_primitives/artifacts.py similarity index 96% rename from weave/legacy/ops_primitives/artifacts.py rename to weave/legacy/weave/ops_primitives/artifacts.py index de1e0e0ffa9..9edd8c6e223 100644 --- a/weave/legacy/ops_primitives/artifacts.py +++ b/weave/legacy/weave/ops_primitives/artifacts.py @@ -3,10 +3,10 @@ import pathlib import typing -from weave.legacy.api import op +from weave.legacy.weave.api import op from weave import types -from weave.legacy import artifact_fs, ref_base -from weave.legacy.artifact_local import WORKING_DIR_PREFIX, LocalArtifact +from weave.legacy.weave import artifact_fs, ref_base +from weave.legacy.weave.artifact_local import WORKING_DIR_PREFIX, LocalArtifact @op(name="FilesystemArtifact-fileRefineType", hidden=True) diff --git a/weave/legacy/ops_primitives/boolean.py b/weave/legacy/weave/ops_primitives/boolean.py similarity index 92% rename from weave/legacy/ops_primitives/boolean.py rename to weave/legacy/weave/ops_primitives/boolean.py index 29c6d1ab283..b3f007a22fd 100644 --- a/weave/legacy/ops_primitives/boolean.py +++ b/weave/legacy/weave/ops_primitives/boolean.py @@ -1,9 +1,9 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class -from weave.legacy import dispatch -from weave.legacy.ops_primitives.dict import dict_ +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class +from weave.legacy.weave import dispatch +from weave.legacy.weave.ops_primitives.dict import dict_ @weave_class(weave_type=types.Boolean) diff --git a/weave/legacy/ops_primitives/csv_.py b/weave/legacy/weave/ops_primitives/csv_.py similarity index 97% rename from weave/legacy/ops_primitives/csv_.py rename to weave/legacy/weave/ops_primitives/csv_.py index b5f4fe460fb..90a7da01bd2 100644 --- a/weave/legacy/ops_primitives/csv_.py +++ b/weave/legacy/weave/ops_primitives/csv_.py @@ -3,8 +3,8 @@ import pyarrow as pa import pyarrow.csv as pa_csv -from weave.legacy import api as weave -from weave.legacy import file_base +from weave.legacy.weave import api as weave +from weave.legacy.weave import file_base def sniff_dialect(path: str) -> type[csv.Dialect]: diff --git a/weave/legacy/ops_primitives/date.py b/weave/legacy/weave/ops_primitives/date.py similarity index 98% rename from weave/legacy/ops_primitives/date.py rename to weave/legacy/weave/ops_primitives/date.py index 08a5083bfa8..bf08300d3c0 100644 --- a/weave/legacy/ops_primitives/date.py +++ b/weave/legacy/weave/ops_primitives/date.py @@ -4,8 +4,8 @@ import dateutil import dateutil.parser -from weave.legacy import weave_types as types -from weave.legacy.api import op, type +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, type @op( diff --git a/weave/legacy/ops_primitives/dict.py b/weave/legacy/weave/ops_primitives/dict.py similarity index 92% rename from weave/legacy/ops_primitives/dict.py rename to weave/legacy/weave/ops_primitives/dict.py index cc6e2a25233..a96a9486530 100644 --- a/weave/legacy/ops_primitives/dict.py +++ b/weave/legacy/weave/ops_primitives/dict.py @@ -1,12 +1,12 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy._dict_utils import typeddict_pick_output_type -from weave.legacy.decorator_class import weave_class -from weave.legacy.decorator_op import op -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.op_args import OpVarArgs -from weave.legacy.ops_primitives._dict_utils import ( +from weave.legacy.weave import weave_types as types +from weave.legacy.weave._dict_utils import typeddict_pick_output_type +from weave.legacy.weave.decorator_class import weave_class +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.op_args import OpVarArgs +from weave.legacy.weave.ops_primitives._dict_utils import ( tag_aware_dict_val_for_escaped_key, ) diff --git a/weave/legacy/ops_primitives/file.py b/weave/legacy/weave/ops_primitives/file.py similarity index 90% rename from weave/legacy/ops_primitives/file.py rename to weave/legacy/weave/ops_primitives/file.py index 75d37ea08db..476c953927b 100644 --- a/weave/legacy/ops_primitives/file.py +++ b/weave/legacy/weave/ops_primitives/file.py @@ -1,13 +1,13 @@ import json import typing -from weave.legacy import environment as weave_env -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import file_base, wandb_file_manager -from weave.legacy.artifact_fs import FilesystemArtifactDir, FilesystemArtifactFile -from weave.legacy.artifact_wandb import WandbArtifact, WandbArtifactManifest +from weave.legacy.weave import environment as weave_env +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import file_base, wandb_file_manager +from weave.legacy.weave.artifact_fs import FilesystemArtifactDir, FilesystemArtifactFile +from weave.legacy.weave.artifact_wandb import WandbArtifact, WandbArtifactManifest @op(name="dir-pathReturnType", hidden=True) diff --git a/weave/legacy/ops_primitives/file_artifact.py b/weave/legacy/weave/ops_primitives/file_artifact.py similarity index 87% rename from weave/legacy/ops_primitives/file_artifact.py rename to weave/legacy/weave/ops_primitives/file_artifact.py index 203e2486241..299c4e87be5 100644 --- a/weave/legacy/ops_primitives/file_artifact.py +++ b/weave/legacy/weave/ops_primitives/file_artifact.py @@ -1,7 +1,7 @@ import typing -from weave.legacy.api import op -from weave.legacy import artifact_fs +from weave.legacy.weave.api import op +from weave.legacy.weave import artifact_fs @op(name="FilesystemArtifactFile-directUrl") diff --git a/weave/legacy/ops_primitives/file_local.py b/weave/legacy/weave/ops_primitives/file_local.py similarity index 83% rename from weave/legacy/ops_primitives/file_local.py rename to weave/legacy/weave/ops_primitives/file_local.py index 022bf141e19..dbf21b4c1a6 100644 --- a/weave/legacy/ops_primitives/file_local.py +++ b/weave/legacy/weave/ops_primitives/file_local.py @@ -1,10 +1,10 @@ import os import typing -from weave.legacy import environment -from weave.legacy import weave_types as types -from weave.legacy.api import op -from weave.legacy import file_local +from weave.legacy.weave import environment +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op +from weave.legacy.weave import file_local @op(name="LocalFile-directUrl") diff --git a/weave/legacy/ops_primitives/geom.py b/weave/legacy/weave/ops_primitives/geom.py similarity index 93% rename from weave/legacy/ops_primitives/geom.py rename to weave/legacy/weave/ops_primitives/geom.py index e0931c1bd39..32264b7b9d5 100644 --- a/weave/legacy/ops_primitives/geom.py +++ b/weave/legacy/weave/ops_primitives/geom.py @@ -1,6 +1,6 @@ from PIL import Image -from weave.legacy import api as weave +from weave.legacy.weave import api as weave @weave.type() diff --git a/weave/legacy/ops_primitives/html.py b/weave/legacy/weave/ops_primitives/html.py similarity index 82% rename from weave/legacy/ops_primitives/html.py rename to weave/legacy/weave/ops_primitives/html.py index 2103ff69428..72d6d25a45d 100644 --- a/weave/legacy/ops_primitives/html.py +++ b/weave/legacy/weave/ops_primitives/html.py @@ -1,7 +1,7 @@ import dataclasses -from weave.legacy import api as weave -from weave.legacy import weave_types as types +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_types as types class HtmlType(types.Type): diff --git a/weave/legacy/ops_primitives/image.py b/weave/legacy/weave/ops_primitives/image.py similarity index 93% rename from weave/legacy/ops_primitives/image.py rename to weave/legacy/weave/ops_primitives/image.py index f44f14357ad..9432887943b 100644 --- a/weave/legacy/ops_primitives/image.py +++ b/weave/legacy/weave/ops_primitives/image.py @@ -5,8 +5,8 @@ import PIL import PIL.Image -from weave.legacy import api as weave -from weave.legacy import weave_types as types +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_types as types class ImageType(types.Type): diff --git a/weave/legacy/ops_primitives/json_.py b/weave/legacy/weave/ops_primitives/json_.py similarity index 90% rename from weave/legacy/ops_primitives/json_.py rename to weave/legacy/weave/ops_primitives/json_.py index 5cefb079741..b781daab769 100644 --- a/weave/legacy/ops_primitives/json_.py +++ b/weave/legacy/weave/ops_primitives/json_.py @@ -1,8 +1,8 @@ import json import typing -from weave.legacy import api as weave -from weave.legacy import file_base +from weave.legacy.weave import api as weave +from weave.legacy.weave import file_base def load_jsonl(jsonlfile): diff --git a/weave/legacy/ops_primitives/list_.py b/weave/legacy/weave/ops_primitives/list_.py similarity index 97% rename from weave/legacy/ops_primitives/list_.py rename to weave/legacy/weave/ops_primitives/list_.py index e61826f6003..6d80a26a62f 100644 --- a/weave/legacy/ops_primitives/list_.py +++ b/weave/legacy/weave/ops_primitives/list_.py @@ -6,20 +6,20 @@ import numpy as np import pandas as pd -from weave.legacy import storage -from weave.legacy import weave_types as types -from weave.legacy import box, execute_fast, errors -from weave.legacy._dict_utils import tag_aware_dict_val_for_escaped_key -from weave.legacy.decorator_class import weave_class -from weave.legacy.decorator_op import op -from weave.legacy.graph import Node -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import storage +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box, execute_fast, errors +from weave.legacy.weave._dict_utils import tag_aware_dict_val_for_escaped_key +from weave.legacy.weave.decorator_class import weave_class +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.graph import Node +from weave.legacy.weave.language_features.tagging import ( tag_store, tagged_value_type, tagged_value_type_helpers, ) -from weave.legacy.op_args import OpVarArgs -from weave.legacy.ops_primitives import projection_utils +from weave.legacy.weave.op_args import OpVarArgs +from weave.legacy.weave.ops_primitives import projection_utils def getitem_output_type(input_types, list_type=types.List): @@ -392,8 +392,8 @@ def flatten_return_type(input_types): def _flatten(l): - from weave.legacy.arrow.arrow_tags import pushdown_list_tags - from weave.legacy.ops_arrow import ArrowWeaveList + from weave.legacy.weave.arrow.arrow_tags import pushdown_list_tags + from weave.legacy.weave.ops_arrow import ArrowWeaveList if isinstance(l, list): tags = None diff --git a/weave/legacy/ops_primitives/list_tag_getters.py b/weave/legacy/weave/ops_primitives/list_tag_getters.py similarity index 74% rename from weave/legacy/ops_primitives/list_tag_getters.py rename to weave/legacy/weave/ops_primitives/list_tag_getters.py index 61e57c3ae93..ca21015dc17 100644 --- a/weave/legacy/ops_primitives/list_tag_getters.py +++ b/weave/legacy/weave/ops_primitives/list_tag_getters.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_types as types -from weave.legacy.language_features.tagging import make_tag_getter_op +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.language_features.tagging import make_tag_getter_op group_tag_getter_op = make_tag_getter_op.make_tag_getter_op( "groupKey", types.Any(), op_name="group-groupkey" diff --git a/weave/legacy/ops_primitives/markdown.py b/weave/legacy/weave/ops_primitives/markdown.py similarity index 82% rename from weave/legacy/ops_primitives/markdown.py rename to weave/legacy/weave/ops_primitives/markdown.py index ad3822dfdfc..b82e7e547f9 100644 --- a/weave/legacy/ops_primitives/markdown.py +++ b/weave/legacy/weave/ops_primitives/markdown.py @@ -1,7 +1,7 @@ import dataclasses -from weave.legacy import api as weave -from weave.legacy import weave_types as types +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_types as types class MarkdownType(types.Type): diff --git a/weave/legacy/ops_primitives/number.py b/weave/legacy/weave/ops_primitives/number.py similarity index 98% rename from weave/legacy/ops_primitives/number.py rename to weave/legacy/weave/ops_primitives/number.py index 2cb4aec1254..c260617c53b 100644 --- a/weave/legacy/ops_primitives/number.py +++ b/weave/legacy/weave/ops_primitives/number.py @@ -4,9 +4,9 @@ import numpy as np -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class -from weave.legacy import timestamp as weave_timestamp +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class +from weave.legacy.weave import timestamp as weave_timestamp binary_number_op_input_type = { "lhs": types.Number(), diff --git a/weave/legacy/ops_primitives/number_bin.py b/weave/legacy/weave/ops_primitives/number_bin.py similarity index 81% rename from weave/legacy/ops_primitives/number_bin.py rename to weave/legacy/weave/ops_primitives/number_bin.py index 97b799fcd3d..d60e531c268 100644 --- a/weave/legacy/ops_primitives/number_bin.py +++ b/weave/legacy/weave/ops_primitives/number_bin.py @@ -1,12 +1,12 @@ import math -from weave.legacy import weave_types as types -from weave.legacy.api import op, use -from weave.legacy import graph -from weave.legacy.ops_primitives import date -from weave.legacy.ops_primitives.dict import dict_ -from weave.legacy.weave_internal import call_fn, define_fn, make_const_node -from weave.legacy.weave_types import Function, NumberBinType +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, use +from weave.legacy.weave import graph +from weave.legacy.weave.ops_primitives import date +from weave.legacy.weave.ops_primitives.dict import dict_ +from weave.legacy.weave.weave_internal import call_fn, define_fn, make_const_node +from weave.legacy.weave.weave_types import Function, NumberBinType @op( diff --git a/weave/legacy/ops_primitives/obj.py b/weave/legacy/weave/ops_primitives/obj.py similarity index 93% rename from weave/legacy/ops_primitives/obj.py rename to weave/legacy/weave/ops_primitives/obj.py index 615fe8eea3f..35e24284d2c 100644 --- a/weave/legacy/ops_primitives/obj.py +++ b/weave/legacy/weave/ops_primitives/obj.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import codify -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class +from weave.legacy.weave import codify +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class # This matches the output type logic of the frontend diff --git a/weave/legacy/ops_primitives/op_def.py b/weave/legacy/weave/ops_primitives/op_def.py similarity index 68% rename from weave/legacy/ops_primitives/op_def.py rename to weave/legacy/weave/ops_primitives/op_def.py index 81caf0be0e2..1d7458476fe 100644 --- a/weave/legacy/ops_primitives/op_def.py +++ b/weave/legacy/weave/ops_primitives/op_def.py @@ -1,7 +1,7 @@ -from weave.legacy import api as weave -from weave.legacy import weave_types as types -from weave.legacy.op_def import OpDef -from weave.legacy.op_def_type import OpDefType +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.op_def import OpDef +from weave.legacy.weave.op_def_type import OpDefType @weave.weave_class(weave_type=OpDefType) diff --git a/weave/legacy/ops_primitives/pandas_.py b/weave/legacy/weave/ops_primitives/pandas_.py similarity index 97% rename from weave/legacy/ops_primitives/pandas_.py rename to weave/legacy/weave/ops_primitives/pandas_.py index 97a9f30c2db..d1e286ed931 100644 --- a/weave/legacy/ops_primitives/pandas_.py +++ b/weave/legacy/weave/ops_primitives/pandas_.py @@ -8,12 +8,12 @@ import pyarrow as pa import pyarrow.parquet as pq -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class -from weave.legacy import box, file_base, graph, mappers_python -from weave.legacy.language_features.tagging import tag_store, tagged_value_type -from weave.legacy.ops_primitives import list_ +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class +from weave.legacy.weave import box, file_base, graph, mappers_python +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave.ops_primitives import list_ # Hack hack hack diff --git a/weave/legacy/ops_primitives/projection_utils.py b/weave/legacy/weave/ops_primitives/projection_utils.py similarity index 98% rename from weave/legacy/ops_primitives/projection_utils.py rename to weave/legacy/weave/ops_primitives/projection_utils.py index 49e96d1e562..127c41d8773 100644 --- a/weave/legacy/ops_primitives/projection_utils.py +++ b/weave/legacy/weave/ops_primitives/projection_utils.py @@ -9,8 +9,8 @@ from sklearn.decomposition import PCA from sklearn.manifold import TSNE -from weave.legacy import errors -from weave.legacy import context_state, environment +from weave.legacy.weave import errors +from weave.legacy.weave import context_state, environment umap_lib = {} diff --git a/weave/legacy/ops_primitives/random_junk.py b/weave/legacy/weave/ops_primitives/random_junk.py similarity index 82% rename from weave/legacy/ops_primitives/random_junk.py rename to weave/legacy/weave/ops_primitives/random_junk.py index e77ffe0a48b..b158c1221ab 100644 --- a/weave/legacy/ops_primitives/random_junk.py +++ b/weave/legacy/weave/ops_primitives/random_junk.py @@ -1,8 +1,8 @@ # Ideas for ops, but not production ready. -from weave.legacy import api as api -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class +from weave.legacy.weave import api as api +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class op( name="root-compare_versions", diff --git a/weave/legacy/ops_primitives/server.py b/weave/legacy/weave/ops_primitives/server.py similarity index 76% rename from weave/legacy/ops_primitives/server.py rename to weave/legacy/weave/ops_primitives/server.py index ccc93e6762d..536343a5d9f 100644 --- a/weave/legacy/ops_primitives/server.py +++ b/weave/legacy/weave/ops_primitives/server.py @@ -1,4 +1,4 @@ -from weave.legacy.decorator_op import op +from weave.legacy.weave.decorator_op import op from weave.version import VERSION diff --git a/weave/legacy/ops_primitives/set_.py b/weave/legacy/weave/ops_primitives/set_.py similarity index 94% rename from weave/legacy/ops_primitives/set_.py rename to weave/legacy/weave/ops_primitives/set_.py index d64e7e9f845..f73d5627709 100644 --- a/weave/legacy/ops_primitives/set_.py +++ b/weave/legacy/weave/ops_primitives/set_.py @@ -1,5 +1,5 @@ -from weave.legacy import weave_types as types -from weave.legacy.decorators import op +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.decorators import op def get_const_union_vals(type_, of_type): diff --git a/weave/legacy/ops_primitives/sql.py b/weave/legacy/weave/ops_primitives/sql.py similarity index 97% rename from weave/legacy/ops_primitives/sql.py rename to weave/legacy/weave/ops_primitives/sql.py index 7c05a41a213..f4f78567744 100644 --- a/weave/legacy/ops_primitives/sql.py +++ b/weave/legacy/weave/ops_primitives/sql.py @@ -2,11 +2,11 @@ import functools import math -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class -from weave.legacy import decorator_type -from weave.legacy.language_features.tagging import tagged_value_type -from weave.legacy.ops_primitives import graph, list_ +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class +from weave.legacy.weave import decorator_type +from weave.legacy.weave.language_features.tagging import tagged_value_type +from weave.legacy.weave.ops_primitives import graph, list_ try: import sqlalchemy diff --git a/weave/legacy/ops_primitives/string.py b/weave/legacy/weave/ops_primitives/string.py similarity index 98% rename from weave/legacy/ops_primitives/string.py rename to weave/legacy/weave/ops_primitives/string.py index db86ae6d233..6f0433f801f 100644 --- a/weave/legacy/ops_primitives/string.py +++ b/weave/legacy/weave/ops_primitives/string.py @@ -4,8 +4,8 @@ import numpy as np -from weave.legacy import weave_types as types -from weave.legacy.api import op, weave_class +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, weave_class @op(name="root-string") diff --git a/weave/legacy/ops_primitives/test_any.py b/weave/legacy/weave/ops_primitives/test_any.py similarity index 73% rename from weave/legacy/ops_primitives/test_any.py rename to weave/legacy/weave/ops_primitives/test_any.py index 4fd7cd14952..95d2c93c2af 100644 --- a/weave/legacy/ops_primitives/test_any.py +++ b/weave/legacy/weave/ops_primitives/test_any.py @@ -2,9 +2,9 @@ import pytest -from weave.legacy import api as weave -from weave.legacy import box -from weave.legacy.ops_primitives import any +from weave.legacy.weave import api as weave +from weave.legacy.weave import box +from weave.legacy.weave.ops_primitives import any cases: list[typing.Tuple[typing.Any, bool]] = [ (1, False), diff --git a/weave/legacy/ops_primitives/test_dict.py b/weave/legacy/weave/ops_primitives/test_dict.py similarity index 80% rename from weave/legacy/ops_primitives/test_dict.py rename to weave/legacy/weave/ops_primitives/test_dict.py index 165a714a3ee..04a85b7509b 100644 --- a/weave/legacy/ops_primitives/test_dict.py +++ b/weave/legacy/weave/ops_primitives/test_dict.py @@ -1,6 +1,6 @@ import weave -from weave.legacy import weave_internal -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_internal +from weave.legacy.weave.language_features.tagging import tagged_value_type def test_keys_type(): @@ -25,7 +25,7 @@ def test_pick(): ), "a", ) - assert weave.use(weave.legacy.ops.TypedDict.pick(obj, key)) == "x" + assert weave.use(weave.legacy.weave.ops.TypedDict.pick(obj, key)) == "x" def test_pick_none_key(): diff --git a/weave/legacy/ops_primitives/test_file.py b/weave/legacy/weave/ops_primitives/test_file.py similarity index 54% rename from weave/legacy/ops_primitives/test_file.py rename to weave/legacy/weave/ops_primitives/test_file.py index 2715f67a579..247beb744f5 100644 --- a/weave/legacy/ops_primitives/test_file.py +++ b/weave/legacy/weave/ops_primitives/test_file.py @@ -1,8 +1,8 @@ import weave -from weave.legacy.ops_primitives import file_local +from weave.legacy.weave.ops_primitives import file_local def test_file_browsing(): - test_dir = weave.legacy.ops.local_path("./testdata/").path("test_dir") + test_dir = weave.legacy.weave.ops.local_path("./testdata/").path("test_dir") assert test_dir.type == file_local.LocalDirType() assert weave.use(test_dir.path("b.txt").contents()) == "howdy\n" diff --git a/weave/legacy/ops_primitives/test_image.py b/weave/legacy/weave/ops_primitives/test_image.py similarity index 89% rename from weave/legacy/ops_primitives/test_image.py rename to weave/legacy/weave/ops_primitives/test_image.py index 12c0e7868fe..66e17caac85 100644 --- a/weave/legacy/ops_primitives/test_image.py +++ b/weave/legacy/weave/ops_primitives/test_image.py @@ -2,8 +2,8 @@ import pytest from PIL import Image -from weave.legacy import storage -from weave.legacy.ops_primitives import api as weave +from weave.legacy.weave import storage +from weave.legacy.weave.ops_primitives import api as weave def test_image(): diff --git a/weave/legacy/ops_primitives/test_list.py b/weave/legacy/weave/ops_primitives/test_list.py similarity index 96% rename from weave/legacy/ops_primitives/test_list.py rename to weave/legacy/weave/ops_primitives/test_list.py index e9e99073bb0..f2bf31f7cd4 100644 --- a/weave/legacy/ops_primitives/test_list.py +++ b/weave/legacy/weave/ops_primitives/test_list.py @@ -1,15 +1,15 @@ import pytest -from weave.legacy import api as weave -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import box -from weave.legacy.language_features.tagging import ( +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import box +from weave.legacy.weave.language_features.tagging import ( make_tag_getter_op, tag_store, tagged_value_type, ) -from weave.legacy.ops_primitives import dict, errors, list_, number, runs +from weave.legacy.weave.ops_primitives import dict, errors, list_, number, runs from weave.legacy.tests.util import geom, weavejs_ops diff --git a/weave/legacy/weave/ops_primitives/test_list_range.py b/weave/legacy/weave/ops_primitives/test_list_range.py new file mode 100644 index 00000000000..d821293f33d --- /dev/null +++ b/weave/legacy/weave/ops_primitives/test_list_range.py @@ -0,0 +1,7 @@ +import pytest + +import weave + + +def test_range(): + assert weave.use(weave.legacy.weave.ops.range(0, 3, 1)).to_pylist_tagged() == [0, 1, 2] diff --git a/weave/legacy/ops_primitives/test_pandas.py b/weave/legacy/weave/ops_primitives/test_pandas.py similarity index 85% rename from weave/legacy/ops_primitives/test_pandas.py rename to weave/legacy/weave/ops_primitives/test_pandas.py index 835aeba71f8..f6977800091 100644 --- a/weave/legacy/ops_primitives/test_pandas.py +++ b/weave/legacy/weave/ops_primitives/test_pandas.py @@ -1,8 +1,8 @@ import pandas as pd -from weave.legacy import api as weave -from weave.legacy import weave_types as types -from weave.legacy.ops_primitives import pandas_ as op_pandas +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.ops_primitives import pandas_ as op_pandas def test_save_dataframe(): diff --git a/weave/legacy/ops_primitives/test_set.py b/weave/legacy/weave/ops_primitives/test_set.py similarity index 89% rename from weave/legacy/ops_primitives/test_set.py rename to weave/legacy/weave/ops_primitives/test_set.py index 2fb0ffebbf4..96b2048a7df 100644 --- a/weave/legacy/ops_primitives/test_set.py +++ b/weave/legacy/weave/ops_primitives/test_set.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import ops_primitives, val_const +from weave.legacy.weave import ops_primitives, val_const def test_difference_type(): diff --git a/weave/legacy/ops_primitives/test_type.py b/weave/legacy/weave/ops_primitives/test_type.py similarity index 79% rename from weave/legacy/ops_primitives/test_type.py rename to weave/legacy/weave/ops_primitives/test_type.py index a2dff5d9c96..4240de94abf 100644 --- a/weave/legacy/ops_primitives/test_type.py +++ b/weave/legacy/weave/ops_primitives/test_type.py @@ -4,11 +4,11 @@ def test_cast_basic(): - valid_cast = weave.legacy.ops.cast(1, weave.types.Int()) + valid_cast = weave.legacy.weave.ops.cast(1, weave.types.Int()) assert valid_cast.type == weave.types.Int() assert weave.use(valid_cast) == 1 - invalid_cast = weave.legacy.ops.cast(1, weave.types.String()) + invalid_cast = weave.legacy.weave.ops.cast(1, weave.types.String()) assert invalid_cast.type == weave.types.String() with pytest.raises(weave.errors.WeaveTypeError): weave.use(invalid_cast) diff --git a/weave/legacy/ops_primitives/test_typeddict.py b/weave/legacy/weave/ops_primitives/test_typeddict.py similarity index 96% rename from weave/legacy/ops_primitives/test_typeddict.py rename to weave/legacy/weave/ops_primitives/test_typeddict.py index 2818487c9fb..6f57ee0ef8a 100644 --- a/weave/legacy/ops_primitives/test_typeddict.py +++ b/weave/legacy/weave/ops_primitives/test_typeddict.py @@ -1,10 +1,10 @@ import pytest import weave -from weave.legacy import weave_internal -from weave.legacy import box -from weave.legacy.arrow.arrow import ArrowWeaveListType -from weave.legacy.language_features.tagging import tag_store, tagged_value_type +from weave.legacy.weave import weave_internal +from weave.legacy.weave import box +from weave.legacy.weave.arrow.arrow import ArrowWeaveListType +from weave.legacy.weave.language_features.tagging import tag_store, tagged_value_type from weave.legacy.tests.util.list_arrow_test_helpers import ArrowNode pick_options = [ diff --git a/weave/legacy/ops_primitives/timestamp_bin.py b/weave/legacy/weave/ops_primitives/timestamp_bin.py similarity index 86% rename from weave/legacy/ops_primitives/timestamp_bin.py rename to weave/legacy/weave/ops_primitives/timestamp_bin.py index ce5b3e1097b..0263ec39fe6 100644 --- a/weave/legacy/ops_primitives/timestamp_bin.py +++ b/weave/legacy/weave/ops_primitives/timestamp_bin.py @@ -1,10 +1,10 @@ -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy.api import op, use -from weave.legacy import graph -from weave.legacy.ops_primitives.dict import dict_ -from weave.legacy.weave_internal import call_fn, define_fn, make_const_node -from weave.legacy.weave_types import Function, TimestampBinType +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op, use +from weave.legacy.weave import graph +from weave.legacy.weave.ops_primitives.dict import dict_ +from weave.legacy.weave.weave_internal import call_fn, define_fn, make_const_node +from weave.legacy.weave.weave_types import Function, TimestampBinType NICE_BIN_SIZES_SEC = [ # TODO: will need more steps along here for smooth zooming. diff --git a/weave/legacy/ops_primitives/type.py b/weave/legacy/weave/ops_primitives/type.py similarity index 85% rename from weave/legacy/ops_primitives/type.py rename to weave/legacy/weave/ops_primitives/type.py index 4b39df3143e..a2575507f75 100644 --- a/weave/legacy/ops_primitives/type.py +++ b/weave/legacy/weave/ops_primitives/type.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy.api import op +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.api import op @op( diff --git a/weave/legacy/ops_primitives/weave_api.py b/weave/legacy/weave/ops_primitives/weave_api.py similarity index 99% rename from weave/legacy/ops_primitives/weave_api.py rename to weave/legacy/weave/ops_primitives/weave_api.py index 86761b21d95..0113200e004 100644 --- a/weave/legacy/ops_primitives/weave_api.py +++ b/weave/legacy/weave/ops_primitives/weave_api.py @@ -23,7 +23,7 @@ runs, uris, ) -from weave.legacy.graph import Node +from weave.legacy.weave.graph import Node @weave_class(weave_type=types.RefType) @@ -636,7 +636,7 @@ def append( @op(mutation=True, name="stream_table-log") def stream_table_log(self: graph.Node, val: typing.Any) -> typing.Any: st_obj = weave_internal.use(self) - from weave.legacy.monitoring import StreamTable + from weave.legacy.weave.monitoring import StreamTable if not isinstance(st_obj, StreamTable): raise errors.WeaveInternalError( diff --git a/weave/legacy/panel.py b/weave/legacy/weave/panel.py similarity index 95% rename from weave/legacy/panel.py rename to weave/legacy/weave/panel.py index 87af22bca47..4938235861e 100644 --- a/weave/legacy/panel.py +++ b/weave/legacy/weave/panel.py @@ -3,10 +3,10 @@ import typing from tarfile import DEFAULT_FORMAT -from weave.legacy import api as weave -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import graph, panel_util, errors, storage +from weave.legacy.weave import api as weave +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph, panel_util, errors, storage def run_variable_lambdas( @@ -157,6 +157,6 @@ def to_json(self): } def _ipython_display_(self): - from weave.legacy import show + from weave.legacy.weave import show show.show(self) diff --git a/weave/legacy/panel_util.py b/weave/legacy/weave/panel_util.py similarity index 80% rename from weave/legacy/panel_util.py rename to weave/legacy/weave/panel_util.py index 5c86926c92e..87c28b25f1d 100644 --- a/weave/legacy/panel_util.py +++ b/weave/legacy/weave/panel_util.py @@ -1,12 +1,12 @@ import typing -from weave.legacy import weave_types -from weave.legacy import storage, graph, panel +from weave.legacy.weave import weave_types +from weave.legacy.weave import storage, graph, panel def make_node(v: typing.Any) -> graph.Node: """Business logic for how values passed to panels are converted to json.""" - from weave.legacy import ops + from weave.legacy.weave import ops if isinstance(v, graph.Node): return v diff --git a/weave/legacy/weave/panels/__init__.py b/weave/legacy/weave/panels/__init__.py new file mode 100644 index 00000000000..1ba950300b7 --- /dev/null +++ b/weave/legacy/weave/panels/__init__.py @@ -0,0 +1,70 @@ +from weave.legacy.weave import context_state as _context_state + +_loading_builtins_token = _context_state.set_loading_built_ins() + +from weave.legacy.weave.panels.panel_auto import * + +# basic +from weave.legacy.weave.panels.panel_basic import * + +# top level board +from weave.legacy.weave.panels.panel_board import Board, BoardPanel, BoardPanelLayout +from weave.legacy.weave.panels.panel_card import Card, CardTab +from weave.legacy.weave.panels.panel_color import Color +from weave.legacy.weave.panels.panel_daterange import DateRange + +# domain +from weave.legacy.weave.panels.panel_domain import * +from weave.legacy.weave.panels.panel_dropdown import Dropdown, DropdownConfig +from weave.legacy.weave.panels.panel_each import Each +from weave.legacy.weave.panels.panel_each_column import EachColumn + +# special +from weave.legacy.weave.panels.panel_expression import * +from weave.legacy.weave.panels.panel_facet import Facet +from weave.legacy.weave.panels.panel_facet_tabs import FacetTabs +from weave.legacy.weave.panels.panel_filter_editor import FilterEditor +from weave.legacy.weave.panels.panel_function_editor import ( + FunctionEditor, + FunctionEditorConfig, +) +from weave.legacy.weave.panels.panel_group import ( + Group, + GroupLayoutFlow, + GroupPanel, + GroupPanelLayout, +) +from weave.legacy.weave.panels.panel_grouping_editor import GroupingEditor + +# Incomplete +from weave.legacy.weave.panels.panel_histogram import * +from weave.legacy.weave.panels.panel_html import PanelHtml + +# layout +from weave.legacy.weave.panels.panel_labeled_item import LabeledItem + +# legacy +from weave.legacy.weave.panels.panel_legacy import * +from weave.legacy.weave.panels.panel_markdown import PanelMarkdown + +# Non-standard editor (todo: update) +from weave.legacy.weave.panels.panel_object_picker import ObjectPicker, ObjectPickerConfig +from weave.legacy.weave.panels.panel_plot import Plot, PlotConstants, Series + +# sidebar specific +from weave.legacy.weave.panels.panel_query import Query, QueryCondition, QueryConfig +from weave.legacy.weave.panels.panel_sections import Sections +from weave.legacy.weave.panels.panel_select import SelectEditor, SelectEditorConfig + +# editors +from weave.legacy.weave.panels.panel_slider import Slider, SliderConfig +from weave.legacy.weave.panels.panel_string import PanelString +from weave.legacy.weave.panels.panel_string_editor import StringEditor +from weave.legacy.weave.panels.panel_table import ColumnDef, Table, TableColumn +from weave.legacy.weave.panels.panel_trace import Trace + +# navigation +from weave.legacy.weave.panels.panel_weavelink import WeaveLink +from weave.legacy.weave.panel import Panel + +_context_state.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/panels/bank.py b/weave/legacy/weave/panels/bank.py similarity index 93% rename from weave/legacy/panels/bank.py rename to weave/legacy/weave/panels/bank.py index b5762d9fe38..0568ae389c2 100644 --- a/weave/legacy/panels/bank.py +++ b/weave/legacy/weave/panels/bank.py @@ -1,6 +1,6 @@ # TODO: move all panelbank stuff here -from weave.legacy.panels import panel_group +from weave.legacy.weave.panels import panel_group def default_panel_bank_flow_section_config(): diff --git a/weave/legacy/panels/panel_auto.py b/weave/legacy/weave/panels/panel_auto.py similarity index 77% rename from weave/legacy/panels/panel_auto.py rename to weave/legacy/weave/panels/panel_auto.py index a50fe9f4170..f97027755b9 100644 --- a/weave/legacy/panels/panel_auto.py +++ b/weave/legacy/weave/panels/panel_auto.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel # Currently Auto is not a real panel, the system handles it. diff --git a/weave/legacy/panels/panel_basic.py b/weave/legacy/weave/panels/panel_basic.py similarity index 84% rename from weave/legacy/panels/panel_basic.py rename to weave/legacy/weave/panels/panel_basic.py index ffabed4b613..8ae26009cf2 100644 --- a/weave/legacy/panels/panel_basic.py +++ b/weave/legacy/weave/panels/panel_basic.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel @weave.type() diff --git a/weave/legacy/panels/panel_board.py b/weave/legacy/weave/panels/panel_board.py similarity index 92% rename from weave/legacy/panels/panel_board.py rename to weave/legacy/weave/panels/panel_board.py index 0db30de2a27..ab14570b1ef 100644 --- a/weave/legacy/panels/panel_board.py +++ b/weave/legacy/weave/panels/panel_board.py @@ -2,9 +2,9 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import panel -from weave.legacy.panels import panel_group +from weave.legacy.weave import weave_internal +from weave.legacy.weave import panel +from weave.legacy.weave.panels import panel_group @dataclasses.dataclass @@ -51,7 +51,7 @@ def varbar(editable=True, items=None) -> panel_group.Group: def main() -> panel_group.Group: - return weave.legacy.panels.Group( + return weave.legacy.weave.panels.Group( layoutMode="grid", showExpressions=True, enableAddPanel=True, @@ -65,11 +65,11 @@ def Board( ): showExpressions = True if editable else "titleBar" vb = vars - if not isinstance(vb, weave.legacy.panels.Group): + if not isinstance(vb, weave.legacy.weave.panels.Group): vb = varbar(editable=editable, items=vars) main = panels - if not isinstance(panels, weave.legacy.panels.Group): + if not isinstance(panels, weave.legacy.weave.panels.Group): main_items = {} main_panel_layouts: list[panel_group.LayedOutPanel] = [] for i, p in enumerate(panels): diff --git a/weave/legacy/panels/panel_card.py b/weave/legacy/weave/panels/panel_card.py similarity index 93% rename from weave/legacy/panels/panel_card.py rename to weave/legacy/weave/panels/panel_card.py index 8979583edf4..0eacbcba710 100644 --- a/weave/legacy/panels/panel_card.py +++ b/weave/legacy/weave/panels/panel_card.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel, panel_util +from weave.legacy.weave import graph, panel, panel_util CardContentType = typing.TypeVar("CardContentType") diff --git a/weave/legacy/panels/panel_color.py b/weave/legacy/weave/panels/panel_color.py similarity index 81% rename from weave/legacy/panels/panel_color.py rename to weave/legacy/weave/panels/panel_color.py index eb44072878e..f984a7fbba4 100644 --- a/weave/legacy/panels/panel_color.py +++ b/weave/legacy/weave/panels/panel_color.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel, panel_util +from weave.legacy.weave import graph, panel, panel_util @weave.type() diff --git a/weave/legacy/panels/panel_daterange.py b/weave/legacy/weave/panels/panel_daterange.py similarity index 93% rename from weave/legacy/panels/panel_daterange.py rename to weave/legacy/weave/panels/panel_daterange.py index 262c64e5410..f06df0b3c75 100644 --- a/weave/legacy/panels/panel_daterange.py +++ b/weave/legacy/weave/panels/panel_daterange.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel, panel_util +from weave.legacy.weave import graph, panel, panel_util @weave.type() diff --git a/weave/legacy/panels/panel_domain.py b/weave/legacy/weave/panels/panel_domain.py similarity index 90% rename from weave/legacy/panels/panel_domain.py rename to weave/legacy/weave/panels/panel_domain.py index f3d0ad9307b..535edd09dab 100644 --- a/weave/legacy/panels/panel_domain.py +++ b/weave/legacy/weave/panels/panel_domain.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel @weave.type(__override_name="wb_trace_tree-traceViewer") # type: ignore diff --git a/weave/legacy/panels/panel_dropdown.py b/weave/legacy/weave/panels/panel_dropdown.py similarity index 88% rename from weave/legacy/panels/panel_dropdown.py rename to weave/legacy/weave/panels/panel_dropdown.py index e39d4e92a87..6eda21e6563 100644 --- a/weave/legacy/panels/panel_dropdown.py +++ b/weave/legacy/weave/panels/panel_dropdown.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel, panel_util +from weave.legacy.weave import graph, panel, panel_util @weave.type() @@ -28,7 +28,7 @@ def __init__( self.config = DropdownConfig() if "choices" in options: self.config.choices = options["choices"] - if isinstance(self.input_node, weave.legacy.graph.VoidNode): + if isinstance(self.input_node, weave.legacy.weave.graph.VoidNode): choices_type = typing.cast(weave.types.List, self.config.choices.type) self.input_node = weave_internal.const( [], weave.types.List(choices_type.object_type) diff --git a/weave/legacy/panels/panel_each.py b/weave/legacy/weave/panels/panel_each.py similarity index 82% rename from weave/legacy/panels/panel_each.py rename to weave/legacy/weave/panels/panel_each.py index ad24f7646a6..6ae0188c5e5 100644 --- a/weave/legacy/panels/panel_each.py +++ b/weave/legacy/weave/panels/panel_each.py @@ -2,9 +2,9 @@ import typing import weave -from weave.legacy import graph, panel -from weave.legacy.panels.bank import default_panel_bank_flow_section_config -from weave.legacy.panels.panel_group import PanelBankSectionConfig +from weave.legacy.weave import graph, panel +from weave.legacy.weave.panels.bank import default_panel_bank_flow_section_config +from weave.legacy.weave.panels.panel_group import PanelBankSectionConfig PanelType = typing.TypeVar("PanelType") diff --git a/weave/legacy/panels/panel_each_column.py b/weave/legacy/weave/panels/panel_each_column.py similarity index 84% rename from weave/legacy/panels/panel_each_column.py rename to weave/legacy/weave/panels/panel_each_column.py index 3df7f7c9625..04b6f01aaa4 100644 --- a/weave/legacy/panels/panel_each_column.py +++ b/weave/legacy/weave/panels/panel_each_column.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import graph, panel -from weave.legacy.panels.panel_group import PanelBankSectionConfig +from weave.legacy.weave import graph, panel +from weave.legacy.weave.panels.panel_group import PanelBankSectionConfig RenderType = typing.TypeVar("RenderType") diff --git a/weave/legacy/panels/panel_expression.py b/weave/legacy/weave/panels/panel_expression.py similarity index 68% rename from weave/legacy/panels/panel_expression.py rename to weave/legacy/weave/panels/panel_expression.py index 3b432283e09..0610f093107 100644 --- a/weave/legacy/panels/panel_expression.py +++ b/weave/legacy/weave/panels/panel_expression.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel @weave.type() diff --git a/weave/legacy/panels/panel_facet.py b/weave/legacy/weave/panels/panel_facet.py similarity index 86% rename from weave/legacy/panels/panel_facet.py rename to weave/legacy/weave/panels/panel_facet.py index 24c4b42d663..bfaa9c3378f 100644 --- a/weave/legacy/panels/panel_facet.py +++ b/weave/legacy/weave/panels/panel_facet.py @@ -2,11 +2,11 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import graph, panel, panel_util -from weave.legacy.arrow import list_ -from weave.legacy.panels import table_state +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import graph, panel, panel_util +from weave.legacy.weave.arrow import list_ +from weave.legacy.weave.panels import table_state @weave.type() @@ -38,10 +38,10 @@ class FacetConfig: default_factory=lambda: None ) xAxisLabel: weave.Node[str] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) yAxisLabel: weave.Node[str] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) @@ -120,14 +120,14 @@ def selected(self): ) x_fn = self.config.table.columnSelectFunctions[self.config.dims.x] y_fn = self.config.table.columnSelectFunctions[self.config.dims.y] - filtered = weave.legacy.ops.List.filter( + filtered = weave.legacy.weave.ops.List.filter( self.input_node, - lambda item: weave.legacy.ops.Boolean.bool_and( - weave.legacy.ops.String.__eq__( + lambda item: weave.legacy.weave.ops.Boolean.bool_and( + weave.legacy.weave.ops.String.__eq__( x_fn, self.config.selectedCell["x"], ), - weave.legacy.ops.String.__eq__( + weave.legacy.weave.ops.String.__eq__( y_fn, self.config.selectedCell["y"], ), diff --git a/weave/legacy/panels/panel_facet_tabs.py b/weave/legacy/weave/panels/panel_facet_tabs.py similarity index 73% rename from weave/legacy/panels/panel_facet_tabs.py rename to weave/legacy/weave/panels/panel_facet_tabs.py index 799c84dbccb..2a62ebea446 100644 --- a/weave/legacy/panels/panel_facet_tabs.py +++ b/weave/legacy/weave/panels/panel_facet_tabs.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import graph, panel -from weave.legacy.panels.panel_group import PanelBankSectionConfig +from weave.legacy.weave import graph, panel +from weave.legacy.weave.panels.panel_group import PanelBankSectionConfig RenderType = typing.TypeVar("RenderType") @@ -11,7 +11,7 @@ @weave.type() class FacetTabsConfig(typing.Generic[RenderType]): tab: weave.Node[typing.Optional[typing.Any]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) panel: RenderType = dataclasses.field(default_factory=lambda: graph.VoidNode()) # type: ignore diff --git a/weave/legacy/panels/panel_filter_editor.py b/weave/legacy/weave/panels/panel_filter_editor.py similarity index 94% rename from weave/legacy/panels/panel_filter_editor.py rename to weave/legacy/weave/panels/panel_filter_editor.py index ccf5ceca6e8..c04e3b07818 100644 --- a/weave/legacy/panels/panel_filter_editor.py +++ b/weave/legacy/weave/panels/panel_filter_editor.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel +from weave.legacy.weave import graph, panel ExpressionType = typing.TypeVar("ExpressionType") diff --git a/weave/legacy/panels/panel_function_editor.py b/weave/legacy/weave/panels/panel_function_editor.py similarity index 91% rename from weave/legacy/panels/panel_function_editor.py rename to weave/legacy/weave/panels/panel_function_editor.py index d8b5e2a13b2..dc3f3f488f3 100644 --- a/weave/legacy/panels/panel_function_editor.py +++ b/weave/legacy/weave/panels/panel_function_editor.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel +from weave.legacy.weave import graph, panel ExpressionType = typing.TypeVar("ExpressionType") diff --git a/weave/legacy/panels/panel_group.py b/weave/legacy/weave/panels/panel_group.py similarity index 97% rename from weave/legacy/panels/panel_group.py rename to weave/legacy/weave/panels/panel_group.py index 3c4046c7410..54e7566e501 100644 --- a/weave/legacy/panels/panel_group.py +++ b/weave/legacy/weave/panels/panel_group.py @@ -3,8 +3,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import ( +from weave.legacy.weave import weave_internal +from weave.legacy.weave import ( errors, codifiable_value_mixin, codify, @@ -13,11 +13,11 @@ panel, panel_util, ) -from weave.legacy.panels.bank import ( +from weave.legacy.weave.panels.bank import ( default_panel_bank_flow_section_config, flow_layout, ) -from weave.legacy.panels.panel_group_panel_info import PanelInfo +from weave.legacy.weave.panels.panel_group_panel_info import PanelInfo ItemsType = typing.TypeVar("ItemsType") @@ -337,7 +337,7 @@ def to_code(self) -> typing.Optional[str]: ",".join([f_name + "=" + f_val for f_name, f_val in field_vals]) + "," ) return ( - f"""weave.legacy.panels.panel_group.Group({input_node_str} {param_str})""" + f"""weave.legacy.weave.panels.panel_group.Group({input_node_str} {param_str})""" ) # @property diff --git a/weave/legacy/panels/panel_group_panel_info.py b/weave/legacy/weave/panels/panel_group_panel_info.py similarity index 100% rename from weave/legacy/panels/panel_group_panel_info.py rename to weave/legacy/weave/panels/panel_group_panel_info.py diff --git a/weave/legacy/panels/panel_grouping_editor.py b/weave/legacy/weave/panels/panel_grouping_editor.py similarity index 94% rename from weave/legacy/panels/panel_grouping_editor.py rename to weave/legacy/weave/panels/panel_grouping_editor.py index ebaa9a7e7fd..ce608643040 100644 --- a/weave/legacy/panels/panel_grouping_editor.py +++ b/weave/legacy/weave/panels/panel_grouping_editor.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel +from weave.legacy.weave import graph, panel ExpressionType = typing.TypeVar("ExpressionType") diff --git a/weave/legacy/panels/panel_histogram.py b/weave/legacy/weave/panels/panel_histogram.py similarity index 86% rename from weave/legacy/panels/panel_histogram.py rename to weave/legacy/weave/panels/panel_histogram.py index 6daa14a64bb..79a91e9f726 100644 --- a/weave/legacy/panels/panel_histogram.py +++ b/weave/legacy/weave/panels/panel_histogram.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel # TODO: This id conflicts with the histogram type! Panel types # should automatically have Panel in the name but they don't at the moment. diff --git a/weave/legacy/panels/panel_html.py b/weave/legacy/weave/panels/panel_html.py similarity index 90% rename from weave/legacy/panels/panel_html.py rename to weave/legacy/weave/panels/panel_html.py index 49d9d5b214a..bba1afb0b55 100644 --- a/weave/legacy/panels/panel_html.py +++ b/weave/legacy/weave/panels/panel_html.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import graph, ops, panel, panel_util +from weave.legacy.weave import graph, ops, panel, panel_util @weave.type() diff --git a/weave/legacy/panels/panel_labeled_item.py b/weave/legacy/weave/panels/panel_labeled_item.py similarity index 93% rename from weave/legacy/panels/panel_labeled_item.py rename to weave/legacy/weave/panels/panel_labeled_item.py index 040038accb6..08c5703dc7f 100644 --- a/weave/legacy/panels/panel_labeled_item.py +++ b/weave/legacy/weave/panels/panel_labeled_item.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import graph, panel, panel_util +from weave.legacy.weave import graph, panel, panel_util ItemType = typing.TypeVar("ItemType") diff --git a/weave/legacy/panels/panel_legacy.py b/weave/legacy/weave/panels/panel_legacy.py similarity index 98% rename from weave/legacy/panels/panel_legacy.py rename to weave/legacy/weave/panels/panel_legacy.py index 4673c7ab3e8..9e7c255dd1d 100644 --- a/weave/legacy/panels/panel_legacy.py +++ b/weave/legacy/weave/panels/panel_legacy.py @@ -73,7 +73,7 @@ class LPanel: def define_panel(p: LPanel): - class DummyClass(weave.legacy.panel.Panel): + class DummyClass(weave.legacy.weave.panel.Panel): id = p.panel_id type_cache.append(DummyClass) diff --git a/weave/legacy/panels/panel_markdown.py b/weave/legacy/weave/panels/panel_markdown.py similarity index 90% rename from weave/legacy/panels/panel_markdown.py rename to weave/legacy/weave/panels/panel_markdown.py index f86f5b8b1a8..fc489f6efc3 100644 --- a/weave/legacy/panels/panel_markdown.py +++ b/weave/legacy/weave/panels/panel_markdown.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import graph, ops, panel, panel_util +from weave.legacy.weave import graph, ops, panel, panel_util @weave.type() diff --git a/weave/legacy/panels/panel_object_picker.py b/weave/legacy/weave/panels/panel_object_picker.py similarity index 95% rename from weave/legacy/panels/panel_object_picker.py rename to weave/legacy/weave/panels/panel_object_picker.py index c3ca313c4cc..35a7321166c 100644 --- a/weave/legacy/panels/panel_object_picker.py +++ b/weave/legacy/weave/panels/panel_object_picker.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import graph, panel +from weave.legacy.weave import weave_internal +from weave.legacy.weave import graph, panel ChoiceType = typing.TypeVar("ChoiceType") diff --git a/weave/legacy/panels/panel_plot.py b/weave/legacy/weave/panels/panel_plot.py similarity index 98% rename from weave/legacy/panels/panel_plot.py rename to weave/legacy/weave/panels/panel_plot.py index 6386d26e719..a14469bec3a 100644 --- a/weave/legacy/panels/panel_plot.py +++ b/weave/legacy/weave/panels/panel_plot.py @@ -4,12 +4,12 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import errors, codifiable_value_mixin, codify, graph, panel -from weave.legacy.ops_primitives import boolean, list_ -from weave.legacy.ops_primitives import dict as dict_ -from weave.legacy.panels import panel_table, table_state +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import errors, codifiable_value_mixin, codify, graph, panel +from weave.legacy.weave.ops_primitives import boolean, list_ +from weave.legacy.weave.ops_primitives import dict as dict_ +from weave.legacy.weave.panels import panel_table, table_state @weave.type() @@ -696,7 +696,7 @@ def to_code(self) -> typing.Optional[str]: param_str = ( ",".join([f_name + "=" + f_val for f_name, f_val in field_vals]) + "," ) - return f"""weave.legacy.panels.panel_plot.Plot({codify.object_to_code_no_format(self.input_node)}, {param_str})""" + return f"""weave.legacy.weave.panels.panel_plot.Plot({codify.object_to_code_no_format(self.input_node)}, {param_str})""" def make_set_all_series(dim_name: str) -> typing.Callable[[Plot, typing.Any], None]: diff --git a/weave/legacy/panels/panel_query.py b/weave/legacy/weave/panels/panel_query.py similarity index 89% rename from weave/legacy/panels/panel_query.py rename to weave/legacy/weave/panels/panel_query.py index e59d4b666e8..0336cd9e0f7 100644 --- a/weave/legacy/panels/panel_query.py +++ b/weave/legacy/weave/panels/panel_query.py @@ -2,9 +2,9 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import graph, panel -from weave.legacy.panels import table_state +from weave.legacy.weave import weave_internal +from weave.legacy.weave import graph, panel +from weave.legacy.weave.panels import table_state @weave.type() @@ -18,7 +18,7 @@ class QueryDimsConfig: @weave.type() class QueryCondition: expression: weave.Node[typing.Any] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) editor: EditorType = dataclasses.field(default_factory=lambda: graph.VoidNode()) # type: ignore @@ -76,7 +76,7 @@ def selected(self): and self.config.tableState.preFilterFunction is not None and self.config.tableState.preFilterFunction.type != weave.types.Invalid() ): - table_node = weave.legacy.ops.List.filter( + table_node = weave.legacy.weave.ops.List.filter( table_node, lambda row: weave_internal.call_fn( self.config.tableState.preFilterFunction, {"row": row} diff --git a/weave/legacy/panels/panel_sections.py b/weave/legacy/weave/panels/panel_sections.py similarity index 73% rename from weave/legacy/panels/panel_sections.py rename to weave/legacy/weave/panels/panel_sections.py index 5808d0ad407..e23cbfff43b 100644 --- a/weave/legacy/panels/panel_sections.py +++ b/weave/legacy/weave/panels/panel_sections.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import graph, panel -from weave.legacy.panels.panel_group import PanelBankSectionConfig +from weave.legacy.weave import graph, panel +from weave.legacy.weave.panels.panel_group import PanelBankSectionConfig RenderType = typing.TypeVar("RenderType") @@ -11,7 +11,7 @@ @weave.type() class SectionsConfig(typing.Generic[RenderType]): section: weave.Node[typing.Optional[typing.Any]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) panel: RenderType = dataclasses.field(default_factory=lambda: graph.VoidNode()) # type: ignore diff --git a/weave/legacy/panels/panel_select.py b/weave/legacy/weave/panels/panel_select.py similarity index 85% rename from weave/legacy/panels/panel_select.py rename to weave/legacy/weave/panels/panel_select.py index 3ab67039bae..07ca97bde51 100644 --- a/weave/legacy/panels/panel_select.py +++ b/weave/legacy/weave/panels/panel_select.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import graph, panel +from weave.legacy.weave import weave_internal +from weave.legacy.weave import graph, panel @weave.type() @@ -27,7 +27,7 @@ def __init__(self, input_node=graph.VoidNode(), vars=None, config=None, **option self.config = SelectEditorConfig() if "choices" in options: self.config.choices = options["choices"] - if isinstance(self.input_node, weave.legacy.graph.VoidNode): + if isinstance(self.input_node, weave.legacy.weave.graph.VoidNode): # TODO: not string! self.input_node = weave_internal.const( [], weave.types.List(self.config.choices.type.object_type) diff --git a/weave/legacy/panels/panel_sidebarcontainer.py b/weave/legacy/weave/panels/panel_sidebarcontainer.py similarity index 89% rename from weave/legacy/panels/panel_sidebarcontainer.py rename to weave/legacy/weave/panels/panel_sidebarcontainer.py index f568b6649f8..23ec5927233 100644 --- a/weave/legacy/panels/panel_sidebarcontainer.py +++ b/weave/legacy/weave/panels/panel_sidebarcontainer.py @@ -1,5 +1,5 @@ -from weave.legacy import graph -from weave.legacy.panels import panel +from weave.legacy.weave import graph +from weave.legacy.weave.panels import panel class VerticalContainerConfig: diff --git a/weave/legacy/panels/panel_slider.py b/weave/legacy/weave/panels/panel_slider.py similarity index 85% rename from weave/legacy/panels/panel_slider.py rename to weave/legacy/weave/panels/panel_slider.py index cbe568a4876..b7eef8aceb4 100644 --- a/weave/legacy/panels/panel_slider.py +++ b/weave/legacy/weave/panels/panel_slider.py @@ -2,8 +2,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import panel +from weave.legacy.weave import weave_internal +from weave.legacy.weave import panel @weave.type() @@ -28,7 +28,7 @@ class Slider(panel.Panel): def __post_init__(self, *args): super().__post_init__(*args) - if isinstance(self.input_node, weave.legacy.graph.VoidNode): + if isinstance(self.input_node, weave.legacy.weave.graph.VoidNode): self.__dict__["input_node"] = weave_internal.const(0) @weave.op() diff --git a/weave/legacy/panels/panel_string.py b/weave/legacy/weave/panels/panel_string.py similarity index 94% rename from weave/legacy/panels/panel_string.py rename to weave/legacy/weave/panels/panel_string.py index 0b61a56fe03..58cecb54794 100644 --- a/weave/legacy/panels/panel_string.py +++ b/weave/legacy/weave/panels/panel_string.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import panel +from weave.legacy.weave import panel ModeOption = typing.Literal["plaintext", "markdown", "diff"] diff --git a/weave/legacy/panels/panel_string_editor.py b/weave/legacy/weave/panels/panel_string_editor.py similarity index 82% rename from weave/legacy/panels/panel_string_editor.py rename to weave/legacy/weave/panels/panel_string_editor.py index a6f6bdae949..3aeb0f440d6 100644 --- a/weave/legacy/panels/panel_string_editor.py +++ b/weave/legacy/weave/panels/panel_string_editor.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel @weave.type() diff --git a/weave/legacy/panels/panel_table.py b/weave/legacy/weave/panels/panel_table.py similarity index 93% rename from weave/legacy/panels/panel_table.py rename to weave/legacy/weave/panels/panel_table.py index a359e7b01c1..e9fb6b0760d 100644 --- a/weave/legacy/panels/panel_table.py +++ b/weave/legacy/weave/panels/panel_table.py @@ -2,11 +2,11 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import codifiable_value_mixin, codify, panel -from weave.legacy.graph import ConstNode, Node, OutputNode, VoidNode -from weave.legacy.panels import table_state -from weave.legacy.panels.panel_query import Query +from weave.legacy.weave import weave_internal +from weave.legacy.weave import codifiable_value_mixin, codify, panel +from weave.legacy.weave.graph import ConstNode, Node, OutputNode, VoidNode +from weave.legacy.weave.panels import table_state +from weave.legacy.weave.panels.panel_query import Query @weave.type("tablePanelConfig") @@ -20,7 +20,7 @@ class TableConfig: class ColumnDef(typing.TypedDict): columnName: str - columnSelectFunction: weave.legacy.graph.Node + columnSelectFunction: weave.legacy.weave.graph.Node @dataclasses.dataclass @@ -128,7 +128,7 @@ def to_code(self) -> typing.Optional[str]: param_str = ( ",".join([f_name + "=" + f_val for f_name, f_val in field_vals]) + "," ) - return f"""weave.legacy.panels.panel_table.Table({codify.object_to_code_no_format(self.input_node)}, {param_str})""" + return f"""weave.legacy.weave.panels.panel_table.Table({codify.object_to_code_no_format(self.input_node)}, {param_str})""" def add_column( self, @@ -165,14 +165,14 @@ def _get_composite_group_key(self: typing.Union[Table, Query]) -> str: # TODO: preserve arrow def _get_pinned_node(self: typing.Union[Table, Query], data_or_rows_node: Node) -> Node: if self.config is None: - return weave.legacy.ops.make_list() + return weave.legacy.weave.ops.make_list() composite_group_key = _get_composite_group_key(self) pinned_data = self.config.pinnedRows.get(composite_group_key) if pinned_data is None or len(pinned_data) == 0: - return weave.legacy.ops.make_list() + return weave.legacy.weave.ops.make_list() - return weave.legacy.ops.make_list( + return weave.legacy.weave.ops.make_list( **{ f"v_{pin_ndx}": OutputNode( data_or_rows_node.type, @@ -213,7 +213,7 @@ def _get_rows_node(self: Table, apply_sort: bool = True) -> Node: and self.config.tableState.preFilterFunction is not None and self.config.tableState.preFilterFunction.type != weave.types.Invalid() ): - data_node = weave.legacy.ops.List.filter( + data_node = weave.legacy.weave.ops.List.filter( data_node, lambda row, index: weave_internal.call_fn( self.config.tableState.preFilterFunction, {"row": row, "index": index} @@ -226,9 +226,9 @@ def _get_rows_node(self: Table, apply_sort: bool = True) -> Node: group_ids: typing.Set[str] = set() if self.config and self.config.tableState.groupBy: group_ids = set(self.config.tableState.groupBy) - data_node = weave.legacy.ops.List.groupby( + data_node = weave.legacy.weave.ops.List.groupby( data_node, - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{ columns[col_id]["columnName"]: weave_internal.call_fn( columns[col_id]["columnSelectFunction"], @@ -242,9 +242,9 @@ def _get_rows_node(self: Table, apply_sort: bool = True) -> Node: ) # Apply Selection - data_node = weave.legacy.ops.List.map( + data_node = weave.legacy.weave.ops.List.map( data_node, - lambda row, index: weave.legacy.ops.dict_( + lambda row, index: weave.legacy.weave.ops.dict_( **{ col_def["columnName"]: ( weave_internal.call_fn( @@ -275,9 +275,9 @@ def make_sort_fn(sort_def, row_node): # else: # return row_node[col_name] - data_node = weave.legacy.ops.List.sort( + data_node = weave.legacy.weave.ops.List.sort( data_node, - lambda row: weave.legacy.ops.make_list( + lambda row: weave.legacy.weave.ops.make_list( **{ f"{sort_ndx}": make_sort_fn(sort_def, row) for sort_ndx, sort_def in enumerate(sort_defs) diff --git a/weave/legacy/panels/panel_trace.py b/weave/legacy/weave/panels/panel_trace.py similarity index 94% rename from weave/legacy/panels/panel_trace.py rename to weave/legacy/weave/panels/panel_trace.py index 5c002a4071b..cd8f0ff4f56 100644 --- a/weave/legacy/panels/panel_trace.py +++ b/weave/legacy/weave/panels/panel_trace.py @@ -2,7 +2,7 @@ import typing import weave -from weave.legacy import panel +from weave.legacy.weave import panel span_typed_dict_type = weave.types.TypedDict( { @@ -62,4 +62,4 @@ def __init__(self, input_node, vars=None, config=None, **options): ) def active_span(self: Trace): index = 0 if self.config is None else self.config.selectedSpanIndex - return weave.legacy.ops_arrow.list_ops.index(self.input_node, index) + return weave.legacy.weave.ops_arrow.list_ops.index(self.input_node, index) diff --git a/weave/legacy/panels/panel_trace_span.py b/weave/legacy/weave/panels/panel_trace_span.py similarity index 85% rename from weave/legacy/panels/panel_trace_span.py rename to weave/legacy/weave/panels/panel_trace_span.py index 460d79bcbfa..35b565d6350 100644 --- a/weave/legacy/panels/panel_trace_span.py +++ b/weave/legacy/weave/panels/panel_trace_span.py @@ -1,5 +1,5 @@ import weave -from weave.legacy import panel +from weave.legacy.weave import panel @weave.type("traceSpanPanel") diff --git a/weave/legacy/panels/panel_weavelink.py b/weave/legacy/weave/panels/panel_weavelink.py similarity index 89% rename from weave/legacy/panels/panel_weavelink.py rename to weave/legacy/weave/panels/panel_weavelink.py index 095926dd167..1d822212323 100644 --- a/weave/legacy/panels/panel_weavelink.py +++ b/weave/legacy/weave/panels/panel_weavelink.py @@ -2,14 +2,14 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import graph, ops, panel, panel_util +from weave.legacy.weave import weave_internal +from weave.legacy.weave import graph, ops, panel, panel_util @weave.type() class WeaveLinkConfig: to: weave.Node[typing.Optional[typing.Any]] = dataclasses.field( - default_factory=lambda: weave.legacy.graph.VoidNode() + default_factory=lambda: weave.legacy.weave.graph.VoidNode() ) vars: typing.Dict[str, graph.Node] = dataclasses.field(default_factory=dict) diff --git a/weave/legacy/panels/table_state.py b/weave/legacy/weave/panels/table_state.py similarity index 97% rename from weave/legacy/panels/table_state.py rename to weave/legacy/weave/panels/table_state.py index 460585136b8..0a333efe967 100644 --- a/weave/legacy/panels/table_state.py +++ b/weave/legacy/weave/panels/table_state.py @@ -7,9 +7,9 @@ import string import typing -from weave.legacy import weave_internal -from weave.legacy import decorator_type, graph, ops, panel, weave_types -from weave.legacy.language_features.tagging import tagged_value_type +from weave.legacy.weave import weave_internal +from weave.legacy.weave import decorator_type, graph, ops, panel, weave_types +from weave.legacy.weave.language_features.tagging import tagged_value_type @decorator_type.type() diff --git a/weave/legacy/panels_py/__init__.py b/weave/legacy/weave/panels_py/__init__.py similarity index 64% rename from weave/legacy/panels_py/__init__.py rename to weave/legacy/weave/panels_py/__init__.py index 3b262288025..90abece1622 100644 --- a/weave/legacy/panels_py/__init__.py +++ b/weave/legacy/weave/panels_py/__init__.py @@ -1,8 +1,8 @@ -from weave.legacy import context_state as _context_state +from weave.legacy.weave import context_state as _context_state _loading_builtins_token = _context_state.set_loading_built_ins() -from weave.legacy.panels_py import ( +from weave.legacy.weave.panels_py import ( generator_templates, panel_autoboard, panel_llm_monitor, @@ -12,6 +12,6 @@ ) # This is just an example, uncomment to enable -# from weave.legacy.panels_py import panel_example_custom_board +# from weave.legacy.weave.panels_py import panel_example_custom_board _context_state.clear_loading_built_ins(_loading_builtins_token) diff --git a/weave/legacy/panels_py/generator_templates.py b/weave/legacy/weave/panels_py/generator_templates.py similarity index 97% rename from weave/legacy/panels_py/generator_templates.py rename to weave/legacy/weave/panels_py/generator_templates.py index 2aa6e524424..63cf1d0c6c8 100644 --- a/weave/legacy/panels_py/generator_templates.py +++ b/weave/legacy/weave/panels_py/generator_templates.py @@ -18,8 +18,8 @@ import dataclasses import typing -from weave.legacy import weave_types -from weave.legacy import decorator_op, graph, registry_mem +from weave.legacy.weave import weave_types +from weave.legacy.weave import decorator_op, graph, registry_mem @dataclasses.dataclass diff --git a/weave/legacy/panels_py/instructions/panel_autoboard.md b/weave/legacy/weave/panels_py/instructions/panel_autoboard.md similarity index 100% rename from weave/legacy/panels_py/instructions/panel_autoboard.md rename to weave/legacy/weave/panels_py/instructions/panel_autoboard.md diff --git a/weave/legacy/panels_py/instructions/panel_llm_monitor.md b/weave/legacy/weave/panels_py/instructions/panel_llm_monitor.md similarity index 100% rename from weave/legacy/panels_py/instructions/panel_llm_monitor.md rename to weave/legacy/weave/panels_py/instructions/panel_llm_monitor.md diff --git a/weave/legacy/panels_py/instructions/panel_trace_monitor.md b/weave/legacy/weave/panels_py/instructions/panel_trace_monitor.md similarity index 93% rename from weave/legacy/panels_py/instructions/panel_trace_monitor.md rename to weave/legacy/weave/panels_py/instructions/panel_trace_monitor.md index 5a3dd39d29b..4f49b229fb0 100644 --- a/weave/legacy/panels_py/instructions/panel_trace_monitor.md +++ b/weave/legacy/weave/panels_py/instructions/panel_trace_monitor.md @@ -25,7 +25,7 @@ pip install weave If you are already using `Langchain`, simply create a tracer and add it as a callback to your next call: ```python -from weave.legacy.monitoring.langchain import WeaveTracer +from weave.legacy.weave.monitoring.langchain import WeaveTracer tracer = WeaveTracer(f"{WB_ENTITY}/{WB_PROJECT}/{WB_STREAM}") llm.run(question, callbacks=[tracer]) @@ -38,7 +38,7 @@ llm.run(question, callbacks=[tracer]) If you have existing code, the Trace decorator (and related utilities) allows you to instrument and log in a variety of formats. For example: ```python -from weave.legacy.monitoring import init_monitor +from weave.legacy.weave.monitoring import init_monitor mon = init_monitor(f"{WB_ENTITY}/{WB_PROJECT}/{WB_STREAM}") @@ -55,7 +55,7 @@ def adder(a, b): Finally, if you want to manually log span data, you can do so as well by logging directly to a StreamTable: ```python -from weave.legacy.monitoring import StreamTable +from weave.legacy.weave.monitoring import StreamTable from weave.stream_data_interfaces import TraceSpanDict st = StreamTable(f"{WB_ENTITY}/{WB_PROJECT}/{WB_STREAM}") diff --git a/weave/legacy/panels_py/panel_autoboard.py b/weave/legacy/weave/panels_py/panel_autoboard.py similarity index 84% rename from weave/legacy/panels_py/panel_autoboard.py rename to weave/legacy/weave/panels_py/panel_autoboard.py index 26ea6c6c468..810bfa88fc4 100644 --- a/weave/legacy/panels_py/panel_autoboard.py +++ b/weave/legacy/weave/panels_py/panel_autoboard.py @@ -26,10 +26,10 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy.panels import panel_plot -from weave.legacy.panels_py.generator_templates import template_registry -from weave.legacy import util +from weave.legacy.weave import weave_internal +from weave.legacy.weave.panels import panel_plot +from weave.legacy.weave.panels_py.generator_templates import template_registry +from weave.legacy.weave import util @weave.type() @@ -72,10 +72,10 @@ def timeseries( x_axis_type = input_node[x_axis_key].type.object_type # type: ignore if weave.types.optional(weave.types.Timestamp()).assign_type(x_axis_type): x_title = "" - bin_fn = weave.legacy.ops.timestamp_bins_nice + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice elif weave.types.optional(weave.types.Number()).assign_type(x_axis_type): x_title = x_axis_key - bin_fn = weave.legacy.ops.numbers_bins_equal + bin_fn = weave.legacy.weave.ops.numbers_bins_equal else: raise ValueError(f"Unsupported type for x_axis_key {x_axis_key}: {x_axis_type}") if mark == "bar": @@ -84,7 +84,7 @@ def timeseries( # TODO: should be midpoint x = lambda row: row[x_axis_key].bin(bin_fn(bin_domain_node, n_bins))["start"] - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( input_node, x=x, x_title=x_title, @@ -108,12 +108,12 @@ def timeseries_avg_line( ) -> weave.Panel: x_axis_type = input_node[x_axis_key].type.object_type # type: ignore if weave.types.optional(weave.types.Timestamp()).assign_type(x_axis_type): - bin_fn = weave.legacy.ops.timestamp_bins_nice + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice elif weave.types.optional(weave.types.Number()).assign_type(x_axis_type): - bin_fn = weave.legacy.ops.numbers_bins_equal + bin_fn = weave.legacy.weave.ops.numbers_bins_equal else: raise ValueError(f"Unsupported type for x_axis_key {x_axis_key}: {x_axis_type}") - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( input_node, x=lambda row: row[x_axis_key].bin(bin_fn(bin_domain_node, 100))["start"], x_title=x_axis_key, @@ -139,13 +139,13 @@ def timeseries_sum_bar( x_axis_type = input_node[x_axis_key].type.object_type # type: ignore if weave.types.optional(weave.types.Timestamp()).assign_type(x_axis_type): x_title = "" - bin_fn = weave.legacy.ops.timestamp_bins_nice + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice elif weave.types.optional(weave.types.Number()).assign_type(x_axis_type): x_title = x_axis_key - bin_fn = weave.legacy.ops.numbers_bins_equal + bin_fn = weave.legacy.weave.ops.numbers_bins_equal else: raise ValueError(f"Unsupported type for x_axis_key {x_axis_key}: {x_axis_type}") - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( input_node, x=lambda row: row[x_axis_key].bin(bin_fn(bin_domain_node, n_bins)), x_title=x_title, @@ -170,13 +170,13 @@ def timeseries_count_bar( x_axis_type = input_node[x_axis_key].type.object_type # type: ignore if weave.types.optional(weave.types.Timestamp()).assign_type(x_axis_type): x_title = "" - bin_fn = weave.legacy.ops.timestamp_bins_nice + bin_fn = weave.legacy.weave.ops.timestamp_bins_nice elif weave.types.optional(weave.types.Number()).assign_type(x_axis_type): x_title = x_axis_key - bin_fn = weave.legacy.ops.numbers_bins_equal + bin_fn = weave.legacy.weave.ops.numbers_bins_equal else: raise ValueError(f"Unsupported type for x_axis_key {x_axis_key}: {x_axis_type}") - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( input_node, x=lambda row: row[x_axis_key].bin(bin_fn(bin_domain_node, n_bins)), x_title=x_title, @@ -192,7 +192,7 @@ def categorical_dist( input_node: weave.Node[list[typing.Any]], key: str, ) -> weave.Panel: - return weave.legacy.panels.Plot( + return weave.legacy.weave.panels.Plot( input_node, y=lambda row: row[key], x=lambda row: row.count(), @@ -317,49 +317,49 @@ def auto_panels( panel = categorical_dist(window_data_node, key) categorical_panels.append(panel) - metrics = weave.legacy.panels.Group( - layoutMode=weave.legacy.panels.GroupLayoutFlow(2, 3), + metrics = weave.legacy.weave.panels.Group( + layoutMode=weave.legacy.weave.panels.GroupLayoutFlow(2, 3), items={"panel%s" % i: panel for i, panel in enumerate(metric_panels)}, ) - categoricals = weave.legacy.panels.Group( - layoutMode=weave.legacy.panels.GroupLayoutFlow(2, 3), + categoricals = weave.legacy.weave.panels.Group( + layoutMode=weave.legacy.weave.panels.GroupLayoutFlow(2, 3), items={"panel%s" % i: panel for i, panel in enumerate(categorical_panels)}, ) control_items = [ - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( input_node, id="data", ), # TODO: We need a filter editor. Can start with a filter expression # editor and make it more user-friendly later - weave.legacy.panels.GroupPanel( - lambda data: weave.legacy.ops.make_list( + weave.legacy.weave.panels.GroupPanel( + lambda data: weave.legacy.weave.ops.make_list( a=data[x_axis].min(), b=data[x_axis].max() ), id="data_range", hidden=True, ), - weave.legacy.panels.GroupPanel(None, id="zoom_range", hidden=True), - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel(None, id="zoom_range", hidden=True), + weave.legacy.weave.panels.GroupPanel( lambda zoom_range, data_range: zoom_range.coalesce(data_range), id="bin_range", hidden=True, ), - weave.legacy.panels.GroupPanel( - lambda data, zoom_range: weave.legacy.panels.DateRange( + weave.legacy.weave.panels.GroupPanel( + lambda data, zoom_range: weave.legacy.weave.panels.DateRange( zoom_range, domain=data[x_axis] ), id="date_picker", ), # TODO: groupby should really be a Dropdown / multi-select instead # of an expression - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( groupby, id="groupby", ), - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( lambda data, bin_range: data.filter( - lambda row: weave.legacy.ops.Boolean.bool_and( + lambda row: weave.legacy.weave.ops.Boolean.bool_and( row[x_axis] >= bin_range[0], row[x_axis] < bin_range[1] ) ), @@ -369,21 +369,21 @@ def auto_panels( ] panels = [ - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( metrics, id="metrics", - layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12), + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12), ), - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( categoricals, id="categoricals", - layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=12), + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=12), ), ] if "step" not in x_axis: panels.insert( 0, - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( timeseries_count_bar( data_node, time_domain_node, @@ -393,18 +393,18 @@ def auto_panels( 150, ), id="volume", - layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), ), ) panels.append( - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( window_data_node, id="table", - layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), ), ) - return weave.legacy.panels.Board(vars=control_items, panels=panels, editable=False) + return weave.legacy.weave.panels.Board(vars=control_items, panels=panels, editable=False) # The interface for constructing this Panel from Python @@ -415,7 +415,7 @@ class AutoBoard(weave.Panel): config: typing.Optional[AutoBoardConfig] = None @weave.op() # type: ignore - def render(self) -> weave.legacy.panels.Group: + def render(self) -> weave.legacy.weave.panels.Group: return auto_panels(self.input_node, self.config) # type: ignore @@ -426,7 +426,7 @@ def render(self) -> weave.legacy.panels.Group: def seed_autoboard( input_node: weave.Node[typing.Any], config: typing.Optional[AutoBoardConfig] = None, -) -> weave.legacy.panels.Group: +) -> weave.legacy.weave.panels.Group: return auto_panels(input_node, config) # type: ignore diff --git a/weave/legacy/panels_py/panel_eval.py b/weave/legacy/weave/panels_py/panel_eval.py similarity index 67% rename from weave/legacy/panels_py/panel_eval.py rename to weave/legacy/weave/panels_py/panel_eval.py index 95ec348fa89..3cf7afacd07 100644 --- a/weave/legacy/panels_py/panel_eval.py +++ b/weave/legacy/weave/panels_py/panel_eval.py @@ -1,5 +1,5 @@ import weave -from weave.legacy.panels import panel_board +from weave.legacy.weave.panels import panel_board # This is not yet general, it describes a board for a specific # formulation of a text extraction problem @@ -13,34 +13,34 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore summary = varbar.add( "summary", - weave.legacy.ops.make_list( - a=weave.legacy.ops.TypedDict.merge( - weave.legacy.ops.dict_(name="baseline"), + weave.legacy.weave.ops.make_list( + a=weave.legacy.weave.ops.TypedDict.merge( + weave.legacy.weave.ops.dict_(name="baseline"), baseline_eval_result["summary"], ), - b=weave.legacy.ops.TypedDict.merge( - weave.legacy.ops.dict_(name="candidate"), + b=weave.legacy.weave.ops.TypedDict.merge( + weave.legacy.weave.ops.dict_(name="candidate"), candidate_eval_result["summary"], ), ), ) - weave.legacy.ops.make_list( + weave.legacy.weave.ops.make_list( a=baseline_eval_result["eval_table"], b=baseline_eval_result["eval_table"] ) concatted_evals = varbar.add( "concatted_evals", - weave.legacy.ops.List.concat( - weave.legacy.ops.make_list( + weave.legacy.weave.ops.List.concat( + weave.legacy.weave.ops.make_list( a=baseline_eval_result["eval_table"].map( - lambda row: weave.legacy.ops.TypedDict.merge( - weave.legacy.ops.dict_(name="baseline"), row + lambda row: weave.legacy.weave.ops.TypedDict.merge( + weave.legacy.weave.ops.dict_(name="baseline"), row ) ), b=candidate_eval_result["eval_table"].map( - lambda row: weave.legacy.ops.TypedDict.merge( - weave.legacy.ops.dict_(name="candidate"), row + lambda row: weave.legacy.weave.ops.TypedDict.merge( + weave.legacy.weave.ops.dict_(name="candidate"), row ) ), ) @@ -50,8 +50,8 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore # join evals together first joined_evals = varbar.add( "joined_evals", - weave.legacy.ops.join_all( - weave.legacy.ops.make_list( + weave.legacy.weave.ops.join_all( + weave.legacy.weave.ops.make_list( a=baseline_eval_result["eval_table"], b=candidate_eval_result["eval_table"], ), @@ -63,7 +63,7 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore # then join dataset to evals dataset_evals = varbar.add( "dataset_evals", - weave.legacy.ops.join_2( + weave.legacy.weave.ops.join_2( dataset_var, joined_evals, lambda row: row["id"], @@ -75,7 +75,7 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore ), ) - main = weave.legacy.panels.Group( + main = weave.legacy.weave.panels.Group( layoutMode="grid", showExpressions=True, enableAddPanel=True, @@ -87,72 +87,72 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore main.add( "avg_f1", - weave.legacy.panels.Plot( + weave.legacy.weave.panels.Plot( summary, x=lambda row: row["avg_f1"], y=lambda row: row["name"], color=lambda row: row["name"], ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=0, w=12, h=4), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=0, w=12, h=4), ) main.add( "latency", - weave.legacy.panels.Plot( + weave.legacy.weave.panels.Plot( concatted_evals, x=lambda row: row["summary"]["latency"], y=lambda row: row["name"], color=lambda row: row["name"], mark="boxplot", ), - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=0, w=12, h=4), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=0, w=12, h=4), ) main.add( "field_name_f1", - weave.legacy.panels.Plot( + weave.legacy.weave.panels.Plot( summary, x=lambda row: row["field_name.f1"], y=lambda row: row["name"], color=lambda row: row["name"], ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=8, h=4), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=8, h=4), ) main.add( "field_shares_f1", - weave.legacy.panels.Plot( + weave.legacy.weave.panels.Plot( summary, x=lambda row: row["field_shares.f1"], y=lambda row: row["name"], color=lambda row: row["name"], ), - layout=weave.legacy.panels.GroupPanelLayout(x=8, y=4, w=8, h=4), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=8, y=4, w=8, h=4), ) main.add( "field_directors_f1", - weave.legacy.panels.Plot( + weave.legacy.weave.panels.Plot( summary, x=lambda row: row["field_directors.f1"], y=lambda row: row["name"], color=lambda row: row["name"], ), - layout=weave.legacy.panels.GroupPanelLayout(x=16, y=4, w=8, h=4), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=16, y=4, w=8, h=4), ) - # ct = main.add('concat_t', concatted_evals, layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=24, h=12)) + # ct = main.add('concat_t', concatted_evals, layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=24, h=12)) # main.add('dataset_table', dataset) # main.add('joined_evals', joined_evals) # main.add( # "dataset_evals", # dataset_evals, - # layout=weave.legacy.panels.GroupPanelLayout(x=0, y=4, w=24, h=6), + # layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=4, w=24, h=6), # ) ##### Example details # more ideas: show examples that all got wrong, or that are confusing - # facet_f1 = weave.legacy.panels.Facet( + # facet_f1 = weave.legacy.weave.panels.Facet( # dataset_evals, # x=lambda row: row["evals.summary"][0]["f1"], # y=lambda row: row["evals.summary"][1]["f1"], @@ -162,10 +162,10 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore # f1_comparison = main.add( # "f1_comparison", # facet_f1, - # layout=weave.legacy.panels.GroupPanelLayout(x=0, y=8, w=12, h=6), + # layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=8, w=12, h=6), # ) - facet_correct = weave.legacy.panels.Facet( + facet_correct = weave.legacy.weave.panels.Facet( dataset_evals, x=lambda row: row["evals.summary"][0]["correct"], x_title="baseline correct", @@ -177,34 +177,34 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore correct_comparison = main.add( "correct_comparison", facet_correct, - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=8, w=12, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=8, w=12, h=6), ) main.add( "help", - weave.legacy.panels.PanelString( + weave.legacy.weave.panels.PanelString( "Click a cell in in the panel to the left to load examples for that cell.\n\nClick a row number in the table below to see details for that row.", mode="markdown", ), - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=8, w=12, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=8, w=12, h=6), ) # main.add( # "example_latencies", - # weave.legacy.panels.Plot( + # weave.legacy.weave.panels.Plot( # dataset_evals, # x=lambda row: row["evals.summary"]["latency"][0], # y=lambda row: row["evals.summary"]["latency"][1], # ), - # layout=weave.legacy.panels.GroupPanelLayout(x=12, y=8, w=12, h=6), + # layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=8, w=12, h=6), # ) - sel_ex_table = weave.legacy.panels.Table(correct_comparison.selected()) + sel_ex_table = weave.legacy.weave.panels.Table(correct_comparison.selected()) sel_ex_table.config.rowSize = 2 sel_ex_table.add_column(lambda row: row["dataset.id"], "id") sel_ex_table.add_column(lambda row: row["dataset.example"], "example") sel_ex_table.add_column(lambda row: row["dataset.label.name"], "label.name") sel_ex_table.add_column( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( baseline=row["evals.result"][0]["name"], candidate=row["evals.result"][1]["name"], ), @@ -212,7 +212,7 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore ) sel_ex_table.add_column(lambda row: row["dataset.label.shares"], "label.shares") sel_ex_table.add_column( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( baseilne=row["evals.result"][0]["shares"].toString(), candidate=row["evals.result"][1]["shares"].toString(), ), @@ -222,14 +222,14 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore lambda row: row["dataset.label.directors"], "label.directors" ) sel_ex_table.add_column( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( baseilne=row["evals.result"][0]["directors"].toString(), candidate=row["evals.result"][1]["directors"].toString(), ), "result.directors", ) sel_ex_table.add_column( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( baseilne=row["evals.summary"][0]["latency"], candidate=row["evals.summary"][1]["latency"], ), @@ -239,19 +239,19 @@ def eval_board(dataset, eval_result0, eval_result1): # type: ignore selected_examples = main.add( "selected_examples", sel_ex_table, - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=14, w=24, h=12), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=14, w=24, h=12), ) main.add( "baseilne_detail", selected_examples.active_data()["evals.summary"][0], - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=26, w=12, h=8), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=26, w=12, h=8), ) main.add( "candidate_detail", selected_examples.active_data()["evals.summary"][1], - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=26, w=12, h=8), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=26, w=12, h=8), ) - return weave.legacy.panels.Board(vars=varbar, panels=main) + return weave.legacy.weave.panels.Board(vars=varbar, panels=main) diff --git a/weave/legacy/panels_py/panel_example_custom_board.py b/weave/legacy/weave/panels_py/panel_example_custom_board.py similarity index 76% rename from weave/legacy/panels_py/panel_example_custom_board.py rename to weave/legacy/weave/panels_py/panel_example_custom_board.py index fa3bd6641df..eb118a56d5d 100644 --- a/weave/legacy/panels_py/panel_example_custom_board.py +++ b/weave/legacy/weave/panels_py/panel_example_custom_board.py @@ -1,7 +1,7 @@ import weave -from weave.legacy import weave_internal as internal -from weave.legacy import weave_types as types -from weave.legacy.panels_py.generator_templates import template_registry +from weave.legacy.weave import weave_internal as internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.panels_py.generator_templates import template_registry # IMPORTANT: To enable this (or any template), it must be imported in # weave/panels_py/__init__.py This example is explicitly commented out since it @@ -40,13 +40,13 @@ ) def board( input_node, -) -> weave.legacy.panels.Group: +) -> weave.legacy.weave.panels.Group: # Define your VarBar variables here control_items = [ - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( internal.const("Example Custom Board"), id="title" ), - weave.legacy.panels.GroupPanel(input_node, id="data"), + weave.legacy.weave.panels.GroupPanel(input_node, id="data"), ] title_var = internal.make_var_node(input_node.type, "title") @@ -54,8 +54,8 @@ def board( # Define your panels here panels = [ - weave.legacy.panels.BoardPanel(title_var, id="title_panel"), - weave.legacy.panels.BoardPanel(data_var, id="table"), + weave.legacy.weave.panels.BoardPanel(title_var, id="title_panel"), + weave.legacy.weave.panels.BoardPanel(data_var, id="table"), ] # Example of using the input type to modify the board: @@ -63,13 +63,13 @@ def board( for column_name, column_type in input_node.type.object_type.property_types.items(): if types.optional(types.Number()).assign_type(column_type): panels.append( - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( data_var[column_name], # type: ignore id=column_name + "_distribution", ) ) - return weave.legacy.panels.Board(vars=control_items, panels=panels) + return weave.legacy.weave.panels.Board(vars=control_items, panels=panels) template_registry.register( diff --git a/weave/legacy/panels_py/panel_llm_monitor.py b/weave/legacy/weave/panels_py/panel_llm_monitor.py similarity index 85% rename from weave/legacy/panels_py/panel_llm_monitor.py rename to weave/legacy/weave/panels_py/panel_llm_monitor.py index d51810eb0a5..ed5c3563ec9 100644 --- a/weave/legacy/panels_py/panel_llm_monitor.py +++ b/weave/legacy/weave/panels_py/panel_llm_monitor.py @@ -2,16 +2,16 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy import weave_internal as internal -from weave.legacy import weave_types as types -from weave.legacy import util, dispatch -from weave.legacy.panels import panel_board, panel_group -from weave.legacy.panels_py import panel_autoboard -from weave.legacy.panels_py.generator_templates import template_registry +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_internal as internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import util, dispatch +from weave.legacy.weave.panels import panel_board, panel_group +from weave.legacy.weave.panels_py import panel_autoboard +from weave.legacy.weave.panels_py.generator_templates import template_registry -panels = weave.legacy.panels -ops = weave.legacy.ops +panels = weave.legacy.weave.panels +ops = weave.legacy.weave.ops # BOARD_ID must be unique across all ops. It must only contain letters and underscores. @@ -101,7 +101,7 @@ # input_node: weave.Node[list[typing.Any]] # @weave.op() -# def render(self) -> weave.legacy.panels.Table: +# def render(self) -> weave.legacy.weave.panels.Table: # table = panels.Table( # self.input_node, # columns=[lambda row: row["inputs"]["messages"][-1]["content"]], @@ -137,7 +137,7 @@ def openai_request_cost(record) -> float: # type: ignore model = record["output.model"] pt = record["summary.prompt_tokens"] ct = record["summary.completion_tokens"] - cost_per_1000 = weave.legacy.ops.case( + cost_per_1000 = weave.legacy.weave.ops.case( [ # finetuned {"when": model.startsWith("ada:"), "then": pt * 0.0016 + ct * 0.0016}, @@ -196,7 +196,7 @@ def board( augmented_data = varbar.add( "augmented_data", source_data.with_columns( - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( **{ "summary.cost": source_data.map( lambda row: openai_request_cost(row) @@ -231,7 +231,7 @@ def board( ## 1. raw_data_range is derived from raw_data filtered_range = varbar.add( "filtered_range", - weave.legacy.ops.make_list( + weave.legacy.weave.ops.make_list( a=filtered_data[timestamp_col_name].min(), b=filtered_data[timestamp_col_name].max(), ), @@ -244,7 +244,7 @@ def board( ## 2.b: Setup a date picker to set the user_zoom_range varbar.add( "time_range", - weave.legacy.panels.DateRange( + weave.legacy.weave.panels.DateRange( user_zoom_range, domain=source_data[timestamp_col_name] ), ) @@ -259,7 +259,7 @@ def board( window_data = varbar.add( "window_data", augmented_data.filter( - lambda row: weave.legacy.ops.Boolean.bool_and( + lambda row: weave.legacy.weave.ops.Boolean.bool_and( row[timestamp_col_name] >= bin_range[0], row[timestamp_col_name] <= bin_range[1], ) @@ -269,7 +269,7 @@ def board( filters = varbar.add( "filters", - weave.legacy.panels.FilterEditor(filter_fn, node=window_data), + weave.legacy.weave.panels.FilterEditor(filter_fn, node=window_data), ) filtered_window_data = varbar.add( @@ -278,14 +278,14 @@ def board( grouping = varbar.add( "grouping", - weave.legacy.panels.GroupingEditor(grouping_fn, node=window_data), + weave.legacy.weave.panels.GroupingEditor(grouping_fn, node=window_data), ) height = 5 ### Overview tab - overview_tab = weave.legacy.panels.Group( + overview_tab = weave.legacy.weave.panels.Group( layoutMode="grid", showExpressions=True, enableAddPanel=True, @@ -304,7 +304,7 @@ def board( n_bins=100, mark="bar", ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=0, w=24, h=height), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=0, w=24, h=height), ) overview_tab.add( @@ -320,7 +320,7 @@ def board( x_domain=user_zoom_range, n_bins=50, ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=height, w=12, h=height), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=height, w=12, h=height), ) overview_tab.add( @@ -336,29 +336,29 @@ def board( x_domain=user_zoom_range, n_bins=50, ), - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=height, w=12, h=height), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=height, w=12, h=height), ) overview_tab.add( "avg_cost_per_req", filtered_window_data["summary.cost"].avg(), # type: ignore - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=height * 2, w=6, h=3), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=height * 2, w=6, h=3), ) overview_tab.add( "avg_prompt_tokens_per_req", filtered_window_data["summary.prompt_tokens"].avg(), # type: ignore - layout=weave.legacy.panels.GroupPanelLayout(x=6, y=height * 2, w=6, h=3), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=6, y=height * 2, w=6, h=3), ) overview_tab.add( "avg_completion_tokens_per_req", filtered_window_data["summary.completion_tokens"].avg(), # type: ignore - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=height * 2, w=6, h=3), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=height * 2, w=6, h=3), ) ( overview_tab.add( "avg_total_tokens_per_req", filtered_window_data["summary.total_tokens"].avg(), # type: ignore - layout=weave.legacy.panels.GroupPanelLayout(x=18, y=height * 2, w=6, h=3), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=18, y=height * 2, w=6, h=3), ), ) @@ -366,12 +366,12 @@ def board( # TODO: This doesn't really work yet (needs some manual UI configuration currently, # and it's ugly). # overview_tab.add( - # "attributes", weave.legacy.panels.EachColumn(filtered_window_data["attributes"]) + # "attributes", weave.legacy.weave.panels.EachColumn(filtered_window_data["attributes"]) # ) ### Requests tab - # requests_tab = weave.legacy.panels.Group( + # requests_tab = weave.legacy.weave.panels.Group( # layoutMode="grid", # showExpressions=True, # ) # l, showExpressions="titleBar") @@ -399,7 +399,7 @@ def board( requests_table_var = overview_tab.add( "table", requests_table, - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=13, w=24, h=8), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=13, w=24, h=8), ) overview_tab.add( "input", @@ -407,19 +407,19 @@ def board( requests_table_var.active_data()["inputs.messages"], columns=[lambda row: row["role"], lambda row: row["content"]], ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=21, w=12, h=8), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=21, w=12, h=8), ) overview_tab.add( "output", requests_table_var.active_row(), - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=21, w=12, h=8), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=21, w=12, h=8), ) - # attributes_tab = weave.legacy.panels.Group(layoutMode="grid") + # attributes_tab = weave.legacy.weave.panels.Group(layoutMode="grid") - # users_tab = weave.legacy.panels.Group(layoutMode="grid") + # users_tab = weave.legacy.weave.panels.Group(layoutMode="grid") - # models_tab = weave.legacy.panels.Group(layoutMode="grid") + # models_tab = weave.legacy.weave.panels.Group(layoutMode="grid") # tabs = panels.Group( # layoutMode="tab", diff --git a/weave/legacy/panels_py/panel_observability.py b/weave/legacy/weave/panels_py/panel_observability.py similarity index 81% rename from weave/legacy/panels_py/panel_observability.py rename to weave/legacy/weave/panels_py/panel_observability.py index a6ea920d2f4..1987039cfcb 100644 --- a/weave/legacy/panels_py/panel_observability.py +++ b/weave/legacy/weave/panels_py/panel_observability.py @@ -1,10 +1,10 @@ import weave -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy.panels import panel_board -from weave.legacy.panels_py.generator_templates import template_registry +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.panels import panel_board +from weave.legacy.weave.panels_py.generator_templates import template_registry -panels = weave.legacy.panels +panels = weave.legacy.weave.panels BOARD_INPUT_WEAVE_TYPE = types.List( types.TypedDict( @@ -76,16 +76,16 @@ def observability( timestamp_col_name = "timestamp" num_buckets = 80 - now = weave.legacy.ops.datetime_now() + now = weave.legacy.weave.ops.datetime_now() - dashboard = weave.legacy.panels.Group( + dashboard = weave.legacy.weave.panels.Group( layoutMode="grid", showExpressions=False, enableAddPanel=False, disableDeletePanel=True, ) - display_states = weave.legacy.ops.dict_( + display_states = weave.legacy.weave.ops.dict_( **{ "running": "running", "failed": "failed", @@ -100,7 +100,7 @@ def observability( varbar = panel_board.varbar(editable=False) source_data = input_node.map( - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{ "timestamp": row[timestamp_col_name], "entity_name": row["entity_name"], @@ -141,18 +141,18 @@ def observability( seven_days_in_seconds = 60 * 60 * 24 * 7 window_start = varbar.add( "window_start", - weave.legacy.ops.from_number(now - seven_days_in_seconds), + weave.legacy.weave.ops.from_number(now - seven_days_in_seconds), hidden=True, ) window_end = varbar.add( "window_end", - weave.legacy.ops.from_number(now), + weave.legacy.weave.ops.from_number(now), hidden=True, ) filtered_range = varbar.add( "filtered_range", - weave.legacy.ops.make_list( + weave.legacy.weave.ops.make_list( a=window_start, b=window_end, ), @@ -173,9 +173,9 @@ def observability( ) window_data = source_data.filter( - lambda row: weave.legacy.ops.Boolean.bool_and( + lambda row: weave.legacy.weave.ops.Boolean.bool_and( row["state"] != "TO_REMOVE", # remove hidden states - weave.legacy.ops.Boolean.bool_and( + weave.legacy.weave.ops.Boolean.bool_and( row[timestamp_col_name] >= bin_range[0], row[timestamp_col_name] <= bin_range[1], ), @@ -196,7 +196,7 @@ def observability( panels.FilterEditor(filter_fn, node=window_data), ) - colors_node = weave.legacy.ops.dict_( + colors_node = weave.legacy.weave.ops.dict_( **{ "running": "rgb(57, 126, 237)", "failed": "rgb(255, 122, 136)", @@ -216,7 +216,7 @@ def observability( queued_time_data = varbar.add( "queued_time_data", filtered_window_data.filter( - lambda row: weave.legacy.ops.Boolean.bool_or( + lambda row: weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "queued", row["state"] == "pending", ), @@ -226,11 +226,11 @@ def observability( is_start_stop_state = weave_internal.define_fn( {"row": source_data.type.object_type}, - lambda row: weave.legacy.ops.Boolean.bool_or( + lambda row: weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "running", - weave.legacy.ops.Boolean.bool_or( + weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "finished", - weave.legacy.ops.Boolean.bool_or( + weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "crashed", row["state"] == "failed", ), @@ -241,17 +241,17 @@ def observability( state_transitions_plot = panels.Plot( filtered_window_data, x=lambda row: row[timestamp_col_name].bin( - weave.legacy.ops.timestamp_bins_nice(bin_range, num_buckets) + weave.legacy.weave.ops.timestamp_bins_nice(bin_range, num_buckets) ), x_title="Time", y=lambda row: row.count(), y_title="Count of transitions by state", label=lambda row: row["state"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "State": row["state"][0], "Count": row.count(), - "Job (s)": weave.legacy.ops.join_to_str(row["job"].unique(), ","), + "Job (s)": weave.legacy.weave.ops.join_to_str(row["job"].unique(), ","), } ), color_title="state", @@ -261,12 +261,12 @@ def observability( domain_x=user_zoom_range, ) - runs_grouped = weave.legacy.ops.List.groupby( + runs_grouped = weave.legacy.weave.ops.List.groupby( queued_time_data, lambda row: row["trace_id"] ) - runs_mapped = weave.legacy.ops.List.map( + runs_mapped = weave.legacy.weave.ops.List.map( runs_grouped, - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{ "trace_id": row["trace_id"][0], "entity_name": row["entity_name"][0], @@ -275,10 +275,10 @@ def observability( "job": row["job"][-1], "enqueued": row[timestamp_col_name][0], "run_start": row[timestamp_col_name][-1], - "duration": weave.legacy.ops.Number.__truediv__( - weave.legacy.ops.Number.__mul__( - weave.legacy.ops.timedelta_total_seconds( - weave.legacy.ops.datetime_sub( + "duration": weave.legacy.weave.ops.Number.__truediv__( + weave.legacy.weave.ops.Number.__mul__( + weave.legacy.weave.ops.timedelta_total_seconds( + weave.legacy.weave.ops.datetime_sub( row[timestamp_col_name].max(), row[timestamp_col_name].min(), ), @@ -292,7 +292,7 @@ def observability( ) runs_mapped_filtered = varbar.add( "runs_mapped_filtered", - weave.legacy.ops.List.filter( + weave.legacy.weave.ops.List.filter( runs_mapped, lambda row: row["duration"] >= 0, ), @@ -302,19 +302,19 @@ def observability( queued_time_plot = panels.Plot( runs_mapped_filtered, x=lambda row: row["run_start"].bin( - weave.legacy.ops.timestamp_bins_nice(bin_range, num_buckets) + weave.legacy.weave.ops.timestamp_bins_nice(bin_range, num_buckets) ), x_title="Time", y=lambda row: row["duration"].sum(), y_title="Time spent queued (m)", label=lambda row: grouping_fn(row), - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ - "Job (s)": weave.legacy.ops.join_to_str(row["job"].unique(), ","), - "User (s)": weave.legacy.ops.join_to_str( + "Job (s)": weave.legacy.weave.ops.join_to_str(row["job"].unique(), ","), + "User (s)": weave.legacy.weave.ops.join_to_str( row["entity_name"].unique(), "," ), - "Project (s)": weave.legacy.ops.join_to_str( + "Project (s)": weave.legacy.weave.ops.join_to_str( row["project_name"].unique(), "," ), "Duration (m)": row["duration"].sum(), @@ -329,7 +329,7 @@ def observability( start_stop_states = varbar.add( "start_stop_states", - weave.legacy.ops.List.filter(filtered_window_data, is_start_stop_state), + weave.legacy.weave.ops.List.filter(filtered_window_data, is_start_stop_state), hidden=True, ) @@ -339,7 +339,7 @@ def observability( x_title="Time", y_title="Job", y=lambda row: row["job"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "Job": row["job"][0], "User": row["entity_name"][0], @@ -347,10 +347,10 @@ def observability( "Project": row["project_name"][0], "Run ID": row["run_id"][-1], "Status": row["state"][-1], - "Duration (m)": weave.legacy.ops.Number.__truediv__( - weave.legacy.ops.Number.__mul__( - weave.legacy.ops.timedelta_total_seconds( - weave.legacy.ops.datetime_sub( + "Duration (m)": weave.legacy.weave.ops.Number.__truediv__( + weave.legacy.weave.ops.Number.__mul__( + weave.legacy.weave.ops.timedelta_total_seconds( + weave.legacy.weave.ops.datetime_sub( row[timestamp_col_name].max(), row[timestamp_col_name].min(), ), @@ -378,7 +378,7 @@ def observability( jobs_table = panels.Table(jobs) # type: ignore jobs_table.add_column(lambda row: row["run_id"], "Run", groupby=True) jobs_table.add_column( - lambda row: weave.legacy.ops.run_ops.str_run_link( + lambda row: weave.legacy.weave.ops.run_ops.str_run_link( entity_name=row["queue_uri"][0].split(":").index(2), project_name=row["project_name"][0], name=row["run_id"][0], @@ -396,26 +396,26 @@ def observability( "Current state", ) jobs_table.add_column( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_( a=row["state"][-1] != "running", b=row["state"][-1] == "running", ), - weave.legacy.ops.dict_( - a=weave.legacy.ops.Number.__truediv__( - weave.legacy.ops.timedelta_total_seconds( - weave.legacy.ops.datetime_sub( + weave.legacy.weave.ops.dict_( + a=weave.legacy.weave.ops.Number.__truediv__( + weave.legacy.weave.ops.timedelta_total_seconds( + weave.legacy.weave.ops.datetime_sub( row[timestamp_col_name].max(), row[timestamp_col_name].min(), ) ), 60, ), - b=weave.legacy.ops.Number.__truediv__( - weave.legacy.ops.timedelta_total_seconds( - weave.legacy.ops.datetime_sub( - weave.legacy.ops.from_number( - weave.legacy.ops.datetime_now() + b=weave.legacy.weave.ops.Number.__truediv__( + weave.legacy.weave.ops.timedelta_total_seconds( + weave.legacy.weave.ops.datetime_sub( + weave.legacy.weave.ops.from_number( + weave.legacy.weave.ops.datetime_now() ), row[timestamp_col_name].min(), ) @@ -427,66 +427,66 @@ def observability( "Runtime (m)", ) jobs_table.add_column( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_( a=row["state"][-1] != "running", b=row["state"][-1] == "running", ), - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( a=row["metrics"]["system"]["cpu_cores_util"][-1].avg(), - b=weave.legacy.ops.make_const_node(types.NoneType(), None), + b=weave.legacy.weave.ops.make_const_node(types.NoneType(), None), ), ), "Avg. CPU %", ) jobs_table.add_column( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_( a=row["state"][-1] != "running", b=row["state"][-1] == "running", ), - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( a=row["metrics"]["system"]["gpu_cores_util"][-1].avg(), - b=weave.legacy.ops.make_const_node(types.NoneType(), None), + b=weave.legacy.weave.ops.make_const_node(types.NoneType(), None), ), ), "Avg. GPU %", ) jobs_table.add_column( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_( a=row["state"][-1] != "running", b=row["state"][-1] == "running", ), - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( a=row["metrics"]["system"]["gpu_cores_mem"][-1].avg(), - b=weave.legacy.ops.make_const_node(types.NoneType(), None), + b=weave.legacy.weave.ops.make_const_node(types.NoneType(), None), ), ), "Avg. GPU mem. %", ) jobs_table.add_column( - lambda row: weave.legacy.ops.cond( - weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.cond( + weave.legacy.weave.ops.dict_( a=row["state"][-1] != "running", b=row["state"][-1] == "running", ), - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( a=row["metrics"]["system"]["memory"][-1], - b=weave.legacy.ops.make_const_node(types.NoneType(), None), + b=weave.legacy.weave.ops.make_const_node(types.NoneType(), None), ), ), "Avg. mem (MB)", ) - runs_table_data = weave.legacy.ops.List.groupby( + runs_table_data = weave.legacy.weave.ops.List.groupby( filtered_window_data, lambda row: row["trace_id"] ) runs_table_data_mapped = varbar.add( "runs_table_data", - weave.legacy.ops.List.map( + weave.legacy.weave.ops.List.map( runs_table_data, - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{ "trace_id": row["trace_id"][0], "user": row["entity_name"][0], @@ -522,7 +522,7 @@ def observability( ) runs_table.add_column( lambda row: row.filter( - lambda row: weave.legacy.ops.Boolean.bool_or( + lambda row: weave.legacy.weave.ops.Boolean.bool_or( row["states"][-1] == "crashed", row["states"][-1] == "failed", ), @@ -533,9 +533,9 @@ def observability( finished_runs = varbar.add( "finished_runs", filtered_window_data.filter( - lambda row: weave.legacy.ops.Boolean.bool_or( + lambda row: weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "finished", - weave.legacy.ops.Boolean.bool_or( + weave.legacy.weave.ops.Boolean.bool_or( row["state"] == "crashed", row["state"] == "failed", ), @@ -556,24 +556,24 @@ def observability( no_legend=True, ) - metric_plot_data = weave.legacy.ops.List.groupby( + metric_plot_data = weave.legacy.weave.ops.List.groupby( start_stop_states, lambda row: row["trace_id"] ) metric_plot_data_mapped = varbar.add( "metric_plot_data", - weave.legacy.ops.List.map( + weave.legacy.weave.ops.List.map( metric_plot_data, - lambda row: weave.legacy.ops.dict_( + lambda row: weave.legacy.weave.ops.dict_( **{ "trace_id": row["trace_id"][0], "run_id": row["run_id"][-1], "entity_name": row["entity_name"][0], "project_name": row["project_name"][0], "job": row["job"][0], - "duration": weave.legacy.ops.Number.__truediv__( - weave.legacy.ops.Number.__mul__( - weave.legacy.ops.timedelta_total_seconds( - weave.legacy.ops.datetime_sub( + "duration": weave.legacy.weave.ops.Number.__truediv__( + weave.legacy.weave.ops.Number.__mul__( + weave.legacy.weave.ops.timedelta_total_seconds( + weave.legacy.weave.ops.datetime_sub( row[timestamp_col_name].max(), row[timestamp_col_name].min(), ), @@ -598,7 +598,7 @@ def observability( x_title="Run duration (minutes)", y_title="GPU utilization (%)", y=lambda row: row["GPU util %"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "Run ID": row["run_id"], "Project": row["project_name"], @@ -622,7 +622,7 @@ def observability( x_title="Run duration (minutes)", y_title="CPU utilization (%)", y=lambda row: row["CPU util %"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "Run ID": row["run_id"], "Project": row["project_name"], @@ -646,7 +646,7 @@ def observability( x_title="Run duration (minutes)", y_title="GPU memory (%)", y=lambda row: row["GPU memory %"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "Run ID": row["run_id"], "Project": row["project_name"], @@ -670,7 +670,7 @@ def observability( x_title="Run duration (minutes)", y_title="Memory usage (MB)", y=lambda row: row["Memory (MB)"], - tooltip=lambda row: weave.legacy.ops.dict_( + tooltip=lambda row: weave.legacy.weave.ops.dict_( **{ "Run ID": row["run_id"], "Project": row["project_name"], @@ -724,7 +724,7 @@ def observability( ) selected_jobs = panels.Table(latest_runs_plot_selector.selected_rows()) # type: ignore selected_jobs.add_column( - lambda row: weave.legacy.ops.run_ops.str_run_link( + lambda row: weave.legacy.weave.ops.run_ops.str_run_link( entity_name=row["c_4.Team"], project_name=row["c_4.Project"], name=row["c_4.Run ID"], diff --git a/weave/legacy/panels_py/panel_seedboard.py b/weave/legacy/weave/panels_py/panel_seedboard.py similarity index 60% rename from weave/legacy/panels_py/panel_seedboard.py rename to weave/legacy/weave/panels_py/panel_seedboard.py index 4887f66f854..c21cd0e66de 100644 --- a/weave/legacy/panels_py/panel_seedboard.py +++ b/weave/legacy/weave/panels_py/panel_seedboard.py @@ -1,8 +1,8 @@ import typing import weave -from weave.legacy import weave_internal -from weave.legacy.panels_py.generator_templates import template_registry +from weave.legacy.weave import weave_internal +from weave.legacy.weave.panels_py.generator_templates import template_registry @weave.type() @@ -17,22 +17,22 @@ class PyBoardSeedBoardConfig: def seed_board( input_node: weave.Node[typing.Any], config: typing.Optional[PyBoardSeedBoardConfig] = None, -) -> weave.legacy.panels.Group: +) -> weave.legacy.weave.panels.Group: control_items = [ - weave.legacy.panels.GroupPanel( + weave.legacy.weave.panels.GroupPanel( input_node, id="data", ), ] panels = [ - weave.legacy.panels.BoardPanel( + weave.legacy.weave.panels.BoardPanel( weave_internal.make_var_node(input_node.type, "data"), id="panel", - layout=weave.legacy.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), + layout=weave.legacy.weave.panels.BoardPanelLayout(x=0, y=0, w=24, h=6), ), ] - return weave.legacy.panels.Board(vars=control_items, panels=panels) + return weave.legacy.weave.panels.Board(vars=control_items, panels=panels) template_registry.register( diff --git a/weave/legacy/panels_py/panel_trace_monitor.py b/weave/legacy/weave/panels_py/panel_trace_monitor.py similarity index 81% rename from weave/legacy/panels_py/panel_trace_monitor.py rename to weave/legacy/weave/panels_py/panel_trace_monitor.py index 9bd91d96090..e1cf168c432 100644 --- a/weave/legacy/panels_py/panel_trace_monitor.py +++ b/weave/legacy/weave/panels_py/panel_trace_monitor.py @@ -1,17 +1,17 @@ import os import weave -from weave.legacy import weave_internal -from weave.legacy import weave_internal as internal -from weave.legacy import weave_types as types -from weave.legacy import util, dispatch, graph -from weave.legacy.panels import panel_board, panel_group, panel_trace -from weave.legacy.panels.panel_trace_span import TraceSpanModelPanel, TraceSpanPanel -from weave.legacy.panels_py import panel_autoboard -from weave.legacy.panels_py.generator_templates import template_registry +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_internal as internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import util, dispatch, graph +from weave.legacy.weave.panels import panel_board, panel_group, panel_trace +from weave.legacy.weave.panels.panel_trace_span import TraceSpanModelPanel, TraceSpanPanel +from weave.legacy.weave.panels_py import panel_autoboard +from weave.legacy.weave.panels_py.generator_templates import template_registry -panels = weave.legacy.panels -ops = weave.legacy.ops +panels = weave.legacy.weave.panels +ops = weave.legacy.weave.ops # BOARD_ID must be unique across all ops. It must only contain letters and underscores. @@ -101,7 +101,7 @@ def board( ## 1. raw_data_range is derived from raw_data filtered_range = varbar.add( "filtered_range", - weave.legacy.ops.make_list( + weave.legacy.weave.ops.make_list( a=filtered_data[timestamp_col_name].min(), b=filtered_data[timestamp_col_name].max(), ), @@ -114,7 +114,7 @@ def board( ## 2.b: Setup a date picker to set the user_zoom_range varbar.add( "time_range", - weave.legacy.panels.DateRange( + weave.legacy.weave.panels.DateRange( user_zoom_range, domain=trace_roots[timestamp_col_name] ), ) @@ -129,7 +129,7 @@ def board( window_data = varbar.add( "window_data", trace_roots.filter( - lambda row: weave.legacy.ops.Boolean.bool_and( + lambda row: weave.legacy.weave.ops.Boolean.bool_and( row[timestamp_col_name] >= bin_range[0], row[timestamp_col_name] <= bin_range[1], ) @@ -139,7 +139,7 @@ def board( filters = varbar.add( "filters", - weave.legacy.panels.FilterEditor(filter_fn, node=window_data), + weave.legacy.weave.panels.FilterEditor(filter_fn, node=window_data), ) filtered_window_data = varbar.add( @@ -148,7 +148,7 @@ def board( ### Overview tab - overview_tab = weave.legacy.panels.Group( + overview_tab = weave.legacy.weave.panels.Group( layoutMode="grid", showExpressions=True, enableAddPanel=True, @@ -171,13 +171,13 @@ def board( x_domain=user_zoom_range, n_bins=50, ), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=0, w=6, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=0, w=6, h=6), ) overview_tab.add( "latency_distribution", filtered_window_data.map(lambda row: row["end_time_s"] - row["start_time_s"]), - layout=weave.legacy.panels.GroupPanelLayout(x=6, y=0, w=6, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=6, y=0, w=6, h=6), ) overview_tab.add( @@ -198,12 +198,12 @@ def board( x_domain=user_zoom_range, n_bins=50, ), - layout=weave.legacy.panels.GroupPanelLayout(x=12, y=0, w=6, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=12, y=0, w=6, h=6), ) overview_tab.add( "success_distribution", - weave.legacy.ops.dict_( + weave.legacy.weave.ops.dict_( **{ "success": filtered_window_data.filter( lambda row: row["status_code"] == "SUCCESS" @@ -213,13 +213,13 @@ def board( ).count(), } ), - layout=weave.legacy.panels.GroupPanelLayout(x=18, y=0, w=6, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=18, y=0, w=6, h=6), ) traces_table_var = overview_tab.add( "traces_table", make_span_table(filtered_window_data), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=6, w=24, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=6, w=24, h=6), ) trace_spans = all_spans.filter( @@ -231,13 +231,13 @@ def board( trace_viewer_var = overview_tab.add( "trace_viewer", trace_viewer, - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=12, w=16, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=12, w=16, h=6), ) selected_trace_model = overview_tab.add( "selected_trace_model", TraceSpanModelPanel(traces_table_var.active_data()), - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=18, w=16, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=18, w=16, h=6), ) active_span = trace_viewer_var.active_span() @@ -245,7 +245,7 @@ def board( selected_span_details = overview_tab.add( "selected_span_details", TraceSpanPanel(active_span), - layout=weave.legacy.panels.GroupPanelLayout(x=16, y=12, w=8, h=12), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=16, y=12, w=8, h=12), ) similar_spans = all_spans.filter(lambda row: row["name"] == active_span["name"]) @@ -254,7 +254,7 @@ def board( similar_spans_table_var = overview_tab.add( "similar_spans_table", similar_spans_table, - layout=weave.legacy.panels.GroupPanelLayout(x=0, y=22, w=24, h=6), + layout=weave.legacy.weave.panels.GroupPanelLayout(x=0, y=22, w=24, h=6), ) return panels.Board(vars=varbar, panels=overview_tab) diff --git a/weave/legacy/parallelism.py b/weave/legacy/weave/parallelism.py similarity index 99% rename from weave/legacy/parallelism.py rename to weave/legacy/weave/parallelism.py index a11dcfeac76..bd27582d836 100644 --- a/weave/legacy/parallelism.py +++ b/weave/legacy/weave/parallelism.py @@ -3,7 +3,7 @@ from concurrent.futures import ThreadPoolExecutor from typing import Callable, Generator, Iterator, Optional, TypeVar -from weave.legacy import ( +from weave.legacy.weave import ( cache, context, context_state, diff --git a/weave/legacy/partial_object.py b/weave/legacy/weave/partial_object.py similarity index 96% rename from weave/legacy/partial_object.py rename to weave/legacy/weave/partial_object.py index 306c12a2d73..bec6862b70a 100644 --- a/weave/legacy/partial_object.py +++ b/weave/legacy/weave/partial_object.py @@ -2,8 +2,8 @@ import typing from dataclasses import dataclass, field -from weave.legacy import weave_types as types -from weave.legacy import artifact_fs +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import artifact_fs T = typing.TypeVar("T", bound="PartialObject") @@ -136,7 +136,7 @@ def from_dict(cls, d: dict) -> "PartialObjectType": def save_instance( self, obj: PartialObject, artifact: artifact_fs.FilesystemArtifact, name: str ) -> None: - from weave.legacy import mappers_python + from weave.legacy.weave import mappers_python serializer = mappers_python.map_to_python(self, artifact) result = serializer.apply(obj) @@ -151,7 +151,7 @@ def load_instance( name: str, extra: typing.Optional[list] = None, ) -> PartialObject: - from weave.legacy import mappers_python + from weave.legacy.weave import mappers_python with artifact.open( f"{name}.{self.keyless_weave_type_class.__name__}WithKeys.json" diff --git a/weave/legacy/path_util.py b/weave/legacy/weave/path_util.py similarity index 90% rename from weave/legacy/path_util.py rename to weave/legacy/weave/path_util.py index a6504697c37..e52d5fa912d 100644 --- a/weave/legacy/path_util.py +++ b/weave/legacy/weave/path_util.py @@ -1,7 +1,7 @@ import pathlib import typing -from weave.legacy import errors +from weave.legacy.weave import errors def safe_join(*args: typing.Union[str, pathlib.Path]) -> str: diff --git a/weave/legacy/propagate_gql_keys.py b/weave/legacy/weave/propagate_gql_keys.py similarity index 93% rename from weave/legacy/propagate_gql_keys.py rename to weave/legacy/weave/propagate_gql_keys.py index b32fbd3ad32..a605b407cf3 100644 --- a/weave/legacy/propagate_gql_keys.py +++ b/weave/legacy/weave/propagate_gql_keys.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import weave_types as types -from weave.legacy import gql_op_plugin, graph, input_provider, op_def, partial_object, registry_mem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import gql_op_plugin, graph, input_provider, op_def, partial_object, registry_mem def _propagate_gql_keys_for_node( @@ -13,7 +13,7 @@ def _propagate_gql_keys_for_node( ) -> types.Type: # Mutates node # TODO: see if this can be done without mutations - from weave.legacy.language_features.tagging import ( + from weave.legacy.weave.language_features.tagging import ( opdef_util, tagged_value_type, tagged_value_type_helpers, diff --git a/weave/legacy/pyfunc_type_util.py b/weave/legacy/weave/pyfunc_type_util.py similarity index 98% rename from weave/legacy/pyfunc_type_util.py rename to weave/legacy/weave/pyfunc_type_util.py index c72318e515d..2a12827822f 100644 --- a/weave/legacy/pyfunc_type_util.py +++ b/weave/legacy/weave/pyfunc_type_util.py @@ -1,10 +1,10 @@ import inspect import typing -from weave.legacy import infer_types, op_args +from weave.legacy.weave import infer_types, op_args -from weave.legacy import errors -from weave.legacy import weave_types as types +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types InputTypeItemType = typing.Union[types.Type, typing.Callable[..., types.Type]] InputTypeType = typing.Union[op_args.OpArgs, typing.Mapping[str, InputTypeItemType]] diff --git a/weave/legacy/ref_base.py b/weave/legacy/weave/ref_base.py similarity index 95% rename from weave/legacy/ref_base.py rename to weave/legacy/weave/ref_base.py index 107ca9ca6c1..6951fe602b3 100644 --- a/weave/legacy/ref_base.py +++ b/weave/legacy/weave/ref_base.py @@ -6,17 +6,17 @@ from typing import Sequence from weave.trace.client_context import weave_client as weave_client_context -from weave.legacy import box, context_state, object_context, uris -from weave.legacy.language_features.tagging import tag_store +from weave.legacy.weave import box, context_state, object_context, uris +from weave.legacy.weave.language_features.tagging import tag_store -from weave.legacy import errors -from weave.legacy import weave_types as types +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types # We store Refs here if we can't attach them directly to the object REFS: weakref.WeakValueDictionary[int, "Ref"] = weakref.WeakValueDictionary() if typing.TYPE_CHECKING: - from weave.legacy import weave_types as types + from weave.legacy.weave import weave_types as types from ..trace import weave_client diff --git a/weave/legacy/registry_mem.py b/weave/legacy/weave/registry_mem.py similarity index 95% rename from weave/legacy/registry_mem.py rename to weave/legacy/weave/registry_mem.py index 625c18ca60e..7e0f0905b17 100644 --- a/weave/legacy/registry_mem.py +++ b/weave/legacy/weave/registry_mem.py @@ -1,12 +1,12 @@ import datetime import typing -from weave.legacy import weave_types -from weave.legacy import storage, errors, context_state, op_aliases, op_args, uris -from weave.legacy.op_args import OpNamedArgs +from weave.legacy.weave import weave_types +from weave.legacy.weave import storage, errors, context_state, op_aliases, op_args, uris +from weave.legacy.weave.op_args import OpNamedArgs if typing.TYPE_CHECKING: - from weave.legacy.op_def import OpDef + from weave.legacy.weave.op_def import OpDef class Registry: @@ -122,7 +122,7 @@ def is_chainable(op): # type: ignore return [op for op in self._ops.values() if is_chainable(op)] # type: ignore[no-untyped-call] def load_saved_ops(self): # type: ignore - from weave.legacy import op_def_type + from weave.legacy.weave import op_def_type for op_ref in storage.objects(op_def_type.OpDefType()): try: diff --git a/weave/legacy/run.py b/weave/legacy/weave/run.py similarity index 100% rename from weave/legacy/run.py rename to weave/legacy/weave/run.py diff --git a/weave/legacy/run_streamtable_span.py b/weave/legacy/weave/run_streamtable_span.py similarity index 93% rename from weave/legacy/run_streamtable_span.py rename to weave/legacy/weave/run_streamtable_span.py index ae5f9225c22..1583f6e2af4 100644 --- a/weave/legacy/run_streamtable_span.py +++ b/weave/legacy/weave/run_streamtable_span.py @@ -1,10 +1,10 @@ import typing from typing import Iterable -from weave.legacy import stream_data_interfaces -from weave.legacy.eager import WeaveIter -from weave.legacy import artifact_wandb, uris -from weave.legacy.run import Run +from weave.legacy.weave import stream_data_interfaces +from weave.legacy.weave.eager import WeaveIter +from weave.legacy.weave import artifact_wandb, uris +from weave.legacy.weave.run import Run class RunStreamTableSpan: diff --git a/weave/legacy/runfiles_wandb.py b/weave/legacy/weave/runfiles_wandb.py similarity index 97% rename from weave/legacy/runfiles_wandb.py rename to weave/legacy/weave/runfiles_wandb.py index 9353652927a..201baaed024 100644 --- a/weave/legacy/runfiles_wandb.py +++ b/weave/legacy/weave/runfiles_wandb.py @@ -6,10 +6,10 @@ import shutil import typing -from weave.legacy import environment as weave_env -from weave.legacy import filesystem -from weave.legacy import weave_types as types -from weave.legacy import ( +from weave.legacy.weave import environment as weave_env +from weave.legacy.weave import filesystem +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ( artifact_fs, errors, file_util, diff --git a/weave/legacy/runs.py b/weave/legacy/weave/runs.py similarity index 92% rename from weave/legacy/runs.py rename to weave/legacy/weave/runs.py index bb27e87b374..f20967a4d8e 100644 --- a/weave/legacy/runs.py +++ b/weave/legacy/weave/runs.py @@ -1,7 +1,7 @@ import dataclasses import typing -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types @dataclasses.dataclass diff --git a/weave/legacy/serialize.py b/weave/legacy/weave/serialize.py similarity index 97% rename from weave/legacy/serialize.py rename to weave/legacy/weave/serialize.py index 396a1b956be..f39954a1375 100644 --- a/weave/legacy/serialize.py +++ b/weave/legacy/weave/serialize.py @@ -7,9 +7,9 @@ import random import typing -from weave.legacy import weave_internal -from weave.legacy import weave_types as types -from weave.legacy import storage, errors, graph, memo, value_or_error +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import storage, errors, graph, memo, value_or_error NodeOrOp = typing.Union[graph.Node, graph.Op] diff --git a/weave/legacy/server.py b/weave/legacy/weave/server.py similarity index 97% rename from weave/legacy/server.py rename to weave/legacy/weave/server.py index 51f5f6bd6e4..90c2092b1ae 100644 --- a/weave/legacy/server.py +++ b/weave/legacy/weave/server.py @@ -12,7 +12,7 @@ import requests from werkzeug.serving import make_server -from weave.legacy import ( +from weave.legacy.weave import ( cache, context, engine_trace, @@ -27,8 +27,8 @@ storage, weave_types, ) -from weave.legacy.language_features.tagging import tag_store -from weave.legacy.language_features.tagging.tag_store import isolated_tagging_context +from weave.legacy.weave.language_features.tagging import tag_store +from weave.legacy.weave.language_features.tagging.tag_store import isolated_tagging_context # A function to monkeypatch the request post method # def patch_request_post(): diff --git a/weave/legacy/server_error_handling.py b/weave/legacy/weave/server_error_handling.py similarity index 98% rename from weave/legacy/server_error_handling.py rename to weave/legacy/weave/server_error_handling.py index f7c262a24c0..1e3b10ba96f 100644 --- a/weave/legacy/server_error_handling.py +++ b/weave/legacy/weave/server_error_handling.py @@ -26,9 +26,9 @@ from werkzeug import Response from werkzeug import exceptions as werkzeug_exceptions from werkzeug import http as werkzeug_http -from weave.legacy import errors +from weave.legacy.weave import errors -from weave.legacy import util +from weave.legacy.weave import util class WeaveInternalHttpException(werkzeug_exceptions.HTTPException): diff --git a/weave/legacy/server_interface.py b/weave/legacy/weave/server_interface.py similarity index 100% rename from weave/legacy/server_interface.py rename to weave/legacy/weave/server_interface.py diff --git a/weave/legacy/show.py b/weave/legacy/weave/show.py similarity index 95% rename from weave/legacy/show.py rename to weave/legacy/weave/show.py index 204e84d5b7f..5e59d05272e 100644 --- a/weave/legacy/show.py +++ b/weave/legacy/weave/show.py @@ -5,10 +5,10 @@ from IPython.display import IFrame, display -from weave.legacy import storage, artifact_fs, context, errors, graph, ops, node_ref, panel, ref_base -from weave.legacy import util +from weave.legacy.weave import storage, artifact_fs, context, errors, graph, ops, node_ref, panel, ref_base +from weave.legacy.weave import util from . import usage_analytics -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types from . import weavejs_fixes @@ -21,7 +21,7 @@ def make_varname_for_type(t: types.Type): # type: ignore def make_container( obj: typing.Union[panel.Panel, graph.Node], name: str ) -> panel.Panel: - from weave.legacy.panels import Group + from weave.legacy.weave.panels import Group if isinstance(obj, graph.Node): return Group( diff --git a/weave/legacy/signal_handlers.py b/weave/legacy/weave/signal_handlers.py similarity index 99% rename from weave/legacy/signal_handlers.py rename to weave/legacy/weave/signal_handlers.py index d9c1e89e683..be47f10103a 100644 --- a/weave/legacy/signal_handlers.py +++ b/weave/legacy/weave/signal_handlers.py @@ -10,7 +10,7 @@ import typing from types import FrameType -from weave.legacy import environment +from weave.legacy.weave import environment def dump_folder() -> pathlib.Path: diff --git a/weave/legacy/stitch.py b/weave/legacy/weave/stitch.py similarity index 98% rename from weave/legacy/stitch.py rename to weave/legacy/weave/stitch.py index 9ba0145040b..69aba75e31f 100644 --- a/weave/legacy/stitch.py +++ b/weave/legacy/weave/stitch.py @@ -22,11 +22,11 @@ import dataclasses import typing -from weave.legacy import graph, op_def, registry_mem -from weave.legacy.language_features.tagging import opdef_util +from weave.legacy.weave import graph, op_def, registry_mem +from weave.legacy.weave.language_features.tagging import opdef_util -from weave.legacy import errors -from weave.legacy import weave_types as types +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types from . import _dict_utils diff --git a/weave/legacy/storage.py b/weave/legacy/weave/storage.py similarity index 98% rename from weave/legacy/storage.py rename to weave/legacy/weave/storage.py index cd14615af6c..b3eef9a5220 100644 --- a/weave/legacy/storage.py +++ b/weave/legacy/weave/storage.py @@ -8,7 +8,7 @@ import typing from weave.trace.client_context import weave_client as weave_client_context -from weave.legacy import ( +from weave.legacy.weave import ( artifact_base, artifact_fs, artifact_local, @@ -22,12 +22,12 @@ timestamp, ) -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types Ref = ref_base.Ref if typing.TYPE_CHECKING: - from weave.legacy.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun + from weave.legacy.weave.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun def split_path_dotfile(path, dotfile_name): # type: ignore @@ -63,7 +63,7 @@ def _get_weave_type_with_refs(obj: typing.Any): # type: ignore def _ensure_object_components_are_published( # type: ignore obj: typing.Any, wb_type: types.Type, artifact: artifact_wandb.WandbArtifact ): - from weave.legacy.mappers_publisher import map_to_python_remote + from weave.legacy.weave.mappers_publisher import map_to_python_remote mapper = map_to_python_remote(wb_type, artifact) return mapper.apply(obj) @@ -451,7 +451,7 @@ def to_json_with_refs( # type: ignore """ # This is newer than to_python, save and publish above, and doesn't use the "mapper" # pattern, which is overkill. Much better to just write a simple function like this. - from weave.legacy import op_def + from weave.legacy.weave import op_def if wb_type is None: wb_type = types.TypeRegistry.type_of(obj) @@ -508,7 +508,7 @@ def convert_timestamps_to_epoch_ms(obj: typing.Any) -> typing.Any: def to_weavejs(obj, artifact: typing.Optional[artifact_base.Artifact] = None): # type: ignore - from weave.legacy.arrow import list_ as arrow_list + from weave.legacy.weave.arrow import list_ as arrow_list obj = box.unbox(obj) if isinstance(obj, (str, int, float, bool, type(None))): diff --git a/weave/legacy/stream_data_interfaces.py b/weave/legacy/weave/stream_data_interfaces.py similarity index 100% rename from weave/legacy/stream_data_interfaces.py rename to weave/legacy/weave/stream_data_interfaces.py diff --git a/weave/legacy/timestamp.py b/weave/legacy/weave/timestamp.py similarity index 100% rename from weave/legacy/timestamp.py rename to weave/legacy/weave/timestamp.py diff --git a/weave/legacy/weave/trace_legacy.py b/weave/legacy/weave/trace_legacy.py new file mode 100644 index 00000000000..d8a6ec55a13 --- /dev/null +++ b/weave/legacy/weave/trace_legacy.py @@ -0,0 +1,84 @@ +import os +import typing + +from weave.legacy import artifact_local, graph, ref_base, runs +from weave.legacy import weave_types as types + +from . import errors + + +def get_obj_creator(ref: ref_base.Ref) -> typing.Optional[runs.Run]: + # ref.backend.references(ref.artifact, "Run", {"dir": "input"}) + + # creator = ref.backend.get_creator(ref.artifact) + # # OR + # backend = ref.backend.filter(type="Run", referenced=ref.artifact) + # Extremely inefficient! + # TODO + for art_name in os.listdir(artifact_local.local_artifact_dir()): + if ( + art_name.startswith("run-") + and not art_name.endswith("-output") + and artifact_local.local_artifact_exists(art_name, "latest") + ): + try: + cache_obj = artifact_local.get_local_version(art_name, "latest") + except errors.WeaveSerializeError: + # This happens because we don't load all ecosystem modules, and so we can't + # deserializes everything. + continue + + def _is_creator(run): + if isinstance(run.output, ref_base.Ref) and str(run.output) == str(ref): + # If any input is also the ref, this run did not create obj, since + # the obj already existed. This fixes an infinite loop where list-createIndexCheckpointTag + # which just returns its input would be treated as a the obj creator + # TODO: This whole thing is a pile of hacks, not production ready! Fix! We should + # not need heuristics. + # TODO: for one, if we order all the artifacts by created_at, then + # the first one will be the creator. Can't do that in this branch + # since it doesn't have the created at change. + if any(str(input) == str(ref) for input in run.inputs.values()): + return False + return True + + if isinstance(cache_obj._ref.type, types.List): + for run in cache_obj: + if _is_creator(run): + return run + else: + if _is_creator(cache_obj): + return cache_obj + return None + + +def get_obj_expr(obj): + obj_type = types.TypeRegistry.type_of(obj) + if not isinstance(obj, ref_base.Ref): + return graph.ConstNode(obj_type, obj) + run = get_obj_creator(obj) + if run is None: + return graph.ConstNode(obj_type, obj) + return graph.OutputNode( + obj.type, + run.op_name, + {k: get_obj_expr(input) for k, input in run.inputs.items()}, + ) + + +def used_by(ref, op_name: str) -> list[runs.Run]: + users = [] + for artifact_name in os.listdir(artifact_local.local_artifact_dir()): + if artifact_name.startswith("run-") and not artifact_name.endswith("-output"): + run = artifact_local.get_local_version(artifact_name, "latest") + if run is None: + continue + run_inputs = list(run.inputs.values()) + if run.op_name == op_name and run_inputs: + input0 = run_inputs[0] + if ( + isinstance(input0, artifact_local.LocalArtifactRef) + and ref.uri == input0.uri + ): + users.append(run) + return users diff --git a/weave/legacy/trace_local.py b/weave/legacy/weave/trace_local.py similarity index 95% rename from weave/legacy/trace_local.py rename to weave/legacy/weave/trace_local.py index b38d0df657d..3b969dc5d89 100644 --- a/weave/legacy/trace_local.py +++ b/weave/legacy/weave/trace_local.py @@ -5,14 +5,14 @@ import typing from typing import Mapping -from weave.legacy import artifact_local, graph, op_def, op_policy, runs +from weave.legacy.weave import artifact_local, graph, op_def, op_policy, runs from . import ref_base -from weave.legacy import ( +from weave.legacy.weave import ( storage, weave_internal, ) -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types @dataclasses.dataclass @@ -118,13 +118,13 @@ def get_run(self, run_key: RunKey) -> graph.Node[runs.Run]: return self._single_run(run_key) def get_run_val(self, run_key: RunKey) -> typing.Optional[runs.Run]: - from weave.legacy import execute_fast + from weave.legacy.weave import execute_fast res = execute_fast._execute_fn_no_engine(None, None, self.get_run(run_key)) # type: ignore[no-untyped-call] return res def save_run(self, run: runs.Run): # type: ignore - from weave.legacy.ops_primitives import weave_api + from weave.legacy.weave.ops_primitives import weave_api run_key = RunKey(run.op_name, run.id) if self._should_save_to_table(run_key): diff --git a/weave/legacy/types_numpy.py b/weave/legacy/weave/types_numpy.py similarity index 96% rename from weave/legacy/types_numpy.py rename to weave/legacy/weave/types_numpy.py index c7d661e8780..8d72409f7a3 100644 --- a/weave/legacy/types_numpy.py +++ b/weave/legacy/weave/types_numpy.py @@ -1,6 +1,6 @@ import numpy as np -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types # TODO: this doesn't match how extra works for list types... diff --git a/weave/legacy/uris.py b/weave/legacy/weave/uris.py similarity index 98% rename from weave/legacy/uris.py rename to weave/legacy/weave/uris.py index 465dac98b9f..f207ff32cb7 100644 --- a/weave/legacy/uris.py +++ b/weave/legacy/weave/uris.py @@ -2,7 +2,7 @@ import typing from urllib import parse -from weave.legacy import errors +from weave.legacy.weave import errors if typing.TYPE_CHECKING: from . import ref_base diff --git a/weave/legacy/urls.py b/weave/legacy/weave/urls.py similarity index 96% rename from weave/legacy/urls.py rename to weave/legacy/weave/urls.py index cf7d53cbf38..a74d7e3721c 100644 --- a/weave/legacy/urls.py +++ b/weave/legacy/weave/urls.py @@ -2,7 +2,7 @@ from wandb import util as wb_util -from weave.legacy import context_state, environment +from weave.legacy.weave import context_state, environment BROWSE3_PATH = "browse3" WEAVE_SLUG = "weave" diff --git a/weave/legacy/usage_analytics.py b/weave/legacy/weave/usage_analytics.py similarity index 95% rename from weave/legacy/usage_analytics.py rename to weave/legacy/weave/usage_analytics.py index 5fd8bddc571..f2acef97b0f 100644 --- a/weave/legacy/usage_analytics.py +++ b/weave/legacy/weave/usage_analytics.py @@ -3,7 +3,7 @@ import analytics -from weave.legacy import context_state +from weave.legacy.weave import context_state from . import environment diff --git a/weave/legacy/util.py b/weave/legacy/weave/util.py similarity index 98% rename from weave/legacy/util.py rename to weave/legacy/weave/util.py index 8027cf35a9d..f057ac40073 100644 --- a/weave/legacy/util.py +++ b/weave/legacy/weave/util.py @@ -8,7 +8,7 @@ # import ipynbname import typing -from weave.legacy.errors import WeaveFingerprintErrorMixin +from weave.legacy.weave.errors import WeaveFingerprintErrorMixin sentry_inited = False diff --git a/weave/legacy/val_const.py b/weave/legacy/weave/val_const.py similarity index 90% rename from weave/legacy/val_const.py rename to weave/legacy/weave/val_const.py index c48e34e50cd..458034826e9 100644 --- a/weave/legacy/val_const.py +++ b/weave/legacy/weave/val_const.py @@ -7,7 +7,7 @@ import typing -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types class Const: diff --git a/weave/legacy/value_or_error.py b/weave/legacy/weave/value_or_error.py similarity index 100% rename from weave/legacy/value_or_error.py rename to weave/legacy/weave/value_or_error.py diff --git a/weave/legacy/wandb_api.py b/weave/legacy/weave/wandb_api.py similarity index 98% rename from weave/legacy/wandb_api.py rename to weave/legacy/weave/wandb_api.py index d93a11da936..d0edbeaa8be 100644 --- a/weave/legacy/wandb_api.py +++ b/weave/legacy/weave/wandb_api.py @@ -14,12 +14,12 @@ from gql.transport.requests import RequestsHTTPTransport from requests.auth import HTTPBasicAuth -from weave.legacy import errors -from weave.legacy import environment as weave_env -from weave.legacy import wandb_client_api, engine_trace +from weave.legacy.weave import errors +from weave.legacy.weave import environment as weave_env +from weave.legacy.weave import wandb_client_api, engine_trace # Importing at the top-level namespace so other files can import from here. -from weave.legacy.context_state import WandbApiContext, _wandb_api_context +from weave.legacy.weave.context_state import WandbApiContext, _wandb_api_context tracer = engine_trace.tracer() # type: ignore diff --git a/weave/legacy/wandb_client_api.py b/weave/legacy/weave/wandb_client_api.py similarity index 98% rename from weave/legacy/wandb_client_api.py rename to weave/legacy/weave/wandb_client_api.py index 11bbff4e333..350dd9b8eba 100644 --- a/weave/legacy/wandb_client_api.py +++ b/weave/legacy/weave/wandb_client_api.py @@ -12,7 +12,7 @@ from wandb.errors import CommError as WandbCommError from wandb.sdk.internal.internal_api import _thread_local_api_settings -from weave.legacy import errors +from weave.legacy.weave import errors def wandb_public_api() -> public.Api: diff --git a/weave/legacy/wandb_file_manager.py b/weave/legacy/weave/wandb_file_manager.py similarity index 99% rename from weave/legacy/wandb_file_manager.py rename to weave/legacy/weave/wandb_file_manager.py index ecce61b90d0..af631151048 100644 --- a/weave/legacy/wandb_file_manager.py +++ b/weave/legacy/weave/wandb_file_manager.py @@ -12,8 +12,8 @@ from requests.auth import HTTPBasicAuth from wandb.sdk.lib import hashutil -from weave.legacy import environment as weave_env -from weave.legacy import filesystem, artifact_wandb, cache, errors, wandb_api, engine_trace, weave_http +from weave.legacy.weave import environment as weave_env +from weave.legacy.weave import filesystem, artifact_wandb, cache, errors, wandb_api, engine_trace, weave_http tracer = engine_trace.tracer() # type: ignore diff --git a/weave/legacy/wandb_interface/project_creator.py b/weave/legacy/weave/wandb_interface/project_creator.py similarity index 100% rename from weave/legacy/wandb_interface/project_creator.py rename to weave/legacy/weave/wandb_interface/project_creator.py diff --git a/weave/legacy/wandb_interface/wandb_artifact_pusher.py b/weave/legacy/weave/wandb_interface/wandb_artifact_pusher.py similarity index 95% rename from weave/legacy/wandb_interface/wandb_artifact_pusher.py rename to weave/legacy/weave/wandb_interface/wandb_artifact_pusher.py index fa80c077c16..0bbda99b705 100644 --- a/weave/legacy/wandb_interface/wandb_artifact_pusher.py +++ b/weave/legacy/weave/wandb_interface/wandb_artifact_pusher.py @@ -5,9 +5,9 @@ from wandb import Artifact from wandb.apis.public import api as wb_public -from weave.legacy import engine_trace -from weave.legacy import wandb_client_api -from weave.legacy.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun +from weave.legacy.weave import engine_trace +from weave.legacy.weave import wandb_client_api +from weave.legacy.weave.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun def artifact_commithash_by_digest( diff --git a/weave/legacy/wandb_interface/wandb_lite_run.py b/weave/legacy/weave/wandb_interface/wandb_lite_run.py similarity index 98% rename from weave/legacy/wandb_interface/wandb_lite_run.py rename to weave/legacy/weave/wandb_interface/wandb_lite_run.py index 4cd60b7452b..9703a9d88e4 100644 --- a/weave/legacy/wandb_interface/wandb_lite_run.py +++ b/weave/legacy/weave/wandb_interface/wandb_lite_run.py @@ -15,8 +15,8 @@ from wandb.sdk.internal.sender import _manifest_json_from_proto from wandb.sdk.lib import runid -from weave.legacy import errors -from weave.legacy import engine_trace, wandb_client_api +from weave.legacy.weave import errors +from weave.legacy.weave import engine_trace, wandb_client_api logger = logging.getLogger(__name__) diff --git a/weave/legacy/wandb_interface/wandb_stream_table.py b/weave/legacy/weave/wandb_interface/wandb_stream_table.py similarity index 97% rename from weave/legacy/wandb_interface/wandb_stream_table.py rename to weave/legacy/weave/wandb_interface/wandb_stream_table.py index 63a05b9fb8b..2669282ee2d 100644 --- a/weave/legacy/wandb_interface/wandb_stream_table.py +++ b/weave/legacy/weave/wandb_interface/wandb_stream_table.py @@ -15,8 +15,8 @@ from wandb.sdk.lib.paths import LogicalPath from wandb.sdk.lib.printer import get_printer -from weave.legacy import weave_types -from weave.legacy import ( +from weave.legacy.weave import weave_types +from weave.legacy.weave import ( storage, errors, artifact_base, @@ -27,8 +27,8 @@ runfiles_wandb, wandb_api, ) -from weave.legacy.core_types.stream_table_type import StreamTableType -from weave.legacy.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun +from weave.legacy.weave.core_types.stream_table_type import StreamTableType +from weave.legacy.weave.wandb_interface.wandb_lite_run import InMemoryLazyLiteRun if typing.TYPE_CHECKING: from wandb.sdk.internal.file_pusher import FilePusher @@ -201,8 +201,8 @@ def log(self, row_or_rows: ROW_TYPE) -> None: self._log_row(row) def rows(self) -> graph.Node: - from weave.legacy.ops_domain import stream_table_ops - from weave.legacy.ops_primitives import weave_api + from weave.legacy.weave.ops_domain import stream_table_ops + from weave.legacy.weave.ops_primitives import weave_api if self._weave_stream_table_ref is None: raise errors.WeaveInternalError("ref is None after ensure") @@ -211,7 +211,7 @@ def rows(self) -> graph.Node: ) def _ipython_display_(self) -> graph.Node: - from weave.legacy import show + from weave.legacy.weave import show return show.show(self.rows()) diff --git a/weave/legacy/wandb_util.py b/weave/legacy/weave/wandb_util.py similarity index 98% rename from weave/legacy/wandb_util.py rename to weave/legacy/weave/wandb_util.py index be4161ff038..11c6c717eb9 100644 --- a/weave/legacy/wandb_util.py +++ b/weave/legacy/weave/wandb_util.py @@ -1,8 +1,8 @@ import typing -from weave.legacy import errors -from weave.legacy import weave_types as types -from weave.legacy import ops, ops_domain +from weave.legacy.weave import errors +from weave.legacy.weave import weave_types as types +from weave.legacy.weave import ops, ops_domain class Weave0TypeJson(typing.TypedDict): diff --git a/weave/legacy/weave_http.py b/weave/legacy/weave/weave_http.py similarity index 98% rename from weave/legacy/weave_http.py rename to weave/legacy/weave/weave_http.py index 773625fe9ec..f425dd2567b 100644 --- a/weave/legacy/weave_http.py +++ b/weave/legacy/weave/weave_http.py @@ -13,7 +13,7 @@ import requests.auth import yarl -from weave.legacy import engine_trace, filesystem, server_error_handling +from weave.legacy.weave import engine_trace, filesystem, server_error_handling logging.getLogger("aiohttp.access").setLevel(logging.WARNING) logging.getLogger("aiohttp.client").setLevel(logging.WARNING) diff --git a/weave/legacy/weave_inspector.py b/weave/legacy/weave/weave_inspector.py similarity index 99% rename from weave/legacy/weave_inspector.py rename to weave/legacy/weave/weave_inspector.py index d878e4f8d4d..fdcde5393c6 100644 --- a/weave/legacy/weave_inspector.py +++ b/weave/legacy/weave/weave_inspector.py @@ -105,9 +105,9 @@ import tabulate -from weave.legacy import graph +from weave.legacy.weave import graph -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types from .partial_object import PartialObjectType @@ -137,7 +137,7 @@ def _node_type_name(node: graph.Node) -> str: def _type_name(node_type: types.Type) -> str: - from weave.legacy.language_features.tagging import tagged_value_type + from weave.legacy.weave.language_features.tagging import tagged_value_type if isinstance(node_type, tagged_value_type.TaggedValueType): return f"TV({_type_name(node_type.value)})" diff --git a/weave/legacy/weave_internal.py b/weave/legacy/weave/weave_internal.py similarity index 93% rename from weave/legacy/weave_internal.py rename to weave/legacy/weave/weave_internal.py index e26bcd526c2..4d485658814 100644 --- a/weave/legacy/weave_internal.py +++ b/weave/legacy/weave/weave_internal.py @@ -1,7 +1,7 @@ import typing -from weave.legacy import client_interface, context_state, errors, graph -from weave.legacy import weave_types as types +from weave.legacy.weave import client_interface, context_state, errors, graph +from weave.legacy.weave import weave_types as types def dereference_variables( @@ -83,14 +83,14 @@ def use( def make_var_node(type_: types.Type, name: str) -> graph.VarNode: # Circular import. TODO: fix - from weave.legacy import dispatch + from weave.legacy.weave import dispatch return dispatch.RuntimeVarNode(type_, name) def make_const_node(type_: types.Type, val: typing.Any) -> graph.ConstNode: # Circular import. TODO: fix - from weave.legacy import dispatch + from weave.legacy.weave import dispatch return dispatch.RuntimeConstNode(type_, val) @@ -114,7 +114,7 @@ def make_output_node( type_: types.Type, op_name: str, op_params: dict[str, graph.Node] ) -> graph.OutputNode: # Circular import. TODO: fix - from weave.legacy import dispatch + from weave.legacy.weave import dispatch return dispatch.RuntimeOutputNode(type_, op_name, op_params) @@ -125,7 +125,7 @@ def define_fn( ) -> graph.ConstNode: var_nodes = [make_var_node(t, k) for k, t in parameters.items()] try: - from weave.legacy import op_def + from weave.legacy.weave import op_def with op_def.no_refine(): fnNode = body(*var_nodes) @@ -148,7 +148,7 @@ def define_fn( # and the function doesn't explicitly operate on tagged values. this ensures that the input tags # are propagated appropriately to the output type of the function. def refine_graph(node: graph.Node) -> graph.Node: - from weave.legacy.registry_mem import memory_registry + from weave.legacy.weave.registry_mem import memory_registry if isinstance(node, (graph.ConstNode, graph.VoidNode, graph.VarNode)): return node @@ -176,6 +176,6 @@ def manual_call( You can produce incorrect nodes this way. Use with caution. """ - from weave.legacy import dispatch + from weave.legacy.weave import dispatch return dispatch.RuntimeOutputNode(output_type, op_name, inputs) diff --git a/weave/legacy/weave_pydantic.py b/weave/legacy/weave/weave_pydantic.py similarity index 95% rename from weave/legacy/weave_pydantic.py rename to weave/legacy/weave/weave_pydantic.py index 870b2a83d61..0c9b0f7c7ad 100644 --- a/weave/legacy/weave_pydantic.py +++ b/weave/legacy/weave/weave_pydantic.py @@ -1,6 +1,6 @@ from pydantic import BaseModel, create_model -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_types as types from . import infer_types diff --git a/weave/legacy/weave_types.py b/weave/legacy/weave/weave_types.py similarity index 99% rename from weave/legacy/weave_types.py rename to weave/legacy/weave/weave_types.py index 2b1c45c3891..ad46a314df6 100644 --- a/weave/legacy/weave_types.py +++ b/weave/legacy/weave/weave_types.py @@ -11,20 +11,20 @@ import pydantic from dateutil.parser import isoparse -from weave.legacy import ( +from weave.legacy.weave import ( box, context_state, errors, mappers_python, object_type_ref_util, ) -from weave.legacy import timestamp as weave_timestamp +from weave.legacy.weave import timestamp as weave_timestamp if typing.TYPE_CHECKING: - from weave.legacy import artifact_base - from weave.legacy.artifact_fs import FilesystemArtifact + from weave.legacy.weave import artifact_base + from weave.legacy.weave.artifact_fs import FilesystemArtifact - from weave.legacy import weave_inspector + from weave.legacy.weave import weave_inspector def to_weavejs_typekey(k: str) -> str: @@ -126,7 +126,7 @@ def type_class_of(obj): # type: ignore @staticmethod def type_of(obj: typing.Any) -> "Type": # type: ignore - from weave.legacy import ref_base + from weave.legacy.weave import ref_base if ( context_state.ref_tracking_enabled() @@ -460,7 +460,7 @@ def _make(cls, kwargs={}): # type: ignore def _inspect(self) -> "weave_inspector.TypeInspector": # type: ignore """Only intended to be used by developers to help debug the graph.""" # Circular import, so we do it here. - from weave.legacy import weave_inspector + from weave.legacy.weave import weave_inspector return weave_inspector.TypeInspector(self) @@ -1061,7 +1061,7 @@ def property_types(self) -> dict[str, Type]: # type: ignore @classmethod def typeclass_of_class(cls, check_class): # type: ignore - from weave.legacy import weave_pydantic + from weave.legacy.weave import weave_pydantic if not issubclass(check_class, pydantic.BaseModel): return cls @@ -1278,7 +1278,7 @@ def _hashable(self): # type: ignore @classmethod def type_of_instance(cls, obj): # type: ignore - from weave.legacy import infer_types + from weave.legacy.weave import infer_types attr_types = {} for field in dataclasses.fields(obj): @@ -1361,7 +1361,7 @@ def save_instance(self, obj, artifact, name): # type: ignore def load_instance(self, artifact, name, extra=None): # type: ignore with artifact.open(f"{name}.object.json") as f: # TODO: no circular imports! - from weave.legacy import graph + from weave.legacy.weave import graph return graph.Node.node_from_json(json.load(f)) @@ -1385,7 +1385,7 @@ def _is_assignable_to(self, other_type) -> typing.Optional[bool]: # type: ignor return None def save_instance(self, obj, artifact, name): # type: ignore - from weave.legacy import ref_base + from weave.legacy.weave import ref_base obj_ref = ref_base.get_ref(obj) if obj_ref is None: @@ -1419,7 +1419,7 @@ class LocalArtifactRefType(FilesystemArtifactRefType): @dataclasses.dataclass(frozen=True) class WandbArtifactRefType(FilesystemArtifactRefType): def load_instance(self, artifact, name, extra=None): # type: ignore - from weave.legacy import artifact_wandb + from weave.legacy.weave import artifact_wandb return artifact_wandb.WandbArtifactRef(artifact, name) @@ -1571,7 +1571,7 @@ def merge_types(a: Type, b: Type) -> Type: This implementation must match list.concat implementations (which is the only way to extend a list in Weave). Ie list.concat(list[a], [b]) -> list[merge_types(a, b)] """ - from weave.legacy.language_features.tagging import tagged_value_type + from weave.legacy.weave.language_features.tagging import tagged_value_type if a == b: return a diff --git a/weave/legacy/weavejs_fixes.py b/weave/legacy/weave/weavejs_fixes.py similarity index 99% rename from weave/legacy/weavejs_fixes.py rename to weave/legacy/weave/weavejs_fixes.py index 23c97ee64c7..6957e839a8d 100644 --- a/weave/legacy/weavejs_fixes.py +++ b/weave/legacy/weave/weavejs_fixes.py @@ -8,9 +8,9 @@ import math import typing -from weave.legacy import graph +from weave.legacy.weave import graph -from weave.legacy import weave_types +from weave.legacy.weave import weave_types def _convert_specific_opname_to_generic_opname( diff --git a/weave/legacy/weavify.py b/weave/legacy/weave/weavify.py similarity index 93% rename from weave/legacy/weavify.py rename to weave/legacy/weave/weavify.py index a85567a1e53..af81e62d492 100644 --- a/weave/legacy/weavify.py +++ b/weave/legacy/weave/weavify.py @@ -1,12 +1,12 @@ import typing -from weave.legacy import errors, graph, op_args, val_const +from weave.legacy.weave import errors, graph, op_args, val_const -from weave.legacy import weave_internal -from weave.legacy import weave_types as types +from weave.legacy.weave import weave_internal +from weave.legacy.weave import weave_types as types if typing.TYPE_CHECKING: - from weave.legacy import op_def + from weave.legacy.weave import op_def def verify_weave_fn_is_valid(op: "op_def.OpDef", weavified: graph.Node) -> None: @@ -79,7 +79,7 @@ def weave_fn_body(*args: graph.VarNode) -> graph.Node: def weavify_object(obj: typing.Any) -> graph.Node: - from weave.legacy.ops_primitives import dict_, make_list + from weave.legacy.weave.ops_primitives import dict_, make_list if isinstance(obj, graph.Node): return obj diff --git a/weave/syndata_mon.py b/weave/syndata_mon.py index a9838b99746..69087ef5533 100644 --- a/weave/syndata_mon.py +++ b/weave/syndata_mon.py @@ -10,7 +10,7 @@ import tqdm from faker import Faker -from weave.legacy.arrow.list_ import ArrowWeaveList, dataframe_to_arrow +from weave.legacy.weave.arrow.list_ import ArrowWeaveList, dataframe_to_arrow # Generate the version schedule diff --git a/weave/test_scripts/wandb_artifact_perf.py b/weave/test_scripts/wandb_artifact_perf.py index bc38aeb1d64..af3f3e0aa7d 100644 --- a/weave/test_scripts/wandb_artifact_perf.py +++ b/weave/test_scripts/wandb_artifact_perf.py @@ -13,7 +13,7 @@ import cProfile import time -from weave.legacy import ( +from weave.legacy.weave import ( artifact_wandb, async_map, engine_trace, diff --git a/weave/tests/fixture_fakewandb.py b/weave/tests/fixture_fakewandb.py index c5ea0e69773..4406f36613a 100644 --- a/weave/tests/fixture_fakewandb.py +++ b/weave/tests/fixture_fakewandb.py @@ -11,18 +11,18 @@ import wandb import weave +from weave.legacy.tests.util.tag_test_util import op_add_tag # Note: We're mocking out the whole io_service right now. This is too # high level and doesn't test the actual io implementation. We should # mock wandb_api instead probably. -from weave.legacy import io_service, util, wandb_api, wandb_client_api -from weave.legacy.artifact_wandb import ( +from weave.legacy.weave import io_service, util, wandb_api, wandb_client_api +from weave.legacy.weave.artifact_wandb import ( WandbArtifact, WandbArtifactManifest, WeaveWBArtifactByIDURI, WeaveWBArtifactURI, ) -from weave.legacy.tests.util.tag_test_util import op_add_tag TEST_TABLE_ARTIFACT_PATH = "testdata/wb_artifacits/test_res_1fwmcd3q:v0" ABS_TEST_TABLE_ARTIFACT_PATH = os.path.abspath(TEST_TABLE_ARTIFACT_PATH) diff --git a/weave/tests/trace/op_versioning_solo.py b/weave/tests/trace/op_versioning_solo.py index b2ceec4b2d2..bacc0e27155 100644 --- a/weave/tests/trace/op_versioning_solo.py +++ b/weave/tests/trace/op_versioning_solo.py @@ -1,7 +1,7 @@ import numpy as np import weave -from weave.legacy import artifact_fs +from weave.legacy.weave import artifact_fs @weave.op() diff --git a/weave/tests/trace/test_op_versioning.py b/weave/tests/trace/test_op_versioning.py index f1f2203a323..3fc023e235c 100644 --- a/weave/tests/trace/test_op_versioning.py +++ b/weave/tests/trace/test_op_versioning.py @@ -5,7 +5,7 @@ import pytest import weave -from weave.legacy import artifact_fs, derive_op, op_def +from weave.legacy.weave import artifact_fs, derive_op, op_def from weave.trace_server.trace_server_interface import FileContentReadReq, ObjReadReq diff --git a/weave/tests/trace/test_server.py b/weave/tests/trace/test_server.py index 936eb7aa360..c5aa37a3c74 100644 --- a/weave/tests/trace/test_server.py +++ b/weave/tests/trace/test_server.py @@ -6,13 +6,13 @@ import pytest import requests -from weave.legacy import api as weave -from weave.legacy import client as _client -from weave.legacy import context_state, ops -from weave.legacy import server as _server -from weave.legacy import weave_types as types -from weave.legacy.decorator_op import op -from weave.legacy.weave_internal import make_const_node +from weave.legacy.weave import api as weave +from weave.legacy.weave import client as _client +from weave.legacy.weave import context_state, ops +from weave.legacy.weave import server as _server +from weave.legacy.weave import weave_types as types +from weave.legacy.weave.decorator_op import op +from weave.legacy.weave.weave_internal import make_const_node SERVER_TYPES = ["inprocess", "subprocess", "http"] diff --git a/weave/tests/trace/test_weave_client.py b/weave/tests/trace/test_weave_client.py index b0bd9210ec5..17184676066 100644 --- a/weave/tests/trace/test_weave_client.py +++ b/weave/tests/trace/test_weave_client.py @@ -12,7 +12,7 @@ import weave import weave.trace_server.trace_server_interface as tsi from weave import Evaluation -from weave.legacy import op_def +from weave.legacy.weave import op_def from weave.trace import refs, weave_client from weave.trace.isinstance import weave_isinstance from weave.trace.op import Op diff --git a/weave/tests/trace/test_weaveflow.py b/weave/tests/trace/test_weaveflow.py index a41f4eba9d4..e35b7721551 100644 --- a/weave/tests/trace/test_weaveflow.py +++ b/weave/tests/trace/test_weaveflow.py @@ -5,7 +5,7 @@ from pydantic import Field import weave -from weave.legacy import ref_base +from weave.legacy.weave import ref_base pytestmark = pytest.mark.trace diff --git a/weave/tests/wandb_system_tests_conftest.py b/weave/tests/wandb_system_tests_conftest.py index d2c18bbf3e4..a67debe9871 100644 --- a/weave/tests/wandb_system_tests_conftest.py +++ b/weave/tests/wandb_system_tests_conftest.py @@ -15,7 +15,7 @@ import requests import wandb -from weave.legacy.wandb_api import ( +from weave.legacy.weave.wandb_api import ( WandbApiContext, from_environment, wandb_api_context, diff --git a/weave/trace/api.py b/weave/trace/api.py index 916d27c2841..1c7dbbb4bb0 100644 --- a/weave/trace/api.py +++ b/weave/trace/api.py @@ -6,7 +6,7 @@ import time from typing import Any, Callable, Iterator, Optional, Union -from weave.legacy import urls, util +from weave.legacy.weave import urls, util from weave.trace.call_context import get_current_call from weave.trace.client_context import weave_client as weave_client_context @@ -202,7 +202,7 @@ def serve( ) -> str: import uvicorn - from weave.legacy import wandb_api + from weave.legacy.weave import wandb_api from .serve_fastapi import object_method_app diff --git a/weave/trace/box.py b/weave/trace/box.py index 1c39bf5e39f..a185c50a822 100644 --- a/weave/trace/box.py +++ b/weave/trace/box.py @@ -10,7 +10,7 @@ import numpy as np -from weave.legacy.ref_base import Ref +from weave.legacy.weave.ref_base import Ref T = TypeVar("T") diff --git a/weave/trace/cli.py b/weave/trace/cli.py index 2204e2c4c76..edccda7a382 100644 --- a/weave/trace/cli.py +++ b/weave/trace/cli.py @@ -25,7 +25,7 @@ def cli() -> None: # def start_ui() -> None: # print("Starting server...") # try: -# from weave.legacy import server +# from weave.legacy.weave import server # except ModuleNotFoundError: # print("Run 'pip install weave[engine]' to use the local server.") # sys.exit(1) diff --git a/weave/trace/client_context/weave_client.py b/weave/trace/client_context/weave_client.py index b80375adb56..f74759f26ac 100644 --- a/weave/trace/client_context/weave_client.py +++ b/weave/trace/client_context/weave_client.py @@ -1,8 +1,8 @@ import threading from typing import TYPE_CHECKING, Optional -from weave.legacy import context_state -from weave.legacy.errors import WeaveInitError +from weave.legacy.weave import context_state +from weave.legacy.weave.errors import WeaveInitError if TYPE_CHECKING: from weave.trace.weave_client import WeaveClient diff --git a/weave/trace/custom_objs.py b/weave/trace/custom_objs.py index 99b7d41eb60..32272829af1 100644 --- a/weave/trace/custom_objs.py +++ b/weave/trace/custom_objs.py @@ -4,7 +4,7 @@ import tempfile from typing import Any, Dict, Generator, Iterator, Mapping, Optional, Union -from weave.legacy import artifact_fs +from weave.legacy.weave import artifact_fs from weave.trace import op_type # noqa: F401, Must import this to register op save/load from weave.trace.client_context.weave_client import require_weave_client from weave.trace.op import Op, op @@ -136,7 +136,7 @@ def decode_custom_obj( encoded_path_contents: Mapping[str, Union[str, bytes]], load_instance_op_uri: Optional[str], ) -> Any: - from weave.legacy import artifact_fs + from weave.legacy.weave import artifact_fs load_instance_op = None if load_instance_op_uri is not None: diff --git a/weave/trace/feedback.py b/weave/trace/feedback.py index a9a386aa002..99893df018d 100644 --- a/weave/trace/feedback.py +++ b/weave/trace/feedback.py @@ -5,7 +5,7 @@ from rich.table import Table -from weave.legacy import util +from weave.legacy.weave import util from weave.trace import rich_pydantic_util from weave.trace.client_context import weave_client as weave_client_context from weave.trace.refs import parse_uri diff --git a/weave/trace/op.py b/weave/trace/op.py index 3dca692b28d..b10885d43b8 100644 --- a/weave/trace/op.py +++ b/weave/trace/op.py @@ -17,7 +17,7 @@ runtime_checkable, ) -from weave.legacy import context_state +from weave.legacy.weave import context_state from weave.trace import box, call_context, settings from weave.trace.client_context import weave_client as weave_client_context from weave.trace.context import call_attributes @@ -317,7 +317,7 @@ async def extract(): ``` """ if context_state.get_loading_built_ins(): - from weave.legacy.decorator_op import op as legacy_op + from weave.legacy.weave.decorator_op import op as legacy_op return legacy_op(*args, **kwargs) # type: ignore diff --git a/weave/trace/op_type.py b/weave/trace/op_type.py index 90608f9c99b..fd948b8a2e0 100644 --- a/weave/trace/op_type.py +++ b/weave/trace/op_type.py @@ -13,7 +13,7 @@ from _ast import AsyncFunctionDef, ExceptHandler from typing import Any, Callable, Optional, Union, get_args, get_origin -from weave.legacy import artifact_fs, context_state, errors, storage +from weave.legacy.weave import artifact_fs, context_state, errors, storage from weave.trace.ipython import ( ClassNotFoundError, get_class_source, @@ -21,7 +21,7 @@ ) from weave.trace.refs import ObjectRef -from ..legacy import environment +from ..legacy.weave import environment from . import serializer from .op import Op diff --git a/weave/trace/ref_util.py b/weave/trace/ref_util.py index ebaec7dfe1e..47aed3131dd 100644 --- a/weave/trace/ref_util.py +++ b/weave/trace/ref_util.py @@ -2,7 +2,7 @@ import typing from urllib import parse -from weave.legacy import box +from weave.legacy.weave import box from weave.trace_server import refs_internal DICT_KEY_EDGE_NAME = refs_internal.DICT_KEY_EDGE_NAME @@ -22,7 +22,7 @@ def parse_local_ref_str(s: str) -> typing.Tuple[str, typing.Optional[list[str]]] def val_with_relative_ref( parent_object: typing.Any, child_object: typing.Any, ref_extra_parts: list[str] ) -> typing.Any: - from weave.legacy import context_state, ref_base + from weave.legacy.weave import context_state, ref_base # If we already have a ref, resolve it if isinstance(child_object, ref_base.Ref): @@ -31,7 +31,7 @@ def val_with_relative_ref( # Only do this if ref_tracking_enabled right now. I just want to # avoid introducing new behavior into W&B prod for the moment. if context_state.ref_tracking_enabled(): - from weave.legacy import storage + from weave.legacy.weave import storage child_ref = storage.get_ref(child_object) parent_ref = ref_base.get_ref(parent_object) diff --git a/weave/trace/rich_pydantic_util.py b/weave/trace/rich_pydantic_util.py index 2c5e21fa2ed..ec88c0545db 100644 --- a/weave/trace/rich_pydantic_util.py +++ b/weave/trace/rich_pydantic_util.py @@ -6,7 +6,7 @@ from rich.console import Console from rich.table import Table -from weave.legacy import util +from weave.legacy.weave import util def dict_to_table(d: dict[str, Any]) -> Table: diff --git a/weave/trace/serve_fastapi.py b/weave/trace/serve_fastapi.py index 89ef88b1d8c..98adc7537b1 100644 --- a/weave/trace/serve_fastapi.py +++ b/weave/trace/serve_fastapi.py @@ -12,12 +12,12 @@ except ImportError: from typing_extensions import Annotated # type: ignore -from weave.legacy import cache, op_args, pyfunc_type_util -from weave.legacy.wandb_api import WandbApiAsync +from weave.legacy.weave import cache, op_args, pyfunc_type_util +from weave.legacy.weave.wandb_api import WandbApiAsync from weave.trace.op import Op from weave.trace.refs import ObjectRef -from ..legacy import errors, weave_pydantic +from ..legacy.weave import errors, weave_pydantic key_cache: cache.LruTimeWindowCache[str, typing.Optional[bool]] = ( cache.LruTimeWindowCache(datetime.timedelta(minutes=5)) diff --git a/weave/trace/weave_client.py b/weave/trace/weave_client.py index 11029cc64fd..a16884b955d 100644 --- a/weave/trace/weave_client.py +++ b/weave/trace/weave_client.py @@ -10,7 +10,7 @@ from requests import HTTPError from weave import version -from weave.legacy import urls +from weave.legacy.weave import urls from weave.trace import call_context, trace_sentry from weave.trace.client_context import weave_client as weave_client_context from weave.trace.exception import exception_to_json_str @@ -52,7 +52,7 @@ ) if typing.TYPE_CHECKING: - from ..legacy import ref_base + from ..legacy.weave import ref_base # Controls if objects can have refs to projects not the WeaveClient project. diff --git a/weave/trace/weave_init.py b/weave/trace/weave_init.py index 07de4b40045..55188cad443 100644 --- a/weave/trace/weave_init.py +++ b/weave/trace/weave_init.py @@ -1,6 +1,6 @@ import typing -from weave.legacy import errors +from weave.legacy.weave import errors from weave.trace import autopatch, init_message, trace_sentry, weave_client from weave.trace.client_context import weave_client as weave_client_context from weave.trace_server import remote_http_trace_server, sqlite_trace_server @@ -18,7 +18,7 @@ def reset(self) -> None: def get_username() -> typing.Optional[str]: - from weave.legacy import wandb_api + from weave.legacy.weave import wandb_api api = wandb_api.get_wandb_api_sync() try: @@ -28,7 +28,7 @@ def get_username() -> typing.Optional[str]: def get_entity_project_from_project_name(project_name: str) -> tuple[str, str]: - from weave.legacy import wandb_api + from weave.legacy.weave import wandb_api fields = project_name.split("/") if len(fields) == 1: @@ -76,7 +76,7 @@ def init_weave( else: _current_inited_client.reset() - from weave.legacy import wandb_api + from weave.legacy.weave import wandb_api # Must init to read ensure we've read auth from the environment, in # case we're on a new thread. diff --git a/weave/trace_server/remote_http_trace_server.py b/weave/trace_server/remote_http_trace_server.py index 842e5c43893..568552c23f9 100644 --- a/weave/trace_server/remote_http_trace_server.py +++ b/weave/trace_server/remote_http_trace_server.py @@ -6,8 +6,8 @@ import tenacity from pydantic import BaseModel, ValidationError -from weave.legacy.environment import weave_trace_server_url -from weave.legacy.wandb_interface import project_creator +from weave.legacy.weave.environment import weave_trace_server_url +from weave.legacy.weave.wandb_interface import project_creator from . import requests from . import trace_server_interface as tsi diff --git a/weave/weave_server.py b/weave/weave_server.py index b07886df75d..05870a1ea1d 100644 --- a/weave/weave_server.py +++ b/weave/weave_server.py @@ -25,7 +25,7 @@ from flask_cors import CORS from werkzeug.exceptions import HTTPException -from weave.legacy import ( +from weave.legacy.weave import ( context_state, engine_trace, environment, @@ -41,8 +41,10 @@ wandb_api, weavejs_fixes, ) -from weave.legacy.language_features.tagging import tag_store -from weave.legacy.server_error_handling import client_safe_http_exceptions_as_werkzeug +from weave.legacy.weave.language_features.tagging import tag_store +from weave.legacy.weave.server_error_handling import ( + client_safe_http_exceptions_as_werkzeug, +) logger = logging.getLogger(__name__) @@ -91,7 +93,7 @@ def execute(self, document, *args, **kwargs): # Ensure these are imported and registered -from weave.legacy import ops +from weave.legacy.weave import ops # NOTE: Fixes flask dev server's auto-reload capability, by forcing it to use # stat mode instead of watchdog mode. It turns out that "import wandb" breaks @@ -110,11 +112,11 @@ def execute(self, document, *args, **kwargs): def import_ecosystem(): - from weave.legacy import ops, panels, panels_py + from weave.legacy.weave import ops, panels, panels_py # Attempt to import MVP ecosystem modules try: - from weave.legacy.ecosystem import langchain, replicate + from weave.legacy.weave.ecosystem import langchain, replicate except ImportError: pass @@ -127,7 +129,7 @@ def import_ecosystem(): # except (ImportError, OSError, wandb.Error): # print("Error: Couldn't import faiss module for Weaveflow.") try: - from weave.legacy.ecosystem import all + from weave.legacy.weave.ecosystem import all except (ImportError, OSError, wandb.Error): pass